HOW AI WORKS

그림으로 보는 Transformer
— 상자를 하나씩 열어 보기

Jay Alammar의 The Illustrated Transformer는 Transformer를 처음 배우는 사람들이 가장 많이 읽는 글 중 하나입니다. 이 페이지는 그 글의 흐름을 따라가면서, 그림을 보는 데서 그치지 않고 그림 속 숫자를 직접 계산해 보도록 새로 쓴 한국어 해설입니다. 블랙박스 하나에서 시작해 인코더·디코더 스택, 셀프 어텐션 한 칸, 멀티헤드, 위치 인코딩, 출력 확률과 손실까지 — 상자를 하나씩 열어 가며, 열 때마다 안에서 도는 계산을 브라우저에서 돌려 봅니다.

how-ai-works 시리즈 · 흐름을 빌려 온 글: Jay Alammar, The Illustrated Transformer (2018) · 원전 논문: Vaswani et al., Attention Is All You Need (NeurIPS 2017)

원문을 그대로 읽고 싶다면 — 원문 번역본은 이미 있습니다: nlpinkorean의 한국어 번역. 이 페이지는 번역이 아니라 같은 순서로 새로 쓴 동반 자료입니다. 문장·예시 문장·그림·데모는 모두 여기서 새로 만들었고, 숫자는 전부 페이지 안의 JavaScript가 그 자리에서 계산합니다.
함께 보면 좋은 자료 · How Attention Works (어텐션이 왜 나왔는가) · How Transformer Works (이 페이지 다음 단계 — 파라미터 수, Post-LN/Pre-LN, 워밍업, KV 캐시)
차례
  1. 멀리서 보기 — 블랙박스 하나에서 12개의 상자로
  2. 단어가 벡터가 되는 순간
  3. 인코더 한 층 안 — 섞는 층과 혼자 가공하는 층
  4. 셀프 어텐션, 무엇을 하려는 것인가
  5. 셀프 어텐션 한 칸을 손으로 — 6단계
  6. 같은 계산을 행렬 한 번에
  7. 머리가 여럿인 어텐션
  8. 순서를 알려 주는 법 — 위치 인코딩
  9. 잔차 연결과 Layer Normalization
  10. 디코더 쪽 — 한 단어씩 만들어 내기
  11. 마지막 Linear와 Softmax
  12. 학습은 무엇을 맞추는가 — 원-핫 정답과 손실
  13. 고르는 법 — 탐욕 디코딩과 빔 서치
  14. 더 나아가기
  15. 이해도 퀴즈

§1 멀리서 보기 — 블랙박스 하나에서 12개의 상자로

번역기를 생각해 봅시다. 밖에서 보면 한국어 문장이 들어가 영어 문장이 나오는 상자 하나입니다. Transformer도 처음엔 그렇게 보입니다. 한 겹 열면 상자가 두 개로 갈라집니다 — 입력 문장을 읽어서 이해한 결과를 만드는 쪽(인코더)과, 그 결과를 참고해 출력 문장을 한 단어씩 써 내려가는 쪽(디코더). 한 겹 더 열면 각각이 같은 모양의 층을 6개 쌓은 더미입니다.

중요한 점 두 가지만 먼저 짚어 둡니다. 첫째, 인코더 6개 층은 구조는 같지만 가중치는 따로 가집니다(복사본이 아닙니다). 둘째, 디코더의 모든 층이 인코더 맨 위 층의 출력을 참고합니다 — 인코더 1층이 디코더 1층에, 2층이 2층에 연결되는 게 아닙니다. 아래 그림에서 단계 버튼을 눌러 상자를 열어 보세요.

데모 1 · 상자 열기
단계를 바꾸면 같은 모델을 더 가까이서 봅니다. 3단계에서 층을 클릭하면 그 층의 역할이 나옵니다.

§2 단어가 벡터가 되는 순간

신경망은 글자를 못 읽습니다. 그래서 맨 처음 하는 일은 단어(정확히는 토큰)마다 숫자 벡터 하나를 찾아 붙이는 것입니다. 큰 표(임베딩 행렬)에서 그 단어의 행을 꺼내는 것이고, 논문에서 이 벡터의 길이는 d_model = 512입니다. 문장이 토큰 n개라면, 인코더에 들어가는 것은 n × 512 행렬 하나입니다.

