TRL documentation

RLOO 训练器

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v1.18.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

RLOO 训练器

model badge

概述

TRL 支持 RLOO Trainer,用于训练语言模型,其方法如 Arash Ahmadian、Chris Cremer、Matthias Gallé、Marzieh Fadaee、Julia Kreutzer、Ahmet Üstün 和 Sara Hooker 在论文 Back to Basics: Revisiting REINFORCE Style Optimization for Learning from Human Feedback in LLMs 中所述。

该论文的摘要如下:

以基于人类反馈的强化学习(Reinforcement Learning from Human Feedback, RLHF)形式的人工智能对齐,正日益被视为高性能大语言模型的关键要素。近期文献将近端策略优化(Proximal Policy Optimization, PPO)定位为 RLHF 中强化学习部分的标准方法。然而,PPO 涉及高昂的计算成本以及对超参数调优的敏感性。我们认为,促使 PPO 开发的许多动机原则在 RLHF 中并非实际的关注点,并主张采用一种计算成本更低且能保持甚至提升性能的方法。我们在强化学习的背景下重新审视了基于人类偏好的对齐问题。以简洁性为指导原则,我们证明在 RLHF 场景中 PPO 的许多组件是不必要的,且远为简单的 REINFORCE 风格优化变体在性能上优于 PPO 以及新提出的“无 RL”方法(如 DPO 和 RAFT)。我们的研究表明,针对大语言模型对齐特性的精心适配,能够以较低的成本从在线强化学习优化中获益。

此训练后方法由 Costa Huang 贡献,随后由 Shirin Yamani 进行了重构。

快速入门

本示例演示了如何使用 RLOO 方法训练模型。我们使用 DeepMath-103K 数据集 中的提示词来训练 Qwen 0.5B Instruct 模型。您可以在这里查看该数据集的数据:

以下是用于训练模型的脚本。accuracy_reward 需要 math-verify,您可以通过 pip install trl[math_verify] 进行安装。

# train_rloo.py
from datasets import load_dataset
from trl import RLOOTrainer
from trl.rewards import accuracy_reward

dataset = load_dataset("trl-lib/DeepMath-103K", split="train")

trainer = RLOOTrainer(
    model="Qwen/Qwen2-0.5B-Instruct",
    reward_funcs=accuracy_reward,
    train_dataset=dataset,
)
trainer.train()

使用以下命令执行该脚本:

torchrun --nproc_per_node 8 train_rloo.py

深入了解 RLOO 方法

RLOO 是一种在线学习算法,这意味着它通过利用训练过程中由已训练模型自身生成的数据来迭代地提升性能。RLOO 目标背后的直觉是最大化生成补全的优势,同时确保模型保持在参考策略附近。为了理解 RLOO 的工作原理,可以将其分解为四个主要步骤:生成补全、计算优势、估计 KL 散度以及计算损失。

RLOO

生成补全

在每个训练步骤中,我们采样一批提示词,并为每个提示词生成一组 G G 补全(记为 oi o_i ).

计算奖励

在 RLOO 中,奖励由两部分组成:奖励模型(或奖励函数)提供的奖励,以及一个 KL 惩罚项,用于阻止策略偏离固定的参考策略过远

  1. 对于每个 G G 生成的序列 oi=(oi,1,…,oi,T) o_i = (o_{i,1}, \dots, o_{i,T}) 在查询 q q , 的条件下,我们使用奖励模型计算一个标量奖励 R(oi,q) R(o_i, q) .
  2. 同时,我们估计当前策略 πθ \pi_\theta 与固定的参考策略 πref \pi_{\text{ref}} 在序列上的 KL 散度。序列 oi o_i 的 KL 估计值为: DKL ⁣[πθ∥πref]=∑t=1Tlog⁡πθ(oi,t∣q,oi,<t)πref(oi,t∣q,oi,<t). \mathbb{D}_{\mathrm{KL}}\!\left[\pi_\theta\|\pi_{\mathrm{ref}}\right] = \sum_{t=1}^T \log \frac{\pi_\theta(o_{i,t} \mid q, o_{i,<t})}{\pi_{\mathrm{ref}}(o_{i,t} \mid q, o_{i,<t})}.

分配给序列的最终奖励 oi o_i 为: ri=R(oi,q)−β DKL ⁣[πθ∥πref], r_i = R(o_i, q) - \beta \, \mathbb{D}_{\mathrm{KL}}\!\left[\pi_\theta \|\pi_{\mathrm{ref}}\right],

其中 β>0 \beta > 0 控制 KL 惩罚的强度。

在纯在线设置(num_iterations = 1,默认值)中,数据由当前策略生成。在这种情况下,KL 惩罚直接使用当前策略计算。

在更一般的情况下(例如,每个批次执行多个梯度步骤),数据实际上是由较早的快照生成的 πold \pi_{\text{old}} . 为了使惩罚项与采样分布保持一致,KL 散度是相对于该策略定义的:

DKL ⁣[πold ∥ πref]. \mathbb{D}_{\mathrm{KL}}\!\left[\pi_{\text{old}} \,\|\, \pi_{\text{ref}}\right].

等价地,对于采样的序列 o o , 蒙特卡洛估计为:

DKL ⁣[πold∥πref]=∑t=1Tlog⁡πold(oi,t∣q,oi,<t)πref(oi,t∣q,oi,<t). \mathbb{D}_{\mathrm{KL}}\!\left[\pi_{\text{old}} \|\pi_{\mathrm{ref}}\right] = \sum_{t=1}^T \log \frac{\pi_{\text{old}}(o_{i,t} \mid q, o_{i,<t})}{\pi_{\mathrm{ref}}(o_{i,t} \mid q, o_{i,<t})}.

计算优势

在计算完每个补全的奖励后,我们计算一个基线,即同一批次中除当前样本外所有其他样本的平均奖励。该基线用于降低策略梯度估计的方差。随后,每个补全的优势值通过其自身奖励与该留一法基线之差获得。

形式上,对于一批 G 个补全,补全的基线为:bi=1G−1∑j≠irj b_i = \frac{1}{G-1} \sum_{j \neq i} r_j

然后,每个补全的优势值计算为其奖励与基线之间的差值: Ai=ri−bi A_i = r_i - b_i

计算损失

REINFORCE 损失函数定义如下: LRLOO(θ)=−1G∑i=1GA^i log⁡πθ(oi∣q) \mathcal{L}_{\text{RLOO}}(\theta) = - \frac{1}{G} \sum_{i=1}^G \hat{A}_i \, \log \pi_\theta(o_i \mid q)

在实践中,对同一批次执行多次梯度更新会使动作相对于当前参数实际上变为离策略(off-policy)。为了纠正这一点,我们引入了重要性采样比率。为了防止在采样和梯度更新之间策略发生变化时出现过大的更新,我们对这个比率进行裁剪: LRLOO(θ)=−1G∑i=1Gmin⁡(πθ(oi∣q)πθold(oi∣q)A^i, clip(πθ(oi∣q)πθold(oi∣q),1−ϵ,1+ϵ)A^i) \mathcal{L}_{\text{RLOO}}(\theta) = - \frac{1}{G} \sum_{i=1}^G \min \left( \frac{\pi_\theta(o_i \mid q)}{\pi_{\theta_\text{old}}(o_i \mid q)} \hat{A}_i, \, \text{clip}\left(\frac{\pi_\theta(o_i \mid q)}{\pi_{\theta_\text{old}}(o_i \mid q)}, 1-\epsilon, 1+\epsilon\right) \hat{A}_i \right)

