跳转至

大模型微调(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\)

\[ W = W_0 + \frac{\alpha}{r}BA \]

这里,\(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 后,在仓库根目录执行:

llamafactory-cli train sft.yaml

3. 合并 SFT LoRA,作为 DPO 的起点

具体流程:

原始模型 + SFT LoRA
         ↓ 合并
      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

执行合并:

llamafactory-cli export merge_sft.yaml

合并后,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

执行训练:

llamafactory-cli train dpo.yaml

训练完成后,如何使用模型?

最终模型由下面两部分组成:

models/ecommerce_sft
        +
saves/ecommerce/dpo_lora

推理时必须同时加载它们,不能把 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)

图文输入通过 messagesimages 一起传入,这是 LLaMA-Factory ChatModel 提供的接口。

模型输出调用请求后,业务程序解析 JSON,执行 RAG 检索,再把结果交给模型组织回答。若希望模型继续决定“是否搜索、是否停止”,训练集中还需要加入**工具返回结果之后的决策样本。**

如何验证两阶段训练是否有效?

在按商品或图片分组隔离的测试集上,对比三种模型:

模型 主要观察内容
原始 Qwen3-VL-8B-Instruct 基础工具选择能力
SFT 模型 调用格式、工具名称和参数是否更准确
SFT + DPO 模型 错误调用、无依据回答和重复搜索是否减少

重点统计工具选择准确率、参数正确率、JSON 合法率和端到端回答正确率。DPO 的训练损失下降,并不自动代表业务效果提升。

在这套流程中,SFT 用正确示范训练 LoRA,DPO 用偏好对训练新的 LoRA。最终目标是让电商 Agent 更可靠地决定何时查询商品库、何时搜索网络,以及何时已有足够证据回答用户。

参考

LoRA和QLoRA

大模型微调实战指南:从理论到高效落地的核心路径