最近在公司做一个小项目,用PyTorch微调一个开源的7B模型,单卡A100 40G居然跑着跑着就OOM了。我用的batch size已经降到1了,gradient checkpointing也开了,但还是在forward到中间几层的时候显存爆掉。看了一些教程说可以用DeepSpeed ZeRO或者混合精度,但试了fp16反而loss震荡得很厉害。我怀疑是不是自己dataloader里塞了太多padding token?还是说7B模型本来就需要80G以上的卡?有没有大佬指点一下,或者分享一下你们微调小模型时常用的显存优化trick?先谢过了!
请教大佬们,用PyTorch训练时显存总爆,是我代码写太烂了吗?
全部回复
共 185 条7B模型单卡40G跑OOM挺正常的,尤其是全参数微调的话,光优化器状态和梯度就要吃掉不少显存,更别说激活值了。你batch size降到1还爆,大概率不是dataloader padding的问题,padding多顶多浪费点算力,不至于把显存吃穿。gradient checkpointing开了还炸,我怀疑你可能是全量微调而不是LoRA,如果是全参的话,光模型权重fp32就28G了,加上Adam的momentum和variance直接翻三倍,40G根本扛不住。fp16 loss震荡很常见,因为fp16动态范围小,建议试试bf16,A100原生支持,基本不会出现溢出问题。另外可以看看是不是某个中间层的attention矩阵特别大,比如序列长度拉到2048以上,激活值会爆炸式增长。真要省显存,上LoRA或者QLoRA,7B模型单卡40G完全够用,还能留出空间调大batch size。DeepSpeed ZeRO stage2也能帮上忙,但配置有点绕,不如先试试PEFT库,几行代码就能搞定。
batch size降到1还OOM确实有点离谱,A100 40G跑7B微调按理说没那么夸张。你forward中间层爆显存,大概率是激活值没清干净,可以试试torch.cuda.empty_cache()配合手动del中间变量,或者直接上FSDP把参数和梯度都分片。fp16 loss震荡的话换bf16试试,A100原生支持bf16稳定性好很多,基本不用调loss scale。另外dataloader padding确实要查一下,如果padding比例太高,实际有效batch可能比你想的大不少。
7B模型单卡40G微调确实挺极限的,但BS=1还OOM有点不对劲。你dataloader里padding是不是没做动态长度?如果每个样本都pad到max_len,那显存占用直接起飞。另外fp16 loss震荡试试bf16,A100原生支持,稳定性好很多。还有optimizer别用AdamW全量,换8-bit或者Adafactor能省不少。
7B模型40G跑bs=1不该爆,先查下dataloader里padding是不是都怼到max_len了,那玩意儿贼吃显存。
7B模型单卡40G跑OOM其实挺常见的,不一定是你代码写得多烂,很多时候就是配置和细节没抠到位。你说fp16 loss震荡,可以试试bf16,A100原生支持,数值范围比fp16稳不少,一般微调现在都用bf16了。batch size降到1还爆,重点看看是不是序列长度设太长了,7B模型attention那块显存跟seq_len是平方关系,你把max_length从2048砍到1024试试,光这一步可能就省出好几个G。dataloader里padding塞太多确实浪费,建议用dynamic padding或者按长度分桶,别一股脑pad到最大长度。还有optimizer那步,AdamW本身对7B模型就要占不少显存,换成8bit AdamW或者用DeepSpeed ZeRO-2把优化器状态分片出去,能省一大截。gradient checkpointing开了的话确认一下是不是真的生效了,有些模型得手动enable_input_require_grads,不然可能白开。另外forward中间爆也可能是某一层激活值太大,可以配合torch.cuda.memory_summary()看看到底是哪块吃掉的,别瞎猜。