LLM Architecture và Fundamentals
1. Transformer Architecture — Bức tranh toàn cảnh
Trước khi Transformer ra đời, mọi người xử lý ngôn ngữ theo model RNN/LSTM: đọc từng từ tuần tự, giữ một "hidden state" tích lũy từ trái sang phải. Nghe hợp lý — nhưng có vấn đề chết người: khi câu dài 500 từ, cái hidden state từ từ đầu tiên đã tan loãng gần hết lúc đến từ thứ 500. Bộ nhớ ngắn hạn như con người.
Transformer giải quyết bằng insight đơn giản nhưng genius: xử lý song song toàn bộ, và để model tự học xem vị trí nào cần "nhìn" vào vị trí nào — đó là cơ chế Attention.
flowchart TB I["Input Text: Why is the sky blue?"] --> T["Tokenizer (BPE)"] T --> ID["Token IDs: [8699, 338, 278, 14744, 7254, 29973]"] ID --> E["Embedding + Positional Encoding"] E --> V["Vectors E1..E6"] V --> B["Transformer Blocks × N"] B --> SA["Self-Attention (multi-head)"] SA --> FFN["Feed-Forward Network"] FFN --> R["Residual + LayerNorm"] R --> L["Linear (unembedding) + Softmax"] L --> P["Probability over vocabulary"] P --> S["Sampling (Temperature, Top-p)"] S --> O["Because of Rayleigh scattering..."]
Input Text: "Why is the sky blue?"
│
▼ Tokenizer (BPE)
[Why] [is] [the] [sky] [blue] [?] ← Token IDs (ví dụ: [8699, 338, 278, 14744, 7254, 29973])
│
▼ Embedding + Positional Encoding
[E₁] [E₂] [E₃] [E₄] [E₅] [E₆] ← Mỗi Eᵢ là vector [hidden_dim] chiều (768 hoặc 4096...)
│
▼ Transformer Blocks × N (N=32 ở Llama 3 8B, N=96 ở GPT-4)
┌────────────────────────────────────────────────────────────┐
│ Block 1 │
│ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ Self-Attention │ ──► │ Feed-Forward Network │ │
│ │ (multi-head) │ │ (2 Linear + Activation) │ │
│ └──────────────────┘ └──────────────────────────┘ │
│ + Residual Connection + Layer Normalization (mỗi bước) │
└────────────────────────────────────────────────────────────┘
⋮ (lặp N block)
│
▼ Linear (unembedding) + Softmax
[P(Why), P(is), P(the), ..., P(blue), ...] ← Xác suất mỗi token trong vocab
│
▼ Sampling (dựa vào Temperature, Top-p)
"Because of Rayleigh scattering..." ← Token được chọn
Khác biệt quan trọng giữa các kiến trúc:
| Architecture | Ví dụ | Attention | Use case |
|---|---|---|---|
| Encoder-only | BERT, RoBERTa | Bidirectional (nhìn cả 2 chiều) | Phân loại, NER, Search Embedding |
| Encoder-Decoder | T5, BART | Cross-attention | Dịch thuật, tóm tắt |
| Decoder-only | GPT, Llama, Mixtral | Causal (chỉ nhìn về trước) | Sinh văn bản, Chat, Agents |
SWE app hầu hết xài Decoder-only vì là kiến trúc của ChatGPT/Claude/Llama. Nhưng nếu bạn làm Semantic Search hay build Embeddings Model, hãy biết rằng bạn đang xài Encoder-only.
2. Tokenization — BPE và ẩn số "tiền tốn"
Mô hình không nhìn thấy chữ. Từ hello lúc bạn gõ vào sẽ được biến thành số nguyên (ví dụ: 15339). Quá trình đó gọi là Tokenization.
2.1 Thuật toán BPE (Byte-Pair Encoding)
BPE là thuật toán nén data được tái sử dụng. Ý tưởng siêu đơn giản:
Ban đầu, vocab = tất cả ký tự đơn lẻ:
{'h', 'e', 'l', 'o', ' ', 'w', 'r', ...}
Corpus ví dụ: "low low low lower lower newest newest"
Vòng lặp 1: đếm cặp ký tự hay đứng cạnh nhau nhất
→ ('l', 'o') xuất hiện nhiều nhất → ghép thành 'lo'
Vocab: {'h', 'e', 'lo', ' ', 'w', 'r', ...}
Vòng lặp 2: đếm lại
→ ('lo', 'w') → ghép thành 'low'
Vocab: {'h', 'e', 'low', 'er', ' ', 'new', ...}
Tiếp tục cho đến khi vocab đạt kích thước target (Llama 3: 128,256 tokens)
Kết quả: từ phổ biến có token riêng, từ hiếm bị chặt thành sub-word. Ví dụ tokenization → ['token', 'ization'] (2 tokens).
2.2 Tại sao SWE phải biết điều này?
Chi phí API tính theo token. Tiếng Việt tốn nhiều token hơn tiếng Anh vì vocab train chủ yếu từ English corpus:
"The quick brown fox" → 4 tokens (1 từ ≈ 1 token)
"Con cáo nâu nhanh" → ~8-10 tokens (tùy model, vì mỗi từ tiếng Việt có thể đến 2-3 tokens)
Practical implication: Nếu bạn build chatbot tiếng Việt, context window 128K tokens trên thực tế chứa ít hơn 128K từ. Budget token = Budget tiền. Với Gemini 1.5 Pro tính $3.5/1M tokens, xử lý 1000 request chatbot dài 5000-token mỗi cái = $17.5/ngày — tính nhầm là toang.
2.3 Token ảnh hưởng đến model size thế nào?
Llama 3 8B parameters:
Embedding Matrix = vocab_size × hidden_dim
= 128,256 × 4,096
≈ 500 triệu params
→ ~25% tổng model size chỉ là bảng tra cứu token!
Vocab lớn hơn → Embedding Layer nặng hơn → RAM nhiều hơn
Vocab nhỏ hơn → Mỗi từ dùng nhiều token hơn → Context window nhét ít thông tin hơn
3. Attention Mechanism — Trái tim của Transformer
3.1 Các ma trận Q, K, V
Mỗi token $x_i$ được project qua 3 ma trận weight (được học qua training):
flowchart TB X["x_i (vector 4096 chiều)"] --> Q["× W_Q → Query"] X --> K["× W_K → Key"] X --> V["× W_V → Value"]
x_i (vector 4096 chiều)
│
├── × W_Q → Query: "Token này đang hỏi gì?"
├── × W_K → Key: "Token này cung cấp thông tin gì?"
└── × W_V → Value: "Nếu khớp, token này đưa dữ liệu gì?"
Ẩn dụ dễ hiểu: Tưởng tượng bạn vào thư viện. Query là câu hỏi bạn đặt ra cho thủ thư ("Tôi cần sách về distributed systems"). Key là nhãn trên gáy sách ("Distributed Systems", "Algorithms", "Cooking"). Value là nội dung thực sự trong quyển sách. Attention tính xem Query của bạn khớp với Key nào nhất, rồi "đọc" Value tương ứng.
3.2 Công thức Scaled Dot-Product Attention
$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V$
Bóc từng phần:
Step 1: Tính similarity score
Score = Q × K^T
Kết quả: Matrix [seq_len × seq_len]
Score[i][j] = "Token i quan tâm đến Token j bao nhiêu?"
Step 2: Scale để tránh gradient vanish
Score / sqrt(d_k)
Lý do: Khi d_k lớn (ví dụ: 128), giá trị dot product có thể rất lớn
→ softmax bị saturated → gradient ≈ 0 → model không học được
Step 3: Masking (với Decoder-only)
Che đi Score[i][j] với j > i (không được nhìn về tương lai)
→ Đảm bảo model chỉ dùng thông tin từ quá khứ khi predict
Step 4: Softmax → Attention Weights
Biến Score thành xác suất (tổng mỗi hàng = 1.0)
Ví dụ: Token "it" nhìn vào "The animal" với 0.7, "didn't" với 0.1, "cross" với 0.2
Step 5: Weighted sum với V
Output = Attention_Weights × V
→ Mỗi token nhận được "tin tức tổng hợp" từ các token khác
3.3 Multi-Head Attention — Tại sao cần nhiều "head"?
Một head attention học một loại quan hệ. Nhiều head song song học nhiều kiểu quan hệ cùng lúc:
Input: "John kicked the ball. It bounced away."
│
Token "It" đang xem:
Head 1 → "It" refers to "ball" (coreference, quan hệ tham chiếu)
Head 2 → "It" comes after "." (syntactic, quan hệ cú pháp)
Head 3 → relationship với "bounce" (semantic role)
Head 4 → relates to objects in sentence (entity tracking)
Concat kết quả của N_heads heads → Linear projection → Output
Llama 3 8B có 32 heads, mỗi head dim = 4096 / 32 = 128. GPT-4 có tin đồn ~96 heads.
3.4 Complexity và giới hạn Context Window
Vấn đề lớn nhất của Attention: $O(N^2)$ Memory và Compute với $N$ = sequence length.
N = 1,000 tokens → Attention matrix: 1,000 × 1,000 = 1M entries → OK
N = 10,000 tokens → 10K × 10K = 100M entries → Bắt đầu nặng
N = 100,000 tokens → 100K × 100K = 10B entries → Tốn hàng chục GB chỉ cho 1 request!
Đây là lý do:
- Model 128K context window cực kỳ đắt để inference với prompt dài
- Flash Attention (2, 3) là optimization quan trọng: tính Attention theo block nhỏ, tái sử dụng SRAM (L2 Cache của GPU) thay vì đổ toàn bộ ra HBM (VRAM chính) → giảm memory I/O ~10-20x
4. KV Cache — Bottleneck ẩn của mọi LLM Server
Đây là phần quan trọng nhất cho bất kỳ SWE nào deploy LLM. Hiểu KV Cache là hiểu tại sao token đầu tiên ra chậm nhưng token kế tiếp cứ "chảy" đều đặn.
4.1 Tại sao cần cache?
Decoder-only model sinh text auto-regressive: token thứ N+1 phụ thuộc vào toàn bộ N token trước. Nếu không cache:
Sinh token thứ 5:
Phải tính Attention của token 5 với token [1,2,3,4,5] → 5 phép tính
Sinh token thứ 100:
Phải tính Attention của token 100 với token [1..100] → 100 phép tính
Sinh token thứ 1000:
→ 1000 phép tính, đa phần là RE-COMPUTE những token đã tính rồi!
4.2 Giải pháp: Cache K và V
Trong mỗi Attention Layer, $Q$ của token mới luôn thay đổi, nhưng $K$ và $V$ của các token cũ không thay đổi. Ta cache chúng lại:
Prefill Phase (xử lý full prompt):
Prompt: "Tóm tắt lịch sử Việt Nam cho tôi"
→ Tính Q, K, V cho toàn bộ 10 tokens một lượt (Matrix multiplication song song)
→ Lưu K, V của 10 tokens vào bộ nhớ (KV Cache)
→ GPU: Compute-Bound (parallel matrix ops)
Decode Phase (sinh response từng token):
Token 11: Load KV Cache [1..10] + tính Q,K,V mới cho token 11 → append vào cache
Token 12: Load KV Cache [1..11] + tính Q,K,V mới cho token 12 → append vào cache
...
→ GPU: Memory-Bound (bandwidth của VRAM là bottleneck, không phải compute cores)
4.3 KV Cache chiếm bộ nhớ bao nhiêu?
Công thức:
KV Cache size = 2 (K và V)
× N_layers (số transformer blocks)
× N_heads (số attention heads)
× head_dim (kích thước mỗi head)
× seq_len (số token trong context)
× dtype_bytes (2 bytes với float16)
Ví dụ thực tế — Llama 3 8B với 1 request, context 4096 tokens:
2 × 32 × 32 × 128 × 4096 × 2 bytes = 2 GB
→ Chỉ 1 concurrent request đã tốn 2GB VRAM cho KV Cache!
→ Server GPU A100 (80GB VRAM) sau khi load model (~16GB), còn ~64GB
→ Tối đa ~32 concurrent requests nếu context window 4096
→ User gõ dài hơn (8K, 16K tokens) → số concurrent giảm mạnh
4.4 Implications cho SWE
Thiết kế API Gateway:
- Rate limit theo token, không chỉ theo request
- Monitor VRAM utilization, set limit concurrent req/GPU
- Long-running conversations tốn VRAM hơn short ones
Tối ưu chi phí:
- Trim unnecessary system prompt (mỗi token tốn thêm KV Cache)
- Chia nhỏ conversation: cuộc chat 1000-turn tích KV Cache khổng lồ
- Prefix caching: nếu nhiều request cùng share 1 phần context dài
(ví dụ: document analysis), cache K,V của phần đó và reuse
→ GPT-4 Turbo, Claude 3.5 đều support tính năng này
5. Sampling và Temperature — Bật tắt "chế độ sáng tạo"
Model output là một vector $10^5$ float (xác suất mỗi token trong vocab). Làm sao chọn token tiếp theo?
5.1 Greedy Decoding
Luôn chọn token có xác suất cao nhất. Deterministic, nhưng output nhàm, lặp đi lặp lại ("I think I think I think...").
5.2 Temperature Sampling
$P'(x) = \frac{P(x)^{1/T}}{\sum_j P(x_j)^{1/T}}$
T = 0 (≈ Greedy): Chọn argmax, hoàn toàn deterministic
T = 0.7 (default): Hơi ngẫu nhiên, vẫn coherent → code generation, QA
T = 1.0: Sampling theo xác suất gốc của model
T > 1.5: Rất ngẫu nhiên, có thể vô nghĩa → creative writing
5.3 Top-p (Nucleus Sampling)
Thay vì giới hạn bằng temperature, top-p giới hạn bằng tập token nhỏ nhất sao cho tổng xác suất ≥ p:
vocab sorted by prob: [0.35, 0.25, 0.20, 0.10, 0.05, 0.03, ...]
top_p = 0.9:
token 1: 0.35 → cumsum = 0.35 (< 0.9, keep)
token 2: 0.25 → cumsum = 0.60 (< 0.9, keep)
token 3: 0.20 → cumsum = 0.80 (< 0.9, keep)
token 4: 0.10 → cumsum = 0.90 (= 0.9, keep)
token 5+: loại bỏ hoàn toàn (prob = 0)
Rule of thumb cho SWE:
- Factual extraction / JSON output:
temperature=0, top_p=1— deterministic, dễ parse - Chatbot / General QA:
temperature=0.7, top_p=0.9— default an toàn - Creative writing:
temperature=1.0-1.2— nhưng đừng dùng cho system critical
6. Model Architecture Scale — Tại sao 70B giỏi hơn 7B?
Llama 3 so sánh:
8B 70B 405B
Hidden dim: 4,096 8,192 16,384
Layers (N): 32 80 126
Attention heads: 32 64 128
Context: 128K 128K 128K
VRAM (fp16): ~16GB ~140GB ~810GB
Năng lực tăng theo scale:
7B: Tốt cho task đơn giản, code cơ bản, Q&A ngắn
70B: Reasoning phức tạp, code production-quality, multi-step tasks
405B: Research-level, đối thủ GPT-4
Scaling Laws (Chinchilla): Để model N params học tốt nhất, cần train trên 20 × N tokens. Llama 3 70B train trên 15 nghìn tỷ (15T) tokens — gấp 10 lần công thức Chinchilla để ép thêm performance với model nhỏ.
Mixture of Experts (MoE) — Mixtral, Grok: Thay vì 1 FFN khổng lồ, có 8 FFN "chuyên gia" nhỏ. Mỗi token chỉ kích hoạt 2/8 expert (sparse activation). Mixtral 8x7B có 47B params tổng nhưng chỉ activate ~13B params mỗi token → nhanh như model 13B nhưng chất lượng gần 70B.