在完全在线的单步设置(默认)中, πθ(oi∣q)πθold(oi∣q)=1 \frac{\pi_\theta(o_i \mid q)}{\pi_{\theta_\text{old}}(o_i \mid q)} = 1 这简化为标准的 REINFORCE。

与 GRPO 不同,RLOO 不会通过 KL 项反向传播梯度。在这里,KL 纯粹用于奖励整形:它在 no_grad 下计算,并折叠到标量奖励 ri r_i , 中,该奖励成为分离的 advantage A^i \hat{A}_i .

换句话说,梯度进入策略的唯一路径是重要性比率项 πθ(oi∣q)πθold(oi∣q) \frac{\pi_\theta(o_i \mid q)}{\pi_{\theta_\text{old}}(o_i \mid q)} , 而 KL 项不会反向传播到策略中。

相比之下, GRPO 在其目标函数中引入了一个显式的、可微的 KL 项,该 KL 项基于当前策略 πθ \pi_\theta , 实时计算,因此其梯度会同时流经比率项和 KL 惩罚项。

记录的指标

在训练和评估过程中,我们记录以下指标:

  • num_tokens:迄今为止处理的 token 总数,包括提示词和补全部分。
  • step_time:每个训练步骤(包括生成)的平均耗时(以秒为单位)。
  • completions/mean_length:生成的补全的平均长度。
  • completions/min_length:生成补全的最小长度。
  • completions/max_length:生成的补全的最大长度。
  • completions/mean_terminated_length:以 EOS 终止的生成补全的平均长度。
  • completions/min_terminated_length:以 EOS 终止的生成补全的最小长度。
  • completions/max_terminated_length:以 EOS 终止的生成补全的最大长度。
  • completions/clipped_ratio:被截断(裁剪)的补全比例。
  • rewards/{reward_func_name}/mean:特定奖励函数的平均奖励。
  • rewards/{reward_func_name}/std:特定奖励函数的奖励标准差。
  • reward:对各个函数的奖励求和(按 reward_weights 加权)后得到的总体平均奖励。
  • reward_std:跨函数求和奖励的标准差(按 reward_weights 加权),在完整批次上计算。
  • frac_reward_zero_std:生成批次中奖励标准差为零的样本比例,表明该提示词下的多样性较低(所有回答均正确或均错误)。
  • entropy:生成的补全中 token 预测的平均熵。(如果 mask_truncated_completions=True,则排除被掩码序列的 token。)
  • aux_loss:混合专家(Mixture-of-Experts)模型的负载均衡辅助损失,在乘以 router_aux_loss_coef 并加到总损失之前。仅当模型为 MoE 模型且 router_aux_loss_coef 非零时记录。
  • kl:模型与参考模型之间的平均 KL 散度,基于生成的补全内容计算。仅在 beta 非零时记录。
  • clip_ratio/region_mean: RLOO 目标被裁剪以保持在信任区域内的序列概率比率: clip(ri(θ),1−ϵlow,1+ϵhigh) ,ri(θ)=πθ(oi∣q)πθold(oi∣q) \text{clip}\left( r_{i}(\theta), 1 - \epsilon_\mathrm{low}, 1 + \epsilon_\mathrm{high} \right)\,, \quad r_{i}(\theta) = \frac{\pi_\theta(o_{i} \mid q)}{\pi_{\theta_{\text{old}}}(o_{i} \mid q)} . 较高的值意味着更多的样本被裁剪,从而限制了策略 πθ\pi_\theta 可以变化的幅度。
  • clip_ratio/low_mean: 信任区域下界被裁剪的序列概率的平均比率: ri,t(θ)<1−ϵlowr_{i,t}(\theta) < 1 - \epsilon_\mathrm{low}.
  • clip_ratio/low_min: 信任域下界上每个 completion 的最小裁剪指示器: ri,t(θ)<1−ϵlowr_{i,t}(\theta) < 1 - \epsilon_\mathrm{low}.
  • clip_ratio/high_mean: 信任区域上界被裁剪的序列概率的平均比率: ri,t(θ)>1+ϵhighr_{i,t}(\theta) > 1 + \epsilon_\mathrm{high}.
  • clip_ratio/high_max: 信任域上界上最大的逐完成裁剪指示器: ri,t(θ)>1+ϵhighr_{i,t}(\theta) > 1 + \epsilon_\mathrm{high}.

自定义

使用 vLLM 驱动的生成来加速训练

在使用在线方法训练时,生成过程往往是主要的性能瓶颈。为了加速生成,你可以使用 vLLM,这是一个面向 LLM 的高吞吐量、低延迟推理引擎。要启用它,首先使用以下命令安装该包

pip install trl[vllm]

我们支持在训练期间使用 vLLM 的两种方式:服务器模式和共置模式。

选项 1:Colocate 模式

在此模式下,vLLM 在训练器进程内运行,并与训练模型共享 GPU 内存。这避免了启动单独的服务器,并可能提高 GPU 利用率,但可能导致训练 GPU 上的内存争用。这是默认模式。

from trl import RLOOConfig

training_args = RLOOConfig(
    ...,
    use_vllm=True,  # vllm_mode="colocate" by default
)

选项 2:服务器模式

在此模式下,vLLM 在独立进程中运行(并使用独立的 GPU),并通过 HTTP 与训练器进行通信。如果您拥有专门用于推理的 GPU,则此模式非常理想。

  1. 启动 vLLM 服务器:

    VLLM_SERVER_DEV_MODE=1 vllm serve <model_name> \
        --weight-transfer-config '{"backend": "nccl"}' \
        --logprobs-mode processed_logprobs \
        --max-logprobs -1
  2. 在训练脚本中启用服务器模式:

    from trl import RLOOConfig
    
    training_args = RLOOConfig(
        ...,
        use_vllm=True,
        vllm_mode="server",
    )

请确保服务器使用的 GPU 与训练器不同,否则可能会遇到 NCCL 错误。您可以通过 CUDA_VISIBLE_DEVICES 环境变量来指定要使用的 GPU。

根据模型大小以及训练所需的整体 GPU 内存,您可能需要调整 RLOOConfig 中的 vllm_gpu_memory_utilization 参数,以避免资源利用率不足或内存溢出错误。

我们提供了一个 HF Space,用于根据您的模型配置和实验设置来估算推荐的 GPU 内存利用率。只需按以下方式使用,即可获得 vllm_gpu_memory_utilization 的推荐值:

如果推荐值在您的环境中不起作用,我们建议在推荐值上添加一个小缓冲(例如 +0.05 或 +0.1),以确保稳定性。

如果您仍然遇到内存不足错误,请将 vllm_enable_sleep_mode 设置为 True,这样在优化步骤期间,vllm 参数和缓存将被卸载。有关更多信息,请参阅 使用 vLLM Sleep Mode 减少内存使用。

默认情况下,RLOO 使用 MASTER_ADDR=localhost 和 MASTER_PORT=12345 作为 vLLM 的配置,但你可以通过相应地设置环境变量来覆盖这些值。

有关更多信息,请参阅 使用 vLLM 加速训练。

大规模 RLOO:在多节点上训练 70B+ 模型

在训练 Qwen2.5-72B 这样的大模型时,你需要多项关键优化,以确保训练在多 GPU 和多节点环境下高效且可扩展。这些优化包括:

  • DeepSpeed ZeRO Stage 3:ZeRO 利用数据并行将模型状态(权重、梯度、优化器状态)分布在多个 GPU 和 CPU 上,从而降低每个设备的内存和计算需求。由于大模型无法装入单个 GPU,训练此类模型必须使用 ZeRO Stage 3。更多详情请参阅 DeepSpeed Integration。
  • torchrun:PyTorch 的启动器会在每个节点上的每张 GPU 上启动一个进程,并通过公共的会合点将它们连接起来。更多详情,请参阅 分布式训练。
  • vLLM:请参阅上一节,了解如何使用 vLLM 来加速生成。

