Skip to content

fix(qwen4_exp): fix nvfp4 ftw weight loading, qsa indexer, and ple tables - #293

Draft
3Simplex wants to merge 1 commit into
FlashML-org:mainfrom
3Simplex:fix/qwen4-exp-nvfp4-ftw-support
Draft

fix(qwen4_exp): fix nvfp4 ftw weight loading, qsa indexer, and ple tables#293
3Simplex wants to merge 1 commit into
FlashML-org:mainfrom
3Simplex:fix/qwen4-exp-nvfp4-ftw-support

Conversation

@3Simplex

Copy link
Copy Markdown

I wanted to post this as a reference of the hacks needed to run oakmindai/Qwen3.8-Flash-Next-NVFP4-FTW which were generated by Gemini 3.7 flash.

Summary

Fixes weight loading, tensor layout mismatches, and host table initialization when running Qwen3.8-Flash-Next-NVFP4-FTW (and Qwen4Exp hybrid models) from FTW checkpoints.

Key Changes

  1. python/freetoken/models/qwen4_exp/gdn.py:

    • Initialized out_proj as LinearReplicated to match unquantized BF16 weights in NVFP4 checkpoints where linear attention is excluded from quantization (quantization_config['ignore']).
  2. python/freetoken/models/qwen4_exp/attention.py:

    • Added flexible load_state_dict() in Qwen4ExpIndexer to resolve checkpoints storing indexer weights flat under self_attn.index_* (e.g. index_qk_proj.weight, index_q_norm.weight) in addition to nested self_attn.indexer.*.
  3. python/freetoken/engine/engine.py:

    • Added dynamic fusion in _load_weight_state_dict to concatenate and 16-row pad separate input_mix_weight_down.weight and block_inject_weight.weight tensors into input_mix_weight_down_block_inject.weight when loading FTW checkpoints.
  4. python/freetoken/models/qwen4_exp/ple.py & model.py:

    • Added load_state_dict() override to NGramEmbedding so derived hashing metadata is not strictly expected in dense checkpoint state dicts.
    • Replaced .copy_() onto meta tensors with direct CUDA int64 tensor initialization using torch.cuda.current_device().
    • Added safe runtime device migration check in NGramEmbedding.row_ids().
  5. python/freetoken/models/qwen4_exp/weight.py:

    • Added direct binary loader support in load_ple_table() for unified qwen4_ngram.bin + qwen4_ngram.json files.
    • Added FREETOKEN_ZERO_PLE=1 environment variable support allowing systems without 100+ GB of host RAM to attach ZeroTable.

Test Environment

  • GPU: NVIDIA GeForce RTX 5060 Ti (16 GB VRAM)
  • CPU: 16 physical cores / 16 threads (AVX-512 VNNI / AVX512BF16)
  • System RAM: 128 GiB
  • OS: NixOS / Linux

Hardware Bandwidth Benchmarks (ft bench bw --dtype nvfp4)

  host nixos-clayton   gpu cuda:0 (NVIDIA GeForce RTX 5060 Ti)   cpu 16c/16t
  ceilings: CPU STREAM read 41.3  |  PCIe linear H2D 28.0  D2H 28.3  GB/s   (threshold 2.0x)

  per-dtype (tuning — what the runtime backend pick matches on)
    format      expert       CPU-MoE   PCIe-gather  CPU/PCIe  backend
    nvfp4      7.61 MB     32.0 GB/s     26.2 GB/s     1.22x  offload
       overlapped: CPU-MoE 28.7 + PCIe 15.5 GB/s -> hybrid fetches 35.0% of misses

Generation Benchmark (ft serve / OpenAI API)

  • Model: oakmindai/Qwen3.8-Flash-Next-NVFP4-FTW
  • Backend: qsa_sparse attention + offload MoE
  • Server Flags: FREETOKEN_ZERO_PLE=1 ft serve --max-running-req 1 --cuda-graph-max-bs 1
=== Generation Benchmark Results ===
- Tokens Generated: 127
- Time to First Token (TTFT): 9.76 ms
- Total Time: 9.65 s
- Decode Speed: 13.17 tokens/s

Verification Checklist

  • Tested regression on KAT-Coder-V2.5-Dev-FTW (clean graph capture and generation).
  • Tested full end-to-end inference on oakmindai/Qwen3.8-Flash-Next-NVFP4-FTW.
  • Verified on real hardware with both interactive ft shell and API ft serve.

Environment Details (Auto-detected)


  • OS: NixOS 26.11 (Zokor) (Linux 7.2.0)
  • CPU: AMD Ryzen 9 9950X 16-Core Processor
  • System RAM: 128 GiB & 78GiB used to serve model
  • GPU 0: NVIDIA GeForce RTX 5060 Ti (16311 MiB VRAM)
  • NVIDIA Driver: 595.91.07
  • CUDA Runtime: 13.0
  • PyTorch: 2.11.0+cu130
  • Python: 3.12.14

@benwilson

Copy link
Copy Markdown

Data point from a different checkpoint and a card nobody has reported yet, in case it helps scope which of these hacks are FTW-specific: the standard (non-FTW) NVFP4 checkpoint loads and serves at 4b94bdc with none of these changes, on an RTX 3090.

Environment: FreeToken git 4b94bdc (HEAD 2026-08-30, no local patches), installed with pip install --no-build-isolation "freetoken[accel] @ git+…@4b94bdc"; torch 2.11.0+cu130, CUDA toolkit 13.0, driver 595.71.05; RTX 3090 24 GB (sm_86) on a rented vast.ai host (503 GiB RAM); image pytorch/pytorch:2.11.0-cuda13.0-cudnn9-devel; checkpoint dealignai/Qwen3.8-Flash-Next-ABLITERATED-NVFP4 @ be794b99 (standard compressed-tensors NVFP4 layout, byte-identical quant config to the RadixArk stock checkpoint — not the FTW repack).

ft serve --max-seq-len-override 262144 --kv-reserve-tokens 262144 --moe-backend offload --moe-cache-auto --nvfp4-backend triton --max-running-requests 1 --max-prefill-length 8192 --sampling-defaults model --reasoning-parser qwen3 --tool-call-parser qwen3_coder

  • weights 206/206 in 7 s; PLE table 47.7 GB → pinned host RAM at ~540 MB/s; experts 63.3 GB at ~1 GB/s; --moe-cache-auto resolved moe_cache_size=1677 num_pages=4097; KV 262,208 tokens = 6.19 GiB; 23.28 GiB free before load, 2.01 GiB after CUDA-graph capture (bs=1, 100.8 s cold, 2 s on a warm JIT cache)
  • worker RSS at ready: 113 GiB (so FREETOKEN_ZERO_PLE from this PR is the only path for <128 GiB hosts)
  • needle-in-haystack found at 38,587 / 146,337 / 192,587 prompt tokens, zero warnings
  • prefill in 8192-token chunks: 1,000–1,290 tok/s steady (380–480 on the cold first chunk); 192.6k tokens in 2 min 52 s
  • decode 23–25 tok/s, flat from 38k to 192k context (engine counters); a 55k-token Claude Code turn: cold 56.6 s, warm 7.1 s from the radix cache

So on sm_86 with the Triton NVFP4 path the loader, indexer and PLE init are fine for the compressed-tensors layout; the FTW checkpoint's flat self_attn.index_* keys and split input_mix_weight_down / block_inject tensors look like the actual differentiator. Happy to run the FTW repack on the same box if a direct A/B would help.

@benwilson benwilson mentioned this pull request Aug 31, 2026
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants