Data Parallelism — Chia batch cho nhiều GPU, đơn giản nhất
Hiểu cách training LLM tỷ parameter trên hàng trăm GPU: chia data, tính gradient riêng, rồi đồng bộ hóa. Không cần code, chỉ cần hiểu bản chất chi phí và tốc độ.
Training GPT-4 tốn hàng trăm triệu USD và chạy trên hàng nghìn GPU. Làm sao để một mô hình "ăn" dữ liệu từ nhiều card đồ họa cùng lúc mà không bị lộn xộn? Data Parallelism là câu trả lời đơn giản nhất: chia đều, tính riêng, rồi cập nhật chung.
Vấn đề
Training LLM hiện đại (như Llama 3 70B) cần hàng chục nghìn GPU chạy song song. Nếu chỉ dùng 1 GPU, bạn sẽ phải chờ... vài năm để huấn luyện xong một model tầm cỡ. Nhưng đơn giản là mua thêm GPU không đủ — bạn cần cách chia công việc sao cho mỗi card xử lý một phần data đồng thời, rồi kết hợp lại.
Cách làm cũ (training tuần tự trên 1 GPU) không scale được vì ba lý do:
1. Memory wall: Một GPU A100 chỉ có 80GB VRAM. Với model 70B parameters ở precision BF16, chỉ weights đã chiếm ~140GB — chưa kể gradients và optimizer states (Adam cần thêm 2x bộ nhớ). Không thể nhét vừa.
2. Speed: Training trên 1 GPU với batch size nhỏ (4-8 samples) thì gradient noisy và hội tụ chậm. Batch size lớn thì... không vừa memory. Bạn bị kẹt giữa chậm và nghẽn.
3. Cost: Theo Scaling Laws, training model lớn cần FLOPs tỷ lệ thuận với số parameters và data. Nếu training GPT-4 trên 1 GPU A100 ($10k), bạn cần chạy liên tục ~100 năm. Thuê 10,000 GPU chạy song song 1 tháng rẻ hơn nhiều so với thuê 1 GPU chạy 100 năm.
Ý tưởng cốt lõi
Hãy tưởng tượng bạn đang dạy một lớp học sinh cách giải toán. Thay vì một học sinh làm hết 1000 bài tập, bạn chia cho 10 bạn, mỗi bạn làm 100 bài. Xong bài, các bạn họp lại so đáp án: "Bài này đa số chọn C, vậy đáp án đúng là C". Rồi tất cả cùng ghi chép lại quy tắc mới vào sổ tay — đảm bảo 10 bạn có kiến thức giống hệt nhau.
Data Parallelism làm đúng điều đó:
- Chia data: Batch 1000 samples → chia thành 10 phần, mỗi GPU xử lý 100 samples (local batch).
- Tính local gradient: Mỗi GPU tính gradient (hướng cần điều chỉnh weight) dựa trên phần data của mình.
- All-Reduce: Các GPU "gặp nhau" qua mạng, cộng trung bình tất cả gradient lại. Vì gradient có tính tuyến tính, trung bình của gradient = gradient của trung bình.
- Cập nhật chung: Tất cả GPU cùng cập nhật model với gradient đã tổng hợp. Sau bước này, 10 bản sao model trên 10 GPU giống hệt nhau byte-for-byte.
That's it. Bản chất chỉ là: chia để trị, rồi trung bình hóa.
Misconception phổ biến: Nhiều người nghĩ mỗi GPU train một "phiên bản model khác nhau" rồi vote lấy đa số. Không phải. Tất cả GPU luôn giữ bản sao model giống hệt nhau (replica), chỉ data là khác. Sau mỗi bước, chúng đồng bộ để model giống nhau hoàn toàn — như 10 bản sao sách giáo khoa được sửa lỗi giống hệt nhau sau mỗi buổi học.
Tại sao nó hoạt động
Gradient descent có tính chất tuyến tính (linear): gradient của tổng = tổng của gradient.
Với là tổng batch size, là số GPU, là local batch size. Mỗi GPU tính phần trong ngoặc đơn, rồi ta lấy trung bình phần đó.
Trong thực tế, PyTorch DDP (Distributed Data Parallel) tự động hóa việc này bằng thuật toán Ring-AllReduce: các GPU xếp vòng tròn, truyền gradient như truyền tin, mỗi GPU chỉ cần gửi/nhận từ hai hàng xóm. Sau bước truyền, tất cả GPU đều có tổng gradient. Cách này hiệu quả hơn là một GPU trung tâm thu thập từ tất cả (tránh bottleneck).
Pseudo-code PyTorch:
# Khởi tạo: mỗi GPU có bản sao model giống hệt nhau
model = DistributedDataParallel(model)
for batch in dataloader:
# batch được tự động chia đều cho các GPU
local_output = model(local_batch)
loss = criterion(local_output, local_target)
loss.backward() # Tính local gradient
# Tự động: All-Reduce gradient giữa các GPU
# Tất cả GPU cập nhật giống hệt nhau
optimizer.step()Overlap trick: Thư viện hiện đại (FSDP, DeepSpeed) không chờ backward xong mới truyền gradient. Chúng chia gradient thành từng "bucket" nhỏ, vừa tính xong phần này liền truyền phần đó, che giấu độ trễ mạng (latency hiding).
Ý nghĩa thực tế
So sánh với training 1 GPU:
| Metric | 1 GPU | 8 GPUs (Data Parallel) | Lưu ý |
|---|---|---|---|
| Thời gian/epoch | 8 giờ | ~1 giờ | Gần như chia đều, trừ overhead đồng bộ |
| Batch size hiệu quả | 8 (nhỏ, noisy) | 64 (lớn, ổn định) | Gradient tốt hơn, hội tụ nhanh hơn |
| VRAM cần/GPU | 80GB (full model) | 80GB (full model) | Vẫn cần chứa toàn bộ model |
| Chi phí điện/GPU | 100% | 100% | Tổng tiền điện tăng tuyến tính, nhưng wall-clock giảm |
Ai đang dùng: Mọi framework training LLM (PyTorch DDP, Horovod, JAX pmap) đều dùng. Training GPT-4, Llama 3, Gemini đều bắt đầu từ Data Parallelism làm nền tảng, rồi mới thêm các chiêu phức tạp hơn như Pipeline Parallelism hay Tensor Parallelism.
Limitations — Cái này KHÔNG giải quyết được gì?
-
Model quá lớn cho 1 GPU: Data Parallelism không giảm bộ nhớ cần thiết cho mỗi GPU. Llama 3 405B cần ~800GB chỉ cho weights — không vừa VRAM 80GB dù dùng 1000 GPU. → Cần kết hợp với ZeRO Optimizer để shard optimizer states, hoặc Model Parallelism.
-
Communication bottleneck: Nếu dùng Ethernet thay vì NVLink/InfiniBand, thời gian chờ đồng bộ gradient (đặc biệt với model lớn, gradient có GB dữ liệu) có thể ăn mất 50% thời gian. Data Parallelism chỉ hiệu quả khi bandwidth cao.
-
Batch size quá lớn: Khi chia batch cho 1024 GPU, mỗi GPU chỉ có 1-2 samples, làm gradient quá noisy hoặc tốn thêm bước đồng bộ. Cần kỹ thuật như Gradient Accumulation để giả lập batch lớn mà không tăng số GPU.
Đào sâu hơn
Paper gốc:
- "Large Scale Distributed Deep Networks" (Jeff Dean et al., NIPS 2012) — mặc dù cũ nhưng đặt nền móng cho việc song song hóa data trong Deep Learning.
- "Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour" (Goyal et al., 2017) — chứng minh Data Parallelism có thể scale lên batch size khổng lồ (8k) mà không mất accuracy với các trick learning rate scaling.
Bài liên quan TroiSinh:
Cùng cụm (Training at Scale):
- Pre-training + Fine-tuning — Hiểu vòng đời model trước khi nghĩ đến scale
- Scaling Laws (Chinchilla) — Tại sao cần scale data và model cùng lúc, không chỉ thêm GPU
- Gradient Accumulation — Khi GPU đã nhiều nhưng batch size vẫn chưa đủ lớn
Đọc tiếp (Level 1 — Training Efficiency):
- ZeRO Optimizer — Cách vượt qua giới hạn memory của Data Parallelism bằng cách shard optimizer states
- Pipeline Parallelism — Khi model quá lớn, chia layer cho nhiều GPU thay vì chia data
- Mixed Precision — Kết hợp với Data Parallel để tăng tốc gấp đôi
External resources:
- PyTorch Distributed Overview — Giải thích All-Reduce và Ring-AllReduce bằng hình ảnh trực quan.
- Blog: "How to Train Your Model on 1000 GPUs" (Boris Ginsburg, NVIDIA) — Chi tiết kỹ thuật overlapping communication/computation trong production.
Scaling Laws (Chinchilla) — Quy luật giữa model size, data và compute
Tại sao model 70B thua model 70B? Chinchilla scaling laws giải thích mối quan hệ power-law giữa số lượng tham số (N), dữ liệu (D) và compute budget — và tại sao GPT-4, Llama 3 được train theo cách này.
Mixed Precision (FP16/BF16) — Nửa precision, gấp đôi tốc độ
Dùng 16-bit thay vì 32-bit để train model lớn nhanh gấp 2 lần, tiết kiệm nửa bộ nhớ — bí kíp giúp training GPT-4 không tốn 200 triệu USD