Conversation
H3 loads its text encoder from a stock Qwen3-VL release, so pointing `--text-encoder-path` at a pre-quantized drop-in such as Qwen3-VL-32B-Instruct-FP8 should just work. Three links were missing: the config never read the checkpoint's `quantization_config`, `Qwen3VLModel` dropped `quant_config` instead of forwarding it, and the text-encoder loader bypasses the FSDP path that normally repacks quantized weights. The checkpoint's own config drives the choice, so nothing extra is passed on the command line. Only `quant_method: fp8` is accepted; the vision tower is left alone since these releases ship no scales for it.
decajoin
requested review from
AgainstEntropy,
BBuf,
HaiShaw,
mickqian,
ping1jing2 and
yichiche
as code owners
August 5, 2026 08:59
Collaborator
|
Thank you for the earlier implementation and validation. #34986 now carries the shared-loader implementation, including the bounded per-layer accelerator staging from this PR in commit 28157eb, with Yiqi Yang credited as co-author. Closing this PR as superseded by #34986 so the two post-load paths cannot diverge or run twice. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
MiniMax-H3 loads its text encoder from a stock Qwen3-VL release, so a pre-quantized FP8 drop-in such as
Qwen3-VL-32B-Instruct-FP8should be usable by simply pointing--text-encoder-pathat it. Today that silently fails to quantize: the encoder is built in BF16 regardless of what the checkpoint ships.The encoder is Qwen3-VL-32B consumed at
hidden_states[50]— 50 of the 64 language layers. Under--performance-mode memorythose layers are CPU-resident, so an unquantized encoder costs a large amount of host RAM that a quantized checkpoint would halve.The underlying machinery already existed (
Qwen3VLTextModelacceptsquant_config, and Ideogram4 uses the same path); only the wiring into the H3 encoder was missing.Modifications
Three links were missing between the checkpoint and the quantized linear layers:
configs/models/encoders/minimax_h3_qwen3vl.py—post_diffusers_config_update()now resolves the checkpoint's ownquantization_configinto anFp8Config. Onlyquant_method: fp8is accepted; anything else raises instead of silently loading an unquantized model. BF16 checkpoints are unaffected (quant_configstaysNone).runtime/models/encoders/qwen3vl.py—Qwen3VLModel.__init__acceptedquant_configfrom callers but dropped it instead of forwarding it toQwen3VLTextModel. The vision tower is deliberately left unquantized: FP8 releases list every visual submodule underignored_layersand ship no scales for them.runtime/models/encoders/minimax_h3_qwen3vl.py— the encoder now passesquant_configthrough, and runs the post-load weight repack itself. The text-encoder loader callsload_weights()directly rather than going through the FSDP path that normally invokesprocess_weights_after_loading, so without this the quantized linear methods would never repack their weights and scales.That repack is staged on the accelerator rather than run wherever the parameters happen to sit.
Fp8LinearMethodselects the Marlin repack (auto-enabled on SM80–86) and the DeepGEMM ue8m0 requant fromget_device_capability()— i.e. from the platform, not from the parameter device — and both are CUDA kernels. Under--layerwise-offload-components text_encoderthe weights are still host-resident at that point, so they abort. Modules are staged one at a time, bounding the extra device memory to a single layer.No new CLI flags: the checkpoint's config drives everything.
Unit tests added in
test/unit/test_minimax_h3_text_encoder_quant.py(8 tests): BF16 stays unquantized, FP8 builds a block-quant config,ignored_layerscover the vision tower, non-fp8 methods are rejected, object-form configs are accepted, andthree covering the device staging above.
Accuracy Tests
Environment: 2× RTX 5090 (sm_120), TP=2,
--performance-mode memory, Ref2VA.Three Ref2VA shots were rendered twice with identical seed, steps, references and prompt — only the text encoder differs — covering a single-image reference, a two-image first/last keyframe pair, and a portrait single-image shot. The outputs were reviewed frame by frame against the BF16 renders.
No visible degradation from the quantized encoder: subject identity, clothing, action sequence, lighting and camera motion are all preserved.
Speed Tests and Profiling
Same environment. Both variants were measured back to back in a single run. Memory is sampled per-PID from server launch until the post-warmup ready banner, so the peaks include the warmup request.
The saving lands on host RAM, not GPU. That is expected here: with
--layerwise-offload-components text_encoderthe 50 language layers are CPU-resident, so halving their size shows up as RSS rather than device memory.Throughput is unchanged. The server warmup request (1344×768×124f, 2/50 steps) takes 52.66 s on BF16 and 51.18 s on FP8 — within run-to-run noise. None is expected either: text encoding is a low single-digit percentage of total runtime here, with the DiT denoise and VAE decode dominating. The point of this change is the host-memory headroom, not speed.
Checklist
CI States
Latest PR Test (Base): ❌ Run #31368078666
Latest PR Test (Extra): ❌ Run #31368078458