[Quantization] Support NVFP4-quantized lm_head and embed_tokens via modelopt - #35660
lucaspirola wants to merge 5 commits into
Conversation
|
Hi @lucaspirola, the pre-commit checks have failed. Please run: uv pip install pre-commit
pre-commit install
pre-commit run --all-filesThen, commit the changes and push to your branch. For future commits, Tip Is
|
There was a problem hiding this comment.
Code Review
This pull request introduces support for NVFP4 quantization for the lm_head module. This is achieved by adding a specific quantization method, ModelOptNvFp4LMHeadMethod, for ParallelLMHead layers. This method includes a custom weight loader to correctly handle vocabulary-parallel sharding and scalar parameters associated with NVFP4 quantization. The changes appear to be correct and well-integrated with the existing quantization framework.
ca7e484 to
ea16f90
Compare
|
Hi @lucaspirola, the pre-commit checks have failed. Please run: uv pip install pre-commit
pre-commit install
pre-commit run --all-filesThen, commit the changes and push to your branch. For future commits, Tip Is
|
f7643a4 to
b5fe1ae
Compare
|
This pull request has merge conflicts that must be resolved before it can be |
Add `ModelOptNvFp4LMHeadMethod` to handle NVFP4-quantized lm_head weights in models exported by nvidia-modelopt with lm_head included in quantization (i.e. `*lm_head*` removed from `exclude_modules`). Previously, `ParallelLMHead` (which extends `VocabParallelEmbedding`, not `LinearBase`) was not recognized by `get_quant_method()` and always fell back to `UnquantizedEmbeddingMethod`, causing weight loading to fail when NVFP4 scale parameters were present. The new method: - Provides a custom weight_loader compatible with NVFP4 packed uint8 weights and PerTensorScaleParameter scalars on VocabParallelEmbedding - Handles tensor-parallel sharding via VocabParallelEmbedding shard indices - Sets `params_dtype` needed by Marlin's FP4 kernel preparation On VRAM-constrained GPUs (e.g. 16 GB), quantizing lm_head to NVFP4 saves ~960 MB for a 131K vocab model, enabling significantly more KV cache (e.g. 8K -> 20K tokens context on RTX 5080). Signed-off-by: Lucas Pirola <lucaspirola@gmail.com> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Lucas Pirola <lucaspirola@users.noreply.github.com>
Extend the NVFP4 lm_head support to also handle embed_tokens via a new ModelOptNvFp4EmbeddingMethod. On a 131K-vocab model (Devstral-24B), this reduces embed_tokens from 1280 MB (BF16) to ~360 MB (NVFP4), freeing ~920 MB for KV cache (+57% more context on RTX 5080 16 GB). - Add `nvfp4_embed` flag to ModelOptNvFp4Config and _from_config (supports both hf_quant_config.json and compressed-tensors formats) - Add VocabParallelEmbedding routing in get_quant_method - Add ModelOptNvFp4EmbeddingMethod with per-row FP4 E2M1 dequantization via lookup table (no GEMM kernel needed) - TP-aware weight loading via VocabParallelEmbedding shard indices Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Lucas Pirola <lucaspirola@users.noreply.github.com>
Add assert isinstance(self, ModelOptNvFp4Config) before passing self to ModelOptNvFp4EmbeddingMethod, which expects ModelOptNvFp4Config rather than ModelOptQuantConfigBase. The assert is always true at runtime since nvfp4_embed is only set on ModelOptNvFp4Config. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Lucas Pirola <lucaspirola@users.noreply.github.com>
Some VL models (e.g. Qwen3.5) use 'visual' as their vision encoder prefix rather than 'vision_tower' or 'vision_model'. Signed-off-by: Lucas Pirola <lucaspirola@gmail.com> Signed-off-by: Lucas Pirola <lucaspirola@users.noreply.github.com>
b5fe1ae to
00155cd
Compare
… head path Reported by depthfirst-app review on the rebased PR. After upstream vllm-project#42124 (LM head quantization support for ModelOpt) added ParallelLMHead to the (LinearBase, ParallelLMHead) tuple, the elif isinstance(layer, ParallelLMHead) branch added by this PR became dead code, silently bypassing ModelOptNvFp4LMHeadMethod and its VocabParallelEmbedding-aware weight loader. Naively removing ParallelLMHead from the prior tuple would route non-NVFP4 ModelOpt configs (FP8, MxFp8, MixedPrecision) into self.LMHeadMethodCls = LinearMethodBase, which is abstract and would regress vllm-project#42124. Fix: check ParallelLMHead first and only return LMHeadMethodCls when a subclass has actually overridden it (NVFP4); otherwise fall through to LinearMethodCls, preserving the upstream behavior. Signed-off-by: Lucas Pirola <lucaspirola@users.noreply.github.com>
|
This pull request has merge conflicts that must be resolved before it can be |
|
This pull request has been automatically marked as stale because it has not had any activity within 90 days. It will be automatically closed if no further activity occurs within 30 days. Leave a comment if you feel this pull request should remain open. Thank you! |
Summary
Add support for serving models where
lm_headand/orembed_tokensare quantized to NVFP4 by nvidia-modelopt.lm_head support:
ParallelLMHeadextendsVocabParallelEmbedding(notLinearBase), soModelOptQuantConfigBase.get_quant_method()previously returnedNonefor it, falling back toUnquantizedEmbeddingMethod. This caused weight loading to fail withValueError: There is no module or parameter named 'lm_head.input_scale'when NVFP4 scale tensors were present in the checkpoint.ModelOptNvFp4LMHeadMethod(extendsModelOptNvFp4LinearMethod) with a custom weight_loader that handlesPerTensorScaleParameterscalars and vocab-parallel sharding for NVFP4 packed weights onVocabParallelEmbedding-based layers.lm_headis excluded from quantization (the default), behavior is unchanged.embed_tokens support:
ModelOptNvFp4EmbeddingMethodthat stores embeddings in NVFP4 packed format and dequantizes per-row during lookup via an FP4 E2M1 lookup table (no GEMM kernel needed)."nvfp4_embed": trueinhf_quant_config.jsonorconfig.jsonquantization_config.hf_quant_config.jsonformat ({"quantization": {"nvfp4_embed": true}}) and compressed-tensors format ({"nvfp4_embed": true}).Motivation: On VRAM-constrained GPUs (e.g. 16 GB), quantizing both
lm_headandembed_tokensto NVFP4 saves ~1880 MB total for a 131K-vocab model. On RTX 5080 with Devstral-24B, this enables 32K tokens context (up from 20K with BF16 embeddings, +57.9%).How to produce an NVFP4 checkpoint
lm_head - include in modelopt quantization:
embed_tokens - quantize offline after modelopt export:
Test plan
🤖 Generated with Claude Code