Danh sách bài viết

Bài 13: Kiến trúc Transformer block — Attention + FFN + Residual

Transformer block là đơn vị lặp lại bên trong mọi Transformer (BERT, GPT, Llama). Bài này mổ xẻ bốn thành phần Multi-Head Self-Attention, Feed-Forward Network, LayerNorm và residual connection; so sánh thứ tự Post-Norm (Vaswani 2017) với Pre-Norm (modern GPT-2+, Llama); điểm qua các activation \( \text{ReLU} \to \text{GELU} \to \text{SwiGLU} \); giới thiệu RMSNorm; tính param count ~\( 12 d^2 \) cho một block; điểm qua biến thể 2024-2026 (MoE, Parallel block, no-bias) và implement bằng PyTorch.

25/05/2026
14 phút đọc
1 lượt xem
1

Mục tiêu bài học

Sau bài học, bạn sẽ:

  • Liệt kê bốn thành phần của Transformer block: Multi-Head Attention, FFN, LayerNorm, residual.
  • Vẽ và viết được thứ tự forward cho Post-Norm (Vaswani 2017) và Pre-Norm (modern).
  • Giải thích vì sao Pre-Norm ổn định hơn khi stack sâu.
  • Viết công thức FFN, biết hidden dim thường là \( 4 d_{\text{model}} \) và vai trò của FFN.
  • Phân biệt ReLU, GELU, SwiGLU.
  • So sánh LayerNorm và RMSNorm.
  • Ước lượng param count một block ~\( 12 d^2 \).
  • Biết tên các biến thể modern: MoE, Parallel block, no-bias Llama.
  • Implement TransformerBlock Pre-Norm bằng PyTorch.

Bài này ráp các thành phần đã học (B9 attention, B10 self-attention, B11 multi-head, B12 positional encoding) thành một khối lặp lại. Bài 14 sẽ so sánh ba dạng kiến trúc dùng khối này: encoder-only, decoder-only, encoder-decoder.

2

Transformer block là gì

Transformer block là một module nhận sequence vector \( X \in \mathbb{R}^{n \times d} \) và trả về output cùng shape \( Z \in \mathbb{R}^{n \times d} \). Vì input và output cùng shape, có thể stack nhiều block liên tiếp — output block trước là input block sau.

Một LLM modern thực chất là: embedding + positional encoding → \( N \) Transformer block → một linear head. Toàn bộ "kiến thức" của model phân bố trong tham số của \( N \) block đó.

Số block \( N \) tỉ lệ với capacity:

  • GPT-2 small: 12 block, \( d = 768 \).
  • GPT-3 175B: 96 block, \( d = 12288 \).
  • Llama 3 8B: 32 block, \( d = 4096 \).
  • Llama 3 70B: 80 block, \( d = 8192 \).

Mỗi block "tinh chỉnh" representation thêm một lần. Block đầu thường học pattern bề mặt (token co-occurrence); block giữa học cú pháp, vai trò ngữ pháp; block cuối học ngữ nghĩa, suy luận. Mô tả này là quan sát phổ biến từ interpretability, không phải định luật cố định.

3

Bốn thành phần

Một Transformer block có đúng bốn thành phần:

  • Multi-Head Self-Attention (MHA) — đã học ở Bài 11. Vai trò: mix thông tin giữa các token.
  • Feed-Forward Network (FFN) — hai linear layer với activation giữa. Vai trò: nonlinear transformation per-token.
  • LayerNorm (xuất hiện 2 lần) — chuẩn hoá theo chiều feature. Giữ ổn định scale của activation. Series 3 Bài 23 đã giới thiệu.
  • Residual connection (xuất hiện 2 lần) — ý tưởng từ ResNet (Series 3 Bài 33). Cho gradient chảy thẳng qua skip path, giúp train sâu.

Khác biệt giữa các Transformer hiện đại nằm ở: (a) thứ tự LayerNorm vs sublayer, (b) chọn LayerNorm hay RMSNorm, (c) chọn activation cho FFN, (d) có biến thể FFN nào (MoE) không. Phần "khung" thì giống nhau.

