apply_ti constructs nn.Embeddings and installs skip_torch_weight_init without MODEL_LOAD_LOCK

该报错通常出现在 InvokeAI 使用文本反演(Textual Inversion / TI)嵌入生成图片时, ModelPatcher.apply_ti 在没有持有 MODEL_LOAD_LOCK 的情况下构建 nn.Embedding 并安装全局 skip_torch_weight_init

快速结论:该报错通常出现在 InvokeAI 使用文本反演(Textual Inversion / TI)嵌入生成图片时,ModelPatcher.apply_ti 在没有持有 MODEL_LOAD_LOCK 的情况下构建 nn.Embedding 并安装全局 skip_torch_weight_init 补丁,与模型加载线程发生竞争。优先排查:是否在 TI 生成的同时发生模型加载/缓存加载,以及是否出现 Cannot copy out of meta tensor 或权重未初始化的异常。

适用环境:Issue 确认涉及 InvokeAI 的 Python 后端(invokeai/backend/model_patcher.py、invokeai/backend/model_manager/load/、invokeai/app/invocations/compel.py),以及 Transformers 的 resize_token_embeddings 与 Accelerate 的 init_empty_weights。Issue 讨论中的候选修复验证使用了小型 CPU CLIP 模型;CUDA/MPS 与完整生成行为未被验证,其余具体版本号未在讨论中给出。

最快修复方案:暂无确认的一步修复方案。讨论中验证的候选是 fork 提交 007064ae95(基于上游 2395393f66),将原来未加锁的嵌入调整拆为三个短写锁范围(padding、增长含 skip-init 上下文、teardown),但该提交是经测试的 fork 候选,并非上游 PR 或维护者已接受的修复。

注意事项:该候选修复不覆盖 tokenizer 工作与 yield 上下文期间的锁持有,也未改变另外两处初始化行为;测试覆盖了 CPU 事件竞争与异常路径,但未验证 CUDA/MPS 和完整生成流程,也没有性能结论。若使用上游版本,该问题属于并发竞争,重试可能暂时绕过报错,但不能消除全局 skip_torch_weight_init 泄漏风险。

问题场景

用户在 InvokeAI 中运行 SD1/SDXL 文生图或图生图,且生成请求包含文本反演(TI)嵌入时,ModelPatcher.apply_ti 会在 prompt 路径上执行嵌入构建与 resize。如果同一进程内恰好有模型正在加载(例如触发缓存未命中或切换模型),两个线程会同时操作 torch 模块构造与进程级 monkey-patch,从而触发本问题。

报错原文

apply_ti constructs nn.Embeddings and installs skip_torch_weight_init without MODEL_LOAD_LOCK
Cannot copy out of meta tensor; no data!

原因分析

最可能的原因是锁缺失导致的跨线程竞争。apply_ti 中调用 resize_token_embeddings 构建 nn.Embedding,但没有持有 MODEL_LOAD_LOCK。若此时另一线程正在 accelerate.init_empty_weights() 中运行,后者会在进程范围内替换 register_parameter 将参数路由到 meta,导致嵌入权重落在 meta 设备上,随后赋值时报 Cannot copy out of meta tensor; no data!。此外,skip_torch_weight_init 的保存/恢复不是原子操作:apply_ti 先进入并安装 _no_op,加载线程随后把 _no_op 当作“原始函数”保存,两者退出顺序交错后可能把 _no_op 永久恢复,使后续所有 nn.Linear/nn.Conv*/nn.Embedding 跳过权重初始化。以上为 Issue 中的分析性结论,未在所有硬件上复现验证。

环境排查

  • 确认 InvokeAI 版本是否包含 invokeai/backend/model_patcher.py 中未加锁的 resize_token_embeddings 调用(约 72、105、143 行)。
  • 确认 Transformers 版本中 resize_token_embeddings 在 new_num_tokens=None 时是否仍会进行 padding 构造。
  • 确认 Accelerate 的 init_empty_weights() 行为是否会在加载期间全局替换 register_parameter。
  • 确认复现时是否有并发模型加载:切换模型、首次加载、缓存未命中都可能触发。
  • 确认是否使用 SD1/SDXL 并启用文本反演嵌入。
  • 若使用候选 fork 提交,确认 Python 版本满足其依赖;Issue 未给出明确的 Python、CUDA、显卡版本要求。

解决步骤

  1. 先确认上游版本是否已修复该 Issue。若仍使用旧版本,可优先尝试避免在 TI 生成的同时加载或切换模型,以降低触发竞争的概率。
  2. 若需要代码层面的处理,可优先尝试讨论中验证的 fork 候选提交 007064ae95(基于上游 2395393f66)。该提交将三处 resize 分别纳入短写锁范围:padding、增长(保留原有 skip-init 上下文)和 teardown,并在调用方检查了非可重入加锁问题。
  3. 应用候选后,按讨论中给出的测试范围运行回归:pytest tests/backend/test_model_patcher.py tests/backend/patches tests/app/invocations/test_compel.py -q -rs -n 0,并关注 CPU 事件竞争、skip-init 全局状态恢复和异常 teardown 的用例。
  4. 若无法修改代码,建议保留报错日志中的线程/模型加载信息,便于确认是否为并发加载触发,并向上游反馈或等待官方修复。

验证方法

使用包含 TI 嵌入的 SD1/SDXL 生成,同时触发模型加载或切换,确认不再出现 Cannot copy out of meta tensor; no data!,且生成后 skip_torch_weight_init 相关全局初始izer状态能正确恢复。若应用候选提交,可参考讨论中的结果:未修改上游方法时测试为 5 失败/4 通过,修复后相关套件为 257 通过/16 个硬件相关跳过;这些结果基于 CPU CLIP 测试,CUDA/MPS 与完整生成仍需自行验证。

参考来源

invoke-ai/InvokeAI #9627

候选 fork 提交 007064ae95

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 31855

发表回复

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