Krizhevsky · Sutskever · Hinton — NeurIPS 2012

AlexNet은 실제로
어떻게 동작하는가

224×224 픽셀 하나가 1000개의 확률로 바뀌기까지, 8개의 층에서 벌어지는 일을 직접 돌려보면서 따라갑니다. 이 페이지의 필터·이미지·수치는 원논문과 2012년 원본 소스코드에서 그대로 가져왔습니다.

모든 데모는 브라우저에서 실제로 계산됩니다. 애니메이션이 아니라 연산 결과입니다.

AlexNet이 스스로 배운 첫 번째 층의 커널 96장. 위 절반은 GPU 1이, 아래 절반은 GPU 2가 학습했습니다. 아무도 이렇게 만들라고 시키지 않았습니다. (원논문 Figure 3에서 복원)
한눈에

숫자 여덟 개로 보는 AlexNet

ILSVRC-2012 top-5 오류15.3%2위는 26.2%
학습 파라미터60,965,224
뉴런659,272
가중치를 가진 층8 conv 5 + fc 3
이미지 1장당 곱셈724M
학습 데이터1.2M 이미지 / 1000 클래스
하드웨어GTX 580 3GB × 2대
학습 시간5–6일 90 에폭

파라미터·뉴런·곱셈 횟수는 원본 layers-145.cfg의 설정값을 cuda-convnet의 출력 크기 공식으로 직접 계산한 값입니다. 논문의 “약 6000만 파라미터, 약 65만 뉴런”과 일치합니다.

전체 흐름

여덟 개의 층, 두 갈래의 GPU

가로 폭은 공간 해상도(224→55→27→13→6), 세로 높이는 채널 수를 나타냅니다. 해상도를 줄이고 채널을 늘리는 것 — 이것이 CNN이 “픽셀”을 “의미”로 바꾸는 방식입니다. 블록을 눌러 각 층에서 벌어지는 일을 확인하세요.

클릭 가능 · 점선 블록은 가중치가 없는 연산 가로 = 공간 크기 · 세로 = 채널
224×224 RGB← 공간 축소 · 채널 확대 →1000-way softmax
층이 깊어질수록 필터는 작아지고(11→5→3) 개수는 많아집니다(96→256→384). 해상도가 이미 줄어든 뒤라 작은 필터도 원본 이미지에서는 넓은 영역을 보게 되기 때문입니다. conv5 뉴런 하나는 입력 이미지의 약 195×195 픽셀을 봅니다 — 사실상 이미지 전체입니다.
첫 번째 수수께끼

224에서 왜 55가 나오는가

AlexNet 설명을 읽다 보면 입력이 224냐 227이냐로 늘 논쟁이 붙습니다. 보통 쓰는 공식 ⌊(224−11)/4⌋+1 = 54로는 논문의 55가 안 나오기 때문입니다. 원본 코드를 열어보면 답이 있습니다. cuda-convnet은 내림이 아니라 올림을 씁니다.

출력 크기 계산기layer.py:1004
cuda-convnet (실제 코드)
일반적으로 쓰는 공식
layer.py — 합성곱 층의 출력 크기
# 1004행 — ceil 이 224 입력에서 55 를 만들어낸다
dic['modulesX'] = 1 + int(ceil((2 * padding + imgSize - filterSize) / float(stride)))

올림을 쓰면 필터가 이미지 오른쪽·아래 경계를 살짝 넘어가는 위치까지 계산합니다(넘어간 부분은 0으로 처리). 그래서 224 입력 그대로 55×55가 나옵니다. 227이라는 숫자는 나중에 다른 프레임워크의 내림 공식에 맞추려고 역산해 만든 값이지, 원본 코드가 쓴 값이 아닙니다.

핵심 연산

합성곱: 11×11 창문이 이미지를 훑는다

conv1은 11×11×3 크기의 필터를 4픽셀씩 건너뛰며 이미지 위를 미끄러집니다. 각 위치에서 하는 일은 딱 하나 — 필터와 그 자리 픽셀의 내적. 그 값 하나가 오른쪽 55×55 지도의 점 하나가 됩니다. 아래는 논문 Figure 4의 실제 테스트 이미지에 논문 Figure 3의 실제 커널을 적용한 결과입니다.

