HOW AI WORKS

Transformer는 어떻게 동작하는가
— 블록 하나를 N번

「Attention Is All You Need」라는 제목 때문에 오해하기 쉽지만, Transformer는 어텐션만으로 된 모델이 아닙니다. 파라미터를 세어 보면 어텐션이 아니라 FFN(위치별 2층 MLP)에 더 많이 들어 있고, 학습이 되느냐 마느냐를 가르는 것은 어텐션이 아니라 잔차 연결과 LayerNorm을 어느 순서로 놓았는가입니다. 어텐션의 내부(Q·K·V, 소프트맥스, 멀티헤드)는 How Attention Works에서 이미 뜯어봤으니, 이 자료는 그 부품을 조립해 만든 블록 하나와 그것을 6번 쌓은 모델 전체를 다룹니다 — 왜 순환을 버렸는지, 입력을 왜 √dmodel로 곱하는지, 65M개의 파라미터가 어디에 있는지, 워밍업 4000스텝이 왜 필수였는지, 그리고 생성할 때 KV 캐시가 무엇을 아끼는지까지 브라우저에서 직접 계산해 봅니다.

how-ai-works 시리즈 · 원전: Vaswani et al., Attention Is All You Need (NeurIPS 2017, arXiv:1706.03762)

앞서 읽으면 좋은 자료 · How seq2seq Works (인코더·디코더와 고정 길이 병목) · How Attention Works (Q·K·V와 멀티헤드 — 이 글은 그 다음 이야기입니다) · How Mean and Variance Works (LayerNorm이 무엇을 정규화하는지)
차례
  1. 왜 순환을 버렸는가 — 경로 길이와 병렬성
  2. 블록 하나의 해부 — 한 토큰이 지나가는 길
  3. 입력을 만드는 법 — √dmodel과 위치 인코딩
  4. 어텐션이 쓰이는 세 군데 — 마스크가 전부를 가른다
  5. 잔차와 LayerNorm — Post-LN vs Pre-LN
  6. FFN과 파라미터 65M — 무게는 어디에 있나
  7. 학습 레시피 — 워밍업 4000과 레이블 스무딩
  8. 추론 — 자기회귀 디코딩과 KV 캐시
  9. 결과와 절제 실험 — 표 2·표 3
  10. 정리 — 한 장 요약
  11. 이해도 퀴즈

§1 왜 순환을 버렸는가 — 경로 길이와 병렬성

2017년까지 기계 번역의 표준은 순환 신경망 + 어텐션이었습니다. 어텐션은 이미 있었습니다 — 논문의 기여는 어텐션을 발명한 것이 아니라 순환과 합성곱을 전부 걷어내고 어텐션만 남겨도 되더라는 것입니다(논문 초록). 왜 걷어내고 싶었을까요? 순환의 정의 한 줄 때문입니다.

ht = f(xt, ht−1) ht−1을 계산하기 전에는 ht를 계산할 수 없다 — 길이 n짜리 문장은 반드시 n번 순서대로

GPU는 행렬 곱 수천 개를 동시에 돌리는 장치인데, 순환층은 그 병렬성을 쓸 수가 없습니다. 문장 안의 토큰끼리는 순차적으로 지나가야 하고(논문 3.1절의 표현으로 "sequential operations"), 배치 차원에서만 병렬화가 됩니다. 문장이 길어지면 메모리 제약 때문에 배치도 줄어듭니다.

두 번째 문제는 경로 길이(path length)입니다. 문장의 1번 토큰이 50번 토큰에 영향을 주려면 순환층에서는 신호가 49번의 곱셈을 거쳐야 합니다. 기울기는 그 길을 되돌아오며 소멸하거나 폭발합니다 (How LSTM Works에서 본 바로 그 문제). 셀프 어텐션에서는 모든 토큰이 한 번의 행렬 곱으로 서로에게 직접 연결되므로 경로 길이가 1입니다.

논문 표 1은 이 세 가지(층당 복잡도 · 순차 연산 수 · 최대 경로 길이)를 층 종류별로 비교합니다. 슬라이더로 직접 숫자를 넣어 봅시다.

데모 1 · 논문 표 1 계산기 — 셀프 어텐션은 언제 순환보다 싼가
n = 시퀀스 길이, d = 표현 차원, k = 합성곱 커널 폭, r = 제한된 셀프 어텐션의 이웃 반경. 막대는 층당 연산량(상대 비교)이고, 아래 표는 논문 표 1의 세 열입니다.
층 종류층당 복잡도연산량 (실제 값)순차 연산최대 경로 길이

