iT邦幫忙

2026 iThome 鐵人賽

DAY 4
0

昨天結論是:單序列速度撞到記憶體頻寬的物理上限,調參救不回來,只剩兩條路

今天走第一條:量化

我先講結論,因為這篇會有點長:

量化不是「把模型變小」,是「用精度換記憶體頻寬」。而在多模態模型上,換錯了會整組能力消失。


量化到底在量化什麼?

我剛開始碰這塊的時候最崩潰的一點是,大家講的「量化」根本不是同一件事

有人說 INT4,有人說 GPTQ,有人說 GGUF Q4_K_M,有人說 NVFP4

這些不在同一個軸上

後來我把它整理成四個軸,從此就沒再混亂過:

在問什麼 選項
軸 1 量化什麼 只量 weight(W4A16)/weight+activation(W8A8、W4A4)/KV cache
軸 2 用什麼數值格式 INT8、INT4(整數)/FP8 E4M3、E5M2(浮點)/NVFP4、MXFP4(微縮放 FP4)
軸 3 用什麼方法算出來? RTN、GPTQ、AWQ、SmoothQuant、NVIDIA ModelOpt、GGUF k-quants
軸 4 裝在什麼容器裡? safetensors/GGUF

💡Tip: 下次看到一個量化模型名稱看不懂,就把它拆到這四個軸上。例如 gemma-4-12B-it-NVFP4A16:軸1 = W4A16(weight 4-bit、activation 16-bit)、軸2 = NVFP4、軸3 = ModelOpt、軸4 = safetensors。四個軸一填完就清楚了。


第一個常見誤解:GGUF vs NVFP4 要選哪個?

這題是錯的。它們不在同一個軸上。

  • GGUF 是容器(軸 4)——對應 llama.cpp、Ollama 生態
  • NVFP4 是數值格式(軸 2)——對應 vLLM + Blackwell + safetensors

問「要選 GGUF 還是 NVFP4」,大概就像問「你要選紙箱還是玻璃」

實務上的對應關係是這樣:

你用的推論框架 對應路線
llama.cpp / Ollama GGUF 容器 + k-quants
vLLM + Blackwell safetensors 容器 + NVFP4

我這台是 vLLM + GB10,所以走右邊那條

💡Tip: 有個延伸的坑——不要把 GGUF 的量化命名(Q4_K_M 這種 K-Quant 寫法)寫進 vLLM 的 compose.yaml。它不吃這個。我看過有人這樣寫然後debug 一整晚。


NVFP4 vs MXFP4:都是 FP4,差在哪?

這兩個長得超像,而且選錯不會報錯,只會吐亂碼(這條 Day 5 會再講一次,因為我被咬過)

NVFP4 MXFP4
標準 NVIDIA 專有 OCP 開放標準
Element E2M1(4-bit) E2M1(4-bit)
Block size 16 32
Micro-scale FP8 / E4M3 E8M0(2 的冪次)
Global scale FP32
精度 較細 較粗
硬體 Blackwell tensor core 原生加速 通用

重點在 block size 跟 scale 的表示法不同

所以同一份 4-bit 權重,用錯的 kernel 去讀,layout 對不上

實務後果很直接:vLLM 的 FlashInfer MXFP4+MXFP8 MoE backend 是為 MXFP4 checkpoint(例如 gpt-oss)寫的。餵 NVFP4 checkpoint 進去,它不會報錯,它會吐亂碼或空輸出

💡Tip: 「NVFP4 在 Blackwell 上原生加速」這句話要注意用詞——「原生」不等於「僅限」。不是只有 Blackwell 能跑 NVFP4,只是 Blackwell 有 tensor core 直接支援。


W4A16 vs W4A4:多模態的生死線

這節是這整篇對 OCR 系列最重要的部分

W4A16 = weight 量到 4-bit,activation 保留 16-bit
W4A4 = weight 跟 activation 都量到 4-bit

W4A4 更省、更快。聽起來很香

我實測的結果是:它會把視覺能力整組打壞

在 DGX Spark 上拿 coolthor/gemma-4-12B-it-NVFP4A16 當對照,W4A4 之後這四種能力全部劣化

  • OCR 文字辨識
  • Chart 圖表判讀
  • 場景描述
  • 圖片算術

