Lượng tử hóa

Quantization trong LibreYOLO chạy hoàn toàn trong PyTorch: model.quantize() thay các module Conv2d và Linear của mô hình bằng những module đã lượng tử hóa tương đương rồi hiệu chuẩn chúng. Kết quả vẫn giữ nguyên hợp đồng predict, val, train và save thông thường, nên một mô hình đã lượng tử hóa được chấm bằng đúng các validator dùng cho mô hình float.

Lệnh gọi
model.quantize(recipe="int8", calib="coco128.yaml")
Lệnh
libreyolo quantize --model M.pt --recipe int8 --calib coco128.yaml
Phụ thuộc thêm
Không có. Quantization chạy trong PyTorch.
Họ mô hình
yolo9, rfdetr, birefnet, feynobg
Các recipe
fp16, bf16, fp8, int8, w4a16, w4a8, nvfp4, mxfp4, int2
Artifact triển khai
export(format="pt") cho một checkpoint đã đóng gói, export(format="onnx") cho một đồ thị QDQ INT8

Cài đặt

Quantization không cần gói phụ thuộc thêm nào. Việc thay module, lượt hiệu chuẩn và phép tính mô phỏng đều chạy trong PyTorch, nên pip install libreyolo là toàn bộ yêu cầu. Các artifact triển khai cần đúng những gì định dạng của riêng chúng cần, với đường ONNX thì đó là libreyolo[onnx].

Lượng tử hóa

