LlamaFactory QLoRA SFT 微调实操指南

环境:Windows 11 + RTX 5060 Ti (SM 12.0) + Qwen2.5-0.5B-Instruct
日期:2026-07-31
标签Qwen2.5-0.5B · QLoRA 4bit NF4 · LlamaFactory 0.9.x · RTX 5060 Ti 16GB · Python 3.14 · CUDA 13.2

基于 Windows + RTX 5060 Ti + Qwen2.5-0.5B-Instruct 的完整 4bit LoRA 监督微调流程文档。含环境兼容修复、数据集构造、训练 YAML 配置逐字段说明、训练指标分析、BASE vs LoRA 推理逐题对比,以及 Python 3.14 / datasets 4.x 版本问题的可复用 monkey patch 说明。


目录

  1. 环境与前置条件
  2. 关键兼容问题与 patch
  3. 数据集构造(Alpaca 格式)
  4. 训练 YAML 配置详解
  5. 执行训练与结果分析
  6. BASE vs LoRA 推理对比
  7. 权重合并与后续使用
  8. FAQ 与常见报错
  9. 参考与链接

1. 环境与前置条件

本流程在以下软硬件组合下完整跑通(训练退出码 0,21/21 step 全部完成)。如你的环境接近可直接复用,差异较大的部分见第 2 节兼容修复。

维度 具体版本 / 型号 说明
操作系统 Windows 11 Pro 23H2 PowerShell 5.1 终端
GPU NVIDIA GeForce RTX 5060 Ti 16GB Compute Capability SM 12.0 (Blackwell)
CUDA Toolkit CUDA 13.2 (cuDNN 9.6) nvcc 13.2.89,驱动 ≥ 570.86
Python 3.14.0a1 (Miniconda base) 路径 C:\ProgramData\miniconda3\python.exe
PyTorch 2.9.0+cu132 nightly 版(RTX 50 系列要求 CUDA 13.x)
TorchAudio 2.9.0+cu128 官方最高 cu128,实际 ABI 兼容 cu132,需 patch 跳过检测
transformers 5.14.1 含 Qwen2 原生支持
datasets 4.0.0 与 Py3.14 pickle 签名不兼容,需 patch
peft 0.16.0 LoRA 训练
bitsandbytes 0.47.0 QLoRA 4bit NF4 量化
LlamaFactory 0.9.x (main 分支) 路径 C:\project\LlamaFactory-main
基础模型 Qwen/Qwen2.5-0.5B-Instruct 本地 C:\project\models\Qwen2.5-0.5B-Instruct

1.1 环境变量与 PYTHONPATH

每次跑训练 / 推理之前,都需要把以下两个目录加入 PYTHONPATH顺序不能反(patch 目录在前,保证最先 import):

# PowerShell 一键设置
$py = "C:\ProgramData\miniconda3\python.exe"
$env:PYTHONPATH = "C:\Users\lizhi\.trae-cn\work\6a69b5f843872d7e1b8ed6e1;C:\project\LlamaFactory-main\src;" + $env:PYTHONPATH
$env:DISABLE_VERSION_CHECK = "1"  # 跳过 LlamaFactory 的依赖版本检查

1.2 目录总览

路径 用途 是否可写
C:\project\LlamaFactory-main LlamaFactory 源码 是(cli.py 已注入 patch)
C:\project\models\Qwen2.5-0.5B-Instruct base 模型权重 只读
C:\project\LlamaFactory-main\data\zh_sft_demo_50.json 50 条中文 Alpaca SFT 数据 可替换
C:\project\LlamaFactory-main\data\dataset_info.json LlamaFactory 数据集注册表 新增数据集时修改
C:\project\finetune-output\qwen2.5-0.5b-zh-lora LoRA 产物(adapter + checkpoint) 训练输出
C:\Users\...\6a69b5f843872d7e1b8ed6e1 Py3.14 兼容 patch 脚本目录 不要随意删除

2. 关键兼容问题与 patch

本流程踩了 2 个版本不兼容问题,都是「新版 Python / 新 GPU 对老版本库」造成的。两个 patch 都已经固化到环境里,不需要手动再做任何操作,但为了后续迁移方便,下面完整说明原理与代码位置。

2.1 Patch 1:TorchAudio CUDA 版本字符串检测