以下是一个示例 SLURM 脚本,用于在多个节点上使用 RLOO 训练 70B 模型。该脚本在 4 个节点上训练模型,并使用第 5 个节点进行基于 vLLM 的生成。

#!/bin/bash
#SBATCH --nodes=5
#SBATCH --gres=gpu:8

# Get the list of allocated nodes
NODELIST=($(scontrol show hostnames $SLURM_JOB_NODELIST))

# Assign the first 4 nodes for training and the 5th node for vLLM
TRAIN_NODES="${NODELIST[@]:0:4}"  # Nodes 0, 1, 2, 3 for training
VLLM_NODE="${NODELIST[4]}"  # Node 4 for vLLM

# Run training on the first 4 nodes (Group 1)
srun --nodes=4 --ntasks=4 --nodelist="${NODELIST[@]:0:4}" torchrun \
     --nnodes 4 \
     --nproc_per_node 8 \
     --node_rank $SLURM_PROCID \
     --rdzv_backend c10d \
     --rdzv_endpoint ${NODELIST[0]}:29500 \
     train_rloo.py \
     --server_ip $VLLM_NODE &

# Run vLLM server on the 5th node (Group 2)
srun --nodes=1 --ntasks=1 --nodelist="${NODELIST[4]}" env VLLM_SERVER_DEV_MODE=1 vllm serve Qwen/Qwen2.5-72B --tensor-parallel-size 8 \
    --weight-transfer-config '{"backend": "nccl"}' \
    --logprobs-mode processed_logprobs \
    --max-logprobs -1 &

wait
import argparse

from datasets import load_dataset
from trl import RLOOTrainer, RLOOConfig

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--vllm_server_host", type=str, default="", help="The server IP")
    args = parser.parse_args()

    # Example dataset from TLDR
    dataset = load_dataset("trl-lib/tldr", split="train")

    # Dummy reward function: count the number of unique characters in the completions
    def reward_num_unique_chars(completions, **kwargs):
        return [len(set(c)) for c in completions]

    training_args = RLOOConfig(
        output_dir="Qwen2.5-72B-RLOO",
        per_device_train_batch_size=4,
        bf16=True,
        deepspeed="examples/deepspeed_configs/zero3.json",
        use_vllm=True,
        vllm_mode="server",
        vllm_server_host=args.vllm_server_host.replace("ip-", "").replace("-", "."),  # from ip-X-X-X-X to X.X.X.X
    )

    trainer = RLOOTrainer(model="Qwen/Qwen2.5-72B", args=training_args, reward_funcs=reward_num_unique_chars, train_dataset=dataset)
    trainer.train()

if __name__=="__main__":
    main()

使用自定义奖励函数

RLOOTrainer 支持使用自定义奖励函数,而非稠密奖励模型。为确保兼容性,您的奖励函数必须满足以下要求:

奖励函数可以是同步的 Python 可调用对象,也可以是异步的 async def 协程。当您提供多个异步奖励函数时,它们会被并发等待(通过 asyncio.gather 并行执行),从而使它们的延迟相互重叠。

  1. 输入参数:

    • 该函数必须接受以下关键字参数:

      • prompts(包含提示词),
      • completions(包含生成的补全内容),
      • completion_ids(包含分词后的补全内容),
      • trainer_state (TrainerState):训练器的当前状态。可用于实现动态奖励函数,例如课程学习,其中奖励会根据训练进度进行调整。
      • log_extra:一个可调用对象 log_extra(column: str, values: list),用于向 completions 表添加额外的列。参见示例 6。
      • log_metric:一个可调用对象 log_metric(name: str, value: float),用于将标量指标记录为图表,与 kl、entropy 等并列显示。参见示例 6。
      • 数据集可能包含的所有列名(prompt 除外)。例如,如果数据集包含名为 ground_truth 的列,则函数将以 ground_truth 作为关键字参数被调用。

      满足此要求的最简单方法是在函数签名中使用 **kwargs。

    • 根据数据集格式的不同,输入会有所不同:

      • 对于标准格式,prompts和completions将是字符串列表。
      • 对于对话格式,prompts和completions将是消息字典的列表。
  2. 返回值:该函数必须返回一个浮点数列表。每个浮点数代表对应单个补全的奖励。

示例 1:奖励更长的补全

以下是一个标准格式奖励函数的示例,该函数会奖励更长的补全:

def reward_func(completion_ids, **kwargs):
    """Reward function that assigns higher scores to longer completions (in terms of token count)."""
    return [float(len(ids)) for ids in completion_ids]

您可以按以下方式测试:

>>> prompts = ["The sky is", "The sun is"]  # not used in the reward function, but the trainer will pass it
>>> completions = [" blue.", " in the sky."]  # not used in the reward function, but the trainer will pass it
>>> completion_ids = [[6303, 13], [304, 279, 12884, 13]]
>>> reward_func(prompts=prompts, completions=completions, completion_ids=completion_ids)
[2.0, 4.0]

示例 1.1:奖励更长的补全(基于字符数)

与上一个示例相同,但这次奖励函数基于字符数而非 token 数。

def reward_func(completions, **kwargs):
    """Reward function that assigns higher scores to longer completions (in terms of character count)."""
    return [float(len(completion)) for completion in completions]

您可以按以下方式测试:

>>> prompts = ["The sky is", "The sun is"]
>>> completions = [" blue.", " in the sky."]
>>> completion_ids = [[6303, 13], [304, 279, 12884, 13]]  # not used in the reward function, but the trainer will pass it
>>> reward_func(prompts=prompts, completions=completions, completion_ids=completion_ids)
[6.0, 12.0]

示例 2:奖励具有特定格式的补全

以下是一个奖励函数的示例,用于检查补全内容是否具有特定格式。该示例受论文 DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning 中使用的 format reward 函数启发。 它专为对话格式设计,其中提示和补全内容均由结构化消息组成。

import re

def format_reward_func(completions, **kwargs):
    """Reward function that checks if the completion has a specific format."""
    pattern = r"^<think>.*?</think><answer>.*?</answer>$"
    completion_contents = [completion[0]["content"] for completion in completions]
    matches = [re.match(pattern, content) for content in completion_contents]
    return [1.0 if match else 0.0 for match in matches]

您可以按以下方式测试此函数:

>>> prompts = [
...     [{"role": "user", "content": "What is the result of (1 + 2) * 4?"}],
...     [{"role": "user", "content": "What is the result of (3 + 1) * 2?"}],
... ]
>>> completions = [
...     [{"role": "assistant", "content": "<think>The sum of 1 and 2 is 3, which we multiply by 4 to get 12.</think><answer>(1 + 2) * 4 = 12</answer>"}],
...     [{"role": "assistant", "content": "The sum of 3 and 1 is 4, which we multiply by 2 to get 8. So (3 + 1) * 2 = 8."}],
... ]
>>> format_reward_func(prompts=prompts, completions=completions)
[1.0, 0.0]

示例 3:基于参考的奖励完成

以下是一个奖励函数的示例,用于检查补全内容是否正确。该示例受论文 DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning 中使用的 accuracy reward 函数启发。 此示例针对 standard format 设计,其中数据集包含一个名为 ground_truth 的列。

import re

