跳至主要内容

PT2E 端到端:模型量化、後端部署與演算法/部署的分工

用 PyTorch 2 Export(PT2E)把模型量化成 int8:從 torch.export 拿到的圖開始,看 quantizer 怎麼標註、observer 怎麼插、 convert 之後的圖長什麼樣,最後交給 XNNPACK、ONNX Runtime 這類後端。

每個模組先有一篇概覽,講這一章要回答的問題與核心概念;之後的 notebook 逐一動手驗證。 notebook 裡的輸出都已經跑好,直接讀就能看到結果;想自己改參數再跑,請見下方「在自己的電腦上執行」。

故事線:兩個部分

M00 是共同的地基:把模型抓成一張圖、學會改圖。之後分成兩個部分,分界是 convert_pt2e 產生的 Q/DQ 圖:

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

第一部分 第二部分
章節 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 的分區圖 🔍

✅ 已完成;🔍 內容已寫好,還在校對,之後可能修改。

最精簡的閱讀路徑

時間不多的話,先讀這四篇掌握全貌,再挑需要的章節深入:

  1. 本篇:PT2E 端到端:模型量化、後端部署與演算法/部署的分工
  2. M00 概覽 | 把模型變成一張圖:torch.export、ATen 與 fx
  3. 第一部分概覽 | 從 fp32 模型到 Q/DQ 圖(M01~M06)
  4. 第二部分概覽 | 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