【Bug已解决】`infer_auto_device_map` does not place submodule buffers on `device_map` when submodule is s
【Bug已解决】infer_auto_device_mapdoes not place submodule buffers ondevice_mapwhen submodule is split 解决方案一、现象长什么样用accelerate的infer_auto_device_map把一个大模型自动切到多张卡上。多数 submodule如每个decoder.layer.{i}会完整落在一张卡上但当一个 submodule 被拆分到两张卡之间时比如前几层在 GPU0、后几层在 GPU1这个被拆分的 submodule 里的buffer如LayerNorm的running_mean/running_var、loss_fn的缓存、位置编码表没有被写进device_map。于是 forward 时报RuntimeError Expected all tensors to be on the same device but found at least two devices cuda0 and cuda1或者更隐蔽地buffer 留在 CPUforward 时和 GPU 上的参数做运算 - device mismatch最小判据触发某个 submodule 跨设备被拆分split across GPUs 现象该 submodule 的 buffer 不在 device_map 中 后果buffer 滞留在原设备CPU/错误卡forward device mismatch 报错与否forward 时才暴露RuntimeError最迷惑的是大多数 submodule 正常只有被拆分到边界的那一个出问题。你盯着device_map看半天发现它的 parameters 都被分配了唯独 buffer 那几行缺席。二、背景infer_auto_device_map的产出是一个device_map字典module.path: cuda:0之类描述每个 module 落到哪张卡。accelerate在load_checkpoint_and_dispatch/dispatch_model时会按这个 map 用cpu_offload hook 把参数和 buffer 搬到对应设备。实现上算法是按 parameter 占用的显存逐步贪心分配从 GPU0 开始塞塞满就切到 GPU1。一个 submodule 若在边界被切断它的参数被分到两张卡。但 buffer 的处理与 parameter 是两套逻辑——buffer 的归属只在该 submodule完整落在某卡时才被记录一旦 submodule 被拆buffer 的归属分支被跳过于是 buffer 既没标 GPU0 也没标 GPU1。结果dispatch_model给 parameter 安了家却漏了 buffer。buffer 保持加载时的默认设备通常是 CPU或模型初始化的设备forward 时和已搬到 GPU 的参数做运算device mismatch 爆发。为什么完整 submodule没事因为完整时 buffer 分支正常执行只有拆分这个分支条件没覆盖 buffer才露出破绽。三、根因抽象成代码示意非照抄源码def infer_auto_device_map(model, ...): device_map {} for name, module in model.named_modules(): params_size sum(p.numel() for p in module.parameters()) if params_size 0: dev _assign_device(name, params_size) # 参数分配 device_map[name] dev # BUGbuffer 只在 module 完整落在一张卡时记录 # 若 module 被拆分参数跨卡buffer 分支被跳过 for bname, buf in module.named_buffers(): if not _module_was_split(name): device_map[f{name}.{bname}] dev # 拆分时漏了 return device_map根因链条infer_auto_device_map先给 parameters 分配设备可能把 submodule 拆到两张卡buffer 的归属逻辑依赖该 submodule 是否完整落在单卡的判断当 submodule 被拆分这个判断为 Falsebuffer 归属分支被跳过buffer 没进入device_mapdispatch_model只搬 device_map 里列出的张量buffer 滞留原设备forward 时参数在 GPU、buffer 在别处 - device mismatch。一句话buffer 的分配逻辑没有覆盖submodule 被拆分这一分支。四、最小可运行复现用纯 Python 模拟拆分 submodule 时 buffer 归属被漏掉# repro_buffer_split.py def infer_map(names, split_at): 模拟 device_map 推断参数分配 buffer 归属。 device_map {} split_names set(names[:split_at]) set(names[split_at:]) for n in names: device_map[n] cuda:0 if n split_at else cuda:1 # buffer 归属只在 module 未被拆分时记录 for n in names: if n in split_names: continue # BUG拆分的 module 漏掉 buffer device_map[n .buffer] device_map[n] return device_map def main(): # 假设 submodule block 的参数被拆到 cuda:0 和 cuda:1 names [block.p0, block.p1, block.p2] split_at 2 dm infer_map(range(3), split_at) has_buf any(k.endswith(buffer) and k.startswith(block) for k in dm) print(device_map 含 block 的 buffer, has_buf) assert not has_buf, 拆分 submodule 的 buffer 被遗漏 - 复现成功 if __name__ __main__: main()运行输出device_map 含 block 的 buffer False拆分后的block没有任何 buffer 进入device_map正是真实 bug 的抽象。五、解决方案第一层最小直接修复最小且必须的一步无论 submodule 是否被拆分都给它的 buffer 显式分配一个设备——通常放在持有该 submodule 最后一块参数的那张卡保证 forward 时 buffer 与大部分参数同设备# fix_layer1.py def fix_buffer_assignment(device_map, module_buffers, owner_of_last_param): for bname in module_buffers: # 拆分的 submodulebuffer 放到持有最后参数的卡保证同设备 device_map[bname] owner_of_last_param return device_map更稳妥地直接在infer_auto_device_map的补丁里把 buffer 归属从是否完整改成只要 module 有任何参数被分配就给它 buffer 也分配# 修复buffer 归属不再依赖 module 是否完整 for bname, buf in module.named_buffers(): owner device_map.get(name) or owner_of_last_param device_map[f{name}.{bname}] owner这一层改动最小补上拆分分支的 buffer 归属。但它用最后参数所在卡作为启发式若 submodule 参数均匀分散、buffer 该跟谁并非显然需第二层兜底。六、解决方案第二层结构性改进把buffer 归属做成确定性的规则buffer 跟随其所在 module 的主设备参数总字节数最多的那张卡并由单一函数统一计算不依赖是否拆分的判断# fix_layer2.py from dataclasses import dataclass from typing import Dict, List dataclass(frozenTrue) class TensorRef: name: str bytes: int device: str # 参数被分配到的设备 class DeviceMapBuilder: def __init__(self): self.params: List[TensorRef] [] self.buffers: List[TensorRef] [] def add_param(self, name, nbytes, device): self.params.append(TensorRef(name, nbytes, device)) def add_buffer(self, name, nbytes): self.buffers.append(TensorRef(name, nbytes, ?)) def primary_device_of(self, module_prefix: str) - str: 该 module 参数字节最多的设备作为 buffer 归属。 by_dev: Dict[str, int] {} for p in self.params: if p.name.startswith(module_prefix): by_dev[p.device] by_dev.get(p.device, 0) p.bytes return max(by_dev, keyby_dev.get) if by_dev else cpu def build(self) - Dict[str, str]: dm {p.name: p.device for p in self.params} for b in self.buffers: prefix b.name.rsplit(., 1)[0] dm[b.name] self.primary_device_of(prefix) # 跟随主设备 return dm要点primary_device_of用参数字节最多的设备决定 buffer 归属对拆分 submodule 也确定build对参数和 buffer 统一处理buffer 不再依赖是否完整分支任何拆分情况都落到明确设备forward 不会再 device mismatch。七、解决方案第三层断言 / CI 守护写 pytest 验证拆分 submodule 的 buffer 必在 device_map 中、且与参数同设备# test_device_map_buffers.py import pytest class FakeModel: def named_modules(self): return [(block, None)] def named_buffers(self, prefix): return [(block.running_mean, 1), (block.running_var, 1)] def infer_split_aware(names, split_at): dm {} for i, n in enumerate(names): dm[n] cuda:0 if i split_at else cuda:1 for n in names: dm[n .running_mean] dm[n] # 修复后总是记录 dm[n .running_var] dm[n] return dm def test_split_buffer_in_map(): names [block.p0, block.p1, block.p2] dm infer_split_aware(names, split_at2) assert block.p0.running_mean in dm assert block.p2.running_var in dm def test_buffer_same_device_as_its_param(): names [block.p0, block.p1, block.p2] dm infer_split_aware(names, split_at2) assert dm[block.p2.running_mean] dm[block.p2] def test_no_cpu_stranded_buffer(): names [block.p0] dm infer_split_aware(names, split_at1) for k in dm: if running in k: assert dm[k] ! cpu, buffer 不应滞留 CPUCI 一旦恢复成拆分时跳过 buffer前两个测试立刻变红。八、排查清单forward 报 device mismatch 且怀疑 buffer 时打印device_map搜索出问题的 submodule看其 buffer 是否在 map 中确认该 submodule 是否被拆分到多卡参数跨cuda:0/cuda:1若 buffer 行缺失命中本 bug按第五 / 六节给拆分 submodule 的 buffer 显式分配主设备用dispatch_model后检查 buffer 实际设备next(m.buffers()).device若用device_mapauto确认infer_auto_device_map版本是否已修复把第七节的 pytest 接进 CI守护拆分 submodule 的 buffer 必在 map。九、小结infer_auto_device_map在 submodule 被拆分到多卡时漏掉了该 submodule 的buffer归属——buffer 的分配逻辑只覆盖了module 完整落在单卡的分支拆分时被跳过。结果 buffer 滞留原设备forward 时与已搬走的参数 device mismatch。三层层级第一层拆分 submodule 的 buffer 显式分配到持有其最后参数 / 主设备的卡第二层用DeviceMapBuilder以参数字节最多的设备统一决定 buffer 归属消除拆分分支第三层pytest 验证拆分 submodule 的 buffer 必在 map 且与参数同设备锁进 CI。核心教训凡是按结构切分资源的算法必须保证被切分单元的每一种成员参数、buffer、状态都有归属任何只覆盖完整单元而漏掉被切分单元的分支都是隐性资源错位。