原因是 activation distribution mismatch。activation 的數值分布比 weight 動態範圍大得多,硬壓到 4-bit 會把分布打歪,而視覺塔對這件事特別敏感

所以這條是硬規則:

多模態模型在 Blackwell 上走 W4A16,不要碰 W4A4。

更麻煩的是:不同模態的耐受度不一樣

這是我覺得最反直覺、也最少人講的一點

同一顆 W4A16 模型(gemma-4-12B-it-NVFP4A16),我在 2026-07-08 做過一次系統化驗證:

模態 狀態
文字 ✅ 正常
圖片 ✅ 正常
影片 ✅ 正常
音頻 不可用

音頻壞到什麼程度?它會把真人語音判成「雜訊」,連「有沒有人在說話」都分不出來

我做了 11 次不洩題的嘗試,命中真實內容 0 次

排除過程我跑得很完整:

  • 懷疑缺套件(av / soundfile / librosa)→ 擴建 audio image 後音頻可正常解碼,render 端點也證實音頻 token 有正確插進 prompt → 還是聽不懂
  • 懷疑語言或 prompt 問題 → zh/en 六種問法、音頻放前面放後面、五種不同音檔全跑過 → 還是 0 命中

根因到現在我都沒完全確定。推測是音頻塔的 Linear 層在這個量化 recipe 下比視覺塔脆弱,但也可能是 vLLM dev 版的 gemma4 音頻路徑本身有問題,我沒有隔離出來

這題我還沒解。 可能的解法是重量化時把音頻模組放進 ignore 清單保留 bf16,或直接用非量化版

💡Tip: 這件事對你的實際意義是——「這個量化版本可以用」不是一個單一答案。你要問的是「我要用的那個模態可以用嗎」。跑通了文字就宣布量化沒問題,是我犯過的錯。


量化到底掉多少準確率?

前面都在講「哪個會壞」,你一定想問**「不壞的那些,掉幾分?」**

老實說這題我在自己機器上答不了——我手上只有 NVFP4 這一個版本在線,沒有同模型的 BF16 可以當對照組,跑不出 A/B

所以我去找公開數據。

💡Tip: 先講一個方法論上的提醒——看到量化準確率數字,一定要先問三件事:哪個模型、哪個 benchmark、哪個量化方法。 少任何一項,那個數字就沒有意義。「INT4 掉 2%」這種話單獨拿出來講,等於沒講。

下面每個數字我都會標這三項,還有資料屬性(因為 Neural Magic / Red Hat 講自家量化方案,那是廠商自述,你要打點折扣看)

純文字任務:其實掉得不多

模型 Benchmark 量化 分數(BF16 對照) 屬性
Llama-3.1-8B MMLU 5-shot W8A8-INT 67.8%(68.3%) 廠商自述
Llama-3.1-8B MMLU 5-shot W4A16-GPTQ 66.9%(68.3%) 廠商自述
Llama-3.1-8B GSM8K CoT W8A8-INT 84.8%(82.8%) 廠商自述
Llama-3.1-8B TruthfulQA W4A16-INT 52.8%(54.5%,保留 96.9%) 廠商自述
DeepSeek-R1-70B MATH-500 W4A16-INT 65.6%(67.8%,保留 98.3%) 廠商自述

大致的結論是:

  • W8A8-FP8 幾乎無損(保留率 99.3–100.1%)
  • W8A8-INT8 掉 1–3 分
  • W4A16-GPTQ 跟 W8A8-INT8 差不多——這點蠻反直覺的,4-bit weight 沒有比 8-bit 差多少

(GSM8K 那行你沒看錯,量化後比 BF16 高 2 分。這種事在 benchmark 上會發生,屬於雜訊範圍,不要拿它宣稱「量化讓模型變聰明」🙂)

多模態任務:這裡才是重點

這組數據我看到的時候真的愣了一下:

