Half-Precision
Độ chính xác bán phần (FP16) lưu số bằng 16 bit thay vì 32 bit của FP32, giảm một nửa bộ nhớ và tăng tốc phép tính GPU. Các giới hạn, loss scaling và suy luận FP16.
Độ chính xác bán phần, còn gọi là FP16, float16 hoặc IEEE 754 binary16, là định dạng số dấu phẩy động 16 bit gồm 1 bit dấu, 5 bit số mũ và 10 bit phần định trị. So với độ chính xác đơn 32 bit tiêu chuẩn (FP32), định dạng này giảm một nửa bộ nhớ và băng thông cần cho mỗi số, đồng thời GPU có Tensor Cores thực hiện phép toán ma trận FP16 nhanh hơn FP32 nhiều lần. Đổi lại, phạm vi biểu diễn hẹp, giá trị lớn nhất là 65,504 và có khoảng ba chữ số thập phân có nghĩa. Mạng neural chịu được sai số làm tròn này khá tốt, nhờ đó FP16 trở thành độ chính xác tiêu chuẩn cho suy luận trên GPU và, khi kết hợp với FP32 trong độ chính xác hỗn hợp, là cách phổ biến để tăng tốc huấn luyện.
FP16 so với FP32#
FP32 dành 8 bit cho số mũ và 23 bit cho phần định trị, nên biểu diễn được khoảng từ 10⁻³⁸ đến 10³⁸ với khoảng bảy chữ số có nghĩa. 5 bit số mũ của FP16 giới hạn các giá trị chuẩn trong khoảng từ 6.1 × 10⁻⁵ đến 65,504; các giá trị dưới chuẩn nhỏ hơn có thể xuống tới khoảng 6 × 10⁻⁸ nhưng với độ chính xác giảm. Giá trị lớn hơn sẽ tràn thành vô cực, còn giá trị nhỏ hơn sẽ được làm tròn về 0. Chuyển đổi một model cho thấy cả lợi ích lẫn giới hạn:
import torch
import torchvision
# ResNet-18 stores its weights in FP32 by default
model = torchvision.models.resnet18()
fp32_mb = sum(p.numel() * p.element_size() for p in model.parameters()) / 1e6
# Casting to FP16 stores every weight in 2 bytes instead of 4
model.half()
fp16_mb = sum(p.numel() * p.element_size() for p in model.parameters()) / 1e6
print(f"Weights: {fp32_mb:.1f} MB in FP32, {fp16_mb:.1f} MB in FP16")
# FP16 overflows above 65,504 and rounds to about 3 significant digits
x = torch.tensor([65504.0, 70000.0, 0.1])
print("FP16 values:", x.half().tolist())Trọng số giảm từ 46.8 MB xuống còn 23.4 MB. Ở dòng thứ hai, 65,504 vẫn biểu diễn được dưới dạng giá trị FP16 lớn nhất, 70,000 trở thành inf, còn 0.1 được lưu thành 0.0999755859375, giá trị gần nhất mà FP16 có thể biểu diễn.
Độ chính xác bán phần trong huấn luyện#
Huấn luyện hoàn toàn bằng FP16 làm giảm độ chính xác hoặc khiến nhiều model phân kỳ, vì gradient nhỏ bị hụt về 0 và các cập nhật trọng số quá nhỏ so với trọng số bị làm tròn mất. Huấn luyện độ chính xác hỗn hợp khắc phục cả hai vấn đề: giữ một bản sao trọng số chính FP32 để cập nhật, chạy lượt lan truyền xuôi và lan truyền ngược bằng FP16, đồng thời nhân loss với một hệ số tỷ lệ để gradient nằm trong phạm vi FP16 trước khi được đưa về tỷ lệ ban đầu trong FP32. Các framework tự động điều chỉnh hệ số này, như GradScaler của PyTorch:
Activation FP16 cũng giảm một nửa bộ nhớ mà một batch chiếm dụng trong quá trình huấn luyện, nhờ đó cùng một GPU có thể xử lý batch lớn hơn hoặc đầu vào có độ phân giải cao hơn. Trên phần cứng hỗ trợ, bfloat16 (BF16) thường được ưu tiên cho huấn luyện, vì số mũ có kích thước như FP32 giúp loại bỏ nhu cầu scale loss.
Độ chính xác bán phần trong suy luận#
Suy luận không cần gradient, và activation của hầu hết model thị giác nằm trong phạm vi FP16, nên chuyển một model đã hoàn tất sang FP16 thường giữ độ chính xác gần với FP32 mà không cần dữ liệu hiệu chuẩn như lượng tử hóa model INT8. Các engine GPU như TensorRT xây dựng engine FP16 chạy trên Tensor Cores, còn GPU nhúng như NVIDIA Jetson dựa vào FP16 để suy luận thời gian thực. CPU hưởng lợi ít hơn vì nhiều CPU không có phép toán FP16 tốc độ cao. Ultralytics YOLO huấn luyện trên GPU với độ chính xác hỗn hợp FP16 theo mặc định (amp=True) và dùng quantize=16 cho dự đoán FP16 và xuất model, chẳng hạn với YOLO26:
from ultralytics import YOLO
# Tải model phát hiện YOLO26 nhỏ nhất
model = YOLO("yolo26n.pt")
# # Dự đoán bằng FP16 trên GPU; trên CPU, model PyTorch chạy bằng FP32
results = model.predict("https://ultralytics.com/images/bus.jpg", quantize=16)
# # Xuất model ONNX lưu trọng số dưới dạng FP16
path = model.export(format="onnx", quantize=16)File đã export lưu mỗi trọng số bằng 2 byte thay vì 4; cùng đối số quantize=16 tạo model FP16 cho TensorRT, OpenVINO, CoreML và các định dạng export khác.
Độ chính xác bán phần so với các định dạng liên quan#
- BFloat16 (BF16): Định dạng số dấu phẩy động 16 bit còn lại, có 8 bit số mũ và 7 bit phần định trị, đánh đổi độ chính xác để có phạm vi biểu diễn như FP32 và được ưu tiên khi huấn luyện model lớn.
- FP8: Các định dạng số dấu phẩy động 8 bit trên GPU NVIDIA đời mới giúp giảm tiếp một nửa bộ nhớ, chủ yếu dùng để huấn luyện và triển khai các mô hình ngôn ngữ lớn.
- Lượng tử hóa INT8: Chuyển giá trị thành số nguyên 8 bit với hệ số tỷ lệ theo tensor hoặc theo kênh; phương pháp này nhanh hơn và nhỏ gọn hơn FP16 nhưng cần dữ liệu hiệu chuẩn hoặc huấn luyện có nhận biết lượng tử hóa để duy trì độ chính xác.
Các ứng dụng của độ chính xác bán phần#
- AI biên: Các bộ phát hiện trên module Jetson và GPU nhúng khác chạy bằng FP16 để đạt tốc độ khung hình camera với công suất chỉ vài watt đến vài chục watt.
- Suy luận đám mây: Phục vụ model bằng FP16 giảm một nửa bộ nhớ cho mỗi bản sao model, nên một GPU có thể chứa nhiều bản sao hơn hoặc các batch lớn hơn.
- Mô hình ngôn ngữ lớn: Model có 7 tỷ tham số cần 28 GB cho trọng số ở FP32 và 14 GB ở FP16; do đó, khi dùng FP16, model có thể chạy trên một GPU tiêu dùng 24 GB và vẫn còn chỗ cho bộ nhớ đệm KV.










