Chuyển đến nội dung chính
Tối ưu suy luận · Cập nhật 1 tháng 11, 2026

4.2 Lượng tử hóa W8A8

Xuất phát từ outlier theo kênh của activation, suy ra phép scale tương đương và cường độ chuyển α của SmoothQuant, hiểu hệ số làm mượt, INT8 GEMM và quy trình lượng tử hóa offline

SmoothQuant W8A8 INT8 Activation Outlier

Trong thí nghiệm ở phần trước, chỉ một số 20 đã khiến các giá trị nhỏ như 0.2, 0.5 gần như không đọc nổi. Activation của LLM cũng gặp đúng rắc rối này: vài kênh lớn hơn các kênh khác hàng chục lần, và 255 ô của INT8 không đủ chia. Cách của SmoothQuant giống gánh đôi: bên activation quá nặng thì chuyển một phần sang bên weight, hai bên đều đỡ nặng, rồi lượng tử hóa riêng. Phép chuyển là scale tương đương chặt về mặt toán, nên đầu ra lớp tuyến tính không đổi.

⏰ Độ mới của thông tin: Phần thuật toán theo bài SmoothQuant; tình trạng hỗ trợ của vLLM và LLM Compressor dựa trên tài liệu vLLM v0.32.0 (phát hành 2026-10-18).

📑 Mục lục


1. W8A8 khó ở đâu

W8A8 ở đây chỉ riêng weight INT8 × activation INT8, nên lớp tuyến tính có thể đi thẳng nhân ma trận INT8. Phương án chỉ nén weight (mục 4.3) không đụng activation; W8A8 thì phải xử lý online input khác nhau mỗi lần.

Giả sử vector ẩn của một token phần lớn nằm trong [−1,1][-1,1], nhưng một kênh đạt 80. Lấy Scale theo max tuyệt đối cả hàng thì s=80/127≈0.63s=80/127\approx0.63, số trong [−1,1][-1,1] chỉ rơi vào vài mã từ −2-2 đến 22. Per-token tách được khác biệt giữa các token, nhưng không xử lý được khác biệt kênh trong cùng một token.

Bài SmoothQuant quan sát rằng các giá trị lớn tập trung ở vài kênh cố định và lặp lại giữa các token; phân phối weight thì phẳng hơn nhiều và tương đối dễ lượng tử hóa. Vì vậy có thể chuyển một phần độ khó lượng tử hóa của activation sang weight.


2. Scale tương đương: bên này chia, bên kia nhân

2.1 Công thức

Giữ hướng ma trận như mục 4.1:

Y=XW,X∈RT×Cin,W∈RCin×CoutY=XW,\qquad X\in\mathbb{R}^{T\times C_{\text{in}}},\quad W\in\mathbb{R}^{C_{\text{in}}\times C_{\text{out}}}

Lấy ma trận đường chéo S=diag⁡(s1,…,sCin)S=\operatorname{diag}(s_1,\ldots,s_{C_{\text{in}}}) với mọi sj>0s_j>0:

Y=(XS−1)(SW)=X~W~Y=(XS^{-1})(SW)=\widetilde X\widetilde W

Với kênh đầu vào thứ jj, cột jj của XX chia cho sjs_j, hàng jj của WW nhân với sjs_j. Trong số học chính xác, chia rồi nhân triệt tiêu nhau, đầu ra không đổi.

Phép scale tác động lên mọi token của cùng một kênh, nên SS chỉ có CinC_{\text{in}} hệ số, không phụ thuộc số token, và có thể tính offline. Đổi sang layout weight chuyển vị của PyTorch thì nhân vào cột của weight.

2.2 Nhìn qua một tích vô hướng

Hình dưới chỉ bung một token và một kênh đầu ra. Ô xanh là activation, ô vàng là weight tương ứng, khung nét đứt đỏ đánh dấu kênh thứ 3 bị scale:

SmoothQuant scale theo cặp dọc kênh đầu vào: đỉnh activation hạ, weight tương ứng phóng to, tích giữ nguyên

Trước khi làm mượt, tích trong của [1,1,16][1,1,16] và [2,2,0.5][2,2,0.5] là 12. Activation thứ ba chia 8, weight tương ứng nhân 8, thành [1,1,2][1,1,2] và [2,2,4][2,2,4], tích trong vẫn 12. Max activation từ 16 xuống 2; max weight từ 2 lên 4. Lượng trình của cả hai đầu vào đều dịu hơn.

📌 Điểm then chốt: Sau làm mượt, X~\widetilde X, W~\widetilde W vẫn là số thực, chưa có mã INT8 nào. Bản thân làm mượt không đưa sai số vào; vai trò của nó là làm sai số bước lượng tử hóa kế tiếp nhỏ hơn. SmoothQuant W8A8 = làm mượt + lượng tử hóa. Hai bước không được trộn thành một.


3. Chọn cường độ chuyển α thế nào

Chuyển ít quá thì đỉnh activation còn đó; chuyển nhiều quá thì weight lại xuất hiện đỉnh mới. SmoothQuant dùng cường độ chuyển (migration strength) α\alpha để điều khiển chuyển bao nhiêu:

