最近在做一个智能采购的项目,需要微调大语言模型,让它能更好地理解业务场景。试了几套方案,最后选了 LLaMA-Factory 加 LoRA 的组合,在 AutoDL 的 H20 上跑。LLaMA-Factory 的开箱即用体验确实不错,LoRA 对显存的要求也低得多,而采购对话场景下效果完全够用。
下面把从环境配置到模型部署的整个过程整理一下,供后续参考。
整体方案
| 环节 | 选型 | 备注 |
|---|---|---|
| 微调框架 | LLaMA-Factory 0.9.4 | 支持多种微调方式,文档齐全 |
| 基础模型 | GPT-OSS-20B | 200B 参数的 MoE 模型 |
| 微调方式 | LoRA | 低秩适配,显存友好 |
| 推理引擎 | vLLM | 高性能推理,支持 LoRA 动态加载 |
| 实验监控 | SwanLab | 可视化训练曲线,使用简单 |
| GPU 资源 | AutoDL H20 | 按量租用,性价比高 |
| 远程传文件 | WinSCP | Windows 下免费的图形化 SFTP 工具 |
环境配置
在 AutoDL 上租用实例时,镜像的选择比较关键。我的配置是这样:
- 基础镜像:PyTorch
- Ubuntu 22.04
- Python 3.12(LLaMA-Factory 要求 3.11 以上)
- CUDA 12.8
- PyTorch 2.8.0
注意,GPT-OSS 模型默认会尝试启用 Flash Attention 3,但该特性目前仅支持 Hopper 架构的 GPU(H100/H800 等)。H20 是 Ada 架构,需要强制降级到 Flash Attention 2,后面会提到怎么处理。
项目初始化
1. 克隆 LLaMA-Factory
cd /root/autodl-tmp
# 如果目录不存在,先创建
mkdir -p /root/autodl-tmp
# 下载 0.9.4 版本并解压
wget https://github.com/hiyouga/LLaMA-Factory/archive/refs/tags/v0.9.4.zip
unzip v0.9.4.zip
mv LlamaFactory-0.9.4 LLaMA-Factory
2. 安装依赖
cd /root/autodl-tmp/LLaMA-Factory
pip install -e '.[torch,metrics]' -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install evaluate scikit-learn -i https://pypi.tuna.tsinghua.edu.cn/simple
python -c "import llamafactory, torch; print('LLaMA-Factory 版本:', llamafactory.__version__)"
3. 下载基础模型
模型文件有 40G 左右,下载要花点时间,可以趁不需要 GPU 的时候提前下。
pip install modelscope -i https://pypi.tuna.tsinghua.edu.cn/simple
modelscope download --model openai-mirror/gpt-oss-20b \
--local_dir /root/autodl-tmp/models/gpt-oss-20b
4. 安装 SwanLab
pip install swanlab
训练过程中的损失曲线、学习率等都可以在 SwanLab 网页端看到,配置简单,体验不错。
训练配置
数据集
这次用到的数据集就两个:
- identity_fixed:自定义模型身份,告诉它'我是谁,谁开发的'。
- alpaca_en_demo:通用指令微调数据集,用来增强对话能力。
identity_fixed 示例:
[{"instruction":"你好","input":"","output":"您好,我是智能小助手,一个由刘大漂亮开发的 AI 助手。"},{"instruction":"你是谁?","input":"","output":"您好,我是智能小助手,由刘大漂亮发明。我可以为您提供多种多样的服务。"}]
配置文件
编辑 examples/train_lora/gpt_lora_sft.yaml,关键参数如下:
model_name_or_path: /root/autodl-tmp/models/gpt-oss-20b
lora_rank: 8
lora_alpha: 16
lora_dropout: 0.05
stage: sft
do_train: true
finetuning_type: lora
lora_target: all
dataset: identity_fixed,alpaca_en_demo
template: gpt_oss
cutoff_len: 2048
max_samples: 1000
per_device_train_batch_size: 1
gradient_accumulation_steps: 8
learning_rate: 1.0e-4
num_train_epochs: 3.0
lr_scheduler_type: cosine
warmup_ratio: 0.1
val_size: 0.1
eval_strategy: steps
eval_steps: 100
load_best_model_at_end: true
bf16: true
gradient_checkpointing: true
report_to: swanlab
run_name: gpt-oss-20b-lora
训练步数大致这么估算:数据集总量 1090 条,划分 10% 验证集后训练集约 981 条;有效 batch 大小为 per_device_train_batch_size * gradient_accumulation_steps = 1 * 8 = 8;每个 epoch 步数约为 981 / 8 ≈ 123 步;总共 3 个 epoch,所以大约 369 步。
启动训练
cd /root/autodl-tmp/LLaMA-Factory
llamafactory-cli train examples/train_lora/gpt_lora_sft.yaml \
2>&1|tee logs/training_$(date +%Y%m%d_%H%M%S).log
第一次运行时,终端会提示你输入 SwanLab 的 API key,去设置页面复制粘贴就行。
![图片]
训练过程中打开 SwanLab 就能看到 loss 下降曲线和学习率变化。
![图片]
权重合并(可选)
权重合并就是把 LoRA 适配器和基础模型融合成一个完整的模型文件。这一步不是必须的,vLLM 可以直接加载 LoRA 进行推理。但如果之后想用其他更简单的推理框架,或者生产环境追求一点性能提升,合并可能会更方便。
合并命令:
cd /root/autodl-tmp/LLaMA-Factory
llamafactory-cli export \
--model_name_or_path /root/autodl-tmp/models/gpt-oss-20b \
--adapter_name_or_path saves/gpt-20b/lora/sft \
--export_dir models/gpt20b_lora_sft \
--export_size 2 \
--export_legacy_format false
参数简单明了:基础模型路径、LoRA 权重路径、导出路径。
vLLM 推理部署
安装 vLLM
pip install vllm fastapi uvicorn pydantic -i https://pypi.tuna.tsinghua.edu.cn/simple
vLLM 对 LoRA 的支持很到位,推荐直接加载 LoRA 权重的方式部署,省去合并步骤。而且可以同时加载多个 LoRA 模块,方便 A/B 测试。
启动服务(记得把 your-secret-api-key 换成自己的):
export FLASH_ATTN_FORCE_FA2=1
export DISABLE_FLASH_ATTN_3=1
vllm serve /root/autodl-tmp/models/gpt-oss-20b \
--enable-lora \
--lora-modules gpt-lora=/root/autodl-tmp/LLaMA-Factory/saves/gpt-20b/lora/sft \
--tokenizer /root/autodl-tmp/models/gpt-oss-20b \
--tensor-parallel-size=1 \
--trust-remote-code \
--enable-prefix-caching \
--gpu-memory-utilization 0.9 \
--host 0.0.0.0 \
--port 80 \
--api-key your-secret-api-key
这里设置了两个关键环境变量:FLASH_ATTN_FORCE_FA2=1 和 DISABLE_FLASH_ATTN_3=1,用来强制使用 Flash Attention 2,避免 H20 上因不支持 Flash Attention 3 而报错。
启动成功后,终端会显示类似 'Uvicorn running on http://0.0.0.0:80' 的信息。
![图片]
测试一下:
curl -X POST "http://你的 IP:80/v1/chat/completions" \
-H "Authorization: Bearer your-secret-api-key" \
-H "Content-Type: application/json" \
-d '{ "model": "gpt-lora", "messages": [ {"role": "user", "content": "你好,请介绍一下你自己"} ], "temperature": 0.7, "max_tokens": 200 }'
如果能收到正常回复,说明部署成功。
![图片]
如果已经做过权重合并,也可以用合并后的模型直接推理,命令更简洁:
vllm serve /root/autodl-tmp/LLaMA-Factory/models/gpt20b_lora_sft \
--host 0.0.0.0 \
--port 80 \
--trust-remote-code \
--gpu-memory-utilization 0.9 \
--max-model-len 4096 \
--served-model-name gpt-procurement \
--api-key your-secret-api-key
两种方式的对比:
| 对比项 | LoRA 直接加载 | 合并后使用 |
|---|---|---|
| 是否需要合并 | 否 | 是 |
| 配置复杂度 | 稍高 | 简单 |
| 推理速度 | 略慢 | 略快 |
| 灵活切换 LoRA | 支持 | 不支持 |
| 适合场景 | 开发测试 / 多 LoRA 切换 | 生产环境 |
实际体验下来,性能差异在采购对话这种短文本场景下几乎感觉不出来,所以日常调试还是直接用 LoRA 动态加载最方便。
踩坑记录
Token 不匹配
训练过程中可能会遇到这个错误:
{"error":{"message":"Unexpected token 200002 while expecting start token 200006","type":"BadRequestError"}}
原因是 GPT-OSS 的对话模板里默认在回复末尾添加了 <|end|>,但 tokenizer 认得的是 <|endoftext|>,导致解析出错。修复方法很简单,编辑 LlamaFactory-0.9.4/src/llamafactory/data/template.py,找到 gpt_oss 模板的定义,把 format_assistant=StringFormatter(slots=["{{content}}<|end|>"]) 改成 format_assistant=StringFormatter(slots=["{{content}}"]),然后重新训练。
Python 版本
安装依赖时如果看到 Package 'llamafactory' requires a different Python: 3.10.16 not in '>=3.11.0' 这样的错误,说明 Python 版本太低。新建一个 conda 环境就行:
conda create -n py311 python=3.11
conda activate py311
整体耗时
整套流程走下来,大概需要:
- 环境配置:30 分钟
- 模型下载:1–2 小时(取决于网络)
- 模型训练:约 1 小时(369 步)
- 权重合并(可选):10 分钟
加上踩坑和调试,一个下午绰绰有余。
参考资料