Python
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9s.pt") # Thay cấu trúc rồi hiệu chuẩn. calib là một tập ảnh nhỏ KHÔNG NHÃN,# chỉ đọc theo chiều forward để suy ra dải giá trị và scale của activationqmodel = model.quantize(recipe="int8", calib="coco128.yaml", samples=128) print(qmodel.quant_info())qmodel.val(data="coco8.yaml")          # cùng các validator như một mô hình floatqmodel.save("LibreYOLO9s-int8.pt")     # checkpoint mang theo một manifest quant
CLI
libreyolo quantize --model LibreYOLO9s.pt --recipe int8 --calib coco128.yaml
Tham số
model.quantize(    recipe="int8",    calib="coco128.yaml",      # đường dẫn data.yaml hoặc tên có sẵn; None bỏ qua hiệu chuẩn    samples=128,               # số ảnh hiệu chuẩn tối đa    batch=8,                   # kích thước batch khi hiệu chuẩn    algorithm="auto",          # auto và minmax là như nhau; percentile là lựa chọn còn lại    keep_high_precision=None,  # None dùng chính sách của họ mô hình    verbose=True,)

quantize() biến đổi mô hình đã tải ngay tại chỗ rồi trả về chính nó. Không có gradient nào tham gia: bước thay thế cài đặt các module đã lượng tử hóa, còn lượt hiệu chuẩn chỉ chạy theo chiều forward.

Checkpoint thu được là một checkpoint LibreYOLO bình thường có gắn kèm một manifest quant, nên nó tải lại với cấu trúc và các scale còn nguyên vẹn:

Một checkpoint đã lượng tử hóa tải lại đúng như vậy
from libreyolo import LibreYOLO # Manifest quant dựng lại cấu trúc đã lượng tử hóa và các scale# trước khi trọng số được tảiqmodel = LibreYOLO("LibreYOLO9s-int8.pt")print(qmodel.quant_info())

Các checkpoint mà trainer ghi ra trong một lượt QAT cũng mang manifest đó, nghĩa là best.pt của một lượt chạy như vậy bản thân nó đã là một checkpoint đã lượng tử hóa.

Các recipe

Bốn họ mô hình được hỗ trợ: yolo9, rfdetr, birefnetfeynobg.

RecipeNó làm gìHọ mô hìnhHiệu chuẩn
fp16Ép kiểu về half precision với hợp đồng đầu vào và đầu ra float32. Chỉ dùng cho suy luận (inference).cả bốnkhông
bf16Ép kiểu về bfloat16, vốn giữ nguyên dải số mũ của float32. Đây là cách sửa khi fp16 tràn số trên một mô hình kiểu DETR. Chỉ dùng cho inference.cả bốnkhông
fp8Trọng số và activation E4M3 trên Conv2dLinear: scale trọng số theo từng kênh, scale activation theo từng tensor đã hiệu chuẩn.cả bốnbắt buộc
int8W8A8 trên Conv2dLinear: trọng số đối xứng theo từng kênh, activation affine theo từng tensor.cả bốnbắt buộc, hoặc calib=None nếu chỉ lượng tử hóa trọng số
w4a16Trọng số INT4 đối xứng theo nhóm, nhóm 128 dọc theo in_features, activation float, trên Linear.rfdetr, birefnet, feynobgkhông cần
w4a8Trọng số INT4 theo nhóm cộng với activation INT8 đã hiệu chuẩn, trên Linear.rfdetr, birefnet, feynobgbắt buộc
nvfp4W4A4 NVFP4 trên Linear: phần tử E2M1, khối 16 phần tử, scale khối FP8 E4M3, scale tensor FP32. Scale activation động.rfdetr, birefnet, feynobgkhông cần
mxfp4OCP MXFP4 trên Linear: phần tử E2M1, khối 32 phần tử, scale khối E8M0 là lũy thừa của hai. Scale activation động.rfdetr, birefnet, feynobgkhông cần
int2Chỉ dành cho nghiên cứu: trọng số 2 bit theo nhóm, nhóm 64, cộng với activation INT8, trên Linear. Chỉ lượng tử hóa sau huấn luyện thôi thì không dùng được, nên bắt buộc phải có QAT hoặc QAD.rfdetrbắt buộc

Các recipe dưới 8 bit nhắm tới nn.Linear và bị từ chối với yolo9 một cách có chủ đích: trên phần cứng hiện nay việc tăng tốc đó chỉ có với GEMM, nên các convolution vẫn nằm ở precision cao hơn. YOLO9 dùng int8 hoặc fp8. int2 bị từ chối với birefnetfeynobg vì hai họ mô hình đó chỉ dùng cho inference, nên phần QAT giúp hồi phục mà recipe này phụ thuộc vào lại không có ở đó.

Giá trị mặc định của từng họ mô hình giữ lớp đầu tiên và các head ở dạng float, còn convolution DFL của YOLO9 thì không bao giờ bị lượng tử hóa: nó là một toán tử kỳ vọng tích phân cố định. Ghi đè bằng keep_high_precision=("head.",) khi bạn có lý do để làm vậy.

Dữ liệu hiệu chuẩn không phải dữ liệu huấn luyện

calib= nhận vài trăm ảnh, không đọc nhãn nào, và chỉ chạy theo chiều forward để ước lượng dải giá trị activation. data= trong train()val() là tập dữ liệu (dataset) có nhãn dùng cho gradient và các chỉ số. Đó là hai tham số khác nhau với mục đích khác nhau, và mặc định của calibcoco128.yaml.

algorithm="minmax" giữ các cực trị tuyệt đối quan sát được qua các batch hiệu chuẩn và là thứ mà "auto" chọn. "percentile" dùng trung bình của phân vị 0.1 và 99.9 trên từng batch; nó đã được đo là làm sụp đổ độ chính xác của họ mô hình DETR, bởi các giá trị ngoại lai trong activation của transformer là thành phần chịu lực. Thứ thực sự khắc phục độ nhạy INT8 của mô hình nhỏ là hiệu chuẩn trên đủ số batch: với mặc định coco128, YOLO9-t rơi vào khoảng một điểm mAP so với điểm số float của nó. Thuật toán được chọn sẽ được ghi lại trong manifest của checkpoint.

Lấy lại độ chính xác

QAT chỉ là train() bình thường trên một mô hình đã lượng tử hóa
from libreyolo import LibreYOLO qmodel = LibreYOLO("LibreYOLO9s-int8.pt") # Đây là một lượt tinh chỉnh, không phải chạy từ đầu: dùng learning rate của tinh chỉnhqmodel.train(data="coco8.yaml", epochs=5, lr0=1e-4)
QAD thêm vào các tham số distillation sẵn có
qmodel.train(    data="coco8.yaml",    epochs=5,    lr0=1e-4,    distill_model="LibreYOLO9m.pt",)
CLI
libreyolo train --model LibreYOLO9s-int8.pt --data coco8.yaml --epochs 5 --lr0 1e-4

Các module đã lượng tử hóa vẫn giữ trọng số master fp32 và áp dụng fake quantization với một straight-through estimator, nên gradient vẫn tới được các master và những trainer sẵn có hoạt động không cần đổi gì: EMA, AMP, việc tiếp tục huấn luyện từ checkpoint và các tham số distillation đều kết hợp được với nhau.

QAT là một lượt tinh chỉnh (fine-tuning) trên một mô hình đã được huấn luyện. Hãy dùng learning rate của tinh chỉnh thay vì các giá trị mặc định dành cho huấn luyện từ đầu, nếu không thì chỉ một lượt chạy ngắn cũng sẽ phá hỏng trọng số được huấn luyện sẵn (pretrained), bất kể có quantization hay không. QAD có sẵn hay không thì đi theo mức hỗ trợ distillation của từng họ mô hình, hiện tại nghĩa là yolo9rfdetr.

Các mô hình đã lượng tử hóa bằng fp16bf16 chỉ dùng cho inference, và trainer từ chối chúng kèm một chỉ dẫn tới amp=True.

Xuất

Checkpoint PyTorch đã đóng gói
from libreyolo import LibreYOLO qmodel = LibreYOLO("LibreYOLO9s-int8.pt") # Ghi ra LibreYOLO9s-int8-final.pt: trọng số và scale ít bit đã đóng gói,# các master fp32 bị loại bỏ, phần chưa lượng tử hóa còn lại được ép về fp16qmodel.export(format="pt") # remainder="fp32" giữ nguyên chính xác các tensor chưa lượng tử hóaqmodel.export(format="pt", remainder="fp32")
QDQ INT8 ONNX
from libreyolo import LibreYOLO qmodel = LibreYOLO("LibreYOLO9s-int8.pt") # Các cặp QuantizeLinear/DequantizeLinear ngay trong đồ thị, mang chính# các scale đã hiệu chuẩn hoặc đã huấn luyện bằng QAT của mô hìnhqmodel.export(format="onnx")
CLI
libreyolo export --model LibreYOLO9s-int8.pt --format onnx

format="pt" kết tinh mô hình. Trọng số và scale ít bit đã đóng gói thay thế cho các master, còn phần chưa lượng tử hóa còn lại được ép về fp16 trừ khi truyền remainder="fp32". Bất biến của việc đóng gói là khi giải nén sẽ tái tạo lại kết quả mô phỏng đúng từng bit trên chính thiết bị bạn đã hoàn thiện mô hình, nên tệp đã hoàn thiện đạt đúng điểm số bạn đã đánh giá. Số đo được: YOLO9-s int8 giảm từ 29.5 MB xuống 9.6 MB, RF-DETR-n nvfp4 từ 122 MB xuống 26 MB. Tải một tệp như vậy sẽ cho ra một mô hình sẵn sàng cho inference, và gọi train() trên nó sẽ tự động dựng lại các master từ trọng số đã đóng gói.

format="onnx" áp dụng cho các mô hình int8 và phát ra một đồ thị QDQ mang chính các scale đã hiệu chuẩn hoặc đã huấn luyện bằng QAT của mô hình, thứ mà ONNX Runtime và TensorRT chạy bằng kernel INT8 thật. Đây là một đường đi khác với export(format="onnx", int8=True) trên một mô hình float, nơi ONNX Runtime tự suy ra các scale.

Các recipe ép kiểu thì hoàn toàn không cần bộ xuất dành cho mô hình đã lượng tử hóa:

Quay lại float, giữ nguyên trọng số đã huấn luyện bằng QAT
from libreyolo import LibreYOLO qmodel = LibreYOLO("LibreYOLO9s-int8.pt")qmodel.dequantize() # Giờ mọi bộ xuất float đều dùng được, ở bất kỳ precision nào nó hỗ trợqmodel.export(format="tensorrt", half=True)

Ràng buộc

Phép tính đã lượng tử hóa được thực thi ở dạng mô phỏng, tức fake quantization tính trong các ốc đảo float32 ngay cả khi có AMP. Mô phỏng đúng về mặt số học, nên một điểm val() trên bất kỳ thiết bị nào cũng là một khẳng định thật về phép tính đã lượng tử hóa. Nó không phải một khẳng định về tốc độ.

Hai ngoại lệ được thực thi natively. fp16bf16 là các phép ép kiểu thông thường. Các module fp8 đã hoàn thiện chạy GEMM của chúng trực tiếp trên trọng số E4M3 đã đóng gói thông qua torch._scaled_mm trên phần cứng lớp Ada, Hopper và Blackwell, dùng chính các scale activation đã hiệu chuẩn như trong mô phỏng; đặt LIBREYOLO_KERNELS=off sẽ khôi phục đúng đường mô phỏng ở mọi nơi.

Phạm vi triển khai hẹp hơn danh sách recipe. Ở đây chỉ int8 có một dạng ONNX triển khai được; fp8 và các recipe dưới 8 bit cho lớp linear chạy trong PyTorch và kết tinh qua format="pt". Yêu cầu xuất ONNX từ chúng sẽ báo lỗi kèm đúng chỉ dẫn đó, và yêu cầu bất kỳ định dạng nào không phải ONNX từ một mô hình int8 cũng vậy: hãy dựng các engine phía sau từ đồ thị QDQ.

Xuất một mô hình int8 mà activation của nó chưa từng được hiệu chuẩn sẽ ghi ra một cảnh báo và tạo ra một đồ thị chỉ mang phần lượng tử hóa trọng số.

Đọc từ libreyolo/quant/api.py, libreyolo/models/base/model.py, libreyolo/cli/commands/quantize.py và docs/quantization.md trên nhánh dev. Các con số về kích thước checkpoint là giá trị đo được ghi lại trong docs/quantization.md.