GGML fake-quant keeps stale codes after a bitsandbytes optimizer step, so QAT/QAD silently stops training the quantized weights
Maintainer thường phản hồi trong vòng 2 ngày
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 54/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Lĩnh vực
- machine-learning
Hướng nghiên cứu
Start in modelopt/torch/quantization/ggml/common.py and trace how _input_cache_key decides whether to reuse the encoded payload. Run the provided reproduction with both AdamW and AdamW8bit to confirm the stale-cache behavior. Done means the forward after either optimizer step uses codes encoded from the updated weights.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Describe the bug
The GGML backends cache the encoded payload and reuse it while _input_cache_key is unchanged (modelopt/torch/quantization/ggml/common.py). The key includes the weight's PyTorch version counter (inputs._version). bitsandbytes' 8-bit optimizers update parameters from their own CUDA kernels, which doesn't bump that counter. So after optimizer.step(), the next forward reuses the payload encoded before the step: the fp32 weights move, but the quantized weights the model computes with never change.
Impact: QAT/QAD with a GGML format and a bitsandbytes optimizer (for example transformers' optim="adamw_bnb_8bit", a common choice on smaller GPUs) silently trains only the unquantized layers. Nothing errors, and the loss still goes down because those layers keep training, so it's easy to miss. I tested iq2_xxs; the cache is shared by the GGML formats, so the others should behave the same.
I hit this distilling an IQ2_XXS Qwen3-1.7B (general/ptq/iq2_xxs, MLP only): after 12 steps at lr 1e-4, 100% of the MLP weights had moved and 0% of the codes the forward used had changed.
Steps/Code to reproduce bug
import torch
import modelopt.torch.quantization as mtq
IQ2 = {"num_bits": "iq2_xxs", "block_sizes": {-1: 256}, "backend": "ggml"}
def run(make_opt):
torch.manual_seed(0)
model = torch.nn.Sequential(torch.nn.Linear(512, 512, bias=False)).cuda()
cfg = {"quant_cfg": [{"quantizer_name": "*", "enable": False},
{"quantizer_name": "*weight_quantizer", "cfg": IQ2}],
"algorithm": "max"}
x = torch.randn(64, 512, device="cuda")
model = mtq.quantize(model, cfg, lambda m: m(x))
lin = model[0]
q = lin.weight_quantizer
opt = make_opt(lin.weight)
model(x).square().mean().backward() # forward encodes and caches the payload
before = q._quantizer_cache.packed_weights.clone()
v0 = lin.weight._version
opt.step()
with torch.no_grad():
model(x) # the next forward
used = q._quantizer_cache.packed_weights.clone()
q._quantizer_cache = None
model(x)
fresh = q._quantizer_cache.packed_weights.clone() # a fresh encode of the updated weight
changed = lambda a: (a != before).any(-1).float().mean().item()
print(f"{type(opt).__name__}: weight version {v0} -> {lin.weight._version}; blocks changed: "
f"next forward {changed(used):.1%}, fresh encode {changed(fresh):.1%}")
import bitsandbytes as bnb
run(lambda p: torch.optim.AdamW([p], lr=1e-2))
run(lambda p: bnb.optim.AdamW8bit([p], lr=1e-2))
Output:
AdamW: weight version 1 -> 3; blocks changed: next forward 100.0%, fresh encode 100.0%
AdamW8bit: weight version 1 -> 1; blocks changed: next forward 0.0%, fresh encode 100.0%
Expected behavior
The forward after an optimizer step uses the codes of the updated weight, whichever optimizer made the update.
My workaround is setting weight_quantizer._quantizer_cache = None on every quantized module after each step. Possible fixes: clear the cache from an optimizer step hook in the QAT/QAD trainers, or have the cache check notice changes that bypass the version counter.
Who can help?
- @cjluo-nv (GGML formats)
System information
- Container used (if applicable): none
- OS: Ubuntu 24.04.4 LTS
- CPU architecture: x86_64
- GPU name: Tesla V100-PCIE-32GB
- GPU memory size: 32 GB
- Number of GPUs: 1
- Library versions:
- Python: 3.12.3
- ModelOpt version or commit hash: 0.48.0rc0 (the cache key is unchanged on main at 0df6ab3a2d)
- CUDA: 12.6 (PyTorch build), driver 580.173.02
- PyTorch: 2.8.0+cu126
- Transformers: 5.18.0
- bitsandbytes: 0.50.2
- TensorRT-LLM / ONNXRuntime / TensorRT: not used
- Any other details that may help: Found while reproducing the Nemotron 3.5 Bolt 2-bit recipe at small scale on a V100 (the GGML CUDA extension builds for sm_70 and its IQ2_XXS GPU tests pass). I worked on this with an AI assistant (Claude); the reproduction above was run and checked on my machine.
- Ngôn ngữ chính
- Python
- Star
- 5.4k
- Fork
- 767
- Merge trung bình
- 3 ngày 11 giờ
- Pull request đã merge (30 ngày)
- 203
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của NVIDIA/Model-Optimizer
-
[Bug] modelopt-mcp: all launcher subprocesses hang when the server runs as a stdio MCP server — missing stdin=DEVNULLCó thể đã có người làm @ChenhanYu đã nhận 20 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
NVIDIA/Model-Optimizer#2551 · 1 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 2 ngày
-
Puzzletron Progress 6/8 (calculating one block scores) takes 10 to 20 times more than in tutorialCó thể làm lại được @tejasng053 đã nhận 27 ngày trước và không có pull request nào đang mở. Đang mởbug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
NVIDIA/Model-Optimizer#1667 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
feature request
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 25/100
NVIDIA/Model-Optimizer#2865 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
fp4_kernel_hopper.py uses tl.make_block_ptr, which is removed in Triton main (breaks NVFP4 fake quant)Có thể đã có người làm @kinjalpatel27 đã nhận 7 ngày trước. Đang mở
Độ khó 3/5 Nửa ngày Mức phù hợp với người mới 72/100
NVIDIA/Model-Optimizer#2810 · 1 người được giao ·
Maintainer thường phản hồi trong vòng 2 ngày
-
test_cast_fp4_impl_gpu_mem is flaky on Jetson Thor: mem_get_info reports system-wide memory on integrated GPUsCó thể đã có người làm @LinCanNerd đã nhận 15 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 25/100
NVIDIA/Model-Optimizer#2706 ·
Maintainer thường phản hồi trong vòng 2 ngày
Tất cả issue của NVIDIA/Model-Optimizer
Issue tương tự
-
Python Volcengine provider returns failures instead of raising themCó thể đã có người làm @Ricardo-M-L đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 79/100
agent-infra/sandbox#248 ·
-
Textractor leaves empty temporary files when remote retrieval failsCó thể đã có người làm @HuaTNA đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
Maintainer thường phản hồi trong vòng 1 ngày
-
test(bm_generation): pin the learning parameters and CPU sync shortcutCó thể đã có người làm @iloveeyjafjalla đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
qboson/kaiwu-pytorch-plugin#1341 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
elder-plinius/V3R1T4S#625 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
foxglove/mcap#1907 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày