완전한 트랜스포머 — 인코더와 디코더

어텐션(Attention)이 주인공입니다. 잔차 연결(residual)과 정규화(normalization), 피드포워드(feed-forward), 교차 어텐션(cross-attention)은 모두 그 어텐션을 깊게 쌓을 수 있도록 받쳐 주는 발판(scaffolding)입니다.

유형: Build 언어: Python 선수 지식: Phase 7 · 02 (Self-Attention), Phase 7 · 03 (Multi-Head Attention), Phase 7 · 04 (Positional Encoding) 예상 시간: 약 75분

문제

어텐션 계층(Attention layer) 하나는 특징 추출기(feature extractor)일 뿐 모델 그 자체가 아닙니다. 계층마다 행렬 곱(matmul) 한 번만 수행해서는 언어를 다루기에 용량이 턱없이 부족합니다. 그래서 깊이가 필요합니다. 그러나 적절한 배선이 갖춰지지 않으면 깊이는 쉽게 무너집니다.

2017년 Vaswani 논문은 어텐션 계층 하나를 쌓아 올릴 수 있는 블록(stackable block)으로 바꾼 여섯 가지 설계 결정을 한데 묶었습니다. 그 이후 등장한 모든 트랜스포머(Transformer), 즉 인코더 전용(encoder-only) BERT, 디코더 전용(decoder-only) GPT, 인코더-디코더(encoder-decoder) T5는 모두 같은 뼈대(skeleton)를 물려받았습니다. 2026년 시점에는 블록이 RMSNorm, SwiGLU, pre-norm, RoPE로 다듬어졌지만 뼈대 자체는 그대로입니다.

이 lesson은 바로 그 뼈대를 다룹니다. 이후 이어지는 lesson은 06에서 인코더, 07에서 디코더, 08에서 인코더-디코더로 각각 전문화하여 살펴봅니다.

사전 테스트

2문제 · 이 강의를 시작하기 전에 얼마나 알고 있는지 확인해보세요

1.트랜스포머 블록(Transformer block)에서 잔차 연결(residual connection, x + sublayer(x))의 목적은 무엇인가요?

2.트랜스포머 블록 내에서 피드포워드 네트워크(FFN)의 역할은 무엇이며, 어텐션(attention)과 어떻게 다른가요?

0/2 답변 완료

개념

encoder block vs decoder block — the 2026 skeleton encoder block (bidirectional) input x (N, d) RMSNorm multi-head self-attention + residual RMSNorm SwiGLU FFN + out (N, d) decoder block (causal + cross-attn) input x (M, d) RMSNorm masked self-attention + RMSNorm cross-attention (Q:dec, KV:enc) enc_out + RMSNorm SwiGLU FFN encoder: 2 sublayers per block. decoder: 3 sublayers per block. residuals are dashed, additions are circles.

여섯 구성 요소

  1. 임베딩과 위치 신호(Embedding + positional signal). 토큰을 벡터로 바꿉니다. 위치 정보는 현대적인 방식인 RoPE 또는 고전적인 방식인 사인파(sinusoidal)로 주입합니다.
  2. 셀프 어텐션(Self-attention). 모든 위치가 다른 모든 위치를 참조합니다. 디코더에서는 마스킹을 적용합니다.
  3. 피드포워드 네트워크(Feed-Forward Network; FFN). 위치별로 동작하는 2계층 MLP입니다. W_2 · activation(W_1 · x) 형태이며, 기본 확장 비율은 4배입니다.
  4. 잔차 연결(Residual connection). x + sublayer(x)로 표현합니다. 이 연결이 없으면 약 6계층 이후로 그래디언트(gradient)가 사라집니다.
  5. 계층 정규화(Layer Normalization). LayerNorm을 사용하거나, 현대 스택에서는 RMSNorm을 사용합니다. 잔차 스트림(residual stream)을 안정화하는 역할을 합니다.
  6. 교차 어텐션(Cross-attention; 디코더 전용). 쿼리(Query)는 디코더에서 오고, 키와 값(Key/Value)은 인코더 출력에서 옵니다.

