快速上手:跑通你的第一次 LoRA SFT
3.1 两种入口:YAML 还是 Web UI?
LLaMA-Factory 提供两种使用方式,互为镜像:
| 入口 | 命令 | 适合什么场景 |
|---|---|---|
| YAML + CLI | llamafactory-cli train xxx.yaml | 可复现、可写脚本、可上 git、生产环境 |
| Web UI | llamafactory-cli webui | 第一次跑、不会写 YAML、要演示给同事看 |
它们在底层调的是完全同一份训练代码——Web UI 只是把 YAML 的每个字段做成了网页表单。本章带你两种都跑一次。
3.2 路线 A:用 Web UI 跑通
# 进 LlamaFactory 目录(无论怎么装的,CLI 入口都一样)
cd /Users/zjw/Documents/LLM/train/repos/llama-factory
# 启动 Web UI(默认 7860 端口,可加 --server_port 7861 改)
llamafactory-cli webui
浏览器开 http://localhost:7860/,进入 LLaMA Board 界面,关键操作步骤:
- 顶部 Lang 选
zh,整个界面变中文; - Model name 选
Qwen3-4B-Instruct-2507(或任何你想训的);模型源国内用户可在右上角 Model path source 里切到 ModelScope; - Stage 选
Supervised Fine-Tuning; - Dataset 勾选
identity,alpaca_en_demo(这两个是仓库自带的 demo 数据,无需下载); - LoRA configurations → 把
lora_rank拉到 8、lora_target设为all; - 训练参数:默认值就行;
- 点 Preview command,会输出对应的 CLI 命令(这一步非常宝贵——你能马上学到对应的 YAML 长什么样);
- 点 Start 开始训练,下方就会实时显示 loss 曲线。
预期:在一张 24 GB 显卡上,3 epoch × 1000 条样本,大约 5–10 分钟跑完,loss 从 ~2 降到 ~0.6。
3.3 路线 B:用 YAML + CLI 跑通
打开仓库自带的范例:
cat examples/train_lora/qwen3_lora_sft.yaml
这份文件就是本章最重要的"教学样本",它的结构非常清晰,分成 5 个区段:
### model ← 选基座模型
model_name_or_path: Qwen/Qwen3-4B-Instruct-2507
trust_remote_code: true
### method ← 选训练算法
stage: sft
do_train: true
finetuning_type: lora
lora_rank: 8
lora_target: all
### dataset ← 喂什么数据
dataset: identity,alpaca_en_demo
template: qwen3_nothink
cutoff_len: 2048
max_samples: 1000
preprocessing_num_workers: 16
dataloader_num_workers: 4
### output ← 保存到哪、日志去哪
output_dir: saves/qwen3-4b/lora/sft
logging_steps: 10
save_steps: 500
plot_loss: true
overwrite_output_dir: true
save_only_model: false
report_to: none
### train ← 标准训练超参
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
bf16: true
ddp_timeout: 180000000
resume_from_checkpoint: null
启动:
llamafactory-cli train examples/train_lora/qwen3_lora_sft.yaml
就一行,所有事情都封装在 YAML 里。
llamafactory-cli train examples/train_lora/qwen3_lora_sft.yaml \
learning_rate=2e-4 num_train_epochs=1
适合做超参 sweep。
3.4 训完之后
output_dir(这里是 saves/qwen3-4b/lora/sft/)会出现这些文件:
adapter_config.json ← LoRA 配置
adapter_model.safetensors ← LoRA 权重(增量,几十 MB)
tokenizer* ← tokenizer 副本(推理需要)
training_args.bin
trainer_state.json
training_loss.png ← plot_loss=true 自动生成的曲线
注意只有 LoRA 增量,没有完整模型。如果要拿出去部署完整模型,要 merge 回基座:
llamafactory-cli export \
model_name_or_path=Qwen/Qwen3-4B-Instruct-2507 \
adapter_name_or_path=saves/qwen3-4b/lora/sft \
template=qwen3_nothink \
export_dir=merged_model
merged_model 目录下就是普通 HuggingFace 格式模型,可以 transformers.AutoModelForCausalLM.from_pretrained() 直接加载。
3.5 立刻试用训完的模型
在没 merge 的情况下也能 chat:
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
默认 8000 端口,curl 测试:
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "default",
"messages": [{"role":"user","content":"你是谁?"}]
}'
identity 数据集会让模型回答自己叫 LLaMA Board ——你能直接看到 LoRA 起效了。
3.6 把整套子命令记一下
LLaMA-Factory 用 llamafactory-cli <command> 的子命令风格,本章已经用到 4 个,全套见 src/llamafactory/launcher.py 顶部的 USAGE:
| 子命令 | 用途 |
|---|---|
llamafactory-cli api | 启动 OpenAI 兼容 API |
llamafactory-cli chat | CLI 聊天 |
llamafactory-cli export | merge LoRA、导出模型 |
llamafactory-cli train | 训练(本章重点) |
llamafactory-cli webchat | Web 聊天 |
llamafactory-cli webui | LLaMA Board 训练 UI |
llamafactory-cli env | 打印环境 |
llamafactory-cli version | 打印版本 |
可以用 lmf 作为 llamafactory-cli 的别名缩写。
3.7 入口背后:从 llamafactory-cli train 到 HF Trainer
"一行 llamafactory-cli train xxx.yaml"背后到底发生了什么?查 src/llamafactory/launcher.py(38–186 行)就能完整还原:
| 步 | 位置 | 做什么 |
|---|---|---|
| 1 | cli.py:main() | 根据 USE_V1 选择 v0(默认)或 v1 入口 |
| 2 | launcher.py:38 launch() | 解析 sys.argv[1] 子命令名 |
| 3 | launcher.py:60–102 | 检测 get_device_count() > 1 或 FORCE_TORCHRUN=1 时,自动拼出 torchrun 命令并 subprocess.run 重新拉起子进程 |
| 4 | 子进程内:src/train.py:15 | 从 llamafactory.train.tuner 导入 run_exp,调用 |
| 5 | train/tuner.py:139 run_exp() | 调 read_args()(OmegaConf 解析 YAML + CLI override) |
| 6 | 判 ray_args.use_ray | True → _ray_training_function;False → _training_function |
| 7 | train/tuner.py:68 _training_function() | 调 get_train_args()(parser.py:304–534)拿到 5 个 dataclass |
| 8 | 按 finetuning_args.stage 分发 | pt → run_pt() / sft → run_sft() / rm → run_rm() / ppo → run_ppo() / dpo → run_dpo() / kto → run_kto() |
| 9 | 对应 stage 的 workflow.py | 加载数据 + 加载模型 + 构造 Trainer + trainer.train() |
三个值得知道的关键设计:
- torchrun 是 launcher 自动拉起的,用户不用自己写。单卡时不拉 torchrun,直接 in-process;多卡时一律走 torchrun 重新拉子进程;
- v0 / v1 双入口:默认 v0(本书讲的);设
USE_V1=1切到src/llamafactory/v1/的实验性新架构(trainer 模块化重构,未稳定,ch13 会展开对比); - OmegaConf merge 顺序:先读 yaml,再用 CLI override 覆盖(
parser.py:85–99的read_args())。所以llamafactory-cli train xxx.yaml learning_rate=2e-4一定能覆盖 yaml 里的值。
3.8 整体流程速写
生成等效 YAML"] Preview --> CLI CLI --> Loader[transformers 加载 base 模型] Loader --> PEFT[PEFT 注入 LoRA] PEFT --> Trainer[HF Trainer 主循环] Trainer --> Save[保存 adapter] Save --> Export["llamafactory-cli export
merge → 完整模型"] Save --> Chat["llamafactory-cli chat / api
带 adapter 推理"]