4

Sơ đồ Pre-Norm

Sơ đồ dạng text cho Pre-Norm — variant dùng trong GPT-2+, Llama, Mistral:

Input X
   │
   ├──> LayerNorm > MHA > Add
   │                       │
   │<──────────────────────┘
   │
   ├──> LayerNorm > FFN > Add
   │                       │
   │<──────────────────────┘
   ↓
Output Z

Hai mũi tên dài bên trái là residual — input được cộng thẳng vào output của mỗi sublayer. Hai sublayer (MHA, FFN) được "ôm" bởi cặp (LayerNorm trước → sublayer → residual sau). Output cùng shape với input để stack được.

5

Post-Norm (Vaswani 2017)

Bản nguyên thuỷ trong Attention Is All You Need đặt LayerNorm sau residual:

\[ Y = \text{LayerNorm}\!\big( X + \text{MHA}(X) \big) \]

\[ Z = \text{LayerNorm}\!\big( Y + \text{FFN}(Y) \big) \]

Ý tưởng: chuẩn hoá ngay sau khi cộng residual để output mỗi block có cùng phân phối. Cách này hoạt động tốt với Transformer "không quá sâu" (6 layer encoder + 6 layer decoder như paper gốc) nhưng phát sinh vấn đề khi stack sâu hơn — sẽ phân tích ở Bước 7.

6

Pre-Norm (modern)

Biến thể được phổ biến từ GPT-2 (Radford et al., 2019) và Xiong et al. (2020) đặt LayerNorm trước sublayer:

\[ Y = X + \text{MHA}\!\big( \text{LayerNorm}(X) \big) \]

\[ Z = Y + \text{FFN}\!\big( \text{LayerNorm}(Y) \big) \]

Sự khác biệt nằm ở chỗ residual path giờ "sạch" — chỉ là phép cộng, không qua LayerNorm. Sublayer (MHA / FFN) nhận input đã được chuẩn hoá. Đây là default của GPT-2 trở đi và mọi Llama / Mistral / Qwen / Phi đều dùng.

Một biến thể nhỏ là Sandwich-Norm (Ding et al., 2021) thêm LayerNorm cả trước và sau sublayer, ít phổ biến hơn nhưng dùng trong một số model nội bộ ở Google.

7

Vì sao Pre-Norm thắng

Xiong et al. (2020), On Layer Normalization in the Transformer Architecture, phân tích lý thuyết và thực nghiệm:

  • Post-Norm: gradient ở layer đầu (gần input) có magnitude phụ thuộc số layer \( N \); với \( N \) lớn, gradient có thể bùng nổ hoặc triệt tiêu. Train ổn định cần learning rate warmup dài.
  • Pre-Norm: gradient chảy thẳng qua residual path mà không bị giảm bởi LayerNorm. Magnitude ổn định bất kể \( N \). Có thể bỏ warmup hoặc warmup ngắn.

Hệ quả thực tế: Pre-Norm cho phép train Transformer sâu hơn (50+ layer) mà không cần tinh chỉnh learning rate phức tạp. Đây là lý do tại sao mọi LLM lớn (GPT-3 96 layer, Llama 3 70B 80 layer) đều chọn Pre-Norm.

Đánh đổi nhỏ: Pre-Norm có thể cho kết quả hơi kém hơn Post-Norm ở model nhỏ và shallow (Liu et al., 2020) — nhưng khi scale lên, độ ổn định quan trọng hơn vài phần trăm hiệu năng.

8

Feed-Forward Network

FFN trong Vaswani 2017 là một MLP 2 lớp:

\[ \text{FFN}(x) = W_2 \cdot \text{ReLU}\!\big( W_1 x + b_1 \big) + b_2 \]

Trong đó:

  • \( W_1 \in \mathbb{R}^{d_{\text{ff}} \times d_{\text{model}}} \): expand từ \( d_{\text{model}} \) lên \( d_{\text{ff}} \).
  • \( W_2 \in \mathbb{R}^{d_{\text{model}}\times d_{\text{ff}}} \): project ngược lại về \( d_{\text{model}} \).
  • \( d_{\text{ff}} = 4 \cdot d_{\text{model}} \) là tỉ lệ default.