여기서부터 기억해 둘 성질이 있습니다. 모든 인코더 층은 n × 512를 받아서 n × 512를 내보냅니다. 모양이 변하지 않으니 같은 층을 몇 개든 쌓을 수 있고, 각 토큰은 층을 지나는 내내 자기 자리의 벡터로 흘러갑니다. 이 페이지의 데모는 손으로 따라갈 수 있도록 512 대신 4차원으로 줄였습니다.

데모 2 · 임베딩 조회
문장을 고르면 토큰마다 임베딩 표에서 행 하나를 꺼내 n × 4 행렬 X를 만듭니다. 이 X가 §5–§7의 입력입니다.

§3 인코더 한 층 안 — 섞는 층과 혼자 가공하는 층

인코더 한 층을 열면 부품이 두 개 나옵니다. 아래쪽의 셀프 어텐션과 위쪽의 피드포워드 신경망(FFN)입니다. 둘은 하는 일이 정반대라서 같이 기억하면 좋습니다.

그래서 "섞기 → 각자 소화하기"를 한 층에서 한 번씩 하는 셈이고, 이것을 6번 반복하는 것이 인코더입니다. 아래 데모에서 토큰 순서를 섞어 보면 두 부품의 차이가 숫자로 드러납니다.

데모 3 · 자리를 섞으면 무엇이 변하나
"배가 고파서 밥을 먹었다"의 네 토큰을 넣고, 각 토큰의 FFN 출력과 셀프 어텐션 출력(각 첫 두 성분)을 원래 순서일 때와 비교합니다. 순서를 섞을 때와 토큰 하나를 뺄 때 무엇이 변하는지 보세요.

§4 셀프 어텐션, 무엇을 하려는 것인가

다음 문장을 번역한다고 해 봅시다.

The trophy didn't fit in the suitcase because it was too big.

it은 트로피일까요, 가방일까요? 사람은 "너무 커서 안 들어갔다"면 트로피, "너무 작아서"라면 가방이라고 바로 압니다. 한국어로 옮길 때 "그것"으로 뭉개지 않고 제대로 옮기려면, 모델도 it의 벡터를 만들 때 문장의 다른 단어를 들여다봐야 합니다. 셀프 어텐션은 바로 그 일을 합니다 — 각 단어의 새 벡터를, 문장 속 모든 단어의 벡터를 얼마나 관련 있는지에 따라 가중 평균해서 만듭니다.

RNN이라면 it에 도달할 때까지 앞 단어들을 은닉 상태 하나에 눌러 담아 와야 했습니다. 셀프 어텐션은 거리가 몇 칸이든 한 번에 직접 봅니다.

데모 4 · it은 어디를 보는가
마지막 형용사를 바꾸면 it의 어텐션 가중치가 옮겨 갑니다. 단어를 클릭하면 그 단어가 보는 곳이 표시됩니다.
⚠ 이 가중치는 개념을 보여 주려고 손으로 정한 값입니다. 학습된 모델에서 한 헤드가 이렇게 깔끔하게 한 곳을 가리키는 경우는 드뭅니다(§7).

§5 셀프 어텐션 한 칸을 손으로 — 6단계

이제 "관련 있는 정도"를 실제로 어떻게 재는지 봅니다. 각 토큰의 벡터 x에서 세 개의 새 벡터를 만듭니다. 세 가중치 행렬 WQ, WK, WV를 곱하는 것뿐이고, 이 행렬들이 학습되는 파라미터입니다.

이름비유쓰이는 곳
Query (q)"나는 이런 정보를 찾고 있어" — 검색어내 자리의 출력을 만들 때, 다른 자리들에 던지는 질문
Key (k)"나는 이런 정보를 갖고 있어" — 색인 딱지다른 자리의 q와 내적되어 점수가 됨
Value (v)"뽑혀 가면 실제로 넘겨줄 내용"점수로 가중 평균되는 재료