직접 움직여 보면 논문 3.5절의 주장이 눈에 보입니다 — 셀프 어텐션이 순환보다 싼 조건은 n < d 하나뿐입니다. n²·d 대 n·d²를 n·d로 나누면 남는 것이 n 대 d이니까요. 그리고 논문이 말하듯 기계 번역에서 쓰는 word-piece/BPE 문장은 보통 n이 수십, d는 512라 셀프 어텐션 쪽이 유리합니다. 기본값(n=70, d=512)에서 순환층은 셀프 어텐션의 7배를 씁니다.

뒤집히는 지점을 봐 두세요. n을 d보다 크게 올리면 순환이 오히려 싸집니다. n=2048·d=512에서는 셀프 어텐션이 4배 비쌉니다. "긴 문맥(long context)"이 지금까지도 연구 주제인 이유가 이 n² 한 항입니다 — 논문도 이미 대비책으로 제한된 셀프 어텐션(반경 r 이웃만 보기, 복잡도 r·n·d)을 3.5절에 적어 뒀습니다. 대신 경로 길이가 1에서 n/r로 늘어납니다. 공짜는 없습니다.

세 번째 열 순차 연산도 같이 보세요. 셀프 어텐션·합성곱은 O(1), 순환만 O(n)입니다. 이것이 논문이 8개 P100 GPU로 base 모델을 12시간에 학습시킬 수 있었던 이유입니다. 같은 시기 최고 성능 모델들은 며칠에서 몇 주가 걸렸습니다.

§2 블록 하나의 해부 — 한 토큰이 지나가는 길

Transformer의 전체 구조는 사실 짧습니다. 블록 하나를 정의하고 N번 쌓은 것이 전부입니다. 인코더 블록은 두 개의 서브층, 디코더 블록은 세 개의 서브층으로 되어 있습니다.

인코더 블록 ×6                     디코더 블록 ×6
┌──────────────────────┐          ┌──────────────────────┐
│ ① 멀티헤드 셀프 어텐션 │          │ ① 마스크된 셀프 어텐션 │
│    + 잔차 → LayerNorm │          │    + 잔차 → LayerNorm │
│                      │          │ ② 교차 어텐션 (인코더→) │
│                      │          │    + 잔차 → LayerNorm │
│ ② FFN (2048 → 512)   │          │ ③ FFN (2048 → 512)   │
│    + 잔차 → LayerNorm │          │    + 잔차 → LayerNorm │
└──────────────────────┘          └──────────────────────┘

모든 서브층은 예외 없이 같은 껍질을 쓰고 있습니다. 논문 3.1절의 식 그대로입니다.

LayerNorm( x + Sublayer(x) ) Sublayer는 어텐션이거나 FFN이거나 — 껍질은 항상 같다. 잔차를 더할 수 있으려면 모든 서브층의 출력 차원이 dmodel=512로 같아야 한다

"모든 서브층의 출력이 512"라는 제약은 그냥 디자인 취향이 아닙니다. 잔차 연결 x + 가 성립하려면 더할 수 있는 모양이어야 하고, 이 때문에 임베딩 차원부터 어텐션 출력, FFN 출력까지 전부 512로 못이 박힙니다 (FFN 내부만 2048로 부풀었다가 다시 512로 돌아옵니다, §6).

아래에서 토큰 하나가 모델을 통과하는 전 과정을 단계별로 따라가 봅시다. 각 단계의 shape와 그 단계가 쓰는 파라미터 수를 같이 표시합니다.

데모 2 · shape 추적기 — 문장 하나가 모델을 통과하는 전 과정
논문 base 설정(dmodel=512, h=8, dff=2048, N=6, 공유 어휘 37,000)에서 입력 문장이 각 단계를 지날 때 텐서 모양이 어떻게 변하는지 봅니다. 행을 클릭하면 설명이 나옵니다.
단계연산shape파라미터

세 가지를 짚어 둡시다.

§3 입력을 만드는 법 — √dmodel과 위치 인코딩

