Epochs, Batches & Iterations
Epochs, Batches & Iterations
Bạn dạy võ cho 500 học viên. Cách nào hợp lý nhất để tất cả đều thành thạo?
Hình minh họa
Mô phỏng training loop
500 mẫu · 16 batch/epoch · 3 epoch · tổng 48 iteration
8 = gradient nhiễu · 32 = mặc định · 128 = mượt, cần nhiều RAM
Tập dữ liệu (batch đang được xử lý sáng lên)
Loss theo iteration
Quỹ đạo SGD trên bản đồ loss
Hãy thử: chạy với batch = 8 và xem đường loss zig‑zag mạnh. Chuyển sang batch = 128 — đường loss mượt hơn, quỹ đạo SGD đi gần như thẳng về điểm minimum.
Epoch = 1 vòng qua toàn bộ dữ liệu. Batch = 1 lô nhỏ → 1 lần cập nhật trọng số (qua gradient descent). Iteration = 1 lần cập nhật. Giống đội shipper Shopee: mỗi xe chở 1 batch đơn hàng, giao hết tất cả đơn = xong 1 epoch, hôm sau có đơn mới lại chạy epoch tiếp theo.
ImageNet: 1.2 triệu ảnh, batch_size = 256. Mỗi epoch có bao nhiêu iteration?
Giải thích
Một training loop deep learning gồm ba khái niệm lồng nhau: epoch chứa nhiều batch, mỗi batch thực hiện 1 iteration (forward + backward + cập nhật trọng số).
Với N = tổng số mẫu, B = batch size, E = số epoch. Dấu ceiling vì batch cuối có thể nhỏ hơn B.
| Khái niệm | Định nghĩa | Ví dụ (N=1000, B=100, E=5) |
|---|---|---|
| Batch | Nhóm B mẫu → 1 lần cập nhật trọng số | 100 mẫu / batch |
| Iteration | forward + backward + optimizer.step() | 10 iter / epoch |
| Epoch | 1 vòng qua toàn bộ N mẫu | 5 epoch = 50 iter |
| Shuffle | Xáo trộn thứ tự trước mỗi epoch | Batch khác nhau mỗi epoch |
Vừa (32–256): "sweet spot" cho đa số bài toán computer vision.
Lớn (256–4096): mượt, tận dụng GPU tốt, nhưng cần tăng learning rate theo quy tắc linear scaling và dễ rơi vào sharp minima.
Mỗi iteration gồm: forward pass → tính loss → backpropagation → cập nhật trọng số. Chạy nhiều epoch giúp model thấy dữ liệu nhiều lần, nhưng quá nhiều có thể gây overfitting.
# ------------------------------------------------------------------
# Training loop PyTorch chuẩn — minh hoạ đầy đủ epoch × batch
# ------------------------------------------------------------------
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, Dataset
class DummyDataset(Dataset):
"""Tập dữ liệu giả lập 500 mẫu x 10 chiều."""
def __init__(self, n: int = 500):
self.x = torch.randn(n, 10)
self.y = torch.randint(0, 2, (n,))
def __len__(self):
return len(self.x)
def __getitem__(self, idx):
return self.x[idx], self.y[idx]
# 1) Dataset + DataLoader --------------------------------------------
dataset = DummyDataset(n=500)
loader = DataLoader(
dataset,
batch_size=32, # <-- batch size ở đây
shuffle=True, # xáo trộn trước mỗi epoch
num_workers=4, # 4 worker load song song
pin_memory=True, # tăng tốc transfer CPU → GPU
drop_last=False, # giữ batch cuối kể cả khi <32 mẫu
)
# 2) Mô hình + loss + optimizer --------------------------------------
model = nn.Sequential(
nn.Linear(10, 32),
nn.ReLU(),
nn.Linear(32, 2),
)
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.06, momentum=0.9)
EPOCHS = 5
# 3) Training loop ---------------------------------------------------
for epoch in range(EPOCHS):
running_loss = 0.0
model.train()
for batch_idx, (xb, yb) in enumerate(loader):
# forward
logits = model(xb)
loss = loss_fn(logits, yb)
# backward
optimizer.zero_grad() # xóa grad cũ TRƯỚC khi backward
loss.backward() # tính d(loss)/d(weights)
# update
optimizer.step() # w <- w - lr * grad
running_loss += loss.item()
# log
if batch_idx % 5 == 0:
print(
f"[epoch {epoch+1}/{EPOCHS}] "
f"batch {batch_idx+1}/{len(loader)} "
f"loss={loss.item():.4f}"
)
avg = running_loss / len(loader)
print(f"Epoch {epoch+1} xong — loss TB: {avg:.4f}")
# 4) Tổng số iteration = len(loader) * EPOCHS = ceil(500/32) * 5 = 80
print("Tổng iteration:", len(loader) * EPOCHS)optimizer.step(). Hiệu quả = batch 256 nhưng chỉ cần RAM cho 64 mẫu cùng lúc.# Gradient accumulation: mô phỏng batch lớn trên GPU nhỏ
ACCUM_STEPS = 4 # batch hiệu quả = batch_size * 4
micro_batch_size = 64 # vừa với 8GB GPU
# -> effective batch = 256
model.train()
optimizer.zero_grad()
for i, (xb, yb) in enumerate(loader):
logits = model(xb)
loss = loss_fn(logits, yb) / ACCUM_STEPS # chia đều để trung bình đúng
loss.backward() # gradient TÍCH LUỸ
if (i + 1) % ACCUM_STEPS == 0:
optimizer.step() # update sau 4 batch
optimizer.zero_grad() # mới reset gradBạn train 10 epoch nhưng quên shuffle=True. Điều gì có khả năng xảy ra?
- Batch = 1 lô nhỏ dữ liệu → 1 lần cập nhật trọng số. Epoch = 1 vòng qua toàn bộ dữ liệu.
- iterations = ⌈N / batch_size⌉ × epochs. ImageNet 90 epoch batch 256 ≈ 422.000 update.
- Shuffle trước mỗi epoch: batch khác nhau → gradient đa dạng → thường generalize tốt hơn.
- Batch nhỏ (8–32) = noisy nhưng regularize. Batch lớn (>256) = mượt, cần tăng LR theo linear scaling.
- GPU thiếu RAM? Gradient accumulation: tích lũy gradient qua N batch nhỏ rồi mới optimizer.step().
- Loss curve mượt dần khi tăng batch (noise ~ 1/√B). Batch quá lớn có thể rơi vào sharp minima → test kém.
Kiểm tra hiểu biết
Dataset 10.000 mẫu, batch_size = 100, train 5 epoch. Tổng cộng bao nhiêu lần cập nhật trọng số?
Batch GD dùng toàn bộ N mẫu mỗi update — chính xác nhất về gradient, nhưng cực chậm với dataset lớn. SGD dùng 1 mẫu — nhanh, nhiễu, hay thoát local minima. Mini‑batch GD (batch 32–512) là dung hoà: tận dụng vector hoá GPU + noise vừa phải.
Batch Normalization phụ thuộc vào thống kê trong batch hiện tại. Nếu batch quá nhỏ (≤ 8), thống kê không ổn định → BN hoạt động kém. Giải pháp: GroupNorm hoặc LayerNorm, hoặc dùng Sync BN qua nhiều GPU.
Train lâu có thể crash. Hãy torch.save(model.state_dict(), ...) sau mỗi epoch (hoặc mỗi N batch). Tốt nhất lưu kèm epoch, optimizer state, và loss hiện tại để có thể resume chính xác.
Trong huấn luyện LLM hiện đại, người ta hay đo bằng số token đã thấy thay vì epoch (vì dataset quá lớn — có thể chỉ train 1 epoch!). GPT‑3 train trên ~300B token — chưa hết 1 vòng Common Crawl + Books + Wikipedia.