def reward_func(completions, ground_truth, **kwargs):
    # Regular expression to capture content inside \boxed{}
    matches = [re.search(r"\\boxed\{(.*?)\}", completion) for completion in completions]
    contents = [match.group(1) if match else "" for match in matches]
    # Reward 1 if the content is the same as the ground truth, 0 otherwise
    return [1.0 if c == gt else 0.0 for c, gt in zip(contents, ground_truth)]

您可以按以下方式测试此函数:

>>> prompts = ["Problem: Solve the equation $2x + 3 = 7$. Solution:", "Problem: Solve the equation $3x - 5 = 10$."]
>>> completions = [r" The solution is \boxed{2}.", r" The solution is \boxed{6}."]
>>> ground_truth = ["2", "5"]
>>> reward_func(prompts=prompts, completions=completions, ground_truth=ground_truth)
[1.0, 0.0]

示例 4:多任务奖励函数

以下是在 RLOOTrainer 中使用多个奖励函数的示例。在此示例中,我们定义了两个任务特定的奖励函数:math_reward_func 和 coding_reward_func。math_reward_func 根据数学问题的正确性给予奖励,而 coding_reward_func 则根据代码解决方案是否可运行来给予奖励。

from datasets import Dataset
from trl import RLOOTrainer

# Define a dataset that contains both math and coding problems
dataset = Dataset.from_list(
    [
        {"prompt": "What is 2+2?", "task": "math"},
        {"prompt": "Write a function that returns the sum of two numbers.", "task": "code"},
        {"prompt": "What is 3*4?", "task": "math"},
        {"prompt": "Write a function that returns the product of two numbers.", "task": "code"},
    ]
)

# Math-specific reward function
def math_reward_func(prompts, completions, task, **kwargs):
    rewards = []
    for prompt, completion, t in zip(prompts, completions, task):
        if t == "math":
            # Calculate math-specific reward
            correct = check_math_solution(prompt, completion)
            reward = 1.0 if correct else -1.0
            rewards.append(reward)
        else:
            # Return None for non-math tasks
            rewards.append(None)
    return rewards

# Coding-specific reward function
def coding_reward_func(prompts, completions, task, **kwargs):
    rewards = []
    for prompt, completion, t in zip(prompts, completions, task):
        if t == "code":
            # Calculate coding-specific reward
            works = test_code_solution(prompt, completion)
            reward = 1.0 if works else -1.0
            rewards.append(reward)
        else:
            # Return None for non-coding tasks
            rewards.append(None)
    return rewards

# Use both task-specific reward functions
trainer = RLOOTrainer(
    model="Qwen/Qwen2-0.5B-Instruct",
    reward_funcs=[math_reward_func, coding_reward_func],
    train_dataset=dataset,
)

trainer.train()

在此示例中,math_reward_func 和 coding_reward_func 旨在处理包含数学和编程问题的混合数据集。数据集中的 task 列用于确定对每个问题应用哪个奖励函数。如果数据集中的某个样本没有相关的奖励函数,该奖励函数将返回 None,RLOOTrainer 将继续使用有效的函数和任务。这使得 RLOOTrainer 能够处理具有不同适用性的多个奖励函数。

请注意,RLOOTrainer 会忽略奖励函数返回的 None 奖励,仅考虑相关函数返回的奖励。这确保了模型在相关任务上进行训练,并忽略没有相关奖励函数的任务。

示例 5:异步奖励函数

自定义奖励函数也可以定义为 async def 协程。如果您的奖励依赖于缓慢的 I/O(例如,调用远程服务),这将非常有用。当您传入多个异步奖励函数时,RLOOTrainer 会并发执行它们,从而使它们的延迟相互重叠。

以下是一个异步奖励函数的最小示例,用于模拟 I/O 密集型操作:

import asyncio

async def async_reward_func(prompts, completions, **kwargs):
    # Simulate an I/O-bound call (e.g., HTTP request, database lookup)
    await asyncio.sleep(0.01)
    # Simple toy reward: 1.0 if the completion is non-empty, else 0.0
    return [1.0 if completion else 0.0 for completion in completions]

示例 6:记录额外的列和指标

以下是一个奖励函数的示例,该函数会将额外的列记录到 completions 表中,并将标量指标记录为图表。

import re

def reward_func(completions, ground_truth, log_extra=None, log_metric=None, **kwargs):
    extracted = [re.search(r"\\boxed\{(.*?)\}", c) for c in completions]
    extracted = [m.group(1) if m else None for m in extracted]
    rewards = [1.0 if e == gt else 0.0 for e, gt in zip(extracted, ground_truth)]

    if log_extra:
        log_extra("golden_answer", list(ground_truth))
        log_extra("extracted_answer", [e or "[none]" for e in extracted])

    if log_metric:
        log_metric("accuracy", sum(rewards) / len(rewards))

    return rewards

将奖励函数传递给训练器

要将自定义奖励函数用于 RLOOTrainer,请按以下方式传入:

from trl import RLOOTrainer

trainer = RLOOTrainer(
    reward_funcs=reward_func,
    ...,
)

您可以将多个奖励函数作为列表传入;该列表可以同时包含同步函数和异步函数:

from trl import RLOOTrainer

trainer = RLOOTrainer(
    reward_funcs=[reward_func, async_reward_func1, async_reward_func2],
    ...,
)

奖励将计算为每个函数奖励的总和,或者如果配置中提供了reward_weights,则为加权总和。

请注意,RLOOTrainer 支持多种不同类型的奖励函数。有关更多详细信息,请参阅参数文档。

训练视觉语言模型

RLOO 支持在包含文本和图像的多模态数据集上训练视觉语言模型(VLM)。

支持的模型

已测试:

  • Gemma3 — 例如 google/gemma-3-4b-it
  • LLaVA-NeXT — 例如 llava-hf/llava-v1.6-mistral-7b-hf
  • Qwen2-VL — 例如 Qwen/Qwen2-VL-2B-Instruct
  • Qwen2.5-VL — 例如 Qwen/Qwen2.5-VL-3B-Instruct
  • SmolVLM2 — 例如 HuggingFaceTB/SmolVLM2-2.2B-Instruct

无法保证与所有 VLM 的兼容性。如果您认为某个模型应当被支持,欢迎在 GitHub 上提交 issue——或者更好的是,提交一个包含所需更改的 pull request。

快速入门

使用 rloo_vlm.py 对 VLM 进行微调。在 lmms-lab/multimodal-open-r1-8k-verified 上训练的示例命令:

torchrun --nproc_per_node 8 examples/rloo_visual_math/rloo_visual_math.py \
  --deepspeed examples/deepspeed_configs/zero3.json \
  --model_name_or_path Qwen/Qwen2.5-VL-3B-Instruct \
  --output_dir rloo-Qwen2.5-VL-3B-Instruct \
  --learning_rate 1e-5 \
  --dtype bfloat16 \
  --max_completion_length 1024 \
  --use_vllm \
  --vllm_mode colocate \
  --use_peft \
  --lora_target_modules "q_proj", "v_proj" \
  --log_completions

配置技巧

  • 在视觉-语言投影层上使用 LoRA
  • 启用 4 位量化以降低内存使用
  • VLM 对内存需求较高 — 请从较小的批处理大小开始
  • 大多数模型都兼容 vLLM(server 和 colocate 模式)

数据集格式

每个训练样本应包含:

  • prompt:通过处理器的聊天模板格式化的文本
  • image/images:PIL Image 或 PIL Image 列表

Trainer 会通过模型的图像处理器自动处理图像到张量的转换。

RLOOTrainer

类 trl.RLOOTrainer

< >