순환을 걷어낸 대가가 여기서 청구됩니다. 셀프 어텐션은 집합 연산입니다 — 입력 토큰의 순서를 섞어도 출력이 같은 순서로 섞여 나올 뿐, 값은 그대로입니다. "개가 사람을 물었다"와 "사람이 개를 물었다"를 구분할 방법이 모델 안에 전혀 없습니다. 논문 3.5절이 이를 두고 "모델에 순환도 합성곱도 없으니 시퀀스의 순서를 쓰려면 위치 정보를 주입해야 한다"고 씁니다.

논문의 답은 사인·코사인 함수입니다. 학습되는 파라미터가 아니라 고정된 수식입니다.

PE(pos, 2i) = sin( pos / 100002i/dmodel )   ·   PE(pos, 2i+1) = cos( pos / 100002i/dmodel ) 짝수 차원은 사인, 홀수 차원은 코사인. 파장이 2π에서 10000·2π까지 기하급수로 늘어난다

이 512차원 벡터를 임베딩에 더합니다(이어 붙이는 게 아닙니다). 그런데 더하려면 두 벡터의 크기가 비슷해야 합니다 — 한쪽이 훨씬 크면 다른 쪽은 반올림 오차처럼 묻힙니다. 여기서 논문 3.4절의 마지막 한 문장이 등장합니다.

"In the embedding layers, we multiply those weights by √dmodel."
— 임베딩 층에서는 가중치에 √dmodel을 곱한다.

왜 하필 √dmodel일까요? 두 벡터의 길이를 실제로 재 보면 답이 나옵니다.

데모 3 · √dmodel 저울 — 위치 인코딩이 단어를 덮어쓰는가
위치 인코딩 벡터의 길이는 계산으로 정확히 정해집니다. 임베딩은 초기화 분포에서 뽑아 실제로 재 봅니다 (브라우저가 매번 새로 샘플링합니다). 스케일 곱을 껐다 켜 보세요.

숫자가 예쁘게 떨어집니다. 위치 인코딩 벡터의 길이는 d에 따라 정확히 √(d/2)입니다 — 차원이 짝을 이뤄 sin²+cos²=1이 되고, 그런 짝이 d/2개이기 때문입니다. d=512면 정확히 16입니다(위치와 무관하게 언제나). 한편 1/√d로 초기화한 임베딩의 길이는 약 1입니다. 곱하지 않으면 16 대 1 — 위치가 단어를 완전히 덮어씁니다. √512 = 22.6을 곱하면 22.6 대 16, 비로소 같은 저울에 올라갑니다.

여담 하나. 이 스케일은 가중치 공유와도 얽혀 있습니다. 논문 3.4절은 입력 임베딩·출력 임베딩· 소프트맥스 직전 선형 변환 셋이 같은 행렬을 공유한다고 말합니다(§6에서 파라미터로 확인합니다). 한 행렬이 "단어 → 벡터"와 "벡터 → 로짓" 두 역할을 겸하다 보니 적절한 크기가 서로 다르고, √dmodel은 그 사이를 맞추는 보정이기도 합니다.

왜 사인·코사인인가 — 상대 위치가 선형 변환이 된다

학습되는 위치 임베딩을 쓸 수도 있습니다(논문도 해 봤고, 표 3의 (E)행에 결과가 있습니다 — BLEU 25.7 대 25.8로 거의 같습니다). 그래도 사인파를 고른 이유를 논문은 이렇게 적습니다: "어떤 고정된 오프셋 k에 대해서도 PEpos+k는 PEpos의 선형 함수로 표현된다" — 삼각함수 덧셈정리 때문입니다. 그래서 모델이 "몇 번째인가"가 아니라 "몇 칸 떨어졌는가"를 배우기 쉽고, 학습 때 본 적 없는 긴 문장으로도 외삽이 가능합니다.

말보다 그림이 빠릅니다. 아래 왼쪽은 위치 인코딩 행렬 자체이고, 오른쪽은 두 위치의 인코딩 벡터를 내적한 값입니다.

데모 4 · 위치 인코딩 뷰어 — 내적은 거리에만 의존한다
왼쪽: PE 행렬 히트맵(가로 = 차원 i, 세로 = 위치 pos). 오른쪽: 선택한 기준 위치와 다른 모든 위치의 내적. 기준 위치를 움직여도 곡선의 모양이 따라 움직일 뿐 바뀌지 않는 것을 보세요.
PE 행렬 — 왼쪽 차원은 빠르게, 오른쪽 차원은 느리게 진동
PE(기준) · PE(pos) — 가까울수록 크다

