GOCLAWLLM ENGINEERING
GoClaw 首页

10. 微调:从 Full Fine-tuning 到 LoRA/QLoRA

工程进阶4~8 小时
学习目标
  1. 区分 CPT、SFT、LoRA 与 QLoRA
  2. 解释低秩更新和 target modules
  3. 用固定题集比较 Base 与 Adapter
前置知识
  • 模型前向与优化
  • 训练/验证/测试切分

本章产物LoRA Adapter、训练日志和前后逐题评估。

10.1 预训练、继续预训练和 SFT

阶段数据学到什么
Pretraining大量普通 token 序列通用语言、知识和模式
Continued Pretraining/CPT领域无标注文本领域语言与分布
SFT指令—回答或多轮对话指令遵循、格式、任务行为
Preference Optimizationchosen/rejected 或奖励行为偏好与对齐

如果目标是让模型学习大量领域术语和文体,CPT 可能比只做问答 SFT 更合适;如果目标是固定输入输出行为,SFT 更直接。

10.2 Full Fine-tuning 为什么贵

Full fine-tuning 更新全部参数,因此需要:

对于多个任务,还需要分别保存完整模型。LoRA 通过冻结基础权重,只训练少量低秩更新,降低训练和存储成本。

10.3 LoRA 的数学

原线性层:

y = x W^T

微调希望学习:

W' = W + ΔW

LoRA 假设更新近似低秩:

ΔW = B A

若 W 形状 [d_out, d_in],秩 r:

A: [r, d_in]
B: [d_out, r]

完整更新参数:

d_out × d_in

LoRA 参数:

r × (d_in + d_out)

当 r 远小于维度时,差异巨大。

配套实验:从零实现 LoRA Notebook。实验验证参数冻结、低秩增量训练和部署前权重合并。

常见缩放:

y = xW^T + (alpha / r) × xA^T B^T

运行:

python code/05_lora_from_scratch.py

代码将 B 初始化为 0,使训练开始时 LoRA 分支输出为 0,模型初始行为与基础层一致。

10.4 target_modules 怎样选

常见目标:

只调 Q/V 参数更少;调全部线性层适应能力更强但内存和存储更大。最佳选择取决于任务和模型,不能把某个博客配置当普遍定律。

关键超参数:

10.5 QLoRA

QLoRA:

冻结的基础模型:4-bit 量化
可训练部分:LoRA adapter
梯度:穿过量化基础模型,更新 adapter

它不是“直接训练 4-bit 基础权重”。原论文还引入 NF4、double quantization 和 paged optimizer 等内存优化。

在 NVIDIA 上常见 bitsandbytes + PEFT;在你的 Mac 上更适合使用 MLX-LM 对量化 MLX 模型训练 adapter。不要强行安装 CUDA 专用教程里的 bitsandbytes 路线。

10.6 SFT 数据格式

资料包 data/sft_train.jsonl

{"messages":[
  {"role":"system","content":"你是一位耐心的老师。"},
  {"role":"user","content":"什么是 token?"},
  {"role":"assistant","content":"……"}
]}

高质量 SFT 数据应:

一万条高质量、针对性强的数据,可能比百万条模板化垃圾数据更有价值。

10.7 在 Qwen3-0.6B 上做 LoRA SFT

python code/06_sft_lora.py \
  --model Qwen/Qwen3-0.6B \
  --data data/sft_train.jsonl \
  --epochs 1

这是流程演示数据,只有几条,不能用于得出能力结论。正式实验至少:

  1. 划分验证和测试集。
  2. 训练前先评估 base model。
  3. 固定评估 prompt 和采样。
  4. 观察 train/validation loss。
  5. 对 adapter 后模型运行同一评估。
  6. 人工检查是否出现灾难性遗忘、格式过拟合和错误自信。

若当前 TRL 版本更新了参数名,优先以安装版本的 SFTConfig 帮助和官方文档为准:

python -c "from trl import SFTConfig; help(SFTConfig)"

10.8 使用 MLX-LM LoRA/QLoRA

python -m pip install "mlx-lm[train]"

mkdir -p artifacts/mlx-data
cp data/sft_train.jsonl artifacts/mlx-data/train.jsonl

mlx_lm.lora \
  --model mlx-community/Qwen3-0.6B-4bit \
  --train \
  --data artifacts/mlx-data \
  --iters 100

MLX-LM 要求数据目录按其文档放置 train.jsonl,可选 valid.jsonl,测试时使用 test.jsonl。当模型本身为量化模型时,其训练路线属于 QLoRA。

不要因为 48GB 能装下大模型就立刻对 32B 做 LoRA。先用 0.6B 验证数据格式、loss mask、保存和评估,再逐步扩大。

10.9 过拟合、灾难性遗忘与风格塌缩

应对:

10.10 动手:把 SFT 做成前后可比较的实验

实验 06|LoRA/SFT 资源:0.6B 模型建议 8GB 以上可用内存;时间取决于设备与下载;产物:数据切分、base 结果、Adapter、训练日志和 adapter 结果。

不要从训练命令开始。先准备固定评测文件 data/sft_eval.jsonl,每条至少包含:

{"id":"format-001","messages":[{"role":"user","content":"以 JSON 返回两个字段"}],"required":["answer","reason"]}

完整顺序:

  1. sft_train.jsonl 去重并按任务族切分,防止同模板跨集合泄漏。
  2. 固定 20~100 条小型评测集,训练前保存 base model 的逐题原始输出。
  3. 运行一次小规模 LoRA,确认 loss、显存和 Adapter 保存路径正常。
  4. 用同一 prompt template、采样参数和评测代码生成 adapter 输出。
  5. 对比格式通过率、任务正确率、拒答、安全和通用保留集;逐条保存变化。

训练冒烟命令:

python code/06_sft_lora.py \
  --model Qwen/Qwen3-0.6B \
  --data data/sft_train.jsonl \
  --output artifacts/checkpoints/qwen3-06b-lora-smoke \
  --epochs 0.1

训练报告不能只贴最后一个 loss。至少填写:

证据BaseAdapter解释
格式通过率是否真正学会目标格式
目标任务正确率逐题判分规则是什么
通用保留集是否发生明显遗忘
validation loss是否与 train loss 同向
峰值内存与耗时模型、设备和 dtype

停止条件:如果训练数据只有几条演示样本,可以验收“流程可运行”,但不得写出“能力提升”的结论。如果 Adapter 只让所有答案套用统一模板,应归类为风格过拟合,而不是对齐成功。

先执行数据切分和泄漏检查:

python code/10_prepare_sft_data.py \
  --input data/sft_train.jsonl \
  --output-dir artifacts/sft-data

脚本按 prompt group 切分,而不是随机按行切分。若数据少于三个独立 group,它会发出警告;这时只能验证流程,不能声称验证集具有统计意义。

偏好优化使用独立的 preference JSONL:

python code/13_dpo_lora.py \
  --data data/preference_train.jsonl \
  --dry-run

--dry-run 会校验 chosen/rejected 数据契约但不下载模型;确认数据、版本和资源后,再去掉该参数执行真实 DPO。真实训练必须把 reference model、β、chat template、prompt mask 和独立评估集写入报告。


本章依据

原理性结论以原始论文、官方文档或公开教材为依据。论文中的实验结果只适用于其声明的模型、数据、硬件和评估设置。

  1. 冻结基座权重并训练低秩增量矩阵。

  2. 4-bit 冻结基座、NF4、双重量化和分页优化器。

  3. LoRA Adapter 的配置、训练、保存和合并接口。

  4. Apple Silicon 上量化模型使用 QLoRA、数据格式与 Adapter 工作流。