sj=(max⁡t∣Xtj∣)α(max⁡k∣Wjk∣)1−α,0≤α≤1s_j=\frac{\left(\max_t|X_{tj}|\right)^\alpha} {\left(\max_k|W_{jk}|\right)^{1-\alpha}},\qquad 0\leq\alpha\leq1

Max của activation lấy từ mẫu hiệu chuẩn; max của weight tính thẳng từ tham số. Triển khai còn phải tránh mẫu số gần 0.

  • α\alpha càng lớn thì activation bị ép càng phẳng, càng nhiều độ khó chuyển sang weight; α\alpha càng nhỏ thì ngược lại.
  • Bài báo gọi α=0.5\alpha=0.5 là “điểm ngọt” chung cho dòng OPT, BLOOM; GLM-130B với activation khó lượng tử hóa hơn dùng 0.75.
  • Ablation trong bài cho thấy α\alpha dưới khoảng 0.4 thì activation khó lượng tử hóa, trên khoảng 0.6 thì weight khó (với các mô hình thí nghiệm của họ); các phiên bản sau với dòng LLaMA dùng tới 0.8 thậm chí cao hơn.
  • Giá trị phù hợp phải tìm trên một lô hiệu chuẩn nhỏ theo từng mô hình; bài báo làm grid search trên một tập con của Pile.

Ví dụ INT8 W8A8 của LLM Compressor trong tài liệu vLLM dùng SmoothQuantModifier(smoothing_strength=0.8). smoothing_strength ở đây chính là α\alpha.

⚠️ Lưu ý: Ở đây có hai loại “Scale”. sjs_j là hệ số làm mượt, chỉ làm phép biến đổi tương đương; Scale lượng tử hóa INT8 được tính sau làm mượt theo lượng trình mới, chịu trách nhiệm ánh xạ sang số nguyên.


4. Từ phép biến đổi tới INT8 GEMM

4.1 Quy trình offline

Nửa trên hình dưới là bốn bước offline làm một lần; nửa dưới là đường runtime mỗi lần forward. Đường gấp khúc cam đưa weight INT8 đã xuất và Scale weight bkb_k sang GEMM runtime:

Quy trình offline và runtime của SmoothQuant W8A8: offline thu phạm vi activation, tính và gấp hệ số làm mượt, rồi lượng tử hóa và xuất; runtime lượng tử hóa activation, INT8 GEMM, nhân lại Scale

  1. Thu phạm vi activation: chạy forward trên văn bản hiệu chuẩn đại diện, ghi max theo kênh đầu vào của mỗi lớp tuyến tính sắp lượng tử hóa.
  2. Tính và gấp hệ số làm mượt: S−1S^{-1} không nhất thiết thành toán tử riêng; có thể gộp vào tham số scale của LayerNorm/RMSNorm phía trước, còn SS thì nhân thẳng vào weight.
  3. Lượng tử hóa: chọn độ hạt cho weight và activation, quyết định activation dùng hiệu chuẩn tĩnh hay tính Scale động, rồi kiểm sai số từng lớp và chất lượng cả mô hình.
  4. Xuất: lưu weight INT8 thật và cấu hình lượng tử hóa cho kernel suy luận hỗ trợ định dạng đó.

Gấp có điều kiện. Nếu đầu ra một Norm đồng thời nuôi ba phép chiếu Q, K, V thì cả ba phải dùng cùng một bộ SS, không thì không chia sẻ được một lần S−1S^{-1}. Chỗ residual và toán tử phi tuyến cũng không được tùy ý dịch scale; lấy vị trí biến đổi mà công cụ hỗ trợ làm chuẩn.

Trong ví dụ LLM Compressor của tài liệu vLLM v0.32.0, công thức INT8 W8A8 là làm mượt trước, rồi lượng tử hóa bằng GPTQ:

from llmcompressor.modifiers.quantization import GPTQModifier
from llmcompressor.modifiers.smoothquant import SmoothQuantModifier

recipe = [
    SmoothQuantModifier(smoothing_strength=0.8),
    GPTQModifier(targets="Linear", scheme="W8A8", ignore=["lm_head"]),
]

Giữ lm_head ở độ chính xác gốc là cách làm phổ biến của kiểu recipe này. Checkpoint sau khi xuất có thể được vLLM nạp trực tiếp; lệnh cụ thể xem mục 4.6.

4.2 Runtime tính thế nào

Để thấy luồng dữ liệu, giả sử activation đã làm mượt lượng tử hóa theo token, weight theo kênh đầu ra, và cả hai zero-point đều bằng 0:

X^tj=atQtjX,W^jk=bkQjkW\widehat X_{tj}=a_t Q^X_{tj},\qquad \widehat W_{jk}=b_k Q^W_{jk}

Hai Scale đều rút được ra khỏi dấu tổng:

Y^tk=atbk∑jQtjXQjkW\widehat Y_{tk}=a_t b_k\sum_jQ^X_{tj}Q^W_{jk}

