@xdev-asia/xdev-knowledge-mcp 1.0.58 → 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.
- package/content/blog/ai/nvidia-dli-generative-ai-chung-chi-va-lo-trinh-hoc.md +894 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/index.md +237 -0
- package/data/quizzes/nvidia-dli-generative-ai.json +350 -0
- package/data/quizzes.json +14 -0
- 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>
|