내적 곡선은 기준 위치에서 최댓값 d/2를 찍고 멀어질수록 줄어듭니다 — 그리고 기준을 어디로 옮겨도 모양이 그대로 평행이동합니다. 즉 이 내적은 pos가 아니라 |pos − pos′|의 함수입니다. 어텐션 점수가 q·k 내적으로 계산된다는 것을 떠올리면, 위치 인코딩이 어떤 경로로 "거리에 대한 감각"을 어텐션에 주입하는지 보입니다.

밑(base)을 10000에서 10으로 줄여 보세요. 파장이 전부 짧아져서 먼 위치끼리 구분이 안 되고 내적 곡선이 요동칩니다. 반대로 키우면 느려집니다. 10000은 그 사이에서 고른 값입니다 (오늘날의 RoPE 확장 기법들이 조절하는 것도 바로 이 밑입니다).

§4 어텐션이 쓰이는 세 군데 — 마스크가 전부를 가른다

멀티헤드 어텐션은 모델 안에 세 군데 등장합니다(논문 3.2.3). 세 곳 모두 코드가 같습니다 — 같은 함수에 다른 것을 넣을 뿐입니다. 무엇이 Q가 되고 무엇이 K·V가 되는가, 그리고 마스크를 씌우는가.

위치Q는 어디서K·V는 어디서마스크t번째 행이 보는 범위
① 인코더 셀프 어텐션인코더 이전 층인코더 이전 층없음
(패딩만)
입력 문장 전체
② 디코더 마스크 셀프 어텐션디코더 이전 층디코더 이전 층인과 마스크출력 문장의 1..t
③ 교차 어텐션디코더 이전 층인코더 최종 출력없음
(패딩만)
입력 문장 전체

②의 인과 마스크만 이해하면 나머지는 따라옵니다. 학습할 때 디코더에는 정답 문장이 통째로 들어갑니다 (teacher forcing). 그대로 두면 3번째 단어를 예측하는 자리에서 4번째 단어를 이미 볼 수 있으니, 모델은 아무것도 배우지 않고 정답을 베껴 손실을 0으로 만듭니다. 이것이 라벨 누수입니다. 그래서 소프트맥스 직전에 미래 칸의 점수를 −∞로 덮습니다.

scores = QKT / √dk  →  scores[i][j] = −∞  (j > i)  →  softmax −∞의 지수는 0. 마스크된 칸의 어텐션 가중치는 정확히 0이 되고, 남은 칸의 합은 여전히 1이다
데모 5 · 마스크 행렬 뷰어 — 칸을 클릭해 보세요
행 = 쿼리(출력 위치), 열 = 키(참조 위치). 칠해진 칸만 정보가 흐릅니다. 패딩 마스크를 켜면 실제 구현에서 함께 씌우는 것도 볼 수 있습니다.
정보가 흐르는 칸 −∞로 막힌 칸
칸을 클릭하면 그 칸이 무엇을 뜻하는지 설명합니다.
인코더는 왜 마스크가 없나요? 인코더가 하는 일은 "번역할 원문을 이해하기"입니다. 원문은 처음부터 통째로 주어져 있으니 미래를 볼 것도 없고, 전체를 봐야 더 잘 이해합니다. How Bidirectional RNN Works에서 본 "인코더는 양방향, 디코더는 단방향"과 정확히 같은 구도입니다 — 다만 Transformer에서 그 구분은 층의 종류가 아니라 마스크 행렬 한 장이 됩니다.
여기서 갈라진 두 갈래가 오늘날의 지형입니다. 인코더만 남기고 마스크를 없앤 것이 BERT(대신 가운데를 가리는 MLM으로 학습), 디코더만 남기고 인과 마스크를 유지한 것이 GPT 계열입니다. 2017년의 원 논문은 둘 다 있는 인코더–디코더였습니다 — 번역이라는 과제 자체가 "원문 이해 + 역문 생성"이라 둘 다 필요했기 때문입니다.

§5 잔차와 LayerNorm — Post-LN vs Pre-LN

논문 3.1절의 식은 LayerNorm(x + Sublayer(x))입니다. 더한 다음에 정규화합니다. 이것을 오늘날 Post-LN이라고 부릅니다. 그런데 2017년 이후에 나온 거의 모든 구현(GPT-2 이후, 요즘의 LLM 전부)은 순서를 바꿔 x + Sublayer(LayerNorm(x)), 즉 Pre-LN을 씁니다. 괄호 위치 하나 옮긴 것이 왜 표준이 바뀔 만한 일이었을까요?

