Field Log · Entry
Transformer 구조 입문: Attention·Residual·LayerNorm·FFN 조립하기 (7/10)
오늘의 결론
- Transformer block은 attention만이 아니라 attention + FFN + residual + normalization의 조합입니다.
- attention은 token 사이 정보를 섞고, FFN은 각 token 위치의 표현을 비선형 변환합니다.
- residual은 정보와 gradient의 고속도로를 만들고 LayerNorm은 activation 규모를 안정화합니다.
- encoder, decoder, encoder-decoder는 attention mask와 정보 흐름이 다르며 RAG에서 맡는 역할도 다릅니다.
“Transformer는 attention으로 이루어진 모델”이라는 설명은 절반만 맞습니다. attention만 수십 층 쌓으면 좋은 LLM이 되지 않습니다. 실제 block에는 residual connection, normalization, feed-forward network가 함께 있고, 이 부품들이 깊은 모델을 안정적으로 학습하게 합니다.
이번 글의 목표는 수식을 전부 암기하는 것이 아니라 한 token 행렬이 block 안에서 어떤 shape를 유지하며 어떤 정보가 바뀌는지 따라가는 것입니다.
그림 1. pre-norm decoder block 예시. attention이 위치 사이를 섞고 FFN이 각 위치를 변환하며 residual path가 원래 표현을 더한다.
입력은 Token embedding과 위치 정보의 합
Transformer에는 RNN처럼 순서대로 한 token씩 상태를 넘기는 구조가 없습니다. 따라서 token의 내용과 위치를 함께 표현해야 합니다.
input representation
= token embedding
+ positional information
원 논문은 sinusoidal positional encoding을 사용했습니다. 현대 LLM은 learned position embedding, RoPE 같은 여러 방식을 사용합니다. 구현은 달라도 “같은 token이 어느 위치에 있는지 구분한다”는 목적은 같습니다.
batch B, sequence length T, hidden dimension D라면 block이 받는 기본 shape는 다음과 같습니다.
X: [B, T, D]
대부분의 sublayer는 이 외형을 유지합니다. attention 안에서는 head 차원으로 나뉘고 다시 합쳐집니다.
Multi-head self-attention: 위치 사이 통신
앞 글에서 본 attention은 각 위치가 다른 위치들의 value를 가중합하게 합니다. multi-head에서는 hidden dimension을 여러 head로 나눠 병렬 계산합니다.
Q, K, V: [B, T, D]
reshape [B, H, T, D_head]
D = H × D_head
attention score의 shape는 head별로 [B, H, T, T]입니다. 계산이 끝나면 head를 다시 연결하고 output projection을 거쳐 [B, T, D]로 돌아옵니다.
attention의 역할은 sequence 위치 사이 통신입니다. “E17” 위치가 멀리 있는 “냉각수 유량 저하” 정보를 자신의 표현에 섞을 수 있습니다.
Feed-Forward Network: 각 위치의 계산
attention 뒤에는 position-wise FFN이 있습니다. 모든 token 위치에 같은 작은 MLP를 독립적으로 적용합니다.
FFN(x) = W₂ activation(W₁x + b₁) + b₂
보통 hidden dimension을 중간 dimension으로 넓혔다 다시 줄입니다.
[B, T, D]
→ [B, T, D_ff]
→ activation
→ [B, T, D]
attention이 “다른 token에서 무엇을 가져올지” 정한다면, FFN은 가져온 정보를 포함한 각 token 표현을 어떻게 가공할지 담당합니다. modern LLM에서는 GELU, SiLU/SwiGLU 등 activation과 gated FFN 변형이 쓰입니다.
Residual connection: 원래 정보에 변화량 더하기
sublayer 출력으로 입력을 완전히 교체하지 않고 입력을 다시 더합니다.
y = x + Sublayer(x)
이를 residual 또는 skip connection이라고 합니다. sublayer는 전체 표현을 처음부터 재구성하기보다 현재 표현에 필요한 변화량을 학습할 수 있습니다.
깊은 network에서는 gradient가 여러 layer를 통과해야 합니다. residual path는 비교적 직접적인 정보·gradient 경로를 제공해 수십~수백 layer 학습을 돕습니다.
shape가 같아야 더할 수 있으므로 attention과 FFN은 최종적으로 [B, T, D]를 유지합니다.
LayerNorm: 각 token의 activation 규모 안정화
Layer Normalization은 한 sample의 feature dimension을 기준으로 평균과 분산을 정규화한 뒤 학습 가능한 scale과 bias를 적용합니다.
LayerNorm(x)
= γ × (x - mean(x)) / √(var(x) + ε) + β
Transformer에서는 보통 각 token의 hidden dimension D에 대해 적용합니다. batch의 다른 sample에 의존하지 않아 길이가 다른 sequence와 autoregressive inference에 적합합니다.
LayerNorm이 정보를 모두 같은 값으로 만든다는 뜻은 아닙니다. 상대적인 feature 패턴을 유지하면서 activation 규모를 안정화하고, 학습 가능한 γ와 β로 필요한 scale을 되돌립니다.
Pre-Norm과 Post-Norm
원 Transformer 논문은 sublayer 뒤에 normalization을 적용하는 post-norm 형태로 제시됐습니다.
post-norm:
y = LayerNorm(x + Sublayer(x))
많은 현대 LLM은 sublayer 전에 normalization하는 pre-norm 계열을 사용합니다.
pre-norm:
y = x + Sublayer(LayerNorm(x))
pre-norm은 깊은 network에서 gradient 흐름을 안정화하는 장점이 있어 널리 쓰입니다. 모델 구현을 읽을 때 “Transformer니까 순서가 모두 같겠지”라고 가정하지 말고 config와 code를 확인해야 합니다.
Decoder block 하나 조립하기
pre-norm decoder-only block을 의사 코드로 쓰면 다음과 같습니다.
def transformer_block(x, causal_mask):
# 위치 사이 정보 혼합
x = x + self_attention(layer_norm_1(x), mask=causal_mask)
# 각 위치별 비선형 변환
x = x + feed_forward(layer_norm_2(x))
return x
이 block을 여러 층 쌓고 마지막 hidden state를 vocabulary 크기의 logits로 projection합니다.
token IDs
→ embeddings + positions
→ block × N
→ final norm
→ language-model head
→ logits [B, T, vocabulary]
여러 모델이 embedding table과 output projection weight를 공유하는 weight tying을 사용하기도 합니다.
Encoder, Decoder, Encoder-Decoder
“Transformer”는 하나의 고정 구조 이름이라기보다 attention block을 조립하는 architecture family입니다.
| 구조 | attention | 대표 학습/용도 | RAG에서 흔한 역할 |
|---|---|---|---|
| encoder-only | 양방향 self-attention | masked token, 이해·분류 | embedding, reranker |
| decoder-only | causal self-attention | next-token prediction | 답변 생성, agent reasoning |
| encoder-decoder | encoder 양방향 + decoder causal + cross-attention | sequence-to-sequence | 번역·요약·일부 생성 모델 |
Encoder
각 token이 왼쪽과 오른쪽 전체를 볼 수 있습니다. 문장 전체를 문맥화한 표현을 만드는 데 적합합니다.
Decoder
causal mask로 미래 token을 보지 못합니다. 왼쪽에서 오른쪽으로 생성할 수 있습니다.
Encoder-Decoder
encoder가 입력 sequence를 읽고, decoder는 자신의 이전 출력에 causal self-attention을 적용하면서 encoder output을 cross-attention으로 참고합니다.
Transformer가 병렬 학습에 유리한 이유
RNN은 이전 hidden state가 계산돼야 다음 위치를 계산할 수 있습니다. Transformer의 self-attention은 학습 시 전체 sequence의 Q·K·V 행렬을 한꺼번에 계산할 수 있습니다.
RNN training: x₁ → h₁ → h₂ → h₃ → ...
Transformer: 모든 위치의 QKᵀ를 행렬 연산으로 병렬 계산
이 병렬성은 대규모 GPU/TPU 학습과 잘 맞았습니다. 다만 attention score matrix가 T² 크기라 긴 context 비용이 커지는 trade-off가 있습니다.
생성 시에는 다음 token이 이전 출력에 의존하므로 decoder-only LLM도 token 단위 순차성에서 완전히 벗어나지 못합니다.
Parameter와 Context는 다른 축이다
모델의 크기를 이야기할 때 여러 숫자가 섞입니다.
| 숫자 | 무엇을 뜻하는가 |
|---|---|
| parameter count | 학습된 weight의 수 |
| hidden dimension | token 표현의 폭 |
| layer count | block을 쌓은 깊이 |
| head count | attention projection의 병렬 관점 수 |
| context length | 한 요청에서 받을 수 있는 token 길이 |
| vocabulary size | 출력 후보 token 수 |
parameter가 큰 모델이 항상 context가 긴 것도 아니고, head가 많다고 parameter가 단순 비례하는 것도 아닙니다. 모델 config를 축별로 읽는 습관이 필요합니다.
RAG에서 구조가 중요한 이유
RAG pipeline에는 서로 다른 Transformer 형태가 함께 들어갑니다.
query encoder ─┐
├─ similarity search → candidates
document encoder┘
query + candidate → cross-encoder reranker → relevance score
question + top evidence → decoder LLM → answer
- bi-encoder는 query와 document를 따로 encoder에 넣어 미리 벡터화할 수 있습니다.
- cross-encoder는 query와 document를 함께 읽어 token 간 attention을 충분히 사용하지만 후보마다 실행해야 합니다.
- generator는 검색된 근거를 causal decoder context로 받아 답을 만듭니다.
“모두 Transformer”여도 입력 결합 방식과 학습 목표가 다르면 속도와 역할이 달라집니다.
10분 실습: Shape만 추적하는 block
실제 값보다 shape를 먼저 확인해 보세요.
B, T, D = 2, 128, 768
H = 12
D_HEAD = D // H
print("hidden:", (B, T, D))
print("per head QKV:", (B, H, T, D_HEAD))
print("attention scores:", (B, H, T, T))
print("merged heads:", (B, T, D))
print("FFN expanded:", (B, T, 4 * D))
print("block output:", (B, T, D))
T를 128에서 256으로 바꿔 score matrix 원소 수를 계산해 보세요. sequence가 두 배일 때 T×T는 네 배가 됩니다.
자주 하는 오해
“Transformer는 attention layer 하나다.”
실제 block은 attention, FFN, residual, normalization을 포함하며 이 조합을 여러 층 반복합니다.
“FFN이 token 사이 정보를 섞는다.”
기본 FFN은 각 위치에 독립적으로 같은 함수를 적용합니다. 위치 사이 통신은 attention이 담당합니다.
“encoder가 구식이고 decoder가 최신이다.”
둘은 목적이 다릅니다. retrieval embedding과 reranking에서는 encoder 구조가 여전히 핵심입니다.
스스로 확인하기
- attention과 FFN이 각각 어느 축의 정보를 섞는가?
- residual connection을 쓰려면 input과 sublayer output shape가 왜 같아야 하는가?
- pre-norm과 post-norm의 연산 순서는 어떻게 다른가?
- RAG의 bi-encoder, cross-encoder, generator는 어떤 속도·품질 trade-off를 갖는가?
다음 글에서는 encoder와 decoder의 차이를 대표 모델로 구체화해 BERT와 GPT를 RAG 부품 관점에서 비교합니다.