最近在微调一个7B的Llama模型,单卡A100 80G,尝试用DeepSpeed的ZeRO-3跑,结果一启动就显存爆炸,直接OOM。我查了文档,把offload参数也打开了,optimizer和param都offload到了CPU,batch size降到1,梯度累积也调了,但还是在第一步就崩。
我怀疑是不是我的模型加载方式有问题?或者ZeRO-3需要特殊的模型并行配置?看网上有人说ZeRO-2就够用,但我怕显存不够。有没有大佬遇到过类似情况?是不是我漏了什么关键参数?真诚求教,实在不想为了省钱白嫖半天还跑不起来……
用DeepSpeed跑Llama微调,ZeRO-3总是OOM,是我配置姿势不对吗?
全部回复
共 177 条说实话ZeRO-3的offload不是开了就行,你得确认下是不是把CPU offload的device换成nvme了,或者checkpoint里残留了旧配置。我之前也卡在这,后来发现是zero_optimization段里没写cpu_offload_use_pin_memory,加上之后显存直接降了快一半。另外你试试先只offload optimizer,param留GPU上,7B模型80G其实够放,ZeRO-2说不定真能跑起来,别一上来就上3。
我之前也踩过这个坑,ZeRO-3其实默认会把模型参数也分片,但7B模型直接全量加载进显存做初始化的话,那一下子确实容易爆。你试试在modeling代码里加个from_pretrained(low_cpu_mem_usage=True),或者干脆用HuggingFace的device_map="auto"先让模型分散到CPU再切ZeRO-3,能省不少启动时的峰值。另外offload到CPU后记得把stage3_max_live_parameters和stage3_max_reuse_distance调小点,不然CPU内存狂换页也会拖死你。不过说真的,单卡80G微调7B,ZeRO-2加offload其实完全够,ZeRO-3的通信开销反而更慢,你如果batch size已经1了,不如直接试试ZeRO-2,省心很多。
单卡A100 80G跑7B,ZeRO-3其实有点杀鸡用牛刀了,这阶段offload到CPU的通信开销反而可能成为瓶颈。我遇到过类似情况,最后发现是huggingface的from_pretrained默认会先加载全精度权重到GPU再分片,得用low_cpu_mem_usage=True配合device_map="auto"才行。另外你试试把offload的pin_memory关掉,有时候反而会省显存。如果还不行,直接降到ZeRO-2加梯度检查点,7B用80G完全够跑。
单卡跑7B用ZeRO-3纯属自找麻烦,关掉offload换ZeRO-2试试,大概率直接起飞。
同款配置踩过坑,问题多半不在ZeRO-3本身,而是HuggingFace加载模型时默认先实例化到GPU,再被DeepSpeed接管,这中间峰值直接翻倍。建议用from_pretrained(..., low_cpu_mem_usage=True)配合device_map="cpu",或者干脆用deepspeed.initialize之前先手动把模型挪到CPU。另外offload到CPU时记得设cpu_offload_use_pin_memory,不然数据传输会卡脖子。你试试把zero_force_opt_offload也加上,有时候不显式声明optimizer offload不会生效。
之前跑13B也踩过这坑,ZeRO-3开offload之后显存是下来了但通信开销巨大,第一步反而容易爆。试试把zero_force_ds_cpu_optimizer设成false,或者干脆用ZeRO-2加offload,7B模型80G单卡其实很宽裕。另外确认下你的模型加载是不是用了from_pretrained直接加载全量权重,建议先meta初始化再统一分发,不然每个rank都会复制一份完整模型。
说实话我也踩过一模一样的坑,A100 80G跑7B按理说ZeRO-3不该第一步就炸,八成不是配置问题而是加载逻辑的锅。你试试在model_from_pretrained之前先初始化一下deepspeed,或者直接用deepspeed.initialize把模型包进去,别用原生huggingface的from_pretrained加载完再转,这俩路径的显存峰值差很多。另外offload参数开了不代表就万事大吉,你得确认一下是不是真的把optimizer states和gradient都offload了,有时候只offload了param但忘记开zero_force_opt,照样会爆。还有个野路子,你可以把模型先load到cpu上,然后再用device_map={"": "cpu"}配合zero3的pin_memory,这样能省下不少临时显存。至于ZeRO-2,7B在80G上单卡其实挺稳的,batch size 1加梯度累积完全够用,我后面直接换回ZeRO-2跑通了,省心不少。你要是实在想用ZeRO-3,检查一下stage3_gather_16bit_weights_on_model_save这个参数,有时候它会在保存时疯狂拉显存。最后问一句,你用的是transformers还是accelerate的接口?这俩对deepspeed的兼容性差别挺大的,有时候换个入口就解决了。
看到你说7B单卡A100 80G还开ZeRO-3我就觉得不对劲,这配置其实ZeRO-2完全够用,甚至ZeRO-1加offload都能跑。ZeRO-3本身是为多卡跨节点设计的,单卡上它反而会引入大量通信和内存分区的额外开销,尤其是每个layer都要做all-gather,OOM很可能是被这些临时buffer撑爆的,而不是模型本身占的显存。你先试试把offload的optimizer_state和param都注释掉,只用ZeRO-2,stage2下7B大概需要50多G显存,留出足够余量给激活值。如果还想省显存,可以开activation checkpointing,那个比offload有效得多。另外检查下你加载模型是不是用了from_pretrained然后又把模型塞进DeepSpeed引擎,这个顺序容易出问题,建议直接用deepspeed.initialize把model_engine传进去。我怀疑你漏了zero_force_ds_cpu_offload这个参数,或者没有设gradient_accumulation_steps为1来测试最小配置。最后说句实话,单卡就别折腾ZeRO-3了,省下来的时间够你多跑两轮实验。
单卡80G跑7B其实ZeRO-3不是最优解,你这情况更像是offload配置和通信开销互相打架了。ZeRO-3会把参数分片到每层,但开启offload后每个step都要在CPU和GPU之间来回搬运权重,第一步的峰值显存反而可能比ZeRO-2更高,因为临时buffer和梯度同步都挤在一起。我建议你先试试ZeRO-2加offload optimizer,7B模型本身权重也就14G左右,FP16下ZeRO-2把优化器状态扔CPU,激活值用activation checkpointing,80G完全够跑。你那个OOM是不是还跟混合精度设置有关?checkpoint加载时如果没转成half,CPU offload的数据会被当float32处理,显存直接翻倍。另外确认下你是不是用了zero_force_ds_cpu_optimizer这个参数,有时候它和某些优化器冲突会导致内存碎片。我之前踩过坑是忘了开stage3_gather_16bit_weights_on_model_save,导致加载权重时临时把全量参数拉回GPU,瞬间爆掉。你可以先关掉param offload只留optimizer offload试试,batch size设1但gradient_accumulation_steps调大点,应该能稳住。实在不行就换LoRA吧,7B用LoRA单卡A100跑起来很轻松,效果也不差。
A100 80G单卡跑7B,理论上ZeRO-3不该第一步就炸,你这情况大概率不是显存不够,而是配置里有个隐性坑——ZeRO-3默认会把模型参数也分片,但你如果加载模型时用的是from_pretrained直接load,那参数会先完整进显存再被分片,这一下就超了。我建议你试试先把模型用meta device初始化,再配合deepspeed.initialize,让分片发生在加载前,很多新手都栽在这。另外offload到CPU不是万能的,你optimizer和param都offload了,但CPU内存也得够,而且你提到batch size=1,那梯度累积其实对显存峰值没帮助,它只影响更新频率。我怀疑你漏了zero_force_ds_cpu_optimizer或者zero_allow_untested_optimizer这类参数,某些优化器不被DeepSpeed默认支持时会回退到全精度,直接翻倍。还有个更简单的排查法,先不开offload,纯ZeRO-3跑一个batch看峰值,如果还OOM那就是分片没生效,如果过了再一步步加offload。我自己跑13B也遇到过类似,最后发现是transformers版本和DeepSpeed的兼容问题,升级到最新版就好。实在不行换个思路,用LoRA配ZeRO-2,7B完全够,显存占用能压到30G以下,训练速度还快不少。
之前用ZeRO-3跑13B也遇到过一模一样的坑,单卡A100 80G反而比多卡更容易爆,因为ZeRO-3会把模型参数也分片,但单卡上所有分片还得先汇集到当前设备才能做forward,这个临时buffer吃显存特别狠。你光开offload不够,得把pin_memory和nvme选项也配上,让参数直接走CPU内存或者NVMe,别在GPU上留副本。另外试试stage3_param_persistence_threshold设成0,强制所有参数都走offload路径,默认值有时候会把部分参数留在GPU上。还有个小技巧,先关掉optimizer的offload,只offload参数,因为Adam状态其实比参数本身还占地方,但单卡场景下偶尔会触发奇怪的碎片化问题。要是还崩,干脆降级到ZeRO-2加CPU offload,7B模型实际激活值没那么夸张,ZeRO-2够用了,速度还快不少。最后检查下你dataloader是不是开了num_workers,有时候数据预取也会突然吃满显存。这问题八成不是姿势不对,是ZeRO-3在单卡上设计上就有冗余,别死磕。
遇到过一模一样的坑,后来发现是模型加载时用了from_pretrained默认把权重全塞进显存,ZeRO-3的partitioning根本没生效。试试先model = AutoModel.from_pretrained(..., torch_dtype=torch.float16, low_cpu_mem_usage=True)再包装,或者干脆用deepspeed.initialize时传model而不是先move到cuda。另外确认下你deepspeed版本,旧版对7B这种尺寸的offload有bug,升级到0.10+基本能解。如果还崩,建议直接ZeRO-2加上cpu offload,7B在80G上其实够跑,ZeRO-3反而因为通信开销更容易炸。
说实话你这配置单卡80G跑7B用ZeRO-3本身就是个很拧巴的事儿,ZeRO-3的核心优势是把参数切到多卡上,单卡场景它反而会引入大量通信和内存碎片开销。我怀疑你OOM不是offload没开全,而是模型加载那一步就出了问题,比如你用了from_pretrained直接load模型再扔给DeepSpeed,这样模型完整副本会先占满显存,ZeRO-3的partition还没来得及生效就爆了。我建议你试试用DeepSpeed的zero.Init上下文去初始化模型,或者直接让model_parallel按层切分,很多教程都忽略这个细节。另外你确认一下offload的device是cpu还是nvme,cpu offload也要看你的内存够不够,7B参数加优化器状态全塞内存轻松吃掉几十G,如果你机器内存小于64G照样会卡死。我个人经验是单卡A100跑7B直接ZeRO-2加gradient_checkpointing就够了,batch size哪怕设成1,只要显存能塞下前向+反向的激活,根本不需要把参数挪到CPU。你那个梯度累积调了但没提是否开启pin_memory和num_workers,有时候数据加载也会短暂撑高显存峰值。最后问一句,你DeepSpeed版本是多少?老版本对Llama的attention mask支持有bug,会导致显存分配异常,升级到最新版有时候莫名其妙就解决了。
试试把cpu_offload的pin_memory关掉,有时候这参数在A100上反而会爆显存。
单卡其实没必要上ZeRO-3,ZeRO-2加offload足够7B了,试试把stage改成2然后开offload。
offload开了但显存还爆,八成是没设zero_force_optimizer_steps,试试把stage3_gather_16bit_weights_on_model_save关掉。
说实话你这情况我太熟了,之前用7B模型在A100上跑也撞过这堵墙。ZeRO-3不是光开offload就行,关键是你得确认CPU内存够不够,我猜你把param和optimizer都offload之后,反而因为CPU内存带宽瓶颈导致第一步就爆,因为7B模型全参数加优化器状态在CPU上也得小几十G,加上数据搬运的临时开销,很容易超过物理内存上限。你检查下free -g看swap是不是被吃满了,如果CPU内存小于64G,建议只offload optimizer,param留在GPU上。另外你提到模型加载方式,强烈怀疑你是不是直接用了from_pretrained把全量权重塞进显存,再让DeepSpeed去分片,正确姿势应该用deepspeed.initialize之前先让模型实例化在meta device上,配合zero.Init上下文,让参数按需加载到GPU,不然单卡80G也扛不住7B的原始权重加激活值。还有个小坑,ZeRO-3需要显式设置zero_force_ds_cpu_optimizer为false,不然会跟CPU offload冲突。说实话如果是纯微调不带LoRA,7B单卡用ZeRO-2其实更稳,显存占用大概40G左右,你batch size降到1完全能跑,ZeRO-3那套多机多卡才划算。要不你先试试把offload全关,只留ZeRO-2,看能不能跑通?跑通再慢慢加offload调优也不迟。
试试把reduce_scatter改成allreduce,或者检查下模型是否用了from_pretrained的low_cpu_mem_usage,这俩坑我踩过。
我前几天也踩过这个坑,A100 80G跑7B按理说ZeRO-3不该这么容易爆,你先确认下是不是transformers版本和DeepSpeed的兼容性问题,我之前升级到最新版就解决了。另外offload别全开,optimizer和param都扔CPU反而会拖慢启动阶段,先只offload optimizer试试。还有个小细节,加载模型时记得用low_cpu_mem_usage=True,不然CPU内存会先爆掉导致显存分配异常。
单卡A100 80G跑7B其实ZeRO-2就够了,ZeRO-3主要是为多卡跨节点设计的,单卡上它反而会引入额外的通信和内存碎片开销。你把offload打开后,要注意确认一下zero_force_ds_cpu_optimizer是不是设成了false,不然优化器状态还是可能留在GPU上。另外检查下stage3_gather_16bit_weights_on_model_save和zero3_init这类参数,有时候模型加载时全部参数先进显存也会触发OOM。我之前单卡跑13B用ZeRO-2加offload都很稳,你可以先退回ZeRO-2试试,大概率能跑通。