인코더 블록(BERT, T5 인코더에서 사용)

x -> LN -> MHA(self) -> + -> LN -> FFN -> + -> out
                     ^              ^
                     |              |
                     └── residual ──┘

인코더는 양방향(bidirectional)입니다. 마스킹은 없습니다. 모든 위치가 모든 위치를 볼 수 있습니다.

디코더 블록(GPT, T5 디코더에서 사용)

x -> LN -> MHA(masked self) -> + -> LN -> MHA(cross to encoder) -> + -> LN -> FFN -> + -> out

디코더는 블록마다 세 개의 하위 계층(sublayer)을 갖습니다. 그중 가운데에 있는 교차 어텐션만이 인코더에서 디코더로 정보가 흘러 들어오는 유일한 통로입니다. GPT처럼 순수한 디코더 전용 아키텍처에서는 교차 어텐션을 생략하고, 마스크드 셀프 어텐션(masked self-attention)과 FFN만으로 블록을 구성합니다.

Pre-norm과 post-norm

원 논문에서는 x + sublayer(LN(x)) 방식과 LN(x + sublayer(x)) 방식이라는 두 가지 선택지를 제시했습니다. Post-norm은 2019년 무렵부터 선호도가 떨어지기 시작했습니다. 깊게 학습하려면 세심한 워밍업(warmup) 스케줄이 필요하고 그렇지 않으면 학습이 불안정해지기 때문입니다. Pre-norm, 즉 하위 계층 전에 LN을 두는 방식은 2026년 기준으로 기본값으로 자리 잡았습니다. Llama, Qwen, GPT-3+, Mistral이 모두 이 방식을 사용하고 있습니다.

2026년 현대화된 블록

Vaswani 2017 논문은 LayerNorm과 ReLU를 사용했습니다. 그러나 현대 스택은 둘 다 다른 구성으로 바꾸었습니다. 실제 프로덕션 블록이 어떤 모습으로 구성되는지 비교하면 다음과 같습니다.

구성 요소20172026
NormalizationLayerNormRMSNorm
FFN activationReLUSwiGLU
FFN expansion2.6× (SwiGLU는 세 개의 행렬을 사용해 전체 파라미터 수가 맞춰짐)
PositionSinusoidal absoluteRoPE
AttentionFull MHAGQA 또는 MLA
Bias termsYesNo

RMSNorm은 LayerNorm의 평균 중심화(mean-centering) 과정을 제거한 방식입니다. 뺄셈 연산이 하나 줄어 계산량을 아낄 수 있고, 경험적으로도 안정성이 적어도 LayerNorm과 비슷한 수준을 보입니다. SwiGLU(Swish(W1 x) ⊙ W3 x)는 Llama, PaLM, Qwen 논문에서 ReLU/GELU 기반 FFN보다 퍼플렉서티(perplexity; ppl)를 꾸준히 약 0.5 포인트 더 낮추는 결과를 보였습니다.

파라미터 수(Parameter Count)

d_model = d이고 FFN 확장 비율이 r인 블록 하나의 파라미터 수는 대략 다음과 같습니다.

  • MHA: 4 · d² (Q, K, V, O 사영)
  • FFN (SwiGLU): 3 · d · (r · d)3rd²
  • 정규화(Norm): 무시할 수 있을 정도로 작음

d = 4096, r = 2.6, layers = 32이면 대략 Llama 3 8B 규모에 해당합니다. 이때 총 파라미터 수는 32 · (4·4096² + 3·2.6·4096²) ≈ 32 · (16 + 32) M ≈ 계층당 1.5B 파라미터 × 32 ≈ 7B가 되며, 여기에 임베딩과 출력 헤드를 더하면 공개된 모델의 파라미터 수와 잘 일치합니다.

직접 만들기

Step 1: 기본 구성 요소

