RLHF 高级技术 #
概述 #
随着 RLHF 技术的发展,研究者们提出了多种变体和改进方法,旨在解决传统 RLHF 的局限性,提高训练效率和模型效果。
text
┌─────────────────────────────────────────────────────────────┐
│ RLHF 技术演进 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 传统方法: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ RLHF (2022) → 奖励模型 + PPO │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 简化方法: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ DPO (2023) → 直接偏好优化 │ │
│ │ IPO (2023) → 恒等偏好优化 │ │
│ │ KTO (2023) → 卡尼曼-特沃斯基优化 │ │
│ │ ORPO (2024) → 几率比偏好优化 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 扩展方法: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Constitutional AI → 规则驱动对齐 │ │
│ │ RLAIF → AI 反馈强化学习 │ │
│ │ Iterative DPO → 迭代式 DPO │ │
│ │ Multi-objective RLHF → 多目标 RLHF │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
IPO(Identity Preference Optimization) #
原理 #
IPO 使用恒等函数替代 DPO 中的 sigmoid 函数,提供更稳定的训练:
text
DPO 损失:
────────────────────────
L_DPO = -log σ(β * (log_ratio_chosen - log_ratio_rejected))
IPO 损失:
────────────────────────
L_IPO = (log_ratio_chosen - log_ratio_rejected - 1/(2β))²
优势:
────────────────────────
├── 避免了 sigmoid 的饱和问题
├── 梯度更稳定
├── 对超参数更鲁棒
└── 理论上更优雅
实现 #
python
import torch
import torch.nn.functional as F
class IPOTrainer:
def __init__(self, model, ref_model, beta=0.1, learning_rate=1e-6):
self.model = model
self.ref_model = ref_model
self.beta = beta
self.optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate)
def compute_ipo_loss(self, log_probs_chosen, log_probs_rejected,
ref_log_probs_chosen, ref_log_probs_rejected):
log_ratio_chosen = log_probs_chosen - ref_log_probs_chosen
log_ratio_rejected = log_probs_rejected - ref_log_probs_rejected
diff = log_ratio_chosen - log_ratio_rejected
loss = (diff - 1 / (2 * self.beta)) ** 2
return loss.mean()
def train_step(self, batch):
log_probs_chosen = self.get_log_probs(
self.model, batch["input_ids_chosen"], batch["attention_mask_chosen"]
)
log_probs_rejected = self.get_log_probs(
self.model, batch["input_ids_rejected"], batch["attention_mask_rejected"]
)
with torch.no_grad():
ref_log_probs_chosen = self.get_log_probs(
self.ref_model, batch["input_ids_chosen"], batch["attention_mask_chosen"]
)
ref_log_probs_rejected = self.get_log_probs(
self.ref_model, batch["input_ids_rejected"], batch["attention_mask_rejected"]
)
loss = self.compute_ipo_loss(
log_probs_chosen, log_probs_rejected,
ref_log_probs_chosen, ref_log_probs_rejected
)
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
return {"loss": loss.item()}
KTO(Kahneman-Tversky Optimization) #
原理 #
KTO 基于前景理论(Prospect Theory),不需要成对的偏好数据:
text
前景理论洞察:
────────────────────────
├── 人们对损失比收益更敏感
├── 损失厌恶:损失的痛苦大于同等收益的快乐
└── 可以单独处理好/坏样本
KTO 损失:
────────────────────────
对于好样本(desirable):
L = λ_D * (1 - σ(β * log_ratio))
对于坏样本(undesirable):
L = λ_U * σ(β * log_ratio)
其中 λ_D 和 λ_U 是权重系数
实现 #
python
class KTOTrainer:
def __init__(self, model, ref_model, beta=0.1,
desirable_weight=1.0, undesirable_weight=0.5):
self.model = model
self.ref_model = ref_model
self.beta = beta
self.desirable_weight = desirable_weight
self.undesirable_weight = undesirable_weight
def compute_kto_loss(self, log_probs, ref_log_probs, is_desirable):
log_ratio = log_probs - ref_log_probs
reward = self.beta * log_ratio
if is_desirable:
loss = self.desirable_weight * (1 - torch.sigmoid(reward))
else:
loss = self.undesirable_weight * torch.sigmoid(reward)
return loss.mean()
def train_step(self, batch):
log_probs = self.get_log_probs(
self.model, batch["input_ids"], batch["attention_mask"]
)
with torch.no_grad():
ref_log_probs = self.get_log_probs(
self.ref_model, batch["input_ids"], batch["attention_mask"]
)
is_desirable = batch["is_desirable"]
loss = self.compute_kto_loss(log_probs, ref_log_probs, is_desirable)
return {"loss": loss.item()}
ORPO(Odds Ratio Preference Optimization) #
原理 #
ORPO 将 SFT 和偏好学习统一到一个目标函数中:
text
ORPO 损失:
────────────────────────
L_ORPO = L_SFT + λ * L_preference
其中:
├── L_SFT = -log π_θ(y_w|x) (标准语言模型损失)
├── L_preference = -log σ(log_odds_ratio)
├── log_odds_ratio = log(π_θ(y_w|x) / π_θ(y_l|x))
└── λ 是偏好损失权重
优势:
────────────────────────
├── 无需参考模型
├── SFT 和对齐同时进行
├── 训练更简单
└── 效果与 DPO 相当
实现 #
python
class ORPOTrainer:
def __init__(self, model, lambda_orpo=0.1, learning_rate=1e-6):
self.model = model
self.lambda_orpo = lambda_orpo
self.optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate)
def compute_orpo_loss(self, log_probs_chosen, log_probs_rejected):
sft_loss = -log_probs_chosen.mean()
log_odds_ratio = log_probs_chosen - log_probs_rejected
preference_loss = -torch.log(torch.sigmoid(log_odds_ratio)).mean()
total_loss = sft_loss + self.lambda_orpo * preference_loss
return total_loss, sft_loss, preference_loss
def train_step(self, batch):
log_probs_chosen = self.get_log_probs(
self.model, batch["input_ids_chosen"], batch["attention_mask_chosen"]
)
log_probs_rejected = self.get_log_probs(
self.model, batch["input_ids_rejected"], batch["attention_mask_rejected"]
)
total_loss, sft_loss, preference_loss = self.compute_orpo_loss(
log_probs_chosen, log_probs_rejected
)
self.optimizer.zero_grad()
total_loss.backward()
self.optimizer.step()
return {
"total_loss": total_loss.item(),
"sft_loss": sft_loss.item(),
"preference_loss": preference_loss.item(),
}
Constitutional AI #
原理 #
Constitutional AI 使用规则(宪法)来指导模型对齐,减少对人工标注的依赖:
text
┌─────────────────────────────────────────────────────────────┐
│ Constitutional AI 流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 阶段 1:监督学习(Critique -> Revision) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. 模型生成初始回复 │ │
│ │ 2. 根据"宪法"规则进行自我批评 │ │
│ │ 3. 根据批评修改回复 │ │
│ │ 4. 使用修改后的数据进行 SFT │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 阶段 2:RLHF(使用 AI 反馈) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. 使用 AI 根据宪法评估回复 │ │
│ │ 2. 生成偏好数据 │ │
│ │ 3. 训练奖励模型 │ │
│ │ 4. 使用 RL 优化模型 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 宪法示例: │
│ ├── "选择最无害的回复" │
│ ├── "选择最乐于助人的回复" │
│ ├── "避免生成有害、非法或不道德的内容" │
│ └── "保持诚实,不要编造信息" │
│ │
└─────────────────────────────────────────────────────────────┘
实现 #
python
class ConstitutionalAI:
def __init__(self, model, tokenizer, constitution):
self.model = model
self.tokenizer = tokenizer
self.constitution = constitution
def generate_initial_response(self, prompt):
inputs = self.tokenizer(prompt, return_tensors="pt").to(self.model.device)
outputs = self.model.generate(
**inputs,
max_new_tokens=256,
temperature=0.7,
)
return self.tokenizer.decode(outputs[0], skip_special_tokens=True)
def critique_response(self, prompt, response, principle):
critique_prompt = f"""
原始问题:{prompt}
模型回复:{response}
请根据以下原则批评这个回复:
{principle}
请指出回复中的问题:
"""
return self.generate_initial_response(critique_prompt)
def revise_response(self, prompt, response, critique):
revision_prompt = f"""
原始问题:{prompt}
原始回复:{response}
批评意见:{critique}
请根据批评意见修改回复:
"""
return self.generate_initial_response(revision_prompt)
def generate_revised_data(self, prompts):
revised_data = []
for prompt in prompts:
initial_response = self.generate_initial_response(prompt)
for principle in self.constitution:
critique = self.critique_response(
prompt, initial_response, principle
)
revised = self.revise_response(
prompt, initial_response, critique
)
revised_data.append({
"prompt": prompt,
"response": revised,
})
return revised_data
RLAIF(RL from AI Feedback) #
原理 #
RLAIF 使用 AI 模型(而非人类)提供偏好反馈:
text
RLAIF 流程:
────────────────────────
1. 使用 SFT 模型生成多个回复
2. 使用 AI 评估器(如 GPT-4)对回复排序
3. 使用 AI 偏好数据训练奖励模型
4. 使用 PPO 优化策略模型
优势:
────────────────────────
├── 降低人工标注成本
├── 可扩展性强
├── 标注一致性高
└── 可以利用更强的模型
挑战:
────────────────────────
├── AI 评估器可能有偏见
├── 可能继承评估器的缺陷
├── 需要高质量的评估器
└── 评估成本仍然存在
实现 #
python
class RLAIF:
def __init__(self, policy_model, evaluator_model, tokenizer):
self.policy_model = policy_model
self.evaluator_model = evaluator_model
self.tokenizer = tokenizer
def generate_responses(self, prompt, num_responses=4):
inputs = self.tokenizer(prompt, return_tensors="pt").to(self.policy_model.device)
responses = []
for _ in range(num_responses):
outputs = self.policy_model.generate(
**inputs,
max_new_tokens=256,
temperature=1.0,
do_sample=True,
)
response = self.tokenizer.decode(
outputs[0][inputs["input_ids"].shape[1]:],
skip_special_tokens=True
)
responses.append(response)
return responses
def evaluate_with_ai(self, prompt, responses):
eval_prompt = f"""
问题:{prompt}
请对以下回复按质量从高到低排序(1最好,{len(responses)}最差):
"""
for i, response in enumerate(responses):
eval_prompt += f"回复 {i+1}: {response}\n\n"
eval_prompt += "请只输出排序结果,格式如:3,1,4,2"
inputs = self.tokenizer(
eval_prompt, return_tensors="pt"
).to(self.evaluator_model.device)
outputs = self.evaluator_model.generate(
**inputs,
max_new_tokens=50,
temperature=0.0,
)
ranking_text = self.tokenizer.decode(
outputs[0][inputs["input_ids"].shape[1]:],
skip_special_tokens=True
)
return self._parse_ranking(ranking_text, len(responses))
def _parse_ranking(self, ranking_text, num_responses):
import re
numbers = re.findall(r'\d+', ranking_text)
ranking = [int(n) - 1 for n in numbers if 1 <= int(n) <= num_responses]
return ranking
def create_preference_pairs(self, prompt, responses, ranking):
pairs = []
for i in range(len(ranking)):
for j in range(i + 1, len(ranking)):
chosen_idx = ranking[i]
rejected_idx = ranking[j]
pairs.append({
"prompt": prompt,
"chosen": responses[chosen_idx],
"rejected": responses[rejected_idx],
})
return pairs
多目标 RLHF #
原理 #
多目标 RLHF 同时优化多个目标(如有用性、安全性、真实性):
text
多目标优化:
────────────────────────
总奖励 = Σ w_i * r_i(x, y)
其中:
├── r_i 是第 i 个目标的奖励函数
├── w_i 是第 i 个目标的权重
└── 需要平衡不同目标之间的权衡
挑战:
────────────────────────
├── 目标之间可能冲突
├── 权重选择困难
├── 需要多维奖励模型
└── 评估更复杂
实现 #
python
class MultiObjectiveRLHF:
def __init__(self, policy_model, reward_models, weights):
self.policy_model = policy_model
self.reward_models = reward_models
self.weights = weights
def compute_multi_objective_reward(self, input_ids, attention_mask):
total_reward = 0
for reward_model, weight in zip(self.reward_models, self.weights):
reward = reward_model(input_ids, attention_mask)
total_reward += weight * reward
return total_reward
def compute_pareto_front(self, rewards_list):
pareto_front = []
for i, rewards in enumerate(rewards_list):
is_dominated = False
for j, other_rewards in enumerate(rewards_list):
if i != j:
if all(r <= o for r, o in zip(rewards, other_rewards)):
if any(r < o for r, o in zip(rewards, other_rewards)):
is_dominated = True
break
if not is_dominated:
pareto_front.append(i)
return pareto_front
迭代式 RLHF #
原理 #
迭代式 RLHF 通过多轮训练持续改进模型:
text
迭代流程:
────────────────────────
第 1 轮:
├── 使用初始模型生成回复
├── 收集偏好数据
├── 训练奖励模型
└── 使用 PPO/DPO 优化
第 2 轮:
├── 使用优化后的模型生成回复
├── 收集新的偏好数据
├── 更新奖励模型
└── 继续优化
...重复多轮
优势:
────────────────────────
├── 持续改进
├── 适应新的偏好
├── 提高数据效率
└── 更好的最终效果
实现 #
python
class IterativeRLHF:
def __init__(self, base_model, tokenizer, num_iterations=3):
self.base_model = base_model
self.tokenizer = tokenizer
self.num_iterations = num_iterations
self.current_model = base_model
def run_iteration(self, iteration, prompts):
print(f"Running iteration {iteration + 1}")
responses = self.generate_responses(prompts)
preference_data = self.collect_preferences(prompts, responses)
reward_model = self.train_reward_model(preference_data)
self.current_model = self.train_policy(
self.current_model, reward_model, prompts
)
return self.current_model
def run(self, prompts):
for i in range(self.num_iterations):
self.current_model = self.run_iteration(i, prompts)
return self.current_model
方法对比总结 #
text
┌─────────────────────────────────────────────────────────────┐
│ RLHF 变体方法对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 方法 │ 复杂度 │ 效果 │ 数据需求 │ 适用场景 │
│ ───────────────────────────────────────────────────────── │
│ RLHF │ 高 │ 优秀 │ 成对偏好 │ 大规模生产 │
│ DPO │ 低 │ 良好 │ 成对偏好 │ 快速原型 │
│ IPO │ 低 │ 良好 │ 成对偏好 │ 稳定训练 │
│ KTO │ 低 │ 良好 │ 单样本 │ 数据不完整 │
│ ORPO │ 最低 │ 良好 │ 成对偏好 │ 无参考模型 │
│ CAI │ 中 │ 良好 │ 规则 │ 减少人工 │
│ RLAIF │ 中 │ 良好 │ AI偏好 │ 成本敏感 │
│ │
└─────────────────────────────────────────────────────────────┘
下一步 #
现在你已经了解了 RLHF 的高级技术,接下来学习 实践指南,获取实际项目中的最佳实践经验!
最后更新:2026-04-05