最近在调一个语义分割模型,batch size从8加到16之后,发现显存占用比原来多了快一倍,但是模型参数量没变啊。我怀疑是DataLoader的num_workers设置问题,网上说num_workers>0时子进程会复制模型,但我的代码里模型是在训练循环外定义的,按理说子进程不应该持有模型参数吧?难道是collate_fn里做了GPU上的操作?我确实在那边做了个简单的tensor拼接和数据增强,但都是CPU上的。有没有大佬遇到过类似情况?另外,看到有些人推荐用prefetch_factor调小一点,这个和显存有什么具体关系吗?求指教,真的被这个问题卡了两天了。
PyTorch的DataLoader多进程加载,为什么我的显存不降反升?
全部回复
共 4 条我之前也踩过这个坑,重点不在num_workers,而是你batch size翻倍后,显存里同时存的前向激活值和梯度也会翻倍,尤其语义分割这种高分辨率输入,显存涨得比参数量快多了。collate_fn里如果用了GPU tensor拼接,哪怕是临时操作,也会让子进程持有CUDA context,导致显存被多个worker重复占用,你可以试着把tensor操作全放CPU,或者干脆把collate_fn改成返回list,到训练循环里再拼。prefetch_factor调小确实能减少预加载的样本数量,但主要影响的是内存和CPU占用,对显存影响比较间接,除非你数据加载成了瓶颈导致GPU空转。建议你直接nvidia-smi看下是哪个进程占的显存,如果是多个python进程,那就是worker复制了CUDA context,用fork start method可以缓解。
显存翻倍这个现象确实挺典型的,不过大概率不是DataLoader子进程持有了模型,而是batch size翻倍后,反向传播时保存的激活值本身就跟着翻倍了,语义分割输入分辨率又大,这块开销比想象中猛。你那个collate_fn虽然是CPU操作,但如果在里面调用了类似.cuda()或者隐式触发了CUDA上下文,子进程也可能把显存初始化了,建议用nvidia-smi看看是不是多进程各占了一小块。prefetch_factor调小主要影响CPU预取和GPU消费之间的缓冲队列,对显存影响其实很间接,除非队列里堆积了太多未处理的tensor,但一般不至于占你一半显存。你可以先试试把num_workers设成0跑一遍,如果显存立刻降下来,那就实锤是worker进程的CUDA上下文或数据拷贝问题了。
我之前也踩过这个坑,num_workers>0时子进程确实会fork父进程内存,但模型一般不会整个复制过去,除非你在collate_fn里不小心把模型或CUDA tensor传进去了。你检查下是不是在collate里用了GPU上的操作,哪怕是.cuda()或者.to(device)也会让显存翻倍,因为每个worker都会缓存一份。prefetch_factor调小主要是减少预取批次数,能缓解CPU内存压力,但和显存关系不大,你这情况更像是数据增强或拼接时创建了临时tensor没释放。建议把collate里的操作改成纯CPU,或者用pin_memory=True配合non_blocking=True,看看显存曲线是否正常。
显存翻倍大概率是batch size翻倍直接导致的,跟num_workers没啥关系,你可以先单独测下DataLoader不开多进程试试。