합성곱 신경망
다층 신경망에서 이미지 처리
- 이미지는 높이, 너비, 채널을 가진 (H,W,C) 형태
- 이미지 분류용 다층 신경망은 보통
Flatten으로 이미지를 길이 H×W×C인 벡터로 변환 Flatten은 픽셀값과 순서를 삭제하지 않지만 높이·너비 축을 하나의 축으로 펼침- Dense 레이어는 지역적으로 가까운 픽셀을 따로 구분하지 않고 모든 입력을 각 뉴런에 연결
- 입력 크기와 출력 뉴런 수가 커질수록 (H×W×C)×U+U개의 파라미터가 필요

합성곱 신경망 Convolutional Neural Network
- 합성곱 신경망(CNN)은 작은 지역 패치에 필터를 반복 적용해 특징을 추출
- 필터(filter) 또는 커널(kernel)은 데이터에서 학습하는 가중치
- 같은 필터를 이미지의 여러 위치에서 공유하므로 지역 패턴을 효율적으로 탐색
- 필터 하나는 하나의 피처 맵을 생성하며, 필터 수는 출력 채널 수와 같음
- 수용 영역(receptive field): 특정 뉴런의 값에 영향을 주는 원본 입력 영역
- 피처 맵(feature map): 필터가 각 위치에서 계산한 합성곱 출력

부분 특징에서 전체 특징으로
- 얕은 레이어는 선, 방향, 모서리 같은 낮은 수준의 특징을 추출
- 다음 레이어는 앞에서 찾은 특징을 조합해 눈, 코, 입처럼 더 큰 패턴을 표현
- 깊은 레이어는 부분 특징을 다시 조합해 얼굴이나 사물 전체에 가까운 특징을 표현
- 실제로 학습되는 특징은 데이터와 학습 목표에 따라 달라짐

풀링 pooling
- 풀링은 피처 맵의 지역 값을 요약해 공간 크기를 줄이는 다운샘플링
- 학습하는 가중치가 없으며 합성곱 레이어 수를 자동으로 줄이지 않음
- 공간 크기가 줄어 이후 레이어의 연산량과 Dense 레이어의 파라미터 수를 줄일 수 있음
- 세부 위치 정보가 손실되므로 위치가 중요한 작업에서는 성능에 영향을 줄 수 있음
- 최대 풀링(max pooling)은 지역의 최댓값을 선택
- 평균 풀링(average pooling)은 지역의 평균값을 계산

CNN의 기본 구조
- 특징 추출 부분: 합성곱, 활성화 함수, 필요에 따른 풀링
- 분류 부분:
Flatten또는 전역 풀링으로 특징을 모은 뒤 Dense 레이어로 예측 Flatten은 한 가지 연결 방법이며 모든 CNN에 필수인 것은 아님

Keras 실습
데이터 준비
노트북과 같은 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%
퀴즈
이미지 분류에서 Dense 중심 구조와 비교한 합성곱 레이어의 핵심 장점으로 가장 알맞은 것은 무엇입니까?
퀴즈를 풀려면 대화형 기능을 불러와야 합니다.