Post-LN:   xℓ+1 = LN( xℓ + F(xℓ) )    ·    Pre-LN:   xℓ+1 = xℓ + F( LN(xℓ) ) Post-LN은 잔차 경로가 매 층 LN을 통과한다. Pre-LN은 잔차 경로가 입력에서 출력까지 아무것도 거치지 않고 뚫려 있다

차이는 잔차 고속도로가 끊기는가입니다. Pre-LN에서는 x가 층을 몇 개 지나든 그대로 더해지기만 하니, 기울기가 되돌아올 때 위층에서 아래층까지 변형 없이 도착합니다 (How ResNet Works의 항등 경로와 같은 논리). Post-LN에서는 그 길목마다 LN이 서 있어 기울기가 층마다 다시 스케일됩니다.

말로 하면 추상적이니 실제로 돌려 봅시다. 아래는 d=64짜리 잔차 스트림에 서브층 (선형 → ReLU → 선형, He 초기화)을 N개 쌓고 브라우저가 직접 순전파시킨 결과입니다. 두 가지를 잽니다 — 잔차 스트림의 크기 ‖xℓ‖와, 각 층의 서브층 출력이 그 스트림을 얼마나 덮어쓰는지를 나타내는 가지/줄기 비율 ‖F(xℓ)‖ / ‖xℓ‖입니다.

데모 6 · Post-LN vs Pre-LN 신호 전파 — 한 층은 줄기를 얼마나 덮어쓰는가
초기화 직후(학습 전) 상태입니다. 오른쪽 비율이 1에 가까울수록 그 층이 잔차 스트림을 크게 바꾼다는 뜻이고, 0에 가까울수록 항등에 가깝다는 뜻입니다. 층 수 N을 늘려 보세요.
잔차 스트림 크기 ‖xℓ‖
가지/줄기 비율 ‖F(xℓ)‖ / ‖xℓ‖
Post-LN (원 논문) Pre-LN (오늘날의 표준)

N을 키울수록 두 곡선이 뚜렷하게 갈라집니다.

이 차이가 학습 초기에 무엇을 하는지는 이후 연구가 정리했습니다 — Xiong 외, 「On Layer Normalization in the Transformer Architecture」(ICML 2020)는 초기화 시점의 기울기 크기가 Post-LN에서는 출력층 근처가 깊이와 무관하게 크고, Pre-LN에서는 1/√N로 줄어든다는 것을 보이고, Pre-LN으로 바꾸면 워밍업 단계를 아예 제거할 수 있다는 것을 실험으로 확인합니다.

거꾸로 말하면, 원 논문의 Post-LN은 처음 몇천 스텝이 위태로웠습니다. 논문 5.3절의 워밍업 4000스텝(§7의 데모 8)은 미적 선택이 아니라 Post-LN을 살리기 위한 필수 장치였던 셈입니다. 그래서 이후 표준이 Pre-LN으로 옮겨갔습니다.

Pre-LN에도 대가는 있습니다. 위 왼쪽 차트에서 보듯 잔차 스트림이 계속 자라기만 합니다 — 아무도 정규화해 주지 않으니까요. 그래서 Pre-LN 모델은 마지막 블록 뒤에 LN을 한 번 더 붙입니다 (final_layer_norm). 원 논문에는 없는 층입니다.

LayerNorm이 정규화하는 축을 헷갈리지 마세요. BatchNorm은 배치 축을 따라 "같은 채널을 가진 여러 샘플"의 평균·분산을 냅니다. LayerNorm은 한 샘플 한 토큰의 512개 차원만으로 평균·분산을 냅니다 — 배치와 무관하고, 길이가 제각각인 시퀀스에서도 문제가 없고, 추론 때 이동평균 같은 것도 필요 없습니다. 이것이 시퀀스 모델이 BN 대신 LN을 쓰는 이유입니다 (How Mean and Variance Works의 축 선택기를 보면 둘이 같은 수식에 축만 다르다는 것이 보입니다).

§6 FFN과 파라미터 65M — 무게는 어디에 있나

블록의 두 번째 서브층은 이름이 거창하지만(Position-wise Feed-Forward Networks) 내용은 간단합니다 — 토큰마다 독립적으로 적용되는 2층 MLP입니다. 토큰끼리 섞이지 않습니다.