입력 이미지
입력 224 × 224 × 3
필터 11 × 11 × 3
특징 지도 55 × 55
창 위치
—
픽셀 범위
—
내적 결과
—
진행
0 / 3,025
y[i,j] = Σc=1..3 Σu=0..10 Σv=0..10  w[c,u,v] · x[c, 4i+u, 4j+v]

출력 지도에서 분홍은 양수(필터 모양과 잘 맞음), 파랑은 음수(반대 모양), 회색은 0입니다. 같은 필터 하나가 3,025개 위치 전부에 똑같이 쓰입니다 — 이것이 CNN의 파라미터를 획기적으로 줄이고, 물체가 어디에 있든 알아보게 만드는 이유입니다.

conv1이 배운 것

96장의 필터, 그리고 GPU가 갈라진 흔적

아무도 “가로선 검출기를 만들어라”라고 알려주지 않았습니다. 경사하강법만으로 96개 필터가 스스로 방향과 주파수를 나눠 가졌습니다. 더 놀라운 건 그 다음입니다 — 위쪽 48개(GPU 1)는 색을 거의 무시하는 흑백 엣지 검출기가, 아래쪽 48개(GPU 2)는 색 덩어리 검출기가 되었습니다. 두 GPU가 서로 통신하지 않는 층에서 역할 분담이 저절로 생긴 것이고, 논문은 이 현상이 매 학습마다 반복된다고 적었습니다.

GPU 1 — 방향 · 주파수 선택적커널을 클릭하면 위 데모에 적용됩니다
GPU 2 — 색상 선택적선택: #4
서로 다른 필터 8장이 같은 이미지에서 뽑아내는 것

같은 사진 한 장이 필터에 따라 전혀 다르게 보입니다. 가로 엣지 필터는 가로 줄무늬만, 세로 엣지 필터는 세로 윤곽만, 색상 필터는 색 경계만 밝게 남깁니다 (ReLU를 적용해 음수는 0으로 잘랐습니다). AlexNet은 이런 지도를 96장 만든 뒤, 다음 층에서 그것들을 다시 조합합니다. 위에서 이미지를 바꾸면 여기도 함께 바뀝니다.

혁신 ①

ReLU — 한 줄짜리 코드가 6배를 벌었다

2012년 이전의 표준은 sigmoid와 tanh였습니다. 문제는 양끝이 평평하다는 것. 평평한 곳에서는 기울기가 0에 가까워지고, 그 0에 가까운 값이 층을 거슬러 올라가며 곱해지면 앞쪽 층은 학습 신호를 거의 받지 못합니다. 이것이 “깊은 망은 훈련이 안 된다”는 정설의 실체였습니다.

활성함수와 그 기울기
함수 f(x) 기울기 f′(x)
neuron.cuh — AlexNet의 ReLU 전부
__device__ inline float operator()(float x) const {
    return x < 0.0f ? 0.0f : x;
}

양수 구간에서 기울기가 정확히 1이라서, 층을 아무리 쌓아도 신호가 줄지 않습니다. 논문은 CIFAR-10에서 같은 4층 CNN이 학습 오류 25%에 도달하는 데 ReLU가 tanh보다 6배 적은 반복을 썼다고 보고합니다. 그리고 이렇게 덧붙입니다 — “포화형 뉴런을 썼다면 이 규모의 실험 자체가 불가능했다.”

공간을 줄이는 법

겹치는 맥스 풀링

3×3 창에서 가장 큰 값만 남기고 2픽셀씩 이동합니다. 창 크기(3)가 이동 폭(2)보다 크므로 창이 서로 겹칩니다. 당시 관행은 겹치지 않는 2×2/스트라이드 2였는데, AlexNet은 겹치게 만들어 top-5 오류를 0.3%p 더 낮췄고 과적합도 덜하다고 관찰했습니다.

풀링 시뮬레이터
입력 13 × 13
→
출력 6 × 6

AlexNet의 pool3(13×13 → 6×6)을 그대로 재현했습니다. 밝은 칸이 큰 값입니다. 풀링은 파라미터가 0이지만, 위치가 조금 흔들려도 같은 답을 내게 만드는 이동 불변성을 줍니다.