相关新闻

【计算机毕业设计】基于Vue框架+AI大模型身心健康日志系统

【计算机毕业设计】基于Vue框架+AI大模型身心健康日志系统

1.系统介绍随着社会压力增加,用户对身心健康管理的需求日益增长,传统身心健康管理方式存在交互性差、个性化不足等问题,难以满足用户多元化的情绪疏导、健康记录及社交需求。在此背景下,本研究设计并实现一套基于Vue框架AI大模型的…

2026/11/4 20:28:17 阅读更多 →
【Bug已解决】[Bug]: RNG states from multiple backends (e.g. CUDA + HPU) are saved but only one is restore

【Bug已解决】[Bug]: RNG states from multiple backends (e.g. CUDA + HPU) are saved but only one is restore

【Bug已解决】[Bug]: RNG states from multiple backends (e.g. CUDA HPU) are saved but only one is restored on load_state 解决方案 一、现象长什么样 在一个同时用到多种设备后端的环境里训练(例如模型主体在 CUDA 上、某些预处理 / 评估算子在 H…

2026/11/4 19:50:56 阅读更多 →
商业门头视觉升级,雕花铝单板塑造独特形象立面

商业门头视觉升级,雕花铝单板塑造独特形象立面

做过工装幕墙、写字楼大堂、商业综合体的设计师和采购都有同感:选材最怕的不是贵,而是“上墙就翻车”——色差、变形、曲面生硬、尺寸不对,返工一次次,工期一拖再拖。现在主流工装项目已经统一答案:全品类铝单板。但选…

2026/11/4 0:31:41 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/4 1:35:24 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/5 1:08:50 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/3 11:35:49 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/4 6:23:50 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/5 0:54:28 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/4 19:19:54 阅读更多 →

日新闻

周新闻

月新闻