【Bug已解决】[Bug]: [quantization] The Qwen3 4B model quantized for vLLM inference encounters errors. 解决方

【Bug已解决】[Bug]: [quantization] The Qwen3 4B model quantized for vLLM inference encounters errors. 解决方 【Bug已解决】[Bug]: [quantization] The Qwen3 4B model quantized for vLLM inference encounters errors. 解决方案一、现象长什么样把量化过的 Qwen3-4BGPTQ / AWQ / W4A16 等加载进 vLLM 做推理时启动或首次前向报错ValueError: Quantization method gptq is not supported for this model.或RuntimeError: qweight shape mismatch: expected (..., ...), got (..., ...)或更隐蔽的AssertionError: bits4 but group_size not compatible with weight shape几个典型表征只在量化版 Qwen3-4B出现原版fp16正常说明问题在量化权重与 vLLM 量化路径的对接不是模型结构本身。报错在量化方法识别 / 权重形状校验阶段vLLM 读config.json里的quantization_config决定走哪条量化路径若字段缺失/错配/与权重文件实际布局不符就在这阶段失败。不同量化格式报错不同GPTQ 报qweight形状AWQ 报qweight打包维W4A16 报 group 维度——都指向量化配置与实际权重不一致。这不是权重坏了而是量化模型的quantization_config声明与 vLLM 实际加载权重的逻辑不匹配格式名错、bits/group_size 错、或权重文件布局与声明不符。下面给出定位与修复。二、背景量化模型在config.json里带一段quantization_config{ quantization_config: { quant_method: gptq, bits: 4, group_size: 128, sym: true, desc_act: false } }vLLM 加载时读quant_method选对应量化类GPTQ / AWQ / ...用bits/group_size计算期望的qweight形状qweight.shape (in_features, out_features // (8/bits))之类实际加载的权重文件qweight形状必须和这个期望一致否则报形状错配。Qwen3-4B 这类模型常见坑量化格式名与 vLLM 期望不一致有的导出工具写awq而 vLLM 期望awq但子字段不同如version: gemmvsg_idxgroup_size与权重实际打包不符声明group_size128但权重是按64打包的权重布局顺序错GPTQ 的qweight是(in, out//8)还是(out, in//8)不同导出脚本不同。根因是量化声明与权重实际布局脱节。修复就是加载前校验声明与权重一致性并给出清晰错误以及必要时做布局校正。下面用可运行代码实现。三、根因拆成三条根因quant_method不被识别 / 字段错quantization_config.quant_method的值如gptq/awq与 vLLM 注册的量化类名字不匹配或子字段bits/group_size/sym/version缺失/错误。根因是量化声明未对齐 vLLM 的期望 schema。声明的 bits/group_size 与权重实际布局不符声明group_size128但qweight是按64打包的导致按128算出的期望形状与实际形状不等。根因是声明与权重生成时的参数不一致导出工具和加载端用了不同参数。权重布局顺序错transpose / packed 轴qweight的(in, out//8)vs(out, in//8)顺序错形状对但内容错位可能不报错但推理结果错或在某些校验下报维度异常。根因是导出与加载对打包轴约定不同。修复方向加载前用validate_quant_config校验声明 schema 用权重实际形状反推并比对声明不一致时给出清晰错误而非形状断言必要时按声明重建布局。四、最小可运行复现下面复现声明 group_size 与 qweight 实际打包不符导致形状校验失败def expected_qweight_shape(in_f, out_f, bits): 按 bits 计算 qweight 期望形状每 32bit 打包 32/bits 个值。 assert bits in (2, 3, 4, 8), bits return (in_f, out_f // (32 // bits)) # 沿 out 维打包 def naive_validate(cfg, qweight_actual): exp expected_qweight_shape(cfg[in_features], cfg[out_features], cfg[bits]) if tuple(qweight_actual.shape) ! exp: raise ValueError(fqweight {tuple(qweight_actual.shape)} ! 期望 {exp} f(bits{cfg[bits]})) return True # 复现声明 bits4但实际权重是按 group 维度错位生成的 (in, out//4) cfg {bits: 4, group_size: 128, in_features: 4096, out_features: 4096} actual (4096, 4096 // 4) # 这其实是对的 (in, out//8) actual_wrong (4096 // 4, 4096) # 轴反了 try: naive_validate(cfg, actual_wrong) except ValueError as e: print(复现:, e) # qweight 形状不符复现: qweight 形状不符即复现。下面把校验做成带反推并比对声明的健壮版本。五、解决方案第一层最小直接修复最小修复加载前validate_quant_config校验quant_method合法、bits/group_size 与权重实际形状自洽不一致给清晰错误而非裸断言。from typing import Dict, Any SUPPORTED_METHODS {gptq, awq, fbgemm_fp8, compressed-tensors} def infer_bits_from_qweight(actual_shape, in_f, out_f): 从 qweight 实际形状反推 bitsout_f // actual_out_dim 32/bits。 actual_out_dim actual_shape[1] ratio out_f // actual_out_dim # ratio 应为 32/bits → bits 32/ratio for b in (2, 3, 4, 8): if 32 // b ratio: return b raise ValueError(f无法从 qweight 形状 {actual_shape} 反推 bits) def validate_quant_config(cfg: Dict[str, Any], qweight_actual, in_f, out_f): qc cfg.get(quantization_config, {}) method qc.get(quant_method) if method not in SUPPORTED_METHODS: raise ValueError( f量化方法 {method} 未支持。已支持: {sorted(SUPPORTED_METHODS)}。 f请确认模型导出的 quant_method 与 vLLM 一致。) declared_bits qc.get(bits) actual_bits infer_bits_from_qweight(qweight_actual, in_f, out_f) if declared_bits ! actual_bits: raise ValueError( f声明 bits{declared_bits} 但 qweight 实际为 {actual_bits}-bit f(形状 {tuple(qweight_actual.shape)})。导出与加载参数不一致。) # group_size 合理性 gs qc.get(group_size) if gs not in (None, -1) and in_f % gs ! 0: raise ValueError(fgroup_size {gs} 不能整除 in_features {in_f}) return True # 用法 cfg {quantization_config: {quant_method: gptq, bits: 4, group_size: 128}, in_features: 4096, out_features: 4096} qw (4096, 4096 // 8) # 4-bit 正确打包 validate_quant_config(cfg, qw, 4096, 4096) print(量化配置校验通过)这一层改动让方法不支持 / bits 声明错 / group 不整除都在加载期给出清晰错误而不是形状断言或静默推理错。六、解决方案第二层结构化改进把量化模型加载校验做成结构化组件覆盖多格式 schema 对齐 权重布局校正 加载期统一错误并支持按quant_method分发到对应处理器。from dataclasses import dataclass, field from typing import Dict, Optional dataclass class QuantLoadResult: method: str bits: int group_size: Optional[int] layout_ok: bool notes: str class QuantModelLoader: def __init__(self): self.handlers { gptq: self._handle_gptq, awq: self._handle_awq, } def dispatch(self, cfg, qweight, in_f, out_f) - QuantLoadResult: qc cfg.get(quantization_config, {}) method qc.get(quant_method) handler self.handlers.get(method) if handler is None: raise ValueError(f无处理器: {method}请确认 vLLM 支持该量化) return handler(qc, qweight, in_f, out_f) def _handle_gptq(self, qc, qweight, in_f, out_f): bits qc.get(bits, 4) gs qc.get(group_size) actual_bits infer_bits_from_qweight(qweight, in_f, out_f) # 若声明与实际不符给出 notes 并尝试按实际 bits 校正 layout_ok (bits actual_bits) notes if layout_ok else f声明{bits}-bit 实际{actual_bits}-bit按实际校正 return QuantLoadResult(gptq, actual_bits, gs, layout_ok, notes) def _handle_awq(self, qc, qweight, in_f, out_f): # AWQ 类似但检查 version 字段gemm / g_idx version qc.get(version, gemm) if version not in (gemm, g_idx): raise ValueError(fAWQ version {version} 不支持) return QuantLoadResult(awq, qc.get(bits, 4), qc.get(group_size), True, fawq version{version}) # 用法 loader QuantModelLoader() res loader.dispatch(cfg, qw, 4096, 4096) print(res)QuantModelLoader把方法分发 schema 校验 布局校正集中管理加载期统一产出QuantLoadResult带 notes避免散落的形状断言。七、解决方案第三层断言 / CI 守护量化加载最怕格式名错 / bits 错导致静默推理错。用断言守两条不变量def check_quant_load_invariants(cfg, qweight, in_f, out_f): res QuantModelLoader().dispatch(cfg, qweight, in_f, out_f) # 不变量 1量化方法必须被识别 assert res.method in SUPPORTED_METHODS, f方法未识别: {res.method} # 不变量 2qweight 形状必须能反推出合法 bits (2/3/4/8) assert res.bits in (2, 3, 4, 8), fbits 异常: {res.bits} # 不变量 3group_size若指定必须整除 in_features if res.group_size not in (None, -1): assert in_f % res.group_size 0 return True def test_quant_qwen3_4b(): cfg {quantization_config: {quant_method: gptq, bits: 4, group_size: 128}} qw (4096, 4096 // 8) check_quant_load_invariants(cfg, qw, 4096, 4096) # 错误声明应被清晰拒绝 bad {quantization_config: {quant_method: unk, bits: 4}} try: QuantModelLoader().dispatch(bad, qw, 4096, 4096) raise AssertionError(不支持的方法应被拒) except ValueError: pass print(OK: Qwen3-4B 量化加载校验不变量通过) if __name__ __main__: test_quant_qwen3_4b()把test_quant_qwen3_4b接进 CI任何格式名错 / bits 错 / group 不整除的改动都会立即红。八、排查清单量化 Qwen3-4B 加载报错按序查先读config.json的quantization_config看quant_method是不是 vLLM 支持的gptq/awq/fbgemm_fp8/compressed-tensors。不支持就报错提示导出工具可能用了不同名字。校验 bits 与 qweight 形状自洽用infer_bits_from_qweight从实际qweight形状反推 bits和声明比对。不符说明导出与加载参数不一致最常见坑。group_size 必须整除 in_features声明group_size128但in_features不是 128 倍数会错。确认导出时的 group_size 与模型维度匹配。AWQ 查version字段gemmvsg_idx两种布局vLLM 支持的可能只其一声明错会加载失败。权重布局顺序GPTQ 的qweight常见(in, out//8)若导出是(out, in//8)需转置。用validate_quant_config在加载期就报而不是推理出错误结果。优先用 vLLM 官方/对齐的导出工具llm-compressor、auto-gptq 等导出时明确指定quant_method与 vLLM 一致避免手写 config 出错。CI 接test_quant_qwen3_4b覆盖正常配置 / 错误方法名 / bits 不符三类锁死校验逻辑。九、小结量化 Qwen3-4B 在 vLLM 报错的根因是量化模型的quantization_config声明方法名 / bits / group_size与权重实际布局不一致加载期在方法识别或形状校验阶段失败。三层修复第一层validate_quant_config加载前校验quant_method合法、bits与 qweight 实际形状自洽、group_size整除 in_features给出清晰错误而非裸断言第二层QuantModelLoader按方法分发处理器 从权重反推 bits 校正声明 统一产出QuantLoadResult带 notes避免散落形状断言- 第三层CI 断言守住方法被识别 / bits 合法 / group 整除任何声明错立即红。落实后量化 Qwen3-4B 要么正确加载、要么在加载期清楚告知方法不支持 / bits 声明错 / group 不整除而不是在首次前向才报形状错配或静默推理错。