Ví dụ với \( d = 512 \) (paper gốc): \( 512 \to 2048 \to 512 \). Với Llama 3 8B \( d = 4096 \): \( d_{\text{ff}} \approx 14336 \) (tỉ lệ ~3.5×, đã điều chỉnh để bù SwiGLU thêm tham số).

FFN là per-token: cùng một bộ tham số \( W_1, W_2 \) áp lên từng vector token một cách độc lập. Không có chuyện token này "trộn" vào token khác trong FFN — phần mix đã làm xong ở MHA.

9

Vì sao cần FFN

Nếu chỉ có attention, Transformer vẫn chạy được — nhưng kém hơn rất nhiều. Có ba lý do FFN không thể thiếu:

  • Nonlinearity: attention chỉ là tổ hợp tuyến tính có trọng số. Stack nhiều attention thuần vẫn tương đương một phép tuyến tính lớn. FFN với ReLU / GELU đưa nonlinearity vào, cho model học hàm phi tuyến.
  • Per-token transformation: attention trả lời "trộn thông tin từ các token nào", FFN trả lời "biến thông tin đó thành cái gì". Hai vai trò bổ sung.
  • Capacity / Memory: Geva et al. (2021), Transformer Feed-Forward Layers Are Key-Value Memories, cho thấy FFN hoạt động như associative memory — \( W_1 \) gồm các "key pattern", \( W_2 \) là các "value" tương ứng. Kiến thức factual của model nằm chủ yếu ở FFN.

Hệ quả param: với \( d_{\text{ff}} = 4d \), FFN chiếm \( 2 \cdot d \cdot 4d = 8 d^2 \) tham số, MHA chiếm \( 4 d^2 \) (Bài 11). Tỉ lệ ~\( 2:1 \) — FFN nhiều tham số gấp đôi MHA. Trong các model như Llama 3 với SwiGLU, FFN có thể chiếm 65-70% tổng tham số của block.

10

Activation: ReLU, GELU, SwiGLU

Activation giữa hai linear của FFN đã đổi nhiều lần theo thời gian:

  • ReLU (Vaswani 2017): \( \text{ReLU}(x) = \max(0, x) \). Đơn giản, nhanh, "tắt" nửa đầu vào về 0. Gradient zero ở vùng âm.
  • GELU (BERT, GPT-2): \( \text{GELU}(x) = x \cdot \Phi(x) \) với \( \Phi \) là CDF chuẩn. Smooth approximation của ReLU, không có "kink" cứng tại 0. Hendrycks & Gimpel (2016).
  • SwiGLU (PaLM, Llama, Mistral): biến thể của Gated Linear Unit. Shazeer (2020) chứng minh nó cho perplexity tốt hơn GELU ở cùng compute budget.

Công thức SwiGLU:

\[ \text{SwiGLU}(x) = \big( \text{Swish}(x W_1) \odot (x W_3) \big) W_2 \]

Với \( \text{Swish}(x) = x \cdot \sigma(x) \) và \( \odot \) là phép nhân element-wise. Khác FFN cổ điển ở chỗ có ba linear thay vì hai, và một nhánh đóng vai trò "gate" (cổng) điều biến nhánh kia.

Vì SwiGLU dùng 3 ma trận trọng số thay vì 2, để giữ tổng tham số FFN tương đương ReLU/GELU, các model như Llama thường giảm \( d_{\text{ff}} \) từ \( 4d \) xuống ~\( \frac{8}{3} d \). Llama 3 8B: \( d = 4096 \), \( d_{\text{ff}} = 14336 \) (~3.5×, đã làm tròn lên bội số 256 để align tốt trên GPU).

11

LayerNorm và RMSNorm

LayerNorm (Ba et al., 2016) chuẩn hoá theo chiều feature của từng token:

\[ \text{LN}(x) = \gamma \odot \frac{x - \mu}{\sigma} + \beta, \quad \mu = \frac{1}{d}\sum_i x_i, \quad \sigma = \sqrt{\frac{1}{d}\sum_i (x_i - \mu)^2 + \epsilon} \]

Khác BatchNorm: LayerNorm tính \( \mu, \sigma \) qua chiều feature (\( d \) số), không qua chiều batch — vì vậy hoạt động giống nhau ở train và inference, không phụ thuộc batch size. Phù hợp với sequence model.

RMSNorm (Zhang & Sennrich, 2019) đơn giản hoá: bỏ \( \mu \) và \( \beta \), chỉ scale theo root mean square:

\[ \text{RMSNorm}(x) = \gamma \odot \frac{x}{\text{RMS}(x)}, \quad \text{RMS}(x) = \sqrt{\frac{1}{d}\sum_i x_i^2 + \epsilon} \]

Tại sao Llama / Mistral / Qwen chọn RMSNorm:

  • Nhanh hơn ~15-20% so với LayerNorm (ít phép tính hơn).
  • Ít tham số (\( d \) thay vì \( 2d \)).
  • Thực nghiệm cho perplexity tương đương hoặc tốt hơn ở scale lớn.

Trade-off: với model nhỏ, LayerNorm có thể vẫn tốt hơn vì bias \( \beta \) cho thêm flexibility. Quy ước modern cho LLM ≥ 1B params là RMSNorm.

12

Residual connection

Residual connection (skip connection) đến từ ResNet (He et al., 2015 — Series 3 Bài 33). Công thức tổng quát:

\[ \text{output} = \text{sublayer}(x) + x \]

Hai lý do quan trọng trong Transformer:

  • Gradient flow: \( \frac{\partial \text{output}}{\partial x} = \frac{\partial \text{sublayer}(x)}{\partial x} + I \). Hạng tử \( I \) (identity) đảm bảo gradient luôn có thành phần "chảy thẳng" qua block, không bị làm yếu bởi sublayer. Tránh vanishing gradient ngay cả khi stack rất sâu.
  • Sublayer học residual (delta): thay vì học \( x \mapsto Z \) trực tiếp, sublayer chỉ cần học \( x \mapsto Z - x \) — phần thay đổi nhỏ. Học delta dễ hơn học hàm toàn phần.

Trong Pre-Norm block, "residual stream" là một khái niệm hay được nhắc đến trong interpretability (Anthropic, 2022) — đó chính là đường \( X \to Y \to Z \) không qua LayerNorm, nơi tất cả block đọc-ghi vào.

13

Position-wise

"Position-wise" là một thuật ngữ trong paper gốc dùng cho FFN: cùng một FFN áp lên từng vị trí một cách độc lập. Mỗi vector \( x_i \in \mathbb{R}^{d} \) đi qua \( W_1, W_2 \) tách biệt, không tham chiếu các vị trí khác.

Điều này khác hoàn toàn với MHA — nơi mỗi token query nhìn tất cả token khác. Sự phân vai rõ ràng:

  • MHA: trộn thông tin giữa các token (token-mixing).
  • FFN: biến đổi thông tin trong mỗi token (channel-mixing).

Mô hình "token-mix + channel-mix" này được trừu tượng hoá rộng hơn trong MLP-Mixer (Tolstikhin et al., 2021) và các kiến trúc khác.

14

Stack nhiều block

Vì input/output cùng shape, có thể stack \( N \) block:

\[ H_0 = X, \quad H_{\ell+1} = \text{Block}_\ell(H_\ell), \quad \ell = 0, \dots, N-1 \]

Mỗi block có tham số riêng. Số block điển hình theo size:

  • BERT-base: 12 block.
  • BERT-large: 24 block.
  • GPT-2 small / medium / large / XL: 12 / 24 / 36 / 48.
  • GPT-3 175B: 96.
  • Llama 3: 32 (8B), 80 (70B), 126 (405B).

Có một quy luật scaling lỏng: số block ~ \( O(\sqrt{d}) \) hoặc tăng chậm hơn khi tăng total param. Chiều rộng \( d \) tăng nhanh hơn chiều sâu \( N \) — model 70B thường rộng hơn 8B chứ không sâu gấp 9 lần.

