4. SFT 实战
这是什么:SFT(supervised fine-tuning,监督微调)= 拿「问题 + 标准答案」当教材,教模型按你期望的方式回答;LoRA 是只训练小补丁矩阵的省显存做法。
为什么用:前两章把环境和数据备好了,现在要动手。微调不是一条命令就完事,配置里几十个参数各有脾气,踩错一个就训出一堆废权重。
如何用:本章直接用官方示例配置 examples/train_lora/qwen3_lora_sft.yaml,先跑通,再逐项拆解,最后用 chat 验证模型真的变了。
4.1 先跑通官方示例
进到仓库目录,先确认一下你的 GPU:
nvidia-smi
然后直接开跑:
llamafactory-cli train examples/train_lora/qwen3_lora_sft.yaml
你会看到(示意):
[INFO] Loading model weights from disk: ...
[INFO] Fine-tuning method: LoRA
[INFO] Number of trainable parameters: 16,515,072
[INFO] dataset: identity,alpaca_en_demo -> 1000 samples
[INFO] Num examples = 1000, Total train batch size = 8
3%|▎ | 11/375 [00:04<02:08, 2.98it/s, loss=1.234, lr=1.00e-04]
{'train_runtime': 418.3, 'train_loss': 0.9871, 'train_samples_per_second': 2.39}
Total train batch size = 8 是 per_device_train_batch_size(1)乘 gradient_accumulation_steps(8)算出来的;1000 条样本 ÷ 8 = 125 步一轮,3 轮一共 375 步。
第一次运行会自动从 HuggingFace 下载 Qwen/Qwen3-4B-Instruct-2507(约 8GB),国内网络慢就 export HF_ENDPOINT=https://hf-mirror.com。
跑完后去 saves/qwen3-4b/lora/sft/ 看一眼,LoRA 权重就存那儿了。
4.2 配置文件逐项拆解
官方示例分六块,我们一块块看。
model(模型)
| 配置项 | 示例值 | 含义 |
|---|---|---|
model_name_or_path |
Qwen/Qwen3-4B-Instruct-2507 |
基座模型,可以写 HuggingFace 仓库名或本地路径 |
trust_remote_code |
true |
允许加载模型仓库里自带的 Python 代码 |
quantization_bit |
不填 | 填 4 就是 QLoRA 4bit |
quantization_method |
不填 | 量化后端:bnb / hqq / eetq |
method(怎么训)
| 配置项 | 示例值 | 含义 |
|---|---|---|
stage |
sft |
训练阶段:pt / sft / rm / ppo / dpo / kto |
do_train |
true |
明确表示这次是训练而不是评测 |
finetuning_type |
lora |
微调方式:lora / oft / freeze / full |
lora_rank |
8 |
LoRA 矩阵的秩,越大容量越大、学得越细 |
lora_target |
all |
挂到哪些层上,all 表示所有线性层 |
lora_alpha 不写时默认取 lora_rank * 2,一般不用改。
dataset(数据)
| 配置项 | 示例值 | 含义 |
|---|---|---|
dataset |
identity,alpaca_en_demo |
数据集注册名,多个用逗号隔开 |
template |
qwen3_nothink |
对话模板,训练推理必须一致 |
cutoff_len |
2048 |
样本最大 token 数,超了截断 |
max_samples |
1000 |
每个数据集最多取多少条(调试用) |
preprocessing_num_workers |
16 |
预处理数据的进程数 |
dataloader_num_workers |
4 |
读数据的进程数,Windows 报错就设 0 |
identity 这个数据集很关键——它专门教模型「你是谁」,是所有微调的标配。
output(产物)
| 配置项 | 示例值 | 含义 |
|---|---|---|
output_dir |
saves/qwen3-4b/lora/sft |
权重和日志的存放目录 |
logging_steps |
10 |
每几步打印一次 loss |
save_steps |
500 |
每几步存一次检查点 |
plot_loss |
true |
画出 loss 曲线图 |
overwrite_output_dir |
true |
允许覆盖同名输出目录 |
report_to |
none |
训练监控:none / wandb / tensorboard / swanlab / mlflow |
train(训练超参)
| 配置项 | 示例值 | 含义 |
|---|---|---|
per_device_train_batch_size |
1 |
每张卡一次喂几条 |
gradient_accumulation_steps |
8 |
攒几步再更新,等效 batch = 1 × 8 = 8 |
learning_rate |
1.0e-4 |
学习率,LoRA 用 1e-4 左右,全参要小到 1e-5 |
num_train_epochs |
3.0 |
训练轮数 |
lr_scheduler_type |
cosine |
学习率调度策略 |
warmup_ratio |
0.1 |
前 10% 的步数用来热身 |
bf16 |
true |
用 bfloat16 算,显存减半且更稳 |
resume_from_checkpoint |
null |
填检查点路径可以断点续训 |
等效 batch size 很重要:显存不够时把 per_device_train_batch_size 降到 1,用 gradient_accumulation_steps 补上去,训练效果几乎不变。
eval(验证)
默认是注释掉的,想开验证集就把这几行取消注释:
eval_dataset: alpaca_en_demo
val_size: 0.1
per_device_eval_batch_size: 1
eval_strategy: steps
eval_steps: 500
4.3 显存不够?换 QLoRA
按第 2 章的账,4B 模型用 LoRA 只要 8GB 左右,24GB 卡完全够。但如果你要微调 7B 以上的模型,就得上 QLoRA——加两行就行:
quantization_bit: 4
quantization_method: bnb
官方示例在 examples/train_qlora/qwen3_lora_sft_otfq.yaml。quantization_method 可选 bnb(bitsandbytes)、hqq、eetq;quantization_bit 可选 8(bnb/hqq/eetq)、4(bnb/hqq)、3(hqq)、2(hqq)。
4.4 训练完先聊两句
llamafactory-cli chat examples/inference/qwen3_lora_sft.yaml
你会看到(示意):
User: 用一句话介绍你自己
Assistant: 我是一个专注于回答问题的 AI 助手,可以帮你解答各类知识与生活问题。
User: 介绍一下 LLaMA-Factory
Assistant: LLaMA-Factory 是一个开源的大模型微调框架...
这个配置文件长这样:
model_name_or_path: Qwen/Qwen3-4B-Instruct-2507
adapter_name_or_path: saves/qwen3-4b/lora/sft
template: qwen3_nothink
infer_backend: huggingface # choices: [huggingface, vllm, sglang, ktransformers]
trust_remote_code: true
注意 adapter_name_or_path 指向你刚训出来的 LoRA 目录,template 和训练时一模一样——这两处不一致,模型就会答非所问。
4.5 看 loss 曲线
report_to 设成 tensorboard 就能看实时曲线:
report_to: tensorboard
tensorboard --logdir saves/qwen3-4b/lora/sft
你会看到(示意):
loss: 1.24 → 0.71 → 0.52 → 0.41
loss 稳步下降是好信号;如果 loss 突然归零,多半是数据集太简单或者学过头(过拟合),把 num_train_epochs 降到 1~2 再试。想接 Wandb、MLflow、SwanLab 也是改 report_to 一个字段的事。
4.6 常见报错速查
| 报错 | 原因 | 解法 |
|---|---|---|
CUDA out of memory |
显存不够 | 降 batch、开 QLoRA、减 cutoff_len |
Can't pickle local object |
Windows 多进程 | dataloader_num_workers: 0 |
dataset not found |
没注册 | 检查 data/dataset_info.json |
| 回答全是乱码/不搭调 | template 不一致 | 训练和推理用同一个 template |
loss 是 nan |
学习率过大 | 调小 learning_rate,确认 bf16 |
动手改
- 把
max_samples改成50、num_train_epochs改成1,快速跑一次,理解每个参数的影响 - 把
lora_rank从8改成32,对比输出目录里 adapter 权重大小和trainable parameters的变化 - 打开
report_to: tensorboard训一次,把 loss 曲线截图保存,想想哪些样本可能没被学到
小结
- SFT 就是拿「问题 + 标准答案」当教材;LoRA 只训练小补丁矩阵,最省显存
- 官方示例
examples/train_lora/qwen3_lora_sft.yaml直接能跑,先跑通再改 - 配置分六块:model / method / dataset / output / train / eval,
stage、finetuning_type、dataset、template是四个最关键的 - 显存不够就加
quantization_bit: 4+quantization_method: bnb上 QLoRA - 训完用
llamafactory-cli chat验证,adapter_name_or_path和template必须对 report_to可以接 tensorboard / wandb / swanlab / mlflow 看 loss 曲线
下一章:偏好对齐 🐸