BFloat16 (BF16)
BFloat16 (BF16) là định dạng số thực 16 bit có số mũ 8 bit như FP32 và phần định trị 7 bit, giữ nguyên phạm vi của FP32 với một nửa dung lượng bộ nhớ. Khác biệt so với FP16.
BFloat16 (số dấu phẩy động theo định dạng não, BF16) là định dạng dấu phẩy động 16 bit gồm 1 bit dấu, 8 bit số mũ và 7 bit phần định trị. Định dạng này giữ nguyên độ rộng số mũ của dấu phẩy động 32 bit tiêu chuẩn (FP32), nên bao phủ cùng phạm vi giá trị, từ khoảng 1.2 × 10⁻³⁸ đến 3.4 × 10³⁸, trong khi chỉ dùng một nửa bộ nhớ để lưu mỗi số. Đổi lại là độ chính xác: 7 bit phần định trị chỉ cho khoảng hai đến ba chữ số thập phân có nghĩa. Học sâu xử lý tốt sai số làm tròn này, và định dạng được hình thành tại Google Brain dành cho TPU của Google nay đã trở thành định dạng 16 bit mặc định để huấn luyện model lớn trên GPU hiện đại và các bộ tăng tốc.
BF16 biểu diễn số như thế nào#
Giá trị BF16 chính là nửa trên của giá trị FP32: cùng bit dấu và số mũ, nhưng phần định trị giảm từ 23 bit xuống còn 7 bit. Vì vậy, chuyển FP32 sang BF16 chỉ cần làm tròn bỏ 16 bit thấp; khi chuyển ngược lại, các bit 0 được thêm vào cuối, khiến định dạng này dễ triển khai trên phần cứng. Định dạng 16 bit phổ biến còn lại, IEEE FP16 hay độ chính xác bán phần, phân chia các bit theo cách khác với 5 bit số mũ và 10 bit phần định trị, nhờ đó tăng độ chính xác nhưng giảm phạm vi giá trị. So sánh ba định dạng trên cùng các giá trị sẽ cho thấy sự đánh đổi:
import torch
# Largest value, smallest normal value, and spacing just above 1.0 for each format
for dtype in [torch.float32, torch.float16, torch.bfloat16]:
info = torch.finfo(dtype)
print(f"{dtype!s:15} max={info.max:.3g} tiny={info.tiny:.3g} eps={info.eps:.3g}")
# A large activation, a tiny gradient, and a value that needs fine precision
x = torch.tensor([70000.0, 1e-8, 1.001953125])
print("FP16:", x.half().tolist())
print("BF16:", x.bfloat16().tolist())BF16 đạt giá trị tối đa 3.39e38, so với 65,504 của FP16; giá trị chuẩn nhỏ nhất của nó là 1.18e-38, bằng FP32. FP16 biến 70,000 thành vô cực và 1e-8 thành 0, trong khi BF16 giữ hai giá trị này lần lượt là 70,144 và khoảng 1.0e-8. Khoảng cách nhỏ hơn giữa các giá trị của FP16 (eps 0.000977 so với 0.00781 của BF16) giữ chính xác giá trị 1.001953125, còn BF16 làm tròn giá trị đó thành 1.0.
BF16 và FP16 trong huấn luyện#
Trong quá trình huấn luyện, phạm vi giá trị quan trọng hơn độ chính xác. Gradient thường nhỏ hơn các giá trị nhỏ nhất của FP16 và biến mất, vì vậy huấn luyện FP16 với độ chính xác hỗn hợp nhân loss với một hệ số tỷ lệ trước khi lan truyền ngược, rồi giảm hệ số đó bất cứ khi nào gradient bị tràn; kỹ thuật này gọi là scale loss. BF16 không cần scale loss vì phạm vi của nó bằng FP32, và huấn luyện BF16 cho kết quả tương đương FP32 trên các model thị giác, tiếng nói, ngôn ngữ và gợi ý sau cùng số vòng lặp mà không cần thay đổi siêu tham số. Huấn luyện độ chính xác hỗn hợp giữ bản sao chính của trọng số ở FP32 và thực hiện phép nhân ma trận bằng BF16 với tích lũy FP32, vì cộng nhiều tích có phần định trị 7 bit trong BF16 sẽ làm mất quá nhiều thông tin. Việc chọn giữa hai định dạng 16 bit chủ yếu phụ thuộc vào phần cứng:
PyTorch áp dụng BF16 thông qua độ chính xác hỗn hợp tự động với torch.autocast(device_type="cuda", dtype=torch.bfloat16). Ultralytics YOLO cung cấp cùng tùy chọn này cho huấn luyện bằng amp="bf16"; tùy chọn chạy lượt truyền xuôi với chế độ tự động ép kiểu BF16 và tắt bộ chia tỷ lệ gradient được sử dụng trong chế độ FP16 mặc định, trên GPU CUDA có hỗ trợ BF16 gốc.
Hỗ trợ phần cứng#
Cloud TPU thế hệ thứ hai và thứ ba của Google thực hiện phép nhân BF16 bên trong các đơn vị ma trận và tích lũy ở FP32; các thế hệ TPU sau đó vẫn giữ định dạng này. GPU NVIDIA chạy BF16 trên Tensor Core từ thế hệ Ampere trở đi, bao gồm GPU A100, H100 và Blackwell; bộ xử lý Intel Xeon có Advanced Matrix Extensions (AMX) tăng tốc định dạng này trên CPU. Trên NVIDIA A100, phép tính Tensor Core BF16 và FP16 đều đạt đỉnh 312 teraFLOPS, gấp 16 lần mức 19.5 teraFLOPS của phép tính FP32 tiêu chuẩn trên GPU.
BF16 và các định dạng liên quan#
- Độ chính xác bán phần (FP16): Định dạng IEEE 16 bit, có độ chính xác cao hơn nhưng phạm vi nhỏ hơn nhiều. Định dạng này vẫn phổ biến trong suy luận, khi các activation nằm trong phạm vi của nó và độ chính xác bổ sung có ích.
- TF32: Chế độ tính toán Tensor Core của NVIDIA có số mũ 8 bit như BF16 và phần định trị 10 bit như FP16; các thư viện có thể dùng chế độ này cho phép nhân ma trận FP32 trên GPU Ampere trở lên trong khi tensor vẫn được lưu trữ ở dạng 32 bit.
- FP8: Các định dạng dấu phẩy động 8 bit như E4M3 và E5M2, được GPU Hopper và Blackwell hỗ trợ; các định dạng này tiếp tục giảm một nửa bộ nhớ cần dùng cho huấn luyện và suy luận trên những model lớn nhất.
- Lượng tử hóa model: Chuyển trọng số đã huấn luyện sang các định dạng số nguyên như INT8 hoặc INT4 để triển khai.
BF16 trong các mô hình ngôn ngữ lớn#
Hầu hết mô hình ngôn ngữ lớn (LLM) có trọng số mở được huấn luyện bằng độ chính xác hỗn hợp BF16 và phát hành với trọng số BF16; đây là mốc tham chiếu để đánh giá các phiên bản FP8 và INT4 đã lượng tử hóa. Với 2 byte cho mỗi tham số, trọng số của một model 70 tỷ tham số chiếm 140 GB ở BF16, nhiều hơn dung lượng 80 GB của một NVIDIA H100, vì vậy phục vụ model như vậy ở BF16 cần chia model trên ít nhất hai GPU.










