포스트

03. Machine Learning_CNN (Convolution, Pooling, im2col, Transfer Learning)

머신러닝및실습(2) CNN 정리: 합성곱 연산과 출력 크기 공식, 패딩과 스트라이드, 3차원 합성곱, 풀링의 특징, im2col과 Convolution, Pooling 계층 구현(기울기 검증 포함), numpy CNN으로 MNIST 학습, Keras CNN으로 CIFAR-10 분류, 대표 CNN 구조(AlexNet~ResNet), ResNet50 전이 학습 세 가지 실험 비교

03. Machine Learning_CNN (Convolution, Pooling, im2col, Transfer Learning)

학습 목표

  • CNN이 완전연결 신경망과 다른 점, 합성곱 연산과 출력 크기 공식
  • 3차원 합성곱(채널), 필터 개수와 출력 채널, 풀링의 세 가지 특징
  • im2col이 필요한 이유와 이를 이용한 Convolution, Pooling 계층
  • numpy로 만든 CNN(MNIST)과 Keras CNN(CIFAR-10)의 학습 결과 해석
  • AlexNet, VGG, GoogLeNet, ResNet의 핵심 아이디어와 층을 깊게 하는 이유
  • 전이 학습: 동결(frozen)과 미세 조정(fine-tuning) 결과 비교

numpy 코드는 실습 노트북의 im2col, Convolution, Pooling을 그대로 가져와 다시 실행함. TensorFlow 학습 결과(CIFAR-10, ResNet50)는 실습 노트북에서 실행한 출력


1. CNN이란

  • CNN(Convolutional Neural Network): 합성곱(convolution) 계층으로 이미지의 특징을 추출하는 신경망. 이미지, 영상 처리에 사용
  • 완전연결(Dense) 신경망은 입력을 1차원으로 펼치기 때문에 위, 아래, 옆 픽셀의 공간 정보가 사라짐. CNN은 (채널, 높이, 너비) 형상을 유지한 채 계산함
  • 흐름: 입력 → Convolution → Activation(ReLU) → Pooling 을 반복해 특징 맵(feature map)을 만들고, 마지막에 Flatten → Dense로 분류
  • 앞쪽 층의 필터는 가장자리, 색 같은 단순한 특징을, 깊은 층일수록 더 복잡하고 추상적인 특징을 추출함

2. 합성곱 연산

행렬곱(행 × 열)과 달리 필터를 겹친 위치끼리 곱한 뒤 모두 더함.

1
2
3
A = [[1, 2, 3, 0], [0, 1, 2, 3], [3, 0, 1, 2], [2, 3, 0, 1]]   # 4x4 입력
B = [[2, 0, 1], [0, 1, 2], [1, 0, 2]]                          # 3x3 필터
out[i][j] = np.sum(A[i:i+3, j:j+3] * B)
1
2
[[15 16]
 [ 6 15]]

패딩, 스트라이드, 출력 크기

  • 패딩: 합성곱 전에 입력 주변을 특정 값(보통 0)으로 채우는 것. 출력 크기를 조정하려고 사용 (padding='same'이면 입력과 출력 크기가 같음)
  • 스트라이드: 필터를 적용하는 위치의 간격(보폭). 커질수록 출력이 작아짐

식으로 쓰면 출력 높이는 \(OH = \frac{H + 2P - FH}{S} + 1\) (너비도 같은 방식)

1
2
3
4
5
H=4, F=3, P=0, S=1 -> OH=2
H=4, F=3, P=1, S=1 -> OH=4
H=7, F=3, P=0, S=2 -> OH=3
H=28, F=5, P=0, S=1 -> OH=24
H=32, F=3, P=1, S=1 -> OH=32

3차원 합성곱

  • 입력 채널 수와 필터 채널 수가 같아야 하고, 채널별로 합성곱한 결과를 모두 더해 출력 채널 하나를 만듦
  • 필터를 FN개 쓰면 출력 채널도 FN개, 편향도 FN개. 필터의 형상은 (FN, C, FH, FW)
1
2
3
4
5
6
loop:
 [[ 0. -1.]
 [ 9.  0.]]
Convolution(im2col):
 [[ 0. -1.]
 [ 9.  0.]]

노트북의 for문 결과와 im2col 기반 Convolution 계층의 결과가 같음.


3. 풀링

  • 합성곱과 활성화 함수 뒤에 두어 데이터의 크기(가로, 세로)를 줄임 → 매개변수와 연산량이 줄고 과적합 방지에 도움
  • 최대 풀링(영역의 최댓값), 평균 풀링(영역의 평균)
  • 특징 (중요)
    1. 학습할 매개변수가 없음 (합성곱 계층과의 차이)
    2. 채널 수가 그대로 유지됨 (채널마다 독립적으로 계산)
    3. 입력의 작은 변화에 강함 (최댓값 위치가 조금 움직여도 결과가 같음)
