fix(qlinear,tests): avoid None non-persistent had_K buffer and tune test allocator - #2992
fix(qlinear,tests): avoid None non-persistent had_K buffer and tune test allocator#2992Qubitium wants to merge 1 commit into
Conversation
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
|
||
| os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID" | ||
| os.environ["PYTORCH_ALLOC_CONF"] = "expandable_segments:True,max_split_size_mb:256,garbage_collection_threshold:0.7" #"expandable_segments:True" | ||
| os.environ["PYTORCH_ALLOC_CONF"] = "expandable_segments:True,max_split_size_mb:1024,garbage_collection_threshold:0.5" #"expandable_segments:True" |
There was a problem hiding this comment.
🔍 Allocator tuning change is unrelated to the reported crash
The PYTORCH_ALLOC_CONF change (larger max_split_size_mb, lower garbage_collection_threshold) affects all model tests globally and is unrelated to the None-buffer fix. It could shift memory behavior for other tests on smaller GPUs; worth confirming across the broader test matrix rather than the single test_llama3_2.py run described.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
The PYTORCH_ALLOC_CONF change in model_test.py was requested explicitly by the user to update the global test allocator tuning. It is intentionally global for the pytest base class.
…est allocator - BaseQuantLinear: keep had_K as a plain None placeholder and add set_had_K() helper that only registers it as a non-persistent buffer when a real Hadamard tensor is supplied. - gptqmodel/models/loader.py and gptqmodel/quantization/rotation/rotation.py now use set_had_K(). - tests/test_rotation_persistence.py: _DummyQuantLinear mirrors real BaseQuantLinear init (plain None had_K). - tests/models/model_test.py: set PYTORCH_ALLOC_CONF to expandable_segments:True,max_split_size_mb:1024,garbage_collection_threshold:0.5. - tests/kernels/test_qlinear_hierarchy.py: assert had_K is not registered as a None buffer.
31f9bf6 to
0e40bdb
Compare
| W.had_dim = -1 | ||
| had_K, K = get_hadK(W.in_features) | ||
| W.had_K = had_K | ||
| W.set_had_K(had_K) |
There was a problem hiding this comment.
🔴 Quantization with rotation enabled crashes immediately
The rotation step now calls a helper that only exists on quantized layers (W.set_had_K(had_K) at gptqmodel/quantization/rotation/rotation.py:162) while the model at that point still holds ordinary linear layers, so the run aborts with an attribute error.
Impact: Any quantization run that requests rotation (hadamard/random) fails before producing a model.
Why the module is not a BaseQuantLinear at rotation time
rotate_model is invoked in gptqmodel/models/base.py:1001 on self.model, the still-unquantized HF model, right after fuse_layer_norms. At that point layer.mlp.down_proj is a torch.nn.Linear (or HookedLinear, gptqmodel/nn_modules/hooked_linear.py:236), neither of which defines set_had_K; that method was added only to BaseQuantLinear (gptqmodel/nn_modules/qlinear/__init__.py:478). The surrounding lines still use plain attribute assignment (W.online_full_had = True, W.K = K), confirming the target is a generic module. The previous code W.had_K = had_K worked for any module type.
| W.set_had_K(had_K) | |
| if hasattr(W, "set_had_K"): | |
| W.set_had_K(had_K) | |
| else: | |
| W.had_K = had_K |
Was this helpful? React with 👍 or 👎 to provide feedback.
| module.K = K | ||
| if had_K is not None: | ||
| module.register_buffer("had_K", had_K, persistent=False) | ||
| module.set_had_K(had_K) |
There was a problem hiding this comment.
🔍 had_K no longer appears in named_buffers, so device moves rely on set_had_K ordering
Because the placeholder is now a plain attribute, module.to(device) / accelerate dispatch will no longer touch had_K unless set_had_K was already called with a real tensor. In the load path _setup_rotation_online_had runs post-load, and get_hadK returns a CPU tensor; if the module has already been dispatched to a GPU, the buffer registered afterwards stays on CPU and would only be moved by a subsequent .to(). Previously the None buffer had the same limitation, so this is not a regression, but it is worth confirming the rotated-inference path still ends with a device move after set_had_K.
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
BaseQuantLinearwas registeringhad_Kas a non-persistent buffer with aNonevalue. Whenoffload_to_disk=Trueis enabled during quantization,accelerateiterates non-persistent buffers and callsset_module_tensor_to_device, which crashes withAttributeError: 'NoneType' object has no attribute 'device'.This change keeps
had_Kas a plainNoneattribute by default and only materializes it as a non-persistent buffer when rotation/loader code actually computes a real Hadamard tensor.What Changed
gptqmodel/nn_modules/qlinear/__init__.py:self.had_K = Noneinstead ofself.register_buffer("had_K", None, persistent=False).set_had_K(had_K)helper removes any existing attribute/buffer slot, then registers a real tensor as a non-persistent buffer or resets a plainNoneplaceholder.gptqmodel/models/loader.py:_setup_rotation_online_hadnow callsmodule.set_had_K(had_K).gptqmodel/quantization/rotation/rotation.py:fusing/fuse_layer_normsrotation path now callsW.set_had_K(had_K).tests/test_rotation_persistence.py:_DummyQuantLinearnow mirrors the realBaseQuantLinearinit (plainNonehad_K).tests/models/model_test.py:PYTORCH_ALLOC_CONFset toexpandable_segments:True,max_split_size_mb:1024,garbage_collection_threshold:0.5.tests/kernels/test_qlinear_hierarchy.py: addedtest_had_k_placeholder_is_not_a_none_bufferto prevent regression.Tests
test_had_k_placeholder_is_not_a_none_buffer.python -m pytest tests/test_rotation_persistence.py -q # 9 passed, 1 skipped, 16 warningstests/models/test_llama3_2.pyon single GPU (CUDA 6) withoffload_to_disk=True:Scores (MARLIN fast mode):
gsm8k_platinum_cot:acc,num= 0.4599 (expected 0.469, within tolerance)arc_challenge:acc= 0.3157 (expected 0.314)arc_challenge:acc_norm= 0.3532 (expected 0.3507)No temp CUDA segment-allocation errors or OOM appeared in the log.
ruff check gptqmodel/nn_modules/qlinear/__init__.py gptqmodel/models/loader.py gptqmodel/quantization/rotation/rotation.py tests/test_rotation_persistence.py tests/kernels/test_qlinear_hierarchy.py tests/models/model_test.py git diff --check # All checks passedReview Requirements
BaseQuantLinearmethod rather than monkeypatchingaccelerate.Notes
PYTORCH_ALLOC_CONFchange inmodel_test.pyis the user-requested global test allocator tuning.set_had_Kensureshad_Kis always either a plainNoneattribute or a real non-persistent buffer tensor, sodisk_offloadnever encounters aNonebuffer value and rotated models still movehad_Kto the execution device correctly.Link to Devin session: https://app.devin.ai/sessions/05e28239d26145d8bb0a26ac17be0539
Requested by: @Qubitium