Skip to content

Fix hf_ptq.py discarding completed PTQ run on sanity-generate() failure - #2480

Merged
Edwardf0t1 merged 1 commit into
mainfrom
fix/nvbug-6752977-hf-ptq-sanity-generate
Sep 19, 2026
Merged

Edwardf0t1 merged 1 commit into
mainfrom
fix/nvbug-6752977-hf-ptq-sanity-generate

Conversation

@Edwardf0t1

@Edwardf0t1 Edwardf0t1 commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Type of change: Bug fix

post_quantize() in examples/hf_ptq/hf_ptq.py ran the optional post-quantization sanity-check full_model.generate() unguarded, directly before export_quantized(). Any exception raised there aborted the whole run and discarded a completed calibration without exporting a checkpoint.

Root cause (traced from NVBug 6752977, DGX Spark GB10 / DeepSeek-R1-Distill-Llama-8B / NVFP4): get_model() loads with device_map="auto", relying on accelerate's infer_auto_device_map/get_max_memory() to decide GPU vs. CPU placement. On DGX Spark's unified-memory single-GPU host, that memory probe under-reports GPU capacity, so part of even an 8B model can land on CPU — and the existing fallback shrinks the GPU budget further (* gpu_mem_percentage), compounding it. Calibration survives this because it never invokes the real fake-quant kernel, but the post-PTQ sanity generate() does, and NVFP4's dynamic-block-quantize op (modelopt/torch/quantization/tensor_quant.py) hard-asserts amax.is_cuda with no CPU fallback, so any CPU-offloaded layer crashes there — after ~5.8 hours of calibration, before export.