模型 Benchmark 量化 分數(BF16 對照) 屬性
GLM-4V-9B OCRBench RTN W8A8 0.00(782) 一手學術
GLM-4V-9B OCRBench MQuant W8A8 782(782) 一手學術
Qwen-VL-Chat-9.6B DocVQA RTN W8A8 0.03(60.36) 一手學術
Qwen2-VL-7B TextVQA RTN W8A8 33.92(84.43) 一手學術
LLaVA1.5-13B ScienceQA W4A4 MQuant 87.18(90.00,96.9%) 一手學術
Qwen2VL-72B DocVQA W4A8 MQuant 95.49(95.95) 一手學術

看前兩行

同一個模型、同一個 benchmark、同樣是 8-bit。

一個掉到 0 分,一個完全無損

差別只在量化方法:RTN(最 naive 的四捨五入)vs MQuant(針對 VLM 設計的方法)

所以在多模態上,真正的結論是:

「方法」比「位元數」關鍵得多。

你不能說「8-bit 安全、4-bit 危險」。你得說「這個方法這個模型上安不安全」

而且注意 OCRBench 掉到 0 這件事——那不是「準確率下降」,那是能力整組消失。跟我 Day 4 前面講的音頻案例、W4A4 案例是同一種現象

💡Tip: 這也解釋了為什麼 Day 3 講的「先跑 BF16 建立基線」那麼重要。如果你一開始就用量化版,OCRBench 0 分你會以為「這模型不會做 OCR」,而不是「我的量化把它打壞了」。歸因錯誤的代價是你換掉一顆本來很好的模型。

4-bit 浮點格式:NVFP4 真的比較準嗎?

這組只有一個模型的數據,所以參考就好(我標 MAYBE):

量化格式(皆 W4A4,Llama-3.1-8B) 準確率保留 屬性
NVFP4(GPTQ) 95.92% 二手/半廠商
MXFP4(MR-GPTQ) 93.31% 二手/半廠商
INT4(RTN) 92.63% 二手/半廠商
MXFP4(RTN) 87.83% 二手/半廠商

排序符合前面講的理論——NVFP4 的 block size 16 比 MXFP4 的 32 細,scale 表示也更精確,所以保留率最高

但請注意:最好的方法(NVFP4+GPTQ 95.92%)跟最差的(MXFP4+RTN 87.83%)差了 8 個百分點,而這兩個都是 4-bit。 又一次印證「方法 > 位元數」

我沒找到的東西(這很重要)

我想給你一張「繁中 OCR 的量化退化表」

找不到。

  • 繁中 / CJK OCR 的量化評測:完全沒有公開數據
  • 量化後 CER(字元錯誤率)上升多少:找到的資料自相矛盾,我判定不可用
  • 中文 benchmark(CMMLU、C-Eval)的量化對照:沒有
  • NVIDIA 官方一手的 NVFP4 準確率數字:只找到二手與聚合陳述

這個空白本身就是情報。它的意思是:

在繁中 OCR 這個場景,你的量化決策沒有現成答案可抄,只能自己測。

而「自己測」需要一套評測機制——這就是 Day 27 抽樣稽核跟 Day 25 flywheel 要解的事。這系列的每一塊都是這樣互相咬住的


實際部署:vLLM 配置

講完理論,來看真的要動手的部分

換模型時最重要的判斷框架

這是我自己歸納的,換模型時每個參數都要先分類,再決定怎麼處理

類別 包含什麼 怎麼處理
模型專屬 image tag、parser 名稱、max-model-len、量化 flag 全部重查,逐字抄官方來源
硬體專屬 tensor-parallel-sizegpu-memory-utilization、ports 沿用,換機器才改
禁止繼承 moe-backendquantizationkv-cache-dtypeattention-backend、patch 檔 一律先刪,確認需要才加回

為什麼 ③ 要獨立成一類,不併進 ①?

①是「知道要改」,③是「你不會意識到它不該在那裡」。

從舊模型的 compose.yaml 複製貼上,最容易死在 ③ 這類——因為你根本沒注意到那行還在

我踩過的坑(全部真實發生)

