Repository navigation
Conversation
…pairing GLM5-next runs q_a/kv_a (fused), q_b and o_proj in BF16, but its FP8 checkpoints store them as block-FP8 (weight + weight_scale_inv). The loaders held each weight until its scale arrived (in a dict local to one load_weights call) and dequantized there, so a pair split across calls was silently dropped. Add Fp8DequantConfig / Fp8DequantLinearMethod: the weight and its scale are registered under their checkpoint names and load independently, in any order and any number of calls; post-load processing dequantizes. Each FP8 row sits in the first half of the bytes of its BF16 row, so the weight's storage is the BF16 weight's (no extra memory), dequantization happens at the startup address, and a BF16 tensor (a BF16 checkpoint, or a trainer sending BF16) loads into the same rows with no scale, even next to FP8 rows of the same fused weight. Rows already dequantized stay runtime bytes, so a reload that leaves the weight out keeps it. The scale stays registered (retired) so a reload can load it again. The model's loaders only adapt single tensors: Quark's weight_scale is loaded as weight_scale_inv and NoPE kv_a gets its zero RoPE rows (and scale blocks) per tensor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SeFSSrpw7yTpLz2C2sHDKA Signed-off-by: Aaron Hao <aaronhao@inferact.ai>
… pre-processed weights Two fixes found in review: - Layerwise reload captures the weight to the meta device, which makes the strided FP8 view contiguous. Loading into that layout put FP8 row i at byte i * cols, so dequantization (BF16 row i at byte 2 * i * cols) overwrote rows not yet read in weights of more than one chunk. The FP8 view is now always rebuilt over the runtime layout of the storage. - Weights from the weight cache are runtime BF16 already: skip the dequantization when they are pre-processed. Tests: a fused weight spanning several dequantization chunks, layerwise reload with FP8 rows loaded before BF16 ones, pre-processed weights. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SeFSSrpw7yTpLz2C2sHDKA Signed-off-by: Aaron Hao <aaronhao@inferact.ai>
This was referenced Oct 28, 2026
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.
PR 6 of the direct weight reload stack (RFC vllm-project#54477), first part. Stacked on
wu/stack/direct-g= vllm-project#57733 + vllm-project#62407 + vllm-project#58382.Problem
GLM5-next runs its MLA projections (
fused_qkv_a_proj= q_a + kv_a,q_b_proj,o_proj) in BF16, but its FP8 checkpoints (GLM-5.3-Flash) store them as block-FP8:weight+weight_scale_inv. The model's loader, and the MTP loader, held each weight until its scale arrived and dequantized it there.That pairing buffer was a local of one
load_weightscall. An RL update that sends a weight and its scale in different calls (different buckets, or a different order) silently dropped both halves, and the projection kept its old values. A BF16 trainer works only because BF16 tensors skip that path.Fix
Pair nothing in the loader. Register the checkpoint tensors as parameters under their checkpoint names, load each on its own, and combine them in
process_weights_after_loading.New
Fp8DequantConfig/Fp8DequantLinearMethod(layers/quantization/fp8_dequant.py), for linear layers that a quantized checkpoint may store as block-FP8 but that run in BF16:weightis an FP8 view of the BF16 weight's own storage, with FP8 row i in the first half of the bytes of BF16 row i (row stride2 * cols). There is no extra memory, and the TP sharding and fused-shard loading of the regularweight_loader_v2path apply unchanged.weight_scale_invis a regular block-scale parameter. It is retired after post-load processing ([Quantization] Keep deleted and renamed params registered after process_weights_after_loading vllm-project/vllm#62407) and stays registered, so a reload can load it again.GLM5-next (
attention.py) uses it for these projections whenever the checkpoint is quantized. The loaders (model.py,mtp.py) only adapt single tensors:weight_scaleis loaded asweight_scale_inv.kv_a_proj_with_mqagets its zero RoPE rows on the weight, and its extra scale blocks on the scale, each tensor on its own._try_load_fp8_attn_projand_dequant_fp8_blockare removed.Not in this PR (next parts of PR 6, same mechanism): the indexer
wkFP8 dequant (DeepSeek-V2/V3.2 + MTP, HY-V4, dots3, GLM5-next) and MiMo-V2's fused FP8 qkv.Tests
Unit tests on GB200, nightly
dbcf568821with this branch overlaid:tests/quantization/test_fp8_dequant.py, 13 passed (slurm 40414). It covers:load_weightscall, with scales before weights;Run on the first commit of this PR, before the review fixes, the layerwise FP8-first and pre-processed cases fail (slurm 40415).
tests/quantization/test_quark.py -k glm5next: 4 passed (40409). The two loader tests are rewritten for the per-tensor adaptation.tests/model_executor/model_loader/test_direct_reload.py: 11 passed.test_model_finalize.py: 8 passed.End to end on GB200 (slurm 40417), with the sharded-trainer benchmark (vllm-state-transition-verifier
sharded-trainer-bench):fsdp=2, NCCL engine, 1 warmup + 2 measured syncs alternating two perturbed checkpoints. Every tensor changes, and scales are scaled by powers of two.WeightSourcesubclasses were adapted to the [RL] Declare weight ownership and layout on ParamMeta vllm-project/vllm#63256 contract for this run:metadataas acached_property,iter_full, and nom2n_sourceimport._workspace_buffer)weight_scale_invdirect-g+ PR 5), direct, FP8 updates_workspace_buffer)_workspace_buffer)_workspace_bufferis the MLA backend's scratch space. It differs from the fresh server in every run, including the base, so it is not caused by this PR.q_b_projando_proj, which are sharded across the two ranks.🤖 Generated with Claude Code
https://claude.ai/code/session_01SeFSSrpw7yTpLz2C2sHDKA