( model: str | PreTrainedModel | PeftModelreward_funcs: str | transformers.modeling_utils.PreTrainedModel | collections.abc.Callable[..., list[float | None]] | list[str | transformers.modeling_utils.PreTrainedModel | collections.abc.Callable[..., list[float | None]]]args: trl.trainer.rloo_config.RLOOConfig | None = Nonetrain_dataset: datasets.arrow_dataset.Dataset | datasets.iterable_dataset.IterableDataset | None = Noneeval_dataset: datasets.arrow_dataset.Dataset | datasets.iterable_dataset.IterableDataset | datasets.dataset_dict.DatasetDict | datasets.dataset_dict.IterableDatasetDict | dict[str, datasets.arrow_dataset.Dataset | datasets.iterable_dataset.IterableDataset] | None = Noneprocessing_class: transformers.tokenization_utils_base.PreTrainedTokenizerBase | transformers.processing_utils.ProcessorMixin | None = Nonereward_processing_classes: transformers.tokenization_utils_base.PreTrainedTokenizerBase | list[transformers.tokenization_utils_base.PreTrainedTokenizerBase] | None = Nonecallbacks: list[transformers.trainer_callback.TrainerCallback] | None = Noneoptimizers: tuple[typing.Optional[torch.optim.Optimizer], typing.Optional[torch.optim.lr_scheduler.LambdaLR]] = (None, None)quantization_config: BitsAndBytesConfig | None = Nonepeft_config: PeftConfig | None = None )

参数

  • model (str 或 PreTrainedModel 或 PeftModel) — 要训练的模型。可以是以下之一:
    • 一个字符串,可以是托管在 huggingface.co 模型仓库中的预训练模型的 model id,也可以是包含使用 save_pretrained 保存的模型权重的 目录 的路径,例如 './my_model_directory/'。该模型使用 <ModelArchitecture>.from_pretrained 加载(其中 <ModelArchitecture> 由模型配置推导得出),并传入 args.model_init_kwargs 中的关键字参数。如果未在 args.model_init_kwargs 中指定 dtype,则默认为 float32。这与 from_pretrained 不同,在后者中(自 Transformers v5 起),dtype 是从模型配置中推断得出的。
    • 一个 PreTrainedModel 对象。仅支持因果语言模型。
    • 一个 PeftModel 对象。仅支持因果语言模型。
  • reward_funcs (RewardFunc | list[RewardFunc]) — 用于计算奖励的奖励函数。要计算奖励,我们会使用提示(prompts)和补全(completions)调用所有奖励函数,并将奖励求和。可以是以下形式之一:
    • 单个奖励函数,例如:

      • 字符串:model ID,即托管在 huggingface.co 模型仓库中的预训练模型,或指向使用 save_pretrained 保存模型权重的目录的路径,例如 './my_model_directory/'。该模型通过 from_pretrained 加载,并设置 num_labels=1 以及 args.model_init_kwargs 中的关键字参数。

      • 一个 PreTrainedModel 对象:仅支持序列分类模型。

      • 自定义奖励函数:该函数会接收提示词和生成的补全内容,以及数据集中的其他附加列。它应返回一个奖励列表。自定义奖励函数可以是同步的,也可以是异步的,并且当奖励不适用于某些样本时,也可以返回 None。这在多任务训练中非常有用,因为不同的奖励函数适用于不同类型的样本。当某个奖励函数对某个样本返回 None 时,该奖励函数将被排除在该样本的奖励计算之外。更多详情,请参阅 使用自定义奖励函数。

        Trainer 的状态也会传递给奖励函数。Trainer 的状态是 TrainerState 的一个实例,可以通过访问奖励函数签名中的 trainer_state 参数来获取。

    • 奖励函数列表,其中每个项可以独立为上述任意类型。允许在列表中混合使用不同类型(例如,字符串模型 ID 和自定义奖励函数)。

  • args (RLOOConfig,可选) — 此训练器的配置。如果为 None,则使用默认配置。
  • train_dataset (Dataset 或 IterableDataset) — 用于训练的 Dataset。它必须包含一个名为 "prompt". 数据集中的其他列将被忽略。样本的格式可以是以下两种之一:
    • 标准:每个样本包含纯文本。
    • 对话式:每个样本包含结构化的消息(例如角色和内容)。

    当 train_dataset 是一个 IterableDataset(例如流式数据集)时,必须在训练参数中设置 max_steps,因为其长度无法推断,而确定训练循环的上限并配置学习率调度器需要知道总的训练步数。

  • eval_dataset (Dataset、IterableDataset、DatasetDict、IterableDatasetDict 或 dict[str, Dataset | IterableDataset]) — 用于评估的数据集。它必须满足与 train_dataset 相同的要求。
  • processing_class (PreTrainedTokenizerBase、ProcessorMixin、可选) — 用于处理数据的处理类。填充侧必须设置为“left”。如果为 None,则使用 from_pretrained 从模型名称加载处理类。必须设置填充标记 tokenizer.pad_token。如果处理类未设置填充标记,则默认使用 tokenizer.eos_token。
  • reward_processing_classes (PreTrainedTokenizerBase 或 list[PreTrainedTokenizerBase], 可选) — 与在 reward_funcs. 中指定的奖励函数相对应的处理类。可以是以下形式之一:
    • 单个处理类:当 reward_funcs 仅包含一个奖励函数时使用。
    • 处理类列表:必须与 reward_funcs 中奖励函数的顺序和长度一致。 如果设置为 None,或者列表中对应 PreTrainedModel 的元素为 None,则会使用 from_pretrained 自动加载该模型的 tokenizer。对于 reward_funcs 中属于自定义奖励函数(而非 PreTrainedModel)的元素,reward_processing_classes 中对应的条目将被忽略。
  • callbacks (列表 TrainerCallback, 可选) — 用于自定义训练循环的回调列表。这些回调将被添加到 此处.

    如果要移除其中一个默认使用的回调,请使用 remove_callback 方法。

  • optimizers (tuple[torch.optim.Optimizer | None, torch.optim.lr_scheduler.LambdaLR | None],可选,默认为 (None, None)) — 一个包含要使用的优化器和调度器的元组。默认情况下,将在您的模型上使用 AdamW 的实例,并使用由 args 控制的、通过 get_linear_schedule_with_warmup 给出的调度器。
  • quantization_config (BitsAndBytesConfig, 可选) — 从模型标识符加载模型时使用的量化配置。与 peft_config 结合用于 QLoRA 训练。如果模型已经实例化,则忽略此配置。
  • peft_config (PeftConfig,可选) — 用于包装模型的 PEFT 配置。如果为 None,则不包装模型。

Trainer for the Reinforce Leave One Out (RLOO) method. This algorithm was initially proposed in the paper Back to Basics: Revisiting REINFORCE Style Optimization for Learning from Human Feedback in LLMs.

示例:

>>> from trl import RLOOTrainer
>>> from trl.rewards import accuracy_reward
>>> from datasets import load_dataset

>>> dataset = load_dataset("trl-lib/DeepMath-103K", split="train")

>>> trainer = RLOOTrainer(
...     model="Qwen/Qwen2.5-0.5B-Instruct",
...     reward_funcs=accuracy_reward,
...     train_dataset=dataset,
... )
>>> trainer.train()

训练

< >

( resume_from_checkpoint: str | bool | None = Nonetrial: optuna.Trial | dict[str, Any] | None = Noneignore_keys_for_eval: list[str] | None = None ) → ~trainer_utils.TrainOutput

参数

  • resume_from_checkpoint (str 或 bool,可选) — 如果为 str,则为之前 Trainer 实例保存的检查点的本地路径。如果为 bool 且等于 True,则加载之前 Trainer 实例保存在 args.output_dir 中的最后一个检查点。如果存在,训练将从此处加载的模型/优化器/调度器状态中恢复。
  • trial (optuna.Trial 或 dict[str, Any],可选) — 用于超参数搜索的试验运行或超参数字典。
  • ignore_keys_for_eval (list[str],可选) — 在训练期间收集预测结果以进行评估时,应忽略的模型输出(如果输出是字典)中的键列表。

