最近在折腾一个图像分类项目,单卡跑没问题,但想试试多卡加速。参考官方文档写了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和init_process_group的顺序反了。你得先初始化进程组,再根据local_rank设设备,不然NCCL拿到的rank和实际GPU对不上。另外试试在init_process_group里显式传rank=local_rank和world_size,别省,torchrun虽然会自动注入环境变量,但有时候版本不匹配会漏。如果还报错,把NCCL_DEBUG=INFO加上看下日志,能定位是通信超时还是设备不可用。
这问题我踩过坑,大概率是torch.cuda.set_device和init_process_group的顺序搞反了。先初始化进程组,再设device,不然NCCL拿到的是错误的rank对应GPU。另外torchrun会自动设LOCAL_RANK,别手动覆盖,直接int(os.environ['LOCAL_RANK'])就行。
还有个小细节,如果你代码里用了torch.distributed.barrier(),确保所有进程都跑到那一步,不然也会莫名报NCCL错。我之前就是漏了个if dist.get_rank() == 0的判断,导致子进程卡住。建议把set_device放到init_process_group后面,再试试。
这个报错我太熟了,之前调DDP的时候也被NCCL折磨过一阵。你提到torch.cuda.set_device(local_rank),这里有个坑是torchrun其实已经帮你把环境变量LOCAL_RANK设好了,但如果你在代码里又手动set_device,顺序不对的话主进程和子进程的cuda visible devices确实容易对不上。我一般习惯在init_process_group之后立刻用torch.cuda.set_device(int(os.environ['LOCAL_RANK'])),而且确保每个rank只访问它自己的那块卡,不要有全局的cuda:0这种硬编码。另外你检查过RANK和WORLD_SIZE这两个环境变量吗?有时候torchrun传的和代码里读的不一致也会触发NCCL的invalid usage,尤其是多机场景下。还有个小建议,可以试试把nccl的timeout调大一点,像init_process_group(..., timeout=timedelta(minutes=5)),有时候是握手慢导致的假报错。如果还不行,先降低到backend='gloo'跑通逻辑再换回nccl,能快速定位是通信问题还是初始化问题。我之前就靠这招排查出是网卡IP配置不对,而不是代码问题。
这种报错八成是rank和device没对齐,torchrun会自动设好环境变量,你代码里千万别手动调set_device,直接用local_rank当device id就行。我之前踩过一模一样的坑,后来把init_process_group放最前面,再拿torch.cuda.device(local_rank)包住模型就稳了。你检查下是不是在init之前就碰了cuda,或者数据加载那边用了默认设备,也会导致NCCL找不着卡。
试试把set_device换成torch.cuda.set_device(local_rank)放init_process_group后面,顺序反了容易这样。
初始化得先于DDP包装,环境变量用torchrun会自动配好,大概率是device和rank没对齐。
我之前也踩过这个坑,NCCL那个报错十有八九不是通信本身的问题,而是设备初始化顺序搞乱了。你用了torchrun的话,其实不需要自己手动调torch.cuda.set_device,因为torchrun会自动帮你设置好local_rank对应的环境变量,你再手动set一遍反而可能把主进程的设备覆盖掉。我当时的做法是,在init_process_group之前先确认一下当前进程的rank和local_rank是从env里读出来的,然后直接用torch.device(f'cuda:{local_rank}')来给模型和tensor指定设备,全程不要碰set_device。另外你检查一下有没有在代码里提前创建了默认的CUDA context,比如在import之后随便跑了个什么小tensor在GPU上,这会导致NCCL拿到的设备索引和实际进程绑定的不一致。还有个比较隐蔽的点,就是数据加载器的num_workers如果设得太大,子进程fork之后也可能继承到不对的CUDA状态,建议先设成0或者1来排除这个变量。最后如果还不行,试试把环境变量NCCL_DEBUG=INFO打开,看它具体卡在哪个阶段,有时候是网卡选错了,加个NCCL_SOCKET_IFNAME指定一下内网接口就好了。
这问题我上周刚踩过,八成是local_rank和device_id没对上。torchrun会自动设LOCAL_RANK环境变量,你代码里直接读os.environ['LOCAL_RANK']再传给set_device就行,别自己硬编码。另外init_process_group里最好显式传world_size和rank,不然多机训练时容易出幺蛾子。你先试试把set_device放在init_process_group前面,顺序反了NCCL可能找不到正确的GPU。
你这多半是环境变量MASTER_ADDR和RANK没设对,torchrun其实会自动配,检查下代码里有没有手动覆盖。
试试把set_device去掉,直接用local_rank作为device index传模型,省得主从进程打架。
八成是local_rank没从环境变量读,torchrun会自动设,你手动set_device反而搞乱了,直接删掉那行试试。
这报错我熟,之前也卡过一阵子。你试试在init_process_group之后再加一句torch.cuda.set_device(local_rank),顺序很关键,得先初始化再设设备。还有torchrun其实会自动传rank和world_size的环境变量,你代码里别自己手动覆盖,不然很容易对不上。另外可以把nccl换成gloo跑一遍看看,能通就是网卡或驱动的问题。
多半是环境变量没吃透,torchrun会帮你设好RANK和LOCAL_RANK,但你代码里如果用args.local_rank去拿,得记得在main函数里先解析再set_device,顺序反了就会这样。我之前还遇到过NCCL_P2P_DISABLE没设导致多卡互相找不到的情况,你可以试着加上这个环境变量跑一下,排查效率会高很多。
我之前也栽在这上面,后来发现是初始化时world_size没对上,torchrun默认会用nproc_per_node,但你代码里如果硬编码了world_size=1就会报invalid usage。检查下init_process_group里是不是忘了传rank和world_size,或者传错变量了。还有个小技巧,可以先在单卡上把set_device注释掉,用dist.get_rank()打印下设备号,看看子进程到底识别到哪张卡。
这问题我踩过一模一样的坑,多半是torch.cuda.set_device和init_process_group的顺序搞反了,要先把环境变量里的LOCAL_RANK读出来再set_device,最后才init。另外试试把torchrun换成python -m torch.distributed.launch,有些版本对env的兼容性不一样。还有,检查下是不是忘了设MASTER_ADDR和MASTER_PORT,虽然默认有值但显式写出来更稳。如果还不行,把nccl换成gloo跑一遍,能通就是网卡或驱动的问题。
这问题我当初也踩过,NCCL那个invalid usage多半不是设备号对不上,而是rank和device的映射乱了。torchrun会自动设置LOCAL_RANK环境变量,但你手动调set_device反而可能覆盖了它的逻辑,尤其是如果你在init_process_group之前就调了set_device,子进程会拿到错误的cuda上下文。我建议你先别手动设device,直接靠torchrun分配,然后代码里只用local_rank去索引模型和tensor,比如model.cuda(local_rank)这样。另外你确认一下是不是漏了设置MASTER_ADDR和MASTER_PORT,虽然torchrun会给默认值,但某些版本下不显式写会跟NCCL的初始化顺序冲突。还有个坑是数据集加载时每个进程都要有独立的sampler,不然数据重复也会导致奇怪的同步错误。你可以先只跑一个进程看看是不是还报错,如果单进程也挂那就跟DDP无关,是环境问题;如果单进程正常,那就把init_process_group挪到set_device前面,顺序改成先初始化再设卡。最后检查一下CUDA_VISIBLE_DEVICES,有时候容器里没映射好也会触发这个。
八成是distributed初始化顺序问题,先init_process_group再设device试试。我之前这么搞就好了。
大概率是torch.cuda.set_device和init_process_group顺序反了,先初始化再设卡试试。
你这大概率是local_rank的锅,torchrun会自动设置环境变量,你手动set_device反而容易和NCCL的通信组对不上。我建议直接把set_device那行去掉,让torchrun自己管设备分配,或者用device = f'cuda:{local_rank}'来传参。另外可以试试在init_process_group前加个os.environ['MASTER_ADDR']和MASTER_PORT,有时候缺了这俩也会出这种诡异报错。我之前遇到过类似问题,把rank和world_size打印出来核对一下,基本就能定位是环境变量没传全还是初始化顺序的问题了。
我之前也踩过这个坑,NCCL那个报错十有八九不是设备号对不上,而是rank和device的映射没走对。你代码里如果手动调了set_device,那torchrun其实已经帮你把local_rank传进环境变量了,你再设一遍反而容易乱,建议直接靠dist.get_rank()去拿当前进程的GPU,别自己指定。另外init_process_group里最好显式传world_size和rank,虽然torchrun会填,但有时候你本地调试或者IDE里跑,环境变量没注入全就炸了。我后来是改成用os.environ['LOCAL_RANK']直接读,再配合torch.cuda.set_device(int(local_rank)),顺序是先set_device再init_process_group,你试试看这个顺序会不会好一点。还有个容易忽略的点,NCCL对网络接口很敏感,如果多机或者有虚拟网卡,得设NCCL_SOCKET_IFNAME,不然也会报invalid usage。你可以先加个torch.distributed.barrier()在init后面,看能不能正常同步,如果barrier都过不去那就是初始化问题。数据加载那边也要注意,每个进程的DataLoader要设不同的sampler,否则每个rank看到的数据一样,虽然不报错但训练会白费。最后建议把报错堆栈里NCCL那几行贴出来,有时候能看到具体是哪个操作触发的,比猜快多了。
这问题我上周刚踩过,一模一样。NCCL那个invalid usage八成不是环境变量的问题,是你torch.cuda.set_device和init_process_group的顺序反了。官方文档其实有提,但写得很隐晦,得先初始化进程组,再设device,不然主进程的rank和实际GPU对不上,NCCL握手直接崩。你试试把set_device挪到init_process_group后面,然后别用local_rank当设备号,直接用rank % torch.cuda.device_count(),这样更稳。另外torchrun其实会自动帮你设好LOCAL_RANK和WORLD_SIZE,你代码里如果又手动设了MASTER_ADDR和MASTER_PORT,反而可能跟默认的冲突,建议删掉让torchrun自己管。我之前还漏了dist.barrier()在数据加载之后,多卡同步会卡死,但报错不是这个,你可以顺手加上。如果还不行,把环境变量打印出来对一下,八成是CUDA_VISIBLE_DEVICES没传进子进程。
八成是环境变量MASTER_ADDR和RANK没配对,torchrun会自动设但别手动覆盖local_rank。
试试把set_device那句删了,直接靠device=f'cuda:{local_rank}'传参,很多坑就是这么绕过去的。
我之前也踩过这个坑,多半是init_process_group里没传rank和world_size,torchrun虽然会设环境变量,但代码里最好显式用dist.get_rank()和dist.get_world_size()去拿,不然设备号真容易错位。还有个小细节,set_device最好放在init_process_group之后,顺序反了NCCL可能就找不到卡了。你试试把local_rank改成int(os.environ['LOCAL_RANK']),然后确认一下每张卡的可见性,应该能解决。
我之前也踩过这个坑,NCCL的invalid usage多半不是设备号对不上,而是torch.cuda.set_device和torchrun的机制冲突了。你用了torchrun,它自己会通过环境变量LOCAL_RANK帮你把当前进程的GPU绑定好,这时候你再手动set_device反而容易把主进程的默认设备搞乱,尤其是当rank和local_rank混用的时候。我之前是把set_device去掉,直接依赖torch.cuda.device(local_rank)的上下文管理器,或者干脆在模型和输入数据上都用cuda(local_rank)显式指定,就稳定了。另外你检查下init_process_group里有没有传rank和world_size,虽然torchrun会自动注入,但如果你代码里又手动覆盖了它们,也会导致NCCL握手时设备信息对不上。还有个常见问题就是数据集加载时每个进程都要有自己的sampler,不然数据重复或者错位会触发奇怪的NCCL超时,不一定直接报invalid usage,但排查起来很烦。你可以先只开一个进程试试,确认单进程DDP能跑通,再加到两个,这样能快速隔离是初始化问题还是数据问题。如果还不行,把NCCL_DEBUG=INFO加上,看它具体卡在哪个集合通信阶段,日志里会明确提示是哪个rank找不到哪个设备。