1
2
3
[[2. 3.]
 [4. 2.]]
3채널 입력 -> 출력 shape: (1, 3, 2, 2)

4. im2col과 계층 구현

  • im2col(image to column): 4차원 입력 (N, C, H, W)을 필터가 닿는 영역 단위로 펼쳐 2차원 행렬로 바꾸는 함수
  • 장점: 합성곱을 큰 행렬곱 한 번으로 계산할 수 있어 빠름(BLAS 최적화 활용). 단점: 필터 영역이 겹치는 원소가 중복 저장되어 메모리를 더 씀
  • 합성곱 절차: 입력을 im2col로 펼침 → 필터를 (FN, C×FH×FW)에서 전치해 펼침 → 행렬곱 + 편향 → 결과를 (N, FN, OH, OW)로 reshape
1
2
3
4
5
6
7
8
9
def forward(self, x):
    FN, C, FH, FW = self.W.shape
    N, C, H, W = x.shape
    out_h = (H + 2 * self.pad - FH) // self.stride + 1
    out_w = (W + 2 * self.pad - FW) // self.stride + 1
    col = im2col(x, FH, FW, self.stride, self.pad)      # (N*OH*OW, C*FH*FW)
    col_W = self.W.reshape(FN, -1).T                    # (C*FH*FW, FN)
    out = np.dot(col, col_W) + self.b
    return out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)
1
2
(1,3,4,4) F3 -> (4, 27)
(10,3,7,7) F5 S1 -> (90, 75)
  • (1, 3, 4, 4)에 3×3 필터 → 출력 위치 2×2 = 4행, 3×3×3 = 27열
  • (10, 3, 7, 7)에 5×5 필터 → 10 × 3 × 3 = 90행, 75열

노트북의 Convolution.backward(설명 없이 넘어간 부분)가 맞는지 수치 미분으로 확인했음.

1
dW 최대 오차: 1.66e-09

오차가 1e-9 수준이라 역전파 구현이 정확함.


5. numpy CNN으로 MNIST 학습 (실습 노트북 결과)

구조: Conv(30개, 5×5) → ReLU → Pool(2×2, stride 2) → Affine(100) → ReLU → Affine(10) → SoftmaxWithLoss

  • 28 − 5 + 1 = 24 → 풀링 후 12, 따라서 Affine1 입력은 30 × 12 × 12 = 4,320
  • 학습 데이터 5,000장, 테스트 1,000장, 배치 50, 학습률 0.1, 2,000번 반복 (100번이 1 epoch이므로 20 epoch)
1
2
3
4
5
6
Epoch : 1 => train 정확도 : 9.76 % / test 정확도 : 11.6 % / loss : 2.2981
Epoch : 3 => train 정확도 : 83.14 % / test 정확도 : 79.3 % / loss : 0.4609
Epoch : 5 => train 정확도 : 90.74 % / test 정확도 : 88.7 % / loss : 0.1977
Epoch : 10 => train 정확도 : 96.74 % / test 정확도 : 93.5 % / loss : 0.0297
Epoch : 15 => train 정확도 : 97.68 % / test 정확도 : 93.6 % / loss : 0.0274
Epoch : 20 => train 정확도 : 99.4 % / test 정확도 : 95.4 % / loss : 0.0065

c numpy CNN의 train, test 정확도

1
2
3
CIFAR CNN: [896, 18496, 73856, 20490] total 113738
scratch CNN (conv, affine1, affine2): [780, 432100, 1010] total 433890
같은 입력을 Dense(30*24*24)로 연결하면: 13564800
  • 데이터를 1/12만 썼는데도 test 95.4%에 도달. 합성곱 계층 매개변수는 780개뿐이며, 같은 출력을 Dense로 만들면 1,356만 개가 필요함 → 가중치 공유의 효과
  • train 99.4%와 test 95.4%의 차이(4%p)는 데이터가 적어 생긴 과적합

6. Keras CNN으로 CIFAR-10 (실습 노트북 결과)

CIFAR-10: 32×32 컬러(채널 3) 이미지 10개 클래스, 학습 50,000장, 테스트 10,000장.

a CIFAR-10 학습 데이터 일부

  • 정규화(÷255)와 one-hot 인코딩(to_categorical). one-hot은 해당 클래스 위치만 1, 나머지 0으로 표현하는 방식으로, 클래스 번호를 숫자 그대로 쓰면 생기는 의도치 않은 크기, 순서 관계(예: 1 + 2 = 3)를 막기 위해 사용
  • 구조: [Conv(32/64/128, 3×3, same, ReLU) → MaxPool(2×2)] × 3 → Flatten(2,048) → Dense(10, softmax), Adam(0.001), batch 16, 10 epoch
