flowchart TB
F["fp32 模型"] --> P1["第一部分 M01~M06<br/>export → prepare<br/>→ 校準或訓練 → convert"]
P1 --> QDQ["Q/DQ 圖<br/>fp32 運算,模擬 int8"]
QDQ --> P2["第二部分 M07~M10<br/>認出 dq → op → q<br/>換成 int8 kernel"]
P2 --> D["裝置上執行<br/>.pte/.onnx/自訂硬體"]
P2 -. "Quantizer、op 清單、限制" .-> P1
PT2E 端到端:模型量化、後端部署與演算法/部署的分工
用 PyTorch 2 Export(PT2E)把模型量化成 int8:從 torch.export 拿到的圖開始,看 quantizer 怎麼標註、observer 怎麼插、 convert 之後的圖長什麼樣,最後交給 XNNPACK、ONNX Runtime 這類後端。
每個模組先有一篇概覽,講這一章要回答的問題與核心概念;之後的 notebook 逐一動手驗證。 notebook 裡的輸出都已經跑好,直接讀就能看到結果;想自己改參數再跑,請見下方「在自己的電腦上執行」。
故事線:兩個部分
M00 是共同的地基:把模型抓成一張圖、學會改圖。之後分成兩個部分,分界是 convert_pt2e 產生的 Q/DQ 圖:
| 第一部分 | 第二部分 | |
|---|---|---|
| 章節 | M01~M06 | M07~M10 |
| 起點 | 訓練好的 fp32 模型 | Q/DQ 圖 |
| 終點 | Q/DQ 圖,與它的精度 | 在裝置上跑的 int8 模型 |
| 回答的問題 | 哪裡量化、scale 多少、PTQ 不夠時怎麼 QAT | 誰把單元換成 int8 kernel、換不了怎麼辦、數值與速度怎麼確認 |
| 概覽 | 第一部分概覽 | 第二部分概覽 |
閱讀順序是第一部分 → 第二部分,但圖中的虛線是反方向的:Quantizer 是後端照自己的 kernel 設計的(M03), 所以實務上先選後端,再量化(M07 概覽〈量化之前,先選後端〉)。 M03 從使用者的角度讀、寫 Quantizer;M10 從後端的角度,把硬體限制寫成 Quantizer。 Quantizer 就是兩個部分之間的約定。
學習路徑
| 模組 | 主題 | 狀態 |
|---|---|---|
| M00 | torch.export 與 fx 改圖 |
✅ |
| 第一部分概覽 | 從 fp32 模型到 Q/DQ 圖:故事線與演算法人員的職責 | ✅ |
| M01 | 第一次量化:四個步驟、Q/DQ 圖 | ✅ |
| M02 | observer 與 fake quant:手插 observer、手做 convert | ✅ |
| M03 | 標籤與 Quantizer:observer 的位置由誰決定 | ✅ |
| M04 | Conv + BN 折疊:PTQ 在 prepare 折,QAT 在 convert 才折 | ✅ |
| M05 | PTQ 的精度除錯:SQNR、校準、逐層診斷、mixed precision | ✅ |
| M06 | QAT 的訓練流程:STE、評估前的開關、訓練圖 = convert 圖、int4 下 QAT vs PTQ | ✅ |
| 第二部分概覽 | Q/DQ 圖之後:故事線與後端部署人員的職責 | ✅ |
| M07 | 後端部署總覽:後端的分層、三條路線、Quantizer 要配後端;Q/DQ 圖經 reference representation、lowering、Inductor 變成什麼;fp32 的數值底線 | ✅ |
| M08 | XNNPACK/ExecuTorch:Q/DQ 圖 → lowering → .pte → runtime;不支援的 op 把圖切開;收益分成換 runtime 與量化兩段 |
🔍 |
| M09 | ONNX Runtime:Q/DQ 圖匯出成 QDQ ONNX;五個圖最佳化等級在建 session 時換上 int8 kernel;QDQ node fusion 的 op 清單;ORT 自己的量化工具 | 🔍 |
| M10 | 自訂後端:把虛構 VPU 的硬體約束寫成 Quantizer;只讀 Q/DQ 圖的檢查 pass;不支援的 op 切回 CPU 的分區圖 | 🔍 |
✅ 已完成;🔍 內容已寫好,還在校對,之後可能修改。
最精簡的閱讀路徑
時間不多的話,先讀這四篇掌握全貌,再挑需要的章節深入:
- 本篇:PT2E 端到端:模型量化、後端部署與演算法/部署的分工
- M00 概覽 | 把模型變成一張圖:torch.export、ATen 與 fx
- 第一部分概覽 | 從 fp32 模型到 Q/DQ 圖(M01~M06)
- 第二部分概覽 | Q/DQ 圖之後:交給後端(M07~M10)
兩種角色:演算法人員與後端部署人員
實務上,兩個部分常由兩種人負責。同一個人也可能兼兩種角色,但交接的內容一樣。
| 演算法人員 | 後端部署人員 | |
|---|---|---|
| 負責 | 建模、訓練、量化 | 選定平台、提供 Quantizer、lowering、驗證與部署 |
| 主要章節 | 第一部分(M01~M06) | 第二部分(M07~M10) |
| 交給對方 | Q/DQ 圖、fp32 模型、資料、精度報告 | Quantizer、op 清單、硬體限制 |
| 對哪段數值負責 | Q/DQ 圖 vs fp32 | 裝置輸出 vs Q/DQ 圖 |
合作的順序
sequenceDiagram
participant A as 演算法人員
participant B as 後端部署人員
B->>A: Quantizer、op 清單、shape 與位元數限制
A->>A: 設計並訓練 fp32 模型,避開清單外的 op
A->>A: PTQ 或 QAT,在 Q/DQ 圖上確認精度
A->>B: Q/DQ 圖、fp32 模型、資料、精度報告
B->>B: 合規檢查、lowering、fallback 報告
B->>B: 數值與速度驗證
B-->>A: 回報 fallback 的 op、精度落差、速度
A->>B: 修改後的 Q/DQ 圖
最後兩步通常會來回好幾次。每份交接清單的細節,在兩篇部分概覽的〈角色〉一節。
這樣分工要補上的四件事
| 容易漏掉的 | 為什麼 | 出處 |
|---|---|---|
| 後端的資訊要在設計模型前就給 | op 清單決定圖會被切成幾段,選架構時就能避開 | M10-3 |
| 只交 Q/DQ 圖不夠 | 後端要 fp32 模型當基準、要資料重跑評估,有些路線還要重新 convert | M07 概覽、M08-2 |
| QAT 的一致性是雙方的事 | 後端給的 QAT 設定,要通過「訓練圖 = convert 圖」的檢查 | M06-3 |
| 精度掉了,先分段量再決定誰查 | 損失分成兩段,各由一種角色負責(見下一節) | M05、M07-1 |
精度掉了,先查哪一段
從 fp32 到裝置,中間隔著 Q/DQ 圖。用同一份評估資料跑三個版本,就能把精度損失拆成兩段:
fp32 模型
→ Q/DQ 圖 第一段:量化本身造成的損失(演算法人員)
→ 裝置上的模型 第二段:lowering 與整數 kernel 造成的差異(後端部署人員)
第一段在 PyTorch 裡就量得到(M05、M06);第二段要等後端把圖跑在裝置上才量得到。 下面用三組假設的 accuracy 說明怎麼判斷:
| fp32 | Q/DQ 圖 | 裝置 | 判斷 |
|---|---|---|---|
| 90% | 82% | 82% | 第一段就掉了:裝置只是忠實重現 Q/DQ 圖,要回頭改量化設定或模型 |
| 90% | 89.5% | 82% | 第二段才掉:Q/DQ 圖沒問題,要查 lowering 或後端 kernel |
| 90% | 89.5% | 89.4% | 兩段都正常:裝置與 Q/DQ 圖的小差異是整數捨入 |
第一種情況,後端怎麼調都救不回來;第二種情況,演算法人員再做 QAT 也沒有用。 先分段,才不會兩邊都白忙。最後一種的「小差異」,指裝置與 Q/DQ 圖的輸出只差 1~2 個輸出 LSB(M08、M09 的實測)。
在自己的電腦上執行
環境用 uv 管理(Python 3.12)。graphviz 的子圖需要系統的 dot 執行檔。
brew install graphviz # Ubuntu:sudo apt install graphviz
uv sync
uv run jupyter lab # 或用 VS Code 開 .ipynb,kernel 選 .venv