논문에서 q·k·v는 512가 아니라 64차원입니다(§7에서 이유가 나옵니다). 여기서는 4차원 입력에서 3차원 q·k·v를 만듭니다. 한 자리(쿼리 토큰)의 출력 z가 만들어지는 과정은 다음 여섯 단계입니다.

데모 5 · 셀프 어텐션 손계산기
문장은 데모 2에서 고른 것을 씁니다. 쿼리 토큰을 고르고 단계를 넘기며 숫자를 따라가 보세요.
1 / 6

3단계에서 √d_k로 나누는 이유를 조금 더 봅시다. q와 k의 성분이 평균 0·분산 1 정도의 값이면, 내적은 dk개 항의 합이므로 분산이 dk로 커집니다(표준편차 √dk). dk=64면 점수가 ±8 규모로 흩어지고, softmax가 한 칸에 거의 1을 몰아주게 됩니다. 그러면 나머지 칸의 기울기가 사실상 0이 되어 학습이 멈춥니다. √dk로 나누면 분산이 다시 1 근처로 돌아옵니다. 아래 슬라이더로 확인하세요.

데모 5-2 · 나누지 않으면 softmax가 얼어붙는다
성분이 표준정규분포인 무작위 q와 k 8개로 점수를 만들고 softmax를 취합니다. 고정 시드라 같은 dk면 같은 결과가 나옵니다.

§6 같은 계산을 행렬 한 번에

§5는 쿼리 토큰 하나씩 계산했지만, 실제로는 모든 토큰을 행렬 곱 몇 번으로 한꺼번에 처리합니다. 토큰 벡터들을 행으로 쌓은 X(n × dmodel)에 가중치를 곱하면 Q, K, V가 한 번에 나오고, 여섯 단계는 공식 한 줄로 접힙니다.

Z = softmax( Q·KT / √dk ) · VQ = X·WQ · K = X·WK · V = X·WV · softmax는 행마다

Q·KT는 n × n 행렬이고, (i, j) 칸은 "i번 토큰의 질문과 j번 토큰의 딱지"의 내적입니다. 모든 쌍의 점수를 한 장에 담은 것이죠. GPU가 가장 잘하는 일이 큰 행렬 곱이라서, 이 형태가 RNN보다 훨씬 빠르게 학습되는 이유이기도 합니다.

데모 6 · 행렬로 한 번에, 그리고 §5와 대조
데모 2의 문장으로 전체 행렬을 계산합니다. 강조된 행은 데모 5에서 고른 쿼리 토큰 — 그 행의 Z가 데모 5의 6단계 결과와 같은지 보세요.

§7 머리가 여럿인 어텐션

어텐션 한 번은 결국 가중 평균 한 번입니다. 그런데 한 단어가 동시에 여러 관계를 가질 수 있습니다 — it은 "무엇을 가리키는가"와 "어떤 동사의 주어인가"를 동시에 알아야 합니다. 평균 하나로는 두 관계가 뭉개집니다.

그래서 WQ·WK·WV를 h벌 두고 어텐션을 h번 따로 돌립니다(논문은 h = 8). 각 "헤드"는 서로 다른 부분 공간에서 서로 다른 관계를 봅니다. 결과 Z1…Zh는 다음 FFN이 행렬 하나를 기대하므로 옆으로 이어 붙인 뒤(concat) 행렬 WO를 곱해 다시 dmodel 폭으로 돌려놓습니다.

헤드를 늘린다고 계산이 h배가 되지는 않습니다. 각 헤드의 폭을 d_k = d_model / h로 줄이기 때문입니다(512 / 8 = 64 — §5에서 64였던 이유). 전체 파라미터 수는 h와 상관없이 같습니다.

데모 7 · 헤드 두 개가 서로 다른 곳을 본다
같은 입력 X에 가중치만 다른 헤드 두 개를 돌립니다. 헤드 1은 데모 5·6과 같은 가중치입니다. 각 칸은 행(쿼리)이 열(키)에 준 가중치.
데모 7-2 · 헤드 수와 모양
dmodel = 512에서 h를 바꿔 보세요. 헤드 폭은 줄고, 파라미터는 그대로입니다.