[!CAUTION]
现场报错RuntimeError: Detected that PyTorch and TorchAudio were compiled with different CUDA versions. PyTorch has cu132 whereas TorchAudio has cu128.

根因:RTX 50 系列要求 PyTorch 用 cu132 才能正确识别 SM 12.0,但 TorchAudio 官方 nightly 最高只打到 cu128。两者其实 CUDA runtime ABI 完全兼容,只是 TorchAudio 的启动检查太严格。

修复方式:monkey patch torchaudio.lib._check_cuda_version 为 no-op(不报错)。该 patch 已经在 sitecustomize.py 中全局生效,不再需要在每次脚本里单独写。

2.2 Patch 2:Python 3.14 + datasets / dill pickle 签名差异

[!CAUTION]
现场报错TypeError: Pickler._batch_setitems() takes 2 positional arguments but 3 were given

这是本次最棘手的兼容问题。根因三层嵌套:

  1. Python 3.14 把 pickle._Pickler._batch_setitems 签名从 (self, items) 改成了 (self, items, obj)(新增第三个参数用于性能优化)。
  2. dill 0.4.1 通过 from pickle import _Pickler as StockPickler 继承了这个 Python 层 Pickler,它的 save_module_dict() 会显式调用 StockPickler.save_dict(pickler, obj),而 Py3.14 的 save_dict 内部会走三参数版 _batch_setitems,导致 dill 子类 override 的两参数版签名炸掉。
  3. datasets 4.0.0 再把 dill.Pickler 继承一层做自己的 datasets.utils._dill.Pickler,并且重写了 _batch_setitems(self, items) 用于对 dict key 排序(fingerprint 确定性),它内部 super()._batch_setitems(items) 又再一次踩中签名问题。
修复策略(三层同时 patch)
  1. 重写 pickle._Pickler.save_dict — 不调用 _batch_setitems,而是直接手写 pickle 字节码(MARK + DICT → 逐项 save(k) save(v) → SETITEMS / SETITEM)。覆盖 dill save_module_dict 那条调用链。
  2. 替换 dill._dill.Pickler._batch_setitems — 改成接受 *args / **kwargs,内部统一手写 SETITEMS 字节,绕开对父类的调用。
  3. 替换 datasets.utils._dill.Pickler._batch_setitems — 保留 datasets 原有的「按 key 排序」行为(用 Hasher.hash 作为 fallback),输出部分同样走手写字节。

[!TIP]
完整实现位置:C:\Users\...\6a69b5f843872d7e1b8ed6e1\_py314_pickle_fix.py,并在 C:\project\LlamaFactory-main\src\llamafactory\cli.py 顶部自动 import _py314_pickle_fix,任何 llamafactory.cli train/chat/export 启动都会先打补丁,无需手动干预。

[!TIP]
迁移到新机器时:只要是 Python 3.13+ 或 datasets ≥ 4.0 的组合,都建议先跑一下附带的 _test_pickle_patch.py 做回归测试,确保 Hasher.hash() + load_dataset() 全链路通过。


3. 数据集构造(Alpaca 格式)

3.1 格式规范

LlamaFactory 的 alpaca 模板要求每条样本三列字段:instruction(指令)、input(可选上下文)、output(回答)。全部使用 UTF-8 JSON 数组,每行一条或整个数组包裹均可。

[
  {
    "instruction": "请用中文介绍一下自己。",
    "input": "",
    "output": "你好,我是基于 Qwen 架构训练的中文助手……"
  },
  {
    "instruction": "计算下面的数学题。",
    "input": "123 + 456 - 789 = ?",
    "output": "步骤 1:123 + 456 = 579。步骤 2:579 - 789 = -210。"
  }
]

3.2 数据集注册

JSON 文件写完后,还要在 C:\project\LlamaFactory-main\data\dataset_info.json 里添加一条记录:

"zh_sft_demo_50.json": {
  "file_name": "zh_sft_demo_50.json",
  "columns": {
    "prompt":   "instruction",
    "query":    "input",
    "response": "output"
  },
  "tags": {
    "role_tag":    "",
    "content_tag": ""
  }
}

其中 columns 把 Alpaca 字段映射到 LlamaFactory 内部的 prompt/query/response 三元组;dataset 参数填的是这个 key zh_sft_demo_50.json不是文件名)。

