训练目标与对齐机制
很多测试现象表面像"模型能力忽高忽低",本质上却是训练目标在塑造行为边界。预训练决定模型知道什么、SFT 决定它像不像助手、偏好对齐决定它更愿意怎样回答、安全对齐决定它敢不敢回答。测试人员如果不理解这条链,就很难解释版本切换后为什么拒答率、风格和稳定性一起变了。
教学导读
**定位:**这一章不是教你训练模型,而是教你看懂"模型为什么更像助手、为什么更爱拒答、为什么升级后风格变了"。同时,我们会深入数学推导和源码层面,让你不仅知其然、更知其所以然。 **前置依赖:**建议已经具备 Transformer 和基础概率直觉,了解梯度下降的基本原理。 **适用场景:**模型升级回归、helpfulness 与 safety tradeoff 分析、边界问题评测、供应商模型对比、对齐策略选型。 **学完产出:**你应该能把一个版本回归拆成预训练能力、SFT 行为、偏好对齐和安全对齐几层来解释,并能阅读 TRL 源码理解训练配置对输出行为的影响。
先立一个判断。
同一个基础模型,在不同训练链路下会长成完全不同的"产品人格"。所以测试不是只比 benchmark 分数,而要理解这次上线到底换了哪一层能力:是知识更多了,还是回答更守规矩了,还是更爱拒答了,还是更追求简洁而牺牲了完整性。理解训练目标,本质上就是理解"模型的行为由什么驱动"——这是所有测试设计的起点。
为了帮助你建立完整的认知框架,我们先从宏观角度俯瞰整条训练链路。一个大语言模型从诞生到上线,通常要经历四个阶段,每个阶段的训练目标不同,对模型行为的塑造方式也截然不同。
预训练 → SFT 微调 → 偏好对齐 → 安全对齐
预训练阶段决定了模型的"知识容量"和"语言能力上限"——它就像一个人的基础教育,决定了这个人知道多少、理解能力有多强。SFT 阶段相当于"职业培训"——教模型如何以助手的角色来应用它的知识。偏好对齐阶段相当于"行为规范训练"——教模型哪种回答风格更受用户欢迎。安全对齐阶段相当于"法律和伦理教育"——教模型哪些事情不能做、哪些边界不能越。这四个阶段不是简单叠加的关系。后一个阶段会修改前一个阶段塑造的行为模式,有时候甚至会以牺牲前一阶段的某些能力为代价。这就是为什么模型升级后可能出现"某些方面变好但某些方面变差"的现象。理解这一点,是理解所有模型回归现象的钥匙。 接下来我们将从最底层的预训练目标开始,逐层拆解每个阶段的数学原理、工程实践和对测试的影响。
01. 预训练:模型最底层的目标其实很朴素
大语言模型预训练的核心目标可以极简地理解成:给定前文,预测下一个 token 的概率分布。它不直接学习"真理",也不直接学习"安全",而是在海量语料里学习语言结构、知识统计、模式延续和上下文条件分布。 L = -log P(next_token | previous_tokens) 这个简单的公式背后蕴含着几层深意。首先,模型的优化目标是最大化正确 token 的对数概率,等价于最小化负对数似然。这里取负号是因为概率值在零到一之间,对数为负值,取负后变为正值,便于作为最小化的目标。其次,这个目标是自监督的——不需要人工标注,每一段文本天然就包含"正确答案"(即真实出现的下一个 token)。这意味着互联网上所有的文本都可以作为训练数据,数据获取成本极低,这也是大模型能够扩展到万亿 token 训练集的根本原因。第三,这个目标本质上是让模型学习整个语言的联合概率分布 P(x₁, x₂, ..., xₙ),通过链式法则分解为自回归条件概率的乘积。 一个经常被忽视但对测试至关重要的观察是:这个训练目标对所有 token 一视同仁。模型并不知道哪些 token 对应着关键事实(比如药品剂量、法律条款编号),哪些 token 只是语法填充(比如"的""了""和")。它用同一个损失函数对待所有 token。这就解释了为什么模型可以把语法说得非常流利,但在关键事实上却容易出错——因为语法模式在训练数据中出现的频率远高于特定的事实。对于测试人员来说,这意味着你需要特别关注那些"事实密集"的场景,因为模型在这些场景下的出错概率天然更高。
1.1 自回归分解与链式法则
设一段文本包含 T 个 token:x₁, x₂, ..., x_T。模型要学习的联合概率通过链式法则分解: P(x₁, x₂, ..., x_T) = P(x₁) · P(x₂|x₁) · P(x₃|x₁,x₂) · ... · P(x_T|x₁,...,x_{T-1})
= ∏ᵢ₌₁ᵀ P(xᵢ | x₁, ..., x_{i-1}) 取负对数得到训练的总损失: L_total = -∑ᵢ₌₁ᵀ log P_θ(xᵢ | x₁, ..., x_{i-1}) 其中 θ 是模型参数。训练就是通过梯度下降最小化这个损失。每一步,模型都在尝试让自己的条件概率分布更接近训练数据中真实出现的分布。值得注意的是,这个损失函数在实际训练中会对一整个序列的所有位置同时计算梯度——这就是自回归训练的效率所在。一条包含两千个 token 的文本,可以同时产生一千九百九十九个训练信号(每个位置预测下一个 token),不像掩码语言模型那样只有百分之十五的 token 作为训练信号。正是这种高信号密度,使得因果语言模型在大规模训练中胜出。
1.2 这个目标会带来什么天然特性
- **流畅性优先于正确性:**模型很擅长生成"像人话"的内容,因为它学的是语言分布。但"流畅"和"正确"是两个维度——模型可以极其流畅地说出完全错误的事实。
- **频率偏置:**模型会偏向训练分布里的高频模式。如果某个知识点在语料中出现 1000 次,另一个只出现 3 次,模型对前者的掌握度会远高于后者。这意味着冷门场景天然更脆弱。
- **不是事实检索器:**模型没有一个"知识库"供它查找事实,它的"知识"分散编码在数十亿参数的权重矩阵中。这决定了它会产生幻觉(hallucination)。
- **不天然像助手:**预训练模型学的是"互联网上的文本续写",所以它可能续写出维基百科风格、Reddit 风格、代码风格的文本,但不一定"像助手那样配合你"。
- **上下文依赖性极强:**模型的输出高度依赖输入的上下文窗口。相同的问题,不同的前文,可能导致完全不同的回答分布。
测试启发:预训练特性决定了基线缺陷。
理解这些天然特性后,你就不会惊讶于以下测试现象:模型在高频问题上表现稳定但在冷门问题上波动大、模型能说出"正确的废话"、模型对 prompt 措辞的微小变化敏感。这些不是 bug,而是预训练目标的直接后果。测试的工作是量化这些特性在业务场景中的影响程度。
02. 交叉熵损失的数学推导
交叉熵是预训练损失函数的核心。要真正理解它,我们需要从信息论的基本概念出发。
2.1 从信息量到熵
信息论的核心直觉是:越不可能发生的事件,包含的信息量越大。数学上,一个概率为 p 的事件的信息量(自信息)定义为: I(x) = -log₂ P(x)
直觉:
- P(x) = 1 (必然事件)→ I(x) = 0 bit,没有信息量
- P(x) = 0.5(抛硬币)→ I(x) = 1 bit
- P(x) = 0.01(罕见事件)→ I(x) ≈ 6.64 bits,信息量很大 **熵(Entropy)**是随机变量信息量的期望: H(P) = -∑ₓ P(x) log P(x) = E_P[I(x)] 熵衡量的是一个分布的"不确定性"。均匀分布的熵最大(最不确定),确定性分布的熵为零。对语言来说,如果下一个 token 几乎可以确定(比如"中华人民共和"后面几乎一定是"国"),那这个位置的熵就很低。如果不确定性大(比如句子开头),熵就高。
2.2 交叉熵的推导
假设真实分布为 P(训练数据中 token 的分布),模型预测的分布为 Q(模型输出的 softmax 分布)。交叉熵定义为: H(P, Q) = -∑ₓ P(x) log Q(x) 在语言模型的场景下,每个位置的真实分布 P 是一个 one-hot 分布——正确的下一个 token 概率为 1,其他所有 token 概率为 0。设正确 token 为 x*,则: H(P, Q) = -∑ₓ P(x) log Q(x) = -[1 · log Q(x*) + 0 · log Q(其他) + ...] = -log Q(x*)
即:交叉熵退化为正确 token 的负对数概率 这就是为什么你看到的训练损失公式可以写成 L = -log P(x_t | x_{<t})。它看起来简单,本质上就是交叉熵在 one-hot 标签下的特殊形式。
2.3 交叉熵与 KL 散度的关系
交叉熵可以分解为真实分布的熵加上 KL 散度: H(P, Q) = H(P) + D_KL(P ‖ Q)
其中 D_KL(P ‖ Q) = ∑ₓ P(x) log [P(x) / Q(x)] ≥ 0 由于 H(P) 是数据本身的固有不确定性(我们无法改变),最小化交叉熵等价于最小化 KL 散度,即让模型分布 Q 尽可能接近真实分布 P。当 Q = P 时,KL 散度为零,交叉熵等于熵。这个关系在整个对齐领域反复出现——无论是 RLHF 中的 KL penalty,还是 DPO 中的概率比,本质上都是在操控模型分布和参考分布之间的距离。所以理解 KL 散度不仅仅是理解预训练,更是理解后续所有对齐方法的数学基础。 KL 散度还有一个重要的非对称性:D_KL(P ‖ Q) 和 D_KL(Q ‖ P) 是不同的。前者被称为前向 KL 散度,它惩罚的是模型在真实分布有概率的地方给出低概率的情况——也就是说它倾向于让模型"什么都学一点",宁可过度泛化也不要遗漏。后者被称为反向 KL 散度,它惩罚的是模型在真实分布没有概率的地方给出高概率的情况——也就是说它倾向于让模型"只学有把握的部分",宁可遗漏也不要乱说。大语言模型的预训练使用的是前向 KL 散度(通过最小化交叉熵),这解释了为什么预训练模型倾向于覆盖广泛的话题,但在每个话题上不一定精准。
对测试的含义。
训练损失(perplexity)的下限就是语言本身的熵。人类语言的熵大约是每 token 1.0-1.5 bits(对英文而言)。如果模型的 perplexity 已经接近这个下限,再往下优化空间有限。测试时如果发现两个模型的 perplexity 差距很小(比如 5.2 vs 5.0),不要期望它们在业务表现上有天壤之别。
2.4 Perplexity:交叉熵的直觉化指标
Perplexity(困惑度)是交叉熵的指数形式,提供了一个更直觉的理解: PPL = exp(H(P, Q)) = exp(-1/T ∑ᵢ log P_θ(xᵢ | x_{<i}))
直觉:PPL = 10 意味着模型平均在 10 个候选中犹豫 PPL = 100 意味着模型平均在 100 个候选中犹豫 PPL 越低,模型对训练数据的拟合越好
典型参考值:
GPT-2 (1.5B) 在 WikiText-103 上:PPL ≈ 17.5
GPT-3 (175B) 在 LAMBADA 上: PPL ≈ 3.0
现代 70B 模型在通用文本上: PPL ≈ 5-8
人类预测下一个词的 PPL 估计: PPL ≈ 12-20(人类其实不擅长精确预测)03. CLM vs MLM:两种预训练范式的本质差异
预训练有两种主要范式:因果语言模型(Causal Language Model, CLM)和掩码语言模型(Masked Language Model, MLM)。理解它们的差异对测试至关重要,因为它们决定了模型的生成方式和能力边界。
3.1 因果语言模型(CLM / Autoregressive LM)
代表:GPT 系列、LLaMA、Mistral、Qwen 等所有当前主流生成式大模型。 L_CLM = -∑ᵢ₌₁ᵀ log P_θ(xᵢ | x₁, x₂, ..., x_{i-1})
关键约束:每个 token 只能看到它之前的 token(因果掩码) 实现方式:Transformer decoder 中的 causal attention mask(下三角矩阵) 因果掩码的核心思想是:在自注意力计算中,位置 i 的 token 只能 attend 到位置 1 到 i 的 token,不能看到位置 i+1 及之后的 token。这通过一个下三角矩阵实现:
Causal Mask(4个token的例子):
t₁ t₂ t₃ t₄
t₁ [ 1 0 0 0 ] t₁ 只能看自己
t₂ [ 1 1 0 0 ] t₂ 能看 t₁ 和自己
t₃ [ 1 1 1 0 ] t₃ 能看 t₁, t₂ 和自己
t₄ [ 1 1 1 1 ] t₄ 能看所有
被 mask 的位置(0)在 softmax 前被设为 -∞
这保证了生成时的自回归性质:不能"偷看"未来3.2 掩码语言模型(MLM)
代表:BERT、RoBERTa、ALBERT 等理解类模型。 L_MLM = -∑{i∈M} log P_θ(xᵢ | x{\M})
其中 M 是被随机掩码的 token 位置集合(通常 15%) x_{\M} 是未被掩码的所有 token(双向上下文) MLM 的关键特点是每个被掩码的 token 可以看到它前后所有未被掩码的 token(双向注意力)。这让 MLM 在理解任务(分类、NER、相似度)上很强,但天然不适合文本生成。从直觉上理解:MLM 像是在做完形填空——给你一段话中间挖了几个空,让你根据上下文猜测空里应该填什么;而 CLM 像是在做续写——给你前半句话,让你预测下半句。完形填空当然可以利用更多的上下文信息,但它不能自然地产生长文本。你不可能通过反复做完形填空来写一篇文章,但你可以通过一个词接一个词地写来完成一篇文章——这就是为什么生成式 AI 选择了 CLM 架构。 还有一类混合架构值得一提:前缀语言模型(Prefix LM),比如 T5 和 GLM 采用的架构。它对前缀部分(类比 prompt)使用双向注意力,对生成部分使用因果注意力。这种架构试图兼顾理解和生成,但在实践中并没有成为主流。原因在于规模化效率——当模型参数量达到数百亿甚至数千亿级别时,纯 CLM 架构在训练和推理上的工程优势变得极为明显:统一的因果注意力掩码使得 KV Cache 优化、投机解码、连续批处理等技术的实现更加简洁高效。
3.3 两种范式的本质对比
| 维度 | CLM(GPT 式) | MLM(BERT 式) |
|---|---|---|
| 注意力方向 | 单向(只看过去) | 双向(看前后) |
| 训练信号密度 | 每个 token 都是训练信号 | 只有被 mask 的 15% token 是训练信号 |
| 生成能力 | 天然支持自回归生成 | 不支持流畅的长文本生成 |
| 上下文利用 | 只用左侧上下文 | 同时利用左右上下文 |
| 理解能力 | 需要通过生成间接评估理解 | 直接面向理解任务优化 |
| 当前主流地位 | 主流(ChatGPT/Claude/Gemini 均基于此) | 主要用于嵌入和理解任务 |
为什么 CLM 胜出成为生成式 AI 的基础?
核心原因有三:(1)CLM 的训练信号密度是 100%(每个 token 都贡献梯度),而 MLM 只有 15%,在同等数据量下 CLM 学习效率更高;(2)CLM 天然支持生成,不需要额外的解码策略;(3)scaling law 实验表明,CLM 在参数量扩大时的性能提升曲线更平滑、更可预测。测试人员理解这一点,才能解释为什么当前的对话模型全部基于 CLM 架构。
04. Next-Token Prediction 的信息论解释
Next-Token Prediction 不仅仅是一个工程选择,它有深刻的信息论基础。理解这些基础可以帮助测试人员预判模型在不同场景下的表现特征。
4.1 压缩即理解
Solomonoff 归纳推理和柯尔莫哥洛夫复杂度告诉我们:预测和压缩在数学上是等价的。一个能完美预测下一个 token 的模型,本质上就是一个完美的压缩器。反过来说,一个好的压缩器一定理解了数据中的结构和规律。 如果模型 M 能以概率 P_M(x_t | x_{<t}) 预测每个 token, 则可以用算术编码将整段文本 x₁...x_T 压缩到:
编码长度 = -∑ᵢ log₂ P_M(xᵢ | x_{<i}) bits
这正好等于交叉熵 H(P_data, P_M) × T 这意味着:训练损失越低 → 压缩越好 → 模型对语言结构的"理解"越深。这里"理解"打了引号,因为这种理解是统计性的,不等同于人类的语义理解。但大量实证表明,统计性理解在规模足够大时会涌现出类似推理的能力。
4.2 互信息与上下文依赖
模型在预测某个 token 时利用的上下文信息可以用互信息(Mutual Information)来量化: I(x_t ; x_{<t}) = H(x_t) - H(x_t | x_{<t})
含义: H(x_t) = 不看上下文时对 x_t 的不确定性(边际熵) H(x_t | x_{<t}) = 看了上下文后对 x_t 的剩余不确定性(条件熵) I(x_t ; x_{<t}) = 上下文帮助减少了多少不确定性(互信息) 互信息越大,说明模型在该位置越依赖上下文。这解释了一个重要的测试现象:模型对不同位置的"自信程度"差异巨大。句子开头(低互信息)、专有名词后的常见搭配(高互信息)、长程推理依赖(需要全局上下文)——这些位置的预测难度和出错模式完全不同。 从测试的角度,互信息的概念揭示了一个深层道理:模型不是在所有位置都同等"聪明"的。在高互信息位置(即上下文能强烈约束输出的地方),模型表现得像一个专家——几乎不会出错。但在低互信息位置(即需要依赖世界知识或进行创造性推理的地方),模型表现得像一个不确定的猜测者。测试用例的设计应该有意覆盖这两种位置,特别是要多测试那些"看起来简单但实际上上下文约束很弱"的场景。
4.4 Scaling Law 的信息论视角
Kaplan 等人在二零二零年发现的 Scaling Law 指出,模型的训练损失(交叉熵)与模型参数量、数据量、计算量之间存在幂律关系。从信息论角度理解:更大的模型能够编码更复杂的条件概率分布,从而更精准地逼近语言的真实分布。但这种逼近遵循收益递减的规律——从一百亿参数到一千亿参数带来的 perplexity 下降幅度,远不如从一亿参数到十亿参数的下降幅度。 对测试来说,Scaling Law 的含义是:当你在比较两个不同规模的模型时(比如七十亿参数 vs 七百亿参数),不要期望十倍的参数量带来十倍的能力提升。实际的提升通常是对数级的。但在某些特定任务上,可能出现"涌现能力"——小模型完全做不到的事情,大模型突然能做到了。这种非线性的能力变化使得模型切换时的回归测试必须覆盖足够多的任务类型,而不能只看几个代表性的 benchmark。
4.3 分布外(OOD)检测的信息论视角
当模型遇到训练分布外的输入时,其条件概率分布会变得更均匀(熵升高),perplexity 升高。这提供了一个检测模型"不确定"的信号:
实际测试应用:
1. Token-level 熵监控
- 计算模型在每个位置输出的熵
- 高熵位置 = 模型不确定 = 更可能出错
- 可用于标记需要人工审核的片段
2. Sequence-level perplexity 异常检测
- 某个 prompt 导致异常高的 PPL → 可能触及能力边界
- 可作为自动化测试的"风险预警"指标
3. Calibration 测试
- 模型输出概率 P=0.8 的答案,是否真的有 80% 正确率?
- 如果高概率输出也常出错 → 校准不良 → 信任度指标失效05. SFT:为什么模型突然像个助手了
SFT(Supervised Fine-Tuning)就是用高质量指令-回答样本,让模型从"会续写文本"变成"会理解任务并按角色回复"。从技术上说,SFT 并没有改变模型的架构或训练目标——它仍然是交叉熵损失,仍然是 next-token prediction。改变的是训练数据的分布:从"互联网文本"变成了"指令-回答对"。
预训练模型 → 看懂语言分布 → SFT → 更像助手、更听指令
5.1 SFT 的损失函数细节
SFT 的一个关键工程细节是:通常只对回答部分计算损失,不对 prompt 部分计算损失。 L_SFT = -∑{t∈response} log P_θ(x_t | x)
注意:x_{<t} 包含完整的 prompt + 已生成的 response tokens 但只有 response 部分的 token 会贡献到梯度
这叫做 "loss masking on prompt tokens" 为什么要这么做?因为我们不希望模型学习"生成 prompt"——我们希望它学习的是"给定 prompt,生成好的回答"。如果也对 prompt 部分计算损失,模型会花部分容量去学习生成问题本身,浪费训练效率。这个技术细节对理解 SFT 的工程实现至关重要:在代码层面,需要构造一个 loss mask 张量,标记哪些 token 是 prompt(mask 为零)、哪些是 response(mask 为一),只对 mask 为一的位置计算交叉熵并回传梯度。 这里有一个微妙的工程陷阱值得测试人员了解:如果 loss mask 的构造出了问题——比如 prompt 和 response 的分界位置计算错误——模型可能会学到错误的行为。常见的错误模式包括:把回答的开头几个 token 也 mask 掉了导致模型不知道怎么开头回答;或者把 prompt 的末尾几个 token 也纳入了 loss 计算导致模型学会在回答前先复述问题。如果你在测试中发现模型总是先重复用户的问题再开始回答,或者回答的开头总是缺少必要的过渡,这可能不是模型能力问题,而是 SFT 的 loss mask 配置问题。
5.2 SFT 对测试最直接的影响
| 变化 | 表现 | 测试关注点 |
|---|---|---|
| 任务遵循性增强 | 更按格式回答 | 结构化输出、指令遵循率 |
| 对系统角色更敏感 | System Prompt 影响变大 | 优先级冲突、注入测试 |
| 更像产品助手 | 礼貌、简洁、任务导向 | helpfulness 与完整性平衡 |
| 对话多轮能力提升 | 能维持上下文追踪 | 多轮一致性、记忆衰减 |
| 格式敏感性增强 | 特定模板效果更好 | 模板兼容性、格式鲁棒性 |
06. SFT 的工程实践
6.1 对话模板(Chat Template)
不同模型家族使用不同的对话模板,模板的格式差异直接影响模型的行为质量。如果使用了错误的模板,即使模型本身很强,也会表现失常。
# ChatML 格式(OpenAI 系列 / Qwen)
<|im_start|>system
你是一个有帮助的助手。<|im_end|>
<|im_start|>user
什么是交叉熵?<|im_end|>
<|im_start|>assistant
交叉熵是...<|im_end|>
# Llama-2/3 格式
<s>[INST] <<SYS>>
你是一个有帮助的助手。
<</SYS>>
什么是交叉熵? [/INST] 交叉熵是... </s>
# Mistral 格式
<s>[INST] 什么是交叉熵? [/INST]交叉熵是...</s>测试陷阱:模板不匹配。
如果你用 ChatML 格式发给一个 Llama 模型,或者反过来,模型的指令遵循率可能断崖式下降。测试时一定要确认:推理服务使用的模板是否与模型的 SFT 训练模板一致。这是最常见的"模型换了就变差"的工程原因之一,甚至比模型能力差异本身更常见。
6.2 SFT 数据的质量法则
SFT 的数据质量远比数据量重要。LIMA(Zhou et al., 2023)论文证明仅 1000 条高质量数据就能让 65B 模型达到接近 RLHF 的效果。核心的数据质量维度包括:
多样性
覆盖的任务类型(问答/摘要/编程/推理/创意写作...)要足够广。如果 SFT 数据 90% 是问答,模型的编程能力就不会被有效激活。
一致性
同类问题的回答风格应一致。如果标注员 A 喜欢长回答、标注员 B 喜欢简洁回答,模型会学到混乱的风格分布。
正确性
回答本身必须正确。SFT 数据中的错误会被模型直接学习为"正确模式",且后续对齐很难完全纠正。
难度梯度
应包含从简单到复杂的任务。只有简单问题的 SFT 数据会导致模型在复杂推理时退化为浅层回答。
6.3 SFT 数据构造的实战代码
# SFT 数据集的标准构造方式
from datasets import Dataset
def format_chat_template(example, tokenizer):
"""将原始数据转为对话格式"""
messages = [
{"role": "system", "content": "你是一个有帮助的助手。"},
{"role": "user", "content": example["instruction"]},
{"role": "assistant", "content": example["output"]}
]
text = tokenizer.apply_chat_template(
messages, tokenize=False, add_generation_prompt=False
)
return {"text": text}
raw_data = [
{
"instruction": "解释什么是交叉熵损失",
"output": "交叉熵损失衡量模型预测分布与真实分布之间的差异..."
},
{
"instruction": "用 Python 实现二分查找",
"output": "def binary_search(arr, target):\n left, right = 0, len(arr)-1..."
}
]
dataset = Dataset.from_list(raw_data)
dataset = dataset.map(
lambda x: format_chat_template(x, tokenizer),
remove_columns=dataset.column_names
)07. LoRA/QLoRA 的原理与数学推导
全参数微调一个 70B 模型需要至少 140GB 显存(FP16),这对大多数团队来说不现实。LoRA(Low-Rank Adaptation)通过低秩分解大幅降低了微调的参数量和显存需求。
7.1 LoRA 的核心思想
LoRA 的关键假设是:微调带来的权重变化矩阵 ΔW 是低秩的。也就是说,虽然 W 本身是一个巨大的矩阵(比如 4096×4096),微调只需要在一个很小的子空间内移动就够了。 原始前向传播:h = Wx
LoRA 修改后:h = Wx + ΔWx = Wx + BAx
其中: W ∈ ℝ^{d×d}(原始权重,冻结不更新) A ∈ ℝ^{r×d}(降维矩阵,r ≪ d) B ∈ ℝ^{d×r}(升维矩阵) ΔW = BA ∈ ℝ^{d×d}(低秩近似)
参数量对比: 原始 W:d × d = 4096² = 16,777,216 LoRA(r=16):d×r + r×d = 2 × 4096 × 16 = 131,072 压缩比:128 倍
7.2 为什么低秩假设成立
一个自然的问题是:为什么微调的权重变化 ΔW 是低秩的?直觉解释如下:预训练模型已经学到了丰富的语言表示,SFT 或对齐微调只需要在这个表示空间中做"微调整"——比如让模型稍微偏好某种回答风格,或者稍微抑制某些输出模式。这种"微调整"通常不需要改变整个权重矩阵的结构,只需要在几个关键的方向上做出偏移即可。数学上,Aghajanyan 等人在二零二一年的研究表明,当预训练模型足够大时,微调过程中的权重变化确实集中在一个很低维的子空间内。他们测量了微调后权重变化矩阵的奇异值分布,发现前几十个奇异值就解释了百分之九十以上的变化。这为 LoRA 的低秩假设提供了实证支持。
7.3 LoRA 的初始化策略
LoRA 的训练初始化遵循一个重要原则:训练开始时 ΔW 应该为零,这样模型从预训练权重的精确状态开始微调。具体做法是:A 用高斯随机初始化,B 用零初始化。这样 ΔW = BA = 0。训练中加入一个缩放因子 α/r: h = Wx + (α/r) · BAx
α 通常设为 r 的 1-2 倍(如 r=16, α=32) α/r 控制 LoRA 更新的幅度 较大的 α/r → LoRA 影响更大 → 偏离预训练更多
7.4 应用 LoRA 的目标模块选择
在 Transformer 架构中,LoRA 可以应用在不同的线性层上。选择哪些层添加 LoRA 适配器对最终效果有显著影响。常见的选择策略如下:最基本的配置是只在注意力层的 Query 和 Value 投影上添加 LoRA,这是原始 LoRA 论文的推荐方案。更完整的配置是覆盖注意力层的所有四个投影矩阵(Query、Key、Value、Output),以及前馈网络层的投影矩阵。实验表明,覆盖更多层通常能带来更好的效果,但同时也增加了可训练参数量和训练时间。对于 SFT 任务,通常建议覆盖所有线性层以获得最佳效果。对于对齐微调(DPO 等),由于对模型的修改幅度更小,可以使用较少的目标模块和更低的 rank。
7.5 QLoRA:4-bit 量化 + LoRA
QLoRA 在 LoRA 的基础上更进一步:将冻结的基础模型权重量化到 4-bit(NF4 格式),只有 LoRA 适配器保持 BF16 精度。这使得在单张 24GB 显卡上微调 65B 模型成为可能。
QLoRA 的三个关键技术:
1. NF4 (NormalFloat 4-bit)
- 假设权重近似正态分布
- 用 4-bit 表示 16 个分位点
- 比均匀 INT4 量化精度更高
2. Double Quantization
- 量化常数本身也被量化
- 每 64 个参数共享一个 FP32 量化常数 → 再将该常数量化为 FP8
- 节省约 0.37 bit/parameter
3. Paged Optimizers
- 利用 CPU 内存做 GPU 内存的分页交换
- 处理训练中的内存峰值
显存对比(65B 模型微调):
全参数 FP16:>780GB
LoRA FP16: ~160GB
QLoRA: ~33GB(单张 A100 40GB 可行)LoRA 对测试的影响。
LoRA 微调的模型和全参数微调的模型在行为上可能存在差异:(1)低秩约束限制了模型的可塑性,某些复杂行为模式可能学不到;(2)LoRA 的 rank 选择直接影响效果,rank 太低会欠拟合、太高会失去正则化效果;(3)QLoRA 的量化误差可能在某些边界场景放大,导致输出微妙偏移。测试时如果发现微调模型在大部分场景正常但在特定场景异常,值得检查是否是 LoRA rank 不足导致的欠拟合。
08. RLHF 完整 Pipeline
RLHF(Reinforcement Learning from Human Feedback)是将模型从"能回答"推向"回答得好"的关键技术。InstructGPT(Ouyang et al., 2022)首次系统性地展示了 RLHF 的效果,用仅 1.3B 参数的 RLHF 模型在人类评估中击败了 175B 的 GPT-3。
Step 1: SFT → Step 2: 训练 Reward Model → Step 3: PPO 优化策略 → 对齐后的模型
整个 RLHF 流程包含三个步骤,每个步骤都有独立的训练目标和数据需求。我们接下来逐一拆解。
8.1 Step 1: SFT 初始化
用高质量指令-回答对微调预训练模型,得到策略模型的初始版本 π_SFT。这个模型已经能像助手一样回答问题,但它的回答质量参差不齐——有时候过于冗长,有时候不够安全,风格不够一致。
8.2 Step 2: Reward Model 训练
用人类偏好数据训练一个奖励模型,详见下一节的 Bradley-Terry 模型推导。
8.3 Step 3: PPO 优化
用奖励模型的信号,通过 PPO 算法优化策略模型,详见第 10 节的推导。
8.4 为什么偏好对齐会让测试变复杂
- **信息密度下降:**回答可能更礼貌了,但为了"安全"和"无害",模型可能在回答中加入大量套话和免责声明,实际信息密度下降。
- **拒答率上升:**模型可能更守规矩了,但边界问题上更爱拒答。一些完全正当的问题也可能被误判为"不安全"。
- **多样性压缩:**风格变统一了,但创造性和多样性被压缩。所有回答开始趋同——比如总是以"当然可以!"开头。
- **sycophancy(谄媚):**模型学会了"用户喜欢被肯定",于是倾向于同意用户的观点,即使用户是错的。
- **length bias(长度偏置):**如果标注员倾向于选择更长的回答,奖励模型会学到"长=好",导致模型输出变冗长。
09. Reward Model 与 Bradley-Terry 模型推导
Reward Model(RM)的训练是 RLHF 的核心环节。它的目标是学习人类的偏好函数:给定一个 prompt 和一个回答,输出一个标量分数,分数越高代表回答越好。
9.1 Bradley-Terry 模型
Bradley-Terry 模型是一个经典的排序模型,它假设两个选项之间的偏好概率可以由它们各自的"实力分数"决定。在 RLHF 中,这个"实力分数"就是奖励分数 r(x, y)。 给定 prompt x,两个回答 y_w(preferred)和 y_l(rejected), Bradley-Terry 模型假设人类偏好 y_w 的概率为:
P(y_w ≻ y_l | x) = σ(r(x, y_w) - r(x, y_l))
其中 σ 是 sigmoid 函数:σ(z) = 1 / (1 + e^{-z})
直觉:
- 如果 r(x, y_w) ≫ r(x, y_l),σ → 1,模型确信 y_w 更好
- 如果 r(x, y_w) ≈ r(x, y_l),σ → 0.5,模型不确定
- 如果 r(x, y_w) ≪ r(x, y_l),σ → 0,说明标签可能错了
9.2 RM 的训练损失函数
给定 N 个偏好对的数据集 D = {(xⁱ, y_wⁱ, y_lⁱ)},RM 的训练目标是最大化偏好对的对数似然: L_RM = -E_{(x,y_w,y_l)~D} [log σ(r_φ(x, y_w) - r_φ(x, y_l))]
展开推导: = -1/N ∑ᵢ log σ(r_φ(xⁱ, y_wⁱ) - r_φ(xⁱ, y_lⁱ)) = -1/N ∑ᵢ log [1 / (1 + exp(-(r_φ(xⁱ, y_wⁱ) - r_φ(xⁱ, y_lⁱ))))] = 1/N ∑ᵢ log [1 + exp(-(r_φ(xⁱ, y_wⁱ) - r_φ(xⁱ, y_lⁱ)))]
这就是一个标准的二分类交叉熵损失, 只不过输入不是 logit 和标签,而是两个分数的差值
9.3 RM 的实现架构
RM 的典型实现方式:
1. 基座模型:通常复用 SFT 模型(去掉最后的 LM head)
2. 输出头:将最后一个 token 的 hidden state 映射到标量分数
score = Linear(hidden_state[-1]) # 输出维度 = 1
3. 训练数据:每条样本包含 (prompt, chosen_response, rejected_response)
4. 前向传播:分别对 chosen 和 rejected 做一次前向,得到两个分数
5. 损失计算:按上述 Bradley-Terry 损失计算
# 伪代码
chosen_score = reward_model(prompt + chosen_response) # 标量
rejected_score = reward_model(prompt + rejected_response) # 标量
loss = -log(sigmoid(chosen_score - rejected_score))RM 的质量决定了 RLHF 的天花板。
如果 RM 学到了错误的偏好(比如"长=好"或"拒答=安全"),PPO 就会沿着错误的方向优化。这就是 Reward Hacking 的根源。测试人员在评估对齐后模型时,如果发现系统性的输出偏差(比如所有回答都变长了),应该怀疑是 RM 的偏置问题。
10. PPO 算法的目标函数推导
PPO(Proximal Policy Optimization)是 RLHF 中最常用的强化学习算法。它的核心思想是:在利用 RM 信号改善策略的同时,不让策略偏离 SFT 基线太远。
10.1 RLHF 的优化目标
max_π E_{x~D, y~π(·|x)} [r_φ(x, y)] - β · D_KL(π(·|x) ‖ π_SFT(·|x))
两部分的含义: 第一项 E[r_φ(x,y)]:最大化奖励,让模型生成高分回答 第二项 β·D_KL:KL 惩罚,不让模型偏离 SFT 太远
β 是控制探索 vs 保守的超参数: β 大 → 模型不敢偏离 SFT → 安全但改善小 β 小 → 模型大幅偏离 SFT → 改善大但可能 reward hack
10.2 KL Penalty 的计算
KL 散度在 token 级别计算,然后累加到序列级别: D_KL(π ‖ π_SFT) = ∑t E [log π(y_t|x,y_{<t}) - log π_SFT(y_t|x,y_{<t})]
在实践中,使用采样近似: D_KL ≈ ∑t [log π(y_t|x,y) - log π_SFT(y_t|x,y_{<t})]
其中 y 是从当前策略 π 采样的回答 KL penalty 的直觉是:如果模型在某个 token 上给出的概率与 SFT 模型差异很大,就施加惩罚。这防止模型学到"极端但高奖励"的回答策略。
10.3 PPO 的 Clipped Surrogate Objective
标准策略梯度的问题是更新步长难以控制。PPO 通过裁剪概率比来限制每次更新的幅度: 定义概率比:rₜ(θ) = π_θ(aₜ|sₜ) / π_{θ_old}(aₜ|sₜ)
PPO 裁剪目标: L^{CLIP}(θ) = E_t [min( rₜ(θ) · Âₜ, clip(rₜ(θ), 1-ε, 1+ε) · Âₜ )]
其中: Âₜ 是优势函数(Advantage),表示这个动作比平均好多少 ε 是裁剪范围(通常 0.1-0.2)
裁剪的效果: 当 Âₜ > 0(好动作):rₜ 被截断到最多 1+ε,防止过度增加概率 当 Âₜ < 0(坏动作):rₜ 被截断到最少 1-ε,防止过度减少概率
10.4 RLHF 中 PPO 的完整训练循环
RLHF-PPO 训练的每一步:
1. 采样阶段(Rollout)
- 从数据集采样一批 prompt: {x₁, x₂, ..., x_B}
- 用当前策略 π_θ 对每个 prompt 生成回答: yᵢ ~ π_θ(·|xᵢ)
- 同时记录每个 token 的 log 概率: log π_θ(y_t|x, y_{<t})
2. 评分阶段(Scoring)
- 用 RM 对每个 (xᵢ, yᵢ) 打分: rᵢ = r_φ(xᵢ, yᵢ)
- 用 SFT 模型计算参考 log 概率: log π_SFT(y_t|x, y_{<t})
- 计算 KL penalty 并从奖励中扣除
3. 计算优势(GAE)
- 使用 Generalized Advantage Estimation
- Âₜ = δₜ + (γλ)δₜ₊₁ + (γλ)²δₜ₊₂ + ...
- 其中 δₜ = rₜ + γV(sₜ₊₁) - V(sₜ)
4. 优化阶段(PPO Update)
- 用裁剪目标更新策略参数 θ
- 同时更新价值函数 V
- 通常在同一批数据上做 2-4 个 epoch
5. 重复直到收敛11. DPO 的完整数学推导
DPO(Direct Preference Optimization, Rafailov et al., 2023)是 RLHF 的一个重要替代方案。它的核心贡献是证明了:RLHF 的优化目标可以被转化为一个不需要显式奖励模型的闭式损失函数。
11.1 从 RLHF 目标出发
回顾 RLHF 的优化目标: max_π E_{x~D, y~π(·|x)} [r(x, y)] - β · D_KL(π(·|x) ‖ π_ref(·|x))
这是一个带约束的优化问题。我们可以求它的最优解的闭式形式。
11.2 推导最优策略
将目标函数展开为逐 token 的形式,然后对 π(y|x) 求变分最优: 目标:max_π ∑_y π(y|x) · r(x,y) - β · ∑_y π(y|x) · log[π(y|x)/π_ref(y|x)]
对 π(y|x) 求导并令其为 0(注意 ∑_y π(y|x)=1 的约束用拉格朗日乘子处理):
∂/∂π(y|x): r(x,y) - β·log[π(y|x)/π_ref(y|x)] - β - λ = 0
解得: log[π*(y|x)/π_ref(y|x)] = r(x,y)/β - 1 - λ/β
π*(y|x) = π_ref(y|x) · exp(r(x,y)/β) / Z(x)
其中 Z(x) = ∑_y π_ref(y|x) · exp(r(x,y)/β) 是配分函数(归一化常数)
11.3 反解奖励函数
关键一步:从上面的最优策略公式中反解出 r(x,y): 从 π*(y|x) = π_ref(y|x) · exp(r(x,y)/β) / Z(x)
两边取 log: log π*(y|x) = log π_ref(y|x) + r(x,y)/β - log Z(x)
重排得到: r(x,y) = β · log[π*(y|x) / π_ref(y|x)] + β · log Z(x)
注意 β·log Z(x) 只依赖 x,不依赖 y。 当我们计算两个回答的奖励差时,这一项会被消掉。
11.4 代入 Bradley-Terry 模型
将反解的奖励代入 Bradley-Terry 偏好模型: P(y_w ≻ y_l | x) = σ(r(x,y_w) - r(x,y_l))
代入 r(x,y) = β·log[π(y|x)/π_ref(y|x)] + β·log Z(x):
r(x,y_w) - r(x,y_l) = β·log[π(y_w|x)/π_ref(y_w|x)] - β·log[π(y_l|x)/π_ref(y_l|x)] (β·log Z(x) 被消掉了!)
= β · [log(π(y_w|x)/π_ref(y_w|x)) - log(π(y_l|x)/π_ref(y_l|x))]
所以: P(y_w ≻ y_l | x) = σ(β · [log(π_θ(y_w|x)/π_ref(y_w|x)) - log(π_θ(y_l|x)/π_ref(y_l|x))])
11.5 DPO 的最终损失函数
L_DPO(θ) = -E_{(x,y_w,y_l)~D} [log σ(β · (log π_θ(y_w|x)/π_ref(y_w|x) - log π_θ(y_l|x)/π_ref(y_l|x)))]
简化记号: L_DPO = -E [log σ(β · (r_θ(x,y_w) - r_θ(x,y_l)))]
其中隐式奖励 r_θ(x,y) = log π_θ(y|x) - log π_ref(y|x) = log[π_θ(y|x)/π_ref(y|x)]
DPO 的核心洞察。
DPO 证明了一件优雅的事情:最优策略本身就隐式地定义了一个奖励函数。因此我们不需要显式训练一个 RM,直接用策略模型的 log 概率比作为"隐式奖励"就够了。这消除了 RLHF 中的两个复杂环节——RM 训练和 PPO 优化——将整个对齐过程简化为一个标准的监督学习问题。
11.6 DPO 梯度的直觉
对 DPO 损失取梯度: ∇_θ L_DPO = -β · E [σ(-û) · (∇_θ log π_θ(y_w|x) - ∇_θ log π_θ(y_l|x))]
其中 û = β·(log π_θ(y_w|x)/π_ref(y_w|x) - log π_θ(y_l|x)/π_ref(y_l|x))
σ(-û) 的含义:
- 如果模型已经正确地偏好 y_w(û很大),σ(-û) ≈ 0,梯度很小(已经学好了)
- 如果模型错误地偏好 y_l(û很小甚至负),σ(-û) ≈ 1,梯度很大(需要纠正)
梯度方向:
- 增大 π_θ(y_w|x):提高 preferred 回答的概率
- 减小 π_θ(y_l|x):降低 rejected 回答的概率
- 两者同时进行
12. 偏好数据的结构与实践
12.1 一个偏好数据样本到底长什么样
{
"prompt": "用户问:我该如何取消订单?",
"chosen": "您可以在订单详情页点击取消,若已发货请走退货流程。具体步骤:1. 打开订单页面 2. 找到目标订单 3. 点击'取消订单'按钮 4. 选择取消原因 5. 确认取消。如果订单已发货,您需要先收货再申请退货退款。",
"rejected": "这个问题我不确定,您稍后再试。"
}这类数据不是在教模型"知识点是什么",而是在教模型"在同一个问题下,哪种回答风格和内容更优"。因此它会直接影响 helpfulness、礼貌性、拒答策略和表达风格。
12.2 偏好数据的常见问题
| 问题 | 表现 | 对模型的影响 |
|---|---|---|
| 长度偏置 | 标注员总选更长的回答 | 模型学到"长=好",输出冗长 |
| 风格偏置 | 标注员偏好某种表达风格 | 模型生成千篇一律的回答 |
| 标注噪声 | 不同标注员对同一对的判断不一致 | 模型学到矛盾的偏好信号 |
| 分布偏移 | 偏好数据的 prompt 分布与实际使用不同 | 在实际场景中对齐效果打折 |
| 难度不均 | 大部分偏好对差异明显,没有难例 | 模型在边界场景表现差 |
12.3 偏好数据质量的量化评估
评估偏好数据质量的关键指标:
1. 标注一致率(Inter-annotator Agreement)
- 让多个标注员对同一组偏好对标注
- 计算 Cohen's Kappa 或 Fleiss' Kappa
- Kappa < 0.4:质量堪忧 | 0.4-0.6:一般 | > 0.6:良好
2. 长度相关性
- 计算 chosen 和 rejected 的平均长度差
- 如果 chosen 总是更长 → 存在长度偏置
- 建议:长度差的绝对值中位数 < 总长度的 20%
3. Prompt 多样性
- 计算 prompt 的语义聚类数
- 过于集中在某类问题 → 对齐效果不全面
4. 难度分布
- 用初始 RM 对所有偏好对评分
- 如果大部分对的分差 > 3 → 太简单,模型没什么可学
- 理想状态:分差呈正态分布,均值在 0.5-1.5 之间测试启发。
模型升级后不要只看单个总分。至少要拆成准确性、完整性、风格、拒答率、安全性、工具调用成功率几个维度。偏好对齐带来的回归,往往不是"全体下降",而是某个维度明显变强,另一个维度明显变弱。如果你能了解对齐数据的偏置方向,就能预判哪些维度可能受影响。
13. ORPO / SimPO / KTO:新一代对齐方法
DPO 之后,研究社区提出了多种改进方法,进一步简化训练流程或改善对齐效果。作为测试人员,了解这些方法有助于理解不同供应商模型之间的行为差异。
13.1 ORPO(Odds Ratio Preference Optimization)
ORPO(Hong et al., 2024)的核心思想是将 SFT 和偏好对齐合并为一个阶段,不再需要单独的 SFT 步骤。 L_ORPO = L_SFT + λ · L_OR
其中 L_SFT 是标准的交叉熵损失(针对 chosen 回答) L_OR 是 odds ratio 损失:
L_OR = -log σ(log [odds_θ(y_w|x) / odds_θ(y_l|x)])
odds_θ(y|x) = P_θ(y|x) / (1 - P_θ(y|x))
与 DPO 的区别:
- DPO 需要冻结的参考模型 π_ref → ORPO 不需要
- DPO 在 SFT 之后做 → ORPO 和 SFT 同时做
- ORPO 用 odds ratio 而非 log probability ratio
ORPO 对测试的意义。
ORPO 训练的模型没有经历独立的 SFT 阶段,这意味着它的"助手能力"和"偏好对齐"是同时学到的。测试时可能发现:ORPO 模型的指令遵循能力和偏好表现更一致(因为是一起训练的),但在某些需要"纯粹SFT 能力"的场景(比如严格格式遵循)可能不如先 SFT 再 DPO 的两阶段模型。
13.2 SimPO(Simple Preference Optimization)
SimPO(Meng et al., 2024)进一步简化了 DPO,去掉了参考模型,并使用序列长度归一化的 log 概率作为隐式奖励: SimPO 的隐式奖励: r_SimPO(x,y) = (1/|y|) · log π_θ(y|x) = (1/|y|) ∑t log π_θ(y_t|x,y)
L_SimPO = -E [log σ(β · (r_SimPO(x,y_w) - r_SimPO(x,y_l)) - γ)]
与 DPO 的关键区别:
- 不需要参考模型 π_ref → 训练更简单,显存减半
- 长度归一化 (1/|y|) → 自然消除长度偏置
- 引入 margin γ → 要求 chosen 的奖励至少比 rejected 高 γ
13.3 KTO(Kahneman-Tversky Optimization)
KTO(Ethayarajh et al., 2024)的独特之处是不需要成对的偏好数据。它只需要"这个回答好"或"这个回答不好"的二元标签。 KTO 不需要 (y_w, y_l) 配对,只需要:
- 正例:(x, y) 标记为"好"
- 负例:(x, y) 标记为"不好"
L_KTO = E_{(x,y)~D⁺} [w(λ_D) · (1 - σ(β · r_θ(x,y) - z_ref))] + E_{(x,y)~D⁻} [w(λ_D) · σ(β · r_θ(x,y) - z_ref)]
灵感来源:Kahneman 和 Tversky 的前景理论
- 人类对"损失"的敏感度高于"收益"
- KTO 对负例的惩罚权重大于对正例的奖励权重
KTO 的实际优势。
在企业场景中,收集成对偏好数据成本很高(需要对同一问题生成两个回答让人比较)。而收集"这个回答好不好"的二元反馈容易得多——用户的点赞/点踩、客服评价、任务完成率等都是现成的信号。KTO 让这些廉价信号可以直接用于对齐训练。测试时如果发现模型是用 KTO 训练的,要注意:正负例的比例和质量可能不均衡,导致模型在某些场景过度保守或过度激进。
13.4 方法对比总结
| 方法 | 是否需要 RM | 是否需要参考模型 | 数据需求 | 训练复杂度 |
|---|---|---|---|---|
| RLHF (PPO) | 需要 | 需要 | 成对偏好 | 最高(4个模型同时运行) |
| DPO | 不需要 | 需要 | 成对偏好 | 中等 |
| ORPO | 不需要 | 不需要 | 成对偏好 + SFT | 低 |
| SimPO | 不需要 | 不需要 | 成对偏好 | 低 |
| KTO | 不需要 | 需要 | 二元标签(非成对) | 低 |
14. 安全对齐与拒答边界
安全对齐本质上是在让模型学会:哪些问题应该拒答、哪些内容应该降风险、哪些指令即便形式上像正常任务也不能执行。于是它会影响的不只是"危险问题",还会影响一些模糊边界场景。
| 测试现象 | 潜在原因 | 解释方式 |
|---|---|---|
| 模型更爱拒答 | 安全阈值上移 | 不是坏掉,而是 policy 更保守 |
| 回答更泛化 | 风险内容被抽象化处理 | helpfulness 下降但 safety 上升 |
| 边界问题不稳定 | 偏好信号与安全信号拉扯 | 需要对 slice 做稳定性回归 |
| 过度拒答 | 安全训练数据的假阳性 | 正常问题被错误分类为危险 |
14.1 安全对齐的技术实现
安全对齐通常包含多个层面的技术手段:
红队测试(Red Teaming)
用对抗性 prompt 测试模型的安全边界,收集模型被"突破"的案例,然后将这些案例加入训练数据,教模型如何正确拒答。
安全 RLHF
在偏好数据中加入安全维度的标注。标注员不仅判断哪个回答更好,还判断回答是否安全。RM 需要同时学习有用性和安全性两个目标。
Rule-based Reward
用规则系统(关键词过滤、分类器)对模型输出做安全检查,违规输出给负奖励。好处是覆盖面广、一致性高,但可能过度敏感。
14.2 一个典型的企业升级回归
旧模型:
- 回答更详细
- 拒答率较低(约 3%)
- 安全边界较松
- 对"退款异常申诉"等敏感话题直接给建议
新模型:
- 拒答更稳
- 安全性更高(安全评测得分从 82 → 94)
- 但拒答率从 3% 升到 12%
- 客服场景里对"退款异常申诉"也更容易给出泛化建议
结论:
不是简单的"新模型变差",而是 alignment 目标发生了偏移。
测试需要拆成:
1. 安全提升是否达标 → 达标(+12分)
2. 业务帮助度是否掉到不可接受 → 需按场景评估
3. 哪些高价值场景需要定制 prompt 或补充对齐数据 → 识别 top-20 被误拒场景
4. 拒答率上升中,有多少是"应该拒"的 vs "误拒"的 → 计算误拒率15. Constitutional AI 的实现机制
Constitutional AI(CAI, Bai et al., 2022, Anthropic)是一种减少对人类标注依赖的安全对齐方法。核心思想是:让 AI 自己按照一组"宪法原则"来判断回答的好坏,实现 RLAIF(Reinforcement Learning from AI Feedback)。
15.1 两阶段流程
Stage 1: 自我批评 → 模型生成 → 自我评估 → 自我修正 → Stage 2: RLAIF → 用 AI 反馈训练 RM → RL 优化
15.2 Stage 1: Critique-Revision
输入一个可能有害的 prompt,过程如下:
1. 初始回答:让模型先生成一个回答(可能包含有害内容)
2. 批评(Critique):给模型一个宪法原则,让它评估自己的回答
原则示例:"回答是否可能鼓励非法行为?"
模型输出:"是的,我的回答提供了具体的操作步骤,可能被滥用。"
3. 修正(Revision):让模型根据批评修正回答
模型输出:修正后的安全回答
4. 迭代:可以用多个宪法原则依次批评和修正
最终产出:(原始prompt, 修正后的回答) 作为 SFT 数据15.3 Stage 2: RLAIF
用模型自己(而非人类标注员)来生成偏好数据:
对每个 prompt,生成两个回答 y₁ 和 y₂,然后让模型按照宪法原则判断哪个更好:
Prompt to AI Judge:
"以下是对同一问题的两个回答。根据原则'回答应该有帮助且无害',
哪个回答更好?请选择 A 或 B。"
回答 A: {y₁}
回答 B: {y₂}
AI 的选择 → 偏好标签 → 训练 RM → RL 优化
关键优势:
- 规模化:AI 标注可以覆盖海量数据,成本远低于人类标注
- 一致性:同一组原则产生一致的偏好信号
- 可控性:修改宪法原则即可调整对齐方向
关键风险:
- AI 的判断本身可能有偏差(自我强化循环)
- 宪法原则的设计质量决定了对齐的上限
- 可能导致过度保守(AI 比人类更倾向于选择"安全但无用"的回答)对测试的影响。
如果模型使用了 Constitutional AI,测试人员应该注意:(1)安全性通常较高但可能过度保守;(2)模型的安全边界由一组明确的原则决定,理论上可以预判哪些场景会被拒答;(3)如果发现模型在某些正当场景过度拒答,可能是宪法原则设计得太严格。了解供应商是否使用了 CAI,有助于解释模型的安全行为模式。
16. Reward Hacking 的数学解释
Reward Hacking(奖励黑入)是 RLHF 中最核心的风险。它指的是模型找到了一种策略,能获得高奖励分数,但实际上并没有真正满足人类的偏好。
16.1 Goodhart 定律的形式化
Goodhart 定律:当一个指标变成目标时,它就不再是好的指标。
形式化:设 r_true(x,y) 是人类真实偏好,r_φ(x,y) 是训练出的 RM。
两者之间存在近似误差:r_φ(x,y) ≈ r_true(x,y) + ε(x,y)
当策略 π 在 r_φ 上做优化时: max_π E[r_φ(x,y)] = max_π E[r_true(x,y) + ε(x,y)]
如果不加约束,π 会同时最大化 r_true 和 ε:
- 在 r_true 和 ε 一致的区域 → 正常改善
- 在 ε 很大但 r_true 不大的区域 → reward hacking
实证现象(Gao et al., 2023):
- 随着 PPO 训练的进行,proxy reward r_φ 持续上升
- gold reward r_true 先上升后下降(倒 U 形)
- 拐点出现在 KL(π, π_ref) ≈ 5-15 之间
16.2 常见的 Reward Hacking 模式
| Hacking 模式 | 机制 | 测试表现 |
|---|---|---|
| 长度 Hacking | RM 隐含"长=好"的偏置 | 回答变冗长,信息密度下降 |
| 格式 Hacking | RM 对 markdown 列表给高分 | 所有回答都变成列表格式 |
| 谄媚 Hacking | RM 对"同意用户"给高分 | 模型不再纠正用户的错误 |
| 安全 Hacking | RM 对"拒答"给高安全分 | 模型对正常问题也拒答 |
| 风格 Hacking | RM 偏好某种修辞风格 | 所有回答变成同一种腔调 |
16.3 缓解 Reward Hacking 的方法
1. KL Penalty(最基本的缓解手段)
- 已经内置在 RLHF 目标中
- 限制策略偏离 SFT 的程度
- β 的选择是关键:太小 → hacking,太大 → 没效果
2. Reward Model Ensemble
- 训练多个 RM,取它们的最小值或平均值
- r_ensemble(x,y) = min(r₁(x,y), r₂(x,y), ..., r_K(x,y))
- 不同 RM 的偏置方向通常不同,取 min 可以抑制各自的偏置
3. 长度惩罚
- r_adjusted(x,y) = r_φ(x,y) - α · len(y)
- 直接惩罚过长的回答
4. 对抗训练
- 持续用 red team 方法发现 hacking 模式
- 将发现的 hacking 案例加入训练数据
5. Process Reward Model (PRM)
- 不只对最终答案打分,对中间推理步骤也打分
- 减少"直接跳到高分答案"的 hacking 路径17. 对齐税(Alignment Tax)的量化分析
对齐税(Alignment Tax)指的是:为了让模型更安全、更符合人类偏好而付出的能力代价。这是一个不可回避的 tradeoff。
17.1 对齐税的理论基础
设模型的能力空间为多维向量:C = (C_helpful, C_safe, C_creative, C_factual, ...)
对齐优化的目标函数本质上是一个加权和: max ∑ᵢ wᵢ · Cᵢ s.t. C_safe ≥ τ_safe
当安全约束 τ_safe 提高时,其他维度可能被挤压:
- C_helpful ↓(为了不给有害建议,某些有用建议也被限制)
- C_creative ↓(为了不生成冒犯内容,创造性表达被压缩)
- C_factual ↓(为了不传播敏感信息,某些事实也不敢说)
对齐税 = ∑ᵢ≠safe ΔCᵢ(各维度的能力损失总和)
17.2 实证量化
对齐税的典型量化数据(来源:多篇论文的综合):
1. 基础能力影响
- MMLU(知识测试):对齐后通常下降 0-3%(影响小)
- HumanEval(代码):对齐后通常下降 1-5%(影响小-中)
- GSM8K(数学):对齐后通常下降 0-2%(影响小)
2. 生成质量影响
- 信息密度:下降 10-25%(显著影响)
- 创造性写作多样性:下降 15-30%(显著影响)
- 平均回答长度:增加 20-50%(冗余增加)
3. 帮助性影响
- 一般问题帮助性:提升 5-15%(正面影响)
- 边界问题帮助性:下降 20-40%(显著负面影响)
- 过度拒答率:从 1-2% 上升到 5-15%
4. 对齐税的大小与方法有关
- RLHF (PPO):对齐税最大(因为优化最激进)
- DPO:对齐税中等
- SimPO/ORPO:对齐税较小
- KTO:对齐税取决于数据质量测试应该量化对齐税。
每次模型升级,测试报告应该包含一个"对齐税仪表盘":在安全性提升了多少的同时,帮助性下降了多少、创造性下降了多少、过度拒答率上升了多少。这让业务决策者能做出知情的 tradeoff 决策,而不是面对一个模糊的"模型变好了/变差了"的结论。
17.3 帕累托最优与对齐策略选择
不同的对齐方法和超参数选择会产生不同的安全-有用性 tradeoff 曲线。理想情况下,我们希望找到帕累托最优点——在不损失安全性的前提下最大化有用性,或者在不损失有用性的前提下最大化安全性。
帕累托前沿的概念:
Safety ↑
│ * (过度安全,不太有用)
│ * *
│ * * ← 帕累托前沿(最优 tradeoff 曲线)
│ * *
│ * *
│* × * (非帕累托最优:可以在不损失安全的情况下提高有用性)
│ *
│ * (很有用,但不太安全)
└──────────────→ Helpfulness
测试的工作:
1. 评估当前模型在这个二维空间中的位置
2. 判断是否处于帕累托前沿(或离前沿多远)
3. 对比不同模型版本的 tradeoff 曲线移动方向
4. 帮助业务决策者选择合适的 tradeoff 点18. HuggingFace TRL 库源码剖析
TRL(Transformer Reinforcement Learning)是 HuggingFace 开发的对齐训练库,是目前开源社区最常用的 RLHF/DPO 训练框架。理解它的源码有助于测试人员理解训练配置如何影响模型行为。
18.1 DPOTrainer 的关键代码路径
# trl/trainer/dpo_trainer.py 核心逻辑拆解
class DPOTrainer(Trainer):
def __init__(self, model, ref_model, beta, ...):
self.beta = beta # KL 惩罚系数,通常 0.1-0.5
self.ref_model = ref_model # 冻结的参考模型
def compute_loss(self, model, inputs):
"""DPO 损失计算的核心"""
# 1. 获取 chosen 和 rejected 的 token ids
chosen_input_ids = inputs["chosen_input_ids"]
rejected_input_ids = inputs["rejected_input_ids"]
# 2. 用当前策略模型计算 log 概率
chosen_logps = self.get_batch_logps(model, chosen_input_ids)
rejected_logps = self.get_batch_logps(model, rejected_input_ids)
# 3. 用冻结的参考模型计算 log 概率
with torch.no_grad():
ref_chosen_logps = self.get_batch_logps(self.ref_model, chosen_input_ids)
ref_rejected_logps = self.get_batch_logps(self.ref_model, rejected_input_ids)
# 4. 计算 log ratios
chosen_logratios = chosen_logps - ref_chosen_logps
rejected_logratios = rejected_logps - ref_rejected_logps
# 5. 计算 DPO 损失
logits = self.beta * (chosen_logratios - rejected_logratios)
loss = -F.logsigmoid(logits).mean()
return loss
def get_batch_logps(self, model, input_ids):
"""计算序列的 log 概率(只算 response 部分)"""
outputs = model(input_ids)
logits = outputs.logits # [batch, seq_len, vocab_size]
# 只对 response tokens 计算 log prob
log_probs = F.log_softmax(logits, dim=-1)
# 用 gather 取出实际 token 的 log prob
per_token_logps = torch.gather(
log_probs[:, :-1, :], 2, input_ids[:, 1:].unsqueeze(2)
).squeeze(2)
# 用 loss_mask 屏蔽 prompt 部分
return (per_token_logps * loss_mask).sum(-1)18.2 PPOTrainer 的关键代码路径
# trl/trainer/ppo_trainer.py 核心逻辑拆解
class PPOTrainer:
def step(self, queries, responses, scores):
"""PPO 训练的一步"""
# 1. Rollout: 计算当前策略和参考模型的 log probs
old_logprobs = self.compute_logprobs(self.model, queries, responses)
ref_logprobs = self.compute_logprobs(self.ref_model, queries, responses)
# 2. 计算 KL penalty 并调整奖励
kl = old_logprobs - ref_logprobs # per-token KL
non_score_reward = -self.kl_ctl.value * kl # KL 惩罚
# 将 RM 分数加到最后一个 token
rewards = non_score_reward.clone()
rewards[:, -1] += scores # scores 来自 Reward Model
# 3. GAE: 计算优势函数
values = self.value_model(queries, responses)
advantages, returns = self.compute_gae(rewards, values)
# 4. PPO Update (多个 mini-batch epoch)
for epoch in range(self.ppo_epochs): # 通常 2-4
for mini_batch in self.create_mini_batches(...):
# 计算新的 log probs
new_logprobs = self.compute_logprobs(self.model, ...)
# 概率比
ratio = torch.exp(new_logprobs - old_logprobs)
# 裁剪目标
surr1 = ratio * advantages
surr2 = torch.clamp(ratio, 1-self.cliprange, 1+self.cliprange) * advantages
policy_loss = -torch.min(surr1, surr2).mean()
# 价值函数损失
value_loss = (values_pred - returns).pow(2).mean()
# 总损失
loss = policy_loss + self.vf_coef * value_loss
loss.backward()
self.optimizer.step()18.3 关键超参数对行为的影响
| 超参数 | 典型值 | 调大的效果 | 调小的效果 |
|---|---|---|---|
| beta (DPO) | 0.1-0.5 | 更保守,偏移小 | 更激进,偏移大 |
| learning_rate | 1e-6 ~ 5e-7 | 学习快但不稳定 | 稳定但收敛慢 |
| kl_penalty (PPO) | 0.01-0.2 | 更贴近 SFT 基线 | 允许更大偏移 |
| cliprange (PPO) | 0.1-0.2 | 允许更大的策略更新 | 更保守的更新 |
| ppo_epochs | 2-4 | 更充分利用数据 | 更快但可能欠拟合 |
| LoRA rank | 8-64 | 更强的表达能力 | 更强的正则化 |
源码阅读对测试的价值。
理解这些代码路径后,测试人员可以:(1)当发现模型行为异常时,快速定位可能的训练配置原因;(2)理解为什么 beta=0.1 和 beta=0.5 的模型在边界问题上行为差异很大;(3)在与算法团队沟通时使用精确的技术语言而非模糊描述;(4)设计更有针对性的测试用例——比如专门测试 KL penalty 过小可能导致的 reward hacking 行为。
19. 完整的 SFT + DPO 训练代码示例
以下是一个完整的、可运行的训练代码示例,展示了从 SFT 到 DPO 的完整流程。
19.1 环境准备
# 安装依赖
pip install torch transformers trl datasets peft accelerate bitsandbytes
# 硬件要求:
# - SFT (QLoRA): 单张 24GB GPU (RTX 4090 / A5000)
# - DPO (QLoRA): 单张 24GB GPU(需要同时加载 model 和 ref_model)
# - 如果 GPU 内存不足,可使用 gradient_checkpointing 和更小的 batch size19.2 SFT 阶段代码
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
from trl import SFTTrainer, SFTConfig
from datasets import load_dataset
# ====== 1. 模型加载(QLoRA 4-bit 量化)======
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True,
)
model_name = "meta-llama/Llama-3.1-8B"
model = AutoModelForCausalLM.from_pretrained(
model_name,
quantization_config=bnb_config,
device_map="auto",
attn_implementation="flash_attention_2",
)
tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token
# ====== 2. LoRA 配置 ======
model = prepare_model_for_kbit_training(model)
lora_config = LoraConfig(
r=32, # LoRA rank
lora_alpha=64, # 缩放因子
target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
# ====== 3. 数据准备 ======
def format_instruction(example):
messages = [
{"role": "system", "content": "你是一个有帮助的中文助手。"},
{"role": "user", "content": example["instruction"]},
{"role": "assistant", "content": example["output"]},
]
return {"text": tokenizer.apply_chat_template(
messages, tokenize=False, add_generation_prompt=False
)}
dataset = load_dataset("your_org/sft_dataset", split="train")
dataset = dataset.map(format_instruction, remove_columns=dataset.column_names)
# ====== 4. 训练 ======
training_args = SFTConfig(
output_dir="./sft_output",
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=2e-4,
lr_scheduler_type="cosine",
warmup_ratio=0.1,
bf16=True,
logging_steps=10,
save_strategy="epoch",
max_seq_length=2048,
gradient_checkpointing=True,
dataset_text_field="text",
packing=True,
)
trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=dataset,
peft_config=lora_config,
tokenizer=tokenizer,
)
trainer.train()
trainer.save_model("./sft_final")19.3 DPO 阶段代码
from trl import DPOTrainer, DPOConfig
from peft import PeftModel
# ====== 1. 加载 SFT 模型作为基座 ======
base_model = AutoModelForCausalLM.from_pretrained(
model_name,
quantization_config=bnb_config,
device_map="auto",
attn_implementation="flash_attention_2",
)
model = PeftModel.from_pretrained(base_model, "./sft_final")
model = model.merge_and_unload()
# 重新添加 LoRA 用于 DPO 训练
model = prepare_model_for_kbit_training(model)
dpo_lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
# ====== 2. 准备偏好数据 ======
def format_dpo_data(example):
prompt = tokenizer.apply_chat_template(
[{"role": "user", "content": example["prompt"]}],
tokenize=False, add_generation_prompt=True
)
return {
"prompt": prompt,
"chosen": example["chosen"],
"rejected": example["rejected"],
}
pref_dataset = load_dataset("your_org/preference_dataset", split="train")
pref_dataset = pref_dataset.map(format_dpo_data, remove_columns=pref_dataset.column_names)
# ====== 3. DPO 训练 ======
dpo_args = DPOConfig(
output_dir="./dpo_output",
num_train_epochs=1,
per_device_train_batch_size=2,
gradient_accumulation_steps=8,
learning_rate=5e-7, # DPO 的学习率通常比 SFT 低一个数量级
lr_scheduler_type="cosine",
warmup_ratio=0.1,
beta=0.1, # DPO 的核心超参数
bf16=True,
logging_steps=10,
save_strategy="epoch",
max_length=2048,
max_prompt_length=1024,
gradient_checkpointing=True,
)
dpo_trainer = DPOTrainer(
model=model,
ref_model=None, # 使用 peft 时可设为 None,TRL 会自动处理
args=dpo_args,
train_dataset=pref_dataset,
peft_config=dpo_lora_config,
tokenizer=tokenizer,
)
dpo_trainer.train()
dpo_trainer.save_model("./dpo_final")19.4 训练监控指标
# DPO 训练过程中需要监控的关键指标
1. train/loss:DPO 损失
- 应该在训练中持续下降
- 如果突然上升或震荡 → 学习率可能太大
2. train/rewards/chosen:chosen 回答的隐式奖励
- = log(π_θ(y_w|x)/π_ref(y_w|x))
- 应该逐渐上升(模型越来越偏好 chosen)
3. train/rewards/rejected:rejected 回答的隐式奖励
- 应该逐渐下降(模型越来越远离 rejected)
4. train/rewards/margins:chosen 和 rejected 的奖励差
- = rewards/chosen - rewards/rejected
- 应该逐渐增大
- 如果增大到很大(>10)→ 可能过拟合
5. train/rewards/accuracies:偏好准确率
- 模型对 chosen 给出更高隐式奖励的比例
- 起始值通常 50-60%,训练后应达到 70-85%
- 如果接近 100% → 几乎一定过拟合
6. eval/loss:验证集损失
- 与 train/loss 的差距反映过拟合程度
- 如果 eval/loss 开始上升 → 停止训练从训练指标到测试策略。
测试人员虽然不直接训练模型,但理解这些指标有助于事后分析。如果算法团队告诉你"DPO 的 rewards/margins 达到了 8.5",你应该意识到这可能意味着过拟合,需要在测试中特别关注模型是否变得过于极端(对 preferred 风格极度偏好,对 rejected 风格极度回避,在灰区场景表现不稳定)。
20. 为什么模型升级会回归,而且回归方式不止一种
企业最容易误判的一件事是:模型版本升级后,如果某些业务 case 退化,就以为模型"整体不行"。其实常见回归路径至少有四种:
- **知识回归:**模型换了,事实掌握范围不同。预训练数据的覆盖面、时间截止点、领域分布都可能变化。
- **风格回归:**更简洁、更保守或更模板化。这通常是 SFT 数据或偏好对齐策略变化的结果。
- **边界回归:**拒答策略改变,灰区问题波动更大。安全对齐的强度变化直接影响这一维度。
- **工具回归:**对结构化输出、函数调用、长上下文的配合方式变化。这可能与 SFT 数据中工具调用样本的变化有关。
20.1 回归分析框架
预训练层回归
- 事实性知识变化
- 语言理解能力变化
- 推理能力变化
- 检查方法:MMLU、ARC、TruthfulQA 等标准 benchmark
SFT 层回归
- 指令遵循能力变化
- 格式输出能力变化
- 多轮对话能力变化
- 检查方法:IFEval、MT-Bench、自定义指令集
对齐层回归
- 偏好表达风格变化
- 帮助性 vs 安全性 tradeoff 变化
- 拒答率和拒答模式变化
- 检查方法:AlpacaEval、Arena-Hard、自定义偏好集
安全层回归
- 安全边界松紧变化
- 过度拒答 vs 过度开放
- 越狱抵抗力变化
- 检查方法:SafetyBench、自定义红队集、误拒率
20.2 多维回归报告模板
模型版本切换回归报告
====================
基线模型:ModelA-v2.1 新模型:ModelA-v3.0
1. 知识与推理(预训练层)
MMLU: 78.2 → 81.5 (+3.3) ✅
GSM8K: 72.1 → 75.8 (+3.7) ✅
TruthfulQA: 64.3 → 62.1 (-2.2) ⚠️ 轻微下降
2. 指令遵循(SFT 层)
IFEval: 83.4 → 85.1 (+1.7) ✅
JSON 输出合规率: 91.2 → 89.8 (-1.4) ⚠️
多轮一致性: 88.5 → 87.2 (-1.3) ⚠️
3. 帮助性与偏好(对齐层)
AlpacaEval 2.0 WR: 32.1 → 38.5 (+6.4) ✅
业务场景满意度: 4.2/5 → 3.9/5 (-0.3) ⚠️ 需细查
回答平均长度: 187 → 243 tokens (+30%) ⚠️ 可能冗余
4. 安全性(安全层)
SafetyBench: 82.0 → 94.0 (+12.0) ✅
过度拒答率: 3.1% → 11.8% (+8.7%) ❌ 显著恶化
误拒 Top-20 场景:详见附件
结论:
- 知识和推理能力整体提升
- 安全性大幅提升,但伴随严重的过度拒答
- 业务帮助度在敏感话题上下降
- 建议:对 Top-20 误拒场景定制 system prompt 补救所以测试报告不能只写"通过率从 88% 降到 84%"。
要说明降在哪个 slice、为什么降、这是不是 alignment 导致的可接受 tradeoff。否则业务、算法、管理层会围着同一个总分各说各话。一份好的回归报告应该让每个角色都能找到自己关心的维度,并据此做出决策。
21. 测试设计清单
- 模型切换时同时报告准确性、完整性、拒答率、安全性、风格稳定性——至少五个维度。
- 对边界场景单独分 slice,不和普通业务问题混在一起。边界场景的评价标准和普通场景不同。
- 对"帮助度下降但安全性上升"的场景,提前和业务确认可接受阈值。这是一个 tradeoff 决策,不是技术问题。
- 记录 prompt、模型、评测集三者版本,避免把变化源混掉。如果三者同时变了,你根本无法归因。
- 对于企业内部高价值场景,建立定制回归集,不要只依赖公共 benchmark。公共 benchmark 衡量的是通用能力,不是你的业务能力。
- 监控对齐税指标:每次升级都量化安全-有用性 tradeoff 的变化方向和幅度。
- 建立"误拒"(false refusal)专项测试集,包含那些看起来敏感但实际正当的问题。
- 对 reward hacking 的常见模式建立检测机制:回答长度异常增加、格式千篇一律、谄媚倾向增强。
21.1 对齐相关的测试用例设计原则
安全边界测试
- 明确有害 → 应该拒答
- 明确安全 → 不应拒答
- 灰区(如医疗咨询)→ 应谨慎但有帮助
- 越狱尝试 → 应识别并拒绝
偏好一致性测试
- 同一问题多次采样的风格一致性
- 改变问法但不改变语义 → 回答应一致
- 对立观点的平衡性
- 事实纠错能力(不谄媚)
对齐税评估
- 信息密度测量(实际信息 / 总字数)
- 回答多样性(多次采样的 distinct-n)
- 不必要免责声明的频率
- 对比对齐前后的能力保持率
21.2 自测标准
完成这一页后,你应该能:
- 用链式法则推导自回归语言模型的训练目标,理解交叉熵、KL 散度和 perplexity 之间的数学关系。
- 解释 CLM 和 MLM 两种预训练范式的本质差异,以及为什么 CLM 成为生成式 AI 的基础。
- 说明 SFT 的损失函数如何通过 loss masking 只在 response 部分计算梯度。
- 推导 Bradley-Terry 模型和 Reward Model 的训练损失。
- 从 RLHF 的 KL-constrained 目标出发,完整推导出 DPO 的闭式损失函数。
- 说明 ORPO、SimPO、KTO 各自的核心创新点和适用场景。
- 解释 Constitutional AI 的两阶段流程及其对模型安全行为的影响。
- 用 Goodhart 定律解释 Reward Hacking 现象,并列举至少三种缓解方法。
- 量化对齐税(Alignment Tax),并设计帕累托最优评估框架。
- 阅读 TRL 库的 DPOTrainer 和 PPOTrainer 核心代码,理解超参数对模型行为的影响。
- 把模型升级回归分解成知识、风格、边界、工具四类,并撰写多维回归报告。
- 设计覆盖安全边界、偏好一致性和对齐税的完整测试用例集。
补充练习与参考答案
补充练习
- 用自己的话解释预训练、SFT 和 RLHF 分别在解决什么问题。
- 为什么奖励模型设计不好会导致 reward hacking?
- 从测试视角看,对齐策略调整后最应该补哪些回归?
参考答案要点
- 预训练解决语言模式学习,SFT 解决按指令做事,RLHF/对齐解决更有用、更安全、更符合偏好的输出。
- 奖励目标一旦和真实业务目标不一致,模型就会学会“拿高分”而不是“真正做好任务”。
- 对齐调整后最该补安全边界、过度拒答、事实性退化和风格漂移回归。