15

Param count một block

Giả sử Pre-Norm, MHA với \( d_k = d_v = d / h \), FFN với \( d_{\text{ff}} = 4d \), GELU activation, không tính bias:

  • MHA: 4 ma trận \( W^Q, W^K, W^V, W^O \), mỗi cái \( d \times d \). Tổng: \( 4 d^2 \).
  • FFN: \( W_1 \in d \times 4d \), \( W_2 \in 4d \times d \). Tổng: \( 8 d^2 \).
  • LayerNorm (2 lần): \( 2 \times 2d = 4d \). Negligible khi \( d \) lớn.

Tổng: \( \boxed{\approx 12 d^2} \) tham số / block.

Với SwiGLU (3 ma trận FFN), tỉ lệ thực tế là ~\( 12 d^2 \) khi đã điều chỉnh \( d_{\text{ff}} \) như Bước 10.

Ví dụ Llama 3 8B (\( d = 4096 \), \( N = 32 \)):

\[ \text{params/block} \approx 12 \times 4096^2 \approx 2 \times 10^8 = 200\text{M} \]

\[ \text{total blocks} \approx 32 \times 200\text{M} = 6.4\text{B} \]

Cộng thêm embedding (\( V \times d = 128{,}000 \times 4096 \approx 524\text{M} \)) ra ~7B; embedding tying và một số tinh chỉnh đẩy con số lên ~8B. Phép ước lượng "đếm thô" này thường khớp với con số chính thức ±10%.

16

Decoder block vs Encoder-Decoder block

Cấu trúc block thay đổi nhẹ tuỳ kiến trúc tổng thể (Bài 14 sẽ deep-dive):

  • Encoder block (BERT): self-attention bidirectional (không causal mask) + FFN. Mỗi token nhìn toàn bộ sequence.
  • Decoder block (GPT, Llama): self-attention với causal mask + FFN. Token thứ \( i \) chỉ nhìn token \( \leq i \) (đã thảo luận ở Bài 9 và Bài 10).
  • Encoder-Decoder block (T5, BART, original Transformer): decoder block có 3 sublayer thay vì 2:
    1. Causal self-attention (như decoder).
    2. Cross-attention: \( Q \) từ decoder, \( K, V \) từ output encoder. Cho decoder "nhìn" sang encoder.
    3. FFN.

LLM hiện đại đa phần là decoder-only — đơn giản hơn, cùng một block lặp lại. T5 và BART (encoder-decoder) vẫn dùng cho dịch máy và một số task seq2seq nhưng ít phổ biến trong chat / general assistant.

17

Biến thể 2024-2026

Một số biến thể block đáng nhớ ở thế hệ model hiện tại — chỉ điểm tên, sẽ chi tiết sau:

  • Mixture of Experts (MoE): thay FFN duy nhất bằng \( E \) "expert FFN" + một router. Mỗi token được route tới \( k \) (thường \( k = 2 \)) expert. Tổng param tăng (Mixtral 8x7B có 47B param) nhưng compute mỗi forward chỉ tương đương model dense ~13B. Mixtral 8x7B (Mistral, 2023), GPT-4 (rumor), DeepSeek-V3 (671B param, 37B active, 2024).
  • Parallel block (PaLM, Phi): chạy MHA và FFN song song với cùng input đã norm, rồi cộng kết quả thay vì xếp tuần tự. Tiết kiệm latency, tốt cho TPU. \[ Z = X + \text{MHA}(\text{LN}(X)) + \text{FFN}(\text{LN}(X)) \]
  • No bias (Llama, PaLM): bỏ tham số bias trong mọi Linear. Giảm tham số nhẹ, tăng độ ổn định khi train ở scale lớn.
  • QK-Norm (Henry et al., 2020; Chameleon 2024): thêm RMSNorm cho \( Q, K \) trước khi tính attention score. Giúp ổn định training với rất nhiều head.