返回值

~trainer_utils.TrainOutput

包含全局步数、训练损失和指标的 Object。

主要训练入口。

save_model

< >

( output_dir: str | None = None_internal_call: bool = False )

将保存模型,以便您可以使用 from_pretrained() 重新加载它。

仅从主进程保存。

push_to_hub

< >

( commit_message: str | None = 'End of training'blocking: bool = Truetoken: str | None = Nonerevision: str | None = None**kwargs )

参数

  • commit_message (str,可选,默认为 "End of training") — 推送时提交的提交信息。
  • blocking (bool,可选,默认为 True) — 是否仅在 git push 完成后才返回。
  • token (str,可选,默认为 None) — 具有写入权限的 Token,用于覆盖 Trainer 的原始参数。
  • revision (str, 可选) — 要从中提交内容的 git 版本。默认为 “main” 分支的头部。
  • kwargs (dict[str, Any],可选) — 传递给 ~Trainer.create_model_card 的额外关键字参数。

将 self.model 和 self.processing_class 上传到 🤗 模型库中的仓库 self.args.hub_model_id。

RLOOConfig

类 trl.RLOOConfig

< >

( output_dir: str | None = Noneper_device_train_batch_size: int = 8num_train_epochs: float = 3.0max_steps: int = -1learning_rate: float = 1e-06lr_scheduler_type: transformers.trainer_utils.SchedulerType | str = 'linear'lr_scheduler_kwargs: dict | str | None = Nonewarmup_steps: float = 0optim: transformers.training_args.OptimizerNames | str = 'adamw_torch_fused'optim_args: str | None = Noneweight_decay: float = 0.0adam_beta1: float = 0.9adam_beta2: float = 0.999adam_epsilon: float = 1e-08optim_target_modules: None | str | list[str] = Nonegradient_accumulation_steps: int = 1average_tokens_across_devices: bool = Truemax_grad_norm: float = 1.0label_smoothing_factor: float = 0.0bf16: bool | None = Nonefp16: bool = Falsebf16_full_eval: bool = Falsefp16_full_eval: bool = Falsetf32: bool | None = Nonegradient_checkpointing: bool = Truegradient_checkpointing_kwargs: dict[str, typing.Any] | str | None = Nonetorch_compile: bool = Falsetorch_compile_backend: str | None = Nonetorch_compile_mode: str | None = Noneuse_liger_kernel: bool = Falseliger_kernel_config: dict[str, bool] | None = Noneuse_cache: bool = Falseneftune_noise_alpha: float | None = Nonetorch_empty_cache_steps: int | None = Noneauto_find_batch_size: bool = Falselogging_strategy: transformers.trainer_utils.IntervalStrategy | str = 'steps'logging_steps: float = 10logging_first_step: bool = Falselog_on_each_node: bool = Truelogging_nan_inf_filter: bool = Trueinclude_num_input_tokens_seen: str | bool = 'no'log_level: str = 'passive'log_level_replica: str = 'warning'disable_tqdm: bool | None = Nonereport_to: None | str | list[str] = 'none'run_name: str | None = Noneproject: str = 'huggingface'trackio_space_id: str | None = Nonetrackio_bucket_id: str | None = Nonetrackio_static_space_id: typing.Union[str, NoneType, typing.Literal[False]] = Noneeval_strategy: transformers.trainer_utils.IntervalStrategy | str = 'no'eval_steps: float | None = Noneeval_delay: float = 0per_device_eval_batch_size: int = 8prediction_loss_only: bool = Falseeval_on_start: bool = Falseeval_do_concat_batches: bool = Trueeval_use_gather_object: bool = Falseeval_accumulation_steps: int | None = Noneinclude_for_metrics: list[str] = <factory>batch_eval_metrics: bool = Falsesave_only_model: bool = Falsesave_strategy: transformers.trainer_utils.SaveStrategy | str = 'steps'save_steps: float = 500save_on_each_node: bool = Falsesave_total_limit: int | None = Noneenable_jit_checkpoint: bool = Falsepush_to_hub: bool = Falsehub_token: str | None = Nonehub_private_repo: bool | None = Nonehub_model_id: str | None = Nonehub_strategy: transformers.trainer_utils.HubStrategy | str = 'every_save'hub_always_push: bool = Falsehub_revision: str | None = Noneload_best_model_at_end: bool = Falsemetric_for_best_model: str | None = Nonegreater_is_better: bool | None = Noneignore_data_skip: bool = Falserestore_callback_states_from_checkpoint: bool = Falsefull_determinism: bool = Falseseed: int = 42data_seed: int | None = Noneuse_cpu: bool = Falseaccelerator_config: dict | str | None = Noneparallelism_config: accelerate.parallelism_config.ParallelismConfig | None = Nonedataloader_drop_last: bool = Falsedataloader_num_workers: int = 0dataloader_pin_memory: bool = Truedataloader_persistent_workers: bool = Falsedataloader_prefetch_factor: int | None = Nonedataloader_multiprocessing_context: str | None = Nonedataloader_in_order: bool = Trueremove_unused_columns: bool | None = Falselabel_names: list[str] | None = Nonetrain_sampling_strategy: str = 'random'length_column_name: str = 'length'ddp_find_unused_parameters: bool | None = Noneddp_bucket_cap_mb: int | None = Noneddp_broadcast_buffers: bool | None = Noneddp_static_graph: bool | None = Noneddp_backend: str | None = Noneddp_timeout: int = 1800fsdp: str | None = Nonefsdp_config: dict[str, typing.Any] | str | None = Nonedeepspeed: dict | str | None = Nonedebug: str | list[transformers.debug_utils.DebugOption] = ''skip_memory_metrics: bool = Truedo_train: bool = Falsedo_eval: bool = Falsedo_predict: bool = Falseresume_from_checkpoint: str | None = Nonelocal_rank: int = -1model_init_kwargs: dict[str, typing.Any] | str | None = Nonetrust_remote_code: bool = Falserouter_aux_loss_coef: float | None = Nonedisable_dropout: bool = Falsenum_generations: int | None = 2num_generations_eval: int | None = Nonemax_completion_length: int | None = 512ds3_gather_for_generation: bool = Trueshuffle_dataset: bool | None = Truepad_to_multiple_of: int | str | None = 'auto'generation_batch_size: int | None = Nonesteps_per_generation: int | None = Nonetemperature: float = 1.0top_p: float = 1.0top_k: int = 0min_p: float | None = Nonegeneration_kwargs: dict | str | None = Nonechat_template_kwargs: dict | str | None = Nonerepetition_penalty: float = 1.0cache_implementation: str | None = Noneuse_vllm: bool = Falsevllm_mode: str = 'colocate'vllm_model_impl: str = 'vllm'vllm_enable_sleep_mode: bool = Falsevllm_structured_outputs_regex: str | None = Nonevllm_server_base_url: str | None = Nonevllm_server_host: str = '0.0.0.0'vllm_server_port: int = 8000vllm_server_timeout: float = 240.0vllm_group_port: int = 51216vllm_gpu_memory_utilization: float = 0.3vllm_max_model_length: int | None = Nonevllm_tensor_parallel_size: int = 1beta: float = 0.05num_iterations: int = 1epsilon: float = 0.2epsilon_high: float | None = Nonereward_weights: list[float] | None = Nonenormalize_advantages: bool = Falsereward_clip_range: tuple[float, float] | None = Nonemask_truncated_completions: bool = Falsesync_ref_model: bool = Falseref_model_mixup_alpha: float = 0.6ref_model_sync_steps: int = 512log_completions: bool = Falselog_multimodal: bool = Truenum_completions_to_print: int | None = Nonelog_unique_prompts: bool = Falseuse_transformers_continuous_batching: bool = Falsetransformers_continuous_batching_config: dict | str | None = Noneuse_transformers_paged: bool = False )

