概述
ORPO(Odds Ratio Preference Optimization) 是一种创新的语言模型对齐方法,由 Hong 等人在 2024 年提出1。其核心创新在于:单阶段训练即可同时完成监督微调(Supervised Fine-Tuning, SFT)与偏好对齐,无需像传统方法那样分两阶段进行。
传统方法的局限性
在 ORPO 出现之前,主流的对齐方法(如 DPO 和 GRPO)通常采用两阶段训练范式:
- 第一阶段:SFT —— 使用人类标注的优质响应进行监督微调
- 第二阶段:RLHF/DPO —— 利用偏好数据(preferred/rejected 响应对)进行对齐
这种两阶段方法存在以下问题:
- 训练流程复杂:需要维护多个模型(reference model、reward model 等)
- 计算开销大:DPO 需要 reference model 进行 KL 散度约束,RLHF 更需要额外的 reward model
- 阶段间干扰:SFT 和对齐目标可能相互冲突
ORPO 的核心思想
ORPO 的关键洞察是:将 SFT 和对齐目标统一到一个损失函数中,通过引入优势比(Odds Ratio) 作为偏好信号,实现真正的单阶段训练。
核心观察:优势比天然地同时考虑了(1)模型对目标的”喜欢程度”和(2)模型对拒绝目标的”讨厌程度”。
理论基础
Odds 的定义
给定输入 和响应 ,模型的 odds 定义为:
其中 是模型在给定输入 下生成响应 的概率。当 时,,表示模型更倾向于生成该响应。
优势比(Odds Ratio)
对于偏好数据 ,其中 是偏好响应, 是拒绝响应,优势比定义为:
对优势比取对数得到对数优势比(log odds ratio):
为什么 Odds Ratio 适合作为对齐信号?
- 同时惩罚两者:优势比不仅增大 ,还同时减小
- 概率解释直观: 表示偏好响应相对于拒绝响应有更高的生成概率
- 避免饱和:与简单的概率比不同,odds ratio 在概率接近 1 或 0 时仍能提供有意义的信号
损失函数设计
ORPO 的损失函数由两部分组成:
SFT 损失项
标准语言模型交叉熵损失:
该损失项确保模型在偏好响应上进行生成能力的监督学习。
Odds Ratio 损失项
这是 ORPO 的核心创新:
其中 是 sigmoid 函数。该损失项直接最大化对数优势比,使模型倾向于生成 而非 。
组合损失的直观理解
- SFT 项:确保模型学习偏好响应的分布
- OR 项:在保持生成能力的同时,明确区分偏好与拒绝响应
- 超参数 :平衡两个目标的权重
超参数 的选择
论文建议 ,默认值建议使用 。较大的 会加快对齐速度,但可能影响生成质量。
算法流程
单阶段 vs 两阶段对比
| 特性 | 两阶段(SFT → DPO) | ORPO(单阶段) |
|---|---|---|
| 训练阶段 | 2 个阶段 | 1 个阶段 |
| Reference Model | 需要(DPO) | 不需要 |
| Reward Model | 需要(RLHF) | 不需要 |
| 训练时间 | 较长 | 较短 |
| 实现复杂度 | 较高 | 较低 |
训练稳定性分析
ORPO 的训练稳定性来自以下因素:
- SFT 项的锚定作用:防止模型在 OR 项的优化过程中偏离生成能力太远
- 损失函数的梯度特性:当 和 接近时,梯度较小;差距越大,惩罚越强
- 无需 KL 散度约束:避免了 reference model 分布偏移带来的训练不稳定性
伪代码实现
// ORPO 训练伪代码
// 输入: 偏好数据集 D = {(x, y+, y-)}
// 模型: policy πθ
// 超参数: λ (OR损失权重)
for each batch {(x, y+, y-)} in D do
// 1. 计算 SFT 损失(仅在偏好响应上)
loss_sft = cross_entropy(πθ(y+|x), y+)
// 2. 计算 logits
logits_pos = πθ(x, y+).logits // 对数概率
logits_neg = πθ(x, y-).logits
// 3. 计算 odds 和 log odds ratio
odds_pos = exp(logits_pos) / (1 + exp(logits_pos))
odds_neg = exp(logits_neg) / (1 + exp(logits_neg))
log_or = log(odds_pos) - log(odds_neg)
// 4. 计算 OR 损失
loss_or = -log(sigmoid(log_or))
// 5. 组合损失
loss = loss_sft + λ * loss_or
// 6. 反向传播更新
loss.backward()
optimizer.step()
end for代码实现
PyTorch 实现
#include <bits/stdc++.h>
using namespace std;
// 模拟 PyTorch 风格的 ORPO 实现
struct ORPOLoss {
double lambda; // OR 损失权重
ORPOLoss(double lambda_) : lambda(lambda_) {}
// 计算 log odds
double log_odds(double prob) {
return log(prob / (1.0 - prob + 1e-8) + 1e-8);
}
// sigmoid 函数
double sigmoid(double x) {
return 1.0 / (1.0 + exp(-x));
}
// 计算 ORPO 损失
pair<double, double> compute_loss(
const vector<double>& log_probs_pos, // log P(y+|x)
const vector<double>& log_probs_neg // log P(y-|x)
) {
// 1. SFT 损失:偏好响应的负对数似然
double loss_sft = 0.0;
for (double lp : log_probs_pos) {
loss_sft -= lp;
}
loss_sft /= log_probs_pos.size();
// 2. 计算概率(从 log probabilities)
double prob_pos = exp(log_probs_pos[0]); // 简化为标量
double prob_neg = exp(log_probs_neg[0]);
// 3. 计算 log odds ratio
double log_odds_ratio = log_odds(prob_pos) - log_odds(prob_neg);
// 4. OR 损失
double loss_or = -log(sigmoid(log_odds_ratio));
// 5. 组合损失
double total_loss = loss_sft + lambda * loss_or;
return {total_loss, loss_or};
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
// 创建 ORPO 损失计算器
ORPOLoss orpo(0.1);
// 示例:偏好响应和拒绝响应的对数概率
vector<double> log_probs_pos = {-0.2, -0.3, -0.1}; // y+ 的 log P
vector<double> log_probs_neg = {-1.5, -1.2, -1.8}; // y- 的 log P
auto [loss, or_loss] = orpo.compute_loss(log_probs_pos, log_probs_neg);
cout << "Total ORPO Loss: " << loss << endl;
cout << "OR Loss: " << or_loss << endl;
return 0;
}与 DPO 的对比
| 方面 | DPO | ORPO |
|---|---|---|
| 训练目标 | 最大化偏好概率比 | 最大化 log odds ratio |
| Reference Model | 需要 | 不需要 |
| 损失函数 | ||
| 梯度来源 | 仅来自偏好对齐 | SFT + 偏好对齐 |
训练技巧
- 热身(Warmup):建议先进行少量 SFT 热身,再切换到 ORPO
- 学习率:与标准 SFT 相似,通常使用 1e-5 到 5e-5
- 批量大小:偏好数据集通常较小,建议使用较大的批量以稳定训练
- 早停:监控验证集上的偏好准确率
实验验证
AlpacaEval 结果
ORPO 在 AlpacaEval 基准上展现出竞争力的表现:
| 方法 | AlpacaEval 胜率(相对于 GPT-4) | 训练成本 |
|---|---|---|
| SFT | ~30% | 低 |
| DPO | ~35% | 中(需要 reference) |
| ORPO | ~36% | 低(无需 reference) |
MT-Bench 对比
在 MT-Bench 多轮对话基准上,ORPO 同样表现优异:
- 与 GPT-4 对比:ORPO 训练的模型在多轮对话连贯性上达到竞争力的表现
- 计算效率:训练时间约为 DPO 的 60-70%
训练效率分析
ORPO 的一个显著优势是无需 reference model:
| 计算资源 | DPO | ORPO |
|---|---|---|
| GPU 显存 | 需要保存 | 仅需 |
| 每次迭代计算量 | 2× 前向传播 | 1× 前向传播 |
| 内存开销 | 较大 | 较小 |
这使得 ORPO 特别适合资源受限的场景,例如:
- 个人研究者使用消费级 GPU
- 大规模分布式训练中的内存优化
优缺点分析
优点
- 单阶段训练:简化训练流程,降低实现复杂度
- 无需 Reference Model:减少内存占用和计算开销
- 训练稳定:避免了 KL 散度约束带来的训练不稳定性
- 效率高:训练时间约为传统两阶段方法的 60-70%
缺点
- 理论基础相对薄弱:与 DPO 相比,ORPO 缺乏类似的理论保证(如 Bradley-Terry 模型推导)
- 超参数敏感: 的选择对最终效果有较大影响
- 适用性待验证:在更大规模模型和更复杂任务上的表现尚需更多研究
适用场景
ORPO 特别适合以下场景:
- 资源受限环境:GPU 显存有限,无法同时维护多个模型
- 快速迭代:需要快速进行对齐实验
- 简单偏好任务:偏好数据较为明确,对齐目标相对简单
对于需要严格理论保证或复杂对齐目标的场景,建议使用 DPO 或 GRPO。
相关方法
References
Footnotes
-
Hong, J., et al. (2024). “ORPO: Monolithic Preference Optimization without a Reference Model.” arXiv preprint arXiv:2403.07691. ↩