혁신 ② (지금은 안 쓰는)

LRN — 옆 채널을 누르는 경쟁

같은 위치의 이웃 채널들끼리 서로를 억누르게 하는 정규화입니다. 뇌의 측면 억제(lateral inhibition)에서 따왔고, 혼자 크게 튀는 필터만 살아남게 해서 반응을 더 선명하게 만듭니다. 효과는 top-1 1.4%p, top-5 1.2%p 개선 — 작지만 실재했습니다.

채널 5개(n=5) 창으로 정규화k=2 · α=1e−4 · β=0.75
bi = ai ÷ ( k + α · Σj=i−n/2i+n/2 (aj)² )β
정규화 전 a 정규화 후 b

α를 키우면 주변에 강한 이웃이 많은 채널일수록 더 눌립니다. 혼자 튀는 값은 상대적으로 살아남습니다. LRN은 2015년 배치 정규화가 등장하면서 사실상 사라졌습니다. AlexNet에서도 결정적 요소는 아니었지만, “정규화가 학습을 돕는다”는 아이디어의 출발점이었습니다.

혁신 ③

드롭아웃 — 공범을 만들지 못하게

파라미터 6,096만 개 중 96%가 완전연결층 세 개에 몰려 있습니다. 이미지는 120만 장뿐. 그냥 두면 외워버립니다. AlexNet은 학습할 때마다 FC6·FC7 뉴런의 절반을 무작위로 꺼버리는 방법을 썼습니다. 힌턴의 비유대로, 공범을 찾으려면 용의자들을 따로따로 심문해야 하니까요.

FC6 → FC7 (뉴런은 축약해서 표시)
layers-145.cfg — 드롭아웃 층
[hs1a]        # fc2048a 뒤
type=hs
keep=0.5
inputs=fc2048a

학습 때마다 매번 다른 “가늘어진 망”을 뽑아 쓰는 셈이라, 사실상 지수적으로 많은 모델의 앙상블이 됩니다. 대가는 시간 — 논문은 수렴에 필요한 반복 횟수가 대략 두 배가 된다고 적었습니다. 그래도 드롭아웃 없이는 “심각한 과적합”이 일어났습니다.

혁신 ④

데이터 증강 — 120만 장을 24억 장처럼

256×256 이미지에서 224×224를 무작위로 잘라내고, 절반은 좌우로 뒤집습니다. 잘라낼 수 있는 위치가 32×32 = 1,024가지, 뒤집기까지 하면 2,048배. 여기에 RGB의 주성분 방향으로 색을 흔들어 조명 변화까지 흉내 냅니다. 비용은 거의 0 — GPU가 이전 배치를 학습하는 동안 CPU가 파이썬으로 만들어냈습니다.

증강 파이프라인
원본 256 × 256
→
학습에 들어가는 224 × 224

추론할 때는 네 모서리 + 가운데 = 5개 크롭과 그 좌우 반전, 총 10장의 예측을 평균냅니다. 이 10-크롭 평균만으로도 top-5 오류가 18.3% → 17.0%로 내려갔습니다.

엔지니어링

3GB짜리 GPU 두 대에 망을 쪼개 넣기

GTX 580의 메모리는 3GB. 망이 들어가지 않았습니다. 그래서 각 층의 필터를 절반씩 두 GPU에 나눠 싣고, 통신은 꼭 필요한 층에서만 하도록 설계했습니다. conv2·conv4·conv5는 자기 GPU 안에서만 연결되고, conv3와 완전연결층에서만 양쪽이 만납니다. 제약에서 나온 설계였지만 top-1 1.7%p, top-5 1.2%p를 벌어줬습니다.

GPU 1GPU 2GPU 간 통신
layers-145.cfg — conv3만 양쪽 pool2를 모두 입력으로 받는다
[conv3a]
type=conv
inputs=pool2a,pool2b      # ← 두 GPU의 출력을 모두 받음
filters=192,192
filterSize=3,3
gpu=0

[conv4a]
type=conv
inputs=conv3a            # ← 자기 GPU 안에서만
filters=192
gpu=(상속)
학습 레시피

모멘텀 SGD, 그리고 사람이 직접 내린 학습률

