最近在微调一个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-3是有点杀鸡用牛刀了,offload到CPU后通信开销反而可能拖垮显存。你试试ZeRO-2加offload optimizer,通常这个规模根本用不上ZeRO-3,我跑13B才开。另外检查下model.from_pretrained里有没有加device_map="auto",有时候默认加载到GPU0导致峰值暴涨。还有个坑是zero_optimization里stage3_gather_16bit_weights_on_model_save这个参数,加载时临时占用会翻倍,关掉能省不少。要是还崩,把stage3_prefetch_bucket_size调小点,默认值太大了。
我当初也踩过这个坑,7B模型在单卡上硬上ZeRO-3确实容易第一步就炸,因为ZeRO-3本身会引入额外的通信和状态管理开销,哪怕offload了,前向传播时每个layer的参数都要从CPU拉回来,这个临时buffer占的显存有时候比想象中大得多。你检查下stage3_prefetch_bucket_size和stage3_param_persistence_threshold这两个参数,默认值可能不适合单卡场景,通常需要把param_persistence_threshold调大点,比如设成5e8,让更多参数留在CPU,减少频繁换入换出。另外,你用的是HuggingFace的from_pretrained加载的话,记得加上low_cpu_mem_usage=True,不然模型初始加载到CPU再往GPU搬的过程也会瞬间爆掉。还有个偏门但有效的方法——直接试试ZeRO-2加offload,7B模型在80G卡上,纯ZeRO-2不开offload其实就能塞下,batch size=1的话显存大概40G左右,你之前怕不够可能是被网上那些讨论吓到了,实际跑一下比纠结配置强。最后确认下你的DeepSpeed版本,之前有些版本在Llama的attention实现上有bug,升级到0.9.2以上或者直接用transformers集成的accelerate可能更稳。如果还不行,干脆把模型切成4-bit用QLoRA,效果差不了多少,但省心太多了。
说实话你这个问题我也踩过,7B单卡A100 80G跑ZeRO-3确实有点尴尬,因为ZeRO-3的核心是分片参数,但单卡场景下它反而会引入大量通信和临时缓冲区开销,尤其你把offload全开了以后,CPU内存和PCIe带宽很容易成为瓶颈,第一步要做的其实是把optimizer offload关掉,只留param offload试试。另外你检查过zero_force_ds_cpu_optimizer这个参数没?有时候默认会强制把优化器状态也塞到CPU,跟你的offload设置叠一起反而更吃内存。模型加载方式大概率没问题,但你可以确认下是不是用了from_pretrained直接加载,然后用deepspeed.initialize包装,这里有个坑是如果没设zero3_init标志,模型参数会先完整进GPU再被分片,那第一步就炸很正常。我之前跑13B单卡倒是用ZeRO-2加offload跑通过,batch size调成1,梯度累积32,效果虽然慢但至少不崩,你可以先降级到ZeRO-2试试,毕竟7B的显存占用理论上是够的,重点可能是你max_seq_len没限制,输入padding太长直接撑爆。最后检查下stage3_gather_16bit_weights_on_model_save这个参数,它虽然影响保存,但有些版本会在加载时触发额外显存分配,关掉能省不少。
offload后还OOM大概率是显存碎片化,试试开zero_force_opt_offload加pin_memory,能省不少。
把cpu_offload改成nvme试试,7B参数全塞CPU内存也够呛,我上次这么搞直接卡死。
单卡A100 80G跑7B,其实ZeRO-3本身不是必须的,这卡显存足够放下fp16的7B权重加梯度,主要瓶颈在优化器状态。你开offload之后,CPU内存和PCIe带宽反而可能成为瓶颈,第一步要初始化所有参数,CPU和GPU之间来回倒腾,OOM不一定只算显存,也可能是你的CPU内存被爆了或者通信卡死。我建议你先试试ZeRO-2加上offload optimizer,不开param offload,因为param offload会引入大量小张量传输,速度慢还容易炸。另外检查一下你是不是用了HuggingFace的from_pretrained直接加载,那个会把模型先完整加载到CPU再转移到GPU,中间峰值会翻倍,最好用device_map='auto'或者先load到meta设备上再让DeepSpeed处理。还有个坑,如果你开了ZeRO-3,但没设zero_force_ds_cpu_optimizer=False,有些版本会默认把优化器强制放CPU,导致显存没省多少反而卡死。最后实在不行,把batch size设成1,gradient_accumulation_steps设大,然后看下你是不是忘了设zero3_init_flag,那个没开的话ZeRO-3的显存分区根本不生效,等于白配。我上次也卡这里,查了半天是版本不兼容,降级到0.9.0就好了,你可以试试。
单卡A100 80G跑7B微调,理论上ZeRO-3不该一上来就炸,但你提到的“一启动就OOM”其实挺典型的——多半不是显存不够,而是你模型加载时把参数和优化器状态全塞进显存了。ZeRO-3的offload是分stage的,你光开offload参数不够,得确认zero_optimization配置里offload_optimizer和offload_param的device都明确设成了cpu,而且pin_memory别开,有时候反而会占住显存。另外,你检查过model_parallel或者tensor_parallel相关的设置吗?如果用了HuggingFace的from_pretrained直接加载,模型默认会先完整放到GPU上,再交给DeepSpeed接管,这个瞬间就爆了。我之前遇到过类似问题,解决办法是先with torch.device('meta')初始化模型,再让DeepSpeed的deepspeed.initialize去分配权重,这样能省掉那一段峰值。还有,你把zero_allow_untested_optimizer和zero_force_ds_cpu_optimizer都设成true试试,有些优化器版本不认。说实话,单卡80G跑7B用ZeRO-2加offload就够,ZeRO-3反而因为通信开销和碎片化更容易卡,你不如先把ZeRO-2的offload_optimizer打开,batch size设4,看看能不能跑通第一步。要是还崩,你把stage3_max_live_parameters和stage3_max_reuse_distance调小,这两个参数控制显存里保留的参数量,默认值对7B来说太激进了。最后提醒一句,看下你的CUDA版本和DeepSpeed是不是匹配,我之前因为CUDA 12.1配了旧版DS,直接OOM还报错,升级到0.10.3就好了。
试试先把模型用from_pretrained加载时加个low_cpu_mem_usage=True,八成是加载时已经爆了,跟ZeRO关系不大。
单卡80G跑7B用ZeRO-3确实有点杀鸡用牛刀了,这配置offload反而会频繁搬数据,第一步就崩很可能是内存带宽瓶颈而不是显存问题。我之前试过类似情况,直接换ZeRO-2加gradient checkpointing,batch size开到4都能稳跑,速度还快不少。你那个offload参数是不是把optimizer和params都设成cpu了?可以试试只用optimizer offload,或者干脆检查下是不是transformers版本和deepspeed的stage3不兼容,换个组合可能就好了。
单卡80G跑7B其实ZeRO-2完全够,ZeRO-3那套offload反而容易在通信和内存碎片上翻车。我之前也踩过这坑,后来发现把offload里的pin_memory关掉,再设个stage3_gather_16bit_weights_on_model_save=true就能跑起来。你试试看把zero_optimization里的reduce_bucket_size和stage3_prefetch_bucket_size调小点,默认值太激进了。还有模型加载用from_pretrained别带device_map,让DeepSpeed自己管分配。
试试ZeRO-2加CPU offload吧,7B单卡真没必要硬上ZeRO-3,配置坑太多。
八成是模型加载时没走from_pretrained的device_map,导致每层重复占显存了。
单卡A100 80G跑7B其实ZeRO-2完全够用,ZeRO-3的offload反而会因为CPU通信瓶颈在第一步就卡爆显存,我之前也是这么折腾到怀疑人生。你试试把zero_optimization改成stage:2,然后offload_optimizer留空,只开offload_param,batch size直接拉满到4试试。另外记得检查一下模型加载是不是用了from_pretrained的low_cpu_mem_usage=True,这个容易漏掉。如果还崩,把zero_allow_untested_optimizer设成true,有些算子不兼容会莫名爆显存。
说实话单卡A100 80G跑7B微调,ZeRO-3反而容易把自己搞死,因为stage3每个layer都要collective通信,光激活值和梯度同步的开销就够呛。建议直接换ZeRO-2加offload,7B的参数量16G左右,优化器状态和梯度offload出去后显存完全够用,我上周刚这么跑过13B都没问题。另外你检查下是不是把train_batch_size和train_micro_batch_size_per_gpu搞混了,DeepSpeed里这俩不一样,前者会隐式累加梯度导致第一步就爆。还有个坑是transformers加载模型时记得用low_cpu_mem_usage=True,不然光model.to(device)那一下临时拷贝就会顶爆显存。
单卡A100 80G跑7B微调,ZeRO-3理论上不该第一步就OOM,你八成是撞上显存碎片或者模型加载时的峰值了。我之前用6.7B的模型也踩过类似的坑,后来发现offload到CPU的配置里有个关键参数叫pin_memory,默认开的话会锁页内存,反而拖慢速度还加剧显存交换,建议你先把它关了试试。另外ZeRO-3是会把参数分片到各卡上的,但单卡场景下它并不会减少单卡显存占用,反而因为多了一层分区元数据开销更费显存,所以很多人单卡直接用ZeRO-2甚至干脆不用,效果反而好。你batch size都降到1了还崩,大概率是模型加载时用了from_pretrained默认的float32缓存,试试加载时直接指定torch_dtype=torch.float16,能省下快一半显存。还有个小技巧,把optimizer换成AdamW的8bit版,比如bitsandbytes的,能再挤出几个G。真不行就上LoRA或者QLoRA,7B用LoRA在80G卡上轻松跑,效果也不差,别死磕全量微调。
单卡A100跑7B用ZeRO-3纯属给自己上难度,ZeRO-2加offload完全够,先把stage切回2试试。
单卡A100 80G跑7B其实ZeRO-2完全够,ZeRO-3反而因为引入通信和offload开销,第一步初始化时会把模型参数和优化器状态全塞进显存做分片,这时候OOM很正常。你试试把zero_force_ds_cpu_offload和zero3_init_flag设成false,用huggingface的from_pretrained先加载到CPU再转deepspeed,能避开启动峰值。另外确认下你的offload是不是只开了optimizer没开param,我上次就是漏了param offload导致瞬间爆掉。实在不行就换ZeRO-2加CPU offload optimizer,7B这个量级真没必要上ZeRO-3。
ZeRO-3 offload全开还OOM,八成是没设zero_force_opt_offload或者模型加载时没先meta device初始化。
说实话看到你这个情况我第一反应是,单卡A100跑7B用ZeRO-3本身就是个很拧巴的事,ZeRO-3的核心价值在于跨节点分片,单机单卡上它只增加通信开销和内存碎片,反而更容易把显存撑爆。我之前在单卡上试过类似配置,后来发现真正的问题是HuggingFace的from_pretrained会把完整权重先加载到CPU再分片,你offload到CPU反而让CPU内存先爆了,而且ZeRO-3默认会为每个layer保留临时buffer,这些buffer在forward时合体,瞬间显存峰值远高于你设的batch size预期。你试试把zero_force_ds_cpu_optimizer设为False,或者干脆用ZeRO-2加offload,7B模型在80G上ZeRO-2其实完全够用,甚至不用offload optimizer,只要把activation checkpointing打开,batch size 1加梯度累积就能跑。另外检查一下你是不是忘了设zero3_init_flag,用deepspeed.zero.Init包裹模型加载,这个不设的话权重会先进显存再被分片,等于没省。我上次也是卡在这,折腾两天最后发现是模型加载时没走zero3的lazy_init机制。你可以先跑个不带offload的ZeRO-2试试,如果还OOM再回来调ZeRO-3,别一上来就全上。
试试把zero_3的reduce_bucket_size和stage3_gather_16bit_params_weight_on_scale调小点,我调完就稳了。
单卡A100 80G跑7B微调,ZeRO-3其实有点杀鸡用牛刀了,这阶段offload反而容易因为CPU通信瓶颈卡在第一步。我之前也踩过这坑,后来直接换ZeRO-2 + 梯度检查点,batch size开到4都稳得很。你检查下是不是把reduce_bucket_size和pin_memory给漏了,这两个不设对显存影响挺大的。另外模型加载时记得用from_pretrained(device_map="auto"),不然模型会先全量占显存再开始offload,那就必炸。
单卡A100 80G跑7B,ZeRO-3不开offload本来也就勉强塞得下,你全offload了反而可能因为CPU-GPU传输瓶颈卡在第一步。试试只offload optimizer,把param留在GPU上,然后确认一下你是不是用了HuggingFace的from_pretrained加载,那个会先创建全量模型再分片,肯定炸。顺手把stage3_gather_16bit_weights_on_model_save关掉,能省不少临时显存。
我之前也踩过这坑,最后是换成ZeRO-2加offload optimizer才跑通的,batch size 1加梯度累积照样能训,速度还比ZeRO-3快不少。你换个思路试试,别死磕ZeRO-3,7B真没必要上这个级别。
还有你检查过NVMe offload的配置吗?如果设了分片大小太小,会疯狂读写磁盘,显存不炸但卡死。先看看日志里有没有什么warning,大概率是某个参数类型不匹配。