控制模型和参考模型的参数

  • model_init_kwargs (str 或 dict[str, Any],可选) — 用于 from_pretrained 的关键字参数,当 RLOOTrainer 的 model 参数以字符串形式提供时使用。加载处理类时也会使用 revision 值。
  • trust_remote_code (bool,可选,默认为 False) — 是否允许加载包含自定义 Python 代码的模型和分词器。此参数会传递给 from_pretrained 和 from_pretrained。 同样适用于奖励模型和奖励分词器的加载。
  • router_aux_loss_coef (float, 可选) — 混合专家(MoE)模型中负载均衡辅助损失的系数,以该权重添加到训练损失中。如果未设置,则使用模型配置中声明的值。设置为 0.0 可将其禁用。与非 MoE 模型一起使用时会失败。
  • disable_dropout (bool,可选,默认为 False) — 是否禁用模型中的 dropout。这在配合参考模型进行训练时非常有用,因为它可以防止模型对相同的输入生成不同的 logprobs。

控制数据预处理的参数

  • remove_unused_columns (bool,可选,默认为 False) — 是否仅保留数据集中的 "prompt" 列。如果您使用的自定义奖励函数需要 "prompts" 和 "completions" 以外的任何列,则应保持此参数为 False。
  • num_generations (int, 可选, 默认为 2) — 每个提示词采样的生成数量。有效批次大小(num_processes * per_device_batch_size * gradient_accumulation_steps)必须能被该值整除。
    • gradient_accumulation_steps) 必须能被该值整除。
  • num_generations_eval (int, 可选) — 评估期间采样的生成数量。这允许在评估期间使用更少的生成数量以节省计算资源。如果为 None,则使用 num_generations 的值。
  • max_completion_length (int 或 None,可选,默认为 512) — 生成补全的最大长度。
  • ds3_gather_for_generation (bool,可选,默认为 True) — 此设置适用于 DeepSpeed ZeRO-3。如果启用,策略模型权重将在生成时进行聚合,从而提升生成速度。但是,禁用此选项允许训练超出单个 GPU 显存容量的模型,代价是生成速度变慢。禁用此选项与 vLLM 生成不兼容。
  • shuffle_dataset (bool,可选,默认为 True) — 是否打乱训练数据集。
  • pad_to_multiple_of (int 或 str,可选,默认为 "auto") — 如果设置,提示词 ID 和补全 ID 将被填充为该值的倍数。"auto" 在 Neuron (AWS Trainium) 上填充为 128 的倍数,因为 Neuron 针对每种输入形状只编译一次,而在其他平台上不进行填充。

控制生成的参数

  • generation_batch_size (int, 可选) — 用于生成的批大小。如果为 None,则默认为有效训练批大小: per_device_train_batch_size * num_processes * steps_per_generation。换句话说,每个优化步骤处理一个生成批次。与 steps_per_generation 互斥。
  • steps_per_generation (int,可选) — 每次生成的步数。如果为 None,则默认为 gradient_accumulation_steps。与 generation_batch_size 互斥。
  • temperature (float,可选,默认为 1.0) — 用于采样的温度。温度越高,生成的补全内容越随机。
  • top_p (float,可选,默认为 1.0) — 用于控制所考虑的前几个 token 的累积概率的浮点数。取值必须在 (0, 1] 范围内。设置为 1.0 表示考虑所有 token。
  • top_k (int,可选,默认为 0) — 用于 top-k 过滤时保留的最高概率词汇表 token 数量。如果为 0,则禁用 top-k 过滤,并考虑所有 token。
  • min_p (float, 可选) — 最小 token 概率,该值会按最可能 token 的概率进行缩放。它必须是 0.0 和 1.0 之间的值。典型值在 0.01-0.2 范围内。
  • generation_kwargs (dict[str, Any], 可选) — 在采样补全时传递给 GenerationConfig(如果使用 transformers)或 SamplingParams(如果使用 vLLM)的额外关键字参数。可用于进一步自定义 生成行为,例如设置 suppress_tokens、num_beams 等。如果其中包含与其他生成参数(如 min_p、top_p 等)冲突的键,则会覆盖它们。
  • chat_template_kwargs (dict[str, Any], 可选) — 在生成补全时传递给 apply_chat_template 函数的其他关键字参数。
  • repetition_penalty (float,可选,默认为 1.0) — 一个浮点数,根据新 token 是否出现在提示词和迄今为止生成的文本中来对其进行惩罚。 值 > 1.0 会鼓励模型使用新的 token,而值 < 1.0 会鼓励模型重复使用 token。
  • cache_implementation (str, 可选) — 当 use_vllm 设置为 False 时,用于加速生成的缓存方法实现。

控制由 vLLM 驱动的生成加速的参数

  • use_vllm (bool,可选,默认为 False) — 是否使用 vLLM 来生成补全。如果设置为 True,训练器将使用 vLLM 进行生成,而不是默认的 model.generate()。需要安装 vllm。
  • vllm_mode (str, 可选, 默认为 "colocate") — 当 use_vllm 设置为 True. 时,用于 vLLM 集成的模式。必须是 "server" 或 "colocate".
    • "server":训练器将把生成请求发送到独立的 vLLM 服务器。请确保 vLLM 服务器正在运行(使用 vllm serve 启动)。
    • "colocate":vLLM 将在同一进程中运行,并共享用于训练的 GPU。这避免了部署独立服务器的需求,但可能会与训练过程产生资源争用。
  • vllm_model_impl (str,可选,默认为 "vllm") — 用于 vLLM 的模型实现。必须是 "transformers" 或 "vllm" 之一。"transformers":使用 transformers 后端作为模型实现。"vllm":使用 vllm 库作为模型实现。
  • vllm_enable_sleep_mode (bool,可选,默认为 False) — 启用 vLLM 睡眠模式,以便在优化器步骤期间卸载权重/缓存。这可以保持较低的 GPU 内存占用,但唤醒引擎会增加主机与设备之间的传输延迟。
  • vllm_structured_outputs_regex (str, 可选) — 用于 vLLM 结构化输出的正则表达式。如果为 None(默认值),则禁用结构化输出。

用于控制 vLLM 服务器的参数(仅在 `vllm_mode` 为 `"server"` 时使用)

  • vllm_server_base_url (str, 可选) — vLLM 服务器的基础 URL(例如 "http://localhost:8000")。如果提供了该参数,vllm_server_host 和 vllm_server_port 将被忽略。
  • vllm_server_host (str,可选,默认为 "0.0.0.0") — 要连接的 vLLM 服务器的主机地址。如果提供了 vllm_server_base_url,则忽略此参数。
  • vllm_server_port (int,可选,默认为 8000) — 要连接的 vLLM 服务器端口。如果提供了 vllm_server_base_url,则忽略此参数。
  • vllm_server_timeout (float, 可选, 默认值为 240.0) — 等待 vLLM 服务器启动的总超时时间(秒)。如果超时后服务器仍未启动,则会引发 ConnectionError。
  • vllm_group_port (int, 可选,默认为 51216) — 权重更新组的端口号。用于与 vLLM 服务器通信。除非端口被占用,否则无需更改。

控制共置 vLLM 执行的参数(仅在 `vllm_mode` 为 `"colocate"` 时使用)

  • vllm_gpu_memory_utilization (float,可选,默认值为 0.3) — 控制 vLLM 的 GPU 内存利用率。此设置仅在 vllm_mode 设置为 "colocate" 时生效。如果您使用的是 vllm_mode="server",则必须在通过 --vllm_gpu_memory_utilization 标志启动 vLLM 服务器时单独传递此参数。
  • vllm_max_model_length (int, 可选) — vLLM 的上下文窗口。请将其设置为至少等于数据集中的最大提示长度加上 max_completion_length;如果省略,则从模型配置中推断。
  • vllm_tensor_parallel_size (int,可选,默认为 1) — 控制 vLLM 的张量并行大小。此设置仅在 vllm_mode 设置为 "colocate" 时生效。如果您使用的是 vllm_mode="server",则必须在通过 --vllm_tensor_parallel_size 标志启动 vLLM 服务器时单独传递此参数。

控制训练的参数

  • beta (float,可选,默认为 0.05) — KL 系数。如果为 0.0,则不会加载参考模型,从而减少内存占用并提高训练速度。
  • num_iterations (int,可选,默认为 1) — 每个批次的迭代次数(在算法中记为 μ)。
  • epsilon (float, 可选, 默认为 0.2) — 用于裁剪的 Epsilon 值。
  • epsilon_high (float, 可选) — 用于裁剪的上限 epsilon 值。如果未指定,则默认与参数 epsilon 中指定的下限值相同。论文 DAPO 建议设置为 0.28。
  • reward_weights (list[float], 可选) — 每个奖励函数的权重。必须与奖励函数的数量相匹配。如果为 None,则所有奖励的权重均相等,权重为 1.0。
  • normalize_advantages (bool,可选,默认为 False) — 是否对优势值进行归一化。归一化按生成批次执行,使其均值为 0.0,标准差为 1.0。
  • reward_clip_range (tuple[float, float], 可选) — 奖励的裁剪范围,格式为 (min, max)。如果为 None,则不应用裁剪。
  • mask_truncated_completions (bool,可选,默认为 False) — 启用后,被截断的补全内容将被排除在损失计算之外,从而避免其受到错误惩罚并在训练过程中引入噪声。根据 DAPO 论文,这是保证训练稳定性的良好实践。
  • sync_ref_model (bool,可选,默认为 False) — 是否每隔 ref_model_sync_steps 步使用 ref_model_mixup_alpha 参数将参考模型与当前活动模型进行同步。此同步机制源自 TR-DPO 论文。
  • ref_model_mixup_alpha (float,可选,默认为 0.6) — 来自 TR-DPO 论文的 α 参数,用于控制更新过程中当前策略与先前参考策略之间的混合比例。参考策略根据以下公式更新:π_ref = α * π_θ + (1 - α) * π_ref_prev。要使用此参数,必须将 sync_ref_model=True 设置为 sync_ref_model=True。
  • ref_model_sync_steps (int,可选,默认为 512) — 来自 TR-DPO 论文的 τ 参数,用于确定当前策略与参考策略同步的频率。要使用此参数,必须 设置 sync_ref_model=True。

控制日志记录的参数

  • log_completions (bool,可选,默认为 False) — 是否每隔 logging_steps 步记录一组 (prompt, completion) 对的样本。如果已安装 rich,则会打印该样本。如果启用了 wandb 和/或 trackio 日志记录,则会将其记录到 wandb 和/或 trackio。
  • log_multimodal (bool,可选,默认为 True) — 是否将多模态内容(图像、视频等)与补全内容一起记录。当使用高分辨率多模态数据时,可禁用此选项以减小日志大小。
  • num_completions_to_print (int, 可选) — 使用 rich 打印的补全数量。如果为 None,则记录所有补全。
  • log_unique_prompts (bool,可选,默认为 False) — 是否记录唯一的提示词。如果为 True,则仅记录唯一的提示词。如果为 False,则记录所有提示词。

控制由 Transformers 连续批处理驱动的生成加速的参数

  • use_transformers_continuous_batching (bool, 可选,默认为 False) — 是否使用 transformers 的连续批处理引擎来生成补全。需要 transformers>=5.8.0。
  • transformers_continuous_batching_config (dict, 可选) — ContinuousBatchingConfig 的关键字参数。

已弃用的参数

  • use_transformers_paged —

    参数 use_transformers_paged 已弃用,并将在 v2.0.0 版本中移除。请改用 use_transformers_continuous_batching。

RLOOTrainer 的配置类。

此类仅包含 RLOO 训练特有的参数。完整的训练参数列表,请参阅 TrainingArguments 文档。请注意,此类中的默认值可能与 TrainingArguments 中的不同。

使用 HfArgumentParser,我们可以将该类转换为 argparse 参数,从而在命令行中指定。

这些参数的默认值与 TrainingArguments 不同:

  • logging_steps:默认值为 10,而非 500。
  • gradient_checkpointing:默认值为 True,而非 False。
  • bf16:如果未设置 fp16,则默认值为 True,而不是 False。
  • learning_rate:默认值为 1e-6,而非 5e-5。

参考

  1. RLOO 论文
  2. 回归基础:重新审视 LLM 中基于人类反馈学习的 REINFORCE 风格优化
  3. 论文 - REINFORCE++:一种用于对齐大语言模型的简单高效方法
  4. 博客文章 - 将 RL 重新引入 RLHF
  5. 博客文章 - 解析 RLHF 及其变体:进展与工程实践洞察
  6. Youtube - RLOO:一种用于从人类反馈中学习大语言模型的低成本优化方法

从旧版实现(0.21 及更早版本)迁移指南

随着 0.22.0 版本的发布,我们重新设计了 RLOOTrainer,使其与库中其他在线训练器(例如 GRPOTrainer)更加一致。这一新实现引入了对训练器配置参数和整体结构的若干更改。 以下是 RLOOConfig 主要更改的摘要:

TRL ≤ 0.21.xTRL ≥ 0.22.0
rloo_k重命名为 num_generations
cliprange重命名为 epsilon
kl_coef重命名为 beta
exp_name已重命名为 run_name。使用 run_name = f"{exp_name}__{seed}__{int(time.time())}" 可复现旧行为
normalize_reward已重命名为 normalize_advantages。注意:该函数始终会对优势值进行归一化(尽管旧名称并非如此)
num_ppo_epochs重命名为 num_iterations(默认值:1)
token_level_kl已移除 – KL 现在仅在序列级别计算
dataset_num_proc已移除 – 它未被使用
num_mini_batches重命名为 steps_per_generation
total_episodes请改用 max_steps=total_episodes / gradient_accumulation_steps
local_rollout_forward_batch_size已移除 – 现在会自动设置为 per_device_train_batch_size(或在评估期间设置为 per_device_eval_batch_size)
num_sample_generations已移除 – 使用 logging_steps 来控制生成日志的频率
response_length重命名为 max_completion_length(默认值:512)
stop_token已移除
stop_token_id已移除 – 请改用 processing_class.eos_token_id
missing_eos_penalty已移除 – 使用自定义奖励函数进行替代,该函数检查 eos_token_id 是否位于 completion_ids 中

以下是 RLOOTrainer 主要变更的摘要:

TRL ≤ 0.21.xTRL ≥ 0.22.0
config重命名为 args
reward_model重命名为 reward_funcs,现在同时支持奖励模型和自定义奖励函数
policy重命名为 model
ref_policy已移除 – 参考模型现在会自动从 model 创建
data_collator已移除
Update on GitHub