@xdev-asia/xdev-knowledge-mcp 1.0.57 → 1.0.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (17) hide show
  1. package/content/blog/ai/minimax-danh-gia-chi-tiet-nen-tang-ai-full-stack-trung-quoc.md +450 -0
  2. package/content/blog/ai/nvidia-dli-generative-ai-chung-chi-va-lo-trinh-hoc.md +894 -0
  3. package/content/metadata/authors/duy-tran.md +2 -0
  4. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/01-deep-learning-foundations/lessons/01-bai-1-pytorch-neural-network-fundamentals.md +790 -0
  5. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/01-deep-learning-foundations/lessons/02-bai-2-transformer-architecture-attention.md +984 -0
  6. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/02-diffusion-models/lessons/01-bai-3-unet-architecture-denoising.md +1111 -0
  7. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/02-diffusion-models/lessons/02-bai-4-ddpm-forward-reverse-diffusion.md +1007 -0
  8. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/02-diffusion-models/lessons/03-bai-5-clip-text-to-image-pipeline.md +1037 -0
  9. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/03-llm-applications-rag/lessons/01-bai-6-llm-inference-pipeline-design.md +929 -0
  10. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/03-llm-applications-rag/lessons/02-bai-7-rag-retrieval-augmented-generation.md +1099 -0
  11. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/03-llm-applications-rag/lessons/03-bai-8-rag-agent-build-evaluate.md +1249 -0
  12. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/04-agentic-ai-customization/lessons/01-bai-9-agentic-ai-multi-agent-systems.md +1357 -0
  13. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/04-agentic-ai-customization/lessons/02-bai-10-llm-evaluation-lora-fine-tuning.md +1867 -0
  14. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/index.md +237 -0
  15. package/data/quizzes/nvidia-dli-generative-ai.json +350 -0
  16. package/data/quizzes.json +14 -0
  17. package/package.json +1 -1
