YAML Metadata Warning:empty or missing yaml metadata in repo card

Check out the documentation for more information.

HunyuanImage-3.0-Instruct-Distil Β· MXFP4 mixed-precision (auto-round / vLLM INC path)

This is the --format auto_round the MXFP4-for-experts / MXFP8-for-everything-else recipe, exporting quant_method = "auto-round" so vLLM loads it through the INC dispatch instead of the compressed-tensors one. AR-only, DiT-only and AR+DiT were all run successfully on vLLM 0.29.0 + vLLM-Omni main.

AR+DiT full-pipeline output

Generated output, not a ground-truth reference: AR+DiT on 2 GPUs (AR TP1 + DiT TP1), seed=42, 8 steps, prompt A cute cat.


⚠️ Read this first

  1. On Hopper (SM90: H100/H200) this is a memory saving, not a speed saving. There are no FP4 tensor cores, so the experts run through Marlin as weight-only FP4 (effectively W4A16):

    Using MarlinExperts (weight-only FP4) for AutoRound MXFP4 MoE
    Using MarlinMxfp8LinearKernel for MXFP8 GEMM
    

    Blackwell (b12x / CUTLASS MXFP4) paths exist in vLLM but are untested for this build.


Overview

Field Value
Base model tencent/HunyuanImage-3.0-Instruct-Distil (cfg_distilled=true, use_meanflow=true)
MoE geometry 32 layers Γ— 64 routed experts, moe_topk=8, 1 shared expert/layer, hidden 4096, moe_intermediate=3072
Scheme Mixed: routed experts β†’ MXFP4 (E2M1, group 32, E8M0); all other quantized Linear β†’ MXFP8 (E4M3, group 32, E8M0)
Export format --format auto_round β†’ quant_method = "auto-round", packing_format = "auto_round:llm_compressor"
Loader path vLLM INC (INCConfig.override_quantization_method() maps "auto-round" β†’ "inc")
Quantization tool auto-round 0.15.0, --model_free (no calibration data); wall time 707 s on 1 GPU
Disk size 52.59 GB (48.98 GiB) β€” MXFP8-ct build 91.25 GB, BF16 base 158 GB β‡’ 0.33Γ— BF16
Parameters total 83.04 B: routed experts 77.31 B (93.1 %) @4-bit, self_attn 1.49 B + shared_mlp 1.21 B @8-bit, 3.03 B BF16/FP32
Expert tensor layout …experts.N.{gate_and_up_proj,down_proj}.weight_packed = uint8 [N, K/2] (two E2M1 per byte, low nibble = even column) + weight_scale = uint8 [N, K/32] (E8M0: value = 2^(byteβˆ’127))
Kept in BF16 / FP32 ViT (vision_model, BF16), VAE (FP32, as in the base model), wte/lm_head, guidance_emb, timestep_emb, timestep_r_emb, patch_embed, final_layer, time_embed*, vision_aligner, and the MoE router of all 32 blocks (…mlp.gate.wg)

quantization_config from config.json (verbatim, minus the long extra_config):

{
  "quant_method": "auto-round",
  "packing_format": "auto_round:llm_compressor",
  "bits": 8, "group_size": 32, "sym": true, "data_type": "mx_fp",
  "act_bits": 8, "act_data_type": "mx_fp", "act_dynamic": true, "act_group_size": 32, "act_sym": true,
  "model_free": true, "iters": 0, "enable_quanted_input": false,
  "autoround_version": "0.15.0",
  "block_name_to_quantize": "model.layers",
  "extra_config": {
    ".*mlp\\.experts": { "bits": 4 },                       // ← the fix; stock export has ".mlp.experts."
    "final_layer.model.0.emb_layers.1": { "bits": 16, "data_type": "float", "act_bits": 16, "act_data_type": "float" },
    "... 220 more entries, all bits:16 overrides ..."
  }
}

Note there is no ignore list in this format (unlike the compressed-tensors sibling): the 16-bit exceptions are expressed as extra_config entries with bits: 16, and the non-Linear modules that never quantize anyway (VAE convolutions, ViT patch/position embeddings) simply are not listed.

act_* fields describe the declared scheme. What actually runs on SM90: dynamic MXFP8 activation quantization for dense Linear, but no activation quantization for the experts (Marlin W4A16).


Quantization command