층출력매개변수
Conv2D 32(32, 32, 32)3×3×3×32 + 32 = 896
Conv2D 64(16, 16, 64)3×3×32×64 + 64 = 18,496
Conv2D 128(8, 8, 128)3×3×64×128 + 128 = 73,856
Dense 10(10)2,048×10 + 10 = 20,490
합계 113,738

합성곱 층의 매개변수 수는 식으로 쓰면 \((FH \times FW \times C) \times FN + FN\) (필터 가중치 + 필터마다 편향 하나). 첫 층이면 3 × 3 × 3 × 32 + 32 = 896. 입력 크기(32 × 32)는 매개변수 수와 관계없다는 점이 Dense와 다름.

강의에서는 같은 코드로 Fashion MNIST(28 × 28 흑백 의류 이미지 10종)도 직접 학습해 보라고 했음. 데이터셋만 바꾸면 입력이 흑백(채널 1)이고 크기도 달라서 input_shape와 reshape를 고쳐야 오류가 나지 않음.

1
2
3
4
5
Epoch 1/10 - accuracy: 0.5297 - loss: 1.3259
Epoch 5/10 - accuracy: 0.7844 - loss: 0.6189
Epoch 10/10 - accuracy: 0.8700 - loss: 0.3712
test loss : 0.997825
test accuracy : 71.17 %

b 테스트 이미지 10장 예측 (초록은 정답, 빨강은 오답)

  • train 87.0%와 test 71.2%의 차이가 약 16%p로 과적합이 뚜렷함. Dropout, 데이터 증강, 배치 정규화가 필요한 상황
  • 10장 중 6장 정답. 고양이를 개나 말로, 비행기를 배로 착각함 (32×32에서 모양이나 배경이 비슷한 경우)

7. 대표 CNN 구조

ImageNet과 ILSVRC

  • ImageNet: 100만 장이 넘는 이미지에 클래스 이름을 붙인 대규모 데이터셋
  • ILSVRC(ImageNet Large Scale Visual Recognition Challenge): ImageNet으로 이미지 분류 성능을 겨루는 대회 (2010년 시작)
  • 2010, 2011년 우승은 얕은 구조였고, 2012년 AlexNet부터 딥러닝이 매년 우승. 아래 표의 모델 대부분이 이 대회에서 나왔음
  • ResNet(2015) 이후 사람보다 오차가 낮아지면서 관심이 정확도에서 효율성(MobileNet, ShuffleNet처럼 모바일에서도 돌아가는 가벼운 모델)으로 옮겨 감
모델핵심
LeNet합성곱과 서브샘플링을 반복하는 최초의 CNN 구조 (손글씨 숫자)
AlexNet합성곱과 풀링 반복 후 완전연결. ReLU, LRN(현재는 배치 정규화), Dropout, GPU 사용
ZFNetAlexNet을 시각화 기법으로 분석해 하이퍼파라미터를 조정한 개선판
VGGNet16층(VGG16), 19층(VGG19). 3×3 작은 필터를 2~4번 연속 쓰고 풀링으로 크기를 절반으로 줄이는 구조 반복
GoogLeNetInception 모듈: 크기가 다른 필터와 풀링을 병렬로 적용해 결합. 1×1 합성곱으로 채널 수를 줄여 매개변수와 연산량 감소
ResNet깊어질수록 오히려 학습이 안 되는 문제를 스킵 연결로 해결. 출력이 F(x) + x라서 역전파 때 기울기가 그대로 전달됨
SENetSE block으로 채널별 중요도를 조절해 기존 네트워크 성능 향상

층을 깊게 하는 이유 (출제)

  • 같은 넓이를 보더라도 작은 필터를 여러 층 쌓으면 매개변수가 줄어듦. 예를 들어 5×5 필터 하나는 25개, 3×3 두 층은 같은 5×5 영역을 보면서 18개
  • 층 사이에 활성화 함수가 들어가 비선형 표현력이 커짐
  • 각 층이 단순한 문제를 나눠 맡아 학습이 효율적이 됨 (앞층은 가장자리, 뒷층은 물체의 부분)

정확도 높이기

  • 데이터 확장(augmentation): 회전, 이동, 자르기, 좌우 뒤집기, 밝기/대비 조절, 확대/축소로 학습 이미지를 인위적으로 늘림
  • 전이 학습: 큰 데이터셋(ImageNet)으로 학습된 모델의 가중치를 가져와 새 데이터에 재사용. 데이터가 적을 때 특히 유용

8. 전이 학습: ResNet50 → CIFAR-10 (실습 노트북 결과)

