最近在尝试用DeepSpeed微调一个7B的LLaMA模型,卡是两张A100 40G。用了ZeRO-3,offload也开了,但每次跑起来没几分钟就报OOM。我看了一些教程说7B模型用ZeRO-3应该能跑,但我设置zero_optimization.stage=3之后,显存占用直接冲到35G以上,训练步数一多就崩。我试过调小per_device_train_batch_size到1,还是不行。是不是我offload_param和offload_optimizer的device没设对?或者ZeRO-3本身对多卡通信有额外显存开销?有没有大佬分享下实际成功跑7B微调的config配置?先谢过了。
用DeepSpeed跑LLaMA微调,ZeRO-3总报显存不足,是我配置有问题吗?
全部回复
共 112 条我之前也踩过这个坑,ZeRO-3的显存开销不只是参数本身,还有每层all-gather的临时buffer和通信碎片,40G两张卡其实挺紧的。你试试把zero_optimization里的reduce_bucket_size和allgather_bucket_size调到5e7甚至更小,能省不少峰值显存。另外offload_param的device设成cpu没问题,但offload_optimizer得确认pin_memory开了,不然数据传输会卡。还有个小技巧,把gradient_checkpointing打开,虽然慢点但显存直接掉一大截。我最后是batch_size=1加gradient_accumulation_steps=16跑通的,你可以参考下。
我之前也踩过这个坑,ZeRO-3的显存开销确实比想象中大,不只是参数和优化器状态,还有all-gather的临时缓冲区和通信分片,两张卡40G其实挺紧的。你offload开了的话,看看是不是把offload_param和offload_optimizer都设成cpu了,但offload_param要配合pin_memory用,不然反而会增加显存碎片。另外你那个“跑没几分钟就OOM”很可能是峰值显存问题,训练步数一多,激活值缓存和梯度累积都会波动,建议把zero_force_ds_cpu_optimizer设为false,然后检查一下zero3的reduce_bucket_size和allgather_bucket_size,调小到5e8左右能明显降峰值。我自己是用单卡A100 80G跑的7B,开ZeRO-3加offload,batch size设4,sequence length裁到512,勉强稳定,两张40G的话你试试gradient_checkpointing必须开,然后offload_optimizer的device改成nvme试试,虽然慢但能保命。还有个细节,你确认下transformers版本跟DeepSpeed是否兼容,我之前因为版本不匹配导致ZeRO-3的显存预估完全不准。最后问一下,你微调时用的是LoRA还是全量?如果是全量的话,7B真的不建议ZeRO-3,直接ZeRO-2加offload可能更稳。
offload设cpu试试,另外pinned_memory开一下,之前我这么配才稳。
offload设成cpu后记得把nvme关掉,否则数据倒腾反而吃显存,我踩过这坑。
试试把zero_force_disable_cpu_offload开一下,A100 40G跑7B其实不用offload也行。
我之前也卡在这过,问题多半出在offload上。你把offload_param和offload_optimizer的device都设成nvme试试,光设cpu的话40G还是不够塞7B的梯度状态。另外ZeRO-3的通信量确实比2大不少,多卡下每步都会做全量all-gather,建议把reduce_bucket_size和allgather_bucket_size调小到5e7左右,能省不少显存。还有个小坑,你checkpoint的保存频率太高的话,临时buffers也会爆,试试每500步存一次。
别说,我前阵子也卡这坑里了。你试试把offload_param的device设成nvme,offload_optimizer也一起丢nvme,光靠CPU offload在A100上反而容易爆。另外ZeRO-3确实有通信缓冲开销,得把zero_force_ds_cpu_optimizer设成false,然后手动给reduce_bucket_size和stage3_prefetch_bucket_size调小点,比如1e7左右,显存能省不少。
不过话说回来,两张40G跑7B全参数微调本来就紧巴巴,我后来换成LoRA加ZeRO-2才稳定下来,batch size还能开到4。你要是非得上全参数,建议把stage3_max_live_parameters和stage3_max_reuse_distance也限制一下,不然激活值会偷偷吃掉大量显存。你试试这几项,不行再贴下完整config,我帮你看看哪没拧对。
说实话看到你这个情况我第一反应是offload可能没真正生效,你检查下nvidia-smi的时候留意下CPU内存占用,如果CPU内存没涨那说明offload配置压根没起作用,device参数得写成cpu而不是nvme。另外ZeRO-3在每张卡上其实会保留所有层的参数碎片,加上通信buffer和梯度同步的开销,实测下来35G左右是正常的,但你要知道A100 40G留给激活值的空间其实很紧,7B模型哪怕batch size=1,序列长度一旦超过512,激活值照样能吃掉好几个G。
我之前跑13B的时候也遇到过类似问题,后来是把zero_optimization里的reduce_bucket_size和allgather_bucket_size调小到5e8,同时把stage3_gather_16bit_weights_on_model_save设为true,才勉强稳住。但说实话两张卡跑7B用ZeRO-3性价比很低,纯数据并行加ZeRO-2可能更稳,因为ZeRO-3每步都要做全参数收集,多卡通信延迟会让显存峰值更高。你可以试试关掉offload_optimizer,只保留offload_param,或者干脆把offload改成nvme路径,虽然慢点但能省显存,不过得看你机器有没有合适的SSD。
还有个容易忽略的点是transformers的gradient_checkpointing得开,不开的话激活值直接翻倍,而且尽量把logging和evaluation的step调大,减少中间过程的内存波动,我怀疑你那个跑几分钟就崩可能不是峰值问题,而是累积碎片导致的,可以加个环境变量PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128试试。
说实话我也踩过一样的坑,7B在双卡A100上跑ZeRO-3的OOM,大概率不是offload设备设错,而是你把CPU offload和NVMe offload混着用了。ZeRO-3的param offload和optimizer offload如果同时开,且device都设成cpu,那每个step都会有大量参数在CPU和GPU之间搬动,这个通信峰值很容易把显存瞬间顶上去,尤其是你batch size调到1之后,计算量变小但通信开销占比反而更大了。
我实际跑通的配置是只开optimizer offload,param offload关掉,然后zero_force_ds_cpu_optimizer设成false,让Adam留在GPU侧,这样显存占用稳定在28G左右。另外你注意到没有,ZeRO-3在多卡下每个rank会额外分配一部分buffer用于all-gather,这个buffer大小是reduce_bucket_size和allgather_bucket_size决定的,默认值我记得是5e8,如果你没调小,两张卡就是双份的显存浪费,建议把这俩值降到2e7试试。
还有个容易忽略的点,stage3_max_live_parameters和stage3_max_reuse_distance这两个参数,默认值对7B来说太保守了,会导致模型参数被频繁换入换出,虽然省了峰值但反而增加碎片化OOM风险。你可以试试把前者设成1e9,后者设成1e7,然后开着stage3_gather_16bit_weights_on_model_save,这样能缓解一些。如果还崩,干脆降到ZeRO-2加offload optimizer,7B双卡40G其实勉强够用,只是batch size得再小点,梯度累积开大些,训练速度慢点但至少能跑完。
说实话我看到这个第一反应是,你offload开了但可能没开全,或者开了之后CPU内存也不够。ZeRO-3的offload_param和offload_optimizer都要显式指定device: cpu,而且offload_param的pin_memory最好也开着,不然数据在CPU和GPU之间搬运时会卡在内存分配上。另外7B模型在两张A100 40G上,纯参数加梯度加优化器状态,理论峰值要超过80G,所以必须保证每一步的临时激活值也尽量别占太多,序列长度和gradient checkpointing得一起调。你说batch size都调到1了还是崩,我怀疑是不是你max_seq_len设得太大,比如2048以上,那激活值会爆炸,试着砍到512或者1024看看。还有一点,ZeRO-3在多卡时每个rank会持有部分参数的分片,但通信缓冲区也会额外吃掉几百MB到1G的显存,这个容易被忽略。我之前跑13B是4张A100才稳,两张卡硬上7B确实很极限,建议你先把zero_optimization.reduce_scatter和allgather的bucket_size调小一点,比如5e7,能缓解瞬时峰值。最后可以试试用deepspeed的zero_to_fp32脚本把参数先导出来看看是不是某些层在反向时临时分配了超大tensor,有时候是embedding层的问题。
之前踩过类似的坑,7B在40G卡上跑ZeRO-3其实挺极限的,offload参数和优化器都设成cpu还不够,得把pin_memory也关掉,不然数据搬运的时候显存会爆一下。另外你试试把zero_force_ds_cpu_optimizer设成false,或者干脆用stage=2加offload,7B两张卡其实stage2更稳。还有个小细节,A100的NVLink带宽够高,但多卡通信的buffer会占一部分显存,可以调低reduce_bucket_size和allgather_bucket_size到5e8左右试试。
我之前也卡在这过,后来发现是offload的non_cpu_offload和cpu_offload混着开反而更吃显存。你把offload_param和offload_optimizer都设成cpu,pin_memory开true,然后zero_force_to_cpu_memory也设一下,能压到20G左右。另外ZeRO-3在all-gather时确实有临时buffer,你试试把zero_use_all_reduce_for_guard和zero_allgather_bucket_size调小到1e7,别用默认值。还有个小坑,你检查下模型是否用了gradient_checkpointing,不开这个7B在40G上很难稳。
offload设cpu确实能压显存,但7B两张卡还是紧,试试把gradient_checkpointing开着再调小max_seq_len。
我之前调7B也卡在这个坎上,后来发现光开offload不够,还得把zero_offload_param_device和zero_offload_optimizer_device显式设成cpu,并且zero_optimization.stage3_gather_16bit_weights_on_model_save要设成true,不然保存时也会爆。另外ZeRO-3每个step都会做全量参数收集,通信缓冲区和临时tensor会额外吃几个G,你可以试试把zero_optimization.reduce_bucket_size和allgather_bucket_size从默认的5e8调小到2e8,能省不少峰值显存。还有个小坑,如果你用了gradient_checkpointing,记得和ZeRO-3的cpu_offload配合时,把optimizer换成adamw而不是默认的adam,不然会有重复内存分配。我最后是把per_device_train_batch_size保持1,但把gradient_accumulation_steps提到8,配合上面这些设置才稳定跑完的,你可以试试看。
我之前也卡在这过,ZeRO-3的显存占用其实比想象中高,因为每层参数都要做all-gather,通信缓冲区会吃不少显存,你把zero_force_opt_offload改成true试试,同时offload_param的device指定为cpu,pin_memory开起来。另外7B用两张40G确实紧,我最后是把序列长度裁到512,gradient_checkpointing打开才稳住的,你那个35G可能是峰值,把max_grad_norm调小点也能缓解。
还有个坑是transformers版本和deepspeed的兼容性,我之前用老版本会莫名多分配显存,升级到0.10.1就正常了。你试试把zero3_init改成false,让参数在forward时才加载,虽然会慢点但能压住峰值。另外确认一下你是不是把offload_optimizer的device设成nvme了,那个反而会拖慢并可能报错,直接用cpu就行。
我之前也卡在这过,ZeRO-3的显存占用看着吓人其实有一部分是通信buffer,40G双卡跑7B理论够但很极限。你试试把zero_force_ds_cpu_offload设成true,同时offload_param和offload_optimizer都指到nvme,别用cpu,这样能省不少。另外检查下gradient_checkpointing开了没,这个不开的话activation占的比参数还多。还有个小坑,per_device_train_batch_size调小但train_batch_size没跟着改的话,梯度累积会爆显存,你确认下log里的实际batch大小。
我之前也踩过这个坑,7B配双卡A100看起来挺宽裕,但ZeRO-3一开,通信缓冲区和分片元数据吃掉的显存比你想象的多。你试试把zero_force_opt_optimizer设成false,或者把reduce_bucket_size和allgather_bucket_size调到5e8以下,默认值在40G卡上经常爆。还有offload这块,offload_optimizer的device设成cpu没问题,但offload_param我建议别开,参数offload到cpu会引入大量张量搬运,反而让显存碎片化更严重,你试下只offload优化器状态。另外检查下gradient_accumulation_steps,如果你设得小,每步更新频繁,显存峰值会比预期高很多,我一般调到8以上。还有一个容易忽略的点,你用的是DeepSpeed还是HF的Trainer集成?如果是后者,model_parallelism和zero_3有时会冲突,直接改纯DeepSpeed脚本更容易控制。最后,你确认下是不是真的跑满了两张卡,有时候通信不均匀,一张卡先爆也会报OOM,可以开zero3_leaf_modules手动分配一下。
offload只开param不开optimizer试试,另外A100 40G跑7B batch=1本来就很极限,别全信教程。
遇到过一模一样的坑,最后发现大概率不是配置问题,而是ZeRO-3在微调场景下的隐形显存开销被低估了。你开offload之后,参数和优化器状态确实挪到CPU了,但forward和backward过程中的activation、gradient以及临时buffer照样全在GPU上,而且ZeRO-3的partitioned optimizer在每步更新前要gather所有rank的参数,这个all-gather通信会额外分配一块临时显存,多卡时这块开销会随模型尺寸线性增长,7B在40G卡上其实很极限。我当时是把zero_optimization.stage=3改成stage=2,同时把offload_optimizer的device设为cpu,offload_param保持nvme(如果你有SSD),再配合gradient_checkpointing打开,batch size设1,序列长度砍到512,才勉强稳定跑完。另外你检查下zero_force_ds_cpu_optimizer这个参数,默认false时可能让优化器留在GPU上,开成true能省不少。还有个冷门点:reduce_bucket_size和allgather_bucket_size默认值偏大,手动调小到5e7左右能显著降低峰值显存。如果还不行,试试不用DeepSpeed,直接用HuggingFace的PEFT+LoRA,7B全量微调本来就不适合两张40G,LoRA才是正解,显存直接降到15G以下。
offload设成cpu试试,另外检查下allgather的buffer大小,默认配置在40G卡上确实容易爆。
我之前也卡在这过,ZeRO-3的显存占用峰值确实比预想高,因为通信buffers和临时激活值会吃不少。你试过把zero_force_ds_cpu_offload设成true吗,或者检查下offload_param的device是不是明确写了cpu,有时候默认值会漏。另外7B在40G卡上就算offload全开,batch size=1也悬,我最后是切成ZeRO-2加cpu offload才跑通的,速度慢点但稳定。你那个35G是单卡峰值还是均值?如果持续涨就可能是碎片化问题,加个zero_optimization.grad_accumulation_steps试试也行。