auto-round \
  --model_name tencent/HunyuanImage-3.0-Instruct-Distil \
  --model_free \
  --scheme MXFP8 \
  --layer_config '{.mlp.experts.:{bits:4,data_type:mx_fp}}' \
  --ignore_layers "vision,guidance_emb,timestep_emb,timestep_r_emb,final_layer,wte" \
  --format auto_round \
  --device cuda:0 \
  --output_dir ./HunyuanImage-3.0-Instruct-Distil-MXFP4-AutoRound
  • Do not add a bare gate to --ignore_layers: it expands to .*gate.* and silently swallows the fused expert projections gate_and_up_proj (4096 tensors).
  • The MoE router needs no entry: AutoRound hard-skips anything matching .gate..
  • vision must stay ignored: ViT mlp.fc2 input dim 4304 is not divisible by the 32-element MX block.
  • The run log echoes Scheme: … bits=8 / Packing format: mxfp8-quantized β€” that is the default scheme only, not evidence that --layer_config was ignored. Check the artifact instead (weight_packed count must be 4096).

Inference environment

Component Version
vLLM 0.29.0
vLLM-Omni latest main (validated at 1c7476ec, 0.29.0rc2.dev161+g1c7476ec1, editable)
PyTorch 2.13.0+cu132
FlashInfer 0.6.18
GPU NVIDIA H200 141 GB β€” AR-only 1Γ—, DiT-only 2Γ—, AR+DiT 2Γ—
pip install vllm==0.29.0 && pip install -e /path/to/vllm-omni
python -c "import vllm, vllm_omni, os; print(vllm.__version__, os.path.dirname(vllm_omni.__file__))"

# host-specific, drop if unneeded here
export NCCL_NVLS_ENABLE=0
export VLLM_USE_FLASHINFER_SAMPLER=0
cd /tmp            # cwd must be neutral: children re-import vllm_omni from cwd

Validated run modes (2 GPUs, cards 0 and 1)

Mode deploy YAML Result Evidence from logs
AR-only hunyuan_image3_ar_tp1.yaml (TP1) βœ… Model loading took 46.69 GiB; MXFP4 MoE dispatch line above; text It's a warm and healing style (matches the -mixed and MXFP8 builds verbatim)
DiT-only hunyuan_image3_dit_tp2.yaml (TP2) βœ… image Model loading took 25.3899 GiB/card; Saved generated image … (std 49.34)
AR+DiT hunyuan_image_3_moe_2gpu_tp1.yaml (TP1+TP1) βœ… image both stages loaded (46.69 + 46.63 GiB); Saved generated image … (std 46.44)

Before the metadata fix, all three failed with the KeyError quoted at the top.

export CUDA_VISIBLE_DEVICES=0,1
cd /tmp
python /path/to/vllm-omni/examples/offline_inference/text_to_image/text_to_image.py \
  --model               /path/to/HunyuanImage-3.0-Instruct-Distil-MXFP4-ar \
  --deploy-config       ./hunyuan_image_3_moe_2gpu_tp1.yaml \
  --prompt              "A cute cat" \
  --num-inference-steps 8 \
  --guidance-scale      5.0 \
  --seed                42 \
  --output              ./output.png

The two-stage YAML (stage 0 AR with omni_kv_config.need_send_cache, stage 1 DiT with need_recv_cache, joined by a SharedMemoryConnector) is reproduced in the sibling build's card; devices are local indices, combine with CUDA_VISIBLE_DEVICES.