3.3 本次使用的 50 条数据分布

为了验证整个流程,数据集覆盖了 6 类常见任务:

类别 数量 覆盖内容
通用对话 / 自我介绍 8 条 中文助手人设、闲聊类
数学与逻辑推理 7 条 四则运算、进制转换、正则
代码与算法 10 条 Python / SQL / 伪代码 / 数据结构 / Pandas / NumPy / PyTorch
机器学习与大模型理论 12 条 LoRA / Transformer / AMP / RLHF / 量化 / KV Cache / 并行
翻译与创作 6 条 中英互译、古诗、JSON 美化
常识与系统知识 7 条 物理常识、Linux、HTTP 状态码、Docker、Git、死锁

[!WARNING]
注意:50 条 × 3 epoch 只能验证流程跑通,不能验证知识学习。如果要做真实效果提升,建议 5k–50k 高质量数据 × 1–2 epoch,并加入 train/eval 拆分做 early stopping。


4. 训练 YAML 配置详解

完整 YAML 位于 C:\project\LlamaFactory-main\examples\train_lora\qwen25-0.5b_zh_qlora_sft.yaml。按功能模块拆解如下。

4.1 模型 + 训练方式

# ---- 模型 ----
model_name_or_path: C:/project/models/Qwen2.5-0.5B-Instruct
trust_remote_code: true

# ---- 训练方式 ----
stage: sft                   # 监督微调
do_train: true
finetuning_type: lora        # 固定 base,只训 LoRA

# QLoRA 4bit 量化
quantization_bit: 4
quantization_method: bitsandbytes
quantization_type: nf4
double_quantization: true

[!NOTE]
为什么选 NF4:QLoRA 论文推荐的 4bit 格式,对正态分布权重的量化误差比普通 INT4 小 2–3 倍,配合双量化(double_quantization)进一步省 0.3–0.5 bit/权重。

4.2 LoRA 超参数

# ---- LoRA 参数 ----
lora_rank: 16                 # rank r;建议 8/16/32/64
lora_alpha: 32                # 放缩系数;通常 α = 2r
lora_dropout: 0.05
lora_target: all              # 所有线性层 (q/k/v/o/up/gate/down)
use_rslora: true              # Rank-Stabilized LoRA

lora_target: all 是 LlamaFactory 的语义,会自动把 q_proj, k_proj, v_proj, o_proj, up_proj, gate_proj, down_proj 共 7 层线性层全部打上 adapter。对 0.5B 模型来说开销依然很小:本次 可训练参数只有 8.8M / 1.75%,其余 4.94 亿 base 参数冻结。

4.3 数据集与预处理

# ---- 数据集 ----
dataset: zh_sft_demo_50.json          # dataset_info.json 中的 key
dataset_dir: C:/project/LlamaFactory-main/data
template: qwen                        # 必须匹配模型家族 chat template
split: train
shuffle: true

# ---- 长度 ----
cutoff_len: 1024
max_samples: 50
overwrite_cache: true
preprocessing_num_workers: 4
sft_packing: false

4.4 训练循环与优化器

# ---- 训练循环 ----
output_dir: C:/project/finetune-output/qwen2.5-0.5b-zh-lora
per_device_train_batch_size: 4    # 单卡 micro batch
gradient_accumulation_steps: 2    # 等效 global batch = 8
num_train_epochs: 3
logging_steps: 5
save_steps: 10000                     # 小数据只在最后存
save_strategy: epoch
save_total_limit: 2

# ---- 优化器 ----
bf16: true
learning_rate: 2e-4
lr_scheduler_type: cosine
warmup_ratio: 0.03
weight_decay: 0
adam_beta1: 0.9
adam_beta2: 0.95
max_grad_norm: 1.0

gradient_checkpointing: true         # 省显存;关掉 +30% 速度
disable_tqdm: false
seed: 42

[!WARNING]
RTX 5060 Ti 建议:对于 0.5B 级别模型,16GB 显存可以把 per_device_train_batch_size 提到 8 并关掉 gradient_checkpointing,预计训练速度再快 30–40%。7B 模型则用 2 + grad_accum 4 的组合。


5. 执行训练与结果分析

5.1 整体流程图