옵티마이저는 화려하지 않습니다. 미니배치 128, 모멘텀 0.9, 가중치 감쇠 0.0005. 학습률은 0.01에서 시작해 검증 오류가 더 안 내려가면 사람이 손으로 10으로 나눴고, 학습이 끝날 때까지 세 번 그렇게 했습니다.

vi+1 := 0.9 · vi − 0.0005 · ε · wi − ε · ⟨∂L/∂w |wi⟩Di
wi+1 := wi + vi+1
항목값원본 코드에서
미니배치128--minibatch-size
모멘텀0.9momW=0.9
가중치 감쇠0.0005wc=0.0005
학습률 초기값0.01epsW=0.01
학습률 최종값0.00001epsW=0.00001 — 10으로 세 번 나눈 값
가중치 초기화N(0, 0.01²)initW=0.01
편향 초기화1 또는 0initB=1 — conv2·4·5와 FC층
에폭약 90--epochs=95
편향을 1로 둔 이유가 재미있습니다. ReLU는 입력이 음수면 출력이 0이고, 기울기도 0이라 한번 죽으면 영영 못 살아납니다. 시작부터 양수 입력을 보장해 이 “죽은 ReLU”를 피한 것입니다. 논문에서 한 문장으로 지나가는 디테일이지만, 초기 학습 안정성을 좌우했습니다.
돈이 어디에 쓰이는가

파라미터는 FC에, 계산은 conv에

같은 망인데 “무게”와 “일”이 정확히 반대편에 몰려 있습니다. 합성곱층은 전체 파라미터의 3.8%뿐이지만 곱셈의 92%를 합니다. 완전연결층은 파라미터의 96%를 차지하면서 계산은 8%만 합니다. 이 불균형을 읽어낸 것이 이후 VGG·GoogLeNet·ResNet의 설계 방향을 결정했습니다.

층별 파라미터와 곱셈 횟수 (전체 대비 %)
파라미터 (총 60,965,224) 곱셈 (총 724,406,816)
수치 표로 보기
층출력파라미터곱셈(MAC)

conv1은 파라미터가 3만 5천 개(전체의 0.06%)뿐인데 곱셈은 1억 500만 번을 합니다. 필터 한 장을 3,025개 위치에 재사용하기 때문입니다. 반대로 FC6은 가중치 3,775만 개를 딱 한 번씩만 씁니다.

원본 소스코드

논문만 읽어서는 알 수 없는 것들

컴퓨터역사박물관이 2024년에 공개한 2012년 원본 코드를 읽으면, 논문 설명과 미묘하게 다르거나 논문에 아예 없는 것들이 보입니다. 모두 layers/layers-145.cfg, layer.py, src/에서 직접 확인한 내용입니다.

저장소에는 이런 설정 파일이 150개 넘게 들어 있습니다. 번호가 붙은 실험 로그들 — layers-100부터 layers-184-4gpu까지. 논문 한 편 뒤에 얼마나 많은 시도가 있었는지 파일 이름만으로도 보입니다.

그 다음

벤치마크 하나가 분야를 갈아엎기까지

2010·2011년의 우승자는 SIFT 같은 손으로 설계한 특징 + SVM이었고, 성능은 정체돼 있었습니다. 2012년 AlexNet은 대회에 나온 유일한 신경망이었고 오류율을 절반으로 잘랐습니다. 이듬해 참가 팀은 24개로 늘었고, 거의 전부가 CNN이었습니다.

ILSVRC 우승 모델의 top-5 오류율낮을수록 좋음

2010–2011은 SIFT·Fisher vector 계열, 2012년부터는 전부 CNN입니다. 2015년 ResNet이 3.57%로 사람의 오류율(약 5%)을 넘어섰고, 2017년에는 참가 팀의 76%가 top-5 정확도 95%를 넘기며 벤치마크가 포화됐습니다.

수치 표로 보기
연도top-5 오류율방식
“AlexNet은 데이터만으로는 부족하다는 것을 증명했다. 더 많은 데이터가 아니라, 그 데이터에서 더 많은 의미를 뽑아내는 구조가 판을 다시 짰다.” — 『Sutskever's List』 2장