Các biến thể này thường không thay đổi cấu trúc cơ bản (MHA + FFN + Norm + Residual) — chỉ thay đổi chi tiết bên trong từng thành phần.

18

PyTorch implementation

Implementation tối giản Pre-Norm block (PyTorch 2.x):

import torch
import torch.nn as nn

class TransformerBlock(nn.Module):
    def __init__(self, d_model, num_heads, d_ff, dropout=0.0):
        super().__init__()
        self.ln1 = nn.LayerNorm(d_model)
        self.attn = nn.MultiheadAttention(
            d_model, num_heads, dropout=dropout, batch_first=True
        )
        self.ln2 = nn.LayerNorm(d_model)
        self.ffn = nn.Sequential(
            nn.Linear(d_model, d_ff),
            nn.GELU(),
            nn.Linear(d_ff, d_model),
        )

    def forward(self, x, attn_mask=None):
        # Sublayer 1: MHA với pre-norm + residual
        h = self.ln1(x)
        a, _ = self.attn(h, h, h, attn_mask=attn_mask, need_weights=False)
        x = x + a
        # Sublayer 2: FFN với pre-norm + residual
        x = x + self.ffn(self.ln2(x))
        return x

Một vài lưu ý implementation:

  • batch_first=True cho shape \( (B, n, d) \) thay vì \( (n, B, d) \) — thuận hơn khi đọc code.
  • need_weights=False bỏ tính attention weight để tiết kiệm; nếu cần visualize, bật True.
  • attn_mask chính là causal mask (Bài 9) — tensor \( n \times n \) giá trị bool hoặc float \( -\infty \) ở vị trí cấm.
  • Phiên bản Llama thực tế thay nn.LayerNorm bằng RMSNorm, nn.MultiheadAttention bằng custom MHA + RoPE + GQA, FFN bằng SwiGLU. Đây chỉ là khung tối thiểu để chạy.
19

Mini Transformer stack

Stack 4 block thành một mini Transformer encoder và chạy forward thử:

import torch
import torch.nn as nn

class MiniTransformer(nn.Module):
    def __init__(self, d_model=64, num_heads=4, d_ff=256, num_layers=4):
        super().__init__()
        self.blocks = nn.ModuleList([
            TransformerBlock(d_model, num_heads, d_ff)
            for _ in range(num_layers)
        ])
        self.ln_final = nn.LayerNorm(d_model)

    def forward(self, x, attn_mask=None):
        for block in self.blocks:
            x = block(x, attn_mask=attn_mask)
        return self.ln_final(x)

torch.manual_seed(0)
model = MiniTransformer(d_model=64, num_heads=4, d_ff=256, num_layers=4)
x = torch.randn(2, 10, 64)          # batch=2, seq=10, d_model=64
out = model(x)
print(out.shape)                    # torch.Size([2, 10, 64])

n_params = sum(p.numel() for p in model.parameters())
print(f"Total params: {n_params:,}")

Output có cùng shape với input — vì stack block không đổi shape. ln_final ở cuối là quy ước phổ biến của Pre-Norm Transformer (GPT-2, Llama) để chuẩn hoá đầu ra trước khi đưa qua LM head.

Đếm tham số bằng tay với công thức \( 12 d^2 \): \( 12 \times 64^2 = 49{,}152 \) / block × 4 block = \( ~196 \)K. Kết quả thực tế từ numel() sẽ lớn hơn vì có thêm bias, LayerNorm — đối chiếu được.

20

Vị trí trong full Transformer

Kiến trúc GPT-style hoàn chỉnh (decoder-only):

Tokens [t1, t2, ..., tn]
   │
   ↓ (Embedding + Positional Encoding)
   │
   ↓ Block 1
   ↓ Block 2
   ↓ ...
   ↓ Block N
   │
   ↓ Final LayerNorm
   │
   ↓ Linear (LM head, d → vocab_size)
   │
   ↓ Softmax
   │
   → next-token probability