§8 순서를 알려 주는 법 — 위치 인코딩

데모 3에서 확인했듯, 지금까지의 계산에는 순서 개념이 없습니다. 셀프 어텐션은 문장을 "순서 있는 줄"이 아니라 "토큰 주머니"로 봅니다. 토큰을 섞으면 출력도 똑같이 섞일 뿐 각 토큰의 값은 그대로라, "개가 사람을 물었다"와 "사람이 개를 물었다"를 구분할 방법이 없습니다. 그래서 임베딩에 자리마다 다른 벡터를 더해 넣습니다. 이것이 위치 인코딩(PE)입니다.

PE(pos, 2i) = sin( pos / 100002i/d ) · PE(pos, 2i+1) = cos( pos / 100002i/d )차원 쌍마다 주기가 다른 사인·코사인 — 앞쪽 차원은 빠르게, 뒤쪽 차원은 아주 느리게 돈다

이렇게 만들면 각 위치가 고유한 무늬를 갖고(시계의 초침·분침·시침을 함께 읽으면 시각이 하나로 정해지듯), 두 위치의 내적이 거리에만 의존하게 됩니다. 또 공식이라서 학습 때 본 적 없는 긴 문장에도 값을 만들 수 있습니다.

그림 한 가지는 주의해서 보세요. 논문 공식은 sin과 cos를 한 칸씩 번갈아(짝수 차원 sin, 홀수 차원 cos) 놓지만, 여러 구현(예: 초기 tensor2tensor)과 해설 그림은 앞 절반 sin, 뒤 절반 cos으로 이어 붙입니다. 차원의 순서만 다를 뿐 담긴 정보는 같습니다 — 아래 토글로 비교해 보세요.

데모 8 · 위치 인코딩 무늬
가로 = 차원(d = 128), 세로 = 위치. 색은 −1(파랑) ~ +1(주황). 마우스를 올리면 값이 나옵니다.

아래 곡선: 기준 위치의 PE와 다른 모든 위치 PE의 내적. 기준 위치를 옮겨도 봉우리 모양이 그대로 평행이동합니다 — 내적이 절대 위치가 아니라 거리의 함수라는 뜻입니다.

§9 잔차 연결과 Layer Normalization

인코더 층 그림에서 빠뜨린 부품이 있습니다. 셀프 어텐션과 FFN 각각을 우회로(잔차 연결)가 감싸고 있고, 그 뒤에 LayerNorm이 붙습니다.

출력 = LayerNorm( x + Sublayer(x) )Sublayer는 셀프 어텐션 또는 FFN — 원 논문의 순서(Post-LN)

잔차 연결 덕분에 부품은 입력을 통째로 바꿀 필요 없이 고칠 부분만 더하면 되고, 기울기는 덧셈 경로를 따라 깊은 층까지 줄지 않고 내려갑니다(ResNet과 같은 발상). LayerNorm은 토큰 하나의 벡터 안에서 평균을 빼고 표준편차로 나눠 크기를 맞춥니다. 배치의 다른 문장이나 문장의 다른 토큰과는 섞지 않습니다.

데모 9 · Add & Norm 한 칸
x는 데모 5에서 고른 쿼리 토큰의 입력 벡터, z는 그 토큰의 멀티헤드 어텐션 출력(데모 7에서 WO까지 곱한 4차원)입니다. γ = 1, β = 0. 슬라이더는 z를 몇 배로 키울지입니다.

§10 디코더 쪽 — 한 단어씩 만들어 내기

인코더가 입력 문장을 다 읽고 나면, 맨 위 층의 출력에서 K와 V를 만들어 디코더의 모든 층에 넘깁니다. 디코더 층에는 부품이 하나 더 있습니다 — 셀프 어텐션과 FFN 사이에 끼어 있는 인코더-디코더 어텐션입니다. 여기서 Q는 디코더 쪽에서, K·V는 인코더 쪽에서 옵니다. "지금 쓰려는 단어를 위해 원문의 어디를 볼까"를 정하는 곳입니다.

