核心摘要
微调会改变模型参数,或增加可训练适配器,使基础模型在目标分布上的行为发生变化。它适合稳定的行为、输出结构、工具调用约定或领域任务模式;不适合替代频繁更新的知识库,也不会自动让私有数据安全、事实正确、获得授权或可删除。
应先建立提示词和检索基线。只有当可测量的失败持续存在、训练数据具备授权且有代表性,并且团队能够运行能力、安全、隐私和回归评测时,才进入微调。所有显存、质量、成本和基准数字都应绑定模型修订、分词器、序列长度、优化器、硬件和日期,作为实验结果记录。
微调改变了什么
监督微调(SFT)在选定目标 token 上最小化损失。全量微调更新大部分或全部基础模型权重;参数高效微调(PEFT)冻结基础模型并训练 LoRA 等适配器。DPO 等偏好优化使用偏好数据和不同目标,不能与 SFT 混为一谈。
微调可能改善:
- 稳定的回答格式或风格;
- 分类、抽取、路由或工具调用模式;
- 明确领域分布上的任务表现;
- 数据和评测充分时,小模型的特定任务能力。
它不能可靠提供:
- 当前数据库中的事实;
- 授权或租户隔离;
- 某条记忆样本的删除;
- 对提示注入或不安全输出的免疫;
- 模型必然引用或遵循每条训练样本的保证。
选择干预方式
| 失败或要求 | 首先尝试 | 原因 |
|---|---|---|
| 当前事实或长文档 | 带引用的检索 | 知识可更新且可做访问控制 |
| 稳定输出 Schema 或风格 | 提示词、约束解码,再考虑 SFT | 先测量是否值得写入权重 |
| 重复分类/抽取 | 小型监督模型或 SFT | 有界标签空间更容易评测 |
| 偏好或答案排序 | 先有强 SFT 基线,再做偏好优化 | 目标不同于模仿 |
| 延迟或成本 | 小模型、缓存、批处理、量化推理 | 微调不一定解决服务成本 |
| 授权或安全策略 | 运行时策略和沙箱控制 | 模型权重不是安全边界 |
RAG 与微调可以共存:训练可以教会格式和使用检索的行为,检索则提供当前且经过权限过滤的证据。检索内容仍是不可信数据,不是指令。
全量微调、LoRA 与 QLoRA
全量微调
更新全部权重需要同时考虑权重、梯度、优化器状态、激活、临时缓冲和检查点。单独说“7B 模型需要多少 GB”是不完整的,还必须说明精度、优化器、序列长度、微批量、激活检查点、并行方式和框架。应估算并测量完整训练任务,而不是复制固定数字。
LoRA
LoRA 为选定模块加入低秩更新:
W' = W + scale × B × A
秩、目标模块、缩放、dropout 和初始化都是设计选择。可训练参数比例和质量变化依赖架构与配置,“0.1–1%”不是普遍保证。适配器便于版本管理和切换,但多个适配器可能冲突,合并后也会改变数值行为。
QLoRA
QLoRA 将适配器与量化的冻结基础权重结合。NF4、双重量化、分页优化器、计算 dtype、内核和硬件都会影响结果。量化常能降低部分配置的显存,但不代表固定质量或固定显存;必须用相同基线和任务切片评估量化模型。
数据集工程
来源与授权
每条记录都应保留来源、许可证或许可、采集日期、变换步骤、标注/复核者和删除血缘。移除密钥、不必要的个人数据、凭据及未经授权使用的数据。来源撤回后,模型检查点或适配器并不自动容易清除。
先切分,再变换
在改写、切块、增强或对话模板化之前,先按样本身份创建训练、验证和测试集。跨集合去重近似重复样本,并保留用于检测基准污染的集合。不要在最终测试集上调超参数。
格式与质量
使用基础模型文档规定的聊天模板,保留角色边界。校验 JSONL 记录、分词长度、空轮次、语言、标签、工具参数和拒答样本。审查普通与对抗案例。不要把“100–10,000 条”当目标;样本效率取决于任务熵、基础模型能力、标签噪声和覆盖范围。
import json
from pathlib import Path
def read_instruction_jsonl(path: str):
for line_number, line in enumerate(Path(path).read_text(encoding="utf-8").splitlines(), 1):
if not line.strip():
continue
record = json.loads(line)
if not isinstance(record.get("instruction"), str):
raise ValueError(f"line {line_number}: instruction must be text")
if not isinstance(record.get("output"), str) or not record["output"].strip():
raise ValueError(f"line {line_number}: output must be non-empty text")
yield {
"instruction": record["instruction"].strip(),
"input": str(record.get("input", "")).strip(),
"output": record["output"].strip(),
}
这段代码只校验形状,不校验事实、授权、隐私或任务正确性。
可复现的训练运行
框架 API 和模型许可证会变化。应固定基础模型修订、分词器修订、Python/CUDA/框架版本、数据集哈希、聊天模板、随机种子、硬件、精度和训练配置,并写入实验清单。
下面是说明性的 SFT 骨架,刻意不宣称通用模型 ID、目标模块、序列长度或学习率:
# 说明性骨架:生产使用前必须固定并测试准确的包 API。
from dataclasses import dataclass
@dataclass(frozen=True)
class RunConfig:
base_revision: str
dataset_revision: str
seed: int
max_sequence_length: int
learning_rate: float
effective_batch_size: int
def build_training_record(sample, tokenizer):
messages = [
{"role": "user", "content": sample["instruction"] + "\n" + sample["input"]},
{"role": "assistant", "content": sample["output"]},
]
return tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=False,
)
# 选择与固定版本匹配的 SFTTrainer/SFTConfig 或等价 API。
# 使用训练/验证切分、随机种子、检查点策略、梯度累积和合适的标签掩码。
实际运行必须确认聊天模板、padding 方向、EOS 行为、标签掩码、截断策略、量化计算 dtype、目标模块与基础模型一致。trust_remote_code=True 会执行仓库代码,不应作为默认值,必须使用审查过且固定版本的来源。
应有意识地使用梯度累积:
有效 batch = 设备数 × 每设备微批量 × 累积步数
它会改变优化动态,并不等价于拥有无限显存。应监控每秒 token、峰值显存、损失、验证损失、梯度异常、检查点完整性和中断恢复。
部署前评测
在同一冻结测试集上比较未修改的基础模型、仅提示词基线、相关的 RAG 基线和每个微调候选。至少包括:
- 与任务匹配的准确率或 exact match;
- 结构化输出有效性和 Schema 合规;
- 使用检索时的引用/证据正确性;
- 校准或拒答行为;
- 安全、隐私、越狱、提示注入和工具使用切片;
- 多语言、长上下文、罕见案例和分布外切片;
- 延迟、吞吐、显存、成本和失败率。
BLEU 和 ROUGE 可用于窄领域生成任务,但不是通用事实性、帮助性或安全指标。适当时使用多参考答案或任务评分量表、盲审、评审者一致性和不确定性区间。报告模型修订、提示词、数据、基线、指标定义、硬件、精度、采样策略、日期和样本数。
def exact_match_rate(predictions, references):
if len(predictions) != len(references) or not references:
raise ValueError("predictions and references must have equal non-zero length")
normalized = lambda value: " ".join(value.split()).casefold()
return sum(
normalized(prediction) == normalized(reference)
for prediction, reference in zip(predictions, references)
) / len(references)
不要复用训练样本评测,不要只报告挑选出来的 prompt,也不要仅凭损失推断事实性。验证损失下降可能同时伴随指令遵循、安全性或记忆风险变差。
超参数与消融
学习率、秩、alpha、dropout、轮数、packing、序列长度、批量、warmup、优化器和量化是相互作用的变量。应先做小规模、可记录的搜索,一次改变一个有意义因素,并保存精确配置与基线比较。
有价值的消融包括:
- 基础模型与适配器;
- 不同数据子集和去重策略;
- 目标模块与秩;
- 全精度与量化加载;
- 仅提示词、RAG 与 SFT;
- 去除可能污染或合成记录前后的差异。
不要在模型、数据、目标和评测协议不一致时照搬论文学习率或硬件结果。论文结果只证明其设置下的证据,不是通用配方。
部署与治理
将基础模型、适配器、分词器、聊天模板、提示词、安全策略、依赖锁文件和实验清单一起版本化。加载前验证模型与适配器兼容性,扫描产物和日志中的密钥与个人数据。
服务运行时负责:
- 认证、租户/对象授权和工具允许列表;
- 输入/输出 Schema 校验与内容控制;
- 限流、预算、超时、取消和审计日志;
- 检索权限与删除传播;
- 回滚和流量影子测试。
适配器不是使用训练数据的许可证,私有检查点也不是数据不可访问的证明。训练前应定义保留、删除、访问、导出和事故流程。
常见失败模式
| 失败 | 原因 | 更好的控制 |
|---|---|---|
| 模型记住密钥或基准答案 | 泄漏、重复或污染切分 | 来源、去重、canary、成员推断/隐私检查 |
| 损失下降但任务质量变差 | 模板、标签、分布或指标错误 | 冻结基线和任务切片 |
| 把“私有训练”当作安全保证 | 检查点、日志、托管服务和产物可能泄露 | 威胁模型、访问策略、加密、留存和审查 |
| QLoRA 显存无法复现 | 量化器、序列长度、内核或优化器不同 | 记录完整环境并测量峰值显存 |
| 模型会模仿风格但编造事实 | 风格学习不是事实 grounding | 检索、引用、拒答和事实性评测 |
| 适配器只适配一个基础修订 | 产物未固定或不兼容 | 固定并验证模型/分词器/适配器修订 |
常见问题
微调需要多少数据?
没有通用数量。应从有代表性且有授权的小规模试验和未触碰测试集开始,用学习曲线、错误切片、标注质量和基线差距判断,而不是套用 100/1,000/10,000 规则。
LoRA 的秩控制什么?
秩控制低秩更新的容量,不保证质量。更高的秩可能增加容量、可训练参数和显存,也可能增加过拟合风险。应在受控消融中比较不同秩。
微调能加入可靠的领域知识吗?
它可以改善训练分布上的行为,但不是可查询、持续更新的知识存储。变化中的事实应使用经过权限过滤的检索,并评测事实性,不要假定记忆必然正确。
微调能删除私有数据吗?
不能可靠保证。数据集删除并不证明检查点、适配器、优化器状态、缓存、日志或衍生产物已经删除。训练前就要规划数据血缘和删除测试。
微调与 RAG 可以结合吗?
通常可以。微调教会稳定格式或检索行为,RAG 提供当前证据;检索授权和提示注入防护必须留在运行时。
如何评估微调效果?
使用冻结任务测试、基线比较、安全/隐私和分布外切片、结构化输出检查、人工复核以及成本/延迟测量。公布协议和不确定性,不要只展示一个好看的分数。
一手来源
- Hu 等:LoRA
- Dettmers 等:QLoRA
- Rafailov 等:Direct Preference Optimization
- Hugging Face PEFT 文档
- Hugging Face TRL 文档
- NIST AI Risk Management Framework
- OWASP:大语言模型应用十大风险
总结
微调是实验性干预,不是普遍升级。应先建立基线,使用来源清晰的数据,固定完整环境,测量真实资源路径,并在未触碰切片上评估质量、安全、隐私、泛化、成本和不确定性。当 LoRA 或 QLoRA 的运营取舍适合实验时再采用,并把授权和治理留在模型权重之外。