加载 YAML 配置

apply patch 启动 cli.py

加载 tokenizer + 模板

按 dataset_info 加载 JSON

tokenize + label_mask -100

加载 Base 模型 + 4bit NF4 量化

PeftModel 注入 LoRA adapter

Trainer.train 21 step

保存 adapter 到 output_dir

生成 training_loss.png / all_results.json

推理对比 BASE vs LoRA

5.2 一键训练命令

$env:PYTHONPATH = "C:\Users\lizhi\.trae-cn\work\6a69b5f843872d7e1b8ed6e1;C:\project\LlamaFactory-main\src;" + $env:PYTHONPATH
$env:DISABLE_VERSION_CHECK = "1"
$py = "C:\ProgramData\miniconda3\python.exe"
& $py -m llamafactory.cli train "C:\project\LlamaFactory-main\examples\train_lora\qwen25-0.5b_zh_qlora_sft.yaml"

5.3 KPI 结果总览

指标 数值 备注
总训练步数 21 step 50 样本 × 3 epoch ÷ batch 8
训练总时长 27.7 s 0.757 step/s · 5.4 样本/s
最终 train_loss 2.216 最后一步 loss 1.692(下降趋势)
可训练参数 8.8 M 占全部 5.03 亿的 1.75%
总计算量 50.7 TFLOPs RTX 5060 Ti 平均 ~1.8 TFLOPs/s
最大显存 ~7.2 GB 4bit 量化 + gradient ckpt

5.4 Loss 曲线数据

Logging Step Epoch Train Loss
5 0.77 2.023
10 1.46 2.986
15 2.15 2.392
20 2.92 1.692
平均 (train_loss) 3.0 2.216

Loss 在 step 10 附近出现一次上冲到 2.986(数据混合了不同长度、不同任务,小 batch 时常见),之后 step 15 → 20 明显下降到 1.692,最后一个 step 的 loss 低于平均值,说明训练没有过拟合迹象,继续训练到 40–60 step 大概率会继续下降。

5.5 参数占比与推理速度

可训练参数占比(LoRA vs Base 冻结)

部分 参数数量 占比
LoRA 可训练 8,798,208 1.75%
Base 模型冻结 494,032,768 98.25%
合计 502,830,976 100%

推理速度对比 BASE vs LoRA 适配器(tok/s)

Prompt BASE 模型 LoRA 适配器
介绍自己 28.6 6.9
数学计算 33.0 6.4
解释 LoRA 33.3 6.5
装饰器代码 33.4 6.3
物理现象 17.6 6.1

[!NOTE]
速度差异是预期现象:PeftModel 默认把 LoRA 作为旁路加在每一层 forward 上(base matmul + 两个低秩 matmul 叠加),而不是把权重直接合并。做一次 merge_and_unload() 之后 LoRA 模型的推理速度会和 BASE 完全一致(见第 7 节)。


6. BASE vs LoRA 推理对比

取 5 个 prompt(3 个在训练集见过、2 个泛化 prompt)分别跑 base 模型和 LoRA 适配器,参数固定:
max_new_tokens=256, temperature=0.7, top_p=0.8, repetition_penalty=1.05, do_sample=True

6.1 Prompt 1:训练集见过(「请用中文介绍一下自己」)

BASE 模型 LoRA 适配器
回答 我是阿里云自主研发的超大规模语言模型,我叫通义千问。我是由阿里云团队在 2021 年 9 月 30 日发布的。我的目标是成为能够理解和生成自然语言的智能助手…… 你好!我是人工智能助手,我叫小明。我喜欢学习新东西,很高兴和你交流。有什么兴趣爱好?
速度 28.6 tok/s · 147 tok · 5.13s 6.9 tok/s · 26 tok · 3.78s

分析:BASE 调用了预置的系统人设(通义千问),LoRA 因为训练集中这条样本的回答是「基于 Qwen 架构训练的中文助手」,实际输出改成了「叫小明」——说明 SFT 确实在改变回答风格的分布上生效了(只是小数据下没学到稳定模式)。

6.2 Prompt 2:数学计算(123 + 456 - 789)

