Week 10. CNN 심화와 전이학습
AlexNet에서 ResNet까지 거대 네트워크들의 진화, 그리고 남이 학습시킨 모델을 가져와 내 문제에 적용하는 전이학습.
이번 주에 배우는 것
- AlexNet — 딥러닝 르네상스의 시작
- VGG — 깊이의 힘
- GoogLeNet — 넓이의 힘
- ResNet — 잔차 연결로 100층을 넘다
- 전이학습 — 특성 추출과 미세조정
0. 왜 "거대 모델의 역사"를 굳이 배우는가
오늘날 우리는 models.resnet50(weights='IMAGENET1K_V2') 한 줄로 수천만 장의 이미지로 학습된 모델을 불러옵니다. 너무 편해서 그 속사정을 들여다볼 일이 없지만, 실제로 W9에서 배운 CNN의 기본 블록(합성곱·풀링·ReLU)만으로는 지금의 성능에 결코 도달할 수 없었습니다. 2012년부터 2015년까지, 단 3년 사이에 이 분야는 완전히 다른 모습이 되었고, 그 변화의 핵심 아이디어는 지금도 모든 비전 모델(심지어 트랜스포머조차)의 설계 원칙으로 살아 있습니다.
이번 주의 목표는 두 가지입니다. 첫째, AlexNet → VGG → GoogLeNet → ResNet 으로 이어지는 네 단계의 진화에서 각 모델이 "직전 모델의 어떤 한계"를 깼는지를 이해하는 것. 둘째, 그렇게 학습된 거대 모델을 내 작은 데이터셋에 "빌려다 쓰는" 전이학습의 기법을 배우는 것입니다.
1. AlexNet (2012) — 딥러닝의 부활
2012년 ImageNet 대회. Krizhevsky·Sutskever·Hinton의 AlexNet이 이전 우승자(에러율 26%)를 16%로 끌어내렸습니다. 이 한 번의 사건이 딥러닝 시대를 열었습니다. 비결은 ① GPU 학습, ② ReLU, ③ 드롭아웃, ④ 데이터 증강.
1.1 구조의 상세
AlexNet은 합성곱 5층 + 완전연결 3층, 총 8층에 약 6천만 파라미터를 가졌습니다. 지금 기준에선 작지만 당시엔 엄청난 규모였고, 그래서 GTX 580 GPU 2장에 모델을 "쪼개서" 병렬로 학습시켰습니다. 첫 층은 $11\times 11$ 큰 필터에 stride 4 — 입력 224×224를 빠르게 55×55로 줄입니다.
- ReLU ($f(x)=\max(0,x)$) — 그 전에는 다들 $\tanh$나 시그모이드를 썼는데, AlexNet은 ReLU로 학습 속도를 6배 올렸다고 논문에서 보고합니다. 이유는 W8의 기울기 소실 논의를 참고하세요.
- 드롭아웃(p=0.5) — 완전연결층에만 적용. 6천만 파라미터가 50만 장 이미지에 과적합되는 걸 막기 위한 필수 장치였습니다.
- 데이터 증강 — 랜덤 크롭, 좌우 반전, PCA 기반 색상 변화. 수백만 장을 사실상 "무한 장"으로 늘립니다.
- 로컬 응답 정규화(LRN) — 지금은 거의 안 쓰지만 당시엔 의미가 있었습니다. 나중에 배치 정규화(W8)가 이를 대체합니다.
2. VGG (2014) — 단순함의 승리
3×3 작은 필터만 16~19층 깊이로 단조롭게 쌓아 만든 모델. 단순하고 일관된 구조 덕에 지금도 백본으로 자주 쓰입니다.
2.1 왜 3×3 필터만 고집했는가
옥스포드 VGG 그룹(Simonyan·Zisserman)의 통찰은 이것입니다. 3×3 필터 두 개를 겹치면 5×5 한 개와 같은 수용장(receptive field)을 가진다. 수학적으로 확인해 봅시다.
- 5×5 필터 한 개: 파라미터 $5^2 C_{in} C_{out} = 25 C_{in} C_{out}$
- 3×3 필터 두 개: 파라미터 $2 \times 3^2 C_{in} C_{out} = 18 C_{in} C_{out}$
파라미터 수가 28% 적고, 게다가 비선형(ReLU)이 한 번 더 들어가서 표현력이 더 풍부해집니다. 3×3 세 개는 7×7 한 개와 같은 수용장을 갖고, 파라미터는 $27 vs 49$로 절반 가까이 줄어듭니다. "작은 필터를 많이 쌓자"가 VGG의 철학이고, 이 원칙은 이후 대부분의 CNN이 따르게 됩니다.
단점도 분명합니다. VGG-16은 약 138M 파라미터로 AlexNet의 2배가 넘고, 대부분이 마지막 완전연결층(특히 첫 FC층 $7\times 7 \times 512 \to 4096$이 약 1억 개)에 몰려 있습니다. 이 구조적 낭비가 GoogLeNet과 ResNet이 FC를 버리고 전역 평균 풀링(global average pooling)으로 가는 동기가 됩니다.
3. GoogLeNet / Inception (2014) — 넓이의 힘
"필터 크기를 하나로 정하지 말고 1×1, 3×3, 5×5를 동시에 적용해 합치자". Inception 모듈이라는 이 발상으로 깊이와 너비를 동시에 늘렸습니다.
3.1 1×1 합성곱의 마법
Inception의 진짜 주역은 1×1 합성곱입니다. "1×1이 무슨 의미가 있지? 픽셀 하나만 보는데?" 라고 생각하기 쉬운데, 채널 차원에서 보면 이야기가 다릅니다. 입력이 $H\times W \times 256$일 때 1×1 합성곱 64개를 적용하면 출력은 $H\times W \times 64$ — 즉 공간 정보는 그대로 두고 채널을 압축합니다. 이것은 각 픽셀 위치에서 256차원 벡터를 64차원으로 선형 투영하는 것과 정확히 같고, 따라서 채널 간의 의미 있는 조합을 학습합니다.
이 "채널 병목(bottleneck)"을 3×3, 5×5 앞에 끼우면 계산량이 극적으로 줄어듭니다. 예: $28\times 28 \times 192$ 입력에 $5\times 5 \times 32$ 합성곱 → $28\times 28\times 32$ 출력을 직접 하면 곱셈이 약 1.2억 회. 중간에 1×1로 16채널까지 줄이면 약 1200만 회로 10분의 1이 됩니다. 이 트릭 덕분에 GoogLeNet은 VGG보다 12배 적은 파라미터(약 7M)로 더 좋은 성능을 냈습니다.
또 하나, GoogLeNet은 완전연결층을 전역 평균 풀링으로 대체했습니다. 마지막 feature map이 $7\times 7 \times 1024$라면, 각 채널의 49개 값을 평균 내서 1024차원 벡터 하나를 얻고 이걸로 바로 분류합니다. 파라미터가 0개인 데다 공간적으로 robust 합니다.
4. ResNet (2015) — 층을 무한히 쌓는 법
층을 너무 깊이 쌓으면 오히려 학습이 안 됩니다(기울기 소실의 친구인 "성능 열화"). He Kaiming이 제안한 잔차 연결(residual connection)로 이 문제를 깼습니다.
$$ y = F(x) + x $$입력을 출력에 그대로 더해주면 기울기가 우회로를 통해 흐를 수 있습니다. 이 작은 변화로 152층, 그리고 1000층까지도 학습이 가능해졌습니다.
4.1 "성능 열화"라는 이상한 현상
ResNet 논문의 첫 장에는 충격적인 그래프가 나옵니다. 56층 평면 네트워크(plain net)가 20층보다 훈련 오차부터 더 높습니다. 과적합이라면 훈련 오차는 낮아야 하니, 이건 단순한 최적화 실패입니다. 이론상 56층 네트워크는 20층 네트워크를 "부분적으로" 포함할 수 있습니다(뒤 36층을 항등 함수로 만들면 됨). 그런데 SGD는 그 항등 함수조차 찾지 못했던 겁니다.
He 카이밍의 발상은 기발할 정도로 단순합니다. "항등 함수를 찾기가 그렇게 어렵다면, 기본값을 항등으로 만들어 두고 블록은 차이(residual)만 학습하게 하자." 식으로 쓰면 $y = F(x) + x$이고, $F$를 0으로만 만들면 자동으로 항등이 됩니다. 아무것도 안 하는 게 쉬워진 거죠.
4.2 기울기가 흐르는 고속도로
역전파 관점에서 잔차 연결의 효과를 봅시다. 손실 $L$에 대한 입력 $x$의 기울기는
$$ \frac{\partial L}{\partial x} = \frac{\partial L}{\partial y}\cdot\left(1 + \frac{\partial F}{\partial x}\right) $$핵심은 괄호 안의 "1"입니다. $F$의 기울기가 0에 가까워져도 1이라는 우회로가 남아 있기 때문에 기울기가 절대 0이 되지 않습니다. 이것이 W8의 기울기 소실을 수백 층 규모에서 우회하는 결정적 장치입니다. 오른쪽 데모에서 층이 깊어질수록 잔차 있음(파랑)은 완만하게만 감소하는 반면, 잔차 없음(주황)은 $0.85^L$로 기하급수적으로 0에 수렴하는 걸 볼 수 있습니다.
4.3 Bottleneck 블록과 깊은 ResNet
ResNet-50 이상에서는 계산량을 줄이기 위해 "bottleneck" 블록을 씁니다. 1×1 → 3×3 → 1×1 의 세 층으로 구성되며, 앞뒤 1×1이 채널을 줄였다가 다시 늘립니다. 이 구조는 Inception의 1×1 트릭과 ResNet의 항등 우회로를 결합한 것입니다.
🎮 인터랙티브: 잔차 연결의 효과
같은 50층 네트워크에서 잔차 연결이 있을 때(파랑)와 없을 때(주황) 입력층에 도달하는 기울기 크기를 비교합니다.
4.5 진화의 한 장 요약
| 모델 | 연도 | 층 | 파라미터 | Top-5 오차 | 핵심 아이디어 |
|---|---|---|---|---|---|
| AlexNet | 2012 | 8 | 60M | 16.4% | ReLU, 드롭아웃, GPU |
| VGG-16 | 2014 | 16 | 138M | 7.3% | 3×3만, 단조로운 깊이 |
| GoogLeNet | 2014 | 22 | 7M | 6.7% | Inception, 1×1, GAP |
| ResNet-152 | 2015 | 152 | 60M | 3.6% | 잔차 연결 |
ResNet-152가 VGG-16과 파라미터 수는 비슷한데 오차는 절반 이하라는 점에 주목하세요. 모델의 힘은 "얼마나 큰가"가 아니라 "얼마나 잘 설계됐는가"에 더 크게 달려 있습니다.
5. 전이학습 — 거인의 어깨에 올라타기
ImageNet에서 1400만 장으로 학습된 ResNet은 이미 "이미지의 일반적인 특성"을 알고 있습니다. 우리가 가진 데이터가 1000장뿐이어도, 이 사전학습 모델을 가져와서 마지막 층만 갈아끼우면 좋은 결과를 얻을 수 있습니다.
5.1 왜 특성이 "옮겨 가는가"
Yosinski 등(2014)의 유명한 실험은 CNN의 각 층이 학습한 특성을 시각화해 보여 줍니다. 놀라운 사실은 거의 모든 이미지 데이터셋에서 앞쪽 층은 비슷한 것을 학습한다는 것입니다. 1층은 에지와 색깔 얼룩, 2층은 텍스처, 3층은 간단한 모양… 이런 저수준 특성은 고양이 사진이든 의료 영상이든 위성 사진이든 공통으로 필요합니다. 도메인 특유의 차이는 주로 뒤쪽 층에서 나타납니다. 그래서 "앞은 얼리고 뒤만 학습" 하는 전이학습이 작동하는 겁니다.
5.2 두 가지 모드
- 특성 추출(feature extraction) — 모든 층을 동결하고 마지막 분류기만 새로 학습.
- 미세조정(fine-tuning) — 마지막 몇 층을 풀어 작은 학습률로 재학습.
🎮 인터랙티브: 동결할 층 수 선택
네트워크의 어디서부터 풀어줄지(unfreeze) 정해보세요. 너무 많이 풀면 작은 데이터로 학습이 망가지고, 너무 적게 풀면 새 도메인에 적응하지 못합니다.
5.3 실전에서 자주 빠지는 함정
- 입력 정규화 맞추기. ImageNet 사전학습 모델은 채널별 평균 $[0.485, 0.456, 0.406]$, 표준편차 $[0.229, 0.224, 0.225]$로 정규화된 입력을 기대합니다. 이걸 안 맞추면 사전학습된 필터가 전혀 엉뚱한 입력을 받게 되어 성능이 바닥을 칩니다. W2 데이터 처리의 정규화 논의가 여기서 결정적입니다.
- 배치 정규화 통계. BN 층의 이동 평균/분산은 ImageNet 기준으로 저장돼 있습니다. 미세조정할 때
model.eval()모드로 두면 이 통계가 고정되고,model.train()이면 새 데이터로 갱신됩니다. 데이터가 적을 땐 고정이 안전합니다. - 학습률을 층별로 다르게. 뒤쪽 층은 큰 lr, 앞쪽 층은 작은 lr을 쓰는 "discriminative fine-tuning"(fast.ai)이 실전에서 잘 작동합니다.
6. 학습률 스케줄
전이학습에서는 보통 매우 작은 학습률($10^{-5}$~$10^{-4}$)로 시작합니다. 큰 학습률은 사전학습된 가중치를 망가뜨리기 때문입니다. 워밍업(warmup)과 코사인 감쇠를 함께 쓰는 게 표준입니다.
워밍업이란 처음 수백 스텝 동안 lr을 0에서 목표값까지 선형으로 올리는 것입니다. 초기에 큰 lr을 바로 주면 랜덤 초기화된 새 분류기 층이 이상한 방향으로 튀면서 그 뒤쪽의 안정된 층까지 흔들어 버리는데, 워밍업이 이걸 막습니다. 그 후에는 코사인 곡선을 따라 lr을 부드럽게 0으로 줄여 나가며 최종 수렴을 다듬습니다.
7. 코드 예제 (PyTorch)
import torch
import torchvision.models as models
import torch.nn as nn
# 사전학습된 ResNet50 불러오기
model = models.resnet50(weights='IMAGENET1K_V2')
# 모든 파라미터 동결
for p in model.parameters():
p.requires_grad = False
# 마지막 FC 층만 새 클래스 수에 맞게 교체 (자동으로 학습 가능)
model.fc = nn.Linear(model.fc.in_features, 10)
optimizer = torch.optim.Adam(model.fc.parameters(), lr=1e-3)
📖 더 깊이 공부하기
- AlexNet 원논문 — Krizhevsky 외(2012). 딥러닝 시대의 출생증명서.
- Deep Residual Learning — He 외(2015). ResNet 원논문.
- How transferable are features in deep neural networks? — Yosinski 외(2014). 어디까지 옮길 수 있는가.
- fast.ai Practical Deep Learning — 전이학습 실전 코스의 표준.