AOT compile cache key ignores the LoRA module tree, loading an incompatible artifact (`KeyError: ‘weight’`)

在开启 VLLM_USE_AOT_COMPILE=1 时,AOT 编译缓存的 key 只由 vLLM 配置、环境因子、inductor 因子和 forward 源码哈希组成,不包含 LoRA 包裹后的模块树;因此当同一份配置下“被包裹的层集合”发生变化时,会命中旧缓存并加载参数名不匹配的计算图,最终

快速结论:在开启 VLLM_USE_AOT_COMPILE=1 时,AOT 编译缓存的 key 只由 vLLM 配置、环境因子、inductor 因子和 forward 源码哈希组成,不包含 LoRA 包裹后的模块树;因此当同一份配置下“被包裹的层集合”发生变化时,会命中旧缓存并加载参数名不匹配的计算图,最终在 profile_run 阶段抛出 KeyError: 'weight'。优先排查是否复用了同一个 AOT cache 根目录、以及 LoRA 包裹范围是否与生成该缓存时不同。

适用环境:Issue 中明确给出:vLLM 0.26.0、PyTorch 2.11.0+cu130、VLLM_USE_AOT_COMPILE=1。原文未说明操作系统、Python 版本、CUDA 版本和显卡型号,此处不补写。

最快修复方案:暂无确认的一步修复方案。Issue 讨论中提出的方向是把 LoRA 包裹模块路径集合纳入 cache key(可优先尝试),以及在加载失败时回退到重新编译作为第二道防线;讨论停留在建议阶段,未见合并验证结果。

注意事项:该问题在正常配置下不一定稳定复现。报告者说明其触发前提是两次运行之间修改了 monkey-patched 的包裹逻辑,使某些层(如 kv_a_proj_with_mqa)从“不包裹”变为“包裹”。在 TP=1 bf16 场景下,加载的旧图恰好仍能运行;在 fp8 场景下则是因其他加载错误而触发安全重编译。因此“删缓存后重启”只是规避手段,不是根因修复。

问题场景

在 vLLM 中开启 VLLM_USE_AOT_COMPILE=1 进行 AOT 编译缓存复用,同时启用 LoRA。如果同一个 cache 根目录下先后存在两次运行,且两次运行的 VllmConfig 相同、但 LoRA 包裹的模块集合不同,vLLM 会计算出相同的 AOT cache key,从而加载由另一次运行生成的计算图。Issue 复现方式是通过 hook vllm.lora.model_manager.from_layer,在一次运行中故意不包裹某一层,另一次运行中正常包裹,观察两次运行落入同一个 .../torch_aot_compile/<同一哈希>/rank_0_0/model 路径。

报错原文

KeyError: 'weight'

cached AOT artifact: AOT compile cache key ignores the LoRA module tree, loading an incompatible artifact (`KeyError: 'weight'`)
load path: decorators.py:311 - Directly load AOT compilation from path ...

报错栈终止于编译后的函数内部,且不包含具体模块名,难以定位到是被 LoRA 包裹的哪一层。

原因分析

根据 Issue 讨论中的静态确认,decorators.py 中的 AOT cache key 由 aot_compile_hash_factors(vllm_config) 与 _model_hash_key(self.forward) 组合而成,包括环境因子、vllm_config.compute_hash()、可选的 inductor 因子以及 forward 源码哈希,这些因子都不感知模块树结构。而 LoRA 包裹会改变参数名:某层被包裹后,其权重从 <prefix>.weight 迁移为 <prefix>.base_layer.weight。当旧图仍按 weight 查找参数时就会抛 KeyError: 'weight'。

加载路径在 evaluate_guards 关闭时会应用 disable_guard_check(),因此没有守卫发现这种参数名不匹配,错误被推迟到 profile_run 才暴露。讨论中同时指出,现有加载失败回退路径(Compiling model again due to a load failure ...)能处理其他加载错误,但缺少参数名不匹配时没有进入该回退。

环境排查

  • 确认 vLLM 版本为 0.26.0,PyTorch 为 2.11.0+cu130(Issue 确认组合)。
  • 确认 VLLM_USE_AOT_COMPILE=1 已开启,并记录 AOT 编译缓存的根目录位置。
  • 确认两次运行之间 LoRA 包裹的模块集合是否发生变化(例如 hook 或自定义逻辑改动导致某些层被包裹/不被包裹)。
  • 确认两次运行是否复用了同一个 cache 根目录;如果指向同一哈希目录,即为本问题特征。
  • 确认是否处于 fp8 等量化路径:Issue 提到 fp8 场景下曾出现 Kernel index 1 not found in id_to_kernel,这会干扰对根因的判断。
  • 确认 evaluate_guards 的开关状态,它是错误被隐藏、未在加载阶段及早报出的条件之一。

解决步骤

  1. 先确认是否属于本问题:在开启 VLLM_USE_AOT_COMPILE=1 的情况下,分别以“某些层被 LoRA 包裹”和“不被包裹”运行同一模型、使用同一 cache 根目录,观察两者是否落在同一 torch_aot_compile/<hash>/rank_0_0/model 目录。若哈希相同而模块树不同,即可确认 cache key 冲突。
  2. 规避处理(可优先尝试):在修改 LoRA 包裹逻辑后,清理对应模型的 AOT 编译缓存目录,或改用全新的 cache 根目录,使被包裹与未被包裹的运行不共享缓存条目,从而强制重新编译。
  3. 关注上游修复:讨论中计划将排序后的 LoRA 包裹模块路径集合(对 named_modules 做 BaseLayerWithLoRA 类型判断)作为新因子追加到 decorators.py 的 forward 哈希旁;同一配置 + 同一包裹状态得到同一 key,包裹状态变化则进入新目录重新编译。在补丁可用前,步骤 2 是主要规避手段。
  4. 若希望错误更易定位:讨论中建议在加载时校验产物引用的参数名是否仍可解析,无法解析时回退到重新编译,而不是直接抛出 KeyError;该改动属于讨论建议,尚未确认落地。

验证方法

完成规避或升级后,重复步骤 1 的对比运行:两次运行的 AOT 缓存哈希应不再相同,各自生成独立目录并完成编译;再次执行触发 profile_run 的加载流程,确认不再出现裸 KeyError: 'weight'。同时留意日志中是否出现 Directly load AOT compilation from path ... 后紧接失败的情况,正常情况应看到重新编译提示而非参数查找错误。

参考来源

vllm-project/vllm #55383

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 33292

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注