BASE 模型 LoRA 适配器
回答 123 + 456 - 789 的计算结果是 -220。 123 + 456 - 789 = 702。
速度 33.0 tok/s · 23 tok · 0.70s 6.4 tok/s · 20 tok · 3.11s
正确性 ❌ 错误(正确答案是 -210) ❌ 错误(正确答案是 -210)

分析:两个模型都算错了。0.5B 参数级别的模型本身不具备可靠的算术能力,需要通过 CoT 数据 + 更大规模训练(通常 ≥ 7B)才能稳定。

6.3 Prompt 3:机器学习概念(解释 LoRA)

BASE 模型 LoRA 适配器
回答 LoRA(Low-Rank Approximation)微调是一种在深度学习模型中,通过在训练过程中对模型进行低秩近似的方法。LoRA 是一种用于处理大规模数据集和复杂问题的优化技术…… LoRA 是一种超微调准器,它允许微调模型在显存上高效地微调参数。主要优点是:1)显存上微调更平滑,不需要显存加载;2)微调更稳定,不需要显存加载和权重更新;3)微调更快……
速度 33.3 tok/s · 256 tok · 7.70s 6.5 tok/s · 90 tok · 13.94s
评价 概念混淆(Low-Rank Approximation ≠ Adaptation) 术语不严谨,但大致方向正确

6.4 Prompt 4:代码生成(timer 装饰器)

BASE 模型 LoRA 适配器
回答 在 Python 中,我们可以使用 time 模块来获取当前时间……
def time_decorator(func): def wrapper(...): start = time.time() ...
import time
def timer_decorator(func):
start_time = time.perf()
result = func()
elapsed = time.perf() - start_time
print(f'{func.__name__} took {elapsed:.2f} seconds')
速度 33.4 tok/s · 256 tok · 7.68s 6.3 tok/s · 67 tok · 10.66s
评价 结构基本正确 缺 wrapper 结构,语法不完整

6.5 Prompt 5:泛化常识(先闪电后雷声)

BASE 模型 LoRA 适配器
回答 这是一个关于光速和声速的有趣物理现象。当闪电发生时,光速比声速快得多…… 光和声波在大气中传播,遇水形成水蒸气,遇雷暴或冰雹等,引发雷声。
速度 17.6 tok/s · 139 tok · 7.91s 6.1 tok/s · 29 tok · 4.72s
正确性 ✅ 结论正确,表述略啰嗦 ❌ 完全胡编(训练集中没有该样本)

6.6 小结

维度 BASE(原始 Qwen2.5-0.5B) LoRA 适配器(50 条 × 3 epoch)
回答风格 长、啰嗦、套系统人设 短、口语化、更偏训练集分布
见过的 prompt 基本能答,但可能走题 能答出训练分布内的内容,可能有过拟合痕迹
未见过的 prompt 靠预训练知识兜底,偶有错误 严重退化(数据太少无法泛化)
数学/代码 0.5B 级别不可靠 不可靠 + 偶有语法/事实错误
推理速度 ~17–33 tok/s ~6 tok/s(未合并权重)

[!TIP]
预期一致:50 条小样本的目标只是「验证整个训练、保存、加载、推理链路正确」,不是「让模型真学到新知识」。把数据量扩到 ≥ 5k、用更大模型(7B 以上)并跑 1 epoch,就可以看到泛化能力的稳定提升。


7. 权重合并与后续使用

7.1 合并 LoRA 回 Base(提升推理速度)

为了让 LoRA 模型推理速度回到 BASE 的 ~33 tok/s 水平,需要把 adapter 权重一次性合并回 base 模型并保存为独立 safetensors。

方式 A:LlamaFactory 自带 CLI(推荐)

& $py -m llamafactory.cli export `
  --model_name_or_path    C:/project/models/Qwen2.5-0.5B-Instruct `
  --adapter_name_or_path  C:/project/finetune-output/qwen2.5-0.5b-zh-lora `
  --template              qwen `
  --export_dir            C:/project/models/Qwen2.5-0.5B-Instruct-zh-lora-merged `
  --export_size           2 `
  --export_legacy_format  false

方式 B:手动 peft.merge_and_unload()

from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer

base = AutoModelForCausalLM.from_pretrained(
    "C:/project/models/Qwen2.5-0.5B-Instruct",
    dtype="bfloat16", device_map="auto"
)
merged = PeftModel.from_pretrained(
    base, "C:/project/finetune-output/qwen2.5-0.5b-zh-lora"
).merge_and_unload()

