Thuật toán hạ độ dốc ngẫu nhiên và hạ độ dốc theo lô nhỏ (stochastic and mini-batch gradient descent): Lý do mọi chu trình huấn luyện thực tế đều thực hiện các bước cập nhật nhỏ và có độ nhiễu.
Thuật toán hạ độ dốc (gradient descent), như đã được đề cập trong loạt bài này, có một giả định ngầm định: để thực hiện một bước cập nhật duy nhất, thuật toán phải xử lý toàn bộ dữ liệu huấn luyện. Phương pháp này có thể phù hợp với bốn mẫu dữ liệu, nhưng hoàn toàn bất khả thi đối với quy mô một triệu mẫu.
Quá trình huấn luyện thực tế không vận hành theo phương thức đó. Hệ thống chỉ lấy ngẫu nhiên một nhóm nhỏ các mẫu dữ liệu, gọi là lô nhỏ (mini-batch), và thực hiện bước cập nhật dựa trên nhóm mẫu này. Bài viết phân tích nguyên lý hoạt động, chi phí tính toán và phương thức triển khai trên PyTorch. Các số liệu được dẫn từ một ví dụ thực nghiệm thu nhỏ và tài liệu bài giảng môn Học sâu (Deep Learning) mã CS878 (các bài giảng
Phương pháp hạ độ dốc ngẫu nhiên và theo lô nhỏ (mini-batch): Nguyên nhân mọi chu trình huấn luyện thực tế đều thực hiện các bước dịch chuyển nhỏ và có độ nhiễu.
Phương pháp hạ độ dốc (gradient descent), như đã đề cập trong loạt bài này, dựa trên một giả định ngầm định: để thực hiện một bước cập nhật duy nhất, thuật toán phải xét toàn bộ mẫu dữ liệu huấn luyện. Cách tiếp cận này khả thi với 4 mẫu, nhưng hoàn toàn bất khả thi khi quy mô lên tới một triệu mẫu.
Trong thực tế, quá trình huấn luyện không bao giờ vận hành theo cách thức đó, mà lựa chọn ngẫu nhiên một nhóm nhỏ các mẫu dữ liệu (mini-batch) để tiến hành bước cập nhật. Bài viết này làm rõ cơ chế hoạt động, chi phí tính toán và cách triển khai phương pháp trên nền tảng PyTorch. Các số liệu được trích xuất từ một ví dụ mẫu tinh gọn và từ tài liệu bài giảng môn Học sâu (Deep Learning) mã hiệu CS878 (Bài giảng 07-08).
Gradient là giá trị trung bình
Hàm mất mát (loss) của một mô hình là giá trị trung bình tính trên toàn bộ các mẫu huấn luyện: L = (1/N) Σ ℓᵢ, trong đó ℓᵢ là độ mất mát đối với mẫu i. Do đạo hàm của một giá trị trung bình bằng trung bình cộng các đạo hàm, nên gradient cũng là một giá trị trung bình: ∇L = (1/N) Σ ∇ℓᵢ.
Xét mô hình có hai trọng số, ŷ = w·x, với hàm mất mát bình phương ℓᵢ = ½(ŷᵢ − yᵢ)², cùng 4 mẫu dữ liệu:
x₁ = (4, −1), y₁ = 1
x₂ = (0, 1), y₂ = 3
x₃ = (1, 1), y₃ = 3
x₄ = (1, −1), y₄ = 1
Khởi đầu tại giá trị w = (0, 0). Tại điểm này, gradient của mỗi mẫu là −yᵢ·xᵢ, tạo thành 4 vectơ tương ứng: (−4, 1), (0, −3), (−3, −3) và (−1, 1).
Khi cộng nối tiếp các vectơ này, tổng nhận được là (−8, −4). Chia giá trị trên cho 4, ta có gradient thực tế: ∇L = (−2, −1). Một bước cập nhật với tốc độ học (learning rate) bằng ¼ sẽ dịch chuyển theo hướng ngược lại, đưa w tới giá trị (½, ¼).
Bốn mẫu dữ liệu tương ứng 4 vectơ, khi cộng nối tiếp và chia cho 4 sẽ cho ra gradient thực tế (−2, −1).
Trong trường hợp quy mô dữ liệu tăng lên một triệu mẫu — tài liệu bài giảng dẫn chứng n = 2²⁰, tương đương khoảng một triệu hình ảnh — mỗi bước cập nhật đều phải xử lý toàn bộ số mẫu này. Đây là khối lượng tính toán quá lớn chỉ cho một bước dịch chuyển nhỏ.
Mini-batch: Phép ước lượng khách quan với chi phí thấp
Do đó, giải pháp tối ưu là xáo trộn ngẫu nhiên dữ liệu, chia thành các lô nhỏ (mini-batch), và chỉ lấy trung bình gradient của từng lô riêng biệt:
g_B = (1/B) Σ_{i thuộc lô} ∇ℓᵢ
Với 4 mẫu kể trên và kích thước lô B = 2, giả định sau khi xáo trộn thu được hai lô gồm {1, 3} và {2, 4}.
Lô thứ nhất: ½[(−4, 1) + (−3, −3)] = (−7/2, −1). Độ dài bước quá lớn.
Lô thứ hai: ½[(0, −3) + (−1, 1)] = (−1/2, −1). Độ dài bước quá ngắn.
Bước cập nhật ước tính từ hai lô này lần lượt đưa giá trị tới điểm (7/8, ¼) và (1/8, ¼), nằm về hai phía so với điểm đích thực tế là (½, ¼). Tuy nhiên, giá trị trung bình của chúng: ½[(−7/2, −1) + (−1/2, −1)] = (−2, −1), hoàn toàn trùng khớp với gradient thực tế.
Đây chính là đặc tính then chốt. Xét trên sự lựa chọn ngẫu nhiên của các lô dữ liệu, kỳ vọng toán học của g_B chính là ∇L. Dù phép ước lượng có độ nhiễu, nhưng đây là ước lượng không chệch: không bị lệch về bất kỳ hướng nào.
Gradient của lô thứ nhất quá dài, của lô thứ hai quá ngắn, song giá trị trung bình của cả hai phản ánh chính xác gradient thực tế.
Mức độ nhiễu của phép ước lượng
Nếu gradient của một mẫu có độ phân tán là σ so với gradient thực tế, thì một lô gồm B mẫu sẽ có độ phân tán là σ/√B (trong điều kiện kích thước tập dữ liệu lớn hơn nhiều so với kích thước lô).
Quy luật căn bậc hai này giữ vai trò rất quan trọng. Khi tăng kích thước lô lên 4 lần, chi phí tính toán tăng gấp 4 lần nhưng độ nhiễu chỉ giảm đi một nửa. Việc tăng từ 1 lên 64 mẫu giúp giảm độ phân tán 8 lần. Vượt qua một ngưỡng nhất định, việc mở rộng thêm kích thước lô mang lại mức độ chính xác gia tăng không đáng kể so với chi phí bỏ ra. Một lô nhỏ đã đủ khả năng định hướng cập nhật chuẩn xác.
Vùng phân tán của các gradient theo lô nhỏ quanh gradient thực tế thu hẹp theo tỷ lệ σ/√B, trong khi chi phí tính toán lại tăng tuyến tính theo B.
So sánh hiệu quả trên cùng mức chi phí tính toán
Đặt 3 phương pháp vào cùng một mức chi phí: duyệt qua 1.024 mẫu dữ liệu một lần (được gọi là một chu kỳ huấn luyện - epoch), với cùng một tốc độ học.
Phương pháp duyệt toàn bộ dữ liệu (Full batch) thực hiện một bước cập nhật thận trọng duy nhất. Sau toàn bộ chu kỳ, vị trí đạt được vẫn còn cách xa điểm tối ưu (cực tiểu).
Phương pháp hạ độ dốc ngẫu nhiên thuần túy (SGD với B = 1) thực hiện 1.024 bước cập nhật nhiều dao động. Thuật toán tiến được tới điểm cực tiểu nhưng liên tục dao động xung quanh vị trí này.
Phương pháp hạ độ dốc theo lô nhỏ (Mini-batch với B = 64) thực hiện 16 bước cập nhật ổn định và kết thúc ở vị trí sát điểm cực tiểu với biên độ dao động rất nhỏ.
Yếu tố phần cứng cũng là một nguyên nhân quan trọng. GPU xử lý song song nhiều mẫu dữ liệu cùng lúc. Một lô (batch) gồm 64 mẫu chỉ cần một lệnh gọi GPU để vận hành toàn bộ luồng xử lý; do đó, một chu kỳ huấn luyện (epoch) theo mini-batch chỉ tốn 16 lệnh gọi thay vì 1.024 lệnh.
Khi biểu diễn hàm mất mát (loss) theo các bước lặp, phương pháp huấn luyện toàn bộ dữ liệu (full batch) tạo ra đường đồ thị mượt mà, SGD tạo ra đường gấp khúc, trong khi mini-batch nằm ở khoảng giữa. Tính chất trung gian này là lý do mini-batch SGD trở thành lựa chọn mặc định.
Với một lượt duyệt qua 1.024 mẫu dữ liệu khi so sánh cả ba phương pháp: full batch thực hiện 1 bước, B = 64 thực hiện 16 bước, B = 1 thực hiện 1.024 bước; một bước xử lý với B = 64 sẽ kích hoạt đồng thời toàn bộ 64 luồng tính toán của GPU.
Thử nghiệm trên PyTorch
Trong PyTorch, kích thước lô được xác định tại DataLoader. Mỗi lô tương ứng với một bước lặp (iteration) và một bước cập nhật của thuật toán tối ưu hóa. Một lượt duyệt toàn bộ dữ liệu tạo thành một chu kỳ (epoch).
loader = DataLoader(data, batch_size=64, shuffle=True)
opt = optim.SGD(net.parameters(), lr=0.1)
for epoch in range(3):
for xb, yb in loader:
loss = loss_fn(net(xb), yb)
opt.zero_grad()
loss.backward()
opt.step()
Bộ dữ liệu MNIST gồm 60.000 ảnh huấn luyện. Với kích thước lô bằng 64, số bước lặp trong mỗi chu kỳ là ⌈60.000 / 64⌉ = 938 bước, trong đó lô cuối cùng chứa 32 ảnh còn lại. Quá trình này có thể được kiểm chứng mà không cần dữ liệu thực tế:
import torch.utils.data as D
import torch
X = torch.zeros(60000, 1)
dl = D.DataLoader(X, 64)
*_, last = dl
print(len(dl), len(last))
Kết quả:
938 32
Nếu thay đổi giá trị 64 thành 1, hệ thống sẽ chuyển sang phương pháp SGD thuần túy với 60.000 bước mỗi chu kỳ. Nếu chuyển thành 60.000, quy trình trở về trạng thái full batch với một bước duy nhất mỗi chu kỳ. Thuật toán tối ưu vẫn giữ nguyên là optim.SGD trong cả hai trường hợp. Việc chia theo lô chính là yếu tố định hình nên mini-batch SGD.
Vòng lặp trong PyTorch: Một lô gồm 64 ảnh được xử lý đồng thời qua mạng nơ-ron, và phép chia 60.000 / 64 được làm tròn lên thành 938 bước cho mỗi chu kỳ.
Nhiễu có thể mang lại hiệu quả tích cực
Tại điểm yên ngựa (saddle point) phẳng, gradient thực tế gần như bằng không, khiến phương pháp full batch gần như không thể dịch chuyển. Gradient của mini-batch vẫn duy trì một lượng nhiễu nhỏ, đủ để làm dịch chuyển các trọng số và giúp mô hình tiếp tục quá trình tối ưu hóa dốc xuống.
Tuy nhiên, yếu tố nhiễu không thể giải quyết mọi trở ngại. Trong một thung lũng hẹp và dài, các bước cập nhật vẫn dao động theo đường zíc-zắc giữa hai vách. Việc kiểm soát quỹ đạo zíc-zắc này là nhiệm vụ của động lượng (momentum) - khái niệm tiếp theo trong chuỗi nội dung này.
Các điểm cốt lõi
Hàm mất mát là giá trị trung bình tính trên các mẫu dữ liệu, do đó gradient của hàm này là giá trị trung bình của các gradient trên từng mẫu.
Gradient của mini-batch là phép ước lượng có chi phí tính toán thấp cho gradient thực tế. Phép ước lượng này chứa nhiễu nhưng không chệch: về mặt trung bình, kết quả hoàn toàn chính xác.
Độ nhiễu giảm theo tỷ lệ σ/√B, do đó tăng kích thước lô lên bốn lần chỉ giúp giảm độ nhiễu đi một nửa, trong khi chi phí tính toán tăng gấp bốn lần.
Trong một chu kỳ, mini-batch thực hiện nhiều bước tính toán chi phí thấp chạy song song trên GPU, dung hòa giữa phương pháp full batch mượt mà và SGD thuần túy nhiều biến động.
Trong PyTorch, b
Nguồn tin: Dev.to Machine Learning — Tác giả: Syed Abdul Hanan. Bản dịch tiếng Việt do AI thực hiện, có thể có sai sót.