最近在微调一个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 条单卡A100 80G跑7B其实ZeRO-2完全够,ZeRO-3的通信开销和显存碎片反而容易出问题,你试试把offload全关掉,只用ZeRO-2加梯度checkpointing,batch size开2应该能稳。另外你检查下是不是加载模型的时候用了from_pretrained的默认dtype,fp32会直接翻倍显存,记得转成bf16。我之前遇到类似情况就是加载方式的问题,换成meta device初始化再load state_dict就好了。
单卡80G跑7B其实ZeRO-2完全够,ZeRO-3的offload反而会引入大量CPU-GPU同步开销,第一步就爆可能是你忘了设zero_force_ds_cpu_optimizer或者把stage3_gather_16bit_weights_on_model_save开成了true。试试关掉offload直接用ZeRO-2,batch size开到4,应该能跑起来,我上周刚这么finetune过13B都没炸。另外确认下你是不是用from_pretrained加载时没指定device_map,默认塞GPU0会跟ZeRO抢显存。
单卡A100 80G跑7B还开ZeRO-3,这配置确实有点大炮打蚊子了,offload到CPU之后通信开销会暴涨,第一步崩大概率是CPU内存分配或者NVMe offload没设对。我之前试过类似情况,把zero_optimization里的reduce_bucket_size和allgather_bucket_size调小点,然后确认一下stage3_gather_16bit_weights_on_model_save这个参数,能缓解不少。另外你确认下是不是用了HF的from_pretrained直接加载,那个会先把完整权重放GPU再分片,换成先load到CPU再转成ZeRO-3的分片格式能省一大截显存。真要省事,直接ZeRO-2加offload optimizer其实就够了,7B参数用16位训练峰值也就60多G,你这个卡勉强能塞下。
同款配置踩过坑,你这大概率不是姿势问题,7B在单卡A100上硬上ZeRO-3反而会因通信开销和碎片化内存更早爆。试试直接ZeRO-2加offload,stage2对单卡场景其实比stage3省心得多,显存占用能压到40G左右。另外确认下你加载模型时有没有用low_cpu_mem_usage=True,这个很关键,没设的话光加载就会翻倍吃显存。还有个坑是transformers版本和deepspeed的兼容性,换4.30以上版本会稳很多。实在不行就换QLoRA吧,4bit量化单卡跑7B毫无压力,效果也不差多少。
单卡80G跑7B其实ZeRO-2就够了,ZeRO-3主要是给多卡跨节点用的,单卡上反而会引入额外的通信和碎片开销,参数offload到CPU后第一步优化器更新会有个巨大的内存峰值,我猜你是卡在这。你可以试试ZeRO-2加offload optimizer,或者干脆用LoRA这类PEFT方法,7B全参微调单卡本来就很极限。另外检查下是不是用了HuggingFace的from_pretrained加载时把模型先放CPU再转GPU,那个过程会瞬间翻倍占用,用low_cpu_mem_usage=True能缓解。
单卡A100跑7B还开ZeRO-3属实有点绕远路了,这配置本来是为多机设计的,单卡上反而因为每层都要做all-gather导致临时显存暴涨。我当初也踩过这坑,后来直接换ZeRO-2+offload optimizer,batch size设2,梯度累积8,稳得很。你实在想用ZeRO-3的话,试试把stage3_gather_16bit_weights_on_model_save和stage3_prefetch_bucket_size调小,但我觉得不如直接换方案省心。另外确认下你的CUDA和DeepSpeed版本是不是匹配,有时候版本不对也会莫名OOM。
我盲猜你是把offload全开了,param和optimizer都丢CPU
offload全开还爆的话,八成是模型加载时没走deepspeed的from_pretrained,直接load_state_dict把显存吃满了。
单卡80G跑7B其实ZeRO-2就够了,ZeRO-3主要是为多卡跨节点设计的,单卡上反而会引入大量通信和碎片开销。你试试把offload全关掉,纯ZeRO-2加bf16,batch size开2,大概率能跑起来。另外检查下是不是transformers的from_pretrained加载时把模型放到了GPU上,要先用meta device初始化再让deepspeed接管。我之前也卡在这步,最后发现是cpu offload的pin_memory参数没设false导致内存爆了。
说实话你这个情况我太理解了,上周我刚踩完同一个坑。单卡A100 80G跑7B其实ZeRO-2完全够用,真的不用强行上ZeRO-3,因为ZeRO-3把参数也切分之后,通信开销反而大,而且如果你只是单卡,ZeRO-3的offload逻辑有时候会跟模型加载方式打架,尤其用from_pretrained直接load的话,容易先把全量参数塞进显存再切分,那第一步肯定爆。我之前是把模型先放到CPU上,再用deepSpeed.initialize,同时把offload_optimizer和offload_param的pin_memory设成False,不然CPU内存交换也会卡。另外你可以试试打开zero_force_ds_cpu_optimizer这个参数,有些版本默认不生效,导致optimizer还是留在GPU上。还有一个容易被忽略的点,就是你有没有设zero3_init_flag?如果没设成true,它会在初始化阶段就把模型参数全量放到GPU上,那跟没开offload没区别。实在不行就换ZeRO-2加offload optimizer,7B在80G上跑batch size 2到4都没啥问题,我后来就是这么跑的,稳定得不行。你检查下这几个点,大概率能解决。
单卡80G跑7B用ZeRO-3确实有点尴尬,这配置本身是为多卡设计的,单卡上反而会引入大量通信和碎片化开销。你试试把offload全关掉,纯ZeRO-2加batch size 1,7B的权重加梯度其实勉强能塞进80G。另外检查下是不是模型加载时用了meta device,得确保权重真正分配到GPU上再开始训练。我之前遇到类似情况是transformers版本和deepspeed不兼容,升级一下就好了。
一样的问题我也踩过,A100 80G单卡跑7B其实ZeRO-2完全够,ZeRO-3反而因为通信开销和碎片化更吃显存。你试试把offload全关掉,纯ZeRO-2加batch size 1,大概率能跑起来。另外检查下是否用了transformers的from_pretrained加载,那个会先把模型完整载入显存再分片,换成deepspeed的init脚本能省不少峰值。我之前就是卡在这步,换成HuggingFace的zero.Init后一步到位。实在不行把model并行关掉,纯数据并行反而稳。
说实话你这配置单卡80G跑7B微调,ZeRO-3本身就不是最优解,它主要为了多卡场景设计的,单卡上反而会引入大量通信和碎片化开销。我怀疑你OOM不光是offload没生效,更可能是你在加载模型时用了from_pretrained直接load整个权重,这玩意儿在ZeRO-3下会先往GPU塞一份完整参数,还没等offload就开始炸了。正确姿势应该是先让模型跑到CPU上,用零初始化或者deepspeed的init_empty_weights配合from_pretrained(low_cpu_mem_usage=True)来构造,再让ZeRO-3接管。另外你确认下stage3_param_persistence_threshold和stage3_max_reuse_distance这两个参数有没有调,默认值在单卡上非常激进,会把所有tensor都往CPU倒,反而导致每步都要搬数据,显存没省多少但内存带宽先爆了。我实际试过,单卡A100跑7B微调,ZeRO-2加offload optimizer就足够,峰值显存大概40多G,完全不会OOM,而且速度比ZeRO-3快一倍不止。要不你先试试ZeRO-2,把offload_optimizer打开,stage3相关的全都关掉,batch size用1,梯度累积弄个16,基本能跑起来。还有个坑是transformers版本和deepspeed版本要匹配,新版transformers改了model loading逻辑,老版deepspeed会重复分配显存,建议直接升级到最新版再跑。
ZeRO-3 offload后还是OOM,大概率是模型加载时没用deepspeed.initialize包装,试试用from_pretrained加device_map="auto"。
单卡A100跑7B其实ZeRO-2加offload就够,ZeRO-3反而引入额外通信开销,把stage设回2可能更稳。
说实话你这配置单卡A100 80G跑7B,ZeRO-3确实有点杀鸡用牛刀了,而且大概率不是参数问题,是你把offload打开之后反而踩了坑。ZeRO-3的offload设计初衷是给多卡场景用的,单卡上它会把weights和gradients在CPU和GPU之间来回搬运,每一步通信开销巨大,显存峰值反而可能比纯ZeRO-2更高,因为要留一部分buffer给分片后的临时张量。我建议你直接关掉offload,试一下ZeRO-2加stage3的cpu offload只开optimizer,或者干脆用ZeRO-1加梯度累积,7B模型在80G上其实ZeRO-2就够了,activation用gradient checkpointing压一下。另外你检查下是不是用了transformers的from_pretrained直接加载,那个默认会先创建完整模型再分片,峰值直接爆掉,得用deepspeed.initialize之前先对模型做zero.Init(remote_device='cpu'),让模型参数一开始就在CPU上,然后按需换到GPU。我之前跑13B就是这么干的,单卡A100 80G一点问题没有。你那个batch size 1加梯度累积,如果梯度累积步数设太大,第一步前向传播的中间激活还是会堆积,建议把累积步数降到4以下,同时看看是不是dataloader的num_workers把CPU内存也吃满了。最后实在不行就换个思路,用bitsandbytes的8bit加载,或者直接上QLoRA,效果差不了多少,省心很多。
单卡A100 80G跑7B其实ZeRO-2就够了,ZeRO-3主要是为了多机多卡省显存,单卡反而会引入额外的通信和碎片开销,OOM不奇怪。你试试把offload全关掉,只用ZeRO-2,batch size=1,应该能塞下。另外检查下是不是HuggingFace加载模型时默认把权重放到了GPU,先load到CPU再move到device会省不少峰值显存。
我之前也踩过这坑,ZeRO-3在单卡上经常因为stage3的参数分区和all-gather机制搞出临时buffer,比你想象中吃显存。你干脆直接用deepspeed的zero.Inference模式看能不能跑通前向,或者换adam offload到NVMe试试。实在不行就上LoRA,7B用LoRA跑起来轻松多了,效果也不差。
试试把zero_force_disable_cpu_check加上,另外确认下用的是deepspeed的model_engine而不是原生model。
单卡A100 80G跑7B微调,说实话ZeRO-3反而是最容易踩坑的,因为它的设计初衷是多卡场景下最大化显存利用率,单卡上你把param和optimizer都offload到CPU,第一步就得把整个模型参数从CPU搬回GPU,来回倒腾反而可能触发显存碎片或者临时缓冲区的峰值暴涨,我怀疑你看到的OOM不是真的存不下,而是某个瞬间的峰值冲爆了。你试试把ZeRO-3换成ZeRO-2,然后只offload optimizer,param留在GPU上,7B的fp16权重才14G,加上梯度和优化器状态,A100 80G理论上绰绰有余。另外检查一下你是不是用了flash attention或者某些kernel会预分配激活内存,有时候这些组件会在step之前就吃掉几个G。还有个野路子,你可以用deepspeed的zero.Init上下文去惰性加载模型,配合pin_memory和num_workers=0,能明显降低初始峰值。如果还是崩,干脆别用DeepSpeed,直接HuggingFace的gradient_checkpointing加torch原生混合精度跑,7B单卡完全没压力,你省的这点显存都够再塞一个LoRA了。我之前试过类似配置,ZeRO-3光初始化就浪费了快10G,换成ZeRO-2之后稳定跑完,速度还快了。你确认下你的NVMe或者内存是不是够大,CPU offload如果swap太频繁也会假性OOM,最后问你一句,你那个batch size=1是不是还开了eval模式同时加载了验证集?这也会悄悄吃显存。
遇到过一模一样的坑,A100 80G单卡跑7B其实ZeRO-2完全够,ZeRO-3是给多卡跨节点用的,单卡开它反而会多出一堆索引和通信开销。你offload全开的话,第一步初始化就要把整个模型状态都搬一遍,CPU内存和PCIe带宽反而成了瓶颈,OOM很正常。建议直接把zero_optimization.stage改成2,offload只留optimizer,param别动,batch size哪怕保持4也能跑。另外检查下你是不是用了from_pretrained加载,换成meta device初始化再deepspeed.initialize,能省不少临时显存。
单卡A100 80G跑7B微调,说实话ZeRO-3确实有点大炮打蚊子了,这配置本身就是为了多卡场景设计的,单卡上它反而会引入大量跨节点通信开销,显存没省多少,CPU offload的传输瓶颈倒是先把你卡死了。你试试把zero_optimization改成stage 2,关掉offload,然后开个torch.compile或者直接bf16混合精度,7B全参数微调其实80G是能塞下的,我跑过类似规模,显存峰值大概在65-70G左右。另外确认一下你是不是用HuggingFace的from_pretrained加载的,那个会默认把模型权重复制一份到CPU再搬上GPU,换成low_cpu_mem_usage=True能省不少临时内存。还有个小坑,ZeRO-3的partition_size要设成和GPU数一致,单卡时设1反而会触发奇怪的显存分配。要是还崩,干脆把offload全关了,纯ZeRO-3单卡就是个伪命题,不如直接用原生PyTorch的activation checkpointing加gradient checkpointing,效果更直接。你那个OOM是发生在模型加载阶段还是前向传播阶段?如果能贴下完整日志,基本一眼就能定位。
说实话ZeRO-3在单卡场景下确实容易踩坑,它本来是为多卡通信设计的,单卡跑反而会引入额外的索引开销和碎片化问题,你试试把stage改成2,offload还是照开,7B模型用A100 80G其实很有希望跑起来的。我之前跑13B也是卡在同样的地方,后来发现是cpu_offload的pin_memory参数没设成false,默认true会导致内存分配爆炸,你检查下这个。还有个小技巧,加载模型时用low_cpu_mem_usage=True能省不少CPU侧的内存,虽然看起来跟显存无关,但有时会间接影响ZeRO的内存规划。另外确认下你的模型是不是用from_pretrained直接加载的,如果先load_state_dict再包装deepspeed,那个临时状态字典会吃掉几乎一倍显存,必须用deepspeed.initialize直接接管。最后建议你把zero_force_opt_offload打开,有些版本不强制卸载优化器状态的话,它会在第一阶段偷偷把Adam的momentum留在GPU上。实在不行就降到ZeRO-1试试,7B其实纯数据并行加梯度累积也能塞进80G,只是慢点,至少能跑通。
试试ZeRO-2加CPU offload吧,7B单卡真没必要上ZeRO-3,大概率是通信开销把显存吃满了。