Skip to content

Skip local Hessian's max-calibration forward when no activation needs it - #3042

Closed
cjluo-nv wants to merge 1 commit into
mainfrom
chenjiel/local-hessian-skip-max-forward
Closed

cjluo-nv wants to merge 1 commit into
mainfrom
chenjiel/local-hessian-skip-max-forward

Conversation

@cjluo-nv

@cjluo-nv cjluo-nv commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

What does this PR do?

Type of change: new feature (performance)

local_hessian_calibrate runs max_calibrate first, then its own forward to collect the per-block Hessians. Its weight search needs no forward at all. So when no activation quantizer needs a data-driven amax, the max-calibration forward does nothing: weight-only recipes, constant-amax activations, and dynamic or MX activations all fall in that case. It now passes skip_forward_without_activation_calib=True, as GPTQ (#2864) and imatrix already do.

  • The forward still runs whenever an enabled activation quantizer needs it, as _needs_activation_forward_for_max_calib decides. Weight quantizers are calibrated on the weights directly either way.
  • feat(quant): skip max-calib forward when no activation needs data #1963 left local Hessian out of the skip because it "always needs activations". It does, but it collects them in its own Hessian forward, which runs right after. That forward also still triggers anything the caller's forward_loop must do, such as materializing ZeRO-3 shards.
  • Scope: the shipped local_hessian recipes are W4A4 NVFP4. Their activation quantizers need a data-driven global amax, so they still run both forwards and are unchanged. That includes nvfp4_act_headroom with weight_scale_algorithm: local_hessian, whose activation statistics come from that very forward. The skip helps weight-only, experts-only constant-amax and dynamic-activation local_hessian setups.

Usage

No API change.

Testing

  • phi-2, weight-only static NVFP4 with local_hessian, 128 x 512 calibration tokens, single RTX PRO 6000 (shared workstation, so five runs each):

    forward_loop calls max_calibrate mtq.quantize
    main 2 3.71 s 56.0-60.6 s (mean 57.9)
    This PR 1 0.35 s 52.0-54.2 s (mean 53.0)

    The weight search takes about 48-50 s of either run, so the forward saved is about 3.4 s here. A layerwise run on a large MoE saves one full forward of each layer over all calibration data.

  • New CPU test test_max_calibration_runs_a_forward_only_for_activation_amax, which counts forward_loop calls. A weight-only config runs one forward, and fails with two on main. A static input amax still runs two.

  • tests/unit/torch/quantization/test_local_hessian.py: 13 passed. tests/gpu/torch/quantization/test_nvfp4_fp8_sweep_kernel.py (the local-Hessian kernel and end-to-end tests): 45 passed. Local-Hessian, max and fused-expert tests in test_calib.py, plugins/test_fused_experts.py and tests/unit/recipe: 156 passed.

Before your PR is "Ready for review"

  • Is this change backward compatible?: ✅ (same calibration results; one fewer forward only when it computed nothing)
  • 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 (performance only)
  • Did you get Claude approval on this PR?: ❌

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved local-Hessian calibration so weight-only quantization avoids an unnecessary calibration pass, while configurations with static input activation quantization still perform the additional pass needed for calibration.

local_hessian_calibrate ran max_calibrate's forward before its own
Hessian forward even when no activation quantizer needs a data-driven
amax (weight-only, constant-amax or dynamic activations), so that forward
did nothing. It now opts into skip_forward_without_activation_calib, as
GPTQ and imatrix do: the forward still runs whenever an activation
quantizer needs it, and weight calibration runs on the weights either
way. Weight-only local Hessian on phi-2 (128 x 512 tokens) runs one
forward instead of two: max calibration 3.7 s -> 0.35 s, the whole call
about 58 s -> 53 s.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
@cjluo-nv
cjluo-nv requested review from a team as code owners October 27, 2026 05:27
@cjluo-nv
cjluo-nv requested a review from sychen52 October 27, 2026 05:27
@coderabbitai

coderabbitai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 278953c and c3abac5.

Files selected for processing: 2
  • modelopt/torch/quantization/model_calib.py
  • tests/unit/torch/quantization/test_local_hessian.py

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

Walkthrough

Local Hessian calibration now enables max calibration to skip its forward when no enabled quantizer needs data-driven activation statistics. A parameterized test checks forward-loop counts for weight-only and static-input-amax configurations.

Changes: Local Hessian calibration

Layer / File(s) Summary
Conditional max-calibration forward
modelopt/torch/quantization/model_calib.py, tests/unit/torch/quantization/test_local_hessian.py
local_hessian_calibrate passes skip_forward_without_activation_calib=True to max calibration. The test checks one forward-loop call for weight-only quantization and two when a static input-amax quantizer is configured.

Priority: Low

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

Change: Feature

Possibly related PRs

  • NVIDIA/Model-Optimizer#2864: Adds the skip_forward_without_activation_calib option that this change enables in local Hessian calibration.
Merge Risk: Minimal · up to c3aba

No merge-blocking correctness or calibration risk is established by the change.

Pre-merge checks: Passed 5 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within surv… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 5 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: skipping the local Hessian max-calibration forward when activation calibration is not required.
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 modelopt/torch/quantization/model_calib.py and a unit test. Added Python lines contain no torch.load(..., weights_only=False), unsafe numpy.load, hardcoded `trust_remot…
Full details: Security Anti-Patterns

Explanation

PASS. The PR changes only modelopt/torch/quantization/model_calib.py and a unit test. Added Python lines contain no torch.load(..., weights_only=False), unsafe numpy.load, hardcoded trust_remote_code=True, eval/exec, or # nosec. No pyproject.toml or requirements file changed, so no new dependency license issue is introduced.

Full details: Docstring Coverage

Explanation

Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 4 functions across 2 files.

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@github-actions

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://NVIDIA.github.io/Model-Optimizer/pr-preview/pr-3042/

Built to branch gh-pages at 2026-10-27 05:32 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@codecov

codecov Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.33%. Comparing base (278953c) to head (c3abac5).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3042      +/-   ##
==========================================
- Coverage   79.87%   79.33%   -0.54%     
==========================================
  Files         661      661              
  Lines       75417    75417              
==========================================
- Hits        60237    59830     -407     
- Misses      15180    15587     +407     
Flag Coverage Δ
examples-diffusers 19.92% <0.00%> (-0.03%) ⬇️
examples-gpt-oss 13.44% <0.00%> (-0.01%) ⬇️
examples-hf_ptq 23.45% <0.00%> (+0.04%) ⬆️
examples-llm_distill 13.52% <0.00%> (-0.01%) ⬇️
examples-llm_eval 17.21% <0.00%> (-0.02%) ⬇️
examples-llm_qat 17.38% <0.00%> (-0.06%) ⬇️
examples-llm_sparsity 16.73% <0.00%> (-0.02%) ⬇️
examples-megatron_bridge 26.46% <0.00%> (-0.30%) ⬇️
examples-specdec_bench 13.22% <0.00%> (-0.01%) ⬇️
examples-speculative_decoding 17.46% <0.00%> (-0.08%) ⬇️
examples-torch_onnx 21.23% <0.00%> (-0.06%) ⬇️
examples-torch_trt 15.19% <0.00%> (-0.01%) ⬇️
examples-vllm_serve 17.42% <0.00%> (-0.02%) ⬇️
gpu 59.13% <100.00%> (-1.19%) ⬇️
regression 15.03% <0.00%> (-0.02%) ⬇️
unit 61.33% <100.00%> (+<0.01%) ⬆️

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.

@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.

Bot review (gpt-6.1-sol) — DM the bot to share feedback.

Nudge: exact-SHA verification is unavailable, and the ZeRO-3 compatibility claim needs evidence before the unconditional skip is merge-ready.

Needs action:

  • Confirm the review against base 278953c31c06b6b2170316e801b910c079cb90d0 and head c3abac5e6f511ec383780f15df5e68a61203c613; the available file tools expose an unidentified PR-branch snapshot, not SHA-pinned content.
  • Validate ZeRO-3 weight-amax initialization and refinement at model_calib.py:1225, or retain the max forward for that path; the later Hessian forward runs after max-stat collection ends.

No action needed:

  • No confirmed defect found in the supplied diff and accessible source; the change reuses the existing guard and adds meaningful forward-count coverage.
  • Review tag: fresh-20261028T202642Z/PR-3042.

@cjluo-nv

Copy link
Copy Markdown
Collaborator Author

Closing this. The skip only applies to weight-only local_hessian configs, and the shipped local_hessian recipes also calibrate activations, so they keep the max forward either way. The saving is one forward (about 58 -> 53 s on phi-2). Making the skip safe for ZeRO-3, where the max forward also initializes and refines the weight amax, would need a guard and a ZeRO-3 test, which is more than this saving is worth. The branch stays, so this can be reopened if weight-only local_hessian becomes a common path.

@cjluo-nv cjluo-nv closed this Oct 31, 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.

2 participants