最近在尝试用DeepSpeed微调一个7B的LLM,显卡是两张3090(24G)。参考了几个开源项目的配置,把ZeRO Stage调到了2,offload_optimizer也开了,但跑起来大概十几个step之后就报CUDA OOM。
我看nvidia-smi,显存占用并没有满,但就是报错。后来试了把train_batch_size降到1,gradient_accumulation设成8,还是不行。
有点迷茫:是ZeRO的partition策略没生效,还是我的模型本身太大,两张卡真的带不动?
或者是不是我漏了offload_param?看网上有人说Stage 2要配合offload_param才能稳,但那样速度会不会太慢?
求有经验的老哥指点一下,这种配置到底该怎么调,或者说7B微调最低需要多少显存?谢谢!
用DeepSpeed微调7B模型总是OOM,是ZeRO配置问题还是我显存真不够?
全部回复
共 83 条看到你说nvidia-smi显存没满但还是OOM,我第一反应是碎片化或者峰值问题,不是单纯容量不够。DeepSpeed的ZeRO Stage 2会把optimizer状态和梯度切分,但每张卡上其实还会保留一份完整的模型参数副本,7B模型光fp16权重就14G,加上激活值、临时buffer和通信开销,两张3090确实非常极限。你开了offload_optimizer但没开offload_param,那意味着模型参数还是全量驻留在显存里,这可能是关键——建议试一下把offload_param也打开,让CPU扛一部分参数,虽然会慢不少,但能腾出空间。另外,你降低batch size的做法是对的,但gradient_accumulation只影响更新频率,不影响峰值显存,真正吃显存的是单步前向/后向的激活值,可以试试gradient_checkpointing,能省掉一大块激活缓存。还有个小细节,检查一下你是否设置了partition_activations,有时候ZeRO的激活分区没启用,会导致激活值在每张卡重复计算。我自己的经验是,7B在双3090上跑微调,除非用LoRA或者QLoRA,否则全参数微调基本都得靠NVLink加极度激进的显存优化,不然大概率需要4张卡。建议你先用stage 3加全量offload试试,如果速度能接受,至少不会OOM。
我之前也踩过这个坑,OOM不一定就是显存峰值爆了,很可能是碎片化或者临时buffer导致的,你nvidia-smi看的是空闲,但CUDA context已经申请满了。试试加--max_train_steps限制一下,或者把--gradient_checkpointing打开,这个对7B模型帮助很大,能省差不多一半显存。另外offload_param确实值得试,但开了之后速度会慢不少,你两张3090如果数据并行加ZeRO-2,理论上24G是够跑7B的,除非序列长度拉太长。你训练时的max_seq_len是多少?如果超过2048,那大概率是这块的问题。
我之前也卡在这过,3090双卡跑7B理论上是够的,但OOM不一定只看显存总量。你开了offload_optimizer之后,如果没设offload_param,模型参数和梯度还是占着显存,ZeRO Stage 2只切了优化器状态,参数还是每卡一份,峰值很容易爆。
建议把offload_param也打开,然后确认一下你是不是用了ZeRO Stage 3,虽然慢点但两张卡能稳跑。另外,nvidia-smi显存没满但报错,有可能是PyTorch缓存碎片或者通信缓冲区占的显存没被统计到,可以试试设置PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,有时候挺管用。
我上次调7B就是靠这个组合救回来的,batch size 1加梯度累积没问题,关键还是offload要配全,你试完回来反馈下效果?
offload_param也得开,不然优化器状态还是占显存,试试stage3吧。
你这种情况开offload_param加stage3应该能跑,3090互联带宽也不差。
我之前也踩过这个坑,重点不是显存占用满不满,而是碎片化导致的分配失败。你试试把gradient_checkpointing打开,再配合ZeRO Stage 2 + offload_param,7B在双3090上应该能跑起来,但batch size确实得压到1。另外检查下你是不是把offload_optimizer和offload_param都开了,只开optimizer的话参数还是会占不少显存,而且跑十几个step才爆很可能是activation峰值在作妖。
看到你说nvidia-smi显存没满但还OOM,我第一反应是碎片化或者预留内存的问题,PyTorch的缓存分配器有时候会吃满显存但显示不出来。我之前用3张3090跑13B也遇到过类似情况,后来发现是activation checkpointing没开,7B模型不开这个微调基本必炸,你可以试试把gradient_checkpointing打开,能省掉一大半激活内存。至于offload_param,Stage 2确实主要管优化器状态和梯度,参数offload是Stage 3的活,但如果你把offload_optimizer开了还OOM,大概率是CPU offload的带宽瓶颈导致内存峰值出现在某个step的中间阶段,而不是稳态。另一个坑是zero_allow_untested_optimizer,有些开源配置里会默认关掉AdamW的offload,你检查下DeepSpeed的json里有没有这个字段。还有就是两张3090之间用NVLink还是PCIe,通信开销会影响内存峰值,我建议你把train_batch_size调回2,但把微批次大小设成1,然后看下每个step的峰值内存日志,deepspeed有profiling工具能输出每个tensor的分配情况。最后问一句,你用的模型是原生HF版本还是做了flash-attention的?没装flash-attn的话,7B的seq_len稍长一点就会OOM,这个和ZeRO配置关系不大。
offload_param也得开,不然优化器状态还是占显存,试试stage3加全offload。
offload_param也得开,不然优化器状态还是占显存,试试stage3加cpu offload吧。
你这配置跑7B确实紧,3090互联带宽也拖后腿,建议先开offload_param再调zero阶段。
说实话看到你这个报错我第一反应就是offload_param没开,DeepSpeed的Stage 2默认只offload优化器状态,你要是把参数也offload到CPU,模型权重加梯度加优化器状态这三块的总占用会小很多,7B全精度光参数就28G,两张卡每张才24G,光塞参数就快满了,更别说中间激活值了。
不过你说的“显存占用没满但报OOM”这个现象很关键,大概率不是真的物理显存不够,而是碎片化或者临时峰值导致的,比如在计算注意力的时候激活值会突然暴涨,尤其是序列长度长或者batch内padding多的情况下,这时候就算nvidia-smi显示没满,CUDA也会因为找不到连续内存块直接报错。
你可以试试把序列长度截断到512或者更短,然后开gradient_checkpointing,这能极大压低激活显存,代价就是训练慢一点,但总比OOM强。另外检查一下你是不是用了flash attention或者xformers,有些kernel在特定形状下会临时申请大块显存,换个实现可能就好了。
还有个小坑,DeepSpeed的partition策略有时候在配置里写了但实际没生效,你可以在跑起来后看一眼日志,确认一下是不是真的ZeRO Stage 2,或者干脆用deepspeed.runtime.zero的调试接口打印一下各stage的显存分配。两张3090跑7B理论上够用,微调不是预训练,很多人甚至单卡都能跑起来,问题多半出在配置细节上。
offload_param也得开,光offload优化器不够,7B在24G上双卡Stage 2本来就紧。
你这情况大概率是碎片化内存炸了,试试开--split_batches或者换NVMe offload,两张3090跑7B理论够的。
3090的24G跑7B全参微调确实紧,建议先开offload_param试试,ZeRO2光offload优化器不够。
你这情况我上周刚踩过坑,7B全参数微调两张3090基本是极限中的极限,光靠ZeRO2+offload_optimizer不够。我最后是把offload_param也打开,同时把model并行度调成2,再把序列长度砍到512才勉强跑起来。另外你注意下nvidia-smi那个显存占用,它显示的是峰值后的当前值,OOM往往发生在临时张量分配的时候,表面看不出来。建议你开一下DeepSpeed的日志看它实际每步峰值显存,如果还在涨就得换LoRA或者QLoRA路子。
看到你说nvidia-smi显存没满但报OOM,这个我太有同感了,之前调bloom-7b也踩过这坑。其实很可能是碎片化问题,DeepSpeed在stage2下虽然把optimizer states分出去了,但activation和临时buffer还是会在每个step里动态申请,跑十几个step后显存碎片越积越多,CUDA就罢工了,这时候看占用率确实不是满的。你试了降batch size没用,我猜是因为gradient accumulation并不会减少单step的峰值显存,它只是把梯度累加的次数变多了,真正的峰值还是由单次前向+反向决定的。offload_param我建议你确实开一下,stage2只offload optimizer的话,模型权重和梯度还在显存里,7B光fp16权重就14G,两张卡分摊后每张还要留出给activation和通信buffer的空间,很紧张。另外可以试试把zero_force_ds_cpu_optimizer设成false,有时候这个参数会强制把优化器放CPU结果反而增加拷贝开销。还有个土办法,把max_seq_len砍一截,activation会小很多,7B模型如果序列长度从2048降到1024,峰值显存能降将近一半。如果还不行,那就得考虑换Zero-Offload或者干脆用LoRA了,两张3090跑全参数微调7B确实有点极限,不是配置不对,是物理显存就摆在那。
我之前也遇到过类似情况,显存看着没满但就是OOM,后来发现是PyTorch的缓存分配器在搞鬼,碎片化太严重了。你可以试试在训练脚本里加两句torch.cuda.empty_cache(),或者在dataloader里设个pin_memory=False,有时候能缓解。另外Stage 2确实不用开offload_param,但你要是把optimizer offload到CPU了,那model grads其实还在GPU上,3090的24G跑7B全参数微调本来就紧巴,建议你查一下是不是activation memory爆了,开个gradient_checkpointing试试,能省不少。
3090的24G显存跑7B全参微调确实有点极限,但你这配置理论上不该OOM得这么频繁。先确认下你用的模型是不是Llama架构,有些模型的embedding和lm_head特别占显存,这两块在ZeRO里经常被忽略。另外你开了offload_optimizer但没开offload_param,这俩在Stage 2下其实是分开控制的,建议把param也offload了试试,代价是慢一点但显存能省出好几个G。还有个坑是nvidia-smi看不出来——PyTorch的缓存分配器可能占着显存但没实际用,得看torch.cuda.max_memory_allocated()这个值。我上次调7B用两张4090也遇到过类似情况,最后是把序列长度砍到1024才稳住,你可以先拿短序列跑两步验证下是不是长度问题。
显存没满但OOM大概率是碎片的锅,试试开个memory_cu收敛下碎片,或者换NVMe offload。
3090跑7B stage2其实够呛,建议直接上stage3加offload_param,batch再小点试试。
offload_optimizer开了但param没开,stage2等于半吊子,试试把offload_param也打开,显存会宽裕不少。
我之前也碰到过一模一样的情况,显存看着没满但就是OOM,后来发现是碎片化的问题,光看nvidia-smi不准。你试试把gradient_accumulation和batch size配合着调回默认,然后开个--zero_force_optims_bw这种选项,或者直接把offload_optimizer改成offload_param,我这边是这么解决的。另外7B模型两张24G其实理论够跑stage 2,但关键要看你的序列长度和attention部分吃不吃显存,建议把max_seq_len砍到512试试,大概率能稳。
说实话我也踩过差不多的坑,3090跑7B全参数微调本来就紧巴巴的,两张卡24G加一起也就等效48G,ZeRO Stage2只切了优化器状态和梯度,模型参数和激活值还是每张卡各存一份,7B光fp16权重就14G了,加上激活和临时buffer,OOM很正常。你试试把offload_param也打开,让模型参数也走CPU,虽然慢点但能省不少显存。另外检查下是不是sequence length太长或者attention没有开gradient checkpointing,这个对激活显存影响特别大,我开了之后直接从OOM变成能跑64batch。