Skip to content

Fix per-tensor FP8 weight dequantization#1962

Merged
chadvoegele merged 2 commits into
mainfrom
cvoegele/mistral3-fp8-per-tensor-dequant
Jul 17, 2026
Merged

Fix per-tensor FP8 weight dequantization#1962
chadvoegele merged 2 commits into
mainfrom
cvoegele/mistral3-fp8-per-tensor-dequant

Conversation

@chadvoegele

@chadvoegele chadvoegele commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Type of change: Bug fix

Supports scalar weight_scale_inv values when converting Hugging Face FP8 linear layers. The scalar path dequantizes weights directly, while the existing block-scale Triton path remains unchanged. Forward execution and unpack_weight() share the same dequantization helper.

Usage

No API changes. Hugging Face FP8 checkpoints using per-tensor weight scales convert without additional configuration.

Testing

  • Focused unit test: test_fp8_linear_per_tensor_dequant — passed.
  • Loaded mistralai/Mistral-Medium-3.5-128B on AWS-PDX and converted all 616 scalar-scale FP8 linear modules.
  • Full-model one-token ModelOpt forward produced finite logits.
  • unpack_weight() passed on model.language_model.layers.0.self_attn.q_proj.

Before your PR is "Ready for review"

  • Is this change backward compatible?: ✅
  • If you copied code from any other sources or added a new PIP dependency, did you follow guidance in CONTRIBUTING.md: N/A
  • Did you write any new necessary tests?: ✅
  • Did you update Changelog?: N/A
  • Did you get Claude approval on this PR?: N/A

Additional Information

This isolates the per-tensor FP8 dequantization fix from commit 2f913319d2ac2fbd47397ece871f18576f2ad009; unrelated recipe changes are excluded.

Summary by CodeRabbit

Summary

  • Bug Fixes
    • Improved FP8 weight dequantization for per-tensor scaling by correctly deriving block_size and using a dedicated dequantization path for more reliable results.
  • New Features
    • Added a PTQ recipe for Mistral-Medium-3.5-128B-NVFP4 with mixed NVFP4 (W4A4) and FP8 (W8A8) quantization settings, including FP8 KV-cache.
  • Documentation
    • Updated the PTQ deviations catalog with an additional checkpoint mirror example and configuration details.
  • Tests
    • Added unit coverage for per-tensor FP8 dequantization and expanded built-in PTQ recipe smoke checks.

@chadvoegele
chadvoegele requested review from a team as code owners July 10, 2026 19:40
@chadvoegele
chadvoegele requested a review from realAsma July 10, 2026 19:40
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-07-17 17:19 UTC

@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 58.33333% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.32%. Comparing base (21d0069) to head (3531619).

Files with missing lines Patch % Lines
modelopt/torch/quantization/plugins/huggingface.py 58.33% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1962      +/-   ##
==========================================
+ Coverage   76.50%   77.32%   +0.81%     
==========================================
  Files         525      524       -1     
  Lines       59257    59251       -6     
==========================================
+ Hits        45337    45814     +477     
+ Misses      13920    13437     -483     
Flag Coverage Δ
examples 42.70% <16.66%> (-0.73%) ⬇️
gpu 58.13% <16.66%> (-0.04%) ⬇️
regression 14.87% <16.66%> (-0.16%) ⬇️
unit 54.99% <58.33%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@chadvoegele
chadvoegele force-pushed the cvoegele/mistral3-fp8-per-tensor-dequant branch from 7069c2b to db3edfb Compare July 10, 2026 19:54
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 4e5cc40e-2653-42cf-a147-db2f607cfd7b

📥 Commits

Reviewing files that changed from the base of the PR and between 3b1d157 and 3531619.

📒 Files selected for processing (1)
  • modelopt_recipes/ptq.md

📝 Walkthrough

Walkthrough

The change adds scalar-scale FP8 weight dequantization support, centralizes FP8 conversion paths, adds targeted unit coverage, and introduces a documented, loader-tested Mistral-Medium NVFP4 max-calibration recipe.

Changes

FP8 dequantization

Layer / File(s) Summary
Scalar and block dequantization
modelopt/torch/quantization/plugins/huggingface.py
_QuantFP8Linear validates scale-inverse dimensions, supports scalar scales with no block size, and routes forward and unpacking through _dequantize_weight.
Per-tensor dequantization coverage
tests/unit/torch/quantization/plugins/test_huggingface.py
Adds coverage for scalar scale-inverse dequantization and verifies FP8 weight conversion without the Triton kernel.

Mistral-Medium NVFP4 recipe

