LLaMA-Factory 合并 LoRA 适配器到基础模型
在大模型微调的实践中,LoRA(Low-Rank Adaptation)因其高效、低资源消耗的特性,已成为主流的参数高效微调方法。然而,一个常被忽视但至关重要的环节是:如何将训练好的 LoRA 适配器“落地”为可独立部署的完整模型?
毕竟,在生产环境中,没人希望每次推理都要同时加载基础模型和多个适配器文件——配置复杂、启动慢、兼容性差。这时候,就需要把 LoRA 的增量权重“融合”进原始模型中,生成一个全新的、自包含的模型包。
LLaMA-Factory 提供了极为简洁的方式完成这一操作:通过一条命令llamafactory-cli export和一个 YAML 配置文件,即可实现从 LoRA 到完整模型的无缝转换。
llamafactory-cli export examples/merge_lora/qwen2_5vl_lora_sft.yaml这条命令背后,是一套高度自动化的流程:它会读取配置中的模型路径、适配器位置、输出设置等信息,加载原始模型结构,注入 LoRA 权重,执行融合计算,并最终导出为标准 Hugging Face 格式的模型目录。整个过程无需编写任何代码,适合各类用户快速上手。
我们来看一个典型的配置示例:
### 注意:合并 LoRA 时请勿使用量化模型或设置 quantization_bit 参数 ### model model_name_or_path: Qwen/Qwen2.5-VL-7B-Instruct adapter_name_or_path: saves/qwen2_5vl-7b/lora/sft template: qwen2_vl trust_remote_code: true ### export export_dir: output/qwen2_5vl_lora_sft export_size: 5 export_device: cpu export_legacy_format: false这个配置虽然简短,但每一项都至关重要。
首先是model_name_or_path。这里必须填入的是未经过量化的原始模型路径,比如 Hugging Face Hub 上的Qwen/Qwen2.5-VL-7B-Instruct,而不是你在训练时可能使用的 int4 或 int8 量化版本。原因很简单:LoRA 是对浮点权重施加的增量更新(ΔW),而量化后的权重已经被压缩成整数格式,无法再与 FP16/BF16 的 LoRA 矩阵相加。强行合并会导致数值错乱甚至报错。
如果你之前用的是 QLoRA 训练方案(即 4-bit 基础模型 + LoRA),那么在合并阶段就必须回退到全精度模型。也就是说,你需要确保本地有或能下载到该模型的 FP16 版本。
接着是adapter_name_or_path,指向你训练完成后保存的 LoRA 输出目录。这个路径下应该包含adapter_model.bin、adapter_config.json等 PEFT 标准文件。如果路径错误,或者训练中断导致文件不全,合并就会失败。
template设置决定了输入文本如何被分词和组织成对话格式。对于 Qwen-VL 这类多模态模型,必须设为qwen2_vl,否则图文混合输入可能无法正确解析。这一点很容易被忽略,尤其是当你迁移其他项目的配置时,稍有不慎就会导致输出乱码或指令理解偏差。
trust_remote_code: true几乎是国产大模型的标配选项。像 Qwen、ChatGLM、Baichuan 等模型都有自定义的 tokenizer 或模型类,如果不开启此选项,系统将无法识别这些非标准组件,直接抛出导入异常。
下面进入导出配置部分。
export_dir指定融合后模型的输出路径。LLaMA-Factory 会在该目录下生成完整的模型资产,包括权重文件、tokenizer 配置、生成参数以及专用于 Ollama 的 Modelfile。你可以把这个目录当作一个“即插即用”的模型包,直接上传至 Hugging Face Hub 或部署到服务端。
export_size控制权重文件的分片数量。7B 规模的模型总大小通常超过 10GB,若不分片,单个.safetensors文件可能达到 15GB 以上,不仅难以传输,还可能超出某些文件系统的限制(如 FAT32 最大仅支持 4GB 单文件)。设为5表示最多拆分为 5 个分片,例如:
pytorch_model-00001-of-00005.safetensors pytorch_model-00002-of-00005.safetensors ...同时生成一个model.safetensors.index.json,记录每个参数所属的分片位置。Hugging Face Transformers 库可以智能地按需加载,完全透明化处理。
export_device决定了合并过程运行在哪种设备上。默认推荐auto,会优先使用 GPU 加速以提升速度;但如果显存不足(常见于消费级显卡处理 7B+ 模型),建议改为cpu。虽然 CPU 合并更慢,但内存容量远大于 VRAM,稳定性更高。尤其当你的机器拥有 32GB 以上 RAM 时,CPU 模式反而是更稳妥的选择。
最后,export_legacy_format是否启用旧式.bin格式。现代生态普遍采用.safetensors,因为它具备更快的加载速度和更强的安全性(防止恶意代码注入)。除非你对接的是老旧系统,否则应保持false。
整个合并流程的技术本质其实很清晰:
- 使用
AutoModelForCausalLM.from_pretrained()加载原始模型; - 调用
PeftModel.from_pretrained()将 LoRA 权重绑定到对应层; - 执行
model.merge_and_unload(),将所有 LoRA 的低秩矩阵 A×B 加回到主权重 W 上,得到新权重 W’ = W + ΔW; - 调用
model.save_pretrained()导出完整模型。
这一步是不可逆的。一旦融合完成,原始模型就被覆盖(除非你提前备份)。因此,建议在正式合并前保留一份干净的基础模型副本。
也正因如此,很多团队会选择“先测试后合并”的策略:在开发阶段直接用 LoRA + 基础模型进行推理验证,确认效果达标后再执行最终融合。
为什么非要走这一步?
我们可以列个对比表来看:
| 场景 | LoRA 推理 | 合并后模型 |
|---|---|---|
| 加载复杂度 | 需同时指定 base model + adapter | 单一路径即可 |
| 推理延迟 | 多一次矩阵计算(W + A×B) | 直接使用固化权重,零开销 |
| 兼容性 | 不支持 TensorRT-LLM、ONNX Runtime 等引擎 | 可自由转换为各种加速格式 |
| 分发便利性 | 至少两个组件 | 一个目录打个包就能共享 |
换句话说,LoRA 是训练阶段的“轻骑兵”,而合并后的模型才是生产环境的“正规军”。
实际日志输出也能反映出整个过程的状态。运行命令后,你会看到类似这样的信息:
[INFO|2025-06-14 10:40:12] llamafactory.model.adapter:143 >> Merged 1 adapter(s). [INFO|2025-06-14 10:40:12] llamafactory.model.loader:143 >> all params: 8,292,166,656 [INFO|modeling_utils.py:3580] 2025-06-14 10:40:33,311 >> The model is bigger than the maximum size per checkpoint (5GB) and is going to be split in 4 checkpoint shards. [INFO|2025-06-14 10:40:34] llamafactory.train.tuner:143 >> Ollama modelfile saved in output/qwen2_5vl_lora_sft/Modelfile其中关键点包括:
- 成功融合了一个适配器;
- 总参数量约为 83 亿,符合 Qwen-7B 的规模预期;
- 因超过单文件上限,自动分成了 4 个分片;
- 自动生成了可用于 Ollama 的 Modelfile,省去手动编写模板的麻烦。
这意味着你几乎不需要额外操作,就可以立即进入部署阶段。
比如,使用 Hugging Face Transformers 直接加载:
from transformers import AutoModelForCausalLM, AutoTokenizer model = AutoModelForCausalLM.from_pretrained("output/qwen2_5vl_lora_sft") tokenizer = AutoTokenizer.from_pretrained("output/qwen2_5vl_lora_sft") inputs = tokenizer("你好,请介绍一下你自己", return_tensors="pt").to("cuda") outputs = model.generate(**inputs, max_new_tokens=128) print(tokenizer.decode(outputs[0], skip_special_tokens=True))或者更进一步,利用 LLaMA-Factory 自动生成的Modelfile在 Ollama 中运行:
FROM ./config.json PARAMETER temperature 0.7 PARAMETER top_p 0.9 TEMPLATE """{{ if .System }}<|im_start|>system {{ .System }}<|im_end|> {{ end }}<|im_start|>user {{ .Prompt }}<|im_end|> <|im_start|>assistant """只需两步即可启动服务:
cd output/qwen2_5vl_lora_sft ollama create qwen2_5vl_lora_sft -f Modelfile ollama run qwen2_5vl_lora_sft之后便可像调用官方模型一样,通过 CLI 或 REST API 使用你的定制化模型。
当然,过程中也可能遇到问题。
最常见的报错是:
ValueError: cannot merge int4 weight with fp16 lora这说明你用了量化过的模型作为基础模型。解决办法只有一个:换回原始 FP16 模型路径。
另一个问题是输出乱码或响应异常。这往往不是权重的问题,而是template设置错误。特别是多模态模型,其特殊 token(如<|vision_start|>)必须由正确的 tokenizer 模板支持,否则图像特征会被误解析。
还有可能是显存溢出。如果你在 GPU 上合并大型模型(如 14B 或更高),很容易触发 CUDA out of memory。此时最简单的解决方案就是切换到export_device: cpu,牺牲一点时间换取稳定性。
路径错误也是一个高频陷阱。务必确认adapter_name_or_path指向的是包含adapter_model.bin的确切目录,而不是上级文件夹或训练日志目录。
LLaMA-Factory 的这一设计,真正打通了“训练 → 部署”的最后一公里。它不仅仅是一个微调框架,更是一个面向生产的模型工程平台。
无论是个人开发者想快速验证一个想法,还是企业团队需要构建可复用的模型流水线,这种“一键导出”的能力都能极大降低门槛。更重要的是,它支持几乎所有主流架构——LLaMA、Qwen、Baichuan、ChatGLM、Phi 等,通用性强,扩展性好。
当你完成一次 SFT 或 RLHF 实验后,不再需要手动写脚本去合并权重,也不必担心格式兼容问题。只需要修改几行 YAML,运行一条命令,就能得到一个可以直接上线的服务化模型。
这种“自动化闭环”的理念,正是当前大模型工业化落地的核心方向之一。
🔗 官方项目地址:https://github.com/hiyouga/LLaMA-Factory
📘 文档指南:https://llamafactory.readthedocs.io
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考