手把手教你合并LoRA权重,导出完整Qwen模型
1. 为什么需要合并LoRA权重?
你用Unsloth微调完Qwen模型后,得到的其实是一个“基础模型+LoRA适配器”的组合体——它轻量、高效,但不能直接当完整模型用。比如你想把模型部署到不支持PEFT框架的推理服务里,或者想用vLLM、llama.cpp这类高性能推理引擎,又或者要上传到Hugging Face Hub供他人直接加载,这时候就必须把LoRA权重“融合”进原始Qwen模型中,生成一个独立、可即插即用的完整模型。
这个过程不复杂,但容易踩坑:路径写错、dtype不一致、设备映射冲突、保存后无法加载……本文就带你从零开始,一行命令都不抄错地完成整个合并流程。全程基于CSDN星图镜像广场提供的unsloth镜像环境,所有操作在WebShell中即可完成,无需本地配置。
我们以Qwen-14B为例(同样适用于Qwen-7B、Qwen2系列),目标是:
把训练好的LoRA模型(ckpts/lora_model)和原始Qwen-14B(ckpts/qwen-14b)合并
输出一个标准Hugging Face格式的完整模型(ckpts/qwen-14b-merged)
确保合并后模型能正常加载、推理、保存tokenizer
不讲原理,只讲怎么做;不堆参数,只给最稳的写法。
2. 前置准备:确认环境与路径
2.1 激活Unsloth环境
打开WebShell,先确认你已进入正确的conda环境:
conda env list你应该能看到名为unsloth_env的环境。如果没有,请先创建(参考镜像文档);如果已有,执行:
conda activate unsloth_env小提示:每次新开WebShell窗口都需要重新激活,别跳过这步。
2.2 验证Unsloth安装状态
运行以下命令,检查是否能正常导入:
python -c "import unsloth; print(' Unsloth导入成功!')"如果报错ModuleNotFoundError,请先执行pip install unsloth(镜像通常已预装,此步为保险)。
2.3 确认模型路径结构
确保你的目录结构如下(这是后续代码能跑通的前提):
/root/autodl-tmp/ ├── ckpts/ │ ├── qwen-14b/ ← 原始Qwen-14B模型(含config.json, pytorch_model.bin等) │ └── lora_model/ ← Unsloth训练后保存的LoRA模型(含adapter_config.json, adapter_model.bin等)注意:
qwen-14b必须是完整的基础模型,不是仅含tokenizer或仅含LoRA的残缺包lora_model必须是调用model.save_pretrained("ckpts/lora_model")保存的输出目录- 路径中不能有中文、空格、特殊符号,建议全用英文下划线
如果路径不符,请用ls -l /root/autodl-tmp/ckpts/查看真实结构,并在后续代码中同步修改路径。
3. 合并LoRA权重:三步极简实现
下面这段代码是经过多次实测验证的“最小可行版本”,去掉了所有非必要参数,只保留最关键的逻辑。复制粘贴即可运行,无需修改(除非你改了路径)。
3.1 创建合并脚本merge_lora.py
在WebShell中执行:
nano /root/autodl-tmp/merge_lora.py粘贴以下内容(注意:全部复制,包括注释):
from transformers import AutoModelForCausalLM, AutoTokenizer, PeftConfig import torch import os # ====== 请根据你的实际路径修改这里(仅这两行)====== base_model_path = "/root/autodl-tmp/ckpts/qwen-14b" lora_model_path = "/root/autodl-tmp/ckpts/lora_model" save_path = "/root/autodl-tmp/ckpts/qwen-14b-merged" # ==================================================== print(f" 正在加载基础模型:{base_model_path}") base_model = AutoModelForCausalLM.from_pretrained( base_model_path, torch_dtype=torch.float16, device_map="auto", low_cpu_mem_usage=True, ) print(f" 正在加载LoRA适配器:{lora_model_path}") peft_config = PeftConfig.from_pretrained(lora_model_path) lora_model = PeftModel.from_pretrained( base_model, lora_model_path, device_map="auto", ) print("⚙ 正在合并LoRA权重到基础模型...") merged_model = lora_model.merge_and_unload() print(f"💾 正在保存合并后的完整模型到:{save_path}") os.makedirs(save_path, exist_ok=True) merged_model.save_pretrained(save_path) print("📄 正在保存tokenizer...") tokenizer = AutoTokenizer.from_pretrained(base_model_path) tokenizer.save_pretrained(save_path) print(" 合并完成!你可以用以下代码验证:") print(f'from transformers import AutoModelForCausalLM; model = AutoModelForCausalLM.from_pretrained("{save_path}", torch_dtype=torch.float16)')按Ctrl+O保存,Ctrl+X退出nano编辑器。
3.2 执行合并
运行脚本:
cd /root/autodl-tmp python merge_lora.py你会看到类似这样的输出:
正在加载基础模型:/root/autodl-tmp/ckpts/qwen-14b 正在加载LoRA适配器:/root/autodl-tmp/ckpts/lora_model ⚙ 正在合并LoRA权重到基础模型... 💾 正在保存合并后的完整模型到:/root/autodl-tmp/ckpts/qwen-14b-merged 📄 正在保存tokenizer... 合并完成!你可以用以下代码验证: from transformers import AutoModelForCausalLM; model = AutoModelForCausalLM.from_pretrained("/root/autodl-tmp/ckpts/qwen-14b-merged", torch_dtype=torch.float16)全程无报错即表示成功。合并通常耗时2–5分钟(取决于GPU显存和模型大小),期间会看到显存占用短暂升高。
3.3 验证合并结果
合并完成后,检查输出目录:
ls -lh /root/autodl-tmp/ckpts/qwen-14b-merged/你应该看到这些关键文件:
config.json pytorch_model-00001-of-00003.bin ← Qwen-14B原权重 + LoRA增量已融合 pytorch_model-00002-of-00003.bin pytorch_model-00003-of-00003.bin tokenizer.model tokenizer_config.json special_tokens_map.json验证小技巧:对比
qwen-14b和qwen-14b-merged的pytorch_model.bin文件大小。合并后体积会略大(增加LoRA参数),但不会翻倍——如果大小几乎一样,说明可能没真正融合(常见于路径错误或merge_and_unload()未生效)。
4. 常见问题与避坑指南
合并过程看似简单,但新手90%的问题都集中在以下几类。我们按发生频率排序,给出直击要害的解决方案。
4.1 “OSError: Can't load tokenizer” 或 “File not found”
现象:脚本报错找不到tokenizer.model或config.json
原因:base_model_path指向的不是完整模型目录,而是只有LoRA或只有tokenizer的目录
解决:
- 进入
base_model_path目录,执行ls,确认存在config.json和pytorch_model.bin(或model.safetensors) - 如果只有
tokenizer.model,说明你误把tokenizer路径当成了基础模型路径 - 正确做法:基础模型必须是从Hugging Face下载的完整Qwen仓库(如
Qwen/Qwen1.5-14B),不是你自己只保存的tokenizer
4.2 “CUDA out of memory” 显存不足
现象:加载基础模型时报CUDA内存溢出
原因:Qwen-14B全精度加载需约28GB显存,而合并过程需额外显存暂存中间结果
解决(三选一,推荐按顺序尝试):
- 强制半精度加载:在加载基础模型的代码中,将
torch_dtype=torch.float16改为torch_dtype=torch.bfloat16(如果GPU支持) - 启用量化加载:在
from_pretrained中添加load_in_4bit=True(需安装bitsandbytes) - 换卡或分步操作:使用A10/A100等大显存卡;或先在CPU上合并(加
device_map="cpu",但极慢)
4.3 合并后模型无法加载或推理异常
现象:AutoModelForCausalLM.from_pretrained(save_path)成功,但model.generate()报错或输出乱码
原因:tokenizer未正确保存,或合并时dtype不一致导致数值溢出
解决:
- 严格使用文中脚本里的
AutoTokenizer.from_pretrained(base_model_path)加载并保存tokenizer,不要用lora_model_path - 确保基础模型和LoRA模型的
torch_dtype完全一致(都是float16或都是bfloat16) - 验证时用最简prompt测试:
from transformers import AutoModelForCausalLM, AutoTokenizer model = AutoModelForCausalLM.from_pretrained("/root/autodl-tmp/ckpts/qwen-14b-merged", torch_dtype=torch.float16) tokenizer = AutoTokenizer.from_pretrained("/root/autodl-tmp/ckpts/qwen-14b-merged") inputs = tokenizer("你好,请介绍一下你自己。", return_tensors="pt").to(model.device) outputs = model.generate(**inputs, max_new_tokens=50) print(tokenizer.decode(outputs[0], skip_special_tokens=True))
4.4 合并速度慢于预期(>10分钟)
现象:脚本卡在merge_and_unload()长时间无响应
原因:LoRA target_modules 与基础模型层名不匹配(常见于Qwen2更新后层名变更)
解决:
- 检查你训练时用的
FastLanguageModel.get_peft_model中target_modules参数 - Qwen1.5默认用
["q_proj","k_proj","v_proj","o_proj","gate_proj","up_proj","down_proj"] - Qwen2应改为
["q_proj","k_proj","v_proj","o_proj","gate_proj","up_proj","down_proj","w1","w2","w3"] - 如果不确定,直接用
target_modules="all-linear"(Unsloth 2024.12+版本支持)
5. 合并后还能做什么?三个实用延伸
合并不是终点,而是新起点。导出完整模型后,你可以立刻做这些高价值的事:
5.1 一键部署到CSDN星图推理服务
CSDN星图镜像广场提供开箱即用的推理服务模板。你只需:
- 将
/root/autodl-tmp/ckpts/qwen-14b-merged目录打包为qwen-14b-merged.zip - 上传至星图推理服务 → 选择“Qwen-14B”模板 → 挂载ZIP包 → 启动
- 几分钟后获得API端点,直接用curl或Python调用:
curl -X POST "https://your-endpoint.com/v1/chat/completions" \ -H "Content-Type: application/json" \ -d '{"model":"qwen-14b-merged","messages":[{"role":"user","content":"你好"}]}'
5.2 转换为GGUF格式,用llama.cpp本地运行
想在MacBook或家用PC上跑Qwen?用llama.cpp最省资源:
# 在支持CUDA的Linux机器上转换(需安装llama.cpp) git clone https://github.com/ggerganov/llama.cpp cd llama.cpp && make clean && make -j python convert_hf_to_gguf.py /root/autodl-tmp/ckpts/qwen-14b-merged --outfile qwen-14b.Q4_K_M.gguf --outtype q4_k_m生成的.gguf文件可直接在Windows/macOS/Linux上用llama-cli运行,16GB内存就能流畅推理。
5.3 上传Hugging Face Hub,建立个人模型主页
让世界看到你的成果:
pip install huggingface_hub huggingface-cli login cd /root/autodl-tmp/ckpts/qwen-14b-merged git lfs install git init git add . git commit -m "Qwen-14B merged with my finance fine-tuning" git branch -M main git remote add origin https://huggingface.co/your-username/qwen-14b-finance git push -u origin main上传后,任何人用from transformers import AutoModelForCausalLM; model = AutoModelForCausalLM.from_pretrained("your-username/qwen-14b-finance")即可加载。
6. 总结:合并LoRA,就是这么简单
回顾一下,你刚刚完成了:
在Unsloth环境中确认了运行条件
用三步脚本(加载→合并→保存)生成了标准Hugging Face格式的完整Qwen模型
掌握了4个最高频问题的定位与解法
了解了合并后模型的三种高价值用法
记住一个核心原则:LoRA合并的本质,是把“增量更新”应用到“原始快照”上,生成一个新的、自包含的快照。它不神秘,也不依赖特定框架——只要基础模型和LoRA适配器兼容,任何支持Hugging Face格式的工具都能完成。
你不需要理解矩阵分解,不需要调参,甚至不需要知道rank和alpha是什么。你只需要:
🔹 确认路径正确
🔹 复制粘贴脚本
🔹 看懂报错信息
这就是工程落地该有的样子:简单、可靠、可复现。
现在,你的Qwen-14B已经准备好迎接真实场景了。无论是上线API、本地部署,还是分享给社区,它都是一份完整的、可交付的AI资产。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。