디코더의 셀프 어텐션에는 제약이 하나 있습니다. 아직 쓰지 않은 미래 단어를 보면 안 되므로, 자기보다 오른쪽 자리의 점수를 softmax 전에 −∞로 덮어 가중치를 0으로 만듭니다(마스킹). 출력은 한 번에 한 단어씩 나오고, 방금 나온 단어가 다음 단계의 입력 끝에 붙습니다. <eos>(문장 끝)가 나오면 멈춥니다.

데모 10 · 한 단계씩 생성
"나는 학생 이다" → "i am a student". 단계를 넘기면 디코더 입력이 한 칸씩 늘고, 마스크(왼쪽)와 교차 어텐션(오른쪽)이 어떻게 보이는지 나옵니다.
⚠ 교차 어텐션 가중치는 설명용으로 손으로 정한 값입니다.
1단계

§11 마지막 Linear와 Softmax

디코더 맨 위에서 나오는 것은 여전히 dmodel짜리 실수 벡터입니다. 이것을 단어로 바꾸는 것이 마지막 두 부품입니다.

  1. Linear — dmodel 벡터를 어휘 크기 V만큼 긴 벡터로 늘립니다. 어휘가 1만 개면 점수 1만 개, 이것을 로짓(logits)이라 부릅니다.
  2. Softmax — 로짓을 합이 1인 확률로 바꿉니다. 가장 확률이 높은 칸의 단어를 고르면 그 단계의 출력입니다.

softmax는 로짓의 차이만 봅니다. 모든 로짓에 같은 수를 더해도 확률은 그대로이고, 차이를 키우면(= 온도를 낮추면) 1등이 확률을 독식합니다.

데모 11 · 로짓에서 단어로
장난감 어휘 6개. 로짓을 직접 움직이고, 온도 T로 나눈 뒤의 softmax를 보세요.

§12 학습은 무엇을 맞추는가 — 원-핫 정답과 손실

학습할 때는 정답 문장을 알고 있습니다. 각 자리의 정답을 어휘 크기의 벡터로 적으면, 정답 단어 칸만 1이고 나머지는 0인 원-핫 벡터가 됩니다. 모델의 출력 분포를 이 원-핫 분포에 가깝게 만드는 것이 학습의 목표이고, 두 분포가 얼마나 다른지를 재는 것이 교차 엔트로피 손실입니다.

손실 = − Σ자리 log p( 정답 단어 )원-핫과의 교차 엔트로피는 결국 "정답 칸에 준 확률의 −log"

처음에는 가중치가 무작위라 모든 단어에 비슷한 확률을 줍니다(손실 ≈ log V). 역전파로 가중치를 조금씩 고치면 정답 칸의 확률이 올라가고 손실이 줄어듭니다. 또 학습 때는 디코더에 정답 문장을 한 칸 밀어서 통째로 넣고, 마스킹 덕분에 모든 자리를 한 번에 병렬로 학습합니다 — 추론(데모 10)처럼 한 단어씩 돌 필요가 없습니다.

데모 12 · 학습이 진행되면 분포가 어떻게 바뀌나
"i am a student <eos>" 다섯 자리. 슬라이더는 학습 진행 정도를 흉내 냅니다(무작위 초기 분포 → 정답 쪽으로 이동). 각 칸 색이 진할수록 높은 확률, 테두리가 정답 칸.

§13 고르는 법 — 탐욕 디코딩과 빔 서치

추론할 때 매 단계 1등만 고르는 것을 탐욕(greedy) 디코딩이라고 합니다. 간단하지만, 지금 1등인 단어가 문장 전체로 보면 최선이 아닐 수 있습니다. 빔 서치는 매 단계 상위 k개(빔 크기)의 부분 문장을 들고 가면서, 각각을 한 단어씩 늘린 후보 중 누적 확률 상위 k개만 남깁니다.

데모 13 · 1등만 고르면 놓치는 문장
손으로 정한 작은 확률 트리입니다. 빔 크기를 바꾸면 각 단계에서 살아남는 후보(굵은 선)와 최종 문장이 달라집니다. 점수는 log 확률의 합.

§14 더 나아가기

여기까지가 Transformer의 뼈대입니다. 다음으로 읽을 것들:

§15 이해도 퀴즈