Tách biệt microbatch, lượt lan truyền ngược và cập nhật
Microbatch là nhóm ví dụ được xử lý bởi một lượt lan truyền xuôi trên một bản sao. Lượt lan truyền ngược tính đóng góp của nó vào gradient. Việc cập nhật của optimizer dùng các gradient hiện có để thay đổi tham số. Với tích lũy, nhiều lượt lan truyền xuôi/ngược diễn ra trước lần cập nhật này; các tham số không đổi trong suốt nhóm đó.
Trong PyTorch, các gradient tích lũy trong các tensor được dành cho việc đó. Vì vậy, xóa gradient sau mỗi microbatch sẽ hủy bỏ việc tích lũy mong muốn. Ngược lại, quên đặt lại chúng về không giữa hai nhóm sẽ khiến các ví dụ của lần cập nhật trước đó đóng góp.
Hãy xác định đơn vị ghi log của bạn: số microbatch, lần cập nhật của optimizer, số ví dụ hay token đã xem. Chỉ riêng từ «step» là mơ hồ. Một đường cong mất mát không thể so sánh đúng nếu trục biểu diễn số ví dụ gấp tám lần trong một trong các thí nghiệm.
Nguồn kỹ thuật: PyTorch — tích lũy và đặt lại gradient về không
Tính batch hiệu dụng mà không đếm trùng GPU
Gọi m là số ví dụ trên mỗi microbatch và mỗi bản sao, A là số microbatch được tích lũy, D là số bản sao song song dữ liệu. Nếu các kích thước này không đổi và các ví dụ được phân bổ đúng cách, số ví dụ đóng góp vào một lần cập nhật toàn cục là m × A × D.
Hệ số D không nhất thiết chỉ tất cả các card trong máy. Các GPU chia sẻ cùng một mô hình qua song song tensor hoặc song song pipeline không trở thành bấy nhiêu bản sao dữ liệu. Hãy ghi lại các nhóm thực sự được cấu hình, chứ không chỉ đơn thuần là số lượng thương mại của lô.
Ví dụ số học: hai mẫu cho mỗi microbatch, tám lần tích lũy và hai bản sao cho ra 32 mẫu cho mỗi lần cập nhật toàn cục. Mỗi bản sao xử lý mười sáu mẫu trong nhóm này. Bảng so sánh các số đếm; bảng không xếp hạng bộ nhớ hay tốc độ của chúng.
| m | A | D | Số mẫu cho mỗi lần cập nhật toàn cục |
|---|---|---|---|
| 2 | 8 | 2 | 32 |
| 1 | 16 | 2 | 32 |
| 4 | 4 | 2 | 32 |
| 2 | 8 | 1 | 16 |
Nguồn kỹ thuật: PyTorch — batch hiệu dụng và tích lũy với độ chính xác hỗn hợp · PyTorch — hành vi của các bản sao DistributedDataParallel
Chuẩn hóa loss theo số phần tử thực sự được đánh giá
Đối với loss trung bình trên các microbatch chứa cùng số phần tử liên quan, chia mỗi đóng góp cho A sẽ cho ra mức trung bình của nhóm. Quy tắc này giả định rằng framework chưa thực hiện việc chuẩn hóa này. Với một công cụ xử lý tích lũy, hãy đọc lại hợp đồng của nó trước khi thêm phép chia thủ công.
Với một giá trị loss trung bình trên mỗi token, các độ dài khác nhau sẽ làm thay đổi mẫu số. Cần lấy tổng các loss chia cho số token thực sự được giám sát của nhóm, không tính padding và các vị trí bị bỏ qua. Trung bình của các trung bình microbatch nói chung không cho ra cùng một mục tiêu.
Ví dụ lý thuyết: một microbatch có 512 token được giám sát với loss trung bình là 2; một microbatch khác có 1.536 token với trung bình là 4. Trung bình có trọng số bằng (512 × 2 + 1.536 × 4) ÷ 2.048 = 3,5. Trung bình không trọng số bằng 3 và đánh trọng số quá cao cho nhóm nhỏ. Các giá trị này chỉ minh họa cho phép tính.
Nguồn kỹ thuật: Hugging Face Accelerate — tích lũy với các ví dụ có kích thước thay đổi
Tổ chức một nhóm tích lũy hoàn chỉnh
Trước tiên, hãy chuẩn bị ranh giới của nhóm và mẫu số của nó. Với mỗi microbatch, hãy tính đầu ra, loss đã chuẩn hóa và lượt truyền ngược mà không cập nhật trung gian. Giải phóng các đầu ra bạn không còn cần nữa; việc giữ các loss gắn với đồ thị của chúng trong một danh sách có thể kéo dài vòng đời của các vùng cấp phát.
Sau đóng góp cuối cùng, hãy áp dụng các phép toán đã định trên gradient đầy đủ, rồi cập nhật. Sau đó đặt lại gradient cho nhóm tiếp theo. Nếu vòng lặp kết thúc với ít hơn A microbatch, hãy chọn một cách rõ ràng giữa việc xử lý nhóm một phần này với mẫu số thật của nó hoặc loại bỏ nó; ghi lại các ví dụ liên quan.
Với độ chính xác hỗn hợp dùng GradScaler, hệ số tỷ lệ vẫn không đổi trong suốt quá trình tích lũy. Việc chia tỷ lệ thực tế và clipping (nếu có) diễn ra sau các đóng góp; việc cập nhật scaler diễn ra sau lần thử bước. Các kiểm tra giá trị không hữu hạn có thể ngăn việc thay đổi tham số.
Scheduler phải tuân theo đơn vị mà vòng lặp của bạn công bố. Nếu nó được định nghĩa theo mỗi lần cập nhật optimizer, việc gọi nó ở mỗi microbatch sẽ làm thay đổi lịch trình. Hãy ghi riêng các lần thử và các lần cập nhật thực sự được áp dụng khi hệ thống của bạn có thể bỏ qua một số lần.
Nguồn kỹ thuật: PyTorch — đồ thị autograd và tensor được giữ lại cho lan truyền ngược · PyTorch — tích lũy, bỏ scale, cắt gradient và GradScaler
Trên nhiều GPU, kiểm tra việc giảm gradient và phân chia mẫu
DistributedDataParallel đồng bộ gradient giữa các bản sao. Trong hành vi thông thường, việc giảm lấy trung bình các gradient; do đó loss được cộng và loss được lấy trung bình cục bộ không cùng thang đo. Khi số token khác nhau giữa các bản sao, mẫu số toàn cục và phép giảm này phải được xem xét cùng nhau.
Hãy kiểm tra các ID thực sự được xử lý: vô tình nhân đôi cùng một mẫu trên mọi card không làm tăng thông tin của nhóm lên tương ứng. Để trì hoãn các giao tiếp trung gian, có thể dùng no_sync trên các microbatch trước lần đồng bộ cuối; ngữ cảnh của nó cũng phải bao trùm cả lượt lan truyền xuôi.
Đừng áp dụng quy tắc này cho mọi hệ thống phân tán. Sharding trạng thái, pipeline, hook giao tiếp và framework có thể thay đổi các phép toán thực tế. Hãy bắt đầu với cấu hình được công cụ của bạn hỗ trợ, rồi kiểm tra một nhóm đầy đủ trên mỗi bản sao.
Nguồn kỹ thuật: PyTorch — giảm gradient và phạm vi của no_sync trong DDP
Vì sao cùng một batch hiệu dụng không đảm bảo cùng một trải nghiệm
Đẳng thức m × A × D là một phép đếm. Để tái tạo một gradient của batch lớn, cần có các đóng góp được tính trọng số đúng, cùng trạng thái tham số trong suốt nhóm và các phép toán tương thích với cách phân tách này. Sự gần đúng về mặt số học được kiểm chứng bằng một dung sai phù hợp; nó không thể suy ra chỉ từ tích số.
BatchNorm tính toán thống kê từ các đầu vào trong lượt truyền của nó: nhiều microbatch nhỏ không đưa cho nó cùng những nhóm như một batch lớn. Các phép toán ngẫu nhiên, thứ tự tính toán và việc làm tròn cũng có thể khác nhau. Đừng hứa hẹn các trọng số cuối cùng giống hệt nhau đến từng bit.
Việc thay đổi batch toàn cục cũng có thể làm thay đổi số lần cập nhật cho cùng một số lượng mẫu đã xem. Hãy cố định trước trục so sánh và quy tắc chất lượng của bạn. Đừng thay đổi đồng thời tốc độ học, scheduler và thời lượng mà không ghi lại những giả định mới này.
Nguồn kỹ thuật: PyTorch — thống kê của BatchNorm1d · PyTorch — giới hạn về khả năng tái lập
Kiểm soát bộ nhớ và quyết định bước tiếp theo
Hãy đo đạc một nhóm bao gồm các lượt truyền ngược và lần cập nhật đầu tiên, rồi đến các nhóm tiếp theo. Việc thành công ở lượt truyền xuôi không xác nhận gradient hay các trạng thái do optimizer tạo ra. Các bộ đếm phải giữ đúng phạm vi theo từng device. Hồ sơ bộ nhớ cung cấp phương pháp đọc các baseline và đỉnh.
Nếu một nhóm thất bại, hãy giảm microbatch và tính lại A để giữ nguyên batch hiệu dụng mong muốn khi lựa chọn đó vẫn còn phù hợp. Thay đổi này không đảm bảo việc giảm đỉnh theo tỷ lệ thuận cũng như không đảm bảo thời gian tốt hơn. Nếu trọng số hoặc các trạng thái chiếm ưu thế, chỉ tích lũy có thể là chưa đủ.
Trước một chiến dịch, hãy kiểm tra một lần cập nhật trên một tập nhỏ đã kiểm soát: cùng các mẫu, mất mát có trọng số, gradient hữu hạn, ranh giới nhóm và số bước. Sau đó đánh giá chất lượng với giao thức đã chọn. MLP suy luận nhỏ trong hồ sơ có thể tải xuống không thực thi công thức huấn luyện này; nó không thay thế cho việc kiểm tra đó.
Bản tổng kết cuối cùng của bạn tập hợp m, A, D, các token được giám sát, độ chính xác, phép chuẩn hóa, cách xử lý nhóm cuối và các đỉnh quan sát được. Sau đó hãy quay lại với lựa chọn cấu hình và ngân sách thử nghiệm, tách biệt phần chuẩn bị, các lần thử và các kết quả thực sự được chấp nhận.
- Mất mát nhỏ bất thường: tìm lỗi chia hai lần cho tích lũy.
- Kết quả thay đổi theo cách chia nhỏ: kiểm tra token bị bỏ qua và phép lấy trung bình của các trung bình.
- Tích lũy không có tác dụng: kiểm tra zero_grad và optimizer.step.
- Bộ nhớ tăng dần: tìm các tham chiếu được giữ giữa các microbatch.
- Lịch trình bị lệch: phân biệt lượt truyền ngược và các lần cập nhật.
Nguồn kỹ thuật: Hugging Face — các điểm đo bộ nhớ của một lần huấn luyện
Câu hỏi thực tế
Tích lũy mười sáu microbatch có làm bộ nhớ tăng gấp mười sáu lần không?
Không nhất thiết: các đóng góp được xử lý lần lượt. Gradient vẫn tồn tại, trong khi các activation không cần thiết có thể được giải phóng. Tuy nhiên, đỉnh phụ thuộc vào mô hình, các tham chiếu được giữ và các trạng thái của optimizer; hãy đo cả nhóm đầy đủ.
Hai GPU có luôn gấp đôi batch hiệu dụng không?
Chỉ khi các GPU đó tham gia như hai bản sao dữ liệu với microbatch đã nêu. Những card chia sẻ cùng một mô hình không tự động tạo thành hai bản sao. Hãy kiểm tra các nhóm phân tán và các mẫu được xử lý.
Tôi có thể chia tất cả các mất mát cho số lần tích lũy không?
Quy tắc đơn giản này tương ứng với các microbatch có trọng số bằng nhau, không có phép chuẩn hóa đã được framework xử lý sẵn. Với số lượng token được giám sát thay đổi hoặc một nhóm cuối không đầy đủ, hãy dùng mẫu số thực của mục tiêu.