FFN(x) = max(0, xW1 + b1) W2 + b2 논문 식 (2). 512 → 2048 → 512. 가운데가 4배로 부푼다

"위치별(position-wise)"이 핵심입니다. 같은 W₁·W₂가 모든 토큰에 그대로 적용되니, 이것은 사실상 커널 크기 1인 합성곱 두 개입니다(논문 3.3절이 직접 그렇게 씁니다). 토큰 간 정보 교환은 어텐션이 전담하고, FFN은 각 토큰의 표현을 혼자서 가공합니다. 역할 분담이 깔끔합니다.

이제 논문 표 3에 적힌 65M이 어디에 들어 있는지 직접 세어 봅시다.

데모 7 · 파라미터 계산기 — base 65M · big 213M 재현
논문 표 3의 설정을 그대로 넣으면 base·big의 파라미터 수가 재현됩니다. 슬라이더를 움직여 무엇을 키울 때 무엇이 늘어나는지 보세요.
구성 요소계산식파라미터비중

기본값에서 나오는 결론 세 가지입니다.

dff 슬라이더를 움직이면 비중 막대가 통째로 기울어집니다. 오늘날의 LLM이 dff = 4×dmodel 비율을 대체로 유지하는 것도 이 균형에서 출발했습니다 (SwiGLU를 쓰는 요즘 모델은 게이트가 하나 더 붙어 8/3배로 조정하지만, 발상은 같습니다).

§7 학습 레시피 — 워밍업 4000과 레이블 스무딩

논문 5절은 하이퍼파라미터를 아주 구체적으로 적어 뒀습니다. 그중 둘은 "그냥 잘 되는 값"이 아니라 없으면 모델이 망가지거나 점수가 떨어지는 장치입니다.

① 학습률 스케줄 — 논문 식 (3)

lrate = dmodel−0.5 · min( step−0.5,   step · warmup−1.5 ) warmup_steps = 4000. 앞부분은 선형 증가, 그 뒤는 1/√step 감소

식의 모양이 이상해 보이지만 하는 일은 단순합니다 — 4000스텝까지 직선으로 올리고, 이후 천천히 내립니다. §5에서 본 Post-LN의 초기 불안정 때문에 처음부터 큰 학습률을 쓰면 발산하기 때문입니다.

데모 8 · Noam 스케줄 — 최고 학습률은 언제, 얼마인가
논문 식 (3)을 그대로 그립니다. 두 수를 바꾸면 최고점의 위치와 높이가 어떻게 움직이는지 보세요.

기본값(warmup=4000, d=512)에서 최고 학습률은 약 7.0 × 10⁻⁴, 정확히 4000스텝째에 찍습니다. 공식으로도 간단합니다 — peak = (d · warmup)−0.5. 즉 warmup을 4배 늘리면 최고 학습률은 절반이 됩니다. "워밍업을 길게 주면 더 안정적"이라는 말은 사실 최고 학습률이 낮아진다는 말이기도 합니다.

② 레이블 스무딩 — 퍼플렉시티는 나빠지고 BLEU는 좋아진다

논문 5.4절은 εls = 0.1을 씁니다. 정답 단어에 확률 1.0을 몰아주는 대신 0.9만 주고 나머지 0.1을 어휘 전체에 흩뿌립니다. 그리고 논문은 그 결과를 아주 솔직하게 적습니다.

"This hurts perplexity, as the model learns to be more unsure, but improves accuracy and BLEU score."
— 모델이 더 불확실해지도록 배우므로 퍼플렉시티는 나빠진다. 그러나 정확도와 BLEU는 좋아진다.

표 3의 (D)행이 그 증거입니다. 스무딩을 끄면(εls=0.0) 퍼플렉시티는 4.92 → 4.67로 좋아지는데 BLEU는 25.8 → 25.3으로 떨어집니다.

데모 9 · 레이블 스무딩 — 목표 분포와 손실의 하한
어휘 크기 V에서 ε만큼을 흩뿌릴 때 목표 분포가 어떻게 되는지, 그리고 완벽히 맞혀도 손실이 0이 되지 않는 이유(교차 엔트로피의 하한 = 목표 분포의 엔트로피)를 계산합니다.
논문 표 3 (D)행εls퍼플렉시티 (낮을수록 좋음)BLEU (높을수록 좋음)
스무딩 없음0.04.6725.3
논문 base0.14.9225.8
과하게0.25.4725.7

