昨天結論是:單序列速度撞到記憶體頻寬的物理上限,調參救不回來,只剩兩條路
今天走第一條:量化
我先講結論,因為這篇會有點長:
量化不是「把模型變小」,是「用精度換記憶體頻寬」。而在多模態模型上,換錯了會整組能力消失。
我剛開始碰這塊的時候最崩潰的一點是,大家講的「量化」根本不是同一件事
有人說 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 還是 NVFP4」,大概就像問「你要選紙箱還是玻璃」
實務上的對應關係是這樣:
| 你用的推論框架 | 對應路線 |
|---|---|
| llama.cpp / Ollama | GGUF 容器 + k-quants |
| vLLM + Blackwell | safetensors 容器 + NVFP4 |
我這台是 vLLM + GB10,所以走右邊那條
💡Tip: 有個延伸的坑——不要把 GGUF 的量化命名(Q4_K_M 這種 K-Quant 寫法)寫進 vLLM 的 compose.yaml。它不吃這個。我看過有人這樣寫然後debug 一整晚。
這兩個長得超像,而且選錯不會報錯,只會吐亂碼(這條 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 直接支援。
這節是這整篇對 OCR 系列最重要的部分
W4A16 = weight 量到 4-bit,activation 保留 16-bit
W4A4 = weight 跟 activation 都量到 4-bit
W4A4 更省、更快。聽起來很香
我實測的結果是:它會把視覺能力整組打壞
在 DGX Spark 上拿 coolthor/gemma-4-12B-it-NVFP4A16 當對照,W4A4 之後這四種能力全部劣化:
原因是 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 → 還是聽不懂
根因到現在我都沒完全確定。推測是音頻塔的 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%) | 廠商自述 |
大致的結論是:
(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」,而不是「我的量化把它打壞了」。歸因錯誤的代價是你換掉一顆本來很好的模型。
這組只有一個模型的數據,所以參考就好(我標 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 的量化退化表」
找不到。
這個空白本身就是情報。它的意思是:
在繁中 OCR 這個場景,你的量化決策沒有現成答案可抄,只能自己測。
而「自己測」需要一套評測機制——這就是 Day 27 抽樣稽核跟 Day 25 flywheel 要解的事。這系列的每一塊都是這樣互相咬住的
講完理論,來看真的要動手的部分
這是我自己歸納的,換模型時每個參數都要先分類,再決定怎麼處理:
| 類別 | 包含什麼 | 怎麼處理 |
|---|---|---|
| ① 模型專屬 | image tag、parser 名稱、max-model-len、量化 flag |
全部重查,逐字抄官方來源 |
| ② 硬體專屬 | tensor-parallel-size、gpu-memory-utilization、ports |
沿用,換機器才改 |
| ③ 禁止繼承 | moe-backend、quantization、kv-cache-dtype、attention-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 再講一次,只會製造衝突
generation_config 可能是 greedy — 導致每次輸出一模一樣,你以為是 bug,其實是設定command 用 > 折疊字串 — YAML 折疊後被 shell 拆詞,JSON 的引號會被吃掉start_period 不夠長,容器會無限重啟這張表是我除錯時最常回頭看的東西,按症狀找方向,不要一有問題就先怪量化:
| 症狀 | 先查什麼 | 不要先查 |
|---|---|---|
| KV cache 不足 | 調高 gpu-memory-utilization(方向別反) |
— |
| 每次輸出一模一樣 | 缺 --override-generation-config |
溫度參數 |
| 內容通順但停不下來 | eos_token_id 清單不全 |
量化 |
| 內容亂碼、語意崩壞 | 量化格式與 MoE backend 是否對上 checkpoint | — |
第三行是我的血淚。「內容通順但停不下來」跟「內容壞掉」是完全不同的兩個問題,前者是 EOS 設定,後者才輪到量化
明天我會完整講這個案例,因為它後面還有一個更陰險的轉折 😅
最後給你一個可以照做的順序:
💡Tip: 第 1 點是六點裡最常被跳過、也最貴的一點。直接上量化版然後發現「模型好像怪怪的」,你會分不出那是量化造成的、還是這顆模型本來就不行。沒有基線就沒有歸因。 這句話 Day 25 講 flywheel 的時候還會再出現一次。
今天講完兩條路的第一條
量化幫我把 26B 模型的權重壓到約 12.6 GB,讓 MoE 的活躍權重只剩約 1.9 GB 要搬——這才有昨天算出來的 143 tok/s 理論上限
但量化也帶來新問題:多一層「它到底有沒有壞」的不確定性
而且從音頻那個案例你會發現,這個不確定性不是全有全無,是分模態的
明天講第二條路:推測解碼、batch 調校,然後把 Day 2 的失敗模式跟這三天的硬體限制放在同一張表上
Phase 1 收斂,我們會得到一個結論:單一模型不夠。