Parameter notes β€” --num-inference-steps 8 (distilled: do not use 50); --guidance-scale is a real input (cfg_distilled=true, vLLM-Omni feeds 1000 Γ— guidance_scale as the guidance embedding; the image above used 5.0, vLLM-Omni's Distil e2e reference uses 2.5); --prompt reaches only the AR stage.


Fidelity

Weight side β€” identical to the sibling build by construction

The tensors are byte-identical, so the dequantization error is the same:

Module scheme rel. L2 vs BF16 β‰ˆ effective bits
routed experts gate_and_up_proj / down_proj MXFP4 0.1120 – 0.1124 3.65
self_attn.*, shared_mlp.* MXFP8 0.0267 5.73
router / ViT / VAE / embed (unquantized) BF16/FP32 0.0000 β€”

MXFP4 experts carry 4.21Γ— the relative error of the MXFP8 group (17.7Γ— in noise power) on 93 % of the parameters, and this equals the ideal per-group E8M0 search (optimized RTN) to four decimals β€” i.e. it is the format's own floor, not a tuning miss.

Image side (DiT-only TP2 β€” topology matched, noise floor 41.4 dB)

Candidate vs BF16 PSNR SSIM@1 @1/2 @1/4 @1/8 std
MXFP8-ct (for reference) 31.59 0.979 0.976 0.978 0.982 53.62
this build (INC path) 12.91 0.6698 0.5417 0.3910 0.2611 49.34
sibling -mixed (CT path) 12.99 0.6720 0.5449 0.3963 0.2707 48.86
this build vs -mixed 33.03 0.9799 0.9772 0.9758 0.9822 (flat) β€”
BF16 base (DiT TP2) this build (DiT TP2) sibling -mixed (DiT TP2)
bf16 ar mix
  • SSIM falls with coarser scale vs BF16 β‡’ composition-level divergence, not a texture/style shift. The image is a valid, un-degraded different picture of a cute cat.
  • This build vs the sibling is flat across scales β‡’ same composition, small texture-level numeric differences from the different kernel entry points.

Side-by-side montages

Figure A β€” DiT-only, TP2 for all four builds (topology matched β‡’ the readable axis). β‘’ and β‘£ additionally share the loader format (auto-round/INC), so ⑒↔④ isolates the experts bit width as the only variable. Each cell carries its topology and PSNR / SSIM@1 / SSIM@1/8 vs BF16 (SSIM on 1/8-scale images keeps only low-frequency structure β‡’ "same composition or not").

DiT-only: BF16 / MXFP8-ct / MXFP8-ar / this build

comparison what differs PSNR SSIM@1 SSIM@1/8
β‘‘ ↔ β‘’ (MXFP8-ct vs MXFP8-ar) only the export format 44.46 0.9965 0.9984
β‘’ ↔ β‘£ (MXFP8-ar vs this build) only experts 8β†’4 bit 12.71 0.6623 0.2518
β‘‘ ↔ -MXFP4-mixed (CT side, for cross-check) only experts 8β†’4 bit 12.81 0.6643 0.2617
β‘  ↔ β‘‘ vs BF16 MXFP8 31.59 0.9788 0.9823
β‘  ↔ β‘’ vs BF16 MXFP8 (INC) 31.79 0.9811 0.9842
β‘  ↔ β‘£ vs BF16 MXFP4 (INC) 12.91 0.6698 0.2611

Noise floor: re-running the same build with the same command gives 41.4 dB / SSIM 0.995.

β‡’ Three things worth reading off this figure:

  1. Two MXFP8 formats agree almost perfectly (44.5 dB, above the re-run floor) β‡’ export format is immaterial at MXFP8.
  2. Dropping the experts to MXFP4 moves SSIM@1/8 from ~0.98 to ~0.25 β€” and both independent pairings (INC side 0.2518, CT side 0.2617) agree. That is a composition-level change, not a style/texture shift, and it is a property of MXFP4, not of either loader.
  3. Unlike SSIM-vs-BF16, the ⑒↔④ pair holds topology and format fixed, so the 0.25 cannot be blamed on either confounder.

Figure B β€” AR+DiT full pipeline (only ⑒↔④ are topology-matched).

AR+DiT: BF16 / MXFP8-ct / MXFP8-ar / this build

comparison topology PSNR SSIM@1/8
β‘  ↔ β‘’ (BF16 4-GPU vs MXFP8-ar 2-GPU) cross-topology 26.85 0.8715
β‘’ ↔ β‘£ (MXFP8-ar vs this build) both 2-GPU TP1+TP1 12.88 0.1889

⚠ The full pipeline re-draws the AR CoT every run β€” the AR text + ratio tokens are the DiT's conditioning via omni_kv_config (same-build AR+DiT re-run: 27.8 dB; and two builds with byte-identical weights still disagree at SSIM@1/8 = 0.23 in this mode while agreeing at 0.98 in DiT-only mode). Cross-topology alone already costs 0.87 β†’ 0.98. Read Figure A, not Figure B, for quantization fidelity; Figure B only shows that this build renders a sane image.

Not measured

No task-level benchmark (GenEval / DPG-Bench / CVTG-2K / DrawBench) has been run for this build or for the -mixed sibling. "Different composition" is measured; "worse quality" is not established.


Known limitations

  1. Requires the metadata fix above; without it the checkpoint does not load at all (fails loudly, which is preferable to loading silently with the wrong scheme).
  2. Memory-only on Hopper β€” experts run W4A16 through Marlin.
  3. Composition diverges from BF16/MXFP8 (SSIM@1/8 β‰ˆ 0.26); do not assume parity, and do not mix outputs from this build with outputs from the -mixed build in one A/B comparison (33 dB apart).
  4. The ViT stays BF16 (4304 % 32 β‰  0) β‡’ not a whole-model MXFP4 build.
  5. The upstream INC matcher's MoE branch is keyed on a class name containing fusedmoe, which never matches RoutedExperts on vLLM β‰₯ 0.29; this build works around the resulting brittleness by using an explicit regex key. A cleaner fix belongs in AutoRound (emit a matchable key) or in the INC parser.
  6. block_name_to_quantize = "model.layers" scopes quantization to the 32 shared backbone layers; AR and DiT load the same tensors through different stacks.
  7. images/ holds verification snapshots only; it is not read when loading weights.
Downloads last month
47
Safetensors
Model size
44B params
Tensor type
BF16
Β·
U8
Β·
F8_E4M3
Β·
F32
Β·
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support