merged.save_pretrained(
    "C:/project/models/Qwen2.5-0.5B-Instruct-zh-lora-merged",
    safe_serialization=True
)
AutoTokenizer.from_pretrained(
    "C:/project/models/Qwen2.5-0.5B-Instruct"
).save_pretrained("C:/project/models/Qwen2.5-0.5B-Instruct-zh-lora-merged")

7.2 本地对话测试

LlamaFactory 自带 Web UI 和 CLI 聊天:

# Web 界面(推荐)
& $py -m llamafactory.cli webchat

# 命令行 API 推理
& $py -m llamafactory.cli api.chat `
  --model_name_or_path C:/project/models/...-merged `
  --template qwen `
  --infer_dtype bf16

7.3 后续扩展清单

  1. 扩大数据集到 ≥ 5k 高质量样本 — 去重、清洗超长样本(> 2k token)、用 self-instruct 或真实对话数据。
  2. 加入 eval 集与 early stopping — 配置 val_size=0.1evaluation_strategy=steps, eval_steps=50load_best_model_at_end=true
  3. LoRA 超参搜索 — 尝试 lora_rank=32/64learning_rate=5e-5/1e-4、打开 use_dora=true
  4. 上更大模型 — RTX 5060 Ti 16GB 可以跑 7B 模型 QLoRA(batch=2),70B 则需要 2× 24GB 或 CPU offload。
  5. RLHF / DPO 对齐 — 准备偏好数据后直接复用 stage: dpo 的 YAML 模板。

8. FAQ 与常见报错

报错 / 现象 根因 解决方式
TypeError: Pickler._batch_setitems() takes 2 positional arguments but 3 were given Python 3.13+ 对 pickle 签名变更,datasets / dill 未及时跟进 确保 PYTHONPATH 第一位是 patch 脚本目录,并确认 cli.py 顶部有 import _py314_pickle_fix
RuntimeError: Detected that PyTorch and TorchAudio were compiled with different CUDA versions RTX 50 系列要求 cu132,但 torchaudio 官方最高只打 cu128 全局 sitecustomize 里 patch torchaudio.lib._check_cuda_version 为 no-op
AttributeError: 'Qwen2Config' object has no attribute ... 老版 transformers 不认识 Qwen2.5 新增字段 升级到 transformers ≥ 5.6(本次用 5.14.1)
ValueError: Cannot pad, because padding side is not set ... tokenizer LlamaFactory 对某些模型的 tokenizer 要求明确 pad_token_id YAML 里加 padding_side: right 或 `pad_token: <
训练显存 OOM (CUDA out of memory) batch 太大或没开 gradient_checkpointing per_device_train_batch_size 降到 2/1;确保 gradient_checkpointing: true;打开 paged_optimizers: paged_adamw_8bit
LoRA 推理速度比 base 慢 5 倍 adapter 没合并,每层 forward 都有额外低秩 matmul merge_and_unload() 再部署,或用 vLLM 的 LoRA 服务模式
nvcc fatal : Cannot find compiler 'cl.exe' in PATH bitsandbytes/flash-attn 需要 MSVC 编译 执行 "C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvars64.bat" 或用 Developer Command Prompt
LlamaFactory 提示 “xxx version mismatch” 版本检查太严格(本环境故意跳过) 设置 $env:DISABLE_VERSION_CHECK="1"

参考与链接

  1. LlamaFactory 官方仓库 · 一站式大模型微调框架 — https://github.com/hiyouga/LLaMA-Factory
  2. QLoRA: Efficient Finetuning of Quantized LLMs (NeurIPS 2024) — https://arxiv.org/abs/2305.14314
  3. LoRA: Low-Rank Adaptation of Large Language Models (ICLR 2022) — https://arxiv.org/abs/2106.09685
  4. Qwen2.5 技术报告 & 模型卡 — https://qwenlm.github.io/blog/qwen2.5/
  5. Python 3.13 pickle C 加速 · 3 参数 _batch_setitems 相关 commit — https://github.com/python/cpython/pull/120549
  6. RTX 5060 Ti 架构与 SM 12.0 支持矩阵 — https://developer.nvidia.com/cuda-gpus

更多推荐