最近在微调一个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 条试试把KV cache关掉,或者换ZeRO-2加CPU offload,7B单卡真没必要硬上ZeRO-3。
八成是模型加载时没走from_pretrained的low_cpu_mem_usage,另外检查下是否忘了设zero_allow_untested_optimizer。
单卡A100 80G跑7B,ZeRO-3其实有点杀鸡用牛刀了,这玩意儿是为多卡跨节点设计的,单卡上光把参数分片打散就得吃掉不少额外显存,反而比ZeRO-2更容易爆。你把offload开了还是崩,八成是卡在activation上,试试在模型配置里把gradient_checkpointing打开,能省下大头。另外确认下是不是加载了原版fp32权重,用bf16或者fp16初始化能直接砍一半显存。我上次跑13B就是用ZeRO-2加offload,batch 1稳稳的,你可以先退回去试试,省得白折腾。
同款配置踩过一模一样的坑,当时也是单卡A100 80G跑7B,ZeRO-3一开就秒挂。后来发现大概率不是参数没调对,而是ZeRO-3在单卡场景下根本不会触发它真正的partition逻辑,反而会额外多出一堆进程间通信和显存占用,尤其你把offload全开之后,7B的模型参数+优化器状态+梯度全往CPU塞,但CPU内存带宽和PCIe传输会成为瓶颈,第一步前向传播可能就卡死或OOM了。我后来直接换回ZeRO-2,offload只开optimizer,batch size开到4,稳定跑完整个LoRA微调流程,显存峰值大概在60-70G左右。你如果非要ZeRO-3,建议先检查一下是否真的加载了train_state,有些时候huggingface的from_pretrained会预占显存,可以试试用meta device初始化再load_state_dict,另外确认一下zero_force_ds_cpu_optimizer是不是设成false了,那个默认值在部分版本里会导致cpu adam分配异常。纯个人经验,建议先跑个batch的profile看看显存分配曲线,别急着归咎于OOM,大概率是配置里某个隐藏开关没对齐版本。
试试把zero_optimization里的reduce_bucket_size调小点,另外确认下模型是用from_pretrained加载的,不是自己手动初始化的。
说实话ZeRO-3在单卡上本来就容易踩这种坑,它设计初衷是多卡场景下把参数分片到各卡,你单卡跑其实分片没意义,反而多了层通信和重构开销,显存管理逻辑也更激进。你把offload全开了,但注意ZeRO-3默认会把模型参数也按层切分,加载的时候需要先完整读一遍再分片,这瞬间峰值内存可能就爆了。我建议你换个思路,直接用ZeRO-2加offload,7B模型在80G上其实很宽裕,哪怕全参数微调也能塞下。我之前跑13B单卡A100就是这么干的,ZeRO-2不开offload,batch size 2,梯度累积8,稳得很。你那个batch size 1还OOM,大概率是模型加载时没走from_pretrained的低内存模式,试试加low_cpu_mem_usage=True,或者先meta设备初始化再加载权重。还有个骚操作,把model.half()改成bfloat16,某些层能省不少显存。如果实在想用ZeRO-3,记得关掉zero_force_ds_cpu_optimizer,有时候这个默认选项会跟你手动offload冲突。反正别死磕ZeRO-3,单卡真没必要。
同样配置踩过坑,大概率不是模型加载的问题,而是ZeRO-3默认把所有权重都切分到各rank上,单卡场景反而会引入额外通信和临时buffer开销,显存峰值比ZeRO-2还高。你试下把zero_force_ds_cpu_offload设成false,或者干脆换ZeRO-2加offload,7B在80G上其实绰绰有余。另外确认下你是不是用了from_pretrained直接加载,最好先load_state_dict再包装deepspeed,不然容易重复占一份CPU内存。
我之前也踩过类似的坑,ZeRO-3的offload不是开了就行,关键得看stage3_param_persistence和stage3_gather_16bit_weights_on_load这几个参数,默认值在7B上很容易把显存吃满。你试试把stage3_max_live_parameters和stage3_max_reuse_distance调小一点,比如1e8和1e9,能显著减少峰值显存。另外加载模型时别用from_pretrained直接load,先用meta device初始化再让DeepSpeed接管,这样能省一大截。还有个小偏方,如果你只是微调,ZeRO-2加offload其实完全够跑7B,没必要硬上ZeRO-3,我后来换回ZeRO-2反而稳了。
你这配置看着没问题,但7B单卡上ZeRO-3还不如ZeRO-2省心,先把offload全关了试试。
单卡A100 80G跑7B其实ZeRO-2就够了,ZeRO-3那套offload到CPU的IO开销反而容易在第一步初始化时把内存打爆,你试试把offload全关掉,只留ZeRO-2的stage,batch size调到4左右,我上次就是这么跑通的。另外你模型加载是不是用了from_pretrained直接塞进GPU?先load到CPU再手动移到device上能省不少峰值显存。还有个小坑,检查下是不是把train_batch_size和train_micro_batch_size_per_gpu搞混了,前者是全局的,后者才是单卡实际值,我之前就是被这个坑了半天。
- 你这情况我太熟了,当时用ZeRO-3跑13B也是第一步直接爆,后来发现是模型加载时用了from_pretrained没加low_cpu_mem_usage,导致CPU内存也炸了,连带显存被拖垮。
- 其实ZeRO-3的offload不是光开参数就行,还得配一下zero_force_ds_cpu_offload和pin_memory这些,不然数据搬移效率极低,反而更容易卡在初始阶段。
- 另外你试试把stage3_gather_16bit_weights_on_model_save设成true,这能省不少临时显存,虽然保存模型慢点,但至少能跑起来。
- 不过说实话,单卡80G跑7B用ZeRO-2加offload就够了吧,ZeRO-3更吃通信和调度,单卡上反而可能因为碎片化更糟心。
- 我上次是直接换成了FSDP,配置简单多了,而且默认行为更稳,你可以先拿它排除下是不是DeepSpeed版本的问题。
同款配置踩过坑,问题大概率出在模型加载上,你用from_pretrained的话记得加上low_cpu_mem_usage=True,不然ZeRO-3的partition逻辑会和HF的加载流程冲突。另外offload到CPU后,你检查过NVMe offload的路径权限没?我之前就是忘了设这个,看起来是offload了实际还在GPU上。还有个小技巧,可以先试试ZeRO-2+offload,7B模型80G其实够跑,没必要死磕ZeRO-3,我最后就是这么解决的。
你这配置看着没啥毛病,大概率是模型加载时没走deepspeed的初始化流程,试试from_pretrained里加上device_map="auto"。
我上次也是折腾半天,最后发现是tokenizer导致显存预分配爆了,换成ZeRO-2加offload反而稳得很。
单卡A100 80G跑7B,理论上ZeRO-3不该第一步就炸,你这情况八成不是显存不够,是配置里有个隐形坑。我之前也遇到过类似问题,后来发现是zero_optimization里忘了设stage3_gather_16bit_weights_on_model_save,但那个不影响训练,真正关键的是stage3_max_live_params和stage3_max_reuse_distance,这俩参数默认值在小batch下会疯狂缓存激活值,你得手动调小,比如设成1e8甚至更低。另外你确认offload的pin_memory开没开?没开的话CPU offload会变成瓶颈,反而导致临时显存峰值暴涨。还有,你加载模型是不是用了from_pretrained直接全量load?那会先把权重塞满显存再分片,等于白给,正确做法是先init_empty_weights再用load_with_checkpoint。我上次就是这么救回来的,batch size直接能上4。你再试试把zero_force_ds_cpu_optimizer设成false,有时候默认的CPU Adam会额外吃显存。要是还不行,干脆降到ZeRO-2加offload,7B参数其实CPU内存够的话完全能跑,速度还更快。
试试把zero_optimization里的stage3_gather_16bit_weights_on_model_save设为true,另外确认下你是不是用from_pretrained加载后没调get_moe_param?
offload加cpu别全开,留点给gpu,顺手把zero3的allgather分块大小调小点,我之前就是这么救回来的。
说实话ZeRO-3在单卡场景下反而容易出问题,它本来就是为多卡跨节点设计的,单卡上开offload后通信开销和内存碎片管理反而会拖垮你。我之前也踩过这个坑,最后发现是HuggingFace的from_pretrained默认会先把权重加载到GPU再分片,这时候你直接换用device_map='auto'配合accelerate可能会好点,或者干脆用meta device初始化再让DeepSpeed接管。另外你确认一下stage3_param_persistence_threshold和stage3_max_live_parameters这两个参数没调过?默认值在7B模型上特别容易导致临时显存峰值,把max_live_parameters设小到1e8试试。还有个小技巧,可以先不加载optimizer状态,用stage3_gather_16bit_weights_on_model_save=True让权重在保存时才聚合,训练时全程保持分片状态。如果还是崩,我建议直接退回ZeRO-2加offload,7B模型80G其实完全够用,我跑过13B都行,关键是你得把zero_optimization下的reduce_bucket_size和allgather_bucket_size调小到5e7左右,不然通信缓冲也会吃显存。反正别迷信ZeRO-3,单卡场景下它就是个伪需求。
试试把zero_force_ds_cpu_optimizer设为false,或者干脆用ZeRO-2加offload,7B单卡真没必要上ZeRO-3。
八成是模型加载时直接进了显存,试试先把模型放CPU再转deepspeed,或者用zero.Init上下文包裹一下。
single GPU上ZeRO-3纯属给自己找事,ZeRO-2加offload足够跑7B了,别死磕。
说到这个我踩过一模一样的坑,当时也是7B+单卡A100,ZeRO-3开offload反而比ZeRO-2更容易炸。你试试把stage3_gather_16bit_weights_on_model_save和stage3_prefetch_bucket_size调小点,另外确认下你是不是用了transformers的from_pretrained加载,那个会先把全量权重塞进显存。
还有个关键点,ZeRO-3配offload必须开zero_force_ds_cpu_optimizer,不然optimizer状态还是留GPU上。我最后干脆换回ZeRO-2+offload,batch size开到4反而稳稳跑完,速度还快不少。你可以先拿个小数据集验证下配置,别一上来就全量冲。
之前跑7B也踩过这个坑,ZeRO-3的显存开销其实藏得挺深,光是offload参数打开还不够,像stage3_gather_16bit_weights_on_model_save和reduce_scatter这些细节都会影响显存峰值,建议去DeepSpeed的GitHub issue里翻翻。另外你模型加载是不是用了from_pretrained直接拉?换成meta device初始化再加载权重能省不少内存。我后来干脆降到ZeRO-2加offload,batch size开2,反而稳得很,速度还快一截。你试过把zero_allow_untested_optimizer设成true吗?有时候是优化器兼容的坑。
同款配置踩过坑,你八成是卡在ZeRO-3的partition size和CPU offload的交互上了,试试把stage3_gather_16bit_weights_on_model_save和stage3_prefetch_bucket_size调小一点,或者直接关掉offload_param只留optimizer offload,我上次这么搞就过了。还有,你模型加载用的from_pretrained是不是默认把全部权重塞进GPU了?加个low_cpu_mem_usage=True能省一大截。另外,7B其实ZeRO-2加offload完全够跑,别死磕3,我最后就是用2跑通的,速度还快不少。