DGX Spark · part 36
[Benchmark] Gemma 4 12B omni 上 DGX Spark:weight-only NVFP4 贏 W4A4,還保住多模態
❯ cat --toc
TL;DR
我在 DGX Spark GB10 上量化了 Google 剛出的 omni Gemma 4 12B(同一顆吃文字 + 圖片 + 語音 + 影片),用 vLLM 測 throughput。結論:weight-only NVFP4(W4A16)是贏家 —— 7.7GB、24.9 tok/s,四種模態全都還能用。 直覺會選的全 NVFP4(W4A4)其實一無所獲:沒比較快(23.9 vs 24.9,差在誤差範圍內),還把圖片/語音/影片弄壞 —— 因為它的 activation 量化是用文字校準的,把分布外的多模態 embedding 夾爆了。FP8 在中間,15.9 tok/s / 13GB。還有一個坑:在純 transformers 裡每種量化都「比 BF16 慢」(沒 native kernel),真正的加速只有上 vLLM 才看得到。
白話版
Gemma 4 12B 是一顆能讀文字、圖片、語音、影片的模型。量化會讓檔案變小,在對的 runtime 上也會變快。意外的是:最激進的 4-bit 設定(權重和 activation 都量化)反而把圖片/語音/影片的理解弄壞,而且根本沒比較快。只量化權重的版本檔案更小、速度持平、能力全保住。所以對多模態模型,不要量太兇反而贏。
數字:NVFP4 weight-only 是贏家
同一台 DGX Spark(GB10、sm_121a、頻寬 273 GB/s),vLLM 0.22.1 native,單流 decode、warm、EN+ZH:
| 格式 | 檔案 | tok/s | vs BF16 | Omni |
|---|---|---|---|---|
| BF16 | 23 GB | 7.7 | — | ✅ |
| FP8 dynamic | 13 GB | 15.9 | 2.1× | ✅ |
| NVFP4 W4A4 | 7.7 GB | 23.9 | 3.1× | ❌ 壞掉 |
| NVFP4 W4A16(weight-only) | 7.7 GB | 24.9 | 3.2× | ✅ |
weight-only 那版檔案最小、速度跟 W4A4 持平,而且是唯一一個還保住多模態的 4-bit 版本。直覺選的全 W4A4 —— 速度上一點便宜都沒佔到,還把多模態整個賠掉。多那一層 activation 量化,在這裡是純虧。
為什麼 W4A4 會把 omni 弄壞
NVFP4 W4A4 把權重跟 activation 都壓到 4-bit。activation 的 scale 是跑一次校準算出來的 —— 而我跟大家做 LLM 一樣,用文字校準(UltraChat)。
坑就在這。當你餵一張圖或一段語音,那個 encoder-free 的 projector 會把原始的 patch / waveform embedding 送進語言模型。這些 activation 的分布跟文字不一樣。用文字校出來的 4-bit activation 範圍會把它們夾掉,模型就崩了:我實測下來,圖片跟影片回空白、語音吐出一堆不相關的字,但純文字完全正常。
關鍵線索是語音:它壞了,可是語音 projector 本身是留 BF16 的。所以兇手不是被量化的 projector,是 text tower 的 activation 量化,對多模態 activation 來說整個是分布外。
weight-only NVFP4(W4A16)沒有 activation 量化 —— activation 留 BF16 —— 所以沒有校準對不上的問題。圖片、語音、影片全都回來了:
- 圖片:「錄音室裡兩隻貓,戴著耳機,牆上有 'ON AIR' 霓虹燈,專業麥克風,筆電。」
- 語音:把一段 LibriSpeech 轉成文字 —— "Mr. Quilter is the apostle of the middle classes..."。
- 影片:「一個穿黑色緊身衣的女子走在夜晚的濕街上。」
而且 weight-only 在速度上一毛都不用付:在這種 bandwidth-bound 的 dense 模型上,W4A16 那條路(dequant 回 BF16)甚至小贏一點(24.9 對 23.9,算平手)。所以 W4A4 的 activation 量化在這裡換不到任何 throughput。這跟我在 Part 32 對 dense 模型測到的一致 —— 關鍵在頻寬,不是 FP4 ALU。
HF-eager 陷阱:用錯 runtime,量化看起來「更慢」
有個數字差點把我帶歪。同一份量化權重,改用純 transformers(HF eager)跑:
| 格式 | HF eager tok/s | vLLM native tok/s |
|---|---|---|
| BF16 | 7.3 | 7.7 |
| FP8 | 5.3 | 15.9 |
| NVFP4 W4A16 | 4.7 | 24.9 |
在 transformers 裡 NVFP4 是最慢的 —— 因為沒有 native FP4 kernel,每次 forward 都把權重拆回 BF16:量化的開銷全付了,頻寬的好處一個都沒拿到。換上 vLLM 的 native kernel,排序整個翻過來 —— NVFP4 變最快,同一份權重差 5 倍。永遠用你真的會拿去 serve 的 runtime 來測。
在 GB10 上 serve gemma4_unified:兩件必備
Gemma 4 12B 是個新架構(gemma4_unified、Gemma4UnifiedForConditionalGeneration)。要在 DGX Spark 上 serve,有兩個不明顯的條件:
- 要有含 native class 的 vLLM build(大約 0.22.x / main)。舊版會 fallback 到 vLLM 的 generic transformers backend,在注意力的 output projection 那步直接崩 —— 因為 Gemma 4 的注意力是非對稱的:head_dim 256 × 16 heads = 4096,跟 hidden 3840 不一樣(它是 GQA,8 個 KV head)。generic backend 預設了對稱的情況,就把投影的形狀切錯了。
VLLM_ATTENTION_BACKEND=TRITON_ATTN—— 這個 backend 才處理得了那個非對稱注意力。
VLLM_ATTENTION_BACKEND=TRITON_ATTN \
vllm serve coolthor/gemma-4-12B-it-NVFP4A16 --max-model-len 4096
(提醒:vLLM 現在的 generic 多模態 wrapper 只吃 image,所以完整的 audio/video serving 還在等上游 —— 但四種模態都能用 transformers 跑。)
真正能在 vLLM 上載入的量化 recipe
這部分很龜毛又沒文件。vLLM 的 native gemma4_unified 會量化特定一組模組,如果你 checkpoint 裡哪些層有量化、哪些沒量化跟它對不起來,它就載不進來。能動的 recipe:
QuantizationModifier(targets="Linear", scheme="NVFP4A16",
ignore=["lm_head", "re:.*embedding_projection.*"])
白話:量化 text tower 加上視覺的 patch_dense;lm_head 跟兩個 embedding_projection(視覺 + 語音)留 BF16。這跟 vLLM 怎麼建這顆模型一致 —— patch_dense 是帶 quant config 建的 ColumnParallelLinear,projector 則是純 BF16 linear。(另外:用 llmcompressor 的 basic pipeline,不要用 sequential —— sequential 的 tracer 在這個新架構上會撞 UserDict 錯誤。)
模型
兩個都在 Hub 上,Apache 2.0,omni 完整:
- coolthor/gemma-4-12B-it-NVFP4A16 —— 最小 + 最快(7.7GB、24.9 tok/s)。
- coolthor/gemma-4-12B-it-FP8-dynamic —— 保守選項(13GB、15.9 tok/s、動態 activation、不用校準)。
TL;DR
- 在 DGX Spark GB10 上,Gemma 4 12B omni weight-only NVFP4 贏:7.7GB、24.9 tok/s(BF16 的 3.2 倍)、四種模態全保住。
- 全 W4A4 是個坑(對多模態模型)—— 沒比較快,還把圖片/語音/影片弄壞(文字校準的 activation 量化對多模態 embedding 是分布外)。
- HF eager 會騙你 —— 在那裡量化看起來更慢;真正的加速要 vLLM 的 native kernel(差 5 倍)。
- 要 serve:有 native
Gemma4Unified的 vLLM +TRITON_ATTN。
本系列其他篇:Part 32 — NVFP4 是壓縮,不是 FP4 核心、Part 33 — NVFP4 W4A4 在 MoE 上贏 FP8、Part 34 — NVFP4 把影片模型縮小卻沒變快、Part 26 — Nemotron Omni 在 GB10 上看影片。
常見問題
- 多模態 LLM 要用 NVFP4 W4A4 還是 weight-only W4A16?
- omni 模型用 weight-only NVFP4(W4A16)。在 DGX Spark GB10 上跑 Gemma 4 12B,W4A16 是 24.9 tok/s 而且圖片/語音/影片都正常;全 W4A4(權重+activation 都 4-bit)沒比較快(23.9 tok/s,在誤差範圍內),卻把多模態弄壞了。W4A4 的 activation scale 是用文字校準的,圖片/語音的 embedding 落在分布外,就被夾掉了。
- Gemma 4 12B 在 DGX Spark GB10 上量化後多快?
- vLLM 0.22.1 下單流 decode:BF16 7.7 tok/s(23GB)、FP8 dynamic 15.9 tok/s(13GB)、NVFP4 weight-only 24.9 tok/s(7.7GB)。NVFP4 是 BF16 的 3.2 倍。但同一份量化權重在純 transformers(HF eager)反而比 BF16 慢 —— 因為那裡沒有 native FP4/FP8 kernel,速度優勢只有上 vLLM 才看得到。
- 為什麼 NVFP4 在 Hugging Face transformers 裡看起來比 BF16 慢?
- transformers 沒有 native FP4/FP8 kernel,所以每次 forward 都把權重拆回 BF16 再算 —— 純開銷、拿不到頻寬帶來的好處。HF eager 下 NVFP4 量到 4.7 tok/s(最慢)。上 vLLM 的 native kernel,同一份權重變 24.9 tok/s(最快)。一樣的權重差 5 倍,所以一定要用你實際 serve 的 runtime 來測。
- Gemma 4(gemma4_unified)怎麼在 vLLM 上跑起來?
- 要用有 native Gemma4UnifiedForConditionalGeneration class 的 vLLM build(大約 0.22.x / main),並設 VLLM_ATTENTION_BACKEND=TRITON_ATTN。Gemma 4 的注意力是非對稱的(head_dim 256 × 16 heads = 4096,跟 hidden 3840 不一樣,是 GQA 8 個 KV head),TRITON_ATTN 是能正確處理這個的 backend。舊版 vLLM 會 fallback 到 transformers backend,在注意力投影那步直接崩。