Lesson 03에서 만든 작은 Matrix 클래스를 독립적으로 사용하기 위해 이 파일에 그대로 복사해 사용합니다.

  • layer_norm(x, eps=1e-5) — 평균을 빼고 표준편차로 나눕니다.
  • rms_norm(x, eps=1e-6) — RMS 값으로 나눕니다. 평균을 빼는 과정은 없습니다.
  • gelu(x)silu(x) * W3 x (SwiGLU).
  • ffn_swiglu(x, W1, W2, W3).
  • encoder_block(x, params)decoder_block(x, enc_out, params).

전체 배선(wiring)은 code/main.py에서 확인할 수 있습니다.

Step 2: 2계층 인코더와 2계층 디코더 연결

여러 계층을 쌓아 올립니다. 인코더 출력을 모든 디코더의 교차 어텐션에 전달합니다. 출력 사영(output projection) 직전에 최종 LN을 추가합니다.

def encode(tokens, params):
    x = embed(tokens, params.emb) + sinusoidal(len(tokens), params.d)
    for block in params.encoder_blocks:
        x = encoder_block(x, block)
    return x

def decode(target_tokens, encoder_out, params):
    x = embed(target_tokens, params.emb) + sinusoidal(len(target_tokens), params.d)
    for block in params.decoder_blocks:
        x = decoder_block(x, encoder_out, block)
    return x

Step 3: 토이 예제에서 순방향 실행

6토큰 길이의 source와 5토큰 길이의 target을 통과시켜 봅니다. 출력 shape가 (5, vocab)이 되는지 확인합니다. 이 lesson은 손실 함수가 아닌 아키텍처를 다루는 것이 목적이므로 학습 단계는 포함하지 않습니다.

Step 4: RMSNorm과 SwiGLU로 교체하기

LayerNorm과 ReLU 기반 FFN을 각각 RMSNorm과 SwiGLU로 바꿉니다. Shape가 그대로 맞아 들어가는지 확인합니다. 함수 하나만 교체하는 것으로 2026년 방식의 현대화가 완성되는 셈입니다.

사용해보기

PyTorch와 TF의 참조 구현은 각각 nn.TransformerEncoderLayer, nn.TransformerDecoderLayer입니다. 그러나 2026년의 프로덕션 코드는 대부분 직접 블록을 작성해서 사용합니다. 그 이유는 다음과 같습니다.

  • Flash Attention은 nn.MultiheadAttention을 통하지 않고 어텐션 내부에서 직접 호출됩니다.
  • GQA와 MLA는 표준 라이브러리의 참조 구현에 포함되어 있지 않습니다.
  • RoPE, RMSNorm, SwiGLU는 PyTorch의 기본 구성 요소가 아닙니다.

HF의 transformers 라이브러리에는 한 번쯤 읽어 볼 만한 깔끔한 참조 블록이 있습니다. 그중 modeling_llama.py는 2026년 디코더 전용 블록의 표준 참조입니다. 약 500줄 분량이므로 한 번 정독해 볼 만한 가치가 충분합니다.

인코더, 디코더, 인코더-디코더 — 언제 어떤 것을 선택할까:

필요선택예시
분류, 임베딩, 텍스트에 대한 QA인코더 전용(Encoder-only)BERT, DeBERTa, ModernBERT
텍스트 생성, 채팅, 코드, 추론디코더 전용(Decoder-only)GPT, Llama, Claude, Qwen
구조화된 입력에서 구조화된 출력으로의 변환(번역, 요약)인코더-디코더(Encoder-decoder)T5, BART, Whisper

디코더 전용 구조는 가장 깔끔하게 스케일이 확장되며, 이해와 생성을 모두 잘 처리하기 때문에 언어 분야에서 주류로 자리 잡았습니다. 반면 인코더-디코더 구조는 번역, 음성 인식, 구조화된 과제처럼 입력에 분명한 "source sequence"가 존재할 때 여전히 가장 좋은 선택지로 남아 있습니다.

산출물 만들기