데모의 "손실 하한"을 먼저 보세요. 목표 분포가 더 이상 한 점에 몰려 있지 않으므로 모델이 목표를 완벽히 재현해도 교차 엔트로피가 0이 되지 않습니다 — 목표 분포 자체의 엔트로피만큼(ε=0.1, V=37,000에서 약 1.38 nats)이 바닥으로 남습니다. "퍼플렉시티가 나빠진다"의 기계적인 이유가 이것입니다. 그런데도 BLEU는 올라갑니다.

왜 그럴까요? 퍼플렉시티는 모델이 정답에 얼마나 확신하는가를 재고, BLEU는 실제로 생성한 문장이 얼마나 맞는가를 잽니다. 번역에는 정답이 여러 개인데 하나에만 확률을 몰아주게 훈련하면 모델이 과신하게 되고, 빔 서치가 대안 후보를 제대로 비교하지 못합니다. 지표가 좋아지는 것과 결과가 좋아지는 것은 다른 일이라는 것을 논문이 직접 보여 준 사례입니다.

③ 나머지 레시피

항목basebig하는 일
드롭아웃 Pdrop0.10.3각 서브층 출력에, 그리고 임베딩+PE 합에도 적용
레이블 스무딩 εls0.10.1위 참조
옵티마이저Adam (β₁=0.9, β₂=0.98, ε=1e−9)β₂가 흔한 0.999가 아니라 0.98
학습 스텝100,000300,000base 12시간, big 3.5일 (8× P100)
체크포인트 평균마지막 5개마지막 20개가중치를 평균내어 한 모델로 — 공짜 앙상블
배치약 25,000 토큰 (문장 수가 아님)길이로 묶어야 패딩 낭비가 적다

§8 추론 — 자기회귀 디코딩과 KV 캐시

학습은 병렬인데 생성은 그렇지 않습니다. 이것이 Transformer의 가장 흔한 오해입니다. 학습 때는 정답 문장을 통째로 넣고 인과 마스크로 한 번에 모든 위치를 계산하지만(1회 순전파), 추론 때는 다음 토큰을 알아야 그 다음을 계산할 수 있으니 토큰 수만큼 반복합니다. 순환층을 걷어낸 Transformer도 생성 단계에서는 순차적입니다.

여기서 낭비가 하나 보입니다. t번째 토큰을 만들 때 계산한 앞 토큰들의 K·V는 t+1번째에서도 똑같습니다(인과 마스크 덕에 과거는 미래를 안 보니까). 매번 다시 계산할 이유가 없습니다 — 저장해 두고 새 토큰 것만 덧붙이면 됩니다. 이것이 KV 캐시입니다.

데모 10 · KV 캐시 계산기 — 무엇을 아끼고 무엇을 내주는가
논문 base(d=512, N=6, h=8) 기준으로 n개 토큰을 생성할 때 캐시 없음 / 있음의 투영 연산량과 캐시가 차지하는 메모리를 계산합니다.

투영 연산은 Q·K·V·O 네 개의 d×d 행렬 곱만 셌습니다(어텐션 점수와 FFN은 제외 — 절감 비율을 보기 위함).

캐시를 쓰면 토큰당 투영 비용이 t에 비례에서 상수로 바뀝니다. n개를 생성하는 전체 비용은 O(n²)에서 O(n)으로 — n=1024면 약 500배입니다. 대신 메모리를 내줍니다. 토큰 하나당 층마다 K와 V를 들고 있어야 하니 2 × N × dmodel × 바이트이고, base·fp16이면 토큰당 12KB입니다.

이 12KB가 오늘날 LLM 서빙의 중심 문제입니다. d와 N을 요즘 모델 크기로 올려 보세요 — 컨텍스트가 길어질수록 KV 캐시가 모델 가중치보다 커집니다. MQA/GQA(여러 쿼리 헤드가 K·V를 공유), PagedAttention(vLLM), KV 양자화 같은 기법이 전부 이 한 줄의 메모리를 깎으려는 시도입니다. 2017년 논문에는 캐시 이야기가 없지만, 인과 마스크를 도입한 순간 이 최적화의 가능성도 함께 만들어진 셈입니다.

마지막으로 무엇을 고를 것인가가 남습니다. 논문은 빔 크기 4, 길이 페널티 α=0.6의 빔 서치를 쓰고, 최대 출력 길이를 입력 길이 + 50으로 잡되 가능하면 일찍 끝냅니다(5.4절). 빔 서치가 무엇을 하는지는 How CTC Beam Search Works와 How seq2seq Works에서 이미 다뤘습니다.

