最近在跑一个简单的ResNet50迁移学习项目,数据集大概2万张图片,每张resize到224x224。我设batch size=32,结果跑第一个epoch就报CUDA out of memory。我用的是RTX 3060 12G,按理说应该够用吧?是不是我DataLoader里开了太多num_workers?还是说模型里用了什么隐藏的显存泄露?尝试把batch size降到8倒是能跑了,但训练慢得离谱,而且准确率也不太行。
想请教下各位大佬,这种场景下一般怎么优化显存?混合精度、梯度累积这些方法真的能立竿见影吗?还是说我哪里写得不规范?求指点,孩子快被显存整自闭了。
用PyTorch做图像分类训练时显存炸了,是我代码写错还是batch size太大?
全部回复
共 158 条12G跑224的resnet50确实不该爆,先查下是不是pin_memory开太高了,混合精度能省一半显存。
12G跑ResNet50加224的输入,batch32按理说真不该爆,先查下是不是pin_memory=True加num_workers开太多,这俩有时候会吃额外显存。混合精度可以试试,AMP在3060上效率提升挺明显的,显存直接砍半,不过准确率波动得留意下。梯度累积确实能救急,但等效batch变大会影响收敛,你降到8觉得慢可能也是这个原因。另外确认下有没有把模型和梯度都留在默认设备上,有时候不经意间把验证集也塞GPU里了。
12G跑ResNet50加224输入,batch32按理说真不紧张,你先看看是不是把验证集也塞进GPU算了,或者pin_memory=True配合num_workers>0有时候会爆。混合精度我试过,显存直接砍半还提速,梯度累积就是省显存但没提速,不过你batch8能跑说明代码没大毛病,准确率不行大概率是学习率没跟着batch调。另外检查下有没有在循环里重复创建loss或者优化器,这种隐性泄漏挺常见的。
12G跑ResNet50加224分辨率,batch32按理说真不该炸,你先查下是不是把验证集的梯度也算了,或者优化器里忘设zero_grad。混合精度基本能省一半显存,直接AMP开起来,梯度累积适合你这种降batch后精度掉的情况,但准确率不行大概率是学习率没跟着调。另外num_workers只影响CPU加载,跟显存关系不大,别在这上面纠结。
12G跑224分辨率的ResNet50,batch32确实不该炸,我3070ti 8G跑同样配置都能稳在24,你这明显有问题。先别急着上混合精度,检查下backbone是不是没冻结,迁移学习如果全量微调,梯度占的内存比参数多好几倍,把requires_grad全关掉只训练最后一层能省一大截。还有num_workers我一般设4就够,开太多反而容易把显存碎片化,另外确认下pin_memory是不是跟cuda:0绑死了,有时候这个会额外吃几个G的缓存。混合精度确实有效,开了之后显存直接砍半,但你要注意BN层在fp16下可能有精度问题,建议先试梯度累积,step设2等效于batch64,效果比单纯降batch强很多,训练慢的问题也能缓解。准确率不行大概率不是batch size的锅,你检查下学习率是不是没跟着调,迁移学习用0.001起步有点猛,降到0.0001试试。
3060 12G跑ResNet50加224输入,batch 32按理说真不该炸,我拿同卡跑过差不多的配置,batch 64都能勉强塞进去。你先把混合精度开了试试,AMP那几行代码加上去显存直接砍半,训练速度还能提一截,这基本是标配了。num_workers那个基本不占显存,顶多占点内存,锅不在它,但你可以看看是不是DataLoader里pin_memory设了True,这个有时候会跟CUDA缓存打架。另外你检查下backbone是不是真的冻结了,如果没设requires_grad=False,反向传播会算全量梯度,显存直接翻倍。梯度累积确实能救,但那是给大batch等效用的,你既然想用32,不如先开AMP把显存压下来,再试试能不能跑起来。准确率不行大概率不是batch size的锅,2万张图迁移学习用32和8的差距没那么离谱,你检查下学习率是不是没跟着调,batch小了通常得把lr也降一点。实在不行就把resize改成192或者用mixup加CutMix,显存和精度能平衡点。别自闭,这问题十个人八个都遇到过,调调就顺了。
12G跑ResNet50加224的输入,batch32按理说不会爆,你检查下是不是把梯度也存了或者开了pin_memory,这两个挺吃显存的。混合精度确实立竿见影,显存能省一半,训练速度还快,我3060上跑类似任务都是batch64加AMP。梯度累积适合你这种降batch后准确率掉的情况,等效大batch但显存不变,不过注意别累积太多不然收敛会不稳。还有个小技巧,把图片用albumentations做预处理,省内存效果也比torchvision强。
12G跑224的resnet50按理够,八成是验证集忘了关梯度,试试torch.no_grad加混合精度。
12G跑ResNet50加224的输入,batch32按理说是能挤一挤的,但我猜你大概率是忘了关梯度计算或者没开混合精度。先别急着怪num_workers,那个只占CPU内存不占显存,除非你开了pin_memory,但顶多也就多几个G的临时缓冲。我建议你先用torch.cuda.max_memory_allocated()打印一下峰值,看看是不是前向传播就把显存吃满了,如果是的话,八成是模型没设成eval模式或者输入没归一化导致激活值爆炸。混合精度真的立竿见影,显存直接砍半,3060的算力也支持,用autocast加GradScaler就行,代码改动不超过十行。梯度累积也能救急,但会拖慢训练节奏,而且你batch8跑得慢不一定是显存问题,可能是数据加载瓶颈,试试把num_workers调到4或者8,配合persistent_workers=True。至于准确率不行,batch太小确实会影响BN统计量,但更大的可能是你学习率没跟着调,batch减半学习率也该减半。最后检查下是不是用了ImageFolder默认的transform,有些预训练模型要跑标准化,不然数值范围不对也会让显存异常。你先跑个profiler看看具体哪步爆的,别自闭,这问题大家刚玩都遇到过。
12G跑ResNet50加224输入,batch32按理不该炸,你检查下是不是在训练循环里把每个batch的loss和输出都存进list了,那玩意儿会悄悄累积计算图。混合精度确实立竿见影,3060的显存带宽上AMP能省将近一半,梯度累积更适合你这种显存卡在临界点的情况,但记得要同步调整学习率。另外num_workers只影响数据加载的CPU内存,跟显存没关系,重点看下transform里有没有意外的归一化层把中间变量留在GPU上。
12G跑ResNet50加224分辨率,batch32按理说真不该炸,你八成是忘了关梯度或者模型没切eval模式,先查查代码里有没有accumulate梯度没清的情况。混合精度真能立竿见影,直接砍一半显存,3060的安培架构支持得挺好,torch.cuda.amp两行代码的事。梯度累积对显存没帮助,它只是等效增大batch,你batch都炸了就别指望它了。另外num_workers只影响CPU加载,跟显存八竿子打不着,别被误导。我怀疑你训练慢可能不是batch小的问题,看看是不是数据加载瓶颈,试试把pin_memory开起来,或者用albumentations做预处理,比torchvision那套快不少。
12G跑224的ResNet50,batch32确实紧,先开混合精度试试,能省一半显存。
梯度累积也行,但得配合学习率调整,不然收敛慢。
12G跑ResNet50按理说32的batch确实不紧张,但迁移学习如果没冻结BN层或者开了梯度检查点,显存占用会翻倍。你可以先试试把num_workers降到4,然后pytorch的dataloader加个pin_memory=True,有时候缓存页也会吃显存。混合精度我实测能省40%左右,但记得给loss加个scaler,不然精度会飘。梯度累积倒是能跑,但等效batch变大会影响收敛,建议配合学习率调整。
说实话12G跑ResNet50加224分辨率,batch32按理真不该爆,你这情况我第一反应是pin_memory和num_workers开太狠了,尤其windows下worker多了反而吃显存,因为每个worker会复制一部分数据到显存里。我之前也踩过这坑,把num_workers从8降到4,pin_memory关掉,显存直接少了快2G,你可以先试试这个。混合精度绝对是立竿见影的,amp包一下forward和loss计算,显存直接砍半,而且3060的tensor core不吃白不吃,训练速度还能提一截。梯度累积的话,如果你不是特别追求batch大小对BN统计量的影响,其实可以batch设16然后累积两步,效果跟32差不多,但注意BN层在累积模式下要小心,最好用syncBN或者干脆别用累积。另外你提到准确率不行,我怀疑是不是迁移学习时没冻结BN或者lr没调好,ResNet50的预训练权重默认是ImageNet的,你新数据集如果分布差很多,建议先用小lr训分类头,再解冻全部层微调,不然直接全量训练容易震荡。最后提一句,检查下是不是开了梯度裁剪或者loss里有什么奇怪操作,有时候自定义loss里不小心保留计算图也会导致显存泄漏,跑几步看nvidia-smi的显存曲线是不是持续上涨就知道了。
12G跑224的ResNet50 batch32确实有点紧,但你这情况大概率不是显存泄露,是默认精度加DataLoader开销叠加的结果。建议先开AMP混合精度,显存能省将近一半,配合梯度累积到等效batch32,速度应该能回来。另外num_workers别盲调,我一般设4就够,开太多反而吃内存。如果你用的是torchvision的预训练模型,记得把BN层设成eval模式,有时也会省不少显存。
3060 12G跑ResNet50按理说够,先开混合精度试试,能省一半显存。另外你确认下是不是把梯度也存了,或者优化器设置有问题。
12G跑ResNet50加224分辨率,batch32按理说不该爆,你先看下是不是在训练循环里把每个batch的loss和输入都攒着没清,或者是验证阶段忘了加no_grad。混合精度确实立竿见影,显存能砍一半,但记得给loss scaler留个位置,不然小batch下梯度容易下溢。梯度累积也管用,但你要是batch8都慢,那可能瓶颈在数据加载,试试把num_workers降到4或者2,有时候开太多反而卡在IO上。
12G跑ResNet50加224分辨率,batch32按理说真不该爆,你先确认下是不是在backbone里顺手把requires_grad全开了,迁移学习一般得冻结前几层,不然光backward就吃掉大半显存。混合精度确实立竿见影,开了之后显存直接砍半,3060的Ampere架构支持得挺好,但记得给loss缩放加个autocast包一下。梯度累积我建议先别急着上,那玩意只能解决batch太小导致的BN震荡,跟显存爆是两个维度的问题。另外num_workers别开太多,4到6个就够,开多了反而容易触发奇怪的显存碎片。你降到8能跑但准确率崩,大概率是BN的running stats没更新好,建议看看是不是忘了调model.train()。
12G跑ResNet50加224输入,batch32按理说真不该爆,你八成是忘了关梯度缓存或者验证阶段也跟着算梯度了。混合精度基本是必开的,显存直接砍半,3060支持得好得很。梯度累积也能救,但注意要配合梯度缩放,不然loss会飘。另外num_workers跟显存没关系,那是吃CPU的,你降到8能跑大概率不是这问题,建议先查查代码里有没有把整个验证集也塞进显存。
12G跑ResNet50加224输入,batch32确实有点紧,但直接炸说明可能真不是显存的事。你试试把num_workers降到4或者干脆2,有时候数据加载线程太多会额外占显存缓存。混合精度基本是必开的,显存直接砍半,我3060跑类似任务batch能拉到48,训练速度也快不少。梯度累积适合不想降batch又想模拟大batch的情况,但准确率波动会大点,建议先把amp和pin_memory开起来,再排查下代码里有没有把验证集的梯度也算了。