核心摘要

微调会改变模型参数,或增加可训练适配器,使基础模型在目标分布上的行为发生变化。它适合稳定的行为、输出结构、工具调用约定或领域任务模式;不适合替代频繁更新的知识库,也不会自动让私有数据安全、事实正确、获得授权或可删除。

应先建立提示词和检索基线。只有当可测量的失败持续存在、训练数据具备授权且有代表性,并且团队能够运行能力、安全、隐私和回归评测时,才进入微调。所有显存、质量、成本和基准数字都应绑定模型修订、分词器、序列长度、优化器、硬件和日期,作为实验结果记录。

微调改变了什么

监督微调(SFT)在选定目标 token 上最小化损失。全量微调更新大部分或全部基础模型权重;参数高效微调(PEFT)冻结基础模型并训练 LoRA 等适配器。DPO 等偏好优化使用偏好数据和不同目标,不能与 SFT 混为一谈。

微调可能改善:

  • 稳定的回答格式或风格;
  • 分类、抽取、路由或工具调用模式;
  • 明确领域分布上的任务表现;
  • 数据和评测充分时,小模型的特定任务能力。

它不能可靠提供:

  • 当前数据库中的事实;
  • 授权或租户隔离;
  • 某条记忆样本的删除;
  • 对提示注入或不安全输出的免疫;
  • 模型必然引用或遵循每条训练样本的保证。

选择干预方式

失败或要求 首先尝试 原因
当前事实或长文档 带引用的检索 知识可更新且可做访问控制
稳定输出 Schema 或风格 提示词、约束解码,再考虑 SFT 先测量是否值得写入权重
重复分类/抽取 小型监督模型或 SFT 有界标签空间更容易评测
偏好或答案排序 先有强 SFT 基线,再做偏好优化 目标不同于模仿
延迟或成本 小模型、缓存、批处理、量化推理 微调不一定解决服务成本
授权或安全策略 运行时策略和沙箱控制 模型权重不是安全边界

RAG 与微调可以共存:训练可以教会格式和使用检索的行为,检索则提供当前且经过权限过滤的证据。检索内容仍是不可信数据,不是指令。

全量微调、LoRA 与 QLoRA

全量微调

更新全部权重需要同时考虑权重、梯度、优化器状态、激活、临时缓冲和检查点。单独说“7B 模型需要多少 GB”是不完整的,还必须说明精度、优化器、序列长度、微批量、激活检查点、并行方式和框架。应估算并测量完整训练任务,而不是复制固定数字。

LoRA

LoRA 为选定模块加入低秩更新:

text
W' = W + scale × B × A

秩、目标模块、缩放、dropout 和初始化都是设计选择。可训练参数比例和质量变化依赖架构与配置,“0.1–1%”不是普遍保证。适配器便于版本管理和切换,但多个适配器可能冲突,合并后也会改变数值行为。

QLoRA

QLoRA 将适配器与量化的冻结基础权重结合。NF4、双重量化、分页优化器、计算 dtype、内核和硬件都会影响结果。量化常能降低部分配置的显存,但不代表固定质量或固定显存;必须用相同基线和任务切片评估量化模型。

数据集工程

来源与授权

每条记录都应保留来源、许可证或许可、采集日期、变换步骤、标注/复核者和删除血缘。移除密钥、不必要的个人数据、凭据及未经授权使用的数据。来源撤回后,模型检查点或适配器并不自动容易清除。

先切分,再变换

在改写、切块、增强或对话模板化之前,先按样本身份创建训练、验证和测试集。跨集合去重近似重复样本,并保留用于检测基准污染的集合。不要在最终测试集上调超参数。

格式与质量

使用基础模型文档规定的聊天模板,保留角色边界。校验 JSONL 记录、分词长度、空轮次、语言、标签、工具参数和拒答样本。审查普通与对抗案例。不要把“100–10,000 条”当目标;样本效率取决于任务熵、基础模型能力、标签噪声和覆盖范围。

python
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、目标模块、序列长度或学习率:

python
# 说明性骨架:生产使用前必须固定并测试准确的包 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 会执行仓库代码,不应作为默认值,必须使用审查过且固定版本的来源。

应有意识地使用梯度累积:

text
有效 batch = 设备数 × 每设备微批量 × 累积步数

它会改变优化动态,并不等价于拥有无限显存。应监控每秒 token、峰值显存、损失、验证损失、梯度异常、检查点完整性和中断恢复。

部署前评测

在同一冻结测试集上比较未修改的基础模型、仅提示词基线、相关的 RAG 基线和每个微调候选。至少包括:

  • 与任务匹配的准确率或 exact match;
  • 结构化输出有效性和 Schema 合规;
  • 使用检索时的引用/证据正确性;
  • 校准或拒答行为;
  • 安全、隐私、越狱、提示注入和工具使用切片;
  • 多语言、长上下文、罕见案例和分布外切片;
  • 延迟、吞吐、显存、成本和失败率。

BLEU 和 ROUGE 可用于窄领域生成任务,但不是通用事实性、帮助性或安全指标。适当时使用多参考答案或任务评分量表、盲审、评审者一致性和不确定性区间。报告模型修订、提示词、数据、基线、指标定义、硬件、精度、采样策略、日期和样本数。

python
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 提供当前证据;检索授权和提示注入防护必须留在运行时。

如何评估微调效果?

使用冻结任务测试、基线比较、安全/隐私和分布外切片、结构化输出检查、人工复核以及成本/延迟测量。公布协议和不确定性,不要只展示一个好看的分数。

一手来源

总结

微调是实验性干预,不是普遍升级。应先建立基线,使用来源清晰的数据,固定完整环境,测量真实资源路径,并在未触碰切片上评估质量、安全、隐私、泛化、成本和不确定性。当 LoRA 或 QLoRA 的运营取舍适合实验时再采用,并把授权和治理留在模型权重之外。