Mục lục
- Mục tiêu bài học
- Dropout là gì
- Lịch sử và paper gốc
- Cơ chế Bernoulli mask
- Inverted Dropout (chuẩn hiện đại)
- Tại inference
- Vì sao Dropout giảm overfit
- Tham số p — dropout rate
- PyTorch API — nn.Dropout
- model.train() vs model.eval()
- Đặt Dropout ở đâu
- Variants
- Dropout cho Transformer
- Dropout vs BatchNorm
- Monte Carlo Dropout
- Hyperparameter tuning
- Code Python — MLP có / không Dropout trên MNIST
- Bài tập
Mục tiêu bài học
Sau bài học, bạn sẽ:
- Mô tả được cơ chế dropout: Bernoulli mask, vai trò của \( p \), khác biệt giữa train và inference.
- Viết đúng công thức inverted dropout và chứng minh expected value bằng activation gốc.
- Hiểu vì sao dropout hoạt động: ensemble ngầm \( 2^n \) subnetwork, phá co-adaptation, force redundancy.
- Dùng
nn.Dropout(p)đúng vị trí trong model PyTorch. - Biết tại sao
model.train()/model.eval()bắt buộc switch khi có dropout. - Phân biệt dropout với các variant: DropConnect, Spatial Dropout, DropPath, attention dropout.
- Biết Monte Carlo Dropout để ước lượng uncertainty.
Bài này nối Bài 21 — Overfitting trong Deep Learning (chẩn đoán vấn đề) với Bài 23 — Batch Normalization (kỹ thuật normalize activation).
Dropout là gì
Dropout là kỹ thuật regularization: trong mỗi forward pass khi train, ngẫu nhiên đặt output của một phần neuron về 0. Tỷ lệ neuron bị "drop" do tham số \( p \) (dropout rate) quyết định — thường \( p = 0.5 \) cho hidden layer.
Ý tưởng cốt lõi: nếu mạng không thể dựa vào sự có mặt của bất kỳ neuron cụ thể nào (vì neuron đó có thể bị tắt bất kỳ lúc nào), nó buộc phải học các biểu diễn dư thừa và phân tán. Không neuron nào trở thành "đường tắt" độc nhất cho một feature.
Tại inference (eval / test / production), dropout tắt hoàn toàn — mọi neuron đều hoạt động. Đây là điểm cốt yếu để nhớ: dropout là một hành vi chỉ khi train, không hề ảnh hưởng đến forward khi predict.
Lịch sử và paper gốc
Dropout do nhóm Hinton ở Toronto đề xuất:
- Hinton et al. (2012) — preprint "Improving neural networks by preventing co-adaptation of feature detectors" (arXiv:1207.0580).
- Srivastava, Hinton, Krizhevsky, Sutskever, Salakhutdinov (2014) — paper chính thức "Dropout: A Simple Way to Prevent Neural Networks from Overfitting", JMLR 15.
Bối cảnh: 2012 là năm AlexNet thắng ImageNet (cùng nhóm), và overfitting là rào cản chính khi mạng đủ rộng. Dropout cho phép train mạng lớn hơn dữ liệu mà không bị mất generalization.
Trong vòng vài năm sau 2014, dropout trở thành thành phần mặc định trong gần như mọi kiến trúc deep learning có MLP/FC layer — từ VGG, đến RNN/LSTM, đến Transformer (GPT, BERT). Cho CNN thuần thì dropout dần bị thay bằng Batch Normalization, sẽ giải thích ở mục 14.
Cơ chế Bernoulli mask
Với mỗi neuron có activation \( a_i \), dropout sinh ra một biến ngẫu nhiên \( m_i \) tuân theo phân phối Bernoulli:
\[ m_i \sim \mathrm{Bernoulli}(1 - p) \]
Nghĩa là \( m_i = 1 \) với xác suất \( 1 - p \) (giữ neuron), \( m_i = 0 \) với xác suất \( p \) (drop). Activation sau dropout:
\[ \tilde{a}_i = a_i \cdot m_i \]
Mỗi forward pass sinh một mask mới và độc lập — một neuron có thể bị drop ở batch này nhưng được giữ ở batch sau. Mask cũng độc lập giữa các neuron trong cùng layer.
Vấn đề: nếu chỉ làm \( \tilde{a}_i = a_i \cdot m_i \), expected value bị thu nhỏ:
\[ \mathbb{E}[\tilde{a}_i] = a_i \cdot \mathbb{E}[m_i] = a_i \cdot (1 - p) \]
Tức activation trung bình bị nhỏ lại theo tỉ lệ \( 1 - p \). Layer phía sau nhận input scale khác nhau giữa train (scale \( 1 - p \)) và inference (scale \( 1 \)) — gây mismatch lúc predict. Hai cách khắc phục: original dropout (scale tại inference) và inverted dropout (scale tại train) — mục kế.
Inverted Dropout (chuẩn hiện đại)
Inverted dropout chuyển bước scale từ inference sang train. Công thức khi train:
\[ \tilde{a}_i = \frac{a_i \cdot m_i}{1 - p}, \quad m_i \sim \mathrm{Bernoulli}(1 - p) \]
Vì \( \mathbb{E}[m_i] = 1 - p \), expected value sau scale trở về đúng \( a_i \):
\[ \mathbb{E}[\tilde{a}_i] = a_i \cdot \frac{\mathbb{E}[m_i]}{1 - p} = a_i \cdot \frac{1 - p}{1 - p} = a_i \]
Tại inference, không cần làm gì thêm: tắt dropout (mask toàn 1, không chia), layer phía sau vẫn nhận activation cùng scale như khi train.
So sánh:
- Original dropout (paper 2014): train scale = 1, inference scale by \( 1 - p \). Bất tiện vì phải chỉnh code inference.
- Inverted dropout (modern): train scale by \( 1/(1 - p) \), inference giữ nguyên. Code inference sạch — chỉ cần tắt dropout là xong.
PyTorch, TensorFlow, JAX đều dùng inverted dropout. Khi đọc paper cũ thấy công thức không chia \( 1 - p \), hiểu rằng đó là original — kết quả tương đương sau khi áp dụng scaling tương ứng tại inference.
Tại inference
Khi predict / evaluate / inference:
- Dropout layer không drop neuron nào.
- Không chia, không scale (vì train đã scale rồi với inverted dropout).
- Output bằng đúng input — dropout layer ở chế độ eval là identity function.
Trong PyTorch, điều này được kích hoạt bằng model.eval() — sẽ giải thích kỹ ở mục 10. Khi quên gọi eval(), dropout vẫn drop khi predict → output có random component → cùng input có thể trả output khác nhau ở mỗi lần chạy. Đây là một trong những bug khó debug nhất với người mới.
Vì sao Dropout giảm overfit
Có 3 cách giải thích thường gặp, bổ sung cho nhau:
1) Ensemble ngầm \( 2^n \) subnetwork. Mạng có \( n \) neuron có dropout, mỗi forward pass tương đương sample 1 subnetwork (mỗi neuron in/out → \( 2^n \) khả năng). Train với dropout giống như train đồng thời \( 2^n \) mạng chia sẻ weight. Tại inference (dropout off, weight giữ scale gốc) gần đúng với trung bình hình học của \( 2^n \) mạng đó — đó là tinh thần "implicit ensemble" trong paper 2014.
2) Phá co-adaptation giữa neuron. Không có dropout, các neuron có xu hướng "đồng tiến hoá" — neuron A chỉ hữu ích khi neuron B cũng active, tạo thành chuỗi phụ thuộc mong manh, dễ overfit. Dropout buộc mỗi neuron phải hữu ích độc lập với phần lớn các neuron khác — vì các neuron khác có thể bị tắt bất kỳ lúc nào.
3) Force redundancy. Mạng phải có nhiều path để biểu diễn cùng một feature, vì một path bất kỳ có thể bị cắt. Hệ quả: feature distributed, robust với nhiễu input và weight perturbation.
Cả ba góc nhìn đều dẫn đến cùng kết luận thực nghiệm: dropout giảm gap giữa train và val accuracy. Hiệu ứng rõ rệt nhất khi mạng có nhiều tham số so với dữ liệu (high capacity), ít rõ khi dữ liệu rất lớn so với mạng — đây là lý do nhiều LLM hiện đại huấn luyện trên trillion token chỉ dùng dropout rất nhỏ hoặc tắt hẳn.
Tham số p — dropout rate
\( p \) là xác suất một neuron bị drop. Trong PyTorch, đối số p của nn.Dropout(p) chính là tỉ lệ drop (không phải tỉ lệ giữ).
Giá trị thực hành:
- \( p = 0.5 \) — đề xuất gốc của Hinton cho hidden layer của MLP. Vẫn dùng làm baseline.
- \( p = 0.1 - 0.2 \) — cho input layer hoặc layer đầu, vì input đã thưa thông tin, drop nhiều quá làm mất feature gốc.
- \( p = 0.3 - 0.5 \) — cho hidden layer trong MLP / FC head của CNN hiện đại.
- \( p = 0.1 \) — phổ biến trong Transformer (attention dropout, residual dropout).
Lưu ý ranh giới:
- \( p \) quá lớn (\( \geq 0.7 \)): drop quá nhiều, mạng không học đủ → underfit. Loss train không giảm, val cũng kém.
- \( p \) quá nhỏ (\( \leq 0.05 \)): hiệu ứng regularize gần như không có. Tương đương không dùng dropout.
- \( p = 0 \) trong PyTorch
nn.Dropout(0)là identity — code chạy nhưng không drop.
Quy trình thực tế: bắt đầu \( p = 0.5 \) cho hidden layer của MLP / FC head. Nếu val loss vẫn cao hơn train nhiều → giữ hoặc tăng nhẹ. Nếu train loss không giảm tốt → giảm \( p \) xuống 0.3 - 0.2.
PyTorch API — nn.Dropout
Cách dùng cơ bản:
import torch.nn as nn
model = nn.Sequential(
nn.Linear(784, 256),
nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(256, 128),
nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(128, 10),
)
Một số API liên quan:
nn.Dropout(p=0.5): dropout 1D — drop từng phần tử độc lập. Dùng cho FC / MLP layer.nn.Dropout2d(p=0.5): drop nguyên channel (Spatial Dropout) cho input shape \( (N, C, H, W) \). Dùng cho CNN.nn.Dropout3d(p=0.5): tương tự cho 3D conv (video / volumetric).nn.AlphaDropout: variant cho SELU activation, giữ mean và variance của input.F.dropout(x, p=0.5, training=self.training): dạng functional, phải truyền cờtrainingthủ công.
Khuyến nghị: trong subclass nn.Module luôn dùng nn.Dropout (layer-based) thay vì F.dropout. Lý do: layer-based tự động đọc self.training của parent module — gọi model.eval() là đủ để tắt; functional thì phải nhớ truyền cờ, dễ sai.
model.train() vs model.eval()
Đây là phần QUAN TRỌNG NHẤT của bài. Mọi nn.Module trong PyTorch có thuộc tính self.training (bool). nn.Dropout đọc cờ này khi forward:
self.training = True→ drop neuron theo Bernoulli.self.training = False→ identity (không drop, không scale).
Hai hàm chuyển trạng thái:
model.train()— bật mode train cho model và toàn bộ submodule (recursive).model.eval()— bật mode eval cho model và toàn bộ submodule.
Mẫu training loop chuẩn:
for epoch in range(epochs):
model.train() # bật dropout
for x, y in train_loader:
loss = criterion(model(x), y)
opt.zero_grad()
loss.backward()
opt.step()
model.eval() # tắt dropout
with torch.no_grad():
for x, y in val_loader:
preds = model(x)
# tính accuracy / val loss
Hệ quả khi quên:
- Quên gọi
eval()khi validate / predict: dropout vẫn active → output có random → cùng input cho output khác nhau giữa các lần chạy. Reproducibility hỏng, val accuracy nhiễu, mỗi lần gọimodel(x)ra số khác → hỗn loạn khi debug. - Quên gọi
train()sau khi đãeval(): dropout không active khi train → mất hiệu ứng regularize, overfit nặng hơn. - Inference tại production mà không
eval(): latency không đổi nhưng kết quả không tất định — bug nghiêm trọng, dễ qua review.
Quy tắc: luôn gọi model.train() ở đầu mỗi epoch train, luôn gọi model.eval() trước mọi vòng validate / predict. Coi như một phần bắt buộc của training loop, không phải optional.
Lưu ý: model.eval() ảnh hưởng cả BatchNorm (chuyển từ batch statistics sang running mean/var) — sẽ học chi tiết ở bài kế.
Đặt Dropout ở đâu
Quy tắc:
- Sau activation: Linear → ReLU → Dropout. Vì dropout drop activation, đặt sau ReLU (đã loại giá trị âm) là chuẩn.
- Trước layer cuối (output): dropout trên hidden feature ngay trước output layer là vị trí có hiệu ứng regularize mạnh nhất.
- KHÔNG đặt sau softmax hay output layer: dropout làm hỏng phân phối xác suất (drop một số class về 0, không còn tổng 1) và hỏng tính chất calibration. Output luôn phải full.
- Input dropout (\( p \approx 0.1 - 0.2 \)) đôi khi có ích — Hinton 2014 dùng trên MNIST input. Modern thực hành ít phổ biến vì đã có data augmentation.
- KHÔNG đặt dropout giữa Linear và ReLU: vẫn chạy được nhưng không phải convention. ReLU áp lên input đã có 0 (do drop) tạo phân phối kì lạ ở output.
Ví dụ FC head sau CNN:
fc_head = nn.Sequential(
nn.Flatten(),
nn.Linear(512 * 7 * 7, 4096),
nn.ReLU(inplace=True),
nn.Dropout(p=0.5),
nn.Linear(4096, 4096),
nn.ReLU(inplace=True),
nn.Dropout(p=0.5),
nn.Linear(4096, 1000), # output: KHÔNG dropout sau đây
)
Đây là pattern của VGG, AlexNet — dropout trong FC head, không trong conv body.
Variants
Dropout có nhiều biến thể, mỗi loại phù hợp với một kiểu kiến trúc:
- DropConnect (Wan et al., 2013): drop weight thay vì activation. Mỗi weight \( W_{ij} \) có xác suất \( p \) bị set về 0 trong forward pass. Phép tính nặng hơn, ít dùng trong thực hành ngoài research.
- Spatial Dropout (Dropout2d) — Tompson et al. (2015): drop nguyên channel feature map của CNN. Lý do: activation trong cùng một channel của CNN có spatial correlation cao, drop từng phần tử như dropout 1D không phá tương quan; drop cả channel mới ngắt được correlation. PyTorch:
nn.Dropout2d(p). - DropPath / Stochastic Depth (Huang et al., 2016): drop cả residual block / layer trong ResNet với xác suất \( p \). Khi train, mỗi forward sample một sub-network nông hơn → regularize và rút ngắn computation. Dùng trong DeiT, Swin Transformer, ConvNeXt.
- Variational Dropout (Gal & Ghahramani, 2016): cho RNN — dùng cùng một mask qua tất cả time step của một sequence (thay vì sample mask mới mỗi step). Giữ tính nhất quán theo thời gian.
- Zoneout (Krueger et al., 2017): variant cho RNN — thay vì drop hidden state về 0, giữ lại giá trị hidden state cũ với xác suất \( p \).
Trong thực hành: Dropout1D cho MLP/FC, Spatial Dropout cho CNN, DropPath cho ResNet/Transformer sâu, Variational Dropout cho LSTM/GRU.
Dropout cho Transformer
Trong kiến trúc Transformer (Vaswani et al., 2017), dropout xuất hiện ở nhiều vị trí:
- Attention dropout: áp lên ma trận attention weight sau softmax (trước nhân với \( V \)). Drop random một số entry của attention map → buộc head không lệ thuộc vào pattern attention cụ thể. Paper gốc dùng \( p = 0.1 \).
- Residual / hidden state dropout: áp lên output của attention và FFN, trước khi cộng vào residual stream. Paper gốc cũng \( p = 0.1 \).
- Embedding dropout: áp ngay sau khi cộng token embedding với positional embedding.
Trong PyTorch, các module nn.TransformerEncoderLayer và nn.TransformerDecoderLayer đều có tham số dropout:
layer = nn.TransformerEncoderLayer(
d_model=512,
nhead=8,
dim_feedforward=2048,
dropout=0.1, # áp cho attention + FFN + residual
)
Trong các LLM lớn (GPT-2/3, LLaMA), dropout thường bị tắt hoặc giảm về 0 ở pre-training trên dữ liệu lớn (vì không còn overfit), và bật trở lại khi fine-tune với dataset nhỏ.
Dropout vs BatchNorm
Hai kỹ thuật có chồng chéo nhưng không thay thế hoàn toàn:
- BatchNorm (sẽ học ở Bài 23) chuẩn hoá activation theo batch statistic. Nó có hiệu ứng regularize như side effect — vì noise của batch statistic làm forward không tất định.
- Dropout + BatchNorm cùng layer có thể conflict. Li et al. (2018) "Understanding the Disharmony Between Dropout and Batch Normalization by Variance Shift" chỉ ra rằng dropout thay đổi variance của activation giữa train (có chia \( 1 - p \)) và inference (không) — BatchNorm tích luỹ running statistic ở train rồi dùng ở inference, dẫn đến variance shift.
- Modern CNN (ResNet, EfficientNet, ConvNeXt): thường BatchNorm only, ít hoặc không dropout trong conv body. Dropout chỉ xuất hiện ở FC head (nếu có).
- Transformer: dùng LayerNorm (không phải BatchNorm) — không xung đột với dropout. Dropout vẫn là regularization chính.
Quy tắc thực hành: nếu kiến trúc đã dùng BatchNorm dày đặc trong conv body, không cần dropout trong conv body — chỉ giữ dropout trong FC head. Nếu kiến trúc dùng LayerNorm (Transformer, T5, GPT), dropout là một trong những regularizer chính.
Monte Carlo Dropout
Bình thường, inference với dropout off → output tất định. Nhưng nếu cố tình bật dropout khi inference, mỗi lần forward cho output khác nhau. Lặp \( T \) lần và lấy:
- Mean: ước lượng kết quả trung bình của ensemble \( T \) subnetwork.
- Variance: ước lượng uncertainty của model trên input đó. Variance cao → model không chắc; variance thấp → model tự tin.
Đây là Monte Carlo Dropout, Gal & Ghahramani (2016) "Dropout as a Bayesian Approximation" chỉ ra rằng dropout có thể được hiểu như xấp xỉ Bayesian inference trên posterior của weight.
def mc_dropout_predict(model, x, T=50):
model.train() # bật dropout
preds = torch.stack([model(x) for _ in range(T)], dim=0)
mean = preds.mean(dim=0)
var = preds.var(dim=0)
return mean, var
Trong production: dùng MC Dropout khi cần flagging input "khó" (out-of-distribution detection, active learning, medical AI cần confidence calibration). Trade-off: latency tăng \( T \) lần.
Lưu ý: gọi model.train() ở đây chỉ để bật dropout — không phải để train. Không gọi backward() hay optimizer.step(). Sau MC inference, nhớ trả về model.eval() cho phần code khác.
Hyperparameter tuning
Tuning \( p \) là bài toán bias-variance trade-off điển hình:
- Bắt đầu \( p = 0.5 \) (cho MLP), \( p = 0.1 \) (cho Transformer). Validate.
- Nếu val loss >> train loss (overfit) → tăng \( p \) lên 0.6 - 0.7 hoặc dropout nhiều layer hơn.
- Nếu val loss ≈ train loss và cả hai cao (underfit) → giảm \( p \) xuống 0.3 - 0.2, hoặc bỏ dropout, hoặc tăng capacity model.
- Nếu val loss thấp và gap nhỏ → tốt, không cần chỉnh.
Grid search điển hình: \( p \in \{0.1, 0.2, 0.3, 0.5\} \), train mỗi cấu hình vài epoch, chọn cấu hình có val loss tốt nhất.
Khi dùng nhiều dropout layer, thường giữ \( p \) đồng đều giữa các layer. Có thể giảm \( p \) ở layer gần input (vì input đã hiếm) và tăng nhẹ ở các layer FC sâu, nhưng không nhất thiết.
Code Python — MLP có / không Dropout trên MNIST
So sánh hai MLP cùng kiến trúc, một có Dropout, một không. Train trên MNIST, log train và val accuracy:
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
torch.manual_seed(0)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
train_ds = datasets.MNIST(".", train=True, download=True, transform=transforms.ToTensor())
test_ds = datasets.MNIST(".", train=False, download=True, transform=transforms.ToTensor())
train_loader = DataLoader(train_ds, batch_size=128, shuffle=True)
test_loader = DataLoader(test_ds, batch_size=512)
def make_mlp(use_dropout=False, p=0.5):
layers = [nn.Flatten(), nn.Linear(784, 512), nn.ReLU()]
if use_dropout: layers.append(nn.Dropout(p))
layers += [nn.Linear(512, 256), nn.ReLU()]
if use_dropout: layers.append(nn.Dropout(p))
layers += [nn.Linear(256, 10)]
return nn.Sequential(*layers)
def accuracy(model, loader):
model.eval() # tắt dropout
correct = total = 0
with torch.no_grad():
for x, y in loader:
x, y = x.to(device), y.to(device)
correct += (model(x).argmax(1) == y).sum().item()
total += y.size(0)
return correct / total
def train(model, epochs=10):
opt = torch.optim.Adam(model.parameters(), lr=1e-3)
for epoch in range(epochs):
model.train() # bật dropout
for x, y in train_loader:
x, y = x.to(device), y.to(device)
loss = F.cross_entropy(model(x), y)
opt.zero_grad(); loss.backward(); opt.step()
tr = accuracy(model, train_loader)
va = accuracy(model, test_loader)
print(f"epoch {epoch+1:2d} train_acc={tr:.4f} val_acc={va:.4f} gap={tr-va:.4f}")
print(">>> No dropout")
train(make_mlp(use_dropout=False).to(device))
print(">>> With dropout p=0.5")
train(make_mlp(use_dropout=True, p=0.5).to(device))
Kỳ vọng kết quả (con số tham khảo, có thể dao động):
No dropout
epoch 10 train_acc=0.999 val_acc=0.981 gap=0.018
With dropout p=0.5
epoch 10 train_acc=0.984 val_acc=0.983 gap=0.001
Nhận xét:
- Không dropout: train accuracy gần 1.0, val 0.98 — gap thể hiện overfit nhẹ.
- Có dropout: train accuracy thấp hơn (vì dropout làm khó train), nhưng val accuracy bằng hoặc nhỉnh hơn, gap nhỏ — generalize tốt hơn.
Demo behavior khác nhau train / eval:
model = make_mlp(use_dropout=True, p=0.5).to(device)
x = torch.randn(1, 1, 28, 28).to(device)
model.train()
out1 = model(x)
out2 = model(x)
print("train mode — same?", torch.equal(out1, out2)) # False — có random
model.eval()
out3 = model(x)
out4 = model(x)
print("eval mode — same?", torch.equal(out3, out4)) # True — tất định
Output này là minh chứng rõ ràng nhất về model.train() vs model.eval() — output không tất định khi train, tất định khi eval.
Bài tập
- Train MLP
784 → 512 → 256 → 10với ReLU trên MNIST trong 15 epoch. Chạy 2 lần: không dropout và dropout \( p = 0.5 \). Log train_acc, val_acc, gap mỗi epoch. So sánh đường cong, xác nhận dropout giảm gap. - Thử các giá trị \( p \in \{0.1, 0.2, 0.3, 0.5, 0.7\} \) trên cùng MLP. Plot val_acc theo \( p \). Xác định \( p \) tối ưu — và quan sát \( p = 0.7 \) làm underfit.
- Demo train/eval: lấy một input cố định, gọi
model(x)5 lần ở modetrain()và 5 lần ở modeeval(). In các output. Xác nhận train mode cho 5 output khác nhau, eval mode cho 5 output giống nhau. - Implement custom Dropout layer bằng tay (inverted dropout). Kiểm tra: mean của
my_dropout(x, p=0.5)qua nhiều lần lấy mẫu phải xấp xỉ mean củax.
So sánh output vớiclass MyDropout(nn.Module): def __init__(self, p=0.5): super().__init__() self.p = p def forward(self, x): if not self.training or self.p == 0: return x # TODO: sinh Bernoulli mask, scale 1/(1-p) ...nn.Dropout(p)(cùng seed) — phải khớp. - Monte Carlo Dropout: train model dropout \( p = 0.3 \) trên MNIST. Lấy 5 ảnh test, predict với
T = 50lần (dropout bật), tính mean và variance của softmax probability. Quan sát: ảnh "dễ" (digit rõ nét) có variance thấp, ảnh "khó" (digit méo) có variance cao hơn. - Pitfall: viết code predict không gọi
model.eval(). Predict cùng input 3 lần — quan sát output khác nhau. Sửa lại bằng cách thêmmodel.eval(), verify output ổn định. - Đọc lại source
torch/nn/modules/dropout.py. Tìm chỗ checkself.trainingvà chỗ scale1/(1-p). Verify lại lý thuyết qua code.
Gợi ý / đáp án ngắn
- Đường cong train_acc của model dropout thường thấp hơn 1-2% so với no-dropout, nhưng val_acc bằng hoặc cao hơn; gap nhỏ rõ rệt.
- \( p = 0.3 - 0.5 \) thường tốt nhất cho MLP MNIST. \( p = 0.7 \) train_acc và val_acc đều giảm.
- Train mode: 5 output khác nhau. Eval mode: 5 output giống hệt.
-
def forward(self, x): if not self.training or self.p == 0: return x mask = torch.empty_like(x).bernoulli_(1 - self.p) return x * mask / (1 - self.p) - Variance softmax cho ảnh khó (digit ambiguous như "7" vs "1", "4" vs "9") cao hơn 5-10 lần so với ảnh rõ.
- Không
eval(): 3 output khác nhau ở vài chục percentile. Cóeval(): 3 output giống nhau byte-by-byte. - Tìm trong
nn.functional.dropout: dòngif not training or p == 0: return input, và phép chia mask cho1 - p.
Bài kế tiếp: Bài 23 — Batch Normalization — kỹ thuật chuẩn hoá activation theo batch, có hiệu ứng regularize phụ và thay đổi đáng kể tốc độ training của mạng sâu.
- Srivastava, Hinton, Krizhevsky, Sutskever, Salakhutdinov (2014) - Dropout: A Simple Way to Prevent Neural Networks from Overfitting (JMLR)
- Hinton et al. (2012) - Improving neural networks by preventing co-adaptation of feature detectors
- Wan et al. (2013) - Regularization of Neural Networks using DropConnect (ICML)
- Tompson et al. (2015) - Efficient Object Localization Using Convolutional Networks (Spatial Dropout)
- Huang et al. (2016) - Deep Networks with Stochastic Depth (DropPath)
- Gal & Ghahramani (2016) - Dropout as a Bayesian Approximation: Representing Model Uncertainty in Deep Learning
- Gal & Ghahramani (2016) - A Theoretically Grounded Application of Dropout in Recurrent Neural Networks (Variational Dropout)
- Li et al. (2018) - Understanding the Disharmony Between Dropout and Batch Normalization by Variance Shift
- PyTorch Docs - nn.Dropout
- PyTorch Docs - nn.Dropout2d (Spatial Dropout)
- PyTorch Docs - nn.Module.train / eval
- Goodfellow, Bengio, Courville - Deep Learning Book Ch. 7.12 (Dropout)
