Repository navigation
feat(gguf): serve gpt-oss gguf checkpoints on gpt_oss - #838
Conversation
jason-fxz
left a comment
There was a problem hiding this comment.
Thanks for adding gpt-oss GGUF. Four things to fix before merge:
-
YaRN
truncate(models/gpt_oss/gguf.py): the HF gpt-oss config sets"truncate": false, and transformers and FreeToken's HF gpt-oss path both honor it. The GGUFrope_scalingleaves it out, so GGUF files get the truncated correction range (8..18 instead of 8.09..17.40). Frequency dims 9-17 then differ from the HF path, and the two rope tables are already up to 0.23 apart at position 1024. llama.cpp always truncates, so a parity check against it does not catch this. Please add"truncate": Falseas a family constant next to alpha / limit. -
bf16 cast of float experts (
models/gguf/experts.py,_bank_spec): the cast also hits mixed float/quant layers in every family, which main keeps in their own dtype since #702. HauhauCS Qwen3.6-35B Q8_K_P (13 layers of F16 gate_up + Q8_0 down) serves on main with--moe-strategy hybrid. With this PR it fails at startup withValueError: MoE layer 20 gate_up bank (256, 1024, 2048) torch.bfloat16 is not F16 rows of 2048 values, becausecpu_specstill sends those layers to theggufCPU format as F16. On GPU their F16 GEMM also drops to bf16. Please cast only when both gate_up and down are float (the layer runs the bf16 fused MoE) and keep mixed layers in their own dtype. If_expert_rowsstays a view andbanks[...].copy_(r)does the conversion,--load-format dummyalso stops reading the expert data (three F16 layers now peak at +7.9 GiB RSS, vs +3.9 GiB on main). -
NVFP4 down scale vs. down bias (
layers/quantization/moe/gguf.py, non-MMVQ down branch): abovemmvq_max_tokens,weight_scale_2is folded intotopk_weightsand the bias is then added to the unscaledy, so the bias is scaled too:sum w*s*(y + b)instead ofsum w*(s*y + b). No public gpt-oss file has NVFP4 experts yet, but with synthetic NVFP4 experts throughGgmlMoEKernel.apply, prefill (T=256) is 94% off the reference while decode (T=4) is within 0.4%. With a bias, scaleyin place instead; prefill then lands within 0.4% too:if down_scales is not None and down_bias is not None: # the bias adds after weight_scale_2, so the scale cannot fold into the routing weights y.mul_(down_scales[ids, 2].unsqueeze(-1)) elif down_scales is not None: topk_weights = topk_weights * down_scales[ids, 2]
-
w2_biaswithapply_router_weight_on_input(moe/fused.py, both impls): the guard checks onlyw1_bias, butw2_biasis always multiplied by the routing weight. In that mode both impls are 66% off your test reference, which adds it unweighted. Please make the assert cover both:# both bias adds assume the router weight scales the expert output assert (w1_bias is None and w2_bias is None) or not apply_router_weight_on_input, "per-expert biases need the router weight on the output"
Smaller cleanups:
OffloadMoeCache.set_bank_sourceswidens the gguf schema at runtime when bias roles show up. The loader already knows the file is biased when it names the format (experts.py,ExpertBanks("gguf_scaled" if scaled else "gguf", ...)), so registering the biased layouts in_BANK_SCHEMASkeeps it the single place a format's bank layout is declared._LAYER_LEAVES:mlp.gate_proj/up_proj/down_projandmlp.gatematch no gpt-oss module (no dense MLP; the router ismlp.router).- The
pattern is Nonebranch repeats the scalar branch with p = 2;md.get(key, 2)covers it.
Not for this PR: fusing the expert biases into the kernels (the [T, k, 2I] gathers on prefill, the MMVQ glu path on decode) and bias support in the CPU executor. We will follow up on those separately.
…ale nvfp4 before bias
…le nvfp4 before the down bias
5699121 to
15b41ed
Compare
|
All four and the cleanups are in 15b41ed, rebased onto main
Cleanups: biased formats registered in Tests: 113 passed (the PR's four files + |
|
LGTM. |
Upstream merged FlashML-org#844 (MTP drafts) and FlashML-org#838 (gpt-oss GGUF). next already carries FlashML-org#844 with its fixups; upstream's squash matches the PR head we merged, so every conflict keeps next's adapted side. FlashML-org#838 comes in as is, with its one docs/cli.md line added to next's --moe-cpu-layers row. Assisted-by: Claude Opus 5.5
Serves llama.cpp
gpt-ossGGUFs (gpt-oss-20b, gpt-oss-120b) on the family's own model classes. Part of #671 (Model Support:gpt-oss). Rebased onto current main (e328cf6, clean).What it does
gpt_oss/gguf.py):parse_gguf_configfrom the GGUF KVs - yarn rope (factor 32, original 4096; the betas default to 32/1, which llama.cpp hardcodes for gpt-oss - its loader never reads the keys - when a file omits them; the unsloth UD and F16 releases omit them),attention.sliding_window, and the alternatingsliding_attention/full_attentionlayout from layer 0 (every release'slayer_types).attention.sliding_window.patternis read the way llama.cpp'sload_swa_patterndoes: the per-layer array verbatim, else the scalar'sil % p < p - 1, default 2 - no release writes the key, and a wrong-length array refuses. The swiglu's alpha 1.702 / limit 7 are family constants the GGUF does not store, as in llama.cpp. Attention sinks, router bias and q/k/v/o biases load with the dense state dict; the packed embedding takes the GGUF quant (model.pynow passesquant_configthrough, as the qwen3 model does). A GGUF missing any bias tensor llama.cpp requires (openai-moe.cppcreatesattn_output.bias,ffn_gate_inp.bias, the threeffn_*_exps.biasand a qkv bias as required tensors) refuses at config parse naming the file and tensors, instead of serving silently bias-free.swigluoai(the uninterleaved spelling ofgpt_oss_swiglu, same math; the ggmlswiglu_oaiepilogue and the cpu executor already know it) withinterleaved=False.ffn_{gate,up,down}_exps.bias). They ride the expert banks as two roles (gate_up_bias[E, 2I],down_bias[E, H] bf16), so each bias follows its expert through the slot cache on decode (where routing ids become slot ids) and the expert-ordered prefill/resident views. The ggml kernel adds gate/up before the gated activation and down before the routing weight, as llama.cpp'sbuild_moe_ffndoes; the NVFP4weight_scale_2multiply comes before the bias, as there too (no public gpt-oss file reaches that combination).bank_schemawidens for the two roles on the gguf formats. A file carrying the biases on only some layers refuses at bank load.cpu/hybriddecode refuses bias files (no CPU executor format), auto resolves to offload.cpu_specsends every float type to the bf16 executor format.Verification (RTX 5090 32 GB, driver 580.95.05, Ryzen 9 7950X3D, 187 GiB RAM)
UD-Q4_K_XL, ggml-orgMXFP4, unslothF16- 3/3 pass. tok_compare vsopenai/gpt-oss-20btokenizer.json: 0 differing texts (edges, full vocab, 20k combos) on the UD and MXFP4 files./v1/chat/completions: correct math (17*23+45/9 = 396 with steps) and SQL on each; harmony reasoning parses intoreasoning_content; 12 concurrent requests all complete; a repeated prompt hits the radix through the sliding/full mix (0 -> 268 cached; the SWA pool tracks both groups). UD-Q4_K_XL: prefill ~19.2k tok/s (11.8k prompt), decode 145 tok/s, experts offloaded (9.5 GiB host banks).kernel/triton/activation.py- 0.008 max rel err with the biases, 0.26 without (pinned by a test now).fetch_headers --search gpt-oss, 1,093 stubs / 128 repos, kernels probed on the 5090): 969gpt-osstrunks, 948 pass. Failures: 20 base/abliterated files with no chat template, 1 broken upload (notoken_embd). Nothing crashes and nothing mis-loads silently.tests/moe/test_gpt_oss.py,tests/moe/test_fused_moe.py,tests/models/test_models_registry.py,tests/models/test_models_loader.py- 60 passed, including a new per-expert-bias reference check over both fused impls (test_fused_moe.pyextends its existing reference helper with the swigluoai formula and bias params);tests/moe/test_offload.py(the rebased main's seams) passes too. No per-family GGUF test file, per the feat(gguf): serve GLM-5.3-Flash GGUF checkpoints on glm5-next #721 review direction.Not covered
gpt-oss-*-VISION*repos: mainlinegpt-osstext plus aninternvlprojector mmproj (clip.projector_type=internvl, 436v.*tensors) - a community graft no FreeToken tower implements; the text files of those repos serve.cpu/hybrid/--moe-cpu-layersfor gpt-oss files: the per-expert biases have no CPU executor format, refused at kernel selection.