Bài 13 phụ trách phần "Block 1 ... Block N". Bài 6 đã làm phần embedding, Bài 12 phần positional encoding, Bài 9-11 đã làm chi tiết phần attention bên trong block. Bài 14 sẽ chỉ rõ ba dạng kiến trúc tổng thể (encoder-only, decoder-only, encoder-decoder) đều dùng block này nhưng theo cách khác nhau.

21

Bài tập

Bài 1 — Implement Pre-Norm block từ đầu. Không dùng nn.MultiheadAttention, viết MHA bằng tay dựa trên Bài 11 (4 linear projection + scaled dot-product + concat head). Test forward với input random shape \( (2, 8, 64) \), kiểm tra output cùng shape.

Bài 2 — Tính param count. Cho \( d = 512 \), \( d_{\text{ff}} = 2048 \), \( h = 8 \):

  1. Param của MHA (4 ma trận \( d \times d \) + bias).
  2. Param của FFN (2 ma trận, bao gồm bias).
  3. Param của 2 LayerNorm.
  4. Tổng. So với công thức \( 12 d^2 \).

Bài 3 — Post-Norm vs Pre-Norm. Viết hai class PostNormBlockPreNormBlock. Stack mỗi loại 10 block, train trên một dataset toy (ví dụ học hàm \( f(x) = \sin(x) \) với input random embedding). So sánh loss curve và độ ổn định.

Bài 4 — Thay FFN GELU bằng SwiGLU. Implement class SwiGLU với 3 linear, dùng F.silu. So sánh số tham số khi giữ tổng tham số FFN tương đương GELU (gợi ý: giảm \( d_{\text{ff}} \) từ \( 4d \) xuống \( \frac{8}{3} d \)).

Bài 5 — RMSNorm. Implement RMSNorm theo công thức Bước 11, không dùng nn.LayerNorm. So sánh tốc độ với nn.LayerNorm bằng %timeit trên tensor \( (32, 1024, 512) \).

Gợi ý đáp án Bài 2
  • MHA: \( 4 \times (512 \times 512 + 512) = 4 \times 262{,}656 \approx 1.05 \)M.
  • FFN: \( (512 \times 2048 + 2048) + (2048 \times 512 + 512) \approx 2.10 \)M.
  • LayerNorm: \( 2 \times 2 \times 512 = 2{,}048 \).
  • Tổng: ~3.15M tham số / block.
  • Công thức \( 12 d^2 = 12 \times 512^2 = 3.15 \)M — khớp.
22

Tóm tắt

  • Transformer block = MHA + FFN + 2 LayerNorm + 2 residual. Input và output cùng shape \( (n, d) \) để stack được.
  • Post-Norm (Vaswani 2017): LayerNorm sau sublayer + residual. Hoạt động tốt ở model nông, khó train sâu.
  • Pre-Norm (modern): LayerNorm trước sublayer; residual chảy thẳng. Ổn định ở scale lớn, là default từ GPT-2 trở đi.
  • FFN = 2 linear với activation giữa, hidden dim ~\( 4d \). Per-token, vai trò "channel-mixing" và memory. Chiếm ~\( \frac{2}{3} \) param block.
  • Activation: ReLU (gốc) → GELU (BERT, GPT-2) → SwiGLU (Llama, PaLM, Mistral). SwiGLU có 3 linear, dùng gate.
  • LayerNorm chuẩn hoá theo feature; RMSNorm bỏ mean và bias, nhanh hơn, là default cho LLM ≥ 1B.
  • Residual: \( \text{output} = \text{sublayer}(x) + x \). Bảo toàn gradient, giúp stack sâu.
  • Param count: ~\( 12 d^2 \) / block. Ví dụ Llama 3 8B (\( d = 4096 \)) ~200M / block × 32 block ~6.4B.
  • Decoder block = self-attention causal + FFN. Encoder-decoder block thêm cross-attention.
  • Biến thể modern: MoE (Mixtral, DeepSeek-V3), Parallel block (PaLM, Phi), no-bias (Llama), QK-Norm.
  • Bài 14 sẽ so sánh ba dạng kiến trúc tổng thể: encoder-only (BERT), decoder-only (GPT), encoder-decoder (T5).