Quantizationgiáo trình tự học
Chương 73 phút đọcSuy luận số nguyên

7Lượng tử hóa một mạng nơ-ron hoàn chỉnh#

7.1Những gì được lượng tử#

Thành phần Kiểu thường dùng Cách xác định dải
Trọng số int8 đối xứng, per-channel trực tiếp từ giá trị trọng số
Bias int32, scale SwSxS_w S_x suy ra, không cần dải
Activation uint8/int8 bất đối xứng, per-tensor cần dữ liệu (calibration) hoặc tính lúc chạy
Đầu vào, đầu ra mô hình tùy cấu hình đầu vào thường biết trước (ví dụ pixel [0,1][0, 1])

Theo cách xử lý activation, có ba chế độ:

  • Weight-only: chỉ nén trọng số; lúc chạy giải lượng tử trọng số về FP16/FP32 rồi tính. Lợi về bộ nhớ và băng thông, không lợi về tốc độ tính.
  • Dynamic quantization: trọng số lượng tử sẵn; activation được lượng tử lúc chạy với dải tính từ chính tensor đó. Không cần dữ liệu calibration, nhưng tốn chi phí tìm min/max mỗi lần chạy.
  • Static quantization: dải activation được cố định trước nhờ calibration. Nhanh nhất và là điều kiện để chạy thuần số nguyên như Hình 9.

7.2Mô phỏng lượng tử bằng nút fake-quant#

Trong thực tế, ta hiếm khi viết kernel số nguyên để thử nghiệm. Thay vào đó, ta chèn các nút FakeQuant vào đồ thị FP32 ở đúng những vị trí sẽ bị lượng tử khi triển khai:

Hình 10
Hình 10. Đồ thị có nút fake-quant. Mọi phép toán vẫn chạy bằng FP32, nhưng các tensor chỉ nhận giá trị trên lưới lượng tử.

Mục 6.5 đã kiểm chứng rằng cách mô phỏng này cho kết quả trùng khớp với phép tính số nguyên thật. Đồ thị này được dùng cho cả hai việc: đánh giá PTQ và huấn luyện QAT (Chương 9).

7.3Gộp BatchNorm vào tích chập (BN folding)#

Khi suy luận, BatchNorm là một phép biến đổi affine cố định theo từng kênh:

BN(y)=γ y−μσ2+ϵ+β,y=W∗x+b.\text{BN}(y) = \gamma\,\frac{y - \mu}{\sqrt{\sigma^2 + \epsilon}} + \beta, \qquad y = W * x + b.

Thế yy vào và nhóm lại, ta được một phép tích chập mới với trọng số và bias

W′=γσ2+ϵ W,b′=γ (b−μ)σ2+ϵ+β,W' = \frac{\gamma}{\sqrt{\sigma^2 + \epsilon}}\, W, \qquad b' = \frac{\gamma\,(b - \mu)}{\sqrt{\sigma^2 + \epsilon}} + \beta,

trong đó phép nhân với WW áp dụng theo từng kênh đầu ra. Phải gộp BN trước khi lượng tử, vì trên phần cứng BN không tồn tại như một lớp riêng. Nếu lượng tử WW rồi mới gộp, trọng số thực sự chạy trên thiết bị là W′W' với dải khác hẳn. Đây cũng là nguyên nhân khiến dải giữa các kênh trở nên chênh lệch (Mục 5.3): hệ số γ/σ2+ϵ\gamma / \sqrt{\sigma^2 + \epsilon} khác nhau cho từng kênh.

Python
def fold_bn(conv, bn):
    std = torch.sqrt(bn.running_var + bn.eps)
    w = conv.weight * (bn.weight / std).reshape(-1, 1, 1, 1)
    b0 = conv.bias if conv.bias is not None else torch.zeros_like(bn.running_mean)
    b = bn.weight * (b0 - bn.running_mean) / std + bn.bias
    fused = nn.Conv2d(conv.in_channels, conv.out_channels, conv.kernel_size,
                      conv.stride, conv.padding, groups=conv.groups, bias=True)
    fused.weight.data, fused.bias.data = w.detach().clone(), b.detach().clone()
    return fused

Trên mô hình thí nghiệm, sai khác lớn nhất giữa logit của mạng gốc và mạng đã gộp là 5,72×10−65{,}72 \times 10^{-6}, đúng bằng mức sai số làm tròn của FP32.

7.4Gộp lớp (layer fusion)#

Tổng quát hơn, các chuỗi như Conv → BN → ReLU hay Linear → ReLU được gộp thành một phép toán. Lợi ích: bớt các cặp quantize/dequantize trung gian (bớt sai số, bớt chi phí), và activation trung gian không bao giờ phải ghi ra bộ nhớ. Framework nào cũng có bước này (fuse_modules trong PyTorch eager mode, hoặc tự động trong trình chuyển đổi TFLite).


Tự kiểm tra

3 câu. Chọn đáp án để xem ngay lời giải thích.

  1. 1BatchNorm phải được gộp vào tích chập ở thời điểm nào?

  2. 2Nút fake-quant làm gì?

  3. 3Chế độ nào không cần dữ liệu calibration?

Đã trả lời 0/3