Skip to content

Generators: FP8 emission + expert unfusing + download tool#244

Closed
woolcoxm wants to merge 3 commits into
JustVugg:devfrom
woolcoxm:feat/generators-fp8-unfuse
Closed

Generators: FP8 emission + expert unfusing + download tool#244
woolcoxm wants to merge 3 commits into
JustVugg:devfrom
woolcoxm:feat/generators-fp8-unfuse

Conversation

@woolcoxm

Copy link
Copy Markdown
Contributor

Summary

Three changes to the test-model generators:

  1. FP8 e4m3 emission β€” generators can now write weights as FP8 + 128Γ—128 block scales in the exact layout of the real GLM-5.2-FP8 checkpoint, so the converter's FP8β†’int4 dequant path can be tested locally without the 379 GB download.
  2. Expert weight unfusing β€” splits HF's fused 3-D gate_up_proj into the per-expert 2-D layout the real checkpoint, converter, and C engine all expect. Fixes a silent data-loss bug.
  3. FP8 download tool β€” download_fp8.py with ModelScope+HF dual-source, parallel shards, resume.

Closes #238, closes #239.

Problem 1: No local FP8 fixture

The converter's FP8β†’int4 dequant path could only be exercised by downloading the 379 GB checkpoint. There was no way to test it locally, meaning dequant bugs would only surface during the multi-hour production conversion.

Fix: New glm_fp8_emit.py helper with fp8_block_quantize / fp8_block_dequantize / save_fp8_safetensors. Both generators gained --fp8 flag. The oracle round-trips weights through FP8 before computing the reference.

Problem 2: Fused expert weights (bug)

HF's GlmMoeDsaForCausalLM fuses gate_proj + up_proj into a 3-D gate_up_proj [E, 2M, I]. But the real checkpoint stores experts unfused as per-expert 2-D tensors:

model.layers.10.mlp.experts.0.gate_proj.weight       (2048, 6144)  FP8 e4m3
model.layers.10.mlp.experts.0.gate_proj.weight_scale_inv  (16, 48)  F32
model.layers.10.mlp.experts.0.up_proj.weight          (2048, 6144)  FP8 e4m3
...

The converter's ndim != 2 guard silently skipped the 3-D fused tensors. The engine then crashed with missing tensor: model.layers.3.mlp.experts.5.gate_proj.weight.

Fix: unfuse_experts() in glm_fp8_emit.py splits gate_up_proj [E, 2M, I] β†’ per-expert gate_proj [M, I] + up_proj [M, I], and down_proj [E, I, M] β†’ per-expert down_proj. Called after reference generation but before saving, in both generators.

Also fixed: make_glm_oracle.py FP8 round-trip guard p.dim() < 2 β†’ p.dim() != 2 (3-D fused experts crashed fp8_block_quantize).

Problem 3: Download tool

The 379 GB FP8 download is the pipeline bottleneck. HuggingFace per-stream throttling drops throughput after ~50 GB. download_fp8.py tries ModelScope first (no throttling), falls back to HF, with parallel shards + resume.

Changes

File Change
c/tools/glm_fp8_emit.py NEW: FP8 block quantize/dequantize, unfuse_experts, save_fp8_safetensors
c/tools/make_glm_oracle.py --fp8 flag, FP8 round-trip, unfuse, dim guard fix
c/tools/make_glm_bench_model.py --fp8 flag, unfuse, manual config.json in FP8 path
c/download_fp8.py NEW: dual-source parallel download with resume

Validation

generator --fp8 β†’ 570 e4m3 tensors + 629 scale_inv (was 90 when fused)
converter --group-size 0   β†’ per-row int4 (fmt=2), engine loads clean
converter --group-size 128 β†’ grouped int4 (fmt=4), engine loads clean
dequant error: grouped 1.14-1.22x lower than per-row

Test plan

  • Generator --fp8 produces valid FP8 checkpoint with per-expert 2-D tensors
  • Converter ingests FP8 fixture, produces int4 output
  • Engine loads converted model with 0 missing-tensor errors
  • FP8 round-trip: dequant matches quant (maxdiff 0.0)
  • Download tool resumes (79/141 β†’ in progress)

woolcoxm added 3 commits July 15, 2026 02:27
Both test-model generators (make_glm_oracle.py, make_glm_bench_model.py) can now
emit weights as FP8 e4m3 + 128x128 block scale_inv, in the same layout as the real
GLM-5.2-FP8 checkpoint. This lets convert_fp8_to_int4.py exercise its FP8->int4
dequant path on a local fixture without the 379 GB download.

- New shared helper glm_fp8_emit.py: FP8 block quantize/dequantize (FBGEMM/TE
  scale=amax/448 convention) + state_dict emitter. Only exactly-2-D tensors are
  quantized; 1-D/3-D and norms/router/e_score_correction_bias are kept as f32,
  mirroring the converter's classify() + ndim!=2 guard.
- make_glm_bench_model.py: opt-in --fp8 writes model.safetensors in FP8 layout
  (config.json written explicitly since the FP8 path bypasses save_pretrained);
  manifest gains a 'format' field. Default bf16 behavior unchanged.
- make_glm_oracle.py: opt-in --fp8 round-trips quantizable weights through FP8
  before computing ref_glm.json, so the reference reflects exactly the FP8 model
  the converter ingests. Default bf16 oracle contract unchanged.

Verified end-to-end: FP8 model -> converter --indir -> int4 U8 + .qs F32 output,
bit-identical dequant between helper and converter (maxdiff 0.0).
… download tool

Consolidates all experiment branches into one Windows-dev branch:

1. Group-scaled int4 (fmt=4, gs=128) β€” glm.c
   - QT struct: added gs field
   - matmul_i4_grouped: AVX2 kernel, verified to 3e-08 vs f32
   - Format detection: auto-detects from .qs scale array size
   - expert_load: both mmap and slab+pread paths handle fmt=4
   - qt_bytes: fmt=4 case added

2. Per-tensor-type mixed precision β€” convert_fp8_to_int4.py
   - Split classify() into sh/o/kvb/attn/dmlp sub-types
   - New args: --shared-bits, --o-bits, --kvb-bits, --attn-bits, --dmlp-bits
   - Plan: shared expert + o_proj + kv_b_proj at int8, rest grouped int4
   - Only +5.3 GB RAM vs +0 for pure int4

3. EXPERT_BUDGET (miss-aware) β€” glm.c
   - Caps distinct experts per layer across batch-union
   - Always keeps cache hits, only drops misses
   - Up to 1.8x faster decode on low-RAM hosts

4. Two-step shared-expert prediction (PILOT_TWO) β€” glm.c
   - la_predict kind==2 + pilot_prefetch integration
   - +3.1% recall over baseline PILOT

5. FP8 download tool β€” download_fp8.py
   - ModelScope + HuggingFace dual-source
   - Parallel shard download with stall recovery

6. Tiny model generation β€” make_glm_oracle.py
   - Generated and tested locally for pipeline validation
@JustVugg

Copy link
Copy Markdown
Owner

Closing β€” like #243, this is already on dev, arrived via your own earlier commits.

I checked each file rather than guessing:

Zero unique content left. The --fp8 path, the ref-after-round-trip design (computing ref_glm.json after the FP8 round-trip so the reference matches what the converter ingests β€” that's a careful piece of thinking, and exactly the trap #281 fell into from the other direction) and unfuse_experts are all live on dev.

Also closing #238 (FP8 emission + unfusing) and #239 (dual-source download tool) β€” I verified download_fp8.py has ModelScope + HF, resume, and parallel workers, so both are delivered.

Nothing lost here except the bookkeeping.

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