最近在微调一个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 条这情况我也踩过坑,7B模型单卡A100 80G跑ZeRO-3确实容易OOM,尤其是一启动就崩。建议你检查下zero_optimization.stage3_gather_16bit_weights_on_model_save是不是设成了false,另外试试把gradient_accumulation_steps提到8以上,同时关掉offload_optimizer只offload param,我这样改完就没崩过了。你用的是transformers的from_pretrained加载吗?可以加个torch_dtype=torch.float16试试。
老实说,ZeRO-3在单卡A100 80G上跑7B模型还OOM,确实有点反直觉,毕竟理论上这卡显存是够的。我猜问题可能出在模型加载方式上——你用from_pretrained直接加载的话,它会先把完整模型参数塞进显存,然后DeepSpeed再尝试shard,但这时候已经炸了。我自己的经验是,得用deepspeed.initialize之前先把模型放到CPU上,或者用huggingface的device_map='auto'结合load_in_8bit试试,虽然精度会降点但至少能跑起来。另外,offload开太多其实不一定好,比如optimizer offload到CPU后,反向传播时频繁搬运数据反而可能让显存碎片化更严重,我见过有人把optimizer offload关掉只开param offload反而跑通了。还有个小细节,ZeRO-3默认会为每个参数保留元数据,你试试设zero_force_ds_cpu_optimizer=false或者调大reduce_bucket_size,说不定能省点显存。至于ZeRO-2,其实单卡场景下它确实更友好,因为不需要跨卡通信的额外开销,7B模型用ZeRO-2加offload大概率能跑,我见过有人batch size开到4都没问题。最后,检查下你是不是还开了什么奇怪的gradient checkpointing或混合精度配置,有时候这些和ZeRO-3的stage3会有冲突导致显存暴增。
ZeRO-3对7B模型单卡确实容易炸,试试把stage3_max_live_parameters和max_reuse_distance调小点。
单卡A100 80G跑7B用ZeRO-3确实容易OOM,我之前也踩过这个坑。你试试先把offload的optimizer和param都关掉,只用ZeRO-2加上阶段性的梯度累积,7B模型其实够用了。另外检查下是不是加载了deepspeed的默认config,有个stage3_gather_16bit_weights_on_model_save参数可能吃内存。实在不行换个方法,用bitsandbytes的4bit量化微调,显存占用直接减半。
A100 80G跑7B按理说ZeRO-3不该第一步就炸,检查下是不是模型加载时没做deepspeed.initialize,或者offload参数里忘了加pin_memory。我之前也遇到过类似情况,后来把ZeRO-3的stage3_gather_16bit_weights_on_model_save改成true才稳住,你可以试试。另外确认下transformers版本是不是太新,有时候跟DeepSpeed兼容性会有坑。
实测7B用ZeRO-3确实容易踩坑,尤其单卡A100 80G时,offload全开反而可能因为CPU-GPU传输瓶颈导致显存碎片化。建议试试只offload optimizer,或者干脆切ZeRO-2加gradient checkpointing,我这样跑7B的QLoRA峰值也就40G左右。另外检查下huggingface加载模型时有没有设low_cpu_mem_usage=True,这个也容易忽略。
试过把activation checkpointing打开吗?这个对节省显存效果很显著,ZeRO-3配合offload时如果没开这个,第一步优化器初始化就可能爆掉。另外确认下你的模型加载是不是用了from_pretrained(..., torch_dtype=torch.float16),A100跑7B用fp16能省一半显存。我上次也是卡在第一步,把这两项加上就稳了。
你这情况我跑13B时也遇到过,7B单卡A100按理说不该这么惨。建议检查下model parallel或tensor parallel是不是默认开了,ZeRO-3有时会跟HuggingFace的auto模型加载冲突,手动指定一下device_map="auto"试试。另外offload到CPU后记得把pin_memory关掉,不然容易爆CPU内存连带炸显存。我最后换回ZeRO-2配gradient checkpointing反而稳了,batch size能提到4。
这情况我也遇到过,A100 80G按理说跑7B微调不至于一上来就炸,但ZeRO-3的显存开销其实比想象中高,尤其你全量微调而不是LoRA的话。我怀疑问题不在offload,而在模型加载时默认把所有参数都塞进了GPU,ZeRO-3虽然分片了,但初始化的分配逻辑可能没及时生效。你可以试试在加载模型前先设置deepspeed.zero.Init()上下文,让模型参数在CPU上初始化再分片,很多开源脚本会忽略这一步。另外检查下是否开了pin_memory和num_workers,这两个偶尔会导致CPU内存撑爆间接影响显存。还有个歪招:把offload_param的device设为nvme试试,虽然慢但能验证是不是纯显存瓶颈。如果不想折腾,直接换ZeRO-2加batch size 1大概率也能跑,7B模型单卡ZeRO-2其实挺稳的,offload都不用开。你用的transformers版本是不是太新了?4.30以后的版本有些兼容性坑,回退到4.28试试。
老实说,你这个问题我摸索过好几轮,A100 80G跑7B模型用ZeRO-3确实容易翻车,特别是刚启动那会显存峰值会很高。我猜你可能是默认加载了全量模型参数到GPU,ZeRO-3虽然会分片,但初始化阶段如果没配合pin_memory或者stage3_gather_16bit_weights_on_model_save这些参数,很容易在第一步炸掉。建议你先试下deepspeed.initialize之前用model.half()把模型转成fp16,或者直接调低stage3_max_live_parameters和stage3_max_reuse_distance,这两个默认值对7B模型有点激进。另外,offload到CPU后记得检查下系统内存是不是够大,我有次就是CPU内存被swap吃满导致假死。其实ZeRO-2配合gradient checkpointing对7B模型完全够用,显存占用大概30G出头,没必要硬上ZeRO-3给自己找麻烦。你不如先跑个ZeRO-2试试,如果还OOM,八成是代码里有什么变量没释放,或者dataloader的num_workers设太多占用了显存。
试试用ZeRO-2加cpu offload,显存占用比ZeRO-3稳定不少,7B单卡跑起来没问题。
这个思路不错,收藏了。
说实话,你这个情况我太熟了,之前用ZeRO-3跑7B的时候也卡在第一步OOM,后来折腾了好久才发现问题可能不在offload,而是模型加载时显存被临时占满没释放。A100 80G跑7B按理说ZeRO-3应该是够的,但你试试把model的offload也打开,或者先加载half精度版本,有些transformers的默认加载方式会先吃满显存再切offload。另外有个坑是ZeRO-3需要配合stage3_gather_16bit_weights_on_model_save这个参数,否则显存管理会有冗余。我还试过一个方法,就是先不用DeepSpeed,直接把模型用device_map="auto"加载到CPU,再手动分配层到GPU,虽然麻烦但能绕过第一步的初始化峰值。至于ZeRO-2,其实7B在80G上单卡跑batch size 1带gradient checkpointing是没问题的,我试过能跑起来,但如果你需要更大的batch,ZeRO-3确实更理想,只是配置要抠得很细。你检查下deepspeed的config里是否设了optimizer的offload到cpu的pin_memory?这个忘记开的话,offload效率会下降很多。最后建议你在启动前先跑个小测试,比如用torch.cuda.memory_summary()看下每个步骤的显存峰值,定位是模型本身还是优化器状态炸的。
A100 80G跑7B模型按理说ZeRO-3不应该第一步就崩,可能是加载模型时没把权重分布到多卡状态里,或者你offload配置的顺序不对,试试把optimizer_states和parameters的offload都设成cpu,但pin_memory关掉,有些版本pin会导致额外显存占用。另外检查下是否开了activation_checkpointing,这个对7B挺关键的,能省不少中间显存。如果还不行,建议先用ZeRO-2跑一下看看具体显存占用,排除硬件或驱动问题。
说实话你这情况我太熟了,之前我用ZeRO-3跑13B也是第一步就炸,后来发现很多时候是模型加载和显存碎片的问题。单卡A100 80G跑7B按理说ZeRO-3不该OOM这么夸张,我怀疑你offload虽然开了但可能没开对——比如offload_param和offload_optimizer都得显式指定device为cpu,有些版本默认还是gpu。另外你试过把model的dtype转成bf16吗?ZeRO-3配合bf16能省不少显存,而且7B精度损失基本可忽略。还有个小坑,如果你是用huggingface的from_pretrained直接加载,记得设low_cpu_mem_usage=True,不然加载过程本身就会多占显存。实在不行的话,我建议先切到ZeRO-2试试,7B模型在单卡A100上ZeRO-2开offload optimizer到cpu,batch size=1应该能跑,显存占用大概在40-50G左右,比ZeRO-3稳定很多。你贴一下你的deepspeed config文件呗,我帮你看看是不是参数漏了,比如zero_optimization.stage3_gather_16bit_weights_on_model_save这个开关有时候也会影响运行时内存。
ZeRO-2其实够用了,7B模型单卡A100不用offload反而更稳,你试试关掉offload。
试试把cpu_offload改成只offload optimizer,param留gpu,或者直接换ZeRO-2,7B模型其实够用。
ZeRO-3对7B模型在单卡A100上确实容易OOM,尤其是你开了offload后第一步崩,很可能是模型加载时没加上auto模式或者stage3_gather_16bit_weights_on_model_save没配好。建议先试试ZeRO-2,7B模型其实80G显存勉强能跑,batch size设1加梯度累积,把pin_memory关掉,说不定能撑住。另外也可以检查下huggingface的加载代码,用device_map="auto"配合load_in_8bit或bitsandbytes量化,能省不少显存。
我也遇到过类似的情况,A100 80G跑7B按理说ZeRO-3不该这么容易崩。你检查过zero_optimization.stage3_gather_16bit_weights_on_model_save这个参数没?有时候默认配置会把参数全卡在显存里,改成false能省不少。另外建议试试先把模型用meta device加载,等到forward才调进显存,这样能绕开第一步就爆显存的问题。
讲真,你这个配置单看挺合理的,但ZeRO-3在7B模型上直接OOM确实有点诡异。我猜问题可能出在模型加载时,DeepSpeed ZeRO-3默认会把所有参数都按分片来初始化,如果加载前模型就已经完整加载到显存里,那第一步就会炸。试试用huggingface的from_pretrained时加上device_map="auto"或者直接用deepspeed.initialize来接管模型初始化,别让模型在加载阶段就占满显存。另外offload到CPU虽然能省显存,但CPU和GPU之间的传输带宽很容易成为瓶颈,尤其是optimizer和param都offload的话,第一步的通信开销就够呛。我自己的经验是,如果单卡A100 80G跑7B,ZeRO-2配合gradient checkpointing其实完全够用,batch size可以开到4-8,而且训练速度比ZeRO-3快很多,没必要硬上ZeRO-3。你试过把offload只开optimizer,param留在GPU吗?或者检查一下你的deepspeed配置文件里是不是漏了“zero_force_ds_cpu_optimizer”: false这个参数,有时候默认行为会强制用CPU优化器导致额外开销。还有个小技巧,用torch.cuda.empty_cache()在加载前清一次缓存,有时候能避开显存碎片问题。