大模型微调(LLM Fine-Tuning)¶
简介¶
模型微调(Fine-tuning)是利用特定领域的数据集 对已预训练的大模型 进行进一步训练的过程,以优化模型在特定任务上的性能。微调的核心步骤包括数据准备、选择基础模型、设置微调参数和进行微调训练。
为什么需要微调¶
预训练模型(Base 或 Instruct 版)虽然"什么都懂一点",但直接拿来用往往不够:
- 不懂你的领域黑话 —— 电商的"仅退款""拦截件""SKU 锁库存",通用模型可能一知半解
- 不知道你的业务规则 —— 什么时候该直接答、什么时候必须查系统,这是你公司的决策逻辑,模型天生不会
- 输出格式/语气不对 —— 你想要客服腔、想要结构化工具调用、想要"不编造、不确定就兜底",基座不会自动这么干
微调解决的就是这三件事:领域知识、业务规则、输出风格。
大白话:预训练是地基,微调是装修。
预训练模型如同一栋毛坯房,水电管线、承重结构(即通用语义理解能力)已完备。微调,则是在此基础上进行“精装修”——根据住户(特定任务)的需求,更换地板材质、调整灯光布局,而非重建整栋楼。
换句话说,预训练赋予了W 丰富的世界知识和语言结构,而微调只是让 W 在面对特定数据分布(如法律文书、医疗问答)时,重新加权某些语义维度。例如,在医疗微调中,“头痛”与“布洛芬”的关联权重会被增强,而“头痛”与“老板”的关联则被抑制——这正是通过梯度下降微调 W 实现的。
微调的本质:更新参数,但量可大可小¶
技术上,微调就是把你的数据喂进去,做反向传播,更新模型权重。但"更新多少"是可选的:
全量微调(Full Fine-tuning)¶
将预训练模型视为 “初始权重”,用目标任务的有监督数据**重新训练所有层的参数(从输入层到输出层)**,让模型完全适配目标任务。
-
优点:无参数冻结,模型可充分学习任务特性,效果好,在数据量充足时能达到最优性能。
-
缺点:
- 成本极高:全量微调会更新模型所有参数,代价惊人:以 LLaMA-7B 为例,需更新 70 亿参数,显存占用轻松突破 80GB,训练成本可能需要数万美元。
- 数据需求高:若标注数据不足(如仅数千条),易导致 “过拟合”(模型记住训练数据,泛化能力差)。
参数高效微调(Parameter-Efficient Fine Tuning, PEFT)¶
PEFT 是针对 FFT “高成本” 痛点提出的技术,核心是 冻结大部分预训练参数(基座),仅更新少量参数,在降低成本的同时保持性能。以下是最主流的 PEFT 方法。现在做项目,基本无脑选 LoRA / QLoRA。 其余方法更多是论文对比基线或特殊场景补充。
| 方法 | 思路 | 训练参数量 | 特点 |
|---|---|---|---|
| LoRA | 在注意力/MLP 层旁路加低秩矩阵 A×B,只训 A、B | 0.1%~1% | 事实标准,效果接近全参数,推理可合并无延迟 |
| QLoRA | LoRA + 基座 4bit 量化 | 0.1%~1% | 单卡 24G 可微调 7B~13B,显存受限首选 |
| Adapter Tuning | 在 Transformer 层间插瓶颈结构小模块 | 1%~5% | 早于 LoRA,结构直观,现在用得少了 |
| Prefix Tuning | 输入前加一段可学习"虚拟 token" | 0.1% 左右 | 不改模型结构,但长文本场景略吃亏 |
| P-Tuning v2 | Prefix 的改进,每层都加 | 0.1%~1% | 深层 prefix,NLU 任务效果好于初版 |
| IA³ | 学习向量对激活值做缩放 | 万分之一级 | 参数极致小,但适用场景窄、调参敏感 |
| BitFit | 只训所有 bias 项 | 极小 | 极限轻量,效果一般,仅极端资源受限时用 |
微调的两个维度¶
维度一:训什么目标(训练范式)¶
- 继续预训练:无标注领域文本,补领域词汇和语感。
- 使用海量互联网数据(数百GB甚至TB级别)
- 学习语言的通用模式、世界知识、推理能力
- 形成“基础智力”,但缺乏专业深度
- 成本极高:GPT-3训练成本约460万美元
- SFT(监督微调):用「指令 → 理想回答」配对数据训练,教模型遵循指令、按格式输出、按规则决策。该方法最基础、最常用。
- 使用少量专业数据(通常只需几百到几千条样本)
- 在预训练模型基础上调整参数
- 让模型掌握特定领域的知识和技能
- 成本极低:通常是预训练的1%甚至更少
- DPO / RLHF(偏好对齐):偏好数据,教"哪种回答更好",打磨语气、安全性、拒答边界。
维度二:怎么更新参数(手段)¶
全量微调/ LoRA / QLoRA / Adapter / Prefix Tuning……
总结:需要注意,LoRA是实现SFT的参数高效微调(PEFT)技术。也就是说进行监督微调的具体手段是可以选择的:全量微调/高效参数微调。
DPO同理SFT,但是更倾向于高效参数微调,DPO 是精细校准——学习率只有 SFT 的 1/20(5e-6 vs 1e-4),目的是在 SFT 已经"会做"的基础上微调偏好倾向。这种小幅度调整,LoRA 那 0.1%~1% 的参数容量完全够用,上全参数反而容易过拟合。
真实项目中的组合:LoRA-SFT+DPO是当前中小规模项目的标配。
实例¶
在电商问答Agent项目中,用户可能上传商品图片并询问价格、尺寸等信息。系统需要根据用户输入,选择直接回答、商品库检索、网络搜索或图像裁剪。
本文以 Qwen3-VL-8B-Instruct 为基础模型,使用 LLaMA-Factory 完成两阶段训练:
LoRA-SFT 学习工具调用规范 → LoRA-DPO 优化工具选择偏好。
其中,SFT 和 DPO 决定“怎样学习”,LoRA 决定“训练哪些参数”。
用户输入(图 + 文本)
↓
意图识别 / 工具路由 ← 我们要微调的就是这一步,Qwen3-VL-8B
↓
Answer_directly / RAG_Search / Web_Search / Image_Crop
↓
RAG 检索(SigLIP2 召回 + BGE-reranker 重排)/ Web 检索 / Grounding DINO 裁剪
↓
动态多轮判断(最多 3 轮)
↓
答案生成
LoRA 的作用¶
LoRA 冻结模型原有权重 \(W_0\),只训练新增的低秩矩阵 \(A\) 和 \(B\):
这里,\(r\) 是低秩矩阵的秩,\(\alpha\) 控制更新的缩放比例。这样可以减少需要训练的参数以及相应的梯度、优化器状态开销。
| 阶段 | 数据形式 | 学习目标 | 更新参数 |
|---|---|---|---|
| SFT | 输入+标准输出 | 学会正确调用工具 | SFT LoRA 参数 |
| DPO | 输入+chosen+rejected | 更偏好合理的调用方式 | DPO LoRA 参数 |
LoRA 进行 SFT¶
1. 准备工具调用示范¶
例如,用户上传商品图片,询问“这件衬衫多少钱”,标准行为应该是查询商品库。
采用 LLaMA-Factory 的 ShareGPT 数据格式:
[
{
"conversations": [
{
"from": "human",
"value": "<image>这件衬衫多少钱?"
},
{
"from": "gpt",
"value": "{\"tool\":\"RAG_Search\",\"arguments\":{\"category\":\"衬衫\",\"query\":\"查询图片中衬衫的价格\"}}"
}
],
"images": ["images/shirt_001.jpg"]
}
]
将数据保存为 data/ecommerce_sft.json,图片放在 data/images/shirt_001.jpg。
这里训练的是**输出结构化调用请求**,实际查询由外部程序执行。商品价格由数据库提供,不需要写入模型参数。
接着,在 data/dataset_info.json 中添加数据集定义,保留文件中已有的其他条目:
"ecommerce_sft": {
"file_name": "ecommerce_sft.json",
"formatting": "sharegpt",
"columns": {
"messages": "conversations",
"images": "images"
}
}
多模态样本中,<image> 的数量需要与 images 列表中的图片数量一致,例如,如果用户上传两张商品图进行对比,则写成:
{
"conversations": [
{
"from": "human",
"value": "第一件:<image>\n第二件:<image>\n这两件衣服有什么区别?"
},
{
"from": "gpt",
"value": "这里填写标准输出"
}
],
"images": [
"images/shirt_001.jpg",
"images/shirt_002.jpg"
]
}
2. 创建 sft.yaml¶
# ==================== 基础模型 ====================
# 基础模型的 Hugging Face 名称,也可以填写本地模型目录
model_name_or_path: Qwen/Qwen3-VL-8B-Instruct
# 对话模板:把用户输入和模型回答转换成模型需要的格式
# 使用 Qwen3-VL 的非思考模板,与本文不输出长推理过程的数据对应
template: qwen3_vl_nothink
# 允许加载模型仓库中的自定义代码;应确保模型来源可信
trust_remote_code: true
# 图像预处理的最大像素数,即宽 × 高的上限
# 262144 = 512 × 512,但不代表所有图片都会变成正方形
# 调小通常可以减少显存开销,但可能损失商品细节
image_max_pixels: 262144
# ==================== 训练方式 ====================
# 训练阶段:SFT,即使用“输入 + 标准输出”进行监督微调
stage: sft
# 执行训练
do_train: true
# 使用 LoRA 微调:冻结原有权重,主要训练新增的低秩参数
finetuning_type: lora
# ==================== LoRA 参数 ====================
# 在框架识别出的、允许训练的目标线性层中添加 LoRA
# 不表示全参数微调;仍受下方视觉模块冻结配置约束
lora_target: all
# LoRA 的秩 r,决定低秩矩阵的大小
# 秩越大,可训练参数越多,但不保证效果一定更好
lora_rank: 8
# LoRA 更新的缩放参数 alpha
# 标准 LoRA 的缩放系数为 alpha / r,本例为 16 / 8 = 2
lora_alpha: 16
# LoRA 分支的 dropout 概率:训练时随机丢弃 5% 的输入元素
# 用于缓解过拟合,推理时关闭
lora_dropout: 0.05
# 冻结视觉编码部分,不更新其参数
# 模型仍然会读取和理解图片
freeze_vision_tower: true
# 冻结视觉特征与语言模型之间的多模态投影模块
# 结合上一项,本例主要训练语言模型部分的 LoRA
freeze_multi_modal_projector: true
# ==================== 数据配置 ====================
# 数据集目录,相对于运行命令时的工作目录
# 该目录中应包含 dataset_info.json
dataset_dir: data
# 数据集注册名称,必须对应 dataset_info.json 中的键
# 不是直接填写 JSON 文件名
dataset: ecommerce_sft
# 训练序列的长度上限,单位为 token,不是汉字数
# 包括模板、提示词、回答,以及多模态处理产生的相关 token
# 太短可能截断训练内容,需要检查较长图文样本
cutoff_len: 2048
# ==================== 训练超参数 ====================
# 每张 GPU 每次前向/反向计算处理的样本数
per_device_train_batch_size: 1
# 累积 8 个微批次的梯度,再进行一次参数更新
# 普通数据并行下:
# 有效 batch size = 每卡 batch size × 累积步数 × GPU 数量
# 本例单卡为 1 × 8 × 1 = 8
gradient_accumulation_steps: 8
# 学习率:1.0e-4 = 0.0001
# 控制 LoRA 参数每次更新的幅度,是需要通过验证集调整的参数
learning_rate: 1.0e-4
# 训练轮数:完整遍历训练集 3 次
num_train_epochs: 3.0
# 启用 BF16 混合精度训练,需要硬件支持
# 不代表所有参数和计算都会统一变成 BF16
bf16: true
# ==================== 输出与保存 ====================
# 训练输出目录,主要保存 LoRA adapter、检查点和训练记录
# LoRA adapter 本身不是完整的基础模型
output_dir: saves/ecommerce/sft_lora
# 在按步记录日志的策略下,每 10 个参数更新步记录一次日志
# 这里不是每处理 10 条样本
logging_steps: 10
# 在按步保存的策略下,每 100 个参数更新步保存一次检查点
# 配合梯度累积,单卡一个更新步通常对应处理 8 条样本
save_steps: 100
这份配置的含义是:冻结视觉模块和基础模型权重,通过 LoRA 让语言模型部分学习如何根据图文输入选择工具、生成参数,并输出规范的调用格式。
安装好支持 Qwen3-VL 的 LLaMA-Factory 后,在仓库根目录执行:
3. 合并 SFT LoRA,作为 DPO 的起点¶
具体流程:
创建 merge_sft.yaml:
model_name_or_path: Qwen/Qwen3-VL-8B-Instruct
adapter_name_or_path: saves/ecommerce/sft_lora
template: qwen3_vl_nothink
finetuning_type: lora
trust_remote_code: true
export_dir: models/ecommerce_sft
export_device: cpu
export_size: 5
执行合并:
合并后,SFT 学到的更新已经进入 models/ecommerce_sft 的模型权重。DPO 阶段将它作为冻结的底座,再训练一个新的 LoRA adapter。
LoRA 进行 DPO¶
1. 准备 DPO 偏好对示范¶
SFT 提供正确示范,DPO 则进一步告诉模型:对于同一个输入,哪一种输出更值得选择。 例如,同样询问商品价格:
- chosen:查询商品库。
- rejected:在没有依据的情况下直接报出价格。
保存为 data/ecommerce_dpo.json:
[
{
"conversations": [
{
"from": "human",
"value": "<image>这件衬衫多少钱?"
}
],
"chosen": {
"from": "gpt",
"value": "{\"tool\":\"RAG_Search\",\"arguments\":{\"category\":\"衬衫\",\"query\":\"查询图片中衬衫的价格\"}}"
},
"rejected": {
"from": "gpt",
"value": "{\"tool\":\"Answer_directly\",\"arguments\":{\"answer\":\"这件衬衫售价199元。\"}}"
},
"images": ["images/shirt_001.jpg"]
}
]
实际项目中,可以让 SFT 模型对训练输入生成多个候选,再通过规则校验、工具执行结果和人工审核构建偏好对。例如:
| 问题场景 | 优选行为 | 较差行为 |
|---|---|---|
| 查询本店商品价格 | 查询商品库 | 无依据报价 |
| 图片主体很小、背景复杂 | 先裁剪主体 | 忽略干扰直接检索 |
| 已获得充分的商品信息 | 根据证据回答 | 重复调用搜索 |
| 商品型号无法确认 | 请求补充信息 | 随意选取相似商品 |
同一个偏好对必须共享相同的问题、图片和历史信息。 不能给 chosen 提供额外证据,否则比较的就不只是输出质量。
在 dataset_info.json 中添加:
"ecommerce_dpo": {
"file_name": "ecommerce_dpo.json",
"formatting": "sharegpt",
"ranking": true,
"columns": {
"messages": "conversations",
"chosen": "chosen",
"rejected": "rejected",
"images": "images"
}
}
2. 创建 dpo.yaml¶
# 使用合并后的 SFT 模型
model_name_or_path: models/ecommerce_sft
template: qwen3_vl_nothink
trust_remote_code: true
image_max_pixels: 262144
stage: dpo
do_train: true
finetuning_type: lora
# 新建 DPO LoRA
lora_target: all
lora_rank: 8
lora_alpha: 16
lora_dropout: 0.05
freeze_vision_tower: true
freeze_multi_modal_projector: true
# DPO 配置
pref_loss: sigmoid
pref_beta: 0.1
pref_ftx: 0.0
dataset_dir: data
dataset: ecommerce_dpo
cutoff_len: 2048
per_device_train_batch_size: 1
gradient_accumulation_steps: 8
learning_rate: 5.0e-6
num_train_epochs: 1.0
bf16: true
output_dir: saves/ecommerce/dpo_lora
logging_steps: 10
save_steps: 100
执行训练:
训练完成后,如何使用模型?¶
最终模型由下面两部分组成:
推理时必须同时加载它们,不能把 DPO adapter 加载到未经 SFT 的原始模型上:
from llamafactory.chat import ChatModel
model = ChatModel({
"model_name_or_path": "models/ecommerce_sft",
"adapter_name_or_path": "saves/ecommerce/dpo_lora",
"finetuning_type": "lora",
"template": "qwen3_vl_nothink",
"infer_backend": "huggingface",
"image_max_pixels": 262144,
"trust_remote_code": True,
})
responses = model.chat(
messages=[
{"role": "user", "content": "<image>这件衬衫多少钱?"}
],
images=["data/images/shirt_001.jpg"],
do_sample=False,
max_new_tokens=256,
)
print(responses[0].response_text)
图文输入通过 messages 和 images 一起传入,这是 LLaMA-Factory ChatModel 提供的接口。
模型输出调用请求后,业务程序解析 JSON,执行 RAG 检索,再把结果交给模型组织回答。若希望模型继续决定“是否搜索、是否停止”,训练集中还需要加入**工具返回结果之后的决策样本。**
如何验证两阶段训练是否有效?¶
在按商品或图片分组隔离的测试集上,对比三种模型:
| 模型 | 主要观察内容 |
|---|---|
| 原始 Qwen3-VL-8B-Instruct | 基础工具选择能力 |
| SFT 模型 | 调用格式、工具名称和参数是否更准确 |
| SFT + DPO 模型 | 错误调用、无依据回答和重复搜索是否减少 |
重点统计工具选择准确率、参数正确率、JSON 合法率和端到端回答正确率。DPO 的训练损失下降,并不自动代表业务效果提升。
在这套流程中,SFT 用正确示范训练 LoRA,DPO 用偏好对训练新的 LoRA。最终目标是让电商 Agent 更可靠地决定何时查询商品库、何时搜索网络,以及何时已有足够证据回答用户。