@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,790 @@
1
+ ---
2
+ id: 019c9619-nv01-p1-l01
3
+ title: 'Bài 1: PyTorch & Neural Network Fundamentals'
4
+ slug: bai-1-pytorch-neural-network-fundamentals
5
+ description: >-
6
+ PyTorch tensors, autograd, nn.Module. Build neural network from scratch.
7
+ Training loop, loss functions, optimizers. GPU acceleration basics.
8
+ CNN architecture, pooling, batch normalization.
9
+ duration_minutes: 90
10
+ is_free: true
11
+ video_url: null
12
+ sort_order: 1
13
+ section_title: "Part 1: Deep Learning Foundations"
14
+ course:
15
+ id: 019c9619-nv01-7001-c001-nv0100000001
16
+ title: 'Luyện thi NVIDIA DLI — Generative AI with Diffusion Models & LLMs'
17
+ slug: luyen-thi-nvidia-dli-generative-ai
18
+ ---
19
+
20
+ <h2 id="gioi-thieu">1. Giới thiệu</h2>
21
+
22
+ <p>Bài học đầu tiên trong series luyện thi <strong>NVIDIA DLI — Generative AI</strong> sẽ trang bị cho bạn nền tảng <strong>PyTorch</strong> vững chắc. Đây là framework chính được sử dụng trong toàn bộ khóa học DLI, từ <strong>Diffusion Models</strong> đến <strong>Large Language Models (LLMs)</strong>.</p>
23
+
24
+ <p>Trong bài assessment của NVIDIA DLI, bạn sẽ phải viết code trực tiếp — không phải trắc nghiệm. Vì vậy, nắm vững các pattern cơ bản của PyTorch là điều kiện tiên quyết.</p>
25
+
26
+ <blockquote><p><strong>Exam tip:</strong> NVIDIA DLI assessment yêu cầu bạn viết và debug code PyTorch trực tiếp. Hãy chắc chắn bạn có thể viết <strong>training loop</strong>, <strong>nn.Module</strong>, và thao tác <strong>tensor</strong> mà không cần nhìn tài liệu.</p></blockquote>
27
+
28
+ <figure><img src="/storage/uploads/2026/04/nvidia-dli-bai1-neural-network-architecture.png" alt="Kiến trúc Deep Neural Network — Input Layer, Hidden Layers, Output Layer, Backpropagation" loading="lazy" /><figcaption>Kiến trúc Deep Neural Network — Input Layer, Hidden Layers, Output Layer, Backpropagation</figcaption></figure>
29
+
30
+ <h2 id="pytorch-tensors-autograd">2. PyTorch Tensors & Autograd</h2>
31
+
32
+ <h3 id="tensor-basics">2.1 Tensor Basics</h3>
33
+
34
+ <p><strong>Tensor</strong> là cấu trúc dữ liệu cốt lõi của PyTorch — tương tự NumPy array nhưng có thể chạy trên <strong>GPU</strong> và hỗ trợ <strong>automatic differentiation</strong>.</p>
35
+
36
+ <pre><code class="language-python">import torch
37
+
38
+ # Tạo tensor từ list
39
+ x = torch.tensor([1.0, 2.0, 3.0])
40
+
41
+ # Tạo tensor với shape cụ thể
42
+ zeros = torch.zeros(3, 4) # shape: (3, 4)
43
+ ones = torch.ones(2, 3, 4) # shape: (2, 3, 4)
44
+ rand = torch.randn(64, 3, 32, 32) # batch of 64 RGB 32x32 images
45
+
46
+ # Kiểm tra shape và dtype
47
+ print(rand.shape) # torch.Size([64, 3, 32, 32])
48
+ print(rand.dtype) # torch.float32
49
+ print(rand.device) # cpu
50
+ </code></pre>
51
+
52
+ <h3 id="tensor-operations">2.2 Tensor Operations & Broadcasting</h3>
53
+
54
+ <p>PyTorch hỗ trợ <strong>broadcasting</strong> tương tự NumPy — cho phép thực hiện phép tính giữa tensors có shape khác nhau.</p>
55
+
56
+ <pre><code class="language-python"># Reshape operations
57
+ x = torch.randn(2, 3, 4)
58
+ y = x.view(2, 12) # reshape thành (2, 12)
59
+ z = x.permute(0, 2, 1) # swap dims: (2, 4, 3)
60
+ w = x.unsqueeze(0) # thêm dim: (1, 2, 3, 4)
61
+
62
+ # Matrix multiplication
63
+ a = torch.randn(3, 4)
64
+ b = torch.randn(4, 5)
65
+ c = a @ b # shape: (3, 5)
66
+ # hoặc: c = torch.matmul(a, b)
67
+
68
+ # Broadcasting
69
+ x = torch.randn(64, 256) # (batch, features)
70
+ bias = torch.randn(256) # (features,)
71
+ result = x + bias # bias được broadcast: (64, 256)
72
+ </code></pre>
73
+
74
+ <table>
75
+ <thead>
76
+ <tr><th>Operation</th><th>Syntax</th><th>Ghi chú</th></tr>
77
+ </thead>
78
+ <tbody>
79
+ <tr><td>Reshape</td><td><code>x.view()</code> / <code>x.reshape()</code></td><td><code>view</code> yêu cầu contiguous memory</td></tr>
80
+ <tr><td>Transpose</td><td><code>x.permute()</code> / <code>x.T</code></td><td><code>permute</code> linh hoạt hơn cho nhiều dims</td></tr>
81
+ <tr><td>Add dim</td><td><code>x.unsqueeze(dim)</code></td><td>Thường dùng để chuẩn bị broadcasting</td></tr>
82
+ <tr><td>Remove dim</td><td><code>x.squeeze(dim)</code></td><td>Xóa dim có size = 1</td></tr>
83
+ <tr><td>Matrix mul</td><td><code>a @ b</code></td><td>Equivalent to <code>torch.matmul</code></td></tr>
84
+ <tr><td>Concat</td><td><code>torch.cat([a, b], dim=0)</code></td><td>Nối theo dim chỉ định</td></tr>
85
+ </tbody>
86
+ </table>
87
+
88
+ <h3 id="autograd">2.3 Autograd — Automatic Differentiation</h3>
89
+
90
+ <p><strong>Autograd</strong> là hệ thống tự động tính gradient của PyTorch. Khi bạn set <code>requires_grad=True</code>, PyTorch sẽ theo dõi mọi phép tính trên tensor đó và xây dựng <strong>computational graph</strong>.</p>
91
+
92
+ <pre><code class="language-python"># Autograd cơ bản
93
+ x = torch.tensor([2.0, 3.0], requires_grad=True)
94
+ y = x ** 2 + 3 * x # y = x² + 3x
95
+ loss = y.sum() # scalar output
96
+ loss.backward() # tính gradient
97
+
98
+ print(x.grad) # dy/dx = 2x + 3 → tensor([7., 9.])
99
+ </code></pre>
100
+
101
+ <pre><code class="language-text">Computational Graph:
102
+
103
+ x (requires_grad=True)
104
+
105
+ ├──→ x ** 2 ──→ + ──→ y ──→ sum() ──→ loss
106
+ │ ↑ │
107
+ └──→ 3 * x ─────┘ backward()
108
+
109
+ x.grad = 2x + 3
110
+ </code></pre>
111
+
112
+ <blockquote><p><strong>Exam tip:</strong> Trong DLI assessment, bạn có thể gặp lỗi "trying to backward through the graph a second time". Giải pháp: dùng <code>loss.backward(retain_graph=True)</code> hoặc tính lại forward pass. Đây là lỗi rất phổ biến khi làm bài.</p></blockquote>
113
+
114
+ <h2 id="nn-module">3. nn.Module & Building Networks</h2>
115
+
116
+ <h3 id="nn-module-pattern">3.1 nn.Module Pattern</h3>
117
+
118
+ <p>Mọi neural network trong PyTorch đều kế thừa từ <code>nn.Module</code>. Đây là pattern bắt buộc phải thuộc lòng:</p>
119
+
120
+ <pre><code class="language-python">import torch.nn as nn
121
+
122
+ class SimpleNet(nn.Module):
123
+ def __init__(self, input_dim, hidden_dim, output_dim):
124
+ super().__init__() # PHẢI gọi super().__init__()
125
+ self.fc1 = nn.Linear(input_dim, hidden_dim)
126
+ self.relu = nn.ReLU()
127
+ self.fc2 = nn.Linear(hidden_dim, output_dim)
128
+
129
+ def forward(self, x):
130
+ x = self.fc1(x)
131
+ x = self.relu(x)
132
+ x = self.fc2(x)
133
+ return x
134
+
135
+ # Sử dụng
136
+ model = SimpleNet(784, 256, 10)
137
+ x = torch.randn(32, 784) # batch of 32
138
+ output = model(x) # shape: (32, 10)
139
+ </code></pre>
140
+
141
+ <h3 id="common-layers">3.2 Common Layers</h3>
142
+
143
+ <table>
144
+ <thead>
145
+ <tr><th>Layer</th><th>Công dụng</th><th>Params chính</th></tr>
146
+ </thead>
147
+ <tbody>
148
+ <tr><td><code>nn.Linear(in, out)</code></td><td>Fully connected layer</td><td>in_features, out_features</td></tr>
149
+ <tr><td><code>nn.Conv2d(in_ch, out_ch, k)</code></td><td>2D convolution</td><td>in_channels, out_channels, kernel_size</td></tr>
150
+ <tr><td><code>nn.BatchNorm2d(ch)</code></td><td>Batch normalization</td><td>num_features</td></tr>
151
+ <tr><td><code>nn.GroupNorm(g, ch)</code></td><td>Group normalization</td><td>num_groups, num_channels</td></tr>
152
+ <tr><td><code>nn.ReLU()</code></td><td>Activation function</td><td>inplace (optional)</td></tr>
153
+ <tr><td><code>nn.Dropout(p)</code></td><td>Regularization</td><td>p = drop probability</td></tr>
154
+ <tr><td><code>nn.Embedding(V, D)</code></td><td>Token embedding</td><td>num_embeddings, embedding_dim</td></tr>
155
+ </tbody>
156
+ </table>
157
+
158
+ <h3 id="nn-sequential">3.3 nn.Sequential — Quick Models</h3>
159
+
160
+ <p>Với mô hình đơn giản, bạn có thể dùng <code>nn.Sequential</code> thay vì tạo class:</p>
161
+
162
+ <pre><code class="language-python"># Cách nhanh với nn.Sequential
163
+ model = nn.Sequential(
164
+ nn.Linear(784, 256),
165
+ nn.ReLU(),
166
+ nn.Dropout(0.2),
167
+ nn.Linear(256, 128),
168
+ nn.ReLU(),
169
+ nn.Linear(128, 10)
170
+ )
171
+
172
+ # Kiểm tra model
173
+ print(model)
174
+ # Đếm parameters
175
+ total_params = sum(p.numel() for p in model.parameters())
176
+ print(f"Total params: {total_params:,}")
177
+ </code></pre>
178
+
179
+ <h3 id="mlp-mnist">3.4 Code: MLP cho MNIST</h3>
180
+
181
+ <pre><code class="language-python">import torch
182
+ import torch.nn as nn
183
+ from torchvision import datasets, transforms
184
+
185
+ # Data
186
+ transform = transforms.Compose([
187
+ transforms.ToTensor(),
188
+ transforms.Normalize((0.1307,), (0.3081,))
189
+ ])
190
+ train_data = datasets.MNIST('./data', train=True, download=True,
191
+ transform=transform)
192
+ train_loader = torch.utils.data.DataLoader(train_data, batch_size=64,
193
+ shuffle=True)
194
+
195
+ # Model
196
+ class MNISTClassifier(nn.Module):
197
+ def __init__(self):
198
+ super().__init__()
199
+ self.flatten = nn.Flatten()
200
+ self.layers = nn.Sequential(
201
+ nn.Linear(28 * 28, 512),
202
+ nn.ReLU(),
203
+ nn.Dropout(0.2),
204
+ nn.Linear(512, 256),
205
+ nn.ReLU(),
206
+ nn.Dropout(0.2),
207
+ nn.Linear(256, 10)
208
+ )
209
+
210
+ def forward(self, x):
211
+ x = self.flatten(x) # (B, 1, 28, 28) → (B, 784)
212
+ return self.layers(x) # (B, 784) → (B, 10)
213
+ </code></pre>
214
+
215
+ <h2 id="training-loop">4. Training Loop Pattern</h2>
216
+
217
+ <h3 id="loss-functions">4.1 Loss Functions</h3>
218
+
219
+ <table>
220
+ <thead>
221
+ <tr><th>Loss Function</th><th>Dùng khi</th><th>Input shape</th></tr>
222
+ </thead>
223
+ <tbody>
224
+ <tr><td><code>nn.CrossEntropyLoss()</code></td><td>Multi-class classification</td><td>logits (B, C), labels (B,)</td></tr>
225
+ <tr><td><code>nn.MSELoss()</code></td><td>Regression, diffusion noise prediction</td><td>(B, *) vs (B, *)</td></tr>
226
+ <tr><td><code>nn.BCEWithLogitsLoss()</code></td><td>Binary / multi-label classification</td><td>logits (B, C), labels (B, C)</td></tr>
227
+ <tr><td><code>nn.L1Loss()</code></td><td>Regression (robust to outliers)</td><td>(B, *) vs (B, *)</td></tr>
228
+ </tbody>
229
+ </table>
230
+
231
+ <blockquote><p><strong>Exam tip:</strong> <code>nn.CrossEntropyLoss</code> đã bao gồm <strong>softmax</strong> bên trong — KHÔNG cần thêm softmax ở output layer. Đây là lỗi mà nhiều người mắc phải trong assessment. <code>nn.MSELoss</code> sẽ rất quan trọng khi bạn học <strong>Diffusion Models</strong> (predict noise).</p></blockquote>
232
+
233
+ <h3 id="optimizers">4.2 Optimizers</h3>
234
+
235
+ <pre><code class="language-python"># SGD — cơ bản, can thiệp learning rate thủ công
236
+ optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
237
+
238
+ # Adam — phổ biến nhất, adaptive learning rate
239
+ optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
240
+
241
+ # AdamW — Adam + weight decay đúng cách (dùng cho Transformers)
242
+ optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=0.01)
243
+ </code></pre>
244
+
245
+ <table>
246
+ <thead>
247
+ <tr><th>Optimizer</th><th>Khi nào dùng</th><th>Đặc điểm</th></tr>
248
+ </thead>
249
+ <tbody>
250
+ <tr><td><strong>SGD</strong></td><td>CNNs, khi cần kiểm soát tỉ mỉ</td><td>Cần tune lr cẩn thận, thêm momentum</td></tr>
251
+ <tr><td><strong>Adam</strong></td><td>Mặc định cho hầu hết tasks</td><td>Hội tụ nhanh, ít cần tune</td></tr>
252
+ <tr><td><strong>AdamW</strong></td><td>Transformers, LLMs, Diffusion</td><td>Weight decay tách riêng, chuẩn hơn Adam</td></tr>
253
+ </tbody>
254
+ </table>
255
+
256
+ <h3 id="full-training-loop">4.3 Full Training Loop</h3>
257
+
258
+ <p>Đây là pattern quan trọng nhất — bạn phải viết được training loop hoàn chỉnh trong assessment:</p>
259
+
260
+ <pre><code class="language-python">import torch
261
+ import torch.nn as nn
262
+
263
+ # Setup
264
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
265
+ model = MNISTClassifier().to(device)
266
+ criterion = nn.CrossEntropyLoss()
267
+ optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
268
+
269
+ # Training loop
270
+ num_epochs = 10
271
+ for epoch in range(num_epochs):
272
+ model.train() # BẬT training mode (dropout, batchnorm active)
273
+ total_loss = 0
274
+
275
+ for batch_idx, (images, labels) in enumerate(train_loader):
276
+ images, labels = images.to(device), labels.to(device)
277
+
278
+ # Forward pass
279
+ outputs = model(images)
280
+ loss = criterion(outputs, labels)
281
+
282
+ # Backward pass
283
+ optimizer.zero_grad() # QUAN TRỌNG: reset gradients
284
+ loss.backward() # tính gradients
285
+ optimizer.step() # update weights
286
+
287
+ total_loss += loss.item()
288
+
289
+ avg_loss = total_loss / len(train_loader)
290
+ print(f"Epoch [{epoch+1}/{num_epochs}], Loss: {avg_loss:.4f}")
291
+
292
+ # Evaluation
293
+ model.eval() # TẮT dropout, batchnorm dùng running stats
294
+ with torch.no_grad(): # KHÔNG tính gradients → tiết kiệm memory
295
+ correct = 0
296
+ total = 0
297
+ for images, labels in test_loader:
298
+ images, labels = images.to(device), labels.to(device)
299
+ outputs = model(images)
300
+ _, predicted = torch.max(outputs, 1)
301
+ total += labels.size(0)
302
+ correct += (predicted == labels).sum().item()
303
+
304
+ print(f"Accuracy: {100 * correct / total:.2f}%")
305
+ </code></pre>
306
+
307
+ <pre><code class="language-text">Training Loop Flow:
308
+
309
+ ┌─────────────────────────────────────────────┐
310
+ │ FOR EACH EPOCH │
311
+ │ ┌────────────────────────────────────────┐ │
312
+ │ │ FOR EACH BATCH │ │
313
+ │ │ │ │
314
+ │ │ 1. images, labels = batch.to(device) │ │
315
+ │ │ │ │ │
316
+ │ │ 2. outputs = model(images) FORWARD │ │
317
+ │ │ │ │ │
318
+ │ │ 3. loss = criterion(outputs, labels) │ │
319
+ │ │ │ │ │
320
+ │ │ 4. optimizer.zero_grad() RESET │ │
321
+ │ │ │ │ │
322
+ │ │ 5. loss.backward() BACKWARD │ │
323
+ │ │ │ │ │
324
+ │ │ 6. optimizer.step() UPDATE │ │
325
+ │ │ │ │ │
326
+ │ └──────────────┼─────────────────────────┘ │
327
+ │ ▼ │
328
+ │ Next Epoch │
329
+ └─────────────────────────────────────────────┘
330
+ </code></pre>
331
+
332
+ <blockquote><p><strong>Exam tip:</strong> Thứ tự <code>zero_grad() → backward() → step()</code> là BẮT BUỘC. Quên <code>zero_grad()</code> sẽ khiến gradients bị tích lũy qua các batch — đây là bug #1 trong DLI assessment. Luôn nhớ <code>model.train()</code> trước training và <code>model.eval()</code> + <code>torch.no_grad()</code> trước evaluation.</p></blockquote>
333
+
334
+ <h3 id="gpu-acceleration">4.4 GPU Acceleration</h3>
335
+
336
+ <pre><code class="language-python"># Kiểm tra GPU
337
+ print(torch.cuda.is_available()) # True/False
338
+ print(torch.cuda.device_count()) # Số GPU
339
+ print(torch.cuda.get_device_name(0)) # Tên GPU
340
+
341
+ # Di chuyển model và data lên GPU
342
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
343
+ model = model.to(device)
344
+
345
+ # TRONG training loop — data cũng phải lên cùng device
346
+ images = images.to(device)
347
+ labels = labels.to(device)
348
+ </code></pre>
349
+
350
+ <blockquote><p><strong>Exam tip:</strong> Lỗi phổ biến: model trên GPU nhưng data vẫn trên CPU (hoặc ngược lại). PyTorch sẽ báo lỗi <em>"Expected all tensors to be on the same device"</em>. Luôn đảm bảo model và data cùng <code>device</code>.</p></blockquote>
351
+
352
+ <h2 id="cnn-architecture">5. CNN Architecture</h2>
353
+
354
+ <h3 id="conv-layers">5.1 Convolutional Layers</h3>
355
+
356
+ <p><strong>Convolution</strong> trượt một <strong>kernel (filter)</strong> qua input image để trích xuất features. Mỗi kernel phát hiện một pattern cụ thể (edges, textures, shapes).</p>
357
+
358
+ <pre><code class="language-python"># Conv2d cơ bản
359
+ conv = nn.Conv2d(
360
+ in_channels=3, # RGB input
361
+ out_channels=32, # 32 filters → 32 feature maps
362
+ kernel_size=3, # 3×3 kernel
363
+ stride=1, # bước nhảy
364
+ padding=1 # zero-padding để giữ spatial size
365
+ )
366
+
367
+ # Output shape calculation:
368
+ # H_out = (H_in + 2*padding - kernel_size) / stride + 1
369
+ # Ví dụ: (32 + 2*1 - 3) / 1 + 1 = 32 (giữ nguyên size)
370
+ </code></pre>
371
+
372
+ <pre><code class="language-text">Convolution Operation:
373
+
374
+ Input (3 channels) Kernel (3×3) Output (1 feature map)
375
+ ┌─────────────┐ ┌───────┐ ┌──────────┐
376
+ │ ■ ■ ■ ■ ■ ■│ × │ w w w │ = │ ○ ○ ○ ○ │
377
+ │ ■ ■ ■ ■ ■ ■│ │ w w w │ │ ○ ○ ○ ○ │
378
+ │ ■ ■ ■ ■ ■ ■│ │ w w w │ │ ○ ○ ○ ○ │
379
+ │ ■ ■ ■ ■ ■ ■│ └───────┘ │ ○ ○ ○ ○ │
380
+ │ ■ ■ ■ ■ ■ ■│ └──────────┘
381
+ │ ■ ■ ■ ■ ■ ■│ 32 kernels → 32 feature maps
382
+ └─────────────┘
383
+
384
+ Shape flow: (B, 3, 32, 32) → Conv2d(3, 32, 3, padding=1) → (B, 32, 32, 32)
385
+ </code></pre>
386
+
387
+ <h3 id="pooling">5.2 Pooling Layers</h3>
388
+
389
+ <p><strong>Pooling</strong> giảm spatial dimensions, giúp giảm computation và tăng receptive field:</p>
390
+
391
+ <table>
392
+ <thead>
393
+ <tr><th>Pooling</th><th>Cách hoạt động</th><th>Khi nào dùng</th></tr>
394
+ </thead>
395
+ <tbody>
396
+ <tr><td><code>nn.MaxPool2d(2)</code></td><td>Lấy giá trị max trong mỗi 2×2 window</td><td>Feature detection, CNNs thông thường</td></tr>
397
+ <tr><td><code>nn.AvgPool2d(2)</code></td><td>Lấy trung bình trong mỗi 2×2 window</td><td>Smoother features</td></tr>
398
+ <tr><td><code>nn.AdaptiveAvgPool2d((1,1))</code></td><td>Pool về size cố định bất kể input</td><td>Trước fully-connected layer</td></tr>
399
+ </tbody>
400
+ </table>
401
+
402
+ <h3 id="normalization">5.3 Batch Normalization vs Group Normalization</h3>
403
+
404
+ <p>Đây là kiến thức <strong>cực kỳ quan trọng</strong> cho phần <strong>Diffusion Models</strong> ở các bài sau.</p>
405
+
406
+ <table>
407
+ <thead>
408
+ <tr><th>Thuộc tính</th><th>BatchNorm</th><th>GroupNorm</th></tr>
409
+ </thead>
410
+ <tbody>
411
+ <tr><td>Normalize theo</td><td>Batch dimension (N)</td><td>Channel groups (C/G)</td></tr>
412
+ <tr><td>Phụ thuộc batch size</td><td>Có — batch nhỏ thì noisy</td><td>Không — hoạt động tốt mọi batch size</td></tr>
413
+ <tr><td>Training vs Inference</td><td>Khác nhau (running stats)</td><td>Giống nhau</td></tr>
414
+ <tr><td>Phổ biến trong</td><td>CNNs truyền thống (ResNet)</td><td><strong>Diffusion Models</strong>, nhóm nhỏ</td></tr>
415
+ <tr><td>Syntax</td><td><code>nn.BatchNorm2d(C)</code></td><td><code>nn.GroupNorm(G, C)</code></td></tr>
416
+ </tbody>
417
+ </table>
418
+
419
+ <pre><code class="language-python"># BatchNorm — normalize across batch
420
+ bn = nn.BatchNorm2d(64) # 64 channels
421
+
422
+ # GroupNorm — normalize within groups of channels
423
+ gn = nn.GroupNorm(
424
+ num_groups=32, # chia 64 channels thành 32 groups (2 ch/group)
425
+ num_channels=64
426
+ )
427
+
428
+ # Cả hai nhận input shape: (B, C, H, W)
429
+ x = torch.randn(8, 64, 16, 16)
430
+ print(bn(x).shape) # (8, 64, 16, 16)
431
+ print(gn(x).shape) # (8, 64, 16, 16)
432
+ </code></pre>
433
+
434
+ <pre><code class="language-text">BatchNorm vs GroupNorm:
435
+
436
+ BatchNorm: normalize theo ↓ (batch axis) GroupNorm: normalize theo → (channel groups)
437
+ ┌────────────────────────┐ ┌────────────────────────┐
438
+ │ Sample 1: [c1 c2 c3 c4]│ │ Sample 1: [c1 c2│c3 c4]│
439
+ │ Sample 2: [c1 c2 c3 c4]│ ← normalize │ group1│group2 │ ← normalize
440
+ │ Sample 3: [c1 c2 c3 c4]│ mỗi column │ │ │ mỗi group
441
+ │ Sample 4: [c1 c2 c3 c4]│ │ Sample 2: [c1 c2│c3 c4]│
442
+ └────────────────────────┘ └────────────────────────┘
443
+
444
+ → Diffusion Models dùng GroupNorm vì batch_size thường nhỏ
445
+ và noise level thay đổi → BatchNorm statistics không ổn định
446
+ </code></pre>
447
+
448
+ <blockquote><p><strong>Exam tip:</strong> Khi xây dựng <strong>U-Net cho Diffusion Models</strong> (bài sau), bạn sẽ luôn dùng <strong>GroupNorm</strong> thay vì BatchNorm. Lý do: diffusion training thường dùng batch size nhỏ, và mỗi sample có noise level khác nhau → BatchNorm statistics bị noisy. Nhớ quy tắc: <strong>Diffusion = GroupNorm</strong>.</p></blockquote>
449
+
450
+ <h3 id="cnn-code">5.4 Code: Simple CNN cho Image Classification</h3>
451
+
452
+ <pre><code class="language-python">class SimpleCNN(nn.Module):
453
+ def __init__(self, num_classes=10):
454
+ super().__init__()
455
+
456
+ # Conv Block 1: (B, 1, 28, 28) → (B, 32, 14, 14)
457
+ self.block1 = nn.Sequential(
458
+ nn.Conv2d(1, 32, kernel_size=3, padding=1),
459
+ nn.BatchNorm2d(32),
460
+ nn.ReLU(),
461
+ nn.MaxPool2d(2)
462
+ )
463
+
464
+ # Conv Block 2: (B, 32, 14, 14) → (B, 64, 7, 7)
465
+ self.block2 = nn.Sequential(
466
+ nn.Conv2d(32, 64, kernel_size=3, padding=1),
467
+ nn.BatchNorm2d(64),
468
+ nn.ReLU(),
469
+ nn.MaxPool2d(2)
470
+ )
471
+
472
+ # Conv Block 3: (B, 64, 7, 7) → (B, 128, 1, 1)
473
+ self.block3 = nn.Sequential(
474
+ nn.Conv2d(64, 128, kernel_size=3, padding=1),
475
+ nn.BatchNorm2d(128),
476
+ nn.ReLU(),
477
+ nn.AdaptiveAvgPool2d((1, 1)) # global average pooling
478
+ )
479
+
480
+ # Classifier
481
+ self.classifier = nn.Linear(128, num_classes)
482
+
483
+ def forward(self, x):
484
+ x = self.block1(x) # (B, 32, 14, 14)
485
+ x = self.block2(x) # (B, 64, 7, 7)
486
+ x = self.block3(x) # (B, 128, 1, 1)
487
+ x = x.view(x.size(0), -1) # (B, 128)
488
+ x = self.classifier(x) # (B, 10)
489
+ return x
490
+ </code></pre>
491
+
492
+ <pre><code class="language-text">CNN Architecture Flow:
493
+
494
+ Input: (B, 1, 28, 28)
495
+
496
+ ┌────▼────────────────────────┐
497
+ │ Conv2d(1→32, 3×3, pad=1) │
498
+ │ BatchNorm2d(32) │ Block 1
499
+ │ ReLU │
500
+ │ MaxPool2d(2) │
501
+ └────┬────────────────────────┘
502
+ │ (B, 32, 14, 14)
503
+ ┌────▼────────────────────────┐
504
+ │ Conv2d(32→64, 3×3, pad=1) │
505
+ │ BatchNorm2d(64) │ Block 2
506
+ │ ReLU │
507
+ │ MaxPool2d(2) │
508
+ └────┬────────────────────────┘
509
+ │ (B, 64, 7, 7)
510
+ ┌────▼────────────────────────┐
511
+ │ Conv2d(64→128, 3×3, pad=1) │
512
+ │ BatchNorm2d(128) │ Block 3
513
+ │ ReLU │
514
+ │ AdaptiveAvgPool2d(1,1) │
515
+ └────┬────────────────────────┘
516
+ │ (B, 128, 1, 1)
517
+ ┌────▼────────────────────────┐
518
+ │ Flatten → (B, 128) │
519
+ │ Linear(128, 10) │ Classifier
520
+ └────┬────────────────────────┘
521
+ │ (B, 10)
522
+
523
+ Output logits
524
+ </code></pre>
525
+
526
+ <h2 id="cheat-sheet">6. Cheat Sheet</h2>
527
+
528
+ <table>
529
+ <thead>
530
+ <tr><th>Concept</th><th>Code Pattern</th><th>Ghi nhớ</th></tr>
531
+ </thead>
532
+ <tbody>
533
+ <tr><td>Tạo model</td><td>class MyModel(nn.Module)</td><td>Luôn gọi <code>super().__init__()</code></td></tr>
534
+ <tr><td>Forward pass</td><td><code>output = model(x)</code></td><td>Gọi model như function, không gọi <code>.forward()</code> trực tiếp</td></tr>
535
+ <tr><td>Training mode</td><td><code>model.train()</code></td><td>Bật dropout, batchnorm training stats</td></tr>
536
+ <tr><td>Eval mode</td><td><code>model.eval()</code> + <code>torch.no_grad()</code></td><td>Tắt dropout, dùng running stats</td></tr>
537
+ <tr><td>Training loop</td><td>zero_grad → forward → loss → backward → step</td><td>Thứ tự QUAN TRỌNG</td></tr>
538
+ <tr><td>GPU transfer</td><td><code>.to(device)</code></td><td>Cả model VÀ data</td></tr>
539
+ <tr><td>Classification loss</td><td><code>nn.CrossEntropyLoss()</code></td><td>Đã bao gồm softmax</td></tr>
540
+ <tr><td>Diffusion loss</td><td><code>nn.MSELoss()</code></td><td>Predict noise, tính MSE</td></tr>
541
+ <tr><td>Diffusion norm</td><td><code>nn.GroupNorm(G, C)</code></td><td>Không phụ thuộc batch size</td></tr>
542
+ <tr><td>Transformer optimizer</td><td><code>AdamW</code></td><td>Weight decay đúng cách</td></tr>
543
+ </tbody>
544
+ </table>
545
+
546
+ <h2 id="practice-questions">7. Practice Questions</h2>
547
+
548
+ <p>Các câu hỏi sau mô phỏng dạng coding assessment của NVIDIA DLI — bạn cần đọc code, tìm lỗi, và viết code hoàn chỉnh.</p>
549
+
550
+ <h3 id="q1">Q1: Fix the broken training loop</h3>
551
+
552
+ <p>Đoạn code dưới đây có bug khiến model không hội tụ. Tìm và sửa lỗi:</p>
553
+
554
+ <pre><code class="language-python">for epoch in range(10):
555
+ model.train()
556
+ for images, labels in train_loader:
557
+ images, labels = images.to(device), labels.to(device)
558
+ outputs = model(images)
559
+ loss = criterion(outputs, labels)
560
+ loss.backward()
561
+ optimizer.step()
562
+ </code></pre>
563
+
564
+ <details>
565
+ <summary>Xem đáp án Q1</summary>
566
+
567
+ <p><strong>Bug:</strong> Thiếu <code>optimizer.zero_grad()</code> trước <code>loss.backward()</code>. Không reset gradients sẽ khiến gradients tích lũy qua các batch, model không hội tụ hoặc hội tụ sai.</p>
568
+
569
+ <pre><code class="language-python">for epoch in range(10):
570
+ model.train()
571
+ for images, labels in train_loader:
572
+ images, labels = images.to(device), labels.to(device)
573
+ outputs = model(images)
574
+ loss = criterion(outputs, labels)
575
+
576
+ optimizer.zero_grad() # ← THÊM DÒNG NÀY
577
+ loss.backward()
578
+ optimizer.step()
579
+ </code></pre>
580
+
581
+ <p>Thứ tự chuẩn: <code>zero_grad() → backward() → step()</code>. Trong thực tế, <code>zero_grad()</code> có thể đặt trước <code>forward</code> cũng được, nhưng PHẢI có trước <code>backward()</code>.</p>
582
+ </details>
583
+
584
+ <h3 id="q2">Q2: Implement a 3-layer CNN</h3>
585
+
586
+ <p>Viết một CNN class nhận input RGB images (3, 64, 64) và output 5 classes. Yêu cầu:</p>
587
+ <ul>
588
+ <li>3 convolutional blocks, mỗi block: Conv2d → BatchNorm2d → ReLU → MaxPool2d(2)</li>
589
+ <li>Channels: 3 → 32 → 64 → 128</li>
590
+ <li>Kết thúc bằng AdaptiveAvgPool2d + Linear</li>
591
+ </ul>
592
+
593
+ <details>
594
+ <summary>Xem đáp án Q2</summary>
595
+
596
+ <pre><code class="language-python">class ThreeLayerCNN(nn.Module):
597
+ def __init__(self, num_classes=5):
598
+ super().__init__()
599
+ self.features = nn.Sequential(
600
+ # Block 1: (B, 3, 64, 64) → (B, 32, 32, 32)
601
+ nn.Conv2d(3, 32, kernel_size=3, padding=1),
602
+ nn.BatchNorm2d(32),
603
+ nn.ReLU(),
604
+ nn.MaxPool2d(2),
605
+
606
+ # Block 2: (B, 32, 32, 32) → (B, 64, 16, 16)
607
+ nn.Conv2d(32, 64, kernel_size=3, padding=1),
608
+ nn.BatchNorm2d(64),
609
+ nn.ReLU(),
610
+ nn.MaxPool2d(2),
611
+
612
+ # Block 3: (B, 64, 16, 16) → (B, 128, 8, 8)
613
+ nn.Conv2d(64, 128, kernel_size=3, padding=1),
614
+ nn.BatchNorm2d(128),
615
+ nn.ReLU(),
616
+ nn.MaxPool2d(2),
617
+ )
618
+ self.pool = nn.AdaptiveAvgPool2d((1, 1))
619
+ self.classifier = nn.Linear(128, num_classes)
620
+
621
+ def forward(self, x):
622
+ x = self.features(x) # (B, 128, 8, 8)
623
+ x = self.pool(x) # (B, 128, 1, 1)
624
+ x = x.view(x.size(0), -1) # (B, 128)
625
+ x = self.classifier(x) # (B, 5)
626
+ return x
627
+
628
+ # Verify
629
+ model = ThreeLayerCNN(num_classes=5)
630
+ x = torch.randn(4, 3, 64, 64)
631
+ print(model(x).shape) # torch.Size([4, 5])
632
+ </code></pre>
633
+ </details>
634
+
635
+ <h3 id="q3">Q3: Trace tensor shapes through a network</h3>
636
+
637
+ <p>Cho model dưới đây và input shape <code>(8, 1, 32, 32)</code>. Ghi lại shape tại mỗi bước:</p>
638
+
639
+ <pre><code class="language-python">model = nn.Sequential(
640
+ nn.Conv2d(1, 16, kernel_size=5, stride=2, padding=2), # Step A
641
+ nn.ReLU(),
642
+ nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=0), # Step B
643
+ nn.ReLU(),
644
+ nn.AdaptiveAvgPool2d((4, 4)), # Step C
645
+ nn.Flatten(), # Step D
646
+ nn.Linear(32 * 4 * 4, 10), # Step E
647
+ )
648
+ </code></pre>
649
+
650
+ <details>
651
+ <summary>Xem đáp án Q3</summary>
652
+
653
+ <p>Công thức output size: <code>H_out = (H_in + 2*padding - kernel_size) / stride + 1</code></p>
654
+
655
+ <pre><code class="language-text">Input: (8, 1, 32, 32)
656
+
657
+ Step A: Conv2d(1, 16, k=5, s=2, p=2)
658
+ H = (32 + 2*2 - 5) / 2 + 1 = 16
659
+ → (8, 16, 16, 16)
660
+
661
+ Step B: Conv2d(16, 32, k=3, s=1, p=0)
662
+ H = (16 + 2*0 - 3) / 1 + 1 = 14
663
+ → (8, 32, 14, 14)
664
+
665
+ Step C: AdaptiveAvgPool2d((4, 4))
666
+ → (8, 32, 4, 4)
667
+
668
+ Step D: Flatten()
669
+ → (8, 512) # 32 * 4 * 4 = 512
670
+
671
+ Step E: Linear(512, 10)
672
+ → (8, 10)
673
+ </code></pre>
674
+
675
+ <p><strong>Key insight:</strong> <code>AdaptiveAvgPool2d</code> luôn output size cố định bất kể input — rất hữu ích khi input size có thể thay đổi.</p>
676
+ </details>
677
+
678
+ <h3 id="q4">Q4: GroupNorm vs BatchNorm — khi nào dùng gì?</h3>
679
+
680
+ <p>Bạn đang xây dựng một <strong>U-Net cho Diffusion Model</strong>. Mỗi block có Conv2d → ??? → SiLU. Bạn chọn normalization nào và tại sao? Viết code cho 1 block.</p>
681
+
682
+ <details>
683
+ <summary>Xem đáp án Q4</summary>
684
+
685
+ <p><strong>Chọn GroupNorm.</strong> Lý do:</p>
686
+ <ol>
687
+ <li><strong>Batch size nhỏ:</strong> Diffusion training thường dùng batch size 1-8 vì images lớn → BatchNorm statistics quá noisy</li>
688
+ <li><strong>Noise levels khác nhau:</strong> Mỗi sample trong batch có timestep (noise level) khác nhau → normalize across batch không hợp lý</li>
689
+ <li><strong>Inference consistency:</strong> GroupNorm hoạt động giống nhau ở train và inference</li>
690
+ </ol>
691
+
692
+ <pre><code class="language-python">class DiffusionBlock(nn.Module):
693
+ def __init__(self, in_channels, out_channels, num_groups=32):
694
+ super().__init__()
695
+ self.conv = nn.Conv2d(in_channels, out_channels,
696
+ kernel_size=3, padding=1)
697
+ self.norm = nn.GroupNorm(num_groups, out_channels)
698
+ self.act = nn.SiLU() # SiLU phổ biến hơn ReLU trong diffusion
699
+
700
+ def forward(self, x):
701
+ x = self.conv(x)
702
+ x = self.norm(x)
703
+ x = self.act(x)
704
+ return x
705
+
706
+ # Ví dụ sử dụng
707
+ block = DiffusionBlock(64, 128, num_groups=32)
708
+ x = torch.randn(2, 64, 32, 32) # batch_size = 2, nhỏ!
709
+ print(block(x).shape) # (2, 128, 32, 32)
710
+ </code></pre>
711
+
712
+ <p><strong>Quy tắc:</strong> Trong mọi kiến trúc Diffusion (U-Net, DiT), luôn dùng <code>nn.GroupNorm</code>. Activation thường là <code>nn.SiLU()</code> (Swish) thay vì ReLU.</p>
713
+ </details>
714
+
715
+ <h3 id="q5">Q5: Debug gradient issue — detach() vs torch.no_grad()</h3>
716
+
717
+ <p>Đoạn code dưới đây có vấn đề gì? Output của <code>feature_extractor</code> không nên có gradient (freeze backbone), nhưng <code>classifier</code> vẫn cần train.</p>
718
+
719
+ <pre><code class="language-python">feature_extractor = pretrained_model.features
720
+ classifier = nn.Linear(512, 10).to(device)
721
+ optimizer = torch.optim.Adam(classifier.parameters(), lr=1e-3)
722
+
723
+ for images, labels in train_loader:
724
+ images, labels = images.to(device), labels.to(device)
725
+
726
+ # Extract features (should be frozen)
727
+ with torch.no_grad():
728
+ features = feature_extractor(images)
729
+
730
+ # Classify
731
+ outputs = classifier(features)
732
+ loss = criterion(outputs, labels)
733
+
734
+ optimizer.zero_grad()
735
+ loss.backward() # ← Có vấn đề?
736
+ optimizer.step()
737
+ </code></pre>
738
+
739
+ <details>
740
+ <summary>Xem đáp án Q5</summary>
741
+
742
+ <p><strong>Vấn đề:</strong> Code này thực ra <strong>hoạt động đúng</strong> cho trường hợp này! <code>torch.no_grad()</code> ngăn gradient computation cho <code>feature_extractor</code>, và <code>features</code> tensor sẽ không có <code>requires_grad</code>. Gradient vẫn flow qua <code>classifier</code> bình thường.</p>
743
+
744
+ <p><strong>Tuy nhiên</strong>, có 2 cách tiếp cận và bạn cần hiểu sự khác biệt:</p>
745
+
746
+ <pre><code class="language-python"># Cách 1: torch.no_grad() — KHÔNG tính gradient, tiết kiệm memory
747
+ with torch.no_grad():
748
+ features = feature_extractor(images)
749
+ # features.requires_grad = False
750
+ # Gradient KHÔNG flow ngược qua feature_extractor
751
+ # ✅ Dùng khi muốn freeze hoàn toàn, tiết kiệm GPU memory
752
+
753
+ # Cách 2: .detach() — tách tensor khỏi computational graph
754
+ features = feature_extractor(images).detach()
755
+ # features.requires_grad = False
756
+ # Feature extractor VẪN tính forward (tốn memory cho graph)
757
+ # nhưng gradient bị cắt tại .detach()
758
+ # ⚠️ Kém hiệu quả hơn vì vẫn build graph rồi mới cắt
759
+
760
+ # Cách 3: Freeze parameters — approach phổ biến nhất
761
+ for param in feature_extractor.parameters():
762
+ param.requires_grad = False
763
+ # ✅ Rõ ràng nhất, thường dùng trong fine-tuning
764
+ </code></pre>
765
+
766
+ <table>
767
+ <thead>
768
+ <tr><th>Approach</th><th>Gradient flow</th><th>Memory</th><th>Khi nào dùng</th></tr>
769
+ </thead>
770
+ <tbody>
771
+ <tr><td><code>torch.no_grad()</code></td><td>Không tính graph</td><td>Tiết kiệm nhất</td><td>Inference, frozen features</td></tr>
772
+ <tr><td><code>.detach()</code></td><td>Cắt tại điểm detach</td><td>Tốn hơn</td><td>Khi cần partial gradient flow</td></tr>
773
+ <tr><td>Freeze params</td><td>Không update weights</td><td>Vẫn build graph</td><td>Fine-tuning rõ ràng</td></tr>
774
+ </tbody>
775
+ </table>
776
+ </details>
777
+
778
+ <h2 id="ket-luan">8. Kết luận</h2>
779
+
780
+ <p>Bài 1 đã trang bị cho bạn toàn bộ nền tảng PyTorch cần thiết cho khóa NVIDIA DLI Generative AI. Hãy chắc chắn bạn có thể:</p>
781
+
782
+ <ul>
783
+ <li>Viết <strong>training loop</strong> hoàn chỉnh mà không cần nhìn tài liệu</li>
784
+ <li>Tạo <strong>nn.Module</strong> class với <code>__init__</code> và <code>forward</code></li>
785
+ <li>Tính <strong>tensor shapes</strong> qua từng layer</li>
786
+ <li>Phân biệt <strong>GroupNorm vs BatchNorm</strong> — đặc biệt quan trọng cho Diffusion Models</li>
787
+ <li>Debug các lỗi phổ biến: thiếu <code>zero_grad()</code>, device mismatch, gradient issues</li>
788
+ </ul>
789
+
790
+ <p>Bài tiếp theo: <strong>Bài 2 — Sequence Models & Attention Mechanism</strong> — nền tảng cho Transformers và LLMs.</p>