~/blog/dgx-spark-huihui-gemma4-fp8-mtp-34pct

DGX Spark · part 28

想用 MTP 加速 abliterated Gemma 4?vanilla draft 對不上被改過的 body

cat --toc

TL;DR

想用 MTP 加速 abliterated 版本的 Gemma 4?vanilla 官方 draft 是配 vanilla body 訓練的,body 被 abliterate 改過後 draft 跟不上 — n 越深複合 error 越大,猜對機率越低,vanilla draft 形同失效。

自量化 huihui-ai/Huihui-gemma-4-26B-A4B-it-abliterated 成 FP8-Dynamic ship 上 HF(2026-05-09 我在 HF 上沒找到此 base 其他 vLLM-loadable FP8 衍生)。同 config 對照:abliterated body 跟 vanilla baseline 完全一樣(39.4 vs 39.3 tok/s),n=1 上 MTP 加成也一樣;但 n=4 deep speculation 上 vanilla 過去做到 108 tok/s(Part 27 config),huihui 同 stack 只到 50。元兇推測是 per-position acceptance decay:huihui 每 step 衰減 ~22pp(65/43/29/21,vLLM /metrics 實讀)。⚠️ 寫 blog 兩稿我自己擺了三個 fabrication(60 tok/s baseline、92/85/78/70 vanilla decay、Gemma 4 license),都被 Codex 連續 attack 打掉。這篇是第三版,把坑老實寫進去當反面教材。

衝突在哪:vanilla draft 跟 abliterated body 對不上

先講結論再給證據。Speculative decoding(spec decode)三角:主模型 body小 draft modelnum_speculative_tokens=n(一次猜幾格)。draft 一次猜 n 個 token,body 一次性驗證,猜對的接受、猜錯的退回。

Google 釋出的 gemma-4-26B-A4B-it-assistant 是個 4-layer Gemma4Assistant,配 vanilla google/gemma-4-26B-A4B-it body 訓練。draft 模型學到的就是「vanilla body 在這個 hidden state 下,下一個 token 機率分布大概長這樣」。

但 abliteration 流派(huihui / TrevorJS / llmfan46 / Heretic ARA)做的事情,就是改 body 的 weight 讓 distribution shift — 拔掉 refusal direction 的代價是整個 hidden state 子空間的偏移。draft 看到的不再是它訓練時記得的 vanilla 分布,猜對機率自然下降。

更糟的是 autoregressive draft 的複合:

  • n=1 → draft 只猜下一格(從 body 真實 hidden state 出發),只吃一次 distribution mismatch。可以接受。
  • n=4 → draft 猜 pos 0,然後用自己 pos 0 的 prediction 當 input 往 pos 1 推,再用 pos 1 推 pos 2…… 每滾一格疊一次 mismatch。n 越深,猜對機率越低。

所以 deep speculation 在 abliterated body 上結構性失效:不是 draft 慢,是 draft 答案越來越離譜被 body 全 reject。下面整篇用測試把這個 mechanism 釘在數字上。

為什麼自量化

huihui-ai 是 abliteration 圈出量大的 publisher 之一,他們的 26B Gemma 4 BF16 在 HF 上 download 數量級數萬(2026-05-09 我這邊看 14188)。Quant 衍生(同樣是 2026-05-09 抓)的狀況:

格式公開狀態
BF16 原版✅ huihui-ai 自己
FP8-Dynamic(vLLM 用)❌ HF 上我搜不到
NVFP4✅ sakamakismile
GGUF(多家)✅ groxaxo / bullerwins / mradermacher / iamhsouna
MLX(Apple)✅ vanch007
Q8_0 GGUF✅ puppert

vLLM 用戶要跑這個 model 只剩兩條路:--quantization fp8 runtime 動態量化(實測 ~6× 慢),或自己預量化成 FP8 safetensors。GGUF 路徑能餵 llama.cpp / ollama / oobabooga textgen / LM Studio 等(NVFP4 路徑也有人放),但 vLLM 直接吃 GGUF 還在實驗階段、不適合生產 — 所以這個 base model 在 vLLM 圈的 FP8 niche 還沒人補,我們補。

