原帖把模型搬到 cuda:2,却用 device_ids=[0,1,2,3] 包装 DataParallel;后者要求基础模型在列表第一张卡:
RuntimeError: module must have its parameters and buffers on device cuda:0 (device_ids[0]) but found one of them on device: cuda:2
先按原目标决定是否需要 DataParallel
原题想让两个 Notebook 各自使用一张 GPU,却在每个模型上列了四张卡。若按单卡隔离目标配置,直接使用一张可见卡即可;确实需要多卡时,再让模型初始位置与 device_ids[0] 一致。两种需求应有明确配置,不能同时混用。
如果目标是两个 Notebook 分别独占 GPU 2 与 GPU 0,通常无需让每个 Notebook 再占四张卡。先检查当前进程的可见性与模型位置:
import os
import torch
print("josh050608")
print("visible:", os.environ.get("CUDA_VISIBLE_DEVICES"))
print("device_ids:", getattr(model, "device_ids", None))
print("parameters:", sorted({
str(p.device) for p in model.parameters()}))
print("buffers:", sorted({
str(b.device) for b in model.buffers()}))
单卡隔离可以在启动 kernel 前设置 CUDA_VISIBLE_DEVICES,只暴露所需物理 GPU;进程内使用重新编号后的 cuda:0,直接 model.to('cuda:0'),不包装 DataParallel。已经初始化 CUDA 后再改可见性,需要重启 kernel 才能可靠生效。
如果确实要以 GPU 2 为主卡使用多卡,在四卡均可见时保持顺序一致:
base_model = base_model.to("cuda:2")
model = torch.nn.DataParallel(base_model, device_ids=[2, 0, 1, 3])
inputs = inputs.to("cuda:2")
base_model 是尚未包装的模型,输入设备和输出处理也应按此设计。不要对已经错误包装的对象反复套 DataParallel。
验证所有参数与 buffers 初始位于 device_ids[0],再做一次前向。设备编号应以当前进程的打印结果与完整堆栈为准。
可见性重编号后,主卡列表也跟着改变
例如启动某个 kernel 前只暴露物理 GPU 2,进程内通常只看到一张 cuda:0。此时不能再传 [2,0,1,3];该列表只适用于四张卡全部可见且编号对应的环境。已经初始化 CUDA 后改变可见性,应重启 kernel。
以下合成配置检查不调用 GPU,用于说明 DataParallel 的基础约束:
def check_primary(device_ids, parameter_devices, buffer_devices):
if not device_ids:
raise ValueError("没有指定多卡设备")
primary = f"cuda:{device_ids[0]}"
actual = set(parameter_devices) | set(buffer_devices)
if actual - {
primary}:
raise ValueError(f"基础模型应先放到 {primary},实际为 {sorted(actual)}")
return primary
assert check_primary([2, 0, 1, 3], ["cuda:2"], ["cuda:2"]) == "cuda:2"
try:
check_primary([0, 1, 2, 3], ["cuda:2"], [])
except ValueError as exc:
print("primary mismatch:", exc)
真实模型要同时检查 parameters 和 buffers,BatchNorm 统计量等 buffer 也受主卡约束。替换子层后若忘记迁移,新层可能留在 CPU,也会产生后续设备问题。
从未包装的 base_model 开始配置,先 .to(primary),再包装 DataParallel,并将输入送到同一主卡。不要把错误对象层层嵌套包装。最终先前向,再验证 loss/backward,并通过设备统计确认每个 Notebook 的占卡范围符合最初目的。
参考:原始求助帖;技术依据:DataParallel(2.5)、PyTorch CUDA 环境变量(2.5)。