§9 결과와 절제 실험 — 표 2·표 3

논문이 설득력을 가진 이유는 구조가 예뻐서가 아니라 더 적은 계산으로 더 높은 점수를 냈기 때문입니다. 표 2(WMT 2014 번역)의 핵심 줄만 옮기면 이렇습니다.

모델EN→DE BLEUEN→FR BLEU학습 비용 (FLOPs)
GNMT + RL24.639.922.3 · 1019
ConvS2S25.1640.469.6 · 1018
Transformer (base)27.338.13.3 · 1018
Transformer (big)28.441.82.3 · 1019

base 줄을 보세요. EN→DE에서 ConvS2S보다 2점 이상 높으면서 학습 비용은 약 1/3입니다. big이 세운 28.4는 당시 앙상블까지 포함한 최고 기록을 넘었고, 학습에는 8개 P100으로 3.5일이 걸렸습니다. 게다가 논문은 6.3절에서 구성 구문 분석(constituency parsing)에도 거의 손대지 않고 적용해 WSJ만으로 F1 91.3, 준지도로 92.7을 냅니다 — "번역용 트릭이 아니라 범용 구조"라는 주장의 근거였습니다.

표 3의 절제 실험은 더 유익합니다. 무엇을 바꾸면 어떻게 망가지는지가 적혀 있으니까요. 행을 클릭해 보세요.

데모 11 · 논문 표 3 — 무엇을 건드리면 얼마나 손해인가
newstest2013(dev) 기준. 퍼플렉시티는 낮을수록, BLEU는 높을수록 좋습니다. base 행 대비 좋아진 칸은 초록, 나빠진 칸은 빨강입니다.
변경NdmodeldffhdkPPLBLEU
행을 클릭하면 그 결과가 무엇을 뜻하는지 설명합니다.

읽어 낼 것들:

§10 정리 — 한 장 요약

질문답
왜 순환을 버렸나순차 연산 O(n) → O(1), 최대 경로 길이 O(n) → O(1). 단, 층당 복잡도는 n·d² → n²·d라 n < d일 때만 이득
블록 하나의 구성인코더 = 셀프 어텐션 + FFN / 디코더 = 마스크 셀프 + 교차 + FFN. 전부 LayerNorm(x + Sublayer(x)) 껍질
왜 √dmodel을 곱하나PE 벡터 길이는 정확히 √(d/2)(d=512 → 16). 곱하지 않으면 임베딩(≈1)이 위치에 묻힌다
인코더·디코더를 가르는 것층 종류가 아니라 마스크 행렬 한 장. 코드는 같다
멀티헤드는 비싼가아니다. dk=dmodel/h라 h를 바꿔도 파라미터가 동일
파라미터는 어디에FFN > 어텐션(블록당 2:1), 임베딩이 전체의 약 30%. base 65M · big 213M
워밍업 4000은 왜Post-LN의 초기 불안정 때문. Pre-LN으로 바꾸면 없어도 된다 (오늘날의 표준)
레이블 스무딩 0.1퍼플렉시티는 나빠지고(4.67 → 4.92) BLEU는 좋아진다(25.3 → 25.8)
생성도 병렬인가아니다. 학습만 병렬. 추론은 토큰마다 순차 → KV 캐시로 O(n²)를 O(n)으로
그래서 성과는EN→DE 28.4 · EN→FR 41.8 BLEU를 8× P100 3.5일에. 구문 분석에도 그대로 적용돼 F1 92.7

이 논문에서 오늘까지 이어진 것과 바뀐 것

부품2017년 논문오늘날 흔한 선택
정규화 위치Post-LNPre-LN (+ 마지막 LN)
정규화 방식LayerNormRMSNorm
위치 정보사인·코사인 절대 위치RoPE / ALiBi (상대 위치)
FFN 활성함수ReLUSwiGLU / GeGLU
K·V 헤드쿼리와 같은 수 (MHA)GQA / MQA (캐시 절감)
구조인코더–디코더디코더 전용이 다수
바뀌지 않은 것잔차 + 정규화로 감싼 블록을 N번 쌓는다 · 토큰 간 섞기는 어텐션이, 토큰 내부 가공은 FFN이 · 마스크로 인과성을 준다 — 골격은 그대로입니다

§11 이해도 퀴즈