Transformer 블록
5장의 어텐션은 토큰끼리 정보를 주고받는 장치일 뿐이다. 이것만으로는 언어 모델이 되지 않는다. 순서를 알려 줄 위치 정보, 수십~백여 층을 쌓아도 신호와 기울기가 살아남게 하는 잔차 연결과 정규화, 토큰 하나하나를 깊게 가공하는 FFN이 함께 있어야 한다. 이 장에서는 오늘날 GPT·Llama 계열이 쓰는 decoder-only Transformer 블록을 부품 단위로 뜯어 보고, 마지막에는 Llama 3 8B의 파라미터 80억 개를 직접 한 자리까지 세어 본다.
- 원래의 인코더-디코더 Transformer와 오늘날의 decoder-only 구조를 비교하고, 블록 하나의 데이터 흐름(Norm → Attention → +잔차 → Norm → FFN → +잔차)을 그릴 수 있다.
- 어텐션이 순서에 무관(permutation equivariant)하다는 사실을 계산으로 확인하고, 사인파 위치 인코딩과 RoPE가 이를 어떻게 깨는지 설명할 수 있다.
- RoPE에서 \(q_m^\top k_n\)이 상대 거리 \(m-n\)에만 의존하는 이유를 회전 행렬로 증명할 수 있다.
- 잔차 스트림의 "읽고 더하기" 관점, Pre-norm과 Post-norm의 차이, LayerNorm과 RMSNorm의 수식을 안다.
- FFN·SwiGLU·MoE의 구조와 파라미터 비중을 이해하고, 임의의 설정에서 총 파라미터·토큰당 FLOPs·가중치 메모리를 계산할 수 있다.
전체 그림: 인코더-디코더에서 decoder-only로
2017년 구글의 논문 "Attention Is All You Need"가 제안한 Transformer는 원래 기계 번역 모델이었다. 원문(예: 영어)을 읽는 인코더(encoder)와 번역문(예: 독일어)을 한 토큰씩 써 나가는 디코더(decoder) 두 덩어리로 되어 있었다. 인코더의 어텐션은 양방향이라 모든 원문 토큰이 서로를 본다. 디코더는 자기가 지금까지 쓴 토큰만 보는 인과적(masked) 셀프 어텐션과, 인코더 출력을 들여다보는 크로스 어텐션(cross-attention)을 함께 가진다. 원 논문의 기본 모델은 dmodel = 512, 인코더·디코더 각 6층, 헤드 8개, FFN 2048차원이었다.
그 뒤 길이 갈렸다. BERT(2018)는 인코더만 떼어 내 문장 이해에, T5(2019)는 인코더-디코더를 그대로 텍스트-투-텍스트 과제에 썼다. 그리고 GPT(2018) 계열은 디코더만 남겼다. 크로스 어텐션이 빠지면 "원문"과 "번역문"의 구분이 없어지고, 프롬프트와 생성 중인 답이 하나의 토큰 열로 이어진다. 모든 문제를 "다음 토큰 예측"(7장) 하나로 풀 수 있다는 단순함, 그리고 학습 효율 덕분에 GPT-3·4, Llama, Claude, Gemini 같은 오늘날의 대형 언어 모델은 거의 모두 decoder-only 구조다.
decoder-only 모델 하나를 위에서 아래까지 따라가면 다음과 같다. 토큰 ID가 임베딩 표(4장)를 거쳐 d차원 벡터가 되고, 그 벡터가 N개의 똑같은 모양의 블록을 차례로 통과한다. 블록마다 두 개의 하위 층(어텐션, FFN)이 있고, 각 하위 층은 입력을 정규화해서 읽고, 계산한 결과를 원래 벡터에 더한다. 마지막 블록을 지난 벡터를 한 번 더 정규화한 뒤, LM head(d × V 행렬)로 어휘 크기 V만큼의 로짓을 만들고 softmax로 다음 토큰 확률을 얻는다(8장).
| 구성 요소 | 원 논문 (2017) | GPT-2 (2019) | Llama 3 (2024) |
|---|---|---|---|
| 구조 | 인코더-디코더 | decoder-only | decoder-only |
| 위치 정보 | 사인파 PE (입력에 더함) | 학습된 위치 임베딩 (1024개) | RoPE (base 500,000) |
| 정규화 | LayerNorm, Post-norm | LayerNorm, Pre-norm | RMSNorm, Pre-norm |
| FFN | ReLU, 4d | GELU, 4d | SwiGLU, 3.5d (14,336) |
| 어텐션 | MHA | MHA | GQA (KV 헤드 8) |
| 편향(bias) | 있음 | 있음 | 없음 |
층과 토큰의 격자: 정보는 위로, 그리고 앞에서 뒤로
Transformer의 계산을 한 장의 그림으로 압축하면 격자가 된다. 가로축은 토큰 위치, 세로축은 층이다. 각 토큰 위치에는 아래에서 위로 관통하는 벡터 하나의 "기둥"이 있고(이것이 그 토큰의 잔차 스트림이다), 층마다 두 종류의 일이 일어난다.
- 어텐션: 기둥 사이로 정보가 옮겨 다닌다. 인과적 마스크 때문에 토큰 i는 자기 자신과 앞쪽 토큰(j ≤ i)에서만 읽을 수 있다. 정보는 격자에서 오른쪽(뒤쪽 토큰)으로만 흐른다.
- FFN: 각 기둥 안에서, 토큰마다 독립적으로 벡터를 가공한다. 기둥 사이 이동은 없다.
맨 위 층, 맨 마지막 토큰의 벡터가 다음 토큰 예측에 쓰인다. 그 벡터에는 층을 거듭하며 앞쪽 모든 토큰에서 끌어온 정보가 쌓여 있다. 아래 시뮬레이터는 교육용으로 만든 작은 예시다. 6개 토큰에 8차원 랜덤 임베딩과 층마다 다른 랜덤 Q·K·V 가중치를 주고 실제로 인과적 어텐션을 계산해, 그 가중치를 연결선의 밝기로 그린다.
Transformer 스택 3D: 잔차 기둥과 인과적 어텐션
이 그림에서 두 가지를 기억해 두자. 첫째, 깊이는 "정보를 몇 번 옮기고 가공할 수 있는가"이다. 토큰 A의 정보가 토큰 B로 옮겨진 뒤 그것이 다시 C로 옮겨지려면 최소 두 층이 필요하다. 둘째, 어텐션의 연결선은 층마다 n(n+1)/2개로 시퀀스 길이의 제곱에 비례한다. 긴 컨텍스트가 비싼 이유이자 8장의 KV 캐시가 필요한 이유다.
위치 정보가 필요한 이유: 어텐션은 순서를 모른다
5장의 셀프 어텐션 수식을 다시 보자. 입력 행렬 \(X\)(행 = 토큰)에서 \(Q = XW_Q,\ K = XW_K,\ V = XW_V\)를 만들고
를 계산한다. 이 식 어디에도 "몇 번째 토큰"이라는 정보가 없다. 토큰 순서를 섞는 것은 \(X\)의 행을 치환 행렬 \(P\)로 섞는 것(\(X \to PX\))인데, 그러면 \(QK^\top \to P\,QK^\top P^\top\)이 되고, 행별 softmax는 행·열을 같이 섞어도 값의 묶음이 그대로이므로
FFN은 토큰마다 따로 작동하니 역시 순서를 모른다. 결국 위치 정보가 없으면 Transformer에게 "개가 사람을 물었다"와 "사람이 개를 물었다"는 같은 단어 주머니(bag of words)다. 그래서 입력이나 어텐션 계산에 위치 인코딩(positional encoding)을 넣어야 한다. 아래 시뮬레이터로 직접 확인해 보자.
순서 섞기: 위치 인코딩이 없으면 출력은 따라 섞일 뿐
decoder-only 모델은 인과적 마스크 때문에 순서를 섞으면 각 토큰이 보는 집합 자체가 바뀐다. 첫 토큰은 자기만 보고, 열 번째 토큰은 열 개를 평균한다. 그래서 위치 인코딩을 아예 빼도(NoPE) 모델이 암묵적으로 위치를 어느 정도 추정할 수 있다는 연구가 있다(2023년). 그래도 실전 LLM은 거의 모두 명시적인 위치 정보(현재는 대부분 RoPE)를 쓴다. 상대 거리를 정확히 다루는 쪽이 훨씬 유리하기 때문이다.
사인파 위치 인코딩
원 논문은 위치 p마다 고정된 d차원 벡터를 만들어 토큰 임베딩에 더했다. 차원 쌍 (2i, 2i+1)마다 주파수가 다른 사인·코사인을 쓴다.
이진수 카운터를 떠올리면 직관이 생긴다. 1의 자리 비트는 매번 바뀌고, 2의 자리는 두 번에 한 번, 4의 자리는 네 번에 한 번 바뀐다. 사인파 PE는 이것의 연속판이다. 빠른 차원은 가까운 위치를 구별하고, 느린 차원은 먼 위치를 구별한다. 게다가 각 쌍은 위치가 k만큼 이동할 때 각도 kωi만큼 회전하는 2D 벡터이므로, PE(p+k)는 PE(p)의 선형 변환(회전)으로 쓸 수 있다. 저자들은 이 성질 덕분에 모델이 상대 위치를 쉽게 배울 것이라 기대했다.
두 위치의 PE 내적을 계산하면 이 회전 구조가 그대로 드러난다. \(\sin a\sin b + \cos a\cos b = \cos(a-b)\)이므로
PE 히트맵과 거리별 내적
GPT-2·GPT-3는 사인파 대신 위치마다 벡터 하나를 학습하는 학습된 위치 임베딩을 썼다(GPT-2: 1024 × 768 = 약 79만 개 파라미터). 단점은 학습 때 본 최대 길이를 넘는 위치에 대한 벡터가 아예 없다는 것이다. 그리고 두 방식 모두 위치 정보를 입력에 더해서 섞어 버리므로, 내용 정보와 위치 정보가 같은 벡터 안에서 간섭한다. 이 두 문제를 동시에 해결한 것이 다음 절의 RoPE다.
RoPE: 위치만큼 회전시킨다
RoPE(Rotary Position Embedding, Su et al. 2021)는 위치 정보를 입력에 더하지 않고, 어텐션 직전에 쿼리와 키 벡터를 회전시킨다. d차원 q를 2차원 쌍 d/2개로 나누고, i번째 쌍을 위치 m에 비례한 각도 \(m\theta_i\)만큼 돌린다.
핵심은 두 회전된 벡터의 내적이다. 회전 행렬은 직교 행렬이므로 \(R(a)^\top = R(-a)\), \(R(a)R(b) = R(a+b)\)이다. 따라서
RoPE 회전: q·k는 m − n에만 의존한다
실제 구현에서는 회전 행렬을 곱하지 않는다. 각 쌍에 \(\cos m\theta_i\), \(\sin m\theta_i\)를 원소별로 곱해 더하는 \(O(d)\) 연산이면 충분하고, cos/sin 표는 미리 계산해 둔다. RoPE는 Q와 K에만 적용하고 V에는 적용하지 않는다. 위치는 "누구를 볼지"(점수)에만 영향을 주고, 가져오는 내용(값)은 그대로 둔다. 학습 길이보다 긴 문맥으로 늘릴 때는 θ를 줄여 회전을 느리게 만드는 위치 보간(PI), NTK-aware 스케일링, YaRN 같은 기법을 쓴다.
잔차 스트림: 정보 고속도로
블록의 출력을 다음 블록에 그대로 넘기는 대신, Transformer는 블록이 계산한 값을 입력에 더한다.
이렇게 보면 각 블록은 스트림을 통째로 바꾸는 것이 아니라, 스트림에서 필요한 것을 읽어(정규화 → 계산) 작은 수정 \(\Delta = F_l(\cdot)\)을 써 넣는 장치다. 마지막 층의 벡터는 임베딩 + 모든 블록 출력의 합 \(x_L = x_0 + \sum_l \Delta_l\)이다. 해석 가능성 연구에서는 이 스트림을 여러 블록이 공유하는 "통신 버스"로 본다. 앞 층의 어텐션 헤드가 써 놓은 정보를 몇 층 뒤의 FFN이 읽어 가는 식이다.
학습 측면의 이점은 3장의 기울기 소실과 직결된다. 연쇄법칙으로 \(\partial x_L/\partial x_0\)를 계산하면, 잔차가 없을 때는 야코비안의 곱 \(\prod_l J_l\)이라 층마다 1보다 작은 배율이 곱해져 기하급수적으로 사라진다. 잔차가 있으면
잔차 스트림: 더하느냐, 덮어쓰느냐
정규화: LayerNorm과 RMSNorm
잔차 스트림에는 층마다 값이 더해지므로 크기가 점점 커지거나 특정 차원이 튈 수 있다. 하위 층에 넣기 전에 벡터의 스케일을 일정하게 맞춰 주는 것이 정규화다. Transformer는 배치가 아니라 토큰 하나의 d개 원소에 대해 통계를 낸다. 원 논문과 GPT-2는 LayerNorm을 쓴다.
Llama, Mistral, Gemma 등 최근 모델 대부분은 평균 빼기를 생략한 RMSNorm(Zhang & Sennrich, 2019)을 쓴다.
정규화: 같은 입력, 두 가지 결과
Pre-norm과 Post-norm
정규화를 어디에 두느냐도 중요하다. 원 논문은 잔차를 더한 뒤에 정규화했다(Post-norm). GPT-2 이후 대부분은 하위 층에 들어가기 전에 정규화한다(Pre-norm).
Post-norm은 잘 학습되면 성능이 약간 좋다는 보고도 있지만, 층이 깊어지면 출력 쪽 층의 기울기가 커져 학습 초기에 발산하기 쉽고 학습률 warm-up에 민감하다. Pre-norm은 위 잔차 시뮬레이터의 "직통로"가 정규화에 의해 끊기지 않으므로 수십~백여 층에서도 안정적이다. 그래서 GPT-2, GPT-3, Llama 등 거의 모든 대형 LLM이 Pre-norm을 쓴다. 최근에는 하위 층 출력에도 정규화를 한 번 더 거는 변형(Gemma 2의 "sandwich norm" 등)도 쓰인다.
FFN: 토큰마다 독립적인 2층 MLP
어텐션이 토큰 사이에 정보를 옮긴다면, FFN(Feed-Forward Network, MLP)은 각 토큰의 벡터를 그 자리에서 가공한다. 구조는 2장의 2층 MLP 그대로다. d차원을 4d로 넓혔다가 비선형 함수를 거쳐 다시 d로 줄인다.
그래서 MHA를 쓰는 표준 블록에서 FFN이 블록 파라미터의 약 2/3(8d² / 12d²)를 차지한다. GQA로 K, V가 작아진 Llama 3 8B에서는 비중이 더 커져, 층당 어텐션 4,194만 개 대 FFN 1억 7,616만 개로 약 81%다. 모델이 배운 "지식"의 상당 부분이 FFN에 들어 있다고 보는 이유다.
키-값 메모리 해석
FFN을 다르게 읽어 보자. \(W_1\)의 각 행 \(k_j\)를 "키", \(W_2\)의 각 열 \(v_j\)를 "값"이라 하면
Geva et al.(2021)은 실제로 FFN 뉴런 상당수가 특정 패턴("~로 끝나는 문장", "TV 프로그램 이름" 등)에 반응하고, 그 값 벡터가 다음 토큰 분포를 특정 단어 쪽으로 미는 것을 보였다. 사실 관계를 편집하는 연구(ROME 등)가 FFN 가중치를 고치는 것도 이 해석에 기반한다.
SwiGLU: 게이트가 달린 FFN
PaLM, Llama 이후의 모델은 SwiGLU(Shazeer, 2020)를 쓴다. 행렬을 세 개 두고, 하나는 SiLU를 거친 "게이트", 하나는 그대로의 "업" 경로로 만들어 원소별로 곱한다.
MoE: 전문가 중 몇 명만 깨운다
FFN이 지식을 담는다면, FFN을 키우면 모델이 더 많이 안다. 하지만 FFN을 키우면 토큰마다 연산도 그만큼 는다. MoE(Mixture of Experts)는 FFN을 E개의 "전문가"로 복제하고, 토큰마다 라우터가 그중 k개만 골라 계산한다. 파라미터(지식 용량)는 E배로 늘지만 토큰당 연산은 k배만 는다.
MoE의 어려움은 부하 불균형이다. 라우터가 몇몇 전문가만 편애하면 그 전문가는 과로하고 나머지는 놀며, 학습이 진행될수록 편애가 강화된다. 또 병렬 하드웨어에서는 전문가마다 처리할 토큰 수(용량(capacity))를 미리 정해 두는데, 넘치는 토큰은 그 전문가에서 드롭된다(그 토큰은 잔차 연결만 통과한다). 그래서 부하 균형 보조 손실 \(\mathcal{L}_{aux} = E\sum_i f_i P_i\)(fi: 전문가 i로 간 토큰 비율, Pi: 라우터 확률 평균)을 더해 고르게 나누도록 유도한다. 균등하면 1, 쏠릴수록 커진다.
MoE 라우팅: top-k, 용량, 드롭
MoE는 같은 연산량으로 더 큰 모델을 만드는 가장 실용적인 방법이 되어, 2024~2025년의 대형 공개 모델(DeepSeek-V3: 전체 약 671B / 활성 약 37B, 전문가 256개 중 8개 + 공유 전문가 1개; Llama 4, Qwen3 MoE 등) 상당수가 MoE다. 대신 메모리는 전체 파라미터만큼 필요하다. Mixtral 8x7B는 토큰당 13B 모델만큼 계산하지만, BF16 가중치만 약 94 GB를 올려 둬야 한다.
파라미터와 FLOPs 직접 세기
이제 블록의 모든 부품을 알았으니 모델 크기를 정확히 셀 수 있다. Llama 계열(편향 없음, RMSNorm, SwiGLU, GQA)에서 head_dim \(d_h = d/H\), KV 헤드 수 \(H_{kv}\)라 하면 한 층의 파라미터는
Llama 3 8B로 검산해 보자. d = 4096, L = 32, H = 32, Hkv = 8, dh = 128, dff = 14,336, V = 128,256, 임베딩과 LM head는 따로다.
| 항목 | 식 | 개수 |
|---|---|---|
| WQ, WO | 2 × 4096 × 4096 | 33,554,432 |
| WK, WV | 2 × 4096 × (8 × 128) | 8,388,608 |
| SwiGLU | 3 × 4096 × 14336 | 176,160,768 |
| RMSNorm 2개 | 2 × 4096 | 8,192 |
| 층당 합계 | 218,112,000 | |
| × 32층 | 6,979,584,000 | |
| 임베딩 + LM head | 2 × 128256 × 4096 | 1,050,673,152 |
| 최종 RMSNorm | 4096 | 4,096 |
| 총계 | 8,030,261,248 ≈ 8.03B |
연산량은 더 간단하다. 행렬-벡터 곱에서 가중치 하나당 곱하기 1번 + 더하기 1번이므로, 토큰 하나의 순전파는 약 2N FLOPs다(임베딩 조회는 곱셈이 없으니 엄밀히는 임베딩을 뺀 N이지만 LM head는 포함한다). 여기에 어텐션 점수 계산 \(QK^\top\)과 가중합이 컨텍스트 길이 n에 비례해 층당 약 \(4\,n\,d\) FLOPs(\(QK^\top\)와 가중합이 각각 \(2nd\)) 더해진다. 학습은 역전파까지 순전파의 약 3배, 즉 토큰당 약 6N FLOPs다(7장의 스케일링 법칙). 가중치 메모리는 BF16이면 파라미터당 2바이트다.
파라미터·FLOPs 계산기
파라미터 N ≈ 12·L·d² (MHA·4d FFN 기준), 추론 FLOPs ≈ 2N/토큰, 학습 FLOPs ≈ 6N/토큰, BF16 가중치 ≈ 2N 바이트. 70B 모델은 가중치만 140 GB라 H100 80 GB 한 장에 올라가지 않는다. 그래서 텐서 병렬로 여러 장에 나누거나 9장의 양자화로 줄인다.
핵심 정리
- 오늘날 LLM은 거의 모두 decoder-only Transformer다. 임베딩 → N × [Norm → 인과적 어텐션 → +잔차 → Norm → FFN → +잔차] → 최종 Norm → LM head → softmax.
- 어텐션과 FFN은 순서에 무관(순열 등변)하다. 위치 정보는 입력에 더하는 사인파/학습 PE, 또는 Q·K를 회전시키는 RoPE로 넣는다.
- RoPE는 차원 쌍을 위치 × θi만큼 회전시켜 \(q_m^\top k_n = q^\top R((n-m)\theta)k\)가 되게 한다. 점수가 상대 거리에만 의존하고, base를 키우면 긴 컨텍스트로 확장된다.
- 잔차 스트림은 층을 관통하는 정보 고속도로다. 각 블록은 읽고 더할 뿐이며, 항등 경로 덕분에 기울기가 깊은 층까지 소실되지 않는다. Pre-norm은 이 경로를 깨끗하게 유지한다.
- LayerNorm은 평균을 빼고 표준편차로 나누며, RMSNorm은 RMS로만 나눈다(파라미터 절반, 이동 불변성 없음). 둘 다 스케일 불변이다.
- FFN(또는 SwiGLU)은 블록 파라미터의 2/3 이상을 차지하는 키-값 메모리다. MoE는 FFN을 E개로 복제해 top-k만 계산하므로 전체 파라미터와 활성 파라미터가 갈라진다.
- Llama 계열 층당 파라미터는 2d² + 2·d·Hkv·dh + 3·d·dff + 2d. 추론 ≈ 2N FLOPs/토큰, BF16 가중치 ≈ 2N 바이트.
확인 퀴즈
1. 위치 정보가 전혀 없는 양방향 셀프 어텐션에 "개가 사람을 물었다"와 "사람이 개를 물었다"를 넣었다. 두 입력에서 토큰 "개"의 출력 벡터는?
2. RoPE에서 쿼리를 위치 m, 키를 위치 n에서 회전시킨 뒤 내적한다. m = 10, n = 7일 때와 같은 점수를 주는 경우는?
3. 잔차 연결이 깊은 네트워크 학습을 돕는 가장 직접적인 이유는?
4. 입력 벡터의 모든 원소에 같은 상수 c를 더했다. 출력이 변하지 않는 정규화는? (γ = 1, β = 0, ε 무시)
5. d = 4096인 Llama식 블록에서 SwiGLU FFN의 dff = 14,336이다. FFN 파라미터는?
6. Mixtral 8x7B(전문가 8개, top-2)에 대한 설명으로 옳은 것은?