跳至主要内容

第一部分概覽 | 從 fp32 模型到 Q/DQ 圖(M01~M06)

M00 把模型抓成一張圖,也學會怎麼改圖。第一部分(M01~M06)在這張圖上做量化: 輸入是訓練好的 fp32 模型,輸出是 convert_pt2e 產生的 Q/DQ 圖,以及在 Q/DQ 圖上量到的精度。

Q/DQ 圖是第一部分的終點,也是第二部分的起點。 這篇只給故事線與分工;每章的定義、公式與實驗,在各章的概覽與 notebook。

一張圖看完第一部分

PT2E 只有幾個 API 呼叫,六章各打開其中一段:

fp32 模型
  → torch.export        抓成 ATen op 組成的圖(M00)
  → prepare             Quantizer 貼標籤,再照標籤插 observer(M02、M03)
  → 校準或訓練          PTQ 校準算 scale;QAT 訓練 weight(M05、M06)
  → convert_pt2e        observer 換成 q/dq,weight 存成 int8(M02)
  → Q/DQ 圖             交給第二部分

prepare 在 PTQ 是 prepare_pt2e(插 observer),在 QAT 是 prepare_qat_pt2e(插 fake quant)。 Conv + BN 的折疊不是獨立的一步:PTQ 在 prepare 一開始就折,QAT 留到 convert 才折(M04)。

故事線:三段

先走一遍(M01)

章節 問題 一句話的答案
M01 量化要決定哪些事? 量化哪些 tensor、每個 tensor 的 scale/zero point、怎麼交給後端
PT2E 在哪裡回答它們? 依序是 Quantizer 的標籤、observer 的校準、convert 產生的 q/dq

M01 也先講清楚一件事:Q/DQ 圖的運算仍是 fp32,只在數值上模擬 int8,不會變快。

打開三個黑盒子(M02~M04)

章節 問題 一句話的答案
M02 prepare 與 convert 對圖做了什麼? 只是改圖:插 observer,再換成 q/dq 並把 weight 預先量化
M03 observer 插在哪裡由誰決定? Quantizer 在 node 上貼的標籤;prepare 自己不知道哪裡該量化
M04 BN 怎麼折進 conv? PTQ 在 prepare 折;QAT 保留 BN 訓練,convert 才折

這三章拆開的是同一條流程:M02 看「怎麼插」、M03 看「插在哪」、M04 看「插之前圖先被改了什麼」。

用在訓練好的模型上(M05、M06)

章節 問題 一句話的答案
M05 PTQ 之後精度掉了,往哪裡查? 輸出 SQNR → 換校準資料 → 逐層 SQNR → 排除候選並重量
M06 PTQ 不夠時,QAT 怎麼訓練? 用 STE 訓練 fake quant 圖,並確保訓練時的圖等於 convert 之後的圖

M05、M06 都用同一個訓練好的 TinyConvNet:int8 下 PTQ 已經貼著 fp32,int4 下 QAT 才明顯追回 PTQ 掉的精度(M06-4)。

貫穿第一部分的規則

規則 出處
Q/DQ 圖仍是 fp32 運算,只模擬 int8,不會變快 M01、M02-3
prepare/convert 不知道後端;量化位置與規格全由 Quantizer 決定 M03
換了 Quantizer 或版本,先 dump 標籤,確認哪些 op 真的被量化 M03-2
PTQ 與 QAT 在不同步驟折 BN,convert 之後的圖相同 M04
PTQ 用對 fp32 輸出的 SQNR;逐層分數是線索,要用實驗確認 M05
QAT 的 fake quant 圖(freeze 後)與 convert 圖輸出要一致 M06-3
QAT 用 test loss,基準是同樣配方再訓練的 fp32 M06-4

最後一個問題留給第二部分:在 Q/DQ 圖上量到的精度,能不能代表部署之後的精度? 答案是可以當代表,但不保證逐位元相同:後端用真正的整數 kernel,捨入位置不同, M08 的實測差異多半只有 1 個輸出 LSB,對 fp32 的 SQNR 幾乎一樣。

第一部分的角色:演算法人員

實務上,第一部分通常由演算法人員負責:為問題建立模型、訓練、量化,交付 Q/DQ 圖。 但量化的規則不是演算法人員自己定的,Quantizer 是照後端的 kernel 設計的(M03、M07 概覽〈量化之前,先選後端〉)。 所以這個角色的工作,從「向後端拿資訊」開始,到「交付 Q/DQ 圖與證據」結束。 兩種角色怎麼合作,見首頁〈兩種角色〉。

開工前,向後端部署人員拿到什麼

最好在設計模型之前拿到,不只是量化之前:op 清單會影響架構的選擇。

項目 為什麼需要 相關章節
目標平台的 Quantizer(PTQ、QAT 設定) 量化位置與規格要配合後端的 kernel M03、M07 概覽
支援 int8 的 op 清單 清單外的 op 留在 fp32,還會把圖切成好幾段 M08-3、M10-3
shape 與位元數的限制 例如只吃 static shape 的後端,prepare 時就會報錯 M10-1
驗證過的 QAT 設定 XNNPACK 預設的 fused fake quant 讓訓練圖 ≠ convert 圖 M06-3

職責

工作 做法 章節
建立、訓練 fp32 模型 優先用清單內的 op;保留 fp32 模型當基準 —
export 依後端決定 static 或動態 batch M00
PTQ 用具代表性的校準資料,看結果的波動 M05-1、M05-2
精度除錯 逐層 SQNR 找候選,再用排除實驗確認 M05-3、M05-4
QAT PTQ 不夠時才做;評估前 BN 切 eval、關 observer M06
特殊結構 weight_norm 這類由別的參數算出的 weight,折 BN 前換成常數 M05-5、M06-6
確認 Q/DQ 圖的精度 交付前最後一個數字,也是後端的比較基準 M05、M06

交付給後端部署人員什麼

只交 Q/DQ 圖不夠。後端要能重現、比較、除錯:

交付物 後端拿來做什麼
Q/DQ 圖 lowering 的輸入
fp32 模型 數值比較的基準,查精度落差從哪來
範例輸入與 shape 重新 export、memory planning
校準與評估資料 在裝置上重跑評估;必要時重新 convert
Q/DQ 圖上的精度報告 裝置上的結果要和它比

Q/DQ 圖可以用 torch.export.save 存成 .pt2。 作者環境試作:M09 用的 Q/DQ 圖存檔再 torch.export.load,輸出逐位元相同, 載入的圖可以直接匯出 QDQ ONNX,也可以 lower 成 ExecuTorch 的 .pte。

有些路線要的 convert 選項和預設不同,所以量化流程最好保持可重跑: 例如 OpenVINO 要 convert_pt2e(..., fold_quantize=False) 才轉得過(M07 概覽的路線 C)。

延伸閱讀

第一部分只講 PT2E 的機制:在哪裡量化、scale 怎麼算、怎麼訓練。量化演算法本身(AdaRound、OCTAV、EWGS 等)不在範圍內,部落格〈量化技術路線〉把它們整理成一張地圖。

LLM 常用的 outlier 處理,例如 SmoothQuant、AWQ、隨機旋轉、SpinQuant(部落格的筆記),改的是 fp32 模型本身(等價的 scaling 或旋轉);照本教材的分工,屬於演算法人員在 export 之前的工作。

下一步

第二部分概覽:Q/DQ 圖交給誰、怎麼變成 int8 kernel,以及後端怎麼把硬體限制寫成 Quantizer、交回第一部分。