This PR does not attempt to fix the underlying device_map/memory-probing behavior (unverified without the actual hardware/logs, which weren't reachable from this environment). Instead it makes the failure mode safe: a failure in the optional sanity check now only skips that check and warns, and export always proceeds, regardless of why generate() failed.

Usage

No new API. Behavior change only: examples/hf_ptq/hf_ptq.py now completes export even if the post-quantization sanity generate() call raises.

Testing

  • Added tests/examples/hf_ptq/test_hf_ptq_args.py::test_post_quantize_export_survives_a_failed_sanity_generate, which drives post_quantize() with a full_model.generate() that raises and asserts export_quantized() still runs.
  • Ran pytest tests/examples/hf_ptq/test_hf_ptq_args.py (48 passed).
  • Ran pre-commit on the changed files (ruff-format reformatted line wrapping only).

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?: ✅
  • Did you get Claude approval on this PR?: ❌

Additional Information

Fixes NVBug 6752977. Linked JIRA: OMNIML-5932.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Quantized checkpoint export now continues when the optional post-quantization generation check fails.
    • A warning is shown when the generation check cannot complete.

@Edwardf0t1
Edwardf0t1 requested review from a team as code owners September 19, 2026 00:00
@coderabbitai

coderabbitai Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: NVIDIA/Model-Optimizer/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 5b40ee56-9beb-4186-9570-f68f52d2bd45

📥 Commits

Reviewing files that changed from the base of the PR and between efbd51878aa50717aaac58b57c44136b3fd1f40f and fb389ab.

📒 Files selected for processing (1)
  • CHANGELOG.rst

Included review availability: Your plan provides up to 12 included reviews per hour; 10 remain after this review.


📝 Walkthrough

Walkthrough

The post-quantization generation sanity check now catches failures, emits a warning, and allows checkpoint export to continue. A regression test verifies this behavior, and the changelog records the fix.

Changes

PTQ export recovery

Layer / File(s) Summary
Generation failure handling and validation
examples/hf_ptq/hf_ptq.py, tests/examples/hf_ptq/test_hf_ptq_args.py, CHANGELOG.rst
The generation check now catches exceptions and warns without stopping export. The regression test verifies that export_quantized runs once after failure. The changelog documents the fix.

Priority: ⬇️ Low

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

Change: Bug fix

Suggested reviewers: cjluo-nv

Merge Risk: ⚪ Minimal · up to fb389

The best-effort generation check warns on failure and still exports the checkpoint, with regression coverage for that behavior. The change is ready to merge.

🚥 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 clearly identifies the main change: preserving a completed PTQ run when the sanity generation step fails.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files. (1 skipped: 1 …
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 PASS. The PR changes only CHANGELOG.rst, examples/hf_ptq/hf_ptq.py, and a regression test. The production addition wraps full_model.generate() in try/except and adds no prohibited security pat…
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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

@codecov

codecov Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.71%. Comparing base (ed5c5ed) to head (fb389ab).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2480      +/-   ##
==========================================
+ Coverage   68.65%   70.71%   +2.06%     
==========================================
  Files         601      601              
  Lines       66394    66394              
==========================================
+ Hits        45581    46952    +1371     
+ Misses      20813    19442    -1371     
Flag Coverage Δ
examples-gpt-oss 13.41% <ø> (ø)
examples-hf_ptq 22.41% <ø> (-0.14%) ⬇️
examples-llm_distill 13.48% <ø> (-0.01%) ⬇️
examples-llm_eval 17.35% <ø> (ø)
examples-llm_qat 17.64% <ø> (-0.06%) ⬇️
examples-llm_sparsity 15.93% <ø> (ø)
examples-specdec_bench 13.17% <ø> (ø)
examples-speculative_decoding 17.77% <ø> (-0.07%) ⬇️
examples-vllm_serve 13.80% <ø> (ø)
unit 58.10% <ø> (ø)

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.

@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 (claude-opus-5) — DM the bot to share feedback.

Approve — the guard around the optional post-PTQ sanity generate() is correct (generated_ids_after_ptq stays None, the decode block is already None-gated, export still runs), and the new regression test drives post_quantize() end-to-end and asserts export_quantized() fires.

No action needed.

The post-quantization sanity-check generate() call in post_quantize()
ran unguarded before export_quantized(), so any exception there (e.g.
device_map="auto" offloading part of the model to CPU, which NVFP4's
dynamic block quantization does not support on CPU) aborted the whole
run and discarded a completed calibration without exporting a
checkpoint. Wrap the call so a failure there only skips the sanity
check and export still proceeds.

Fixes NVBug 6752977.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Zhiyu Cheng <zhiyuc@nvidia.com>
@Edwardf0t1
Edwardf0t1 force-pushed the fix/nvbug-6752977-hf-ptq-sanity-generate branch from efbd518 to fb389ab Compare September 19, 2026 00:22
@Edwardf0t1
Edwardf0t1 enabled auto-merge (squash) September 19, 2026 00:35
@Edwardf0t1
Edwardf0t1 merged commit 0a8e708 into main Sep 19, 2026
38 checks passed
@Edwardf0t1
Edwardf0t1 deleted the fix/nvbug-6752977-hf-ptq-sanity-generate branch September 19, 2026 01:21
@github-actions

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

@chadvoegele chadvoegele added the cherry-pick-done Added by bot once PR is cherry-picked to the release branch label Sep 22, 2026
chadvoegele pushed a commit that referenced this pull request Sep 22, 2026
…re (#2480)

Type of change: Bug fix

`post_quantize()` in `examples/hf_ptq/hf_ptq.py` ran the optional
post-quantization sanity-check `full_model.generate()` unguarded,
directly before `export_quantized()`. Any exception raised there aborted
the whole run and discarded a completed calibration without exporting a
checkpoint.

Root cause (traced from [NVBug
6752977](https://nvbugspro.nvidia.com/bug/6752977), DGX Spark GB10 /
DeepSeek-R1-Distill-Llama-8B / NVFP4): `get_model()` loads with
`device_map="auto"`, relying on `accelerate`'s
`infer_auto_device_map`/`get_max_memory()` to decide GPU vs. CPU
placement. On DGX Spark's unified-memory single-GPU host, that memory
probe under-reports GPU capacity, so part of even an 8B model can land
on CPU — and the existing fallback shrinks the GPU budget further (`*
gpu_mem_percentage`), compounding it. Calibration survives this because
it never invokes the real fake-quant kernel, but the post-PTQ sanity
`generate()` does, and NVFP4's dynamic-block-quantize op
(`modelopt/torch/quantization/tensor_quant.py`) hard-asserts
`amax.is_cuda` with no CPU fallback, so any CPU-offloaded layer crashes
there — after ~5.8 hours of calibration, before export.

This PR does not attempt to fix the underlying
`device_map`/memory-probing behavior (unverified without the actual
hardware/logs, which weren't reachable from this environment). Instead
it makes the failure mode safe: a failure in the optional sanity check
now only skips that check and warns, and export always proceeds,
regardless of why `generate()` failed.

No new API. Behavior change only: `examples/hf_ptq/hf_ptq.py` now
completes export even if the post-quantization sanity `generate()` call
raises.

- Added
`tests/examples/hf_ptq/test_hf_ptq_args.py::test_post_quantize_export_survives_a_failed_sanity_generate`,
which drives `post_quantize()` with a `full_model.generate()` that
raises and asserts `export_quantized()` still runs.
- Ran `pytest tests/examples/hf_ptq/test_hf_ptq_args.py` (48 passed).
- Ran `pre-commit` on the changed files (`ruff-format` reformatted line
wrapping only).

- 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](https://github.com/NVIDIA/Model-Optimizer/blob/main/CHANGELOG.rst)?:
✅
- Did you get Claude approval on this PR?: ❌ <!-- pending: run `/claude
review` -->

Fixes NVBug 6752977. Linked JIRA: OMNIML-5932.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

- **Bug Fixes**
- Quantized checkpoint export now continues when the optional
post-quantization generation check fails.
  - A warning is shown when the generation check cannot complete.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Zhiyu Cheng <zhiyuc@nvidia.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Chad Voegele <cvoegele@nvidia.com>
@chadvoegele chadvoegele removed cherry-pick-done Added by bot once PR is cherry-picked to the release branch cherry-pick-0.47.0 labels Sep 22, 2026
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