OnlineDPO in TRL OnlineDPO 的核心组件 Policy Model(策略模型):被训练的模型 Reference Model(参考模型):固定的基准模型,通常是 Policy Model 的冻结副本 评估组件 (二选一): Reward Model(奖励模型):评分模型,为每个生成结果打分 Judge(判断器):比较器,比较两个生成结果并选出更好的一个 计算核心公式 OnlineDPO的核心是最大化被选中回复相对于被拒绝回复的概率比。
Policy Model(策略模型):被训练的模型
Reference Model(参考模型):固定的基准模型,通常是 Policy Model 的冻结副本
评估组件
(二选一):
OnlineDPO的核心是最大化被选中回复相对于被拒绝回复的概率比。其损失函数为:
Sigmoid损失:
\mathcal{L}{\text{DPO}}(\theta) = -\mathbb{E}{(x,y_w,y_l) \sim \mathcal{D}} \left[ \log \sigma \left( \beta \cdot \left( \log \frac{p\theta(y_w|x)}{p{\text{ref}}(y_w|x)} - \log \frac{p\theta(y_l|x)}{p{\text{ref}}(y_l|x)} \right) \right) \right]
IPO损失:
\mathcal{L}{\text{IPO}}(\theta) = \mathbb{E}{(x,y_w,y_l) \sim \mathcal{D}} \left[ \left( \log \frac{p\theta(y_w|x)/p{\text{ref}}(y_w|x)}{p\theta(y_l|x)/p{\text{ref}}(y_l|x)} - \frac{1}{2\beta} \right)^2 \right]
其中:
# 为每个提示生成两个不同的回复 prompts = inputs["prompt"] # 形状: [batch_size] batch_size = len(prompts) # 使用vLLM或标准生成 if use_vllm: prompt_ids, prompt_mask, completion_ids, completion_mask = _generate_vllm(model, prompts) else: prompt_ids, prompt_mask, completion_ids, completion_mask = _generate(model, prompts)
这一阶段:
# 计算策略模型的对数概率 logprobs = _forward(model, prompt_ids, prompt_mask, completion_ids, completion_mask) # 计算参考模型的对数概率(无梯度) with torch.no_grad(): if ref_model is not None: ref_logprobs = _forward(ref_model, prompt_ids, prompt_mask, completion_ids, completion_mask) else: # PEFT情况,只需禁用adapter with model.disable_adapter(): ref_logprobs = _forward(model, prompt_ids, prompt_mask, completion_ids, completion_mask)
这一阶段:
# 解码生成的回复 completions = processing_class.batch_decode(completion_ids, skip_special_tokens=True) if judge is not None: # 使用判断器进行对比评估 ranks = judge.judge(prompts, list(zip(completions[:batch_size], completions[batch_size:]))) mask = torch.tensor([rank == 0 for rank in ranks], device=device) else: # 使用奖励模型进行评分 scores = reward_model(prompt_completion_ids).scores # 处理未包含EOS的回复(可选降低它们的分数) if missing_eos_penalty is not None: scores[~contain_eos_token] -= missing_eos_penalty # 分割分数并比较 first_half, second_half = scores.split(batch_size) mask = first_half >= second_half
这一阶段:
# 获取被选中和被拒绝回复的索引 batch_range = torch.arange(batch_size, device=device) chosen_indices = batch_range + (~mask * batch_size) rejected_indices = batch_range + (mask * batch_size) # 获取被选中和被拒绝回复的对数概率 chosen_logprobs_sum, rejected_logprobs_sum = torch.split(cr_logprobs_sum, batch_size) chosen_ref_logprobs_sum, rejected_ref_logprobs_sum = torch.split(cr_ref_logprobs_sum, batch_size) # 计算对数概率比值 pi_logratios = chosen_logprobs_sum - rejected_logprobs_sum ref_logratios = chosen_ref_logprobs_sum - rejected_ref_logprobs_sum # 计算DPO损失所需的logits logits = pi_logratios - ref_logratios # 根据指定的损失类型计算损失 if loss_type == "sigmoid": losses = -F.logsigmoid(beta * logits) elif loss_type == "ipo": losses = (logits - 1 / (2 * beta)) ** 2 loss = losses.mean()
这一阶段:
# 执行反向传播 if n_gpu > 1: loss = loss.mean() # 多GPU上平均损失 accelerator.backward(loss, **kwargs) # 返回损失 return loss.detach() / args.gradient_accumulation_steps
这一阶段: