Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion modelopt/torch/quantization/utils/calib_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def setup(self):

def hessian_forward(self, input, *args, **kwargs):
inp = input.to_local() if hasattr(input, "to_local") else input
if self.input_quantizer is not None and self.input_quantizer.is_enabled:
if self.input_quantizer is not None:
hessian_input = self.input_quantizer(inp)
else:
hessian_input = inp
Expand Down
71 changes: 71 additions & 0 deletions tests/gpu/torch/quantization/test_gptq.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@
import pytest
import torch
from _test_utils.torch.transformers_models import get_tiny_llama
from _test_utils.torch.quantization.quantize_common import get_awq_config
from conftest import requires_triton

import modelopt.torch.quantization as mtq
from modelopt.torch.export.unified_export_hf import _export_quantized_weight
from modelopt.torch.quantization.model_calib import gptq
from modelopt.torch.quantization.qtensor.nvfp4_tensor import NVFP4QTensor
from modelopt.torch.quantization.utils.calib_utils import (
GPTQHelper,
compute_hessian_inverse,
gptq_blockwise_update,
gptq_blockwise_update_fused_scalar,
Expand Down Expand Up @@ -149,6 +151,75 @@ def test_gptq_e2e_flow(quant_cfg, tiny_tokenizer):
model = mtq.quantize(model, quant_cfg, forward_loop=calibrate_loop)


def test_awq_gptq_chain_collects_hessian_in_smoothed_coordinates(monkeypatch):
"""GPTQ must collect its Hessian in AWQ's transformed input coordinates."""
captured = []
original_update_weights = GPTQHelper.update_weights

def capture_hessian(self, block_size, perc_damp):
pre_quant_scale = self.module.input_quantizer.pre_quant_scale
assert pre_quant_scale is not None
captured.append(
(
self.hessian.detach().clone(),
self.n_samples,
pre_quant_scale.detach().clone(),
)
)
return original_update_weights(self, block_size, perc_damp)

monkeypatch.setattr(GPTQHelper, "update_weights", capture_hessian)

torch.manual_seed(3407)
model = torch.nn.Linear(16, 16, bias=False, device="cuda").eval()

generator = torch.Generator(device="cuda")
generator.manual_seed(91337)
calibration_data = [
torch.randn(8, 16, generator=generator, device="cuda")
for _ in range(4)
]

def forward_loop(module):
for batch in calibration_data:
module(batch)

config = get_awq_config("awq_lite", block_size=8)
config["algorithm"] = [
{"method": "awq_lite", "alpha_step": 0.1},
{"method": "gptq", "layerwise": {"enable": False}},
]

mtq.quantize(model, config, forward_loop=forward_loop)

assert len(captured) == 1
actual_hessian, actual_n_samples, pre_quant_scale = captured[0]
assert not model.input_quantizer.is_enabled
assert not torch.equal(pre_quant_scale, torch.ones_like(pre_quant_scale))

expected_hessian = torch.zeros_like(actual_hessian)
expected_n_samples = 0
for batch in calibration_data:
expected_hessian, expected_n_samples = update_hessian(
batch * pre_quant_scale,
expected_hessian,
expected_n_samples,
)

untransformed_hessian = torch.zeros_like(actual_hessian)
untransformed_n_samples = 0
for batch in calibration_data:
untransformed_hessian, untransformed_n_samples = update_hessian(
batch,
untransformed_hessian,
untransformed_n_samples,
)

assert actual_n_samples == expected_n_samples
assert not torch.equal(actual_hessian, untransformed_hessian)
torch.testing.assert_close(actual_hessian, expected_hessian, rtol=0, atol=0)


# ---------------------------------------------------------------------------
# Fused Triton GPTQ kernel tests for NVFP4 scalar quantization
# ---------------------------------------------------------------------------
Expand Down