最近在试着用LoRA微调一个7B的对话模型(基座是Qwen2.5),训练阶段用的batch size=1,显存勉强够用(24G)。但微调完合并权重后,用vLLM部署推理,居然比原始模型多吃了快6G显存,而且并发一高就OOM。我确认过合并后的权重没问题,推理时也没加载训练时的优化器状态。查了一圈,有人说可能是KV cache的预分配问题,也有人说是LoRA的adapter没卸载干净。但我用lora_merge_and_unload()合并后明明只保存了合并权重。有没有大佬遇到过类似情况?是LoRA本身就会增加推理时的显存开销,还是我哪里配置错了?求指点,卡在部署这步好几天了。
LoRA微调7B模型后推理显存爆炸,是我的方法不对吗?
全部回复
共 31 条说实话我觉得这事儿大概率不是LoRA本身的锅,7B模型合并后参数量几乎不变,推理显存差异不该这么大。你提到vLLM,我怀疑是它的KV cache预分配策略在作怪,vLLM会根据max_num_seqs和gpu_memory_utilization去预留显存,如果你没显式设置这两个参数,它可能默认把剩余显存全吃进去做cache,导致看起来比原始模型多占很多。我之前用SFT后的模型也遇到过类似情况,后来把gpu_memory_utilization调到0.7,max_num_seqs调小,OOM就缓解了。另外你确认下是不是用了PagedAttention的旧版本,有些版本对合并后模型的KV cache计算有bug,升级到最新版试试。还有个小细节,你保存合并权重时有没有把tokenizer和config也一起覆盖?如果基座配置里残留了LoRA相关的pad_token_id或者attention头数不一致,vLLM可能按错误形状预分配。建议你直接用transformers的from_pretrained加载合并模型,然后打印model.config对比一下原始基座,看有没有多出来的参数或者奇怪的attention实现。如果配置没问题,那就单纯是vLLM的显存规划和你之前用transformers推理的基线不一样,别拿transformers的峰值显存去对比vLLM的预分配值,这俩机制完全不同。
我遇到过类似的情况,但最后发现不是LoRA本身的问题,而是vLLM的KV cache preallocate在作怪。你训练时batch size=1,但推理时vLLM默认会按最大并发数预分配KV cache,Qwen2.5的GQA结构加上7B的hidden size,每多一个并发请求,KV cache的显存占用会非线性增长,6G的增量很可能就是预分配策略导致的。你可以试一下把--max-num-seqs调小,或者用--gpu-memory-utilization限制显存使用比例,看OOM还会不会出现。另外,你合并权重后有没有重新跑过tokenizer的配置检查?有时候adapter的base_model属性会残留,导致vLLM误认为还在用PEFT模式,额外加载了adapter的metadata。我上次就是手动删掉模型目录下的adapter_config.json和adapter_model.bin,只留合并后的权重文件,显存占用立刻降下来了。还有一个坑,如果你用的是transformers的AutoModelForCausalLM加载合并模型,记得传trust_remote_code=True,不然某些版本的Qwen2.5会默认走动态图模式,额外缓存激活值。我自己后来是直接改用llama.cpp的GGUF量化部署,显存直接砍半,虽然速度慢点但稳定多了。你先试试调vLLM参数,如果不行再检查一下权重目录里有没有残留的adapter文件,大概率就是这两件事之一。
看到这个显存对比我第一反应是怀疑你对比的基准不太对。原始模型用vLLM部署时如果开了--max-model-len默认值,KV cache预分配是按最大序列长度来的,而LoRA合并后模型参数量虽然没变,但vLLM对每个请求的显存预留策略可能受模型配置影响,比如你微调时改过rope_scaling或者注意力实现方式,那推理时的KV cache大小就会变。我之前微调CodeLlama也遇到过类似情况,后来发现是vLLM版本和PEFT的兼容性问题,新版本vLLM对LoRA权重合并后的模型会额外分配一些临时buffer,你可以试试用--enforce-eager模式跑一下,如果显存掉下来那就是CUDA graph缓存的问题。另外你确认下合并时用的lora_merge_and_unload()有没有把adapter的缩放系数(alpha)残留到模型配置里,有些情况下保存的config.json里还带着lora_alpha字段,vLLM读到这个会误判成多任务推理模式。最直接的排查办法是先用原生Transformers加载合并权重跑一次推理看峰值显存,如果正常那问题就出在vLLM服务端配置上,可以试试调小--gpu-memory-utilization或者限制--max-num-seqs。我上次遇到OOM是vLLM默认preemption策略在长上下文下会保留更多历史KV,你如果微调时把max_position_embeddings调大了,那部署时这个参数也得同步改回去。总之这问题大概率不是LoRA本身开销,而是工具链对合并模型的兼容性处理。
vLLM的KV cache预分配确实是个坑,尤其你batch size=1训练但推理并发高,显存会按最大并发预留。建议先查下vLLM的--max-num-seqs和--gpu-memory-utilization参数,把利用率调低点试试。另外LoRA合并后理论上不该增加额外显存,除非你量化精度变了或者推理时还挂了adapter配置,检查下模型加载路径是不是干净。我之前碰到过类似情况,最后发现是vLLM版本和transformers不兼容导致重复加载权重,换个版本就好了。
大概率是vLLM的KV cache自动预留策略在作怪,试试设下--max-num-seqs或者--gpu-memory-utilization,别全让它自己分配。
这问题我踩过一模一样的坑,最后查出来是vLLM的KV cache预分配在作怪。你合并权重后虽然只有一套模型,但vLLM默认会按最大序列长度预留缓存,LoRA微调过的模型输出分布变了,实际生成长度可能比基座更激进,导致缓存估算偏保守。可以试试在启动参数里显式指定--max-num-seqs和--max-model-len,把并发数调低点看显存曲线。
另外你说合并后只保存了权重,但有没有检查过tokenizer的special tokens?Qwen2.5用LoRA微调时如果加了新对话模板,合并后tokenizer的pad_token_id可能没同步,vLLM会为每个请求额外分配padding缓存,这也能解释多出来的6G。建议对比下合并前后tokenizer的added_tokens和chat_template。
最后建议用torch.cuda.memory_summary()在推理时打一下内存分配,看是不是真的被KV cache吃掉。我之前遇到类似情况是vLLM的--swap-space默认值太高,直接设成1能省一半预分配。实在不行就用--enforce-eager关掉CUDA graph,虽然慢点但显存能稳下来。
这问题我踩过一模一样的坑,先说结论:LoRA本身在推理时几乎不占额外显存,你多半是vLLM的KV cache预分配策略在捣鬼。合并权重后,模型参数量没变,但vLLM会根据你的max_num_seqs和gpu_memory_utilization参数,把剩余显存全部预留给KV cache,你看着像“多吃6G”,其实是它把动态显存挪去缓存了,并发一高自然OOM。你可以试试把gpu_memory_utilization调低到0.7左右,或者显式设置max_num_seqs=1,看看是不是立刻缓解。另外,确认下你用的是vLLM哪个版本,旧版本对QWen2.5的KV cache对齐有bug,升级到最新版大概率能解决。还有个冷门可能:你合并权重时是不是用了fp16,但原始模型是bf16?精度不匹配会让vLLM重新计算scale,导致临时张量暴涨。我上次就是这原因,重新用bf16合并后直接降了4G。先排查这两个点,别急着怀疑LoRA本身。
并发高才OOM的话,八成是vLLM的KV cache预分配没调好,试试把gpu_memory_utilization调低点。
这问题我也踩过坑,大概率不是LoRA本身的锅。你合并后如果只跑原始权重,vLLM不会额外加载adapter,显存暴涨多半是KV cache的预分配策略变了,尤其是你Qwen2.5用了GQA的话,vLLM默认会按最大并发预留显存,试试启动时调低gpu_memory_utilization或者限制max_num_seqs。另外确认下是不是新版vLLM对7B模型默认开启的paged attention页表大小变了,换个老版本或手动调一下说不定就好了。
说实话,你这情况我怀疑不是LoRA本身的锅,而是vLLM的KV cache预分配策略在作祟。7B模型本来KV cache就吃紧,你合并权重后参数量没变,但vLLM可能按更高精度或更大batch预算来预留显存,尤其并发一高,预分配额度直接翻倍。我自己试过用--kv-cache-dtype fp8或者手动调--max-num-seqs能压下来不少,你可以先降到1看还爆不爆。另外确认下vLLM版本,之前有过旧版对合并后模型显存估算偏高的bug,升级到最新版再跑一次看看。如果还不行,试试直接加载原始模型再单独挂LoRA权重做推理,别合并,有时候反而省显存。
这问题大概率是vLLM的KV cache预分配和并发参数没调,跟LoRA本身关系不大,试试把gpu_memory_utilization调低点。