TRL 文档

训练常见问题解答

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

训练常见问题解答

我应该关注哪些指标?

当执行经典的监督式语言模型微调时,损失(尤其是验证损失)可以很好地指示训练进度。然而,在强化学习(RL)中,损失对于模型性能的指示性降低,并且其值可能会波动,而实际性能却在提高。

为了解决这个问题,我们建议首先关注两个关键指标

平均奖励:首要目标是最大化模型在 RL 训练期间获得的奖励。目标 KL 散度:KL 散度(Kullback-Leibler 散度)衡量两个概率分布之间的差异。在 RL 训练的上下文中,我们使用它来量化当前模型和参考模型之间的差异。理想情况下,我们希望将 KL 散度保持在 0 到 10 之间,以确保模型生成的文本与参考模型生成的文本保持接近。

然而,还有更多指标可用于调试,请查看日志部分

我们为什么要使用参考模型,以及 KL 散度的目的是什么?

当训练 RL 模型时,仅针对奖励进行优化可能会导致意外行为,即模型以不符合良好语言生成的方式利用环境。在 RLHF 的情况下,我们使用奖励模型来预测生成的文本是否受到人类的高度评价。

然而,针对奖励模型进行优化的 RL 模型可能会学习到产生高奖励但不代表良好语言的模式。这可能导致极端情况,即模型生成带有过多感叹号或表情符号的文本以最大化奖励。在某些最坏的情况下,模型可能会生成与自然语言完全无关的模式,但仍然获得高奖励,类似于对抗性攻击。

图:来自 https://huggingface.co/papers/1909.08593 的没有 KL 惩罚的样本。

为了解决这个问题,我们根据当前模型和参考模型之间的 KL 散度,在奖励函数中添加一个惩罚项。通过这样做,我们鼓励模型保持接近参考模型生成的内容。

负 KL 散度有什么问题?

如果你通过纯粹从模型分布中采样来生成文本,那么通常情况下一切都很好。但是当你使用 generate 方法时,有一些需要注意的地方,因为它并不总是纯粹采样,具体取决于设置,这可能会导致 KL 散度变为负数。本质上,当活动模型达到 log_p_token_active < log_p_token_ref 时,我们得到负 KL 散度。这可能发生在以下几种情况中

  • top-k 采样:模型可以平滑概率分布,导致 top-k 标记的概率小于参考模型的标记,但它们仍然被选中
  • min_length:这会忽略 EOS 标记,直到达到 min_length。因此,模型可以为 EOS 标记分配非常低的对数概率,并为所有其他标记分配非常高的概率,直到达到 min_length。

这些只是一些例子。为什么负 KL 是个问题?总奖励 R 的计算公式为 R = r - beta * KL,因此如果模型能够学会如何将 KL 散度驱动为负数,它实际上会获得正奖励。在许多情况下,利用生成中的这种错误可能比实际学习奖励函数容易得多。此外,KL 可以变得任意小,因此实际奖励可能相对于它来说非常小。

那么,你应该如何为 PPO 训练生成文本呢?让我们来看看!

如何为训练生成文本?

为了避免上面描述的 KL 问题,我们建议使用以下设置

generation_kwargs = {
    "min_length": -1, # don't ignore the EOS token (see above)
    "top_k": 0.0, # no top-k sampling
    "top_p": 1.0, # no nucleus sampling
    "do_sample": True, # yes, we want to sample
    "pad_token_id": tokenizer.eos_token_id, # most decoder models don't have a padding token - use EOS token instead
    "max_new_tokens": 32, # specify how many tokens you want to generate at most
}

使用这些设置,我们通常不会遇到任何问题。你也可以尝试其他设置,但如果你遇到负 KL 散度的问题,请尝试回到这些设置,看看问题是否仍然存在。

如何调试你自己的用例?

由于 RL 管道的复杂性,调试它可能具有挑战性。以下是一些提示和建议,以使过程更轻松

  • 从一个可用的示例开始:从 trl 存储库中的一个可用示例开始,逐步修改它以适应你的特定用例。一次更改所有内容可能会使你难以确定潜在问题的来源。例如,你可以从替换示例中的模型开始,一旦你找到了最佳超参数,就尝试切换到你的数据集和奖励模型。如果你一次更改所有内容,你将不知道潜在问题来自哪里。
  • 从小处着手,稍后扩展:训练大型模型可能非常缓慢,并且可能需要几个小时或几天才能看到任何改进。对于调试来说,这不是一个方便的时间尺度,因此在开发阶段尝试使用小型模型变体,并在工作正常后进行扩展。话虽如此,你有时必须小心,因为小型模型可能也没有能力解决复杂的任务。
  • 从简单开始:尝试从一个最简单的示例开始,并在此基础上构建复杂性。你的用例可能需要,例如,一个由许多不同奖励组成的复杂奖励函数 - 尝试首先使用一个信号,看看你是否可以优化它,然后在之后添加更多复杂性。
  • 检查生成结果:检查模型生成的内容始终是一个好主意。可能是你的后处理或提示中存在错误。由于设置不当,你可能会过早地截断生成结果。这些事情在指标上很难看到,但如果你查看生成结果,则非常明显。
  • 检查奖励模型:如果你的奖励没有随着时间推移而提高,则奖励模型可能存在问题。你可以查看极端情况,看看它是否按预期工作:例如,在情感案例中,你可以检查简单的正面和负面示例是否真的获得了不同的奖励。你可以查看数据集的分布。最后,奖励可能由模型无法影响的查询主导,因此你可能需要对其进行归一化(例如,查询+响应的奖励减去查询的奖励)。

这些只是我们发现有用的几个技巧 - 如果你有更多有用的技巧,请随时打开 PR 来添加它们!

< > 在 GitHub 上更新