最近在折腾一个图像分类项目,单卡跑没问题,但想试试多卡加速。参考官方文档写了DistributedDataParallel,结果一跑就报“RuntimeError: NCCL error: invalid usage”。排查了半天,发现好像是我设了torch.cuda.set_device(local_rank)后,主进程和子进程的设备号对不上?我用的启动命令是torchrun --nproc_per_node=2 train.py,代码里也加了init_process_group(backend='nccl')。是不是我多卡初始化顺序有问题,或者环境变量没设全?有没有大佬能帮忙看看常见坑在哪?先谢谢了!
用PyTorch做多卡训练,DDP一直报错找不到设备,求指点
全部回复
共 147 条这个问题我踩过类似的坑,大概率是torch.cuda.set_device(local_rank)和torchrun自动分配的设备号冲突了。torchrun本身会通过环境变量LOCAL_RANK帮你把每个进程绑定到对应GPU,你再手动set_device反而可能导致主进程和设备实际归属不一致,NCCL初始化时就会报invalid usage。建议你先去掉set_device,因为torchrun启动后每个子进程的默认CUDA设备就是local_rank对应的卡,用torch.cuda.current_device()确认一下。另外检查一下init_process_group里有没有显式传rank和world_size参数,如果没传的话,torchrun会自动从环境变量读,但有时候自己手动传反而会覆盖掉正确的值。还有个小细节——你的模型和数据加载器是在init_process_group之后才放到GPU上的吗?如果顺序反了也可能导致设备索引错乱。我之前还吃过亏,就是忘了在DistributedSampler里设置shuffle=True时同步随机种子,结果每个进程的数据顺序不一样。你可以先按这个思路简化初始化流程试试,通常去掉手动指定设备就能跑通。
试试在init_process_group之前先设好环境变量RANK和WORLD_SIZE,torchrun应该会自动传的。
这个问题我也踩过坑,大概率就是torch.cuda.set_device(local_rank)和torchrun的隐式分配冲突了。torchrun会自动帮你设好LOCAL_RANK环境变量,并且每个子进程默认会绑定到对应的GPU,你手动再调set_device反而可能把主进程的设备号覆盖掉,导致NCCL通信时找不到正确的设备。建议你把那一行注释掉试试,或者只在init_process_group之后再调用一次确认。另外,backend='nccl'本身没问题,但检查一下环境变量里有没有设CUDA_VISIBLE_DEVICES,有时候多卡场景下这个变量会限制可见设备,导致子进程拿到的是空设备。如果你代码里还手动传了device_ids给DDP,记得保证每个进程只看到自己的那张卡,别把全局的卡号传进去。我一般是直接参考PyTorch官方那个torchrun例子,完全不管set_device,只在DDP里用local_rank索引模型,就没再出过这种错。你可以先跑一个最简单的print看各个进程的torch.cuda.current_device()返回值是不是正确的。
这个问题我也踩过坑,大概率是torch.cuda.set_device和torchrun自动设置的LOCAL_RANK环境变量冲突了。torchrun本身就会帮每个进程分配好设备,你再手动set_device反而容易把主进程的设备号搞乱,导致其他进程找不到卡。可以试试把set_device那行注释掉,直接靠init_process_group里的rank和world_size来定位设备。另外检查下os.environ['CUDA_VISIBLE_DEVICES']有没有被意外覆盖,有时候多卡环境变量没透传也会报这个错。
试试在torchrun命令前加export MASTER_ADDR和MASTER_PORT,或者检查下init_method参数是不是设成env://了?
这个问题我之前也踩过坑,关键就是torch.cuda.set_device这步得在init_process_group之前调用,而且得确保每个进程的local_rank跟实际的GPU编号一一对应。你用的torchrun会自动设好环境变量,试试把set_device改成torch.cuda.set_device(int(os.environ['LOCAL_RANK'])),然后保证所有进程的初始化顺序一致。另外nccl报invalid usage有时候是显卡间通信链路的问题,可以检查下NCCL_P2P_DISABLE和NCCL_IB_DISABLE这两个环境变量有没有被意外设置。
你试试在init_process_group之前加一句os.environ['MASTER_ADDR'] = 'localhost'和os.environ['MASTER_PORT'] = '12355',有时候torchrun不会自动设置这些环境变量,导致NCCL找不到通信地址。另外set_device用local_rank本身没问题,但确保你的train.py里没有手动指定cuda设备,不然会跟torchrun自动分配的冲突。我之前也踩过类似的坑,检查下torch.distributed.get_rank()能不能正确打印出进程号。
你这个问题我折腾过好几回,大概率是torch.cuda.set_device(local_rank)和init_process_group的顺序搞反了。正确做法是先调init_process_group,再设设备,因为nccl后端初始化时依赖正确的rank映射。另外torchrun会自动设LOCAL_RANK环境变量,你代码里直接用int(os.environ['LOCAL_RANK'])拿就行,不用自己传参,不然容易跟--nproc_per_node对不上。
遇到过类似问题,大概率是torch.cuda.set_device(local_rank)和init_process_group的顺序反了,得先初始化进程组再设设备。另外你检查下环境变量RANK、WORLD_SIZE这些有没有自动配好,torchrun默认会传但有时和代码里的local_rank混着用就乱套了。还有些版本nccl对GPU拓扑敏感,试试把backend改成gloo先排除通信问题。
这个报错我前两天刚遇到过,大概率是torch.cuda.set_device(local_rank)和DDP的初始化顺序冲突了。建议把set_device去掉,直接用torch.distributed.init_process_group里的rank参数自动分配设备,或者通过环境变量LOCAL_RANK来设置。另外检查一下train.py里有没有在init_process_group之前就调用了cuda(),DDP要求所有设备操作必须在初始化之后。
这问题我当初也踩过坑,关键就在torch.cuda.set_device(local_rank)这步。你用torchrun启动的话,它其实会自动给每个进程设置LOCAL_RANK环境变量,但你如果在代码里手动set_device,反而可能和torchrun内部的设备分配逻辑冲突,尤其是主进程rank0和子进程rank1的设备号如果错位,nccl就找不到通信对象了。我建议你试试去掉手动set_device,直接靠torchrun分配,或者改用torch.cuda.set_device(int(os.environ['LOCAL_RANK']))确保从环境变量读。另外init_process_group里的world_size和rank最好也显式传一下,用os.environ['WORLD_SIZE']和os.environ['RANK'],别省这一步。还有个小细节,确保你的数据集在dataloader里用了DistributedSampler,不然每个进程读的数据一样,梯度同步会出问题。你可以先拿mnist这种小数据集跑通流程,排除模型和数据的问题。
这个问题我之前也踩过坑,大概率是torch.cuda.set_device和torchrun自带的设备分配逻辑冲突了。torchrun其实会自动帮你设好LOCAL_RANK环境变量,并在每个进程里把当前GPU设为默认设备,你手动再调一次set_device反而可能把主进程的设备号搞乱,导致NCCL初始化时找不到对应卡。建议你直接把set_device那行删掉,让torchrun接管设备分配,或者改成从os.environ['LOCAL_RANK']读取后只用它来设device而不调set_device。另外检查一下init_process_group里的world_size有没有设成总卡数,有时候漏了rank参数也会出这种莫名其妙的NCCL报错。我之前还遇到过因为torch.distributed.barrier()位置不对导致子进程先跑完、主进程还在等的情况,也会触发类似错误。你可以先试试在init_process_group后加一行print(torch.cuda.current_device()),看两个进程打印的设备号是不是0和1,如果对不上那基本就是设备映射的问题了。
这个问题我也踩过类似的坑,NCCL那个invalid usage报错八成就是设备号冲突。你用了torch.cuda.set_device(local_rank)但没注意torchrun默认会把local_rank从环境变量LOCAL_RANK里传进来,如果你代码里手动设完又没在init_process_group前同步,主进程和子进程的设备上下文确实容易乱掉。我建议你把set_device那行去掉,直接让torchrun管理设备分配,因为torchrun会自动把每个进程绑定到对应的GPU上,你只要在模型初始化前用local_rank获取当前进程的rank就行。另外检查一下你的torch.distributed.init_process_group有没有放在所有cuda操作之前,顺序不对也会导致后端找不到设备。还有个细节是NCCL依赖网络通信,如果多卡跨机或者有虚拟环境干扰,可以试试把backend换成gloo先排除硬件问题。你启动命令看起来没问题,但可以加个--master_port参数避免端口冲突,有时候默认端口被占用也会触发诡异的NCCL报错。
试试把set_device那句去掉,torchrun会自动分配设备,手动设置反而容易冲突。
这报错八成是环境变量和初始化顺序的锅,torchrun其实会自动设置LOCAL_RANK这些变量,你手动set_device反而容易覆盖掉它的逻辑。试试把set_device那行删了,直接用rank = int(os.environ['LOCAL_RANK']),然后在init_process_group之后再调用torch.cuda.set_device(rank)看看。另外确认下是不是所有进程都进到了if __name__ == '__main__'这个入口,有时候子进程没走对地方也会搞出这种设备对不上的问题。我之前遇到过类似情况,把init_method显式指定成env://就稳了,你可以顺手加上。
我之前也踩过这个坑,问题大概率出在torch.cuda.set_device(local_rank)和init_process_group的顺序上。你得先初始化进程组,再设置设备,而且local_rank在torchrun下是自动注入的,不用自己从环境变量读。另外确认下world_size是不是等于2,还有init_method没指定的话默认走的是env://,环境变量得由torchrun帮你设好。还有个容易忽略的点,DistributedSampler记得要传rank和num_replicas,不然数据加载也会乱。要是还报错,可以先试试把nccl换成gloo跑通CPU版本,排除硬件通信问题。
大概率是local_rank没从环境变量读,直接用args传了,试试改成int(os.environ['LOCAL_RANK'])。
这种报错我上次也踩过,十有八九是主进程里还没等init_process_group完成就急着设device了。你试试把set_device放到init后面,或者干脆用local_rank直接给distributed包里的rank做映射,别自己手动设。另外torchrun其实会自动设好RANK和LOCAL_RANK环境变量,你代码里直接读就行,不用额外传参。还有个小坑,nccl对GPU之间的通信方式敏感,如果机器是单机多卡,可以试试把backend换成gloo排除下是不是通信协议的问题。
大概率是init_process_group没传rank和world_size,torchrun会自己注入环境变量,别手动set_device试试。
之前也踩过这个坑,问题八成出在set_device和init_process_group的顺序上,DDP要求先初始化进程组再设device。另外torchrun其实会自动设好LOCAL_RANK环境变量,你代码里如果手动再设一遍容易冲突,建议直接用os.environ['LOCAL_RANK']。还有个小细节,nccl后端对MASTER_ADDR和MASTER_PORT有要求,没设的话默认值在某些环境下会踩雷,你可以显式加上试试。