logo

합성곱 신경망 실습

데이터 준비

노트북과 같은 Torch 백엔드와 난수 초기값을 사용.

import os
os.environ["KERAS_BACKEND"] = "torch"

import keras
import numpy as np

keras.utils.set_random_seed(812)  # 실행 결과 재현용 난수 초기값

Fashion MNIST를 불러오면 훈련 이미지에는 채널 축이 없는 (60000, 28, 28) 형태가 저장됨.

(x_train, y_train), (x_test, y_test) = (
    keras.datasets.fashion_mnist.load_data()
)
  • CNN에 입력되는 이미지는 채널 축이 필요.
  • MNIST 데이터는 (N, 28, 28) 형식이므로 마지막에 채널 차원을 추가하여 (N, 28, 28, 1)로 만듦
  • 컬러 이미지의 경우 (N, H, W, 3)이므로 채널 축이 이미 존재.
x_train_cnn = np.expand_dims(x_train, axis=-1)  # 채널 축 추가
x_test_cnn = np.expand_dims(x_test, axis=-1)

CNN 구성

  • 첫 번째 합성곱은 padding="same"으로 28×28 공간 크기를 유지하며 피처 맵 32개를 생성
  • 첫 번째 2×2 최대 풀링은 공간 크기를 14×14로 축소
  • 두 번째 합성곱은 12×12 피처 맵 64개를 생성
  • 두 번째 풀링은 공간 크기를 6×6으로 축소
  • Flatten은 6×6×64 피처 맵을 길이 2,304인 분류용 벡터로 변환
  • 마지막 Dense 레이어는 10개 클래스의 로짓 출력
keras.utils.set_random_seed(812)  # 모델 가중치 초기값 재현

cnn_model = keras.models.Sequential([
    keras.Input(shape=(28, 28, 1)),  # 28×28 흑백 이미지
    keras.layers.Rescaling(1/255),  # 픽셀값을 0~1로 변환
    # 공간 크기를 유지하며 지역 특징 32개 추출
    keras.layers.Conv2D(
        32, (3, 3), padding="same", activation="relu"
    ),
    # 높이와 너비를 각각 절반으로 축소
    keras.layers.MaxPooling2D((2, 2)),
    # 더 깊은 지역 특징 64개 추출
    keras.layers.Conv2D(64, (3, 3), activation="relu"),
    keras.layers.MaxPooling2D((2, 2)),
    keras.layers.Flatten(),
    keras.layers.Dense(10),  # 10개 클래스의 로짓
])

cnn_model.compile(
    # 실습에서 사용할 고정 학습률
    optimizer=keras.optimizers.SGD(learning_rate=0.001),
    loss=keras.losses.SparseCategoricalCrossentropy(
        from_logits=True  # 마지막 Dense가 로짓을 출력
    ),
    metrics=["accuracy"],
)

각 레이어의 출력 형태와 전체 파라미터 수를 확인.

for layer in cnn_model.layers:
    print(layer.__class__.__name__, tuple(layer.output.shape))

cnn_model.count_params()
실행 결과
Rescaling (None, 28, 28, 1)
Conv2D (None, 28, 28, 32)
MaxPooling2D (None, 14, 14, 32)
Conv2D (None, 12, 12, 64)
MaxPooling2D (None, 6, 6, 64)
Flatten (None, 2304)
Dense (None, 10)
41866

첫 합성곱은 공간 크기를 유지하고, 두 번의 풀링 뒤에는 6×6×64가 됨.

모델 훈련

  • 최대 5에포크 훈련
  • 한 배치에는 이미지 64개 사용
  • 훈련 데이터의 20%를 검증 데이터로 사용
  • SGD 학습률은 0.001로 지정
cnn_history = cnn_model.fit(
    x_train_cnn,
    y_train,
    epochs=5,  # 짧은 실습을 위한 반복 횟수
    batch_size=64,  # 한 번에 처리할 이미지 수
    validation_split=0.2,  # 훈련 데이터의 20%로 검증
    verbose=0,  # 마지막 지표만 확인
)
cnn_history.history["accuracy"][-1]  # 훈련 정확도
실행 결과
0.6410416960716248
cnn_history.history["val_accuracy"][-1]  # 검증 정확도
실행 결과
0.659500002861023
cnn_history.history["val_loss"][-1]  # 검증 손실
실행 결과
1.0048625469207764
  • 5번째 에포크의 훈련 정확도는 0.6410, 검증 정확도는 0.6595
  • 검증 손실은 1.0049
  • 실행 장치와 라이브러리 버전에 따라 구체적인 값은 달라질 수 있음

테스트 데이터 평가

cnn_test_loss, cnn_test_accuracy = cnn_model.evaluate(
    x_test_cnn, y_test, verbose=0  # 진행 막대 숨김
)

cnn_test_accuracy  # 테스트 정확도
실행 결과
0.6417999863624573
  • 테스트 정확도는 약 64.18%

퀴즈

문제 1 / 3맞음: 0힌트: 0틀림: 0채점중: 0남음: 3

Conv2D의 filters와 출력 채널 수의 관계로 가장 알맞은 것은 무엇입니까?

  • ○필터 하나가 피처 맵 하나를 만들므로 filters가 출력 채널 수를 결정한다
  • ○커널의 높이와 너비를 곱한 값이 항상 출력 채널 수가 된다
  • ○입력 이미지의 높이와 너비가 그대로 출력 채널 수가 된다
  • ○한 배치에 포함된 이미지 수가 출력 채널 수를 결정한다

퀴즈를 풀려면 대화형 기능을 불러와야 합니다.

Previous
합성곱 신경망(CNN)