量化 recipe

工具:llm-compressor + FP8_DYNAMIC scheme。Critical ignore list — re:.*router.* 必須在,MoE router weights 不能被量化,否則 expert dispatch 會崩(我們在自己測試時驗過)。

ignore = [
    "re:.*router.*",        # ← 關鍵,MoE router 不能量化
    "lm_head",
    "re:.*embed_tokens.*",
    "re:.*norm.*", "re:.*layernorm.*", "re:.*layer_norm.*",
    "re:.*rmsnorm.*", "re:.*rms_norm.*",
    "re:.*conv1d.*", "re:.*linear_attn.*",
    "re:visual.*", "re:model.visual.*",
    "re:.*patch_embed.*", "re:.*vision.*", "re:.*image.*",
    "re:.*video.*", "re:.*projector.*", "re:.*merger.*",
    "re:.*mlp.gate$", "re:.*shared_expert_gate.*",
    "re:.*embed_audio.*", "re:.*embed_vision.*",
    "re:.*audio_tower.*", "re:.*audio_projector.*",
]

踩坑提醒:llm-compressor 穩定版 PyPI metadata 寫 transformers>=4.56.1,<=4.57.6,但 Gemma4ForConditionalGenerationtransformers v4.57.6 還沒進 — 我實測得用比這個 pin 更新的 transformers 分支(我這邊跑通的版本是 5.x 系列,具體 minor 看你 venv)。修法是 patch llmcompressor/entrypoints/utils.pyuse_auth_token=... 拿掉(我在較新 transformers 分支上需要這樣改才跑通,新版 from_pretrained 是改用 token=...)。

GB10 上量化跑 2.9 分鐘(data-free pipeline,純 calibration + scale derivation,不需要訓練資料)。產出 27 GB FP8。

Smoke test 三模態

模態結果備註
Text 英文✅ haiku 語意正確1.7 秒
Text 繁體中文✅ 80 tok 自嘲幽默,abliteration 還在2.3 秒
Vision(古風美女漢服圖)✅ 漢服、竹林、薄霧、低頭表情全描述對2.8 秒
AudioN/A — Gemma 4 26B-A4B-it 架構本身 audio_config: null不是 quant 的鍋

上路:vanilla / huihui 雙邊同 config 對照

Part 27 我們紀錄過 vanilla Gemma 4 + MTP γ=4 達到 108.78 tok/s(N=5,std < 1%),baseline 40.85(2.66×)。Part 27 的 launch config 是 max_model_len=4096 + gpu_memory_utilization=0.85,但 acceptance 沒 publish 數字(只有 vLLM test 套件的 80% prompt-similarity gate,跟 token-level acceptance 是兩回事)。

今天為了 apples-to-apples 對照,我重新在同一台 GB10 上同 config 跑了 vanilla + huihui 兩邊 — 不直接拿 Part 27 數字當 vanilla 對照(quantization provenance 不同、bench 條件不同)。