@@ -0,0 +1,984 @@
1
+ ---
2
+ id: 019c9619-nv01-p1-l02
3
+ title: 'Bài 2: Transformer Architecture & Attention Mechanism'
4
+ slug: bai-2-transformer-architecture-attention
5
+ description: >-
6
+ Self-attention, multi-head attention, positional encoding.
7
+ Encoder-decoder architecture. BERT, GPT, T5 model families.
8
+ Tokenization: BPE, WordPiece, SentencePiece.
9
+ NLP tasks: classification, NER, QA, summarization.
10
+ duration_minutes: 90
11
+ is_free: true
12
+ video_url: null
13
+ sort_order: 2
14
+ section_title: "Part 1: Deep Learning Foundations"
15
+ course:
16
+ id: 019c9619-nv01-7001-c001-nv0100000001
17
+ title: 'Luyện thi NVIDIA DLI — Generative AI with Diffusion Models & LLMs'
18
+ slug: luyen-thi-nvidia-dli-generative-ai
19
+ ---
20
+
21
+ <h2 id="gioi-thieu">1. Giới thiệu</h2>
22
+
23
+ <p><strong>Transformer</strong> là kiến trúc nền tảng đằng sau mọi mô hình Generative AI hiện đại — từ <strong>GPT</strong>, <strong>BERT</strong>, <strong>Stable Diffusion</strong> đến <strong>LLaMA</strong>. Trong bài assessment của NVIDIA DLI, bạn phải hiểu rõ cách <strong>Attention Mechanism</strong> hoạt động và có khả năng implement nó bằng PyTorch.</p>
24
+
25
+ <p>Bài học này sẽ đi từ <strong>Scaled Dot-Product Attention</strong> đến toàn bộ kiến trúc <strong>Transformer</strong>, sau đó mapping sang các model families và NLP tasks cụ thể.</p>
26
+
27
+ <blockquote><p><strong>Exam tip:</strong> NVIDIA DLI assessment thường yêu cầu bạn hoàn thành code cho attention mechanism hoặc debug lỗi dimension mismatch trong Transformer. Hãy nắm vững <strong>tensor shapes</strong> qua từng bước của attention — đây là chìa khóa để pass assessment.</p></blockquote>
28
+
29
+ <figure><img src="/storage/uploads/2026/04/nvidia-dli-bai2-transformer-architecture.png" alt="Kiến trúc Transformer — Encoder-Decoder, Self-Attention, Cross-Attention" loading="lazy" /><figcaption>Kiến trúc Transformer — Encoder-Decoder, Self-Attention, Cross-Attention</figcaption></figure>
30
+
31
+ <h2 id="attention-mechanism">2. Attention Mechanism</h2>
32
+
33
+ <h3 id="attention-intuition">2.1 Trực giác — "Phần nào quan trọng nhất?"</h3>
34
+
35
+ <p><strong>Attention</strong> trả lời câu hỏi: "Khi xử lý token hiện tại, những token nào trong input sequence là quan trọng nhất?" Thay vì nén toàn bộ sequence vào một fixed-size vector như RNN, Attention cho phép mô hình "nhìn" trực tiếp vào mọi vị trí của input.</p>
36
+
37
+ <p>Cơ chế hoạt động qua 3 thành phần:</p>
38
+
39
+ <ul>
40
+ <li><strong>Query (Q)</strong> — "Tôi đang tìm gì?" — token hiện tại đặt câu hỏi</li>
41
+ <li><strong>Key (K)</strong> — "Tôi chứa thông tin gì?" — mỗi token quảng cáo nội dung của mình</li>
42
+ <li><strong>Value (V)</strong> — "Đây là thông tin thực sự" — nội dung được trả về khi match</li>
43
+ </ul>
44
+
45
+ <pre><code class="language-text">Ví dụ: "The cat sat on the mat because it was tired"
46
+
47
+ Token "it" (Query)
48
+
49
+ ┌─────────────────────────────┤
50
+ │ Attention scores: │
51
+ │ "cat" = 0.72 ← cao! │
52
+ │ "mat" = 0.11 │
53
+ │ "sat" = 0.08 │
54
+ │ "The" = 0.03 │
55
+ │ ... │
56
+ └─────────────────────────────┘
57
+ → "it" attend chủ yếu vào "cat"
58
+ </code></pre>
59
+
60
+ <h3 id="scaled-dot-product">2.2 Scaled Dot-Product Attention</h3>
61
+
62
+ <p>Công thức chính của attention:</p>
63
+
64
+ <pre><code class="language-text">Attention(Q, K, V) = softmax(Q · K^T / √d_k) · V
65
+
66
+ Trong đó:
67
+ Q: Query matrix — shape (seq_len, d_k)
68
+ K: Key matrix — shape (seq_len, d_k)
69
+ V: Value matrix — shape (seq_len, d_v)
70
+ d_k: dimension của Key vectors
71
+ √d_k: scaling factor để tránh gradient vanishing trong softmax
72
+ </code></pre>
73
+
74
+ <p>Tại sao cần <strong>scaling</strong> bằng √d_k? Khi d_k lớn, dot product Q·K^T có thể rất lớn, khiến softmax bão hòa → gradient gần bằng 0. Chia cho √d_k giữ variance ổn định.</p>
75
+
76
+ <pre><code class="language-text">Flow của Scaled Dot-Product Attention:
77
+
78
+ Q ──┐
79
+ │──→ MatMul ──→ Scale (÷√d_k) ──→ Mask (opt.) ──→ Softmax ──→ MatMul ──→ Output
80
+ K ──┘ ↑
81
+
82
+ V ────────────────────────────────────────────────────────────────────┘
83
+
84
+ Shapes (batch_size=B, seq_len=S, d_k=D):
85
+ Q: (B, S, D)
86
+ K^T: (B, D, S)
87
+ Q·K^T: (B, S, S) ← attention score matrix
88
+ softmax: (B, S, S) ← attention weights (mỗi hàng sum = 1)
89
+ × V: (B, S, D) ← weighted output
90
+ </code></pre>
91
+
92
+ <h3 id="attention-code">2.3 Code: Implement Attention từ Scratch</h3>
93
+
94
+ <pre><code class="language-python">import torch
95
+ import torch.nn.functional as F
96
+ import math
97
+
98
+ def scaled_dot_product_attention(Q, K, V, mask=None):
99
+ """
100
+ Q: (batch, seq_len, d_k)
101
+ K: (batch, seq_len, d_k)
102
+ V: (batch, seq_len, d_v)
103
+ mask: (batch, 1, seq_len) or (batch, seq_len, seq_len)
104
+ """
105
+ d_k = Q.size(-1)
106
+
107
+ # Step 1: Tính attention scores
108
+ scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(d_k)
109
+ # scores shape: (batch, seq_len, seq_len)
110
+
111
+ # Step 2: Apply mask (cho causal attention trong decoder)
112
+ if mask is not None:
113
+ scores = scores.masked_fill(mask == 0, float('-inf'))
114
+
115
+ # Step 3: Softmax để có attention weights
116
+ attn_weights = F.softmax(scores, dim=-1)
117
+ # attn_weights shape: (batch, seq_len, seq_len)
118
+
119
+ # Step 4: Weighted sum of values
120
+ output = torch.matmul(attn_weights, V)
121
+ # output shape: (batch, seq_len, d_v)
122
+
123
+ return output, attn_weights
124
+
125
+ # Demo
126
+ batch_size, seq_len, d_k = 2, 5, 64
127
+ Q = torch.randn(batch_size, seq_len, d_k)
128
+ K = torch.randn(batch_size, seq_len, d_k)
129
+ V = torch.randn(batch_size, seq_len, d_k)
130
+
131
+ output, weights = scaled_dot_product_attention(Q, K, V)
132
+ print(f"Output shape: {output.shape}") # (2, 5, 64)
133
+ print(f"Weights shape: {weights.shape}") # (2, 5, 5)
134
+ print(f"Weights sum per row: {weights.sum(dim=-1)}") # mỗi hàng = 1.0
135
+ </code></pre>
136
+
137
+ <blockquote><p><strong>Exam tip:</strong> Lỗi phổ biến nhất khi implement attention: quên <code>K.transpose(-2, -1)</code> hoặc chia sai dimension. Hãy luôn kiểm tra shape sau mỗi bước — <code>scores</code> phải có shape <code>(batch, seq_len, seq_len)</code>.</p></blockquote>
138
+
139
+ <h2 id="multi-head-attention">3. Multi-Head Attention</h2>
140
+
141
+ <h3 id="why-multi-head">3.1 Tại sao cần nhiều heads?</h3>
142
+
143
+ <p>Một head duy nhất chỉ học được <strong>một loại relationship</strong>. Multi-Head Attention cho phép mô hình attend đồng thời vào nhiều <strong>representation subspaces</strong> khác nhau:</p>
144
+
145
+ <ul>
146
+ <li>Head 1: học syntactic relationships (subject-verb)</li>
147
+ <li>Head 2: học coreference (pronoun → noun)</li>
148
+ <li>Head 3: học positional proximity</li>
149
+ <li>Head 4: học semantic similarity</li>
150
+ </ul>
151
+
152
+ <pre><code class="language-text">Multi-Head Attention Flow:
153
+
154
+ Input (batch, seq_len, d_model)
155
+
156
+ ├── Linear → Q ──┐
157
+ ├── Linear → K ──┼── Split thành h heads
158
+ └── Linear → V ──┘
159
+
160
+ ┌─────────────┼─────────────┐
161
+ ▼ ▼ ▼
162
+ Head 1 Head 2 ... Head h
163
+ Attention() Attention() Attention()
164
+ │ │ │
165
+ └─────────────┼─────────────┘
166
+
167
+ Concatenate
168
+
169
+ Linear
170
+
171
+ Output (batch, seq_len, d_model)
172
+
173
+ Shapes (d_model=512, h=8, d_k = d_model/h = 64):
174
+ Input: (B, S, 512)
175
+ Per-head Q/K/V: (B, h, S, 64) ← reshape sau linear
176
+ Per-head out: (B, h, S, 64)
177
+ Concat: (B, S, 512) ← h × d_k = d_model
178
+ Final output: (B, S, 512)
179
+ </code></pre>
180
+
181
+ <h3 id="multi-head-code">3.2 Code: MultiHeadAttention Module</h3>
182
+
183
+ <pre><code class="language-python">import torch
184
+ import torch.nn as nn
185
+ import math
186
+
187
+ class MultiHeadAttention(nn.Module):
188
+ def __init__(self, d_model, num_heads):
189
+ super().__init__()
190
+ assert d_model % num_heads == 0, "d_model phải chia hết cho num_heads"
191
+
192
+ self.d_model = d_model
193
+ self.num_heads = num_heads
194
+ self.d_k = d_model // num_heads
195
+
196
+ # Linear projections cho Q, K, V và output
197
+ self.W_q = nn.Linear(d_model, d_model)
198
+ self.W_k = nn.Linear(d_model, d_model)
199
+ self.W_v = nn.Linear(d_model, d_model)
200
+ self.W_o = nn.Linear(d_model, d_model)
201
+
202
+ def forward(self, query, key, value, mask=None):
203
+ batch_size = query.size(0)
204
+
205
+ # Step 1: Linear projections
206
+ Q = self.W_q(query) # (B, S, d_model)
207
+ K = self.W_k(key)
208
+ V = self.W_v(value)
209
+
210
+ # Step 2: Reshape thành multi-head format
211
+ # (B, S, d_model) → (B, S, h, d_k) → (B, h, S, d_k)
212
+ Q = Q.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
213
+ K = K.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
214
+ V = V.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
215
+
216
+ # Step 3: Scaled dot-product attention cho mỗi head
217
+ scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
218
+
219
+ if mask is not None:
220
+ scores = scores.masked_fill(mask == 0, float('-inf'))
221
+
222
+ attn_weights = torch.softmax(scores, dim=-1)
223
+ context = torch.matmul(attn_weights, V)
224
+ # context: (B, h, S, d_k)
225
+
226
+ # Step 4: Concatenate heads
227
+ # (B, h, S, d_k) → (B, S, h, d_k) → (B, S, d_model)
228
+ context = context.transpose(1, 2).contiguous().view(
229
+ batch_size, -1, self.d_model
230
+ )
231
+
232
+ # Step 5: Final linear projection
233
+ output = self.W_o(context)
234
+ return output
235
+
236
+ # Demo
237
+ d_model, num_heads = 512, 8
238
+ mha = MultiHeadAttention(d_model, num_heads)
239
+
240
+ x = torch.randn(2, 10, d_model) # batch=2, seq_len=10
241
+ output = mha(x, x, x) # self-attention: Q=K=V=x
242
+ print(f"Output shape: {output.shape}") # (2, 10, 512)
243
+ </code></pre>
244
+
245
+ <table>
246
+ <thead>
247
+ <tr><th>Parameter</th><th>Giá trị thường gặp</th><th>Ghi chú</th></tr>
248
+ </thead>
249
+ <tbody>
250
+ <tr><td>d_model</td><td>512, 768, 1024</td><td>Kích thước embedding</td></tr>
251
+ <tr><td>num_heads</td><td>8, 12, 16</td><td>d_model phải chia hết cho num_heads</td></tr>
252
+ <tr><td>d_k = d_model / h</td><td>64, 64, 64</td><td>Mỗi head thường có d_k = 64</td></tr>
253
+ <tr><td>Total params (MHA)</td><td>4 × d_model²</td><td>4 linear layers: W_q, W_k, W_v, W_o</td></tr>
254
+ </tbody>
255
+ </table>
256
+
257
+ <blockquote><p><strong>Exam tip:</strong> Trong DLI assessment, nếu bạn gặp lỗi <code>RuntimeError: shape mismatch</code> ở attention, hãy kiểm tra: (1) <code>d_model % num_heads == 0</code>, (2) <code>view()</code> và <code>transpose()</code> đúng thứ tự, (3) nhớ gọi <code>.contiguous()</code> trước <code>.view()</code> sau transpose.</p></blockquote>
258
+
259
+ <h2 id="transformer-architecture">4. Transformer Architecture</h2>
260
+
261
+ <h3 id="encoder-block">4.1 Encoder Block</h3>
262
+
263
+ <p>Mỗi layer trong <strong>Transformer Encoder</strong> gồm 2 sub-layers với <strong>residual connections</strong> và <strong>layer normalization</strong>:</p>
264
+
265
+ <pre><code class="language-text">Transformer Encoder Block:
266
+
267
+ Input
268
+
269
+
270
+ ┌─────────────────────────────┐
271
+ │ Multi-Head Self-Attention │
272
+ └──────────────┬──────────────┘
273
+
274
+ ┌──────────┴──────────┐
275
+ │ Add & Norm │ ← x + Sublayer(x), rồi LayerNorm
276
+ └──────────┬──────────┘
277
+
278
+ ┌──────────┴──────────┐
279
+ │ Feed-Forward Net │ ← 2 linear layers + ReLU/GELU
280
+ │ FFN(x) = W₂·σ(W₁x + b₁) + b₂
281
+ └──────────┬──────────┘
282
+
283
+ ┌──────────┴──────────┐
284
+ │ Add & Norm │
285
+ └──────────┬──────────┘
286
+
287
+ Output
288
+ </code></pre>
289
+
290
+ <h3 id="decoder-block">4.2 Decoder Block</h3>
291
+
292
+ <p><strong>Decoder</strong> có thêm một sub-layer <strong>Cross-Attention</strong> — attend vào output của encoder:</p>
293
+
294
+ <pre><code class="language-text">Transformer Decoder Block:
295
+
296
+ Input (shifted right)
297
+
298
+
299
+ ┌────────────────────────────────┐
300
+ │ Masked Multi-Head Attention │ ← causal mask: chỉ nhìn tokens trước
301
+ └──────────────┬─────────────────┘
302
+
303
+ ┌──────────┴──────────┐
304
+ │ Add & Norm │
305
+ └──────────┬──────────┘
306
+
307
+ ┌──────────┴──────────────────────────┐
308
+ │ Cross Multi-Head Attention │ ← Q từ decoder, K/V từ encoder
309
+ │ Q = decoder hidden states │
310
+ │ K, V = encoder output │
311
+ └──────────────────┬──────────────────┘
312
+
313
+ ┌──────────┴──────────┐
314
+ │ Add & Norm │
315
+ └──────────┬──────────┘
316
+
317
+ ┌──────────┴──────────┐
318
+ │ Feed-Forward Net │
319
+ └──────────┬──────────┘
320
+
321
+ ┌──────────┴──────────┐
322
+ │ Add & Norm │
323
+ └──────────┬──────────┘
324
+
325
+ Output
326
+ </code></pre>
327
+
328
+ <h3 id="full-transformer">4.3 Full Transformer Architecture</h3>
329
+
330
+ <pre><code class="language-text">┌─────────────────────────────────────────────────────────────────┐
331
+ │ TRANSFORMER ARCHITECTURE │
332
+ ├───────────────────────┬─────────────────────────────────────────┤
333
+ │ ENCODER │ DECODER │
334
+ │ │ │
335
+ │ Input Embedding │ Output Embedding │
336
+ │ + │ + │
337
+ │ Positional Enc. │ Positional Enc. │
338
+ │ │ │ │ │
339
+ │ ┌────┴────┐ │ ┌────┴──────────┐ │
340
+ │ │ MH │ │ │ Masked MH │ │
341
+ │ │ Self- │ │ │ Self- │ │
342
+ │ │ Attn │ │ │ Attention │ │
343
+ │ └────┬────┘ │ └────┬──────────┘ │
344
+ │ Add & Norm │ Add & Norm │
345
+ │ │ │ │ │
346
+ │ ┌────┴────┐ │ ┌────────┴────────────┐ │
347
+ │ │ Feed │ │ │ Cross MH Attention │ │
348
+ │ │ Forward │ ─────┼───►│ Q=dec, K/V=enc │ │
349
+ │ │ Net │ │ └─────────┬───────────┘ │
350
+ │ └────┬────┘ │ Add & Norm │
351
+ │ Add & Norm │ │ │
352
+ │ │ │ ┌────┴────┐ │
353
+ │ × N layers │ │ Feed │ │
354
+ │ │ │ │ Forward │ │
355
+ │ Encoder │ └────┬────┘ │
356
+ │ Output │ Add & Norm │
357
+ │ │ │ │
358
+ │ │ × N layers │
359
+ │ │ │ │
360
+ │ │ Linear + Softmax │
361
+ │ │ │ │
362
+ │ │ Output Probabilities │
363
+ └───────────────────────┴─────────────────────────────────────────┘
364
+ </code></pre>
365
+
366
+ <h3 id="positional-encoding">4.4 Positional Encoding</h3>
367
+
368
+ <p>Transformer không có khái niệm "thứ tự" như RNN. <strong>Positional Encoding</strong> thêm thông tin vị trí vào embedding bằng hàm sin/cos:</p>
369
+
370
+ <pre><code class="language-text">PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
371
+ PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
372
+
373
+ pos: vị trí token trong sequence (0, 1, 2, ...)
374
+ i: chỉ số dimension (0, 1, 2, ..., d_model/2)
375
+ </code></pre>
376
+
377
+ <p>Đây chính xác là concept được <strong>reuse trong Diffusion Models</strong> — sinusoidal embeddings để encode timestep t. Nắm vững positional encoding ở đây sẽ giúp bạn rất nhiều ở phần Diffusion.</p>
378
+
379
+ <pre><code class="language-python">import torch
380
+ import math
381
+
382
+ class PositionalEncoding(nn.Module):
383
+ def __init__(self, d_model, max_len=5000):
384
+ super().__init__()
385
+ pe = torch.zeros(max_len, d_model)
386
+ position = torch.arange(0, max_len).unsqueeze(1).float()
387
+ div_term = torch.exp(
388
+ torch.arange(0, d_model, 2).float()
389
+ * (-math.log(10000.0) / d_model)
390
+ )
391
+ pe[:, 0::2] = torch.sin(position * div_term) # even indices
392
+ pe[:, 1::2] = torch.cos(position * div_term) # odd indices
393
+ pe = pe.unsqueeze(0) # (1, max_len, d_model)
394
+ self.register_buffer('pe', pe)
395
+
396
+ def forward(self, x):
397
+ # x: (batch, seq_len, d_model)
398
+ return x + self.pe[:, :x.size(1), :]
399
+
400
+ # Demo
401
+ pe = PositionalEncoding(d_model=512)
402
+ x = torch.randn(2, 100, 512) # batch=2, seq_len=100
403
+ output = pe(x)
404
+ print(f"Output shape: {output.shape}") # (2, 100, 512)
405
+ </code></pre>
406
+
407
+ <h3 id="layer-norm">4.5 Layer Normalization vs Batch Normalization</h3>
408
+
409
+ <table>
410
+ <thead>
411
+ <tr><th>Feature</th><th>Batch Normalization</th><th>Layer Normalization</th></tr>
412
+ </thead>
413
+ <tbody>
414
+ <tr><td>Normalize across</td><td>Batch dimension</td><td>Feature dimension</td></tr>
415
+ <tr><td>Phụ thuộc batch size</td><td>Có — cần batch đủ lớn</td><td>Không — hoạt động trên từng sample</td></tr>
416
+ <tr><td>Dùng trong</td><td>CNN (computer vision)</td><td>Transformer, RNN (NLP)</td></tr>
417
+ <tr><td>Inference behavior</td><td>Dùng running statistics</td><td>Tính trực tiếp — giống train</td></tr>
418
+ <tr><td>Transformer sử dụng</td><td>Không</td><td><strong>Có — mọi sub-layer</strong></td></tr>
419
+ </tbody>
420
+ </table>
421
+
422
+ <blockquote><p><strong>Exam tip:</strong> Nếu câu hỏi hỏi "vì sao Transformer dùng LayerNorm thay vì BatchNorm?" → trả lời: (1) NLP có variable sequence length → batch stats không ổn định, (2) LayerNorm không phụ thuộc batch size, hoạt động tốt với batch size nhỏ và inference.</p></blockquote>
423
+
424
+ <h2 id="model-families">5. Model Families</h2>
425
+
426
+ <h3 id="encoder-only">5.1 Encoder-only: BERT</h3>
427
+
428
+ <p><strong>BERT</strong> (Bidirectional Encoder Representations from Transformers) chỉ sử dụng phần <strong>Encoder</strong>. Pre-training bằng:</p>
429
+
430
+ <ul>
431
+ <li><strong>MLM (Masked Language Modeling)</strong> — che 15% tokens, dự đoán token bị che</li>
432
+ <li><strong>NSP (Next Sentence Prediction)</strong> — hai câu có liên tiếp không?</li>
433
+ </ul>
434
+
435
+ <p>Vì BERT nhìn được cả hai hướng (bidirectional), nó rất mạnh cho các task <strong>understanding</strong>: classification, NER, QA.</p>
436
+
437
+ <h3 id="decoder-only">5.2 Decoder-only: GPT</h3>
438
+
439
+ <p><strong>GPT</strong> (Generative Pre-trained Transformer) chỉ sử dụng <strong>Decoder</strong> với <strong>causal masking</strong> — mỗi token chỉ attend vào các token trước nó. Pre-training bằng <strong>next-token prediction</strong>.</p>
440
+
441
+ <pre><code class="language-text">Causal Attention Mask (GPT):
442
+
443
+ Token: [The] [cat] [sat] [on]
444
+ The ✓ ✗ ✗ ✗
445
+ cat ✓ ✓ ✗ ✗
446
+ sat ✓ ✓ ✓ ✗
447
+ on ✓ ✓ ✓ ✓
448
+
449
+ ✓ = có thể attend ✗ = bị mask (= -inf trước softmax)
450
+
451
+ → Mỗi token chỉ "thấy" tokens trước nó
452
+ → Phù hợp cho generation: sinh token tiếp theo
453
+ </code></pre>
454
+
455
+ <h3 id="encoder-decoder">5.3 Encoder-Decoder: T5</h3>
456
+
457
+ <p><strong>T5</strong> (Text-to-Text Transfer Transformer) sử dụng đầy đủ kiến trúc <strong>Encoder-Decoder</strong>. Mọi task đều được frame thành "text-in → text-out":</p>
458
+
459
+ <ul>
460
+ <li>Translation: <code>"translate English to French: The cat sat"</code> → <code>"Le chat s'est assis"</code></li>
461
+ <li>Summarization: <code>"summarize: {long text}"</code> → <code>"{summary}"</code></li>
462
+ <li>Classification: <code>"classify: {text}"</code> → <code>"positive"</code></li>
463
+ </ul>
464
+
465
+ <h3 id="model-comparison">5.4 Comparison Table</h3>
466
+
467
+ <table>
468
+ <thead>
469
+ <tr><th>Feature</th><th>BERT (Encoder)</th><th>GPT (Decoder)</th><th>T5 (Enc-Dec)</th></tr>
470
+ </thead>
471
+ <tbody>
472
+ <tr><td>Architecture</td><td>Encoder-only</td><td>Decoder-only</td><td>Encoder-Decoder</td></tr>
473
+ <tr><td>Directionality</td><td>Bidirectional</td><td>Left-to-right (causal)</td><td>Bidirectional enc + causal dec</td></tr>
474
+ <tr><td>Pre-training</td><td>MLM + NSP</td><td>Next-token prediction</td><td>Span corruption</td></tr>
475
+ <tr><td>Attention mask</td><td>Full (nhìn mọi token)</td><td>Causal (chỉ nhìn trước)</td><td>Full enc + causal dec</td></tr>
476
+ <tr><td>Mạnh cho</td><td>Understanding: NER, QA, classification</td><td>Generation: text, code</td><td>Seq2seq: translation, summary</td></tr>
477
+ <tr><td>Output</td><td>Contextual embeddings</td><td>Next token probability</td><td>Target sequence</td></tr>
478
+ <tr><td>Ví dụ models</td><td>BERT, RoBERTa, DeBERTa</td><td>GPT-2/3/4, LLaMA</td><td>T5, BART, mBART</td></tr>
479
+ </tbody>
480
+ </table>
481
+
482
+ <pre><code class="language-text">Decision Tree — Chọn Model Family:
483
+
484
+ ┌─ Cần sinh text dài?
485
+ │ YES → Decoder-only (GPT, LLaMA)
486
+
487
+ Task ───────────┤
488
+ │ ┌─ Input→Output seq?
489
+ │ │ YES → Encoder-Decoder (T5, BART)
490
+ NO ─┤
491
+ │ NO → Encoder-only (BERT)
492
+ │ (classification, NER, embedding)
493
+ └──────────────────────────────────
494
+ </code></pre>
495
+
496
+ <blockquote><p><strong>Exam tip:</strong> Câu hỏi thường gặp: "Cho task X, nên dùng model family nào?" Rule nhanh: (1) Understanding / classification → BERT, (2) Generation → GPT, (3) Seq2seq (translation, summarization) → T5. Nhưng lưu ý: GPT đủ lớn cũng làm được mọi task qua <strong>prompting</strong>.</p></blockquote>
497
+
498
+ <h2 id="tokenization">6. Tokenization</h2>
499
+
500
+ <h3 id="token-vs-word">6.1 Token vs Word</h3>
501
+
502
+ <p>Mô hình ngôn ngữ không làm việc với "từ" mà với <strong>tokens</strong> — đơn vị nhỏ hơn hoặc bằng một từ. Tokenization quyết định cách chia text thành tokens.</p>
503
+
504
+ <pre><code class="language-text">Ví dụ tokenization cho "unbelievable":
505
+
506
+ Word-level: ["unbelievable"] → vocab quá lớn, OOV nhiều
507
+ Character: ["u","n","b","e",...] → sequence quá dài
508
+ Subword (BPE): ["un", "believ", "able"] → cân bằng vocab size và seq length
509
+ </code></pre>
510
+
511
+ <h3 id="bpe-wordpiece-sentencepiece">6.2 BPE, WordPiece, SentencePiece</h3>
512
+
513
+ <table>
514
+ <thead>
515
+ <tr><th>Algorithm</th><th>Sử dụng bởi</th><th>Cách hoạt động</th><th>Đặc điểm</th></tr>
516
+ </thead>
517
+ <tbody>
518
+ <tr><td><strong>BPE</strong> (Byte-Pair Encoding)</td><td>GPT-2/3/4, RoBERTa</td><td>Merge cặp byte phổ biến nhất lặp lại</td><td>Bottom-up, greedy merging</td></tr>
519
+ <tr><td><strong>WordPiece</strong></td><td>BERT, DistilBERT</td><td>Merge pair maximize likelihood</td><td>Dùng <code>##</code> prefix cho subword</td></tr>
520
+ <tr><td><strong>SentencePiece</strong></td><td>T5, LLaMA, mT5</td><td>Unigram LM hoặc BPE trên raw text</td><td>Language-agnostic, không cần pre-tokenize</td></tr>
521
+ </tbody>
522
+ </table>
523
+
524
+ <pre><code class="language-text">Ví dụ so sánh:
525
+
526
+ Input: "I love tokenization"
527
+
528
+ BPE (GPT-2): ["I", " love", " token", "ization"]
529
+ WordPiece (BERT): ["I", "love", "token", "##ization"]
530
+ SentencePiece (T5): ["▁I", "▁love", "▁token", "ization"]
531
+
532
+ WordPiece dùng ## cho continuation
533
+ SentencePiece dùng ▁ cho word start
534
+ </code></pre>
535
+
536
+ <h3 id="tokenizer-code">6.3 Code: Tokenization với HuggingFace</h3>
537
+
538
+ <pre><code class="language-python">from transformers import AutoTokenizer
539
+
540
+ # Load tokenizers của mỗi model family
541
+ bert_tok = AutoTokenizer.from_pretrained("bert-base-uncased")
542
+ gpt2_tok = AutoTokenizer.from_pretrained("gpt2")
543
+ t5_tok = AutoTokenizer.from_pretrained("t5-small")
544
+
545
+ text = "Transformers are amazing for NLP tasks!"
546
+
547
+ # BERT (WordPiece)
548
+ bert_tokens = bert_tok.tokenize(text)
549
+ print(f"BERT: {bert_tokens}")
550
+ # ['transformers', 'are', 'amazing', 'for', 'nl', '##p', 'tasks', '!']
551
+
552
+ # GPT-2 (BPE)
553
+ gpt2_tokens = gpt2_tok.tokenize(text)
554
+ print(f"GPT-2: {gpt2_tokens}")
555
+ # ['Trans', 'formers', 'Ġare', 'Ġamazing', 'Ġfor', 'ĠNLP', 'Ġtasks', '!']
556
+
557
+ # T5 (SentencePiece)
558
+ t5_tokens = t5_tok.tokenize(text)
559
+ print(f"T5: {t5_tokens}")
560
+ # ['▁Transform', 'ers', '▁are', '▁amazing', '▁for', '▁NLP', '▁tasks', '!']
561
+
562
+ # Encode → token IDs
563
+ ids = bert_tok.encode(text, return_tensors="pt")
564
+ print(f"Token IDs shape: {ids.shape}")
565
+
566
+ # Decode ngược lại
567
+ decoded = bert_tok.decode(ids[0])
568
+ print(f"Decoded: {decoded}")
569
+ </code></pre>
570
+
571
+ <p>Vocab size ảnh hưởng đến embedding size và model capacity:</p>
572
+
573
+ <table>
574
+ <thead>
575
+ <tr><th>Model</th><th>Tokenizer</th><th>Vocab Size</th><th>Ghi chú</th></tr>
576
+ </thead>
577
+ <tbody>
578
+ <tr><td>BERT-base</td><td>WordPiece</td><td>30,522</td><td>Lowercase English</td></tr>
579
+ <tr><td>GPT-2</td><td>BPE</td><td>50,257</td><td>Case-sensitive</td></tr>
580
+ <tr><td>T5</td><td>SentencePiece</td><td>32,100</td><td>Multilingual capable</td></tr>
581
+ <tr><td>LLaMA-2</td><td>SentencePiece</td><td>32,000</td><td>BPE variant</td></tr>
582
+ <tr><td>GPT-4</td><td>BPE (cl100k)</td><td>100,256</td><td>Optimized for code + multilingual</td></tr>
583
+ </tbody>
584
+ </table>
585
+
586
+ <h2 id="nlp-tasks">7. NLP Tasks Mapping</h2>
587
+
588
+ <h3 id="task-overview">7.1 Task → Model → Output</h3>
589
+
590
+ <table>
591
+ <thead>
592
+ <tr><th>NLP Task</th><th>Task Type</th><th>Best Model Family</th><th>Output</th></tr>
593
+ </thead>
594
+ <tbody>
595
+ <tr><td>Text Classification</td><td>Sequence classification</td><td>Encoder (BERT)</td><td>Single label</td></tr>
596
+ <tr><td>Sentiment Analysis</td><td>Sequence classification</td><td>Encoder (BERT)</td><td>Positive/Negative</td></tr>
597
+ <tr><td>Named Entity Recognition</td><td>Token classification</td><td>Encoder (BERT)</td><td>Label per token</td></tr>
598
+ <tr><td>Question Answering</td><td>Extractive / Generative</td><td>Encoder or Enc-Dec</td><td>Span or text</td></tr>
599
+ <tr><td>Summarization</td><td>Seq2seq generation</td><td>Enc-Dec (T5, BART)</td><td>Summary text</td></tr>
600
+ <tr><td>Translation</td><td>Seq2seq generation</td><td>Enc-Dec (T5, mBART)</td><td>Translated text</td></tr>
601
+ <tr><td>Text Generation</td><td>Autoregressive</td><td>Decoder (GPT)</td><td>Continuation</td></tr>
602
+ <tr><td>Code Generation</td><td>Autoregressive</td><td>Decoder (CodeGen)</td><td>Code</td></tr>
603
+ </tbody>
604
+ </table>
605
+
606
+ <h3 id="classification-code">7.2 Code: Fine-tune BERT cho Text Classification</h3>
607
+
608
+ <pre><code class="language-python">import torch
609
+ import torch.nn as nn
610
+ from transformers import BertModel, BertTokenizer
611
+
612
+ class BertClassifier(nn.Module):
613
+ def __init__(self, num_classes, model_name='bert-base-uncased'):
614
+ super().__init__()
615
+ self.bert = BertModel.from_pretrained(model_name)
616
+ self.dropout = nn.Dropout(0.1)
617
+ self.classifier = nn.Linear(self.bert.config.hidden_size, num_classes)
618
+
619
+ def forward(self, input_ids, attention_mask):
620
+ # BERT output: last_hidden_state, pooler_output
621
+ outputs = self.bert(input_ids=input_ids,
622
+ attention_mask=attention_mask)
623
+
624
+ # Lấy [CLS] token representation cho classification
625
+ cls_output = outputs.pooler_output # (batch, hidden_size)
626
+ cls_output = self.dropout(cls_output)
627
+ logits = self.classifier(cls_output) # (batch, num_classes)
628
+ return logits
629
+
630
+ # Sử dụng
631
+ tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
632
+ model = BertClassifier(num_classes=3)
633
+
634
+ # Tokenize input
635
+ text = "This movie was absolutely wonderful!"
636
+ encoded = tokenizer(text, return_tensors='pt', padding=True,
637
+ truncation=True, max_length=128)
638
+
639
+ # Forward pass
640
+ logits = model(encoded['input_ids'], encoded['attention_mask'])
641
+ prediction = torch.argmax(logits, dim=-1)
642
+ print(f"Predicted class: {prediction.item()}")
643
+ </code></pre>
644
+
645
+ <pre><code class="language-python"># Training loop cho BERT classifier
646
+ from torch.utils.data import DataLoader
647
+
648
+ optimizer = torch.optim.AdamW(model.parameters(), lr=2e-5)
649
+ loss_fn = nn.CrossEntropyLoss()
650
+
651
+ model.train()
652
+ for epoch in range(3):
653
+ total_loss = 0
654
+ for batch in train_loader:
655
+ optimizer.zero_grad()
656
+
657
+ logits = model(batch['input_ids'], batch['attention_mask'])
658
+ loss = loss_fn(logits, batch['labels'])
659
+
660
+ loss.backward()
661
+ torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
662
+ optimizer.step()
663
+
664
+ total_loss += loss.item()
665
+
666
+ print(f"Epoch {epoch+1}, Loss: {total_loss / len(train_loader):.4f}")
667
+ </code></pre>
668
+
669
+ <blockquote><p><strong>Exam tip:</strong> Khi fine-tune BERT, 3 điểm quan trọng: (1) <strong>Learning rate nhỏ</strong> (2e-5 đến 5e-5) vì model đã pre-trained, (2) <strong>Gradient clipping</strong> (<code>clip_grad_norm_</code>) để ổn định training, (3) Dùng <strong>pooler_output</strong> (CLS token) cho classification, <strong>last_hidden_state</strong> cho token-level tasks (NER).</p></blockquote>
670
+
671
+ <h2 id="cheat-sheet">8. Cheat Sheet</h2>
672
+
673
+ <table>
674
+ <thead>
675
+ <tr><th>Concept</th><th>Key Formula / Pattern</th><th>Ghi nhớ</th></tr>
676
+ </thead>
677
+ <tbody>
678
+ <tr><td>Scaled Dot-Product</td><td><code>softmax(QK^T / √d_k) V</code></td><td>Chia √d_k để tránh softmax bão hòa</td></tr>
679
+ <tr><td>Multi-Head</td><td>Split → h × Attention → Concat → Linear</td><td>d_k = d_model / num_heads</td></tr>
680
+ <tr><td>Positional Encoding</td><td>sin/cos functions</td><td>Reused trong Diffusion timestep embedding</td></tr>
681
+ <tr><td>Encoder (BERT)</td><td>Bidirectional, MLM</td><td>Understanding tasks: NER, QA, classification</td></tr>
682
+ <tr><td>Decoder (GPT)</td><td>Causal mask, next-token</td><td>Generation tasks: text, code</td></tr>
683
+ <tr><td>Enc-Dec (T5)</td><td>Cross-attention, seq2seq</td><td>Translation, summarization</td></tr>
684
+ <tr><td>BPE</td><td>Merge frequent byte pairs</td><td>GPT family</td></tr>
685
+ <tr><td>WordPiece</td><td>Maximize likelihood merge</td><td>BERT family, dùng ## prefix</td></tr>
686
+ <tr><td>SentencePiece</td><td>Language-agnostic on raw text</td><td>T5, LLaMA, dùng ▁ prefix</td></tr>
687
+ <tr><td>Causal Mask</td><td>Lower-triangular matrix</td><td>GPT: mỗi token chỉ thấy trước nó</td></tr>
688
+ <tr><td>Layer Norm</td><td>Normalize across features</td><td>Dùng trong Transformer (không phải BatchNorm)</td></tr>
689
+ <tr><td>Fine-tune LR</td><td>2e-5 → 5e-5</td><td>LR nhỏ vì pre-trained weights</td></tr>
690
+ </tbody>
691
+ </table>
692
+
693
+ <h2 id="practice-questions">9. Practice Questions</h2>
694
+
695
+ <p>Các câu hỏi dạng coding assessment tương tự NVIDIA DLI:</p>
696
+
697
+ <p><strong>Q1:</strong> Implement <code>scaled_dot_product_attention</code> function. Hàm nhận Q, K, V tensors và optional mask, trả về output và attention weights.</p>
698
+
699
+ <details>
700
+ <summary>Xem đáp án Q1</summary>
701
+
702
+ <pre><code class="language-python">import torch
703
+ import torch.nn.functional as F
704
+ import math
705
+
706
+ def scaled_dot_product_attention(Q, K, V, mask=None):
707
+ """
708
+ Args:
709
+ Q: (batch, seq_len, d_k)
710
+ K: (batch, seq_len, d_k)
711
+ V: (batch, seq_len, d_v)
712
+ mask: optional (batch, 1, seq_len) or (batch, seq_len, seq_len)
713
+ Returns:
714
+ output: (batch, seq_len, d_v)
715
+ attn_weights: (batch, seq_len, seq_len)
716
+ """
717
+ d_k = Q.size(-1)
718
+
719
+ # Compute attention scores
720
+ scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(d_k)
721
+
722
+ # Apply mask if provided
723
+ if mask is not None:
724
+ scores = scores.masked_fill(mask == 0, float('-inf'))
725
+
726
+ # Softmax over last dimension (key dimension)
727
+ attn_weights = F.softmax(scores, dim=-1)
728
+
729
+ # Weighted sum of values
730
+ output = torch.matmul(attn_weights, V)
731
+
732
+ return output, attn_weights
733
+
734
+ # Kiểm tra:
735
+ B, S, D = 2, 4, 64
736
+ Q = torch.randn(B, S, D)
737
+ K = torch.randn(B, S, D)
738
+ V = torch.randn(B, S, D)
739
+ out, w = scaled_dot_product_attention(Q, K, V)
740
+ assert out.shape == (B, S, D)
741
+ assert w.shape == (B, S, S)
742
+ assert torch.allclose(w.sum(dim=-1), torch.ones(B, S), atol=1e-6)
743
+ print("All assertions passed!")
744
+ </code></pre>
745
+
746
+ <p><em>Giải thích: Điểm mấu chốt là (1) <code>K.transpose(-2, -1)</code> để matrix multiply đúng, (2) chia <code>math.sqrt(d_k)</code> để scale, (3) <code>masked_fill</code> với <code>-inf</code> trước softmax, (4) softmax trên <code>dim=-1</code>. Lỗi phổ biến: quên transpose K hoặc softmax sai dim.</em></p>
747
+ </details>
748
+
749
+ <p><strong>Q2:</strong> Điều gì xảy ra nếu loại bỏ Positional Encoding khỏi Transformer? Viết code chứng minh.</p>
750
+
751
+ <details>
752
+ <summary>Xem đáp án Q2</summary>
753
+
754
+ <pre><code class="language-python">import torch
755
+
756
+ # Self-attention KHÔNG có positional encoding
757
+ # → output là permutation invariant (thứ tự token không ảnh hưởng)
758
+
759
+ def self_attention_no_pos(x):
760
+ """x: (batch, seq_len, d_model)"""
761
+ d_k = x.size(-1)
762
+ scores = torch.matmul(x, x.transpose(-2, -1)) / (d_k ** 0.5)
763
+ weights = torch.softmax(scores, dim=-1)
764
+ return torch.matmul(weights, x)
765
+
766
+ # Tạo input
767
+ x = torch.randn(1, 4, 8) # 4 tokens, d_model=8
768
+
769
+ # Output gốc
770
+ out1 = self_attention_no_pos(x)
771
+
772
+ # Shuffle thứ tự tokens: [0,1,2,3] → [2,0,3,1]
773
+ perm = [2, 0, 3, 1]
774
+ x_shuffled = x[:, perm, :]
775
+ out2 = self_attention_no_pos(x_shuffled)
776
+
777
+ # Kiểm tra: output cũng bị shuffle theo cùng thứ tự
778
+ inv_perm = [1, 3, 0, 2] # inverse permutation
779
+ out2_reordered = out2[:, inv_perm, :]
780
+
781
+ print(f"Difference: {(out1 - out2_reordered).abs().max().item():.10f}")
782
+ # → gần 0! Attention không phân biệt thứ tự
783
+ # → "The cat sat on mat" = "mat on sat cat The"
784
+ # Đó là lý do PHẢI có Positional Encoding!
785
+ </code></pre>
786
+
787
+ <p><em>Giải thích: Không có Positional Encoding, self-attention là <strong>permutation equivariant</strong> — nó xử lý "The cat sat" giống hệt "sat The cat". Positional Encoding phá vỡ tính đối xứng này, cho phép model phân biệt thứ tự tokens. Trong Diffusion Models, cùng concept này được dùng cho timestep embedding.</em></p>
788
+ </details>
789
+
790
+ <p><strong>Q3:</strong> GPT sử dụng causal masking. Hãy viết code tạo causal mask và giải thích mỗi phần tử trong ma trận mask.</p>
791
+
792
+ <details>
793
+ <summary>Xem đáp án Q3</summary>
794
+
795
+ <pre><code class="language-python">import torch
796
+
797
+ def create_causal_mask(seq_len):
798
+ """
799
+ Tạo causal (look-ahead) mask cho decoder.
800
+ mask[i][j] = 1 nếu token i được attend vào token j (j <= i)
801
+ mask[i][j] = 0 nếu token i KHÔNG được nhìn token j (j > i)
802
+ """
803
+ mask = torch.tril(torch.ones(seq_len, seq_len))
804
+ return mask
805
+
806
+ seq_len = 5
807
+ mask = create_causal_mask(seq_len)
808
+ print("Causal Mask:")
809
+ print(mask)
810
+ # tensor([[1., 0., 0., 0., 0.],
811
+ # [1., 1., 0., 0., 0.],
812
+ # [1., 1., 1., 0., 0.],
813
+ # [1., 1., 1., 1., 0.],
814
+ # [1., 1., 1., 1., 1.]])
815
+
816
+ # Áp dụng vào attention
817
+ def causal_attention(Q, K, V):
818
+ d_k = Q.size(-1)
819
+ seq_len = Q.size(1)
820
+ scores = torch.matmul(Q, K.transpose(-2, -1)) / (d_k ** 0.5)
821
+
822
+ # Apply causal mask
823
+ mask = create_causal_mask(seq_len).unsqueeze(0) # (1, S, S)
824
+ scores = scores.masked_fill(mask == 0, float('-inf'))
825
+ # Kết quả: vị trí tương lai → -inf → softmax → 0
826
+
827
+ weights = torch.softmax(scores, dim=-1)
828
+ print("Attention weights (causal):")
829
+ print(weights[0].detach())
830
+ # Row 0: [1.0, 0.0, 0.0, 0.0, 0.0] ← token 0 chỉ thấy chính nó
831
+ # Row 1: [0.4, 0.6, 0.0, 0.0, 0.0] ← token 1 thấy token 0,1
832
+ # Row 4: [0.1, 0.2, 0.3, 0.2, 0.2] ← token 4 thấy tất cả
833
+
834
+ return torch.matmul(weights, V)
835
+
836
+ B, S, D = 1, 5, 32
837
+ Q = torch.randn(B, S, D)
838
+ K = torch.randn(B, S, D)
839
+ V = torch.randn(B, S, D)
840
+ out = causal_attention(Q, K, V)
841
+ print(f"Output shape: {out.shape}") # (1, 5, 32)
842
+ </code></pre>
843
+
844
+ <p><em>Giải thích: Causal mask là ma trận tam giác dưới (<code>torch.tril</code>). Vị trí <code>mask[i][j]=0</code> (j > i) được fill bằng <code>-inf</code> trước softmax, biến thành 0 sau softmax. Điều này đảm bảo mỗi token chỉ attend vào các token trước nó — essential cho autoregressive generation trong GPT.</em></p>
845
+ </details>
846
+
847
+ <p><strong>Q4:</strong> Cho các use cases sau, hãy chọn model family phù hợp nhất và giải thích lý do:</p>
848
+ <ul>
849
+ <li>(a) Phân loại email spam/not-spam</li>
850
+ <li>(b) Dịch tiếng Việt sang tiếng Anh</li>
851
+ <li>(c) Chatbot tạo text tự do</li>
852
+ <li>(d) Trích xuất tên người từ văn bản (NER)</li>
853
+ </ul>
854
+
855
+ <details>
856
+ <summary>Xem đáp án Q4</summary>
857
+
858
+ <pre><code class="language-python"># Mapping use cases → model families
859
+
860
+ tasks = {
861
+ "(a) Email spam classification": {
862
+ "model_family": "Encoder-only (BERT)",
863
+ "reason": "Classification task — cần hiểu toàn bộ email "
864
+ "(bidirectional). Output = 1 label (spam/not-spam). "
865
+ "BERT + Linear classifier head.",
866
+ "code_hint": "BertForSequenceClassification.from_pretrained('bert-base-uncased', num_labels=2)"
867
+ },
868
+ "(b) Vietnamese → English translation": {
869
+ "model_family": "Encoder-Decoder (T5, mBART)",
870
+ "reason": "Seq2seq task — input sequence (Vietnamese) → output "
871
+ "sequence (English). Encoder hiểu input, decoder sinh "
872
+ "output. T5 hoặc mBART cho multilingual.",
873
+ "code_hint": "T5ForConditionalGeneration.from_pretrained('t5-base')"
874
+ },
875
+ "(c) Free-form chatbot": {
876
+ "model_family": "Decoder-only (GPT, LLaMA)",
877
+ "reason": "Autoregressive generation — sinh text token-by-token, "
878
+ "không cần encoder riêng. GPT/LLaMA với instruction "
879
+ "tuning cho chatbot use case.",
880
+ "code_hint": "AutoModelForCausalLM.from_pretrained('meta-llama/Llama-2-7b-chat-hf')"
881
+ },
882
+ "(d) Named Entity Recognition": {
883
+ "model_family": "Encoder-only (BERT)",
884
+ "reason": "Token classification — cần gán label cho TỪNG token "
885
+ "(B-PER, I-PER, O, B-LOC,...). BERT bidirectional "
886
+ "giúp mỗi token nhìn context cả hai phía.",
887
+ "code_hint": "BertForTokenClassification.from_pretrained('bert-base-uncased', num_labels=9)"
888
+ }
889
+ }
890
+
891
+ for task, info in tasks.items():
892
+ print(f"\n{task}")
893
+ print(f" → {info['model_family']}")
894
+ print(f" Lý do: {info['reason']}")
895
+ print(f" Code: {info['code_hint']}")
896
+ </code></pre>
897
+
898
+ <p><em>Giải thích: Rule tổng quát: (1) Nếu output là 1 label cho toàn bộ input → Encoder (BERT), (2) Nếu output là label cho mỗi token → Encoder (BERT) + token classification head, (3) Nếu output là sequence khác input language/format → Encoder-Decoder (T5), (4) Nếu cần sinh text liên tục → Decoder (GPT). Tuy nhiên, trong thực tế, LLM decoder-only đủ lớn (GPT-4, LLaMA-70B) có thể làm tốt mọi task qua prompting.</em></p>
899
+ </details>
900
+
901
+ <p><strong>Q5:</strong> Debug lỗi sau trong Transformer. Code có bug ở dimension — tìm và sửa:</p>
902
+
903
+ <pre><code class="language-python"># BUG CODE — Tìm và sửa lỗi
904
+ class BrokenMultiHeadAttention(nn.Module):
905
+ def __init__(self, d_model=512, num_heads=8):
906
+ super().__init__()
907
+ self.d_model = d_model
908
+ self.num_heads = num_heads
909
+ self.d_k = d_model // num_heads # 64
910
+
911
+ self.W_q = nn.Linear(d_model, d_model)
912
+ self.W_k = nn.Linear(d_model, d_model)
913
+ self.W_v = nn.Linear(d_model, d_model)
914
+ self.W_o = nn.Linear(d_model, d_model)
915
+
916
+ def forward(self, x, mask=None):
917
+ B = x.size(0)
918
+ Q = self.W_q(x)
919
+ K = self.W_k(x)
920
+ V = self.W_v(x)
921
+
922
+ # BUG: reshape sai thứ tự dimensions
923
+ Q = Q.view(B, self.num_heads, -1, self.d_k) # ← Sai!
924
+ K = K.view(B, self.num_heads, -1, self.d_k)
925
+ V = V.view(B, self.num_heads, -1, self.d_k)
926
+
927
+ scores = torch.matmul(Q, K.transpose(-2, -1)) / (self.d_k ** 0.5)
928
+ weights = torch.softmax(scores, dim=-1)
929
+ context = torch.matmul(weights, V)
930
+
931
+ # BUG: quên contiguous() trước view
932
+ context = context.transpose(1, 2).view(B, -1, self.d_model) # ← Sai!
933
+ return self.W_o(context)
934
+ </code></pre>
935
+
936
+ <details>
937
+ <summary>Xem đáp án Q5</summary>
938
+
939
+ <pre><code class="language-python">import torch
940
+ import torch.nn as nn
941
+
942
+ class FixedMultiHeadAttention(nn.Module):
943
+ def __init__(self, d_model=512, num_heads=8):
944
+ super().__init__()
945
+ self.d_model = d_model
946
+ self.num_heads = num_heads
947
+ self.d_k = d_model // num_heads
948
+
949
+ self.W_q = nn.Linear(d_model, d_model)
950
+ self.W_k = nn.Linear(d_model, d_model)
951
+ self.W_v = nn.Linear(d_model, d_model)
952
+ self.W_o = nn.Linear(d_model, d_model)
953
+
954
+ def forward(self, x, mask=None):
955
+ B = x.size(0)
956
+ Q = self.W_q(x)
957
+ K = self.W_k(x)
958
+ V = self.W_v(x)
959
+
960
+ # FIX 1: Đúng thứ tự: (B, S, d_model) → (B, S, h, d_k) → (B, h, S, d_k)
961
+ # Phải view thành (B, S, h, d_k) TRƯỚC, rồi transpose(1,2)
962
+ Q = Q.view(B, -1, self.num_heads, self.d_k).transpose(1, 2)
963
+ K = K.view(B, -1, self.num_heads, self.d_k).transpose(1, 2)
964
+ V = V.view(B, -1, self.num_heads, self.d_k).transpose(1, 2)
965
+
966
+ scores = torch.matmul(Q, K.transpose(-2, -1)) / (self.d_k ** 0.5)
967
+ weights = torch.softmax(scores, dim=-1)
968
+ context = torch.matmul(weights, V) # (B, h, S, d_k)
969
+
970
+ # FIX 2: Thêm .contiguous() sau transpose trước .view()
971
+ context = context.transpose(1, 2).contiguous().view(B, -1, self.d_model)
972
+ return self.W_o(context)
973
+
974
+ # Verify
975
+ model = FixedMultiHeadAttention(d_model=512, num_heads=8)
976
+ x = torch.randn(2, 10, 512)
977
+ out = model(x)
978
+ print(f"Output shape: {out.shape}") # (2, 10, 512) ✓
979
+ assert out.shape == (2, 10, 512)
980
+ print("Fixed! All correct.")
981
+ </code></pre>
982
+
983
+ <p><em>Giải thích: Có 2 lỗi: <strong>Bug 1:</strong> <code>view(B, num_heads, -1, d_k)</code> sai vì tensor layout trong memory là <code>(B, S, d_model)</code>. Phải view thành <code>(B, S, num_heads, d_k)</code> trước rồi <code>transpose(1, 2)</code> để có <code>(B, num_heads, S, d_k)</code>. View trực tiếp thành <code>(B, num_heads, S, d_k)</code> sẽ trộn lẫn data giữa các heads. <strong>Bug 2:</strong> Sau <code>transpose(1, 2)</code>, tensor không còn contiguous trong memory. Gọi <code>.view()</code> trên non-contiguous tensor gây RuntimeError. Phải thêm <code>.contiguous()</code> trước <code>.view()</code>.</em></p>
984
+ </details>