ImageNet으로 학습한 ResNet50(include_top=False)에 분류기(Dense 10)를 붙여 세 가지로 실험했음.

실험백본분류기 앞단검증 데이터학습률결과 (test)
A동결Flatten테스트셋Adam 기본(1e-3)63.53% (6 epoch에 조기 종료)
B동결GlobalAveragePooling학습셋의 10%Adam 기본61.42% (4 epoch에 조기 종료)
C전체 학습(BN 동결) + 데이터 증강GlobalAveragePooling학습셋의 10% (stratify)1e-579.18% (20 epoch)
1
2
3
4
5
[C] Epoch 1/20  - accuracy: 0.4974 - val_accuracy: 0.6600 - val_loss: 0.9683
[C] Epoch 10/20 - accuracy: 0.7876 - val_accuracy: 0.7628 - val_loss: 0.6876
[C] Epoch 20/20 - accuracy: 0.8436 - val_accuracy: 0.8110 - val_loss: 0.5956
Test loss: 0.6408846974372864
Test accuracy: 0.7918000221252441
  • 동결(A, B)은 ImageNet 특징을 그대로 쓰기 때문에 32×32 CIFAR에 잘 맞지 않아 60%대에 머묾. 직접 만든 작은 CNN(71%)보다도 낮음
  • 미세 조정(C): 백본 전체를 작은 학습률(1e-5)로 다시 학습하고, 증강(뒤집기, 회전, 확대, 이동)을 더하자 79%로 가장 높음. 학습률을 작게 하는 이유는 사전 학습된 가중치를 크게 망가뜨리지 않기 위해서
  • train과 val 정확도 차이가 3%p 정도라 과적합도 작음 (증강 효과)
  • C는 epoch당 약 5분이 걸릴 만큼 비용이 큼. 32×32 입력은 ResNet50의 마지막 특징 맵이 1×1이 되어서 해상도를 키우면 더 좋아질 여지가 있음

실험 A는 validation_data=(x_test, y_test)로 테스트셋을 보고 조기 종료했기 때문에 63.53%는 낙관적으로 편향된 값임(데이터 누수). B, C처럼 학습 데이터에서 검증셋을 떼어 내야 테스트 결과를 믿을 수 있음


정리 노트에서 정정한 부분 정리

위치원래정정이유
VGGNet6층(VGG16)16층(VGG16)이름의 숫자가 가중치 층 수
층을 깊게 하는 이유필터의 크기를 작제하면작은 필터를 여러 층 쌓으면오타. 3×3 두 층 = 18개 대 5×5 하나 = 25개
CNN 흐름Feature map → Convolution → Activation → Pooling입력 → Convolution → Activation → Pooling → 특징 맵특징 맵은 결과물
im2colimage ro column, 2차원으로 변형하면 영역이 겹쳐 메모리 소비image to column. 겹치는 원소가 중복 저장되어 메모리 사용이 늘지만 행렬곱 한 번으로 빠름오타와 표현 정리
패딩출려 크기를 조정출력 크기 조정오타
Convolution 계층self.col_w = Noneself.col_W = None대소문자가 달라 실제로는 forward에서 새 속성이 생김 (동작에는 문제 없음)
전이 학습 실험 A검증 데이터로 테스트셋 사용학습셋에서 검증셋 분리테스트셋으로 조기 종료하면 데이터 누수

최종 핵심 정리

  1. CNN은 이미지의 공간 구조를 유지한 채 합성곱으로 특징을 뽑으며, Convolution → ReLU → Pooling을 반복함
  2. 출력 크기는 (H + 2P − FH) / S + 1이고, 패딩은 출력 크기 조정, 스트라이드가 커지면 출력이 작아짐
  3. 입력과 필터의 채널 수가 같아야 하며, 필터 개수가 곧 출력 채널 수와 편향 개수
  4. 풀링은 학습 매개변수가 없고, 채널 수를 유지하며, 작은 변화에 강함
  5. im2col은 4차원 입력을 2차원으로 펼쳐 합성곱을 행렬곱 한 번으로 계산하게 해 주며, 대신 메모리를 더 씀
  6. 가중치 공유 덕분에 합성곱 계층은 매개변수가 매우 적고, numpy CNN은 MNIST 5,000장만으로 test 95.4%를 달성함
  7. 작은 필터를 깊게 쌓으면 매개변수는 줄고 표현력은 커지며, ResNet은 스킵 연결(F(x) + x)로 깊은 망의 학습 문제를 해결함
  8. 전이 학습은 동결보다 작은 학습률의 미세 조정 + 데이터 증강이 효과적이었고(79.18%), 검증셋은 반드시 학습 데이터에서 떼어 내야 함

End.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.