outputs/skill-transformer-block-reviewer.md를 참고합니다. 이 스킬(skill)은 새로 작성된 트랜스포머 블록 구현을 2026년 기본 구성(pre-norm, RoPE, RMSNorm, GQA, FFN expansion ratio)에 맞추어 검토하고, 빠진 부분이 있다면 지적해 줍니다.

연습문제

  1. 쉬움. d_model=512, n_heads=8, ffn_expansion=4, swiglu=True 조건에서 encoder_block의 파라미터 수를 직접 계산합니다. 이어서 블록을 구현한 뒤 sum(p.numel() for p in block.parameters())로 계산 결과를 검증합니다.
  2. 중간. Post-norm 구성을 pre-norm 구성으로 바꿔 봅니다. 두 구성을 모두 초기화한 뒤, 12계층을 쌓아 무작위 입력을 통과시키고 활성값의 노름(activation norm)을 측정합니다. Post-norm 쪽에서는 활성값이 폭발해야 하고, pre-norm 쪽에서는 일정한 범위 안에 머물러야 합니다.
  3. 어려움. x를 뒤집어 복사하는 토이 복사 과제에 대해 4계층 인코더-디코더를 구현하고 100 step 동안 학습합니다. 학습 손실을 기록합니다. 이후 RMSNorm, SwiGLU, RoPE로 바꿔 보고 손실이 떨어지는지 확인합니다.

핵심 용어

용어흔한 설명실제 의미
블록(Block)"트랜스포머 계층 하나"정규화와 어텐션, 정규화와 FFN을 순서대로 쌓고 잔차 연결로 감싼 스택이다.
잔차(Residual)"스킵 연결(skip connection)"x + f(x) 출력이다. 깊은 스택에서 그래디언트 흐름을 가능하게 한다.
Pre-norm"정규화를 뒤가 아니라 앞에서"현대 방식인 x + sublayer(LN(x))이다. 별도의 워밍업 곡예 없이도 더 깊게 학습할 수 있다.
RMSNorm"평균이 빠진 LayerNorm"RMS로 나누는 정규화이다. 연산이 하나 더 적으면서도 경험적 안정성은 동일하다.
SwiGLU"모두가 갈아탄 FFN"Swish(W1 x) ⊙ W3 x → W2 형태다. 언어 모델 퍼플렉서티에서 ReLU/GELU를 이긴다.
교차 어텐션(Cross-attention)"디코더가 인코더를 보는 방식"쿼리는 디코더에서, 키와 값은 인코더 출력에서 가져오는 MHA다.
FFN 확장(FFN expansion)"중간 MLP가 얼마나 넓은가"은닉 크기와 d_model의 비율이다. 보통 LayerNorm 기준 4, SwiGLU 기준 2.6을 쓴다.
바이어스 없음(Bias-free)"+b 항을 제거"현대 스택은 선형 계층의 바이어스를 생략한다. 퍼플렉서티가 약간 개선되고 모델 크기가 줄어든다.

더 읽을거리

실습 코드

이 강의의 실습 코드 1개

main
Code

산출물

이 강의에서 생성된 프롬프트, 스킬, 코드 산출물 1개

transformer-block-reviewer

Review a transformer block implementation against 2026 defaults and flag drift.

Skill

확인 문제

3문제 · 모두 맞추면 완료 표시가 가능합니다

1.현대 트랜스포머(Llama, Qwen)는 post-norm(하위 계층 이후 LN) 대신 pre-norm(하위 계층 이전 LN)을 사용합니다. 왜 업계가 전환했나요?

2.인코더-디코더 트랜스포머에서 교차 어텐션(cross-attention)은 인코더에서 디코더로 정보가 흐르는 유일한 통로입니다. 이것을 제거하면 어떻게 되나요?

3.2026년 트랜스포머 블록은 LayerNorm 대신 RMSNorm을, ReLU FFN 대신 SwiGLU를 사용합니다. 이 조합이 제공하는 실질적 이점은 무엇인가요?

0/3 답변 완료

추가 문제 풀기

AI가 강의 내용을 바탕으로 새로운 문제를 생성합니다