症狀 根因 解法
套件版本衝突起不來 vLLM pin transformers<5,Gemma 4 需 >=5.5.0 走 native venv,避開 NGC container
pydantic ValidationError CLI 指定 --quantization modelopt,但 config 宣告的是 compressed-tensors 移除 explicit --quantization,讓 vLLM 自動偵測
tool call 抽不出來 --tool-call-parser 用了 pythonic,但模型是 gemma4 格式 對齊模型實際格式
服務起不來、噴記憶體錯誤 max_model_len 多打零(131072000 改回 65536
連 64K request 都裝不下 --kv-cache-memory-bytes 3G 在統一記憶體上自設瓶頸 整行刪掉
記憶體分配失敗 --gpu-memory-utilization 0.3 要 36.49 GiB,實際只剩 28.4 GiB 降到 0.18–0.20,或獨佔機器時 0.5–0.7

第二條我想特別標起來:

當模型 config 已經帶了量化宣告,不要在 CLI 再硬指定,否則會 override 成錯誤值。

checkpoint 自己知道它是怎麼量化的。你在 CLI 再講一次,只會製造衝突

另外五個坑(會讓你懷疑人生的那種)

  1. image tag 只在 nightly — 你照著文件寫的 tag 在 stable 裡不存在
  2. parser 名稱差一個底線就靜默失效 — 不報錯,只是 tool call 永遠抽不出來
  3. generation_config 可能是 greedy — 導致每次輸出一模一樣,你以為是 bug,其實是設定
  4. command> 折疊字串 — YAML 折疊後被 shell 拆詞,JSON 的引號會被吃掉
  5. 冷啟動要數百秒 — healthcheck 的 start_period 不夠長,容器會無限重啟

症狀反查表

這張表是我除錯時最常回頭看的東西,按症狀找方向,不要一有問題就先怪量化

症狀 先查什麼 不要先查
KV cache 不足 調 gpu-memory-utilization(方向別反)
每次輸出一模一樣 --override-generation-config 溫度參數
內容通順但停不下來 eos_token_id 清單不全 量化
內容亂碼、語意崩壞 量化格式與 MoE backend 是否對上 checkpoint

第三行是我的血淚。「內容通順但停不下來」跟「內容壞掉」是完全不同的兩個問題,前者是 EOS 設定,後者才輪到量化

明天我會完整講這個案例,因為它後面還有一個更陰險的轉折 😅


選型順序:六個步驟

最後給你一個可以照做的順序:

  1. 先跑 BF16 驗完功能。量化是優化不是起點。 你要先有一個「功能正確」的基線,才知道量化之後掉的是什麼
  2. 記憶體不夠再降。FP8 通常是第一站
  3. 要 4-bit 先看模態——多模態走 W4A16,不要碰 W4A4
  4. NVFP4 / MXFP4 的差別在 block size(16 / 32)、scale 表示法、硬體原生度
  5. checkpoint 已宣告量化方法時,CLI 不要再指定
  6. 不要把 GGUF 的量化命名寫進 vLLM 設定

💡Tip: 第 1 點是六點裡最常被跳過、也最貴的一點。直接上量化版然後發現「模型好像怪怪的」,你會分不出那是量化造成的、還是這顆模型本來就不行。沒有基線就沒有歸因。 這句話 Day 25 講 flywheel 的時候還會再出現一次。


小結

今天講完兩條路的第一條

量化幫我把 26B 模型的權重壓到約 12.6 GB,讓 MoE 的活躍權重只剩約 1.9 GB 要搬——這才有昨天算出來的 143 tok/s 理論上限

但量化也帶來新問題:多一層「它到底有沒有壞」的不確定性

而且從音頻那個案例你會發現,這個不確定性不是全有全無,是分模態的

明天講第二條路:推測解碼、batch 調校,然後把 Day 2 的失敗模式跟這三天的硬體限制放在同一張表上

Phase 1 收斂,我們會得到一個結論:單一模型不夠。


上一篇
Day 3 - DGX Spark GB10:硬體限制怎麼反過來決定架構
下一篇
Day 5 - Batch 推理、Speculative Decoding 與 Phase 1 收斂
系列文
從 LLM 到 Harness: 打造隱私與可信任的繁中進階 OCR Agent5
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
justin_log
iT邦新手 5 級 ‧ 2026-09-18 13:31:42

期待明天的推測解碼、batch 調校!!!

我要留言

立即登入留言