概述

ORPO(Odds Ratio Preference Optimization) 是一种创新的语言模型对齐方法,由 Hong 等人在 2024 年提出1。其核心创新在于:单阶段训练即可同时完成监督微调(Supervised Fine-Tuning, SFT)与偏好对齐,无需像传统方法那样分两阶段进行。

传统方法的局限性

在 ORPO 出现之前,主流的对齐方法(如 DPOGRPO)通常采用两阶段训练范式:

  1. 第一阶段:SFT —— 使用人类标注的优质响应进行监督微调
  2. 第二阶段: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 适合作为对齐信号?

  1. 同时惩罚两者:优势比不仅增大 ,还同时减小
  2. 概率解释直观 表示偏好响应相对于拒绝响应有更高的生成概率
  3. 避免饱和:与简单的概率比不同,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 的训练稳定性来自以下因素:

  1. SFT 项的锚定作用:防止模型在 OR 项的优化过程中偏离生成能力太远
  2. 损失函数的梯度特性:当 接近时,梯度较小;差距越大,惩罚越强
  3. 无需 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 的对比

方面DPOORPO
训练目标最大化偏好概率比最大化 log odds ratio
Reference Model需要 不需要
损失函数
梯度来源仅来自偏好对齐SFT + 偏好对齐

训练技巧

  1. 热身(Warmup):建议先进行少量 SFT 热身,再切换到 ORPO
  2. 学习率:与标准 SFT 相似,通常使用 1e-5 到 5e-5
  3. 批量大小:偏好数据集通常较小,建议使用较大的批量以稳定训练
  4. 早停:监控验证集上的偏好准确率

实验验证

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

计算资源DPOORPO
GPU 显存需要保存 仅需
每次迭代计算量2× 前向传播1× 前向传播
内存开销较大较小

这使得 ORPO 特别适合资源受限的场景,例如:

  • 个人研究者使用消费级 GPU
  • 大规模分布式训练中的内存优化

优缺点分析

优点

  1. 单阶段训练:简化训练流程,降低实现复杂度
  2. 无需 Reference Model:减少内存占用和计算开销
  3. 训练稳定:避免了 KL 散度约束带来的训练不稳定性
  4. 效率高:训练时间约为传统两阶段方法的 60-70%

缺点

  1. 理论基础相对薄弱:与 DPO 相比,ORPO 缺乏类似的理论保证(如 Bradley-Terry 模型推导)
  2. 超参数敏感 的选择对最终效果有较大影响
  3. 适用性待验证:在更大规模模型和更复杂任务上的表现尚需更多研究

适用场景

ORPO 特别适合以下场景:

  • 资源受限环境:GPU 显存有限,无法同时维护多个模型
  • 快速迭代:需要快速进行对齐实验
  • 简单偏好任务:偏好数据较为明确,对齐目标相对简单

对于需要严格理论保证复杂对齐目标的场景,建议使用 DPOGRPO

相关方法

  • DPO —— 直接偏好优化,ORPO 的主要对比方法
  • GRPO —— 基于组相对策略的优化
  • KTO —— 基于核截断优势比的人类感知优化
  • LLM 训练流程 —— 了解对齐在整体训练流程中的位置

References

Footnotes

  1. Hong, J., et al. (2024). “ORPO: Monolithic Preference Optimization without a Reference Model.” arXiv preprint arXiv:2403.07691.