【Bug已解决】[Bug]: Loading Qwen GPTQ MoE model failed: IndexError: index 0 is out of bounds for dimensio

发布时间:2026/7/28 13:38:30

【Bug已解决】[Bug]: Loading Qwen GPTQ MoE model failed: IndexError: index 0 is out of bounds for dimensio 【Bug已解决】[Bug]: Loading Qwen GPTQ MoE model failed: IndexError: index 0 is out of bounds for dimension 0 with size 0 解决方案一、现象长什么样加载Qwen 的 GPTQ MoE混合专家模型时权重加载阶段抛IndexError进程崩溃。典型日志IndexError: index 0 is out of bounds for dimension 0 with size 0或者更笼统[Bug]: Loading Qwen GPTQ MoE model failed: IndexError: index 0 is out of bounds for dimension 0 with size 0几个特征帮你判断是不是同一个坑报错是IndexError: index 0 is out of bounds for dimension 0 with size 0意思是「对一个长度为 0 的维度取下标 0」——即某个本应为非空的集合/张量实际是空的size 0。错误发生在GPTQ MoE 模型加载阶段不是 forward、不是普通推理。错误里的「dimension 0 with size 0」指向某个列表/张量的第 0 维是空的常见是「专家列表为空」「某层待处理的模块列表为空」「或某投影的张量是空张量」。只在「GPTQ MoE」模型出现普通 GPTQ非 MoE或普通 MoE非 GPTQ可能正常——说明是「GPTQ 量化 MoE 专家」两者叠加时的特殊处理有 bug。用strictFalse强行跳过进程不崩但推理结果错——说明是加载逻辑试图访问一个空集合。二、背景Qwen 的 GPTQ MoE 模型加载时要同时处理「GPTQ 量化」和「MoE 专家」两件复杂事。错误index 0 is out of bounds for dimension 0 with size 0指向「对一个空序列取第 0 个元素」。在 MoE 加载上下文里最可能的几个空集合1. 专家列表为空加载器遍历experts时期望拿到一个非空专家列表如experts[0]、experts[1]…。如果模型 config 里num_experts被读成 0、或专家模块没被正确注册如nn.ModuleList为空代码执行experts[0]→ 空列表取下标 0 → IndexError。2. 待量化/待加载的模块列表为空GPTQ 加载时框架会先「收集需要反量化的线性层列表」再逐个处理。如果该列表因某种条件如模块名过滤规则错误、或 GPTQ 元数据解析失败变成空代码执行modules[0]或for m in modules后某处取modules[0]→ 空 → IndexError。3. 某投影张量为空张量GPTQ 的qweight若解析出错变成numel()0的张量后续qweight[0]或 reshape 到(0, ...)后取维度 0 → IndexError。4. GPTQ 元数据解析失败导致分组为空GPTQ 的g_idx/groups若解析成空按组循环时groups[0]越界。5. config 字段读错num_experts/num_local_experts等字段名在 Qwen 不同子版本里可能不同如moe_intermediate_size、专家相关字段读错导致专家数算成 0 → 专家列表空。6. 条件分支漏注册专家加载器对某类层如共享专家 / 路由专家有特殊处理分支判断错误导致某类专家完全没被加入列表后续访问该空列表 → IndexError。核心加载 MoE GPTQ 时某个「本应非空」的集合专家列表/待处理模块/张量维度因为 config 读错、过滤规则错、或注册遗漏而变成空的size 0代码对它取下标 0 即 IndexError。三、根因根因一句话加载 Qwen 的 GPTQ MoE 模型时某个本应非空的集合专家列表experts、待反量化模块列表、或某投影张量的第 0 维因 config 字段读错num_experts算成 0、模块名过滤规则错误、或专家注册遗漏而变成「空size 0」加载逻辑对该空集合执行xxx[0]或按第 0 维访问触发IndexError: index 0 is out of bounds for dimension 0 with size 0。具体成因num_experts读成 0config 字段名差异Qwen 子版本使专家数算成 0 → 专家列表空。待处理模块列表空GPTQ 反量化前的模块收集因过滤规则错变成空 →modules[0]越界。张量 numel 为 0qweight解析失败成空张量 → reshape 后第 0 维取 0。GPTQ 分组空g_idx/groups解析空 → 按组循环groups[0]越界。专家注册遗漏分支判断错使某类专家没加入列表 → 访问空列表。缺少空集合守卫加载逻辑假设集合非空直接取下标 0无if not list检查。核心矛盾加载逻辑假设「专家/模块/张量集合非空」但 GPTQMoE 叠加时的 config/过滤/注册处理有 bug 让集合变空且无空守卫于是对空集合取下标 0 即 IndexError。四、最小可运行复现下面用纯 Python 模拟「专家列表因 num_experts0 为空加载逻辑取 experts[0] → IndexError」# reproduce_moe_empty.py # 复现num_experts 读成 0 - 专家列表空 - experts[0] IndexError def build_experts_buggy(num_experts): experts list(range(num_experts)) # num_experts0 - 空列表 return experts[0] # 空列表取 [0] - IndexError def build_experts_fixed(num_experts, must_be): if num_experts 0: # 空守卫: 不盲目取下标, 给出清晰错误 raise ValueError(f专家数为 0, 检查 config 的 num_experts/num_local_experts 字段) return list(range(num_experts))[0] if __name__ __main__: try: build_experts_buggy(0) except IndexError as e: print(复现成功:, e) try: build_experts_fixed(0, 8) except ValueError as e: print(修复(清晰错误):, e)运行python reproduce_moe_empty.py会看到空专家列表取[0]崩修复版先守卫空集并给清晰错误。五、解决方案第一层最小直接修复最小修复加载 MoE GPTQ 时对任何「本应非空」的集合专家列表、待处理模块、张量维度先做空守卫——为空时抛出清晰错误指出是num_experts还是模块收集问题而不是盲目取下标 0。# fix_layer1_moe.py def resolve_num_experts(config: dict) - int: 兼容 Qwen 各子版本的专家数字段。 for key in (num_experts, num_local_experts, n_routed_experts, moe_num_experts): if key in config and config[key]: return config[key] raise ValueError(config 中找不到有效的专家数字段(num_experts 等)) def build_experts(num_experts: int) - list: if num_experts 0: raise ValueError(fnum_experts{num_experts} 无效, 专家列表将为空 - IndexError) return list(range(num_experts)) if __name__ __main__: cfg {num_local_experts: 8} # Qwen 子版本字段名 n resolve_num_experts(cfg) print(专家数:, n, 专家列表:, build_experts(n))这一层把「空列表取下标 0 崩」变成「先解析正确专家数 空守卫」并兼容 Qwen 不同子版本字段名避免 IndexError。六、解决方案第二层结构性改进把「MoE GPTQ 加载的集合非空校验」做成模块覆盖专家列表、待反量化模块、张量维度统一在加载前校验# fix_layer2_loader.py from dataclasses import dataclass, field dataclass class MoeGptqLoader: config: dict def num_experts(self) - int: for k in (num_experts, num_local_experts, n_routed_experts): if self.config.get(k): return self.config[k] return 0 def collect_quant_modules(self, module_names: list) - list: # 真实场景: 按命名规则收集需反量化的线性层 collected [m for m in module_names if proj in m] if not collected: raise ValueError( 待反量化的模块列表为空, 检查 GPTQ 元数据/模块名过滤规则) return collected def check_tensors(self, weights: dict): bad [k for k, v in weights.items() if getattr(v, numel, lambda: 1)() 0] if bad: raise ValueError(f以下权重为空张量(会导致 size-0 维度): {bad}) def pre_load_guard(self, module_names, weights): n self.num_experts() assert n 0, f专家数 {n} 无效, 专家列表将空 - IndexError self.collect_quant_modules(module_names) self.check_tensors(weights) return True if __name__ __main__: loader MoeGptqLoader({num_local_experts: 8}) print(专家数:, loader.num_experts()) print(量化模块:, loader.collect_quant_modules([q_proj, k_proj]))这样换模型/换 config 字段名时加载前统一校验专家数/模块列表/张量非空空集合在「取下标 0 前」就被清晰报出。七、解决方案第三层断言 / CI 守护把「MoE GPTQ 加载空集合校验」钉进断言和 CI# fix_layer3_guard.py # ---- pytest 用例进 CI ---- def test_num_experts_field_compat(): from fix_layer2_loader import MoeGptqLoader assert MoeGptqLoader({num_local_experts: 8}).num_experts() 8 assert MoeGptqLoader({num_experts: 16}).num_experts() 16 def test_zero_experts_raises(): from fix_layer2_loader import MoeGptqLoader try: MoeGptqLoader({}).pre_load_guard([q_proj], {}) assert False except AssertionError: pass def test_empty_module_list_raises(): from fix_layer2_loader import MoeGptqLoader loader MoeGptqLoader({num_local_experts: 8}) try: loader.collect_quant_modules([]) assert False except ValueError: pass def test_empty_tensor_caught(): import torch from fix_layer2_loader import MoeGptqLoader loader MoeGptqLoader({num_local_experts: 8}) try: loader.check_tensors({w: torch.empty(0)}) assert False except ValueError: pass再加加载前断言def assert_moe_loadable(loader: MoeGptqLoader, module_names, weights): loader.pre_load_guard(module_names, weights) # 内部已校验非空八、排查清单Qwen GPTQ MoE 加载报IndexError: index 0 ... size 0按序查先确认是空集合取下标错误说dimension 0 with size 0是某集合/张量为空。查 num_expertsconfig 里专家数是否被读成 0注意 Qwen 子版本字段名num_local_experts等。兼容字段名用多候选 key 解析专家数别只认num_experts。查待量化模块列表GPTQ 反量化前的模块收集是否因过滤规则变空。查权重是否空张量qweight等是否解析成numel()0导致 size-0 维度。查 GPTQ 分组g_idx/groups是否解析空按组循环越界。查专家注册分支判断是否漏注册某类专家使列表空。加空守卫任何xxx[0]/ 按维度 0 访问前先if not collection检查。别用 strictFalse 蒙混强行跳过让权重没加载推理错。最后才动模型优先在加载校验层修解析专家数空集守卫不要为绕开去改模型。九、小结Qwen GPTQ MoE 加载报IndexError: index 0 is out of bounds for dimension 0 with size 0根子是加载时某个本应非空的集合专家列表experts、待反量化模块、或某投影张量第 0 维因 config 字段读错num_experts算成 0、模块名过滤错误、或专家注册遗漏而变成空的size 0加载逻辑对它执行xxx[0]或按第 0 维访问触发 IndexError。修复三层第一层用多候选字段名正确解析专家数 对空集合做守卫并给清晰错误第二层抽MoeGptqLoader统一在加载前校验专家数/模块列表/张量非空第三层用 pytest 把「专家数字段兼容」「零专家报错」「空模块列表报错」「空张量捕获」钉进 CI加载前断言。核心认识——MoE 加载的任何「集合/张量维度」都必须假设可能为空访问下标 0 前必须显式守卫尤其 GPTQMoE 叠加时config 字段名跨子版本不一致极易让专家数算成 0必须多候选解析 空集校验绝不能盲目experts[0]。

相关新闻