Skip to content

fix(gptq): use Cholesky factor of inverse Hessian for block updates - #807

Closed
rachitj27 wants to merge 4 commits into
cactus-compute:mainfrom
rachitj27:fix/gptq-cholesky-block-update
Closed

rachitj27 wants to merge 4 commits into
cactus-compute:mainfrom
rachitj27:fix/gptq-cholesky-block-update

Conversation

@rachitj27

@rachitj27 rachitj27 commented Sep 3, 2026 •

Copy link
Copy Markdown

Summary

  • use the upper Cholesky factor of the inverse Hessian for the per-group GPTQ correction in quantize_hadamard, instead of reusing one global inverse for every 128-column block
  • compute the inverse and factor in float64 and reject a non-finite factor, so ill-conditioned or NaN Hessians disable GPTQ cleanly instead of silently producing a wrong correction
  • add tests covering the block update against an explicit per-step recompute, calibrated error reduction, and bad-Hessian fallbacks

Root cause

_gptq_correct_group sliced H^-1[B, B] and H^-1[B, rest] from a single inverse computed once per tensor. The OBS/GPTQ block update needs the inverse Hessian of the columns that are not yet quantized, which changes after every block (it becomes the Schur complement). Using the stale global inverse is only correct for the first block; every later block applies a miscalibrated correction to the remaining columns.

The standard GPTQ remedy is to factor H^-1 = U^T U with U upper triangular. Processing columns left to right, U[rest, rest] is exactly the factor of the correctly updated inverse at every step, so the block update becomes solve_triangular(U[B, B], U[B, rest]) with no refactorisation.

Impact

Conversion only. The weight file format, engine, and inference speed are unchanged; the stored indices and norms are closer to the original weights on calibration data.

Measured with hook-collected Hessians and diagonal-derived input scales (collect_text_hessians and _input_scale_from_diag) on a 1024→2048→1024 MLP block, evaluating output error on held-out activations. Values are error relative to plain Hadamard CQ with no GPTQ, lower is better.

layer bits old GPTQ new GPTQ
up (1024 wide) 4 0.72 0.50
up (1024 wide) 3 0.73 0.51
up (1024 wide) 2 0.76 0.57
down (2048 wide) 4 0.63 0.33
down (2048 wide) 3 0.64 0.35
down (2048 wide) 2 0.66 0.40

The old correction plateaus at roughly 35–40% error reduction regardless of width, while the fixed version keeps improving as layers get wider.

Related: #767 and #790 cover calibration being silently skipped. This change only matters when GPTQ actually runs, so those fixes and this one compound.

Validation

  • python -m pytest cactus/convert/tests -q (83 passed)
  • new tests in cactus/convert/tests/test_cq.py:
    • test_gptq_cholesky_block_update_matches_explicit_obs: block update matches a reference that recomputes inv(H[start:, start:]) from scratch at every group
    • test_gptq_reduces_calibrated_error: GPTQ strictly lowers calibrated output error vs plain Hadamard CQ4
    • test_gptq_disabled_on_bad_hessian / test_gptq_disabled_on_nan_hessian: negative-definite and NaN Hessians fall back with gptq_used=False and indices identical to the no-GPTQ baseline
  • git diff --check clean

🤖 Generated with Claude Code

The per-group GPTQ correction reused the single global inverse Hessian for
every 128-column block. OBS requires the inverse of the *remaining* columns,
which changes after each block is quantized. The upper Cholesky factor U of
H^-1 has the property that U[rest, rest] is the factor of the correctly
updated inverse at every step, so the block update becomes
solve_triangular(U[B,B], U[B,rest]) with no refactorisation.

Also computes the inverse and factorisation in float64 so ill-conditioned
Hessians do not silently disable GPTQ.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A8jVC79MsJ4PX472pAZ1Qk

Signed-off-by: rachitj27 <rjrachit06@gmail.com>
np.linalg.cholesky on a NaN/inf inverse can return a NaN factor without
raising, which would have enabled GPTQ with garbage corrections.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A8jVC79MsJ4PX472pAZ1Qk

Signed-off-by: rachitj27 <rjrachit06@gmail.com>
…ssians

- block update matches an explicit per-step recompute of the remaining
  column inverse (the Schur-complement reference)
- GPTQ lowers calibrated output error vs plain Hadamard CQ4
- negative-definite and NaN Hessians disable GPTQ cleanly

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A8jVC79MsJ4PX472pAZ1Qk

Signed-off-by: rachitj27 <rjrachit06@gmail.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A8jVC79MsJ4PX472pAZ1Qk

Signed-off-by: rachitj27 <rjrachit06@gmail.com>
@rachitj27
rachitj27 marked this pull request as ready for review September 3, 2026 20:17
@rachitj27 rachitj27 closed this Sep 5, 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.

1 participant