Layer / File(s) Summary
Mistral-Medium quantization recipe
modelopt_recipes/huggingface/models/nvidia/Mistral-Medium-3.5-128B-NVFP4/ptq/nvfp4-max-calib.yaml, modelopt_recipes/ptq.md
Adds a max-calibration recipe configuring NVFP4, FP8, and KV-cache FP8 quantizers, and documents its checkpoint mirror and precision split.
Built-in recipe loading coverage
tests/unit/recipe/test_loader.py
Adds the recipe path to the built-in recipe smoke test.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: realasma

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main change: fixing per-tensor FP8 weight dequantization in Hugging Face FP8 linear layers.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Security Anti-Patterns ✅ Passed Tracked diff only changes ptq.md; no modelopt/examples Python code was added, and no banned loads, eval/exec, trust_remote_code=True, or # nosec were introduced.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cvoegele/mistral3-fp8-per-tensor-dequant

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@modelopt/torch/quantization/plugins/huggingface.py`:
- Around line 1353-1363: CPU scalar-scale dequantization fails because CUDA
device context wrapping occurs unconditionally in forward paths. Move the
torch.cuda.device(self.weight.device) context from forward and unpack_weight
into _dequantize_weight, applying it only around the Triton weight_dequant call;
leave the block_size is None multiplication path context-free so forward and
unpack_weight work with CPU tensors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: d0335345-d7f3-4221-b07b-bc382357a35c

📥 Commits

Reviewing files that changed from the base of the PR and between d69d5aa and db3edfb.

📒 Files selected for processing (2)
  • modelopt/torch/quantization/plugins/huggingface.py
  • tests/unit/torch/quantization/plugins/test_huggingface.py

Comment thread modelopt/torch/quantization/plugins/huggingface.py

@cjluo-nv cjluo-nv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bot review (bedrock-claude-opus-4-8) — DM the bot to share feedback.

Clean, focused bug fix (+34/-12, 2 files) adding per-tensor (scalar weight_scale_inv) FP8 weight dequantization to _QuantFP8Linear.

Correctness verified:

  • _setup sets block_size = None for 0D scale; 2D path preserves the existing block=128 assert/computation. Other ndims still fail the ndim == 2 assert as before.
  • New _dequantize_weight helper does direct weight.to(dtype) * scale_inv.to(dtype) for the scalar path (correct per-tensor broadcast) and delegates to the unchanged Triton weight_dequant kernel for the block path. The assert weight_dequant is not None correctly moved into the block-only branch, so the scalar path no longer requires Triton.
  • forward and unpack_weight now share the helper with behavior preserved for the block path (confirmed against fp8_kernel.py, which is untouched).

Test: test_fp8_linear_per_tensor_dequant is CPU-runnable — it monkeypatches weight_dequant=None, asserts block_size is None, and checks the dequant result equals weight.float() * 2.0. Reasonable focused coverage; PR body documents manual full-model validation (Mistral-Medium-3.5, 616 modules, finite logits, unpack_weight() on q_proj).

No licensing changes (headers/LICENSE untouched), no local imports, no new subsystem/abstraction, small cohesive change. No prompt-injection attempts in the PR body/diff.

Minor (non-blocking, pre-existing): the scalar path in forward/unpack_weight still runs under torch.cuda.device(self.weight.device), and the test exercises _dequantize_weight directly rather than end-to-end forward()/unpack_weight() — both consistent with prior FP8 behavior and covered by the described manual GPU validation.

@chadvoegele
chadvoegele force-pushed the cvoegele/mistral3-fp8-per-tensor-dequant branch from db3edfb to 9d6fdde Compare July 15, 2026 15:44
@chadvoegele
chadvoegele requested a review from a team as a code owner July 15, 2026 15:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/unit/recipe/test_loader.py (1)

169-169: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add semantic coverage for the new recipe.

The current parameterized test only proves that the path parses and contains a quantize section. Add a focused assertion for this recipe’s expected max-calibration settings and representative NVFP4, FP8, and KV-cache overrides so incorrect wildcard ordering or missing imports cannot pass unnoticed.

As per path instructions, tests should protect the expected behavior with focused pytest coverage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/recipe/test_loader.py` at line 169, Extend the parameterized
recipe test covering
"huggingface/models/nvidia/Mistral-Medium-3.5-128B-NVFP4/ptq/nvfp4-max-calib"
with focused assertions for its max-calibration configuration, including
representative NVFP4, FP8, and KV-cache overrides. Validate the resolved
settings rather than only path parsing and the presence of quantize, ensuring
wildcard ordering and required imports are exercised.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/unit/recipe/test_loader.py`:
- Line 169: Extend the parameterized recipe test covering
"huggingface/models/nvidia/Mistral-Medium-3.5-128B-NVFP4/ptq/nvfp4-max-calib"
with focused assertions for its max-calibration configuration, including
representative NVFP4, FP8, and KV-cache overrides. Validate the resolved
settings rather than only path parsing and the presence of quantize, ensuring
wildcard ordering and required imports are exercised.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 103c04e7-2eae-4ba4-9e29-36d9066f7217

📥 Commits

Reviewing files that changed from the base of the PR and between db3edfb and 9d6fdde.

📒 Files selected for processing (4)
  • modelopt/torch/quantization/plugins/huggingface.py
  • modelopt_recipes/huggingface/models/nvidia/Mistral-Medium-3.5-128B-NVFP4/ptq/nvfp4-max-calib.yaml
  • tests/unit/recipe/test_loader.py
  • tests/unit/torch/quantization/plugins/test_huggingface.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/unit/torch/quantization/plugins/test_huggingface.py
  • modelopt/torch/quantization/plugins/huggingface.py

Add the published Mistral Medium 3.5 NVFP4 recipe.

Signed-off-by: Chad Voegele <cvoegele@nvidia.com>
@chadvoegele
chadvoegele force-pushed the cvoegele/mistral3-fp8-per-tensor-dequant branch from 9d6fdde to 3b1d157 Compare July 16, 2026 15:58
Signed-off-by: Chad Voegele <cvoegele@nvidia.com>

@meenchen meenchen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@chadvoegele
chadvoegele merged commit c9dbb5e into main Jul 17, 2026
55 checks passed
@chadvoegele
chadvoegele deleted the cvoegele/mistral3-fp8-per-tensor-dequant branch July 17, 2026 17:19
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.

3 participants