模型导出:merge、量化、推理服务
12.1 训完之后的三条路
(LoRA adapter / 全参 checkpoint)"] Out --> A["路径 A: 不 merge
带 adapter 直接推理"] Out --> B["路径 B: merge 成单一权重
给生态工具使用"] Out --> C["路径 C: merge + 量化
压缩到 GGUF / GPTQ / AWQ"] A --> A1["llamafactory-cli chat
+ adapter_name_or_path"] B --> B1["llamafactory-cli export
→ saves/.../merged"] C --> C1["llamafactory-cli export
+ export_quantization_bit=4"] B1 --> B2["transformers / vLLM / SGLang
直接加载"] C1 --> C2["llama.cpp / Ollama / TensorRT-LLM"]
三条路各自的场景:
| 路径 | 适合场景 | 缺点 |
|---|---|---|
| A. 不 merge | 线上挂多个 adapter,按 user 切换 | 有些推理引擎不支持 adapter |
| B. merge | 给一份"普通 HF 模型"出去 | 占盘 / 显存大 |
| C. merge + 量化 | 边缘设备 / CPU 推理 | 有微小精度损失 |
12.2 路径 A:带 adapter 推理
本身不"导出",但作为对比放在前面。第 3 章演示过:
llamafactory-cli chat \
model_name_or_path=Qwen/Qwen3-4B-Instruct-2507 \
adapter_name_or_path=saves/qwen3-4b/lora/sft \
template=qwen3_nothink \
finetuning_type=lora
或 OpenAI 兼容 API:
llamafactory-cli api \
model_name_or_path=Qwen/Qwen3-4B-Instruct-2507 \
adapter_name_or_path=saves/qwen3-4b/lora/sft \
template=qwen3_nothink \
finetuning_type=lora \
infer_backend=vllm # huggingface / vllm / sglang / ktransformers
多 adapter 切换(多个用逗号):
adapter_name_or_path: saves/qwen3-4b/lora/sft,saves/qwen3-4b/lora/legal
12.3 路径 B:merge LoRA
仓库自带 examples/merge_lora/qwen3_lora_sft.yaml:
### Note: DO NOT use quantized model or quantization_bit when merging lora adapters
### model
model_name_or_path: Qwen/Qwen3-4B-Instruct-2507
adapter_name_or_path: saves/qwen3-4b/lora/sft
template: qwen3_nothink
trust_remote_code: true
### export
export_dir: saves/qwen3_sft_merged
export_size: 5 # 每个 safetensors 分片 5 GB
export_device: cpu # cpu / auto;显存不够选 cpu
export_legacy_format: false # false → .safetensors;true → .bin
启动:
llamafactory-cli export examples/merge_lora/qwen3_lora_sft.yaml
得到的 saves/qwen3_sft_merged/ 是一个标准 HF 模型目录:
config.json
generation_config.json
model.safetensors.index.json
model-00001-of-00002.safetensors
model-00002-of-00002.safetensors
tokenizer.json
tokenizer_config.json
special_tokens_map.json
它可以被 transformers.AutoModelForCausalLM.from_pretrained() / vLLM / SGLang / Ollama 等所有支持 HF 格式的工具直接加载。
- QLoRA:基座是 4-bit 量化的,merge 回去会有精度损失,框架会拒绝;
- 已加载的是 GPTQ / AWQ 量化模型:同上;
- 正确做法:训练用 QLoRA 节省显存,但推理时 base 模型用 fp16,adapter 仍可挂载(路径 A)。
12.4 路径 C:merge + 量化导出
导出阶段量化的官方例子 examples/merge_lora/qwen3_gptq.yaml:
### model
model_name_or_path: Qwen/Qwen3-4B-Instruct-2507
template: qwen3_nothink
trust_remote_code: true
### export
export_dir: saves/qwen3_gptq
export_quantization_bit: 4 # 4 / 8
export_quantization_dataset: data/c4_demo.jsonl # ★ 量化校准数据
export_size: 5
export_device: cpu
export_legacy_format: false
校准数据怎么选
| 目标 | 推荐校准数据 |
|---|---|
| 通用对话 | c4_demo.jsonl 或自家通用 100-500 条 |
| 行业模型 | 用你训练数据的 100-500 条样本(必须代表实际分布) |
| 多语言 | 混合中英文校准 |
导出选项对照
export_quantization_bit | 方法 | 显存(7B) | 典型工具 |
|---|---|---|---|
null | 不量化(fp16/bf16) | 14 GB | HF / vLLM |
| 8 | GPTQ 8-bit | 8 GB | HF / vLLM / AutoGPTQ |
| 4 | GPTQ 4-bit | 4 GB | HF / vLLM / Ollama |
12.5 把模型转 GGUF(llama.cpp / Ollama)
LLaMA-Factory 自身不直接导 GGUF,但 merge 后的 HF 目录可以被 llama.cpp 转:
# 装好 llama.cpp 源码后
cd llama.cpp
python convert_hf_to_gguf.py \
/Users/zjw/Documents/LLM/train/repos/llama-factory/saves/qwen3_sft_merged \
--outfile saves/qwen3_sft.gguf \
--outtype f16
# 进一步量化
./quantize saves/qwen3_sft.gguf saves/qwen3_sft.Q4_K_M.gguf Q4_K_M
# Ollama 直接装
ollama create my-qwen3 -f Modelfile
Modelfile 示例:
FROM saves/qwen3_sft.Q4_K_M.gguf
TEMPLATE """<|im_start|>system
{{ .System }}<|im_end|>
<|im_start|>user
{{ .Prompt }}<|im_end|>
<|im_start|>assistant
"""
12.6 部署到推理引擎
vLLM
vllm serve saves/qwen3_sft_merged \
--port 8000 \
--max-model-len 8192 \
--gpu-memory-utilization 0.9
SGLang
python -m sglang.launch_server \
--model-path saves/qwen3_sft_merged \
--port 30000
TensorRT-LLM
需要先用 TensorRT-LLM 的 build 脚本把模型转成 .engine。这一步通常工程师在 GPU 服务器上手动做,仓库不直接覆盖。
HuggingFace Inference Server (TGI)
docker run --gpus all -p 8080:80 \
-v $(pwd)/saves/qwen3_sft_merged:/data \
ghcr.io/huggingface/text-generation-inference:latest \
--model-id /data
12.7 SwanLab / W&B / TensorBoard:把训练全流程留痕
不是导出但常被一起问。三个 report_to 选项:
report_to | 本地查看 |
|---|---|
tensorboard | tensorboard --logdir saves/.../runs |
wandb | wandb login 后自动同步到 wandb.ai |
swanlab | 本地 dashboard,国内访问友好 |
mlflow | 对接现有 MLflow Tracking server |
12.8 导出清单
正式发布模型前的最小检查:
- ✅
tokenizer_config.json里的chat_template字段已生成; - ✅ 跑一次
llamafactory-cli chat用 merged 模型,确认不带 adapter 也能正常回答; - ✅ 跑一份评测(
llamafactory-cli eval),打分对比 base model; - ✅ 加
README.md:训练数据来源、训练 YAML、license、注意事项; - ✅ 上传时压缩
.safetensors,不要.bin; - ✅ 多模态模型还要把
preprocessor_config.json/processor_config.json也带上。
12.9 export_model() 真实流程
"export"子命令落到代码:src/llamafactory/train/tuner.py:234 export_model()。整个流程拆 5 步:
| 步 | 动作 | 关键 YAML 字段 |
|---|---|---|
| 1 | 加载 base + tokenizer + 可能的 LoRA / PiSSA adapter(走 model/loader.py:load_model,但 is_trainable=False) | model_name_or_path / adapter_name_or_path / template |
| 2 | merge_and_unload() 把 LoRA 增量融回基座(PEFT 调用) | —(自动) |
| 3 | 若 export_quantization_bit 设了,按 GPTQ / AWQ 校准 | export_quantization_bit / export_quantization_dataset / export_quantization_nsamples / export_quantization_maxlen |
| 4 | 调 save_pretrained() 写 safetensors + HF config | export_dir / export_size / export_legacy_format |
| 5 | 可选:生成 Ollama modelfile | export_hub_model_id 或单独 --export_ollama |
关键约束:
- QLoRA 训出来的 adapter(base 已是 4-bit BNB)不能 merge——merge_and_unload 需要 base 是 fp16/bf16 才能加上 LoRA 增量;要 merge 必须重新加一份 fp16 base;
- 同一次 export 要么 merge 要么 quantize,不能既 merge 又 quantize(中间必须落一次盘)。先 merge 出 fp16 模型,再二次 export 做量化;
export_device: cpu在显存不够的机器上能让 merge 走 CPU(慢但能跑通)。
12.10 一份完整的 "训→merge→量化→部署" 实战序列
# Step 1: LoRA SFT
llamafactory-cli train examples/train_lora/qwen3_lora_sft.yaml
# Step 2: merge LoRA → 完整 fp16 模型
llamafactory-cli export examples/merge_lora/qwen3_lora_sft.yaml
# 出: saves/qwen3-4b/full/merged/
# Step 3: GPTQ 4-bit 量化(用校准集)
llamafactory-cli export \
model_name_or_path=saves/qwen3-4b/full/merged \
template=qwen3_nothink \
export_dir=saves/qwen3-4b/full/merged_gptq \
export_quantization_bit=4 \
export_quantization_dataset=data/c4_demo.jsonl \
export_quantization_nsamples=128
# Step 4: 出 Ollama modelfile(不量化)
llamafactory-cli export \
model_name_or_path=saves/qwen3-4b/full/merged \
template=qwen3_nothink \
export_dir=saves/qwen3-4b/full/merged_ollama \
export_legacy_format=true
# Step 5: 起 vLLM 服务
python -m vllm.entrypoints.openai.api_server \
--model saves/qwen3-4b/full/merged_gptq \
--quantization gptq \
--max-model-len 8192
这 5 步串完,从训练到 OpenAI 兼容 API 一份完整 pipeline。
12.11 Ollama 导出格式细节
Ollama modelfile 不是单一文件,而是 .modelfile + 一份完整的 GGUF。LLaMA-Factory 的 export 子命令只能出 .modelfile + safetensors 模型目录——真正的 GGUF 转换要另外调 llama.cpp 的 convert_hf_to_gguf.py。完整 Ollama 流程:
- LLaMA-Factory export → 出 HF safetensors 模型目录 +
Modelfile; - 用 llama.cpp 的
convert_hf_to_gguf.py把模型转 GGUF(fp16); - 用 llama.cpp 的
quantize工具量化到 Q4_K_M / Q5_K_M 等; - 修改 Modelfile 里
FROM指向 GGUF; ollama create my-model -f Modelfile。
Modelfile 里 LLaMA-Factory 自动写好的 TEMPLATE / PARAMETER stop / SYSTEM 几行来自 YAML 的 template 字段——template 写错的话 Modelfile 跟着错,Ollama 推理出来全乱码。
12.12 SGLang vs vLLM 部署选型(2025-03 之后)
vLLM 不再是唯一选择。同样 llamafactory-cli api,通过 infer_backend 切换:
infer_backend | 实际后端 | 典型场景 |
|---|---|---|
huggingface | 原生 transformers(默认) | 调试 / 小流量 |
vllm | vLLM AsyncEngine | 常规生产 |
sglang | SGLang Engine | 多轮工具调用、结构化生成、长 KV 场景;部分模型比 vLLM 快 25-270% |
SGLang 在结构化输出(强制 JSON / 正则)和 prefix cache 复用上明显比 vLLM 优秀,适合 agent / 长对话场景。如果是单轮 chat 流量,vLLM 仍是首选(社区更成熟)。