項目
max_model_len8192(對照組)/ 65536(huihui 大 KV 嘗試)
gpu_memory_utilization0.65 / 0.85
kv_cache_dtypefp8
temperature0.7
tool_call_parsergemma4
MTP draft modelgoogle/gemma-4-26B-A4B-it-assistant
vLLM imagevllm/vllm-openai:gemma4-0505-arm64-cu130
MTP fix modmods/gemma4-mtp-fix-only(PR #41745 head 的 gemma4_mtp.py)
FP8 sourcehuihui 自量化(re:.*router.* ignore list 全配)/ vanilla 用 RedHatAI gemma-4-26B-A4B-it-FP8-Dynamic(ignore list 較精簡)

⚠️ Confound 自首:vanilla 跟 huihui 的 FP8 量化路徑不一樣(不同 publisher、不同 ignore list)。Body baseline 兩邊都跑出 ~39 tok/s 是個強信號(quant pipeline 差異沒拉開速度差),但 per-position acceptance 跨兩邊比較時這條 confound 還在,讀者讀數字時要扣掉這個雜訊。

證據 1:sweep n=1..4,看猜對機率掉到哪

如果文章開頭的 mechanism 對,那 throughput 跟 acceptance 都應該隨 n 變大而走低 — n=1 只吃一次 mismatch,n=4 吃四次複合。

huihui FP8 throughput sweep(同 config,只變 n):

num_speculative_tokensThroughputToken-level acceptancePos-0 acceptance
152.6 tok/s69%
251.4 tok/s57%68%
346.9 tok/s46%64%
450.0 tok/s39%66%

huihui 是 n=1 peak,n>1 都更慢,沒有 sweet spot。

bench methodology disclaimer:每個 cell 是 N=10 prompts × T=0.7 × batch=1,prompt 池固定但沒固定 seed,server 都跑過 1 個 warmup prompt 才開始計時。沒做 run-to-run repetition,所以 ±1-2 tok/s 的差距讀者要當 noise(不是「n=2 比 n=1 慢」這種小差距是統計顯著事件)。 acceptance 數字直接從 vLLM /metricsvllm:spec_decode_num_accepted_tokens_per_pos_total 抓,跨 10 prompts 平均。

vanilla Gemma 4 對照(Part 27 已 published,N=5 std<1%):

nThroughputBaseline倍率
4108.78 tok/s40.852.66×

(Part 27 launch config 是 max_model_len=4096 + gpu_mem=0.85,跟我今天 huihui 大 KV 嘗試的 65536/0.85 不完全一致,單純說「vanilla deep spec 上得去」這個結論方向沒問題,但拿來做點對點比較不算 apples-to-apples。Part 27 article 沒 publish token-level acceptance 數字,只有 vLLM test 套件的 80% prompt-similarity gate — 那個 80% 不是 acceptance 指標,我之前文章裡併寫成「~80% acceptance」是誤讀。)

vanilla 在 n=4 上仍能拉到兩倍以上,huihui 在 n=4 上反而慢於 n=1

自首:三個我塞進去的編造數字

寫這篇兩稿過程,我自己塞了三個沒驗證的數字進去,都被 Codex 在 /debate attack 跟 Step 9 fact-check 抓回來。記下來當「LLM 寫技術文沒查就硬寫」的反面教材。

1. Vanilla baseline 60 tok/s(第一稿,實測 39.4)

第一稿沒實測 vanilla 在今天 config 下的 baseline,憑感覺寫「vanilla baseline 推估 ~60 tok/s」當對照,然後得出「huihui 39.3 vs vanilla 60 = 50% abliteration tax」的結論。Codex 動手讀 Part 27 .mdx 文件後指出 vanilla 同 config 的 baseline 沒人測過,我那 60 是憑空的。

實際補測 vanilla 同 config(max_model_len=8192gpu_mem=0.65,小 KV 那組):

baseline(no spec)+ MTP n=1
vanilla FP839.4 tok/s51.9 tok/s
huihui FP839.3 tok/s52.6 tok/s
Δ0.1(noise)0.7(noise)

body 完全不收稅。「50% tax」假說整個錯,這也是整篇從 test-first 改成 mechanism-first framing 的觸發點。

2. Vanilla per-position decay 92/85/78/70(第二稿,實際沒測)

第二稿想對照 huihui 65/43/29/21,我寫了一組「vanilla 估 92/85/78/70」當對比 — 那是假設等比衰減 + 從 token-level 80% 反推的 numerology,不是 measure 出來的。Codex 第二輪 attack 直接抓出 Part 27 自己沒 publish per-position 數字,我那組「估」就是從幾何級數推回去的看似精確的編造。已拿掉,留下 hole 比留 fake precision 好。

3. Gemma 4 license「不是純 Apache 2.0」(第二稿,實際就是)

第二稿我又寫「Gemma 4 受 Gemma Terms of Use 約束、不是純 Apache 2.0」,Codex 第二輪 curl 打臉:

Gemma 1/2/3 走 ToU,Gemma 4 改成 Apache 2.0。HF release 用 apache-2.0 沒問題。

那 Part 27 的 108 tok/s 是哪裡來的?

主要差兩件事:num_speculative_tokens=4 + Part 27 自己的 launch config

  • Part 27 launch:max_model_len=4096 + gpu_memory_utilization=0.85 + num_speculative_tokens=4
  • 我今天小 KV 對照組(同 stack 跑 vanilla / huihui):max_model_len=8192 + gpu_mem=0.65 + n=1 時兩家都 ~52 tok/s
  • 我今天 huihui 大 KV 嘗試:max_model_len=65536 + gpu_mem=0.85 + n=4 → 50 tok/s

vanilla 在 deep spec(n=4)上能拉到兩倍以上(52 → 108);但 huihui 在 deep spec 上不上去(52 → 50)。

關鍵變數是 num_speculative_tokens,不是 KV budget 大小。

Per-position decay 把 mechanism 釘在數字上

文章開頭講的「draft 每滾一格疊一次 distribution mismatch」,從 vLLM /metricsvllm:spec_decode_num_accepted_tokens_per_pos_total 讀出來長這樣:

huihui per-position(n=4 大 KV,實讀):
  pos 0: 65.6%
  pos 1: 43.3%   ← 衰減 22.3pp
  pos 2: 29.2%   ← 衰減 14.1pp
  pos 3: 20.5%   ← 衰減 8.7pp

每往後一格,draft 在自己上一個錯誤 prediction 上滾,猜對機率掉了 ~22pp。串到 pos 3 時 draft 真的是在亂猜了 — pos 3 acceptance 20% 等於 80% 的時候 body 直接 reject draft 那格、改用自己的 forward 結果。

Vanilla 這邊我沒實測 per-position,Part 27 也沒 publish。可以講的只有「vanilla n=4 上拉到 2.66× baseline,平均 acceptance 必須相對高(否則複合收益不會這麼大)」。配對比較我故意不做(理由見下面自首章節)。

對 hikari/kiriha use case 的建議

hikari + kiriha 主力 LLM 走 spec decode 加速時:

用法推薦
Chat / brainstorm(短回覆,低 latency 期望)n=1(huihui 跟 vanilla 都 ~52 tok/s)
長產生 / 寫作(throughput 重要)vanilla n=4 = 108(快兩倍),但失去 abliteration
Abliterated + 不在乎 deep spechuihui FP8 n=1,52 tok/s,自由

繁中重的 task(memory 紀錄 TMMLU+ paired Qwen 3.6 75% 對 Gemma 4 46% 差 29pp)還是留 Qwen 3.6 主力,Gemma 4 abliterated 適合英文 brainstorm + image gen prompt 寫作。

接下來:為 spec decode acceptance 優化的 abliteration

這次反而看到一條值得繼續試的路:abliteration 流派都在壓「refusal rate」,幾乎沒人在調「MTP acceptance」。huihui 的 abliteration 太重(自承「crude POC」),per-position decay 陡;p-e-w/Heretic + ARA 流派(llmfan46 用的)較輕但仍未針對 spec decode 調過。

下一步嘗試:用 Heretic + Optuna,target KLD ~0.025(比 llmfan46 的 0.0468 還低一半)、conservative hparams。實際開跑後從 trial 參數看到 Heretic 的 ARA 預設會同時動 attn.o_projmlp.down_proj(我原本以為只動 attn.o_proj,trial output 打臉了),這個 footprint 對 MoE 架構的 expert path 影響待實測才知道。

這一段全是 hypothesis 不是承諾。我能合理猜的只有 baseline 不會壞(本篇已實測 abliteration 不影響 body 速度)。pos-0 acceptance / decay 斜率 / n=4 throughput 有沒有改善都要實測才知道,結果可能是改善、可能是沒改善、可能是反而更糟(Heretic ARA 流派沒人在「最大化 MTP acceptance」這個 objective 上跑過 Optuna)。

如果真的能把 decay 拉緩到讓 n=4 翻盤,會發一個小眾版 + 補 Part B;沒效就直接寫「這條路試過不行」 — 失敗也有資訊價值,把它寫出來才不浪費。

用法

pip install vllm    # 注意:Gemma 4 MTP 要 PR #41745 之後才有,可能需要從 main build 或用 preview image

# 推薦:huihui FP8 + vanilla MTP n=1(+34% over no spec)
vllm serve coolthor/Huihui-gemma-4-26B-A4B-it-abliterated-FP8-Dynamic \
  --speculative-config '{"method":"mtp","model":"google/gemma-4-26B-A4B-it-assistant","num_speculative_tokens":1}' \
  --kv-cache-dtype fp8 \
  --gpu-memory-utilization 0.65 \
  --max-model-len 8192 \
  --limit-mm-per-prompt '{"image":0,"audio":0,"video":0}' \
  --enable-auto-tool-choice --tool-call-parser gemma4 \
  --trust-remote-code

MTP 注意:Gemma 4 MTP 整合在 vLLM PR #41745(2026-05-06 merged)之後才完整。我自己用 eugr/spark-vllm-dockervllm/vllm-openai:gemma4-0505-arm64-cu130 preview image,然後 bind-mount PR head 的 gemma4_mtp.py 進 container 蓋掉 image 內舊版本(這個 mod 是我 local 自己加在 mods/ 底下的,還沒 push 上 upstream)。踩過的坑:用 docker exec 跳過 recipe runner 會 silently 失去 mod,結果 spec decode acceptance 跑出 0% —— container 啟動後 md5sum gemma4_mtp.py 對照 PR head 是穩當的 sanity check

相關

常見問題

在 GB10 上跑 huihui Gemma 4 abliterated FP8 會不會被收速度稅?
看 `num_speculative_tokens`。同 stack 同 config,abliterated body 的 baseline(no spec)39.3 tok/s,vanilla 量到 39.4 — 兩家在 noise 範圍內一樣。MTP n=1 也一樣:vanilla 51.9、huihui 52.6。差距只在 deep speculation 上開:n=4 大 KV config,vanilla 拉到 108(Part 27),huihui 卡 50,等於慢 54%。代價在 spec depth 不在 body。
為什麼 abliterated 在 deep spec 上吃不到加速?
Per-position acceptance decay。從 vLLM `/metrics` 直讀,huihui n=4 每 step 衰減 22pp:pos 0 65.6%、pos 1 43.3%、pos 2 29.2%、pos 3 20.5%。複合下來把整體 speedup 砍半。MTP draft 模型 `google/gemma-4-26B-A4B-it-assistant` 是 train 在 vanilla body 的 distribution 上的;body 一被 abliteration 偏掉,drafter 自己用前一格 prediction 推下一格就越推越歪。Vanilla per-position 我沒實測,所以不做配對比較 — 前一稿亂編一組數字被打過。
這是 huihui abliterated 第一個 vLLM-loadable FP8 嗎?
2026-05-09 我在 HF 上沒找到 `huihui-ai/Huihui-gemma-4-26B-A4B-it-abliterated` 的其他 FP8 衍生。GGUF(多家)、MLX、NVFP4、Q8_0 都已有 publisher,FP8-Dynamic 是缺口我順手補。寫「第一個」會比我能證的強,所以就不寫。
production 用 huihui FP8 推薦怎麼跑?
要 spec decode 就跑 `num_speculative_tokens=1`。和 vanilla MTP n=1 一樣 ~52 tok/s、69% pos-0 acceptance、abliteration 沒收稅。**不要往 n=2/3/4 加** — 這個 body 越深越慢。如果不在乎 spec decode,baseline 39 tok/s 兩邊一樣。繁中重的工作流還是建議留 Qwen 3.6 abliterated FP8 當主力(TMMLU+ 75% vs Gemma 4 46%)。