这个报错出自 torch.nn.DataParallel,根源是 DataParallel 默认主卡是 cuda:0,但是你的模型参数被提前放到了 cuda:2 上,主设备和模型参数所在显卡不匹配,DP 包装的时候校验失败。DataParallel 的底层逻辑是:模型必须先放在主 GPU 上,DP 会自动把模型副本分发到其他从卡;如果模型提前加载到非 0 号卡,再用 DP 封装,就触发这个参数设备校验报错。很多人手动 to ("cuda:2") 移动模型,或者加载 checkpoint 时指定了设备,之后直接套 DataParallel,就会踩这个坑。
一、报错原理拆解
- DataParallel 默认主设备:device_ids[0],默认就是 cuda:0
- 校验规则:封装 DP 之前,模型所有权重必须在主卡上
- 现场情况:模型参数在 cuda:2,DP 主卡是 cuda:0 → 校验不通过抛出异常
注意:这个是 DataParallel 老 API 的限制,DistributedDataParallel(DDP)没有这个强制校验,DDP 更推荐多卡训练。
二、方案对比
| 方案 | 操作 | 适用场景 | 备注 |
|---|---|---|---|
| 方案 1:模型先放到 cuda:0,再封装 DataParallel(标准写法) | model.to ("cuda:0"),然后 model = nn.DataParallel (model, device_ids=[0,1,2]) | 常规多卡,允许 0 卡做主卡 | 最稳妥,符合 DP 原生设计 |
| 方案 2:修改 DataParallel 的 device_ids,把 2 放在第一位 | model.to("cuda:2"); model = nn.DataParallel(model, device_ids=[2,0,1]) | 你必须以 2 作为主卡 | 不推荐长期用,容易和其他代码冲突 |
| 方案 3:换成 DDP 分布式训练 | 改用 torch.distributed,启动方式 torchrun | 多卡训练正式项目 | DDP 不强制主卡为 0,工业界主流 |
| 方案 4:加载权重时 map_location 控制设备 | torch.load(ckpt, map_location="cuda:0") | 加载旧权重时权重跑到别的卡 | 加载阶段就锁定主卡 |
三、错误示例
❌ 错误:模型先放到 cuda:2,再套DataParallel
model = MyModel()
model = model.to("cuda:2")
model = nn.DataParallel(model, device_ids=[0,1,2]) 触发报错
四、正确写法示例
import torch
import torch.nn as nn
model = MyModel()
✅ 先移动到主卡 cuda:0
model = model.to("cuda:0")
✅ 再封装DataParallel
model = nn.DataParallel(model, device_ids=[0,1,2])
如果业务强制主卡是 2:
model = MyModel()
model = model.to("cuda:2")
device_ids第一个元素就是DP主卡
model = nn.DataParallel(model, device_ids=[2,0,1])
五、完整排查清单
- 封装 DataParallel 之前,打印模型参数设备确认:print(next(model.parameters()).device)
- 检查 torch.load 的 map_location,是否把权重映射到 cuda:2
- 不要在 DP 封装之后再执行 model.to (),会打乱 DP 副本
- 输入数据 device 要和主卡对齐,数据也要送到主卡
- 区分 DP 和 DDP:DDP 是每个进程一张卡,没有这个校验
- 环境变量 CUDA_VISIBLE_DEVICES 会改变卡编号,容易造成卡号混淆
【相关 FAQ】
问题 1:module must have parameters on cuda:0 but found cuda:2,为什么 DataParallel 要求模型必须在 cuda:0?
回答 1:module must have parameters on cuda:0 but found cuda:2,是 DataParallel 的机制限制,DP 默认把 device_ids 列表第一个显卡作为主卡,默认主卡是 cuda:0,模型参数必须预先放在主卡上,否则校验失败。
问题 2:module must have parameters on cuda:0 but found cuda:2,能不能不改模型位置,直接指定 DP 主卡为 cuda:2?
回答 2:可以,module must have parameters on cuda:0 but found cuda:2,构造 DataParallel 的时候把 device_ids 第一个值写成 2,device_ids=[2,0,1],同时模型提前放到 cuda:2,但是这种写法容易和其他代码冲突。
问题 3:module must have parameters on cuda:0 but found cuda:2,DDP 会不会出现这个报错?
回答 3:一般不会,module must have parameters on cuda:0 but found cuda:2 是 DataParallel 独有的校验,DDP 是单进程单卡模式,没有强制要求模型必须放在 0 号卡,正式项目优先 DDP。
问题 4:module must have parameters on cuda:0 but found cuda:2,加载 checkpoint 时权重跑到 cuda:2 怎么处理?
回答 4:module must have parameters on cuda:0 but found cuda:2,加载权重时用 map_location="cuda:0",强制权重映射到主卡 cuda:0,再执行 to 和 DP 封装。
【参考资料】
[2026-03-05](PyTorch DataParallel module must have parameters on cuda)Stack Overflow
[2026-01-10](nn.DataParallel device_ids 主设备规则)PyTorch 官方文档
[2025-11-20](DDP 与 DataParallel 区别与迁移指南)PyTorch Docs
[2026-02-22](torch.load map_location 参数说明)PyTorch 官方文档
【免责声明】
本文基于 PyTorch DataParallel 接口行为总结,DataParallel 已经属于老 API,新版本 PyTorch 更推荐 DDP 分布式训练;显卡编号受 CUDA_VISIBLE_DEVICES 环境变量影响,卡号映射发生变化时需要同步调整代码。