Phần tổng là nhân INT8, cộng dồn INT32, cuối cùng nhân lại atbka_tb_k rồi chuyển sang FP16/BF16. Vậy W8A8 chỉ mô tả hai đầu vào của nhân ma trận; cộng dồn và đầu ra vẫn giữ độ chính xác cao hơn. Điều này cũng giải thích kết luận ở mục 4.1: nếu Scale activation đặt theo kênh jj thì không rút khỏi dấu tổng được, nên phải nhờ làm mượt xóa khác biệt kênh trước.

4.3 Lợi ích và điều kiện áp dụng

Bài báo báo cáo trên các mô hình họ thử, SmoothQuant cao nhất tăng tốc 1.56 lần, giảm nửa bộ nhớ, và mất mát độ chính xác có thể bỏ qua. Cả mô hình có tăng tốc hay không phụ thuộc thời gian INT8 GEMM tiết kiệm có bù nổi chi phí lượng tử hóa activation online, xử lý Scale và chuyển kiểu hay không; ma trận nhỏ, toán tử chưa fuse, hoặc phần lớn thời gian nằm ở Attention và lập lịch thì lợi ích hạn chế.

Phần cứng cũng có biên. Tài liệu vLLM v0.32.0 ghi rõ, tính toán INT8 W8A8 hỗ trợ GPU NVIDIA từ Turing tới Hopper, không hỗ trợ Blackwell từ compute capability 10.0 trở lên. Các card đó nên chuyển sang FP8 (mục 4.5).

Về chất lượng, cần chú ý input mà tập hiệu chuẩn chưa phủ: văn bản dài, code, ký hiệu đặc biệt và ngôn ngữ ít tài nguyên. Khi cần thì giữ các lớp nhạy ở độ chính xác cao.


5. Thực hành: kiểm một tích vô hướng

Nối ví dụ mục 2.2 với lượng tử hóa INT8 đối xứng. qdq nghĩa là Quantize-Dequantize: tính mã số nguyên rồi chuyển ngay về số thực để mô phỏng sai số lượng tử hóa, không chạy INT8 GEMM thật.

def qdq(values):
    amax = max(map(abs, values))
    scale = amax / 127 if amax else 1.0
    return [max(-127, min(127, round(v / scale))) * scale for v in values]


def dot(a, b):
    return sum(x * y for x, y in zip(a, b))


x, w = [1.0, 1.0, 16.0], [2.0, 2.0, 0.5]
s = [1.0, 1.0, 8.0]  # chọn tay cho dễ quan sát; mô hình thật lấy từ thống kê hiệu chuẩn và α
x_s = [v / k for v, k in zip(x, s)]
w_s = [v * k for v, k in zip(w, s)]

print("đầu ra gốc:", dot(x, w))
print("đầu ra sau làm mượt:", dot(x_s, w_s))
print("lượng tử hóa trực tiếp:", round(dot(qdq(x), qdq(w)), 6))
print("làm mượt rồi lượng tử hóa:", round(dot(qdq(x_s), qdq(w_s)), 6))

Hai dòng đầu đều là 12.0, chứng tỏ làm mượt là phép biến đổi tương đương. Lượng tử hóa trực tiếp khoảng 12.094488; làm mượt rồi lượng tử hóa khoảng 12.063240. Sai số từ 0.094 xuống 0.063. Đây chỉ là demo cơ chế trên một kênh đầu ra; hiệu quả trên mô hình thật phải dựa đánh giá cả mô hình.


📝 Tóm tắt

  • INT8 W8A8 lượng tử hóa đồng thời weight và activation; điểm khó là outlier của activation tập trung ở vài kênh.
  • SmoothQuant dùng XW=(XS−1)(SW)XW=(XS^{-1})(SW) để chuyển một phần độ khó lượng tử hóa của activation sang weight, rồi lượng tử hóa riêng.
  • Cường độ chuyển α\alpha cân hai bên; trong bài báo 0.5 là điểm xuất phát thường gặp, từng mô hình cần tìm kiếm.
  • Hệ số làm mượt SS có thể gấp offline vào toán tử phía trước; nhiều phép chiếu dùng chung input phải dùng cùng một bộ hệ số.
  • Scale activation chỉ đặt theo token mới rút khỏi dấu tổng được; đó là lý do căn bản phải làm mượt trước.
  • Trong vLLM v0.32.0, INT8 W8A8 không hỗ trợ Blackwell; các card đó nên chọn FP8.

🎯 Danh sách tự kiểm

  • Giải thích được vì sao Per-token không chặn được outlier theo kênh
  • Chỉ ra được phép làm mượt đổi trục nào của XX và của WW, cùng SS có bao nhiêu hệ số
  • Viết được công thức làm mượt theo α\alpha, nói được tăng α\alpha thì hiệu ứng thế nào
  • Phân biệt được hệ số làm mượt và Scale lượng tử hóa
  • Giải thích được vì sao Q, K, V dùng chung đầu ra một Norm thì phải xử lý cùng nhau
  • Viết được công thức giải lượng tử hóa runtime của W8A8, và nói cộng dồn dùng độ chính xác nào
  • Dùng recipe LLM Compressor mô tả được một lần lượng tử hóa W8A8 SmoothQuant + GPTQ

📚 Tài liệu tham khảo