TRL documentation

RLOO Trainer

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

and get access to the augmented documentation experience

to get started

RLOO Trainer

model badge

Aperçu

TRL prend en charge le RLOO Trainer pour l’entraînement de modèles de langage, tel que décrit dans l’article Back to Basics: Revisiting REINFORCE Style Optimization for Learning from Human Feedback in LLMs de Arash Ahmadian, Chris Cremer, Matthias Gallé, Marzieh Fadaee, Julia Kreutzer, Ahmet Üstün et Sara Hooker.

L'abstract de l'article est le suivant :

L’alignement de l’IA, sous la forme de l’apprentissage par renforcement à partir des retours humains (RLHF), est de plus en plus considéré comme un ingrédient crucial pour les grands modèles de langage à haute performance. L’optimisation de politique proximale (PPO) a été positionnée par la littérature récente comme la méthode canonique pour la partie RL du RLHF. Cependant, elle implique à la fois un coût de calcul élevé et un réglage sensible des hyperparamètres. Nous posons que la plupart des principes motivationnels qui ont conduit au développement de PPO sont moins une préoccupation pratique dans le contexte du RLHF, et nous plaidons pour une méthode moins coûteuse en calcul qui préserve, voire augmente, les performances. Nous réexaminons la formulation de l’alignement à partir des préférences humaines dans le contexte de l’apprentissage par renforcement. En gardant la simplicité comme principe directeur, nous montrons que de nombreux composants de PPO sont inutiles dans un contexte de RLHF et que des variantes d’optimisation de type REINFORCE, bien plus simples, surpassent à la fois PPO et les méthodes « sans RL » récemment proposées, telles que DPO et RAFT. Notre travail suggère qu’une adaptation soignée aux caractéristiques d’alignement des LLM permet de tirer parti de l’optimisation RL en ligne à faible coût.

Cette méthode de post-entraînement a été proposée par Costa Huang puis refactorisée par Shirin Yamani.

Démarrage rapide

Cet exemple montre comment entraîner un modèle à l'aide de la méthode RLOO. Nous entraînons un modèle Qwen 0.5B Instruct avec les prompts du jeu de données DeepMath-103K. Vous pouvez consulter les données du jeu de données ici :

Voici le script pour entraîner le modèle. accuracy_reward nécessite math-verify, que vous pouvez installer avec 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()

Exécutez le script à l'aide de la commande suivante :

accelerate launch train_rloo.py

Explorer plus en profondeur la méthode RLOO

RLOO est un algorithme d'apprentissage en ligne, ce qui signifie qu'il s'améliore de manière itérative en utilisant les données générées par le modèle entraîné lui-même pendant l'entraînement. L'intuition derrière l'objectif de RLOO est de maximiser l'avantage des complétions générées, tout en veillant à ce que le modèle reste proche de la politique de référence. Pour comprendre le fonctionnement de RLOO, il peut être décomposé en quatre étapes principales : génération des complétions, calcul de l'avantage, estimation de la divergence KL et calcul de la perte.

RLOO

Générer des complétions

À chaque étape d'entraînement, nous échantillonnons un lot de prompts et générons un ensemble de G G complétions pour chaque prompt (notées oi o_i ).

Calculer la récompense

Dans RLOO, la récompense se compose de deux éléments : la récompense fournie par le modèle de récompense (ou la fonction de récompense) et une pénalité KL qui décourage la politique de s'écarter trop d'une politique de référence fixe

  1. Pour chacune des G G séquences générées oi=(oi,1,…,oi,T) o_i = (o_{i,1}, \dots, o_{i,T}) conditionnées par une requête q q , nous calculons une récompense scalaire à l'aide d'un modèle de récompense R(oi,q) R(o_i, q) .
  2. Simultanément, nous estimons la divergence de KL entre la politique actuelle πθ \pi_\theta et la politique de référence fixe πref \pi_{\text{ref}} sur la séquence. L'estimation de KL pour la séquence oi o_i est : 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})}.

La récompense finale attribuée à la séquence oi o_i est alors la suivante : ri=R(oi,q)−β DKL ⁣[πθ∥πref], r_i = R(o_i, q) - \beta \, \mathbb{D}_{\mathrm{KL}}\!\left[\pi_\theta \|\pi_{\mathrm{ref}}\right],

où β>0 \beta > 0 contrôle l'intensité de la pénalité KL.

Dans un contexte purement en ligne (num_iterations = 1, par défaut), les données sont générées par la politique actuelle. Dans ce cas, la pénalité KL est calculée directement à l'aide de la politique actuelle.

Dans un contexte plus général (par exemple, plusieurs étapes de gradient par lot), les données sont plutôt générées par un instantané antérieur πold \pi_{\text{old}} . Pour maintenir la pénalité cohérente avec la distribution d'échantillonnage, la divergence KL est définie par rapport à cette politique :

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

De manière équivalente, pour une séquence échantillonnée o o , l'estimation de Monte Carlo est :

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})}.

Calcul de l'avantage

Une fois les récompenses de chaque complétion calculées, nous déterminons une ligne de base en prenant la récompense moyenne de tous les autres échantillons du même lot, en excluant l'échantillon courant. Cette ligne de base est utilisée pour réduire la variance de l'estimation du gradient de politique. L'avantage de chaque complétion est ensuite obtenu en soustrayant cette ligne de base leave-one-out de sa propre récompense.

Formellement, pour un lot de G complétions, la référence pour la complétion est :bi=1G−1∑j≠irj b_i = \frac{1}{G-1} \sum_{j \neq i} r_j

puis l'avantage pour chaque complétion est calculé comme la différence entre sa récompense et la ligne de base : Ai=ri−bi A_i = r_i - b_i

Calcul de la perte

La perte REINFORCE est simplement définie comme : 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)

En pratique, effectuer plusieurs pas de gradient sur le même lot rend les actions effectivement hors politique par rapport aux paramètres actuels. Pour corriger cela, nous introduisons le ratio d'échantillonnage d'importance. Pour éviter des mises à jour excessivement importantes lorsque la politique change entre l'échantillonnage et les pas de gradient, nous tronquons ce ratio : 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)

Dans un contexte entièrement en ligne, en une seule étape (par défaut), πθ(oi∣q)πθold(oi∣q)=1 \frac{\pi_\theta(o_i \mid q)}{\pi_{\theta_\text{old}}(o_i \mid q)} = 1 et cela se réduit à REINFORCE standard.

Contrairement à GRPO, RLOO ne rétropropage pas les gradients à travers le terme KL. Ici, le KL est utilisé uniquement pour la mise en forme de la récompense : il est calculé sous no_grad et intégré à la récompense scalaire ri r_i , qui devient l'avantage détaché A^i \hat{A}_i .

En d'autres termes, le seul chemin par lequel le gradient pénètre dans la politique est le terme du ratio d'importance πθ(oi∣q)πθold(oi∣q) \frac{\pi_\theta(o_i \mid q)}{\pi_{\theta_\text{old}}(o_i \mid q)} , et le terme KL ne rétropropage pas dans la politique.

En revanche, GRPO ajoute un terme KL explicite et différentiable à son objectif, calculé à la volée avec la politique actuelle πθ \pi_\theta , de sorte que son gradient traverse à la fois le terme de ratio et la pénalité KL.

Métriques journalisées

Lors de l'entraînement et de l'évaluation, nous enregistrons les métriques suivantes :

  • num_tokens : le nombre total de jetons traités jusqu'à présent, prompts et complétions inclus.
  • step_time : le temps moyen (en secondes) par étape d'entraînement (génération incluse).
  • completions/mean_length : La longueur moyenne des complétions générées.
  • completions/min_length : La longueur minimale des complétions générées.
  • completions/max_length : La longueur maximale des complétions générées.
  • completions/mean_terminated_length : La longueur moyenne des complétions générées qui se terminent par EOS.
  • completions/min_terminated_length : Longueur minimale des complétions générées qui se terminent par EOS.
  • completions/max_terminated_length : La longueur maximale des complétions générées qui se terminent par EOS.
  • completions/clipped_ratio : Le ratio des complétions tronquées (clippées).
  • rewards/{reward_func_name}/mean : la récompense moyenne issue d'une fonction de récompense spécifique.
  • rewards/{reward_func_name}/std : l'écart-type de la récompense issue d'une fonction de récompense spécifique.
  • reward : La récompense moyenne globale après avoir additionné les récompenses sur l'ensemble des fonctions (pondérées par reward_weights).
  • reward_std : l'écart-type des récompenses cumulées sur l'ensemble des fonctions (pondérées par reward_weights), calculé sur le lot complet.
  • frac_reward_zero_std : La proportion d'échantillons du lot de génération dont l'écart-type de la récompense est nul, ce qui implique qu'il y a peu de diversité pour ce prompt (toutes les réponses sont correctes ou incorrectes).
  • entropy : Entropie moyenne des prédictions de jetons sur l’ensemble des complétions générées. (Si mask_truncated_completions=True, les jetons des séquences masquées sont exclus.)
  • aux_loss : perte auxiliaire d'équilibrage de charge d'un modèle Mixture-of-Experts, avant d'être mise à l'échelle par router_aux_loss_coef et ajoutée à la perte. Journalisée uniquement lorsque le modèle est un modèle MoE et que router_aux_loss_coef est non nul.
  • kl : divergence KL moyenne entre le modèle et le modèle de référence, calculée sur les complétions générées. Journalisé uniquement si beta est non nul.
  • clip_ratio/region_mean: Le ratio des probabilités de séquences pour lesquelles l'objectif RLOO est tronqué afin de rester dans la région de confiance : 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)} . Une valeur plus élevée signifie que plus d'échantillons sont tronqués, ce qui limite l'ampleur des modifications de la politique πθ\pi_\theta peut subir.
  • clip_ratio/low_mean: Le ratio moyen des probabilités de séquences qui ont été rognées sur la borne inférieure de la région de confiance : ri,t(θ)<1−ϵlowr_{i,t}(\theta) < 1 - \epsilon_\mathrm{low}.
  • clip_ratio/low_min: L'indicateur de clip minimal par complétion sur la borne inférieure de la région de confiance : ri,t(θ)<1−ϵlowr_{i,t}(\theta) < 1 - \epsilon_\mathrm{low}.
  • clip_ratio/high_mean: Le ratio moyen des probabilités de séquence qui ont été rognées sur la borne supérieure de la région de confiance : ri,t(θ)>1+ϵhighr_{i,t}(\theta) > 1 + \epsilon_\mathrm{high}.
  • clip_ratio/high_max: L'indicateur de clip maximal par complétion sur la borne supérieure de la région de confiance : ri,t(θ)>1+ϵhighr_{i,t}(\theta) > 1 + \epsilon_\mathrm{high}.

Personnalisation

Accélérer l'entraînement avec la génération propulsée par vLLM

La génération est souvent le principal goulot d'étranglement lors de l'entraînement avec des méthodes en ligne. Pour accélérer la génération, vous pouvez utiliser vLLM, un moteur d'inférence à haut débit et faible latence pour les LLM. Pour l'activer, installez d'abord le package avec

pip install trl[vllm]

Nous prenons en charge deux façons d'utiliser vLLM pendant l'entraînement : le mode serveur et le mode colocalisé.

Option 1 : Mode Colocate

Dans ce mode, vLLM s’exécute dans le processus d’entraînement et partage la mémoire GPU avec le modèle d’entraînement. Cela évite de démarrer un serveur séparé et peut améliorer l’utilisation des GPU, mais peut entraîner une concurrence mémoire sur les GPU d’entraînement. C’est le mode par défaut.

from trl import RLOOConfig

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

Option 2 : Mode serveur

Dans ce mode, vLLM s’exécute dans un processus distinct (et utilise des GPU séparés) et communique avec l’entraîneur via HTTP. C’est idéal si vous disposez de GPU dédiés à l’inférence.

  1. Démarrer le serveur vLLM :

    VLLM_SERVER_DEV_MODE=1 vllm serve <model_name> \
        --weight-transfer-config '{"backend": "nccl"}' \
        --logprobs-mode processed_logprobs \
        --max-logprobs -1
  2. Activer le mode serveur dans votre script d’entraînement :

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

Assurez-vous que le serveur utilise des GPU différents de ceux du formateur, sinon vous risquez de rencontrer des erreurs NCCL. Vous pouvez spécifier les GPU à utiliser avec la variable d’environnement CUDA_VISIBLE_DEVICES.

En fonction de la taille du modèle et des besoins globaux en mémoire GPU pour l’entraînement, vous devrez peut-être ajuster le paramètre vllm_gpu_memory_utilization dans RLOOConfig afin d’éviter une sous-utilisation ou des erreurs de mémoire insuffisante.

Nous mettons à disposition un HF Space pour vous aider à estimer l'utilisation recommandée de la mémoire GPU en fonction de la configuration de votre modèle et des paramètres de votre expérience. Utilisez-le simplement comme suit pour obtenir la recommandation de vllm_gpu_memory_utilization :

Si la valeur recommandée ne fonctionne pas dans votre environnement, nous suggérons d'ajouter une petite marge (par exemple, +0.05 ou +0.1) à la valeur recommandée pour garantir la stabilité.

Si vous rencontrez toujours des erreurs de mémoire insuffisante, définissez vllm_enable_sleep_mode sur True et les paramètres vllm ainsi que le cache seront déchargés pendant l'étape d'optimisation. Pour plus d'informations, consultez Réduire l'utilisation de la mémoire avec le mode veille de vLLM.

Par défaut, RLOO utilise MASTER_ADDR=localhost et MASTER_PORT=12345 pour vLLM, mais vous pouvez remplacer ces valeurs en définissant les variables d’environnement en conséquence.

Pour plus d'informations, consultez Accélérer l'entraînement avec vLLM.

RLOO à grande échelle : entraîner un modèle de 70B+ sur plusieurs nœuds

Lors de l'entraînement de grands modèles comme Qwen2.5-72B, plusieurs optimisations clés sont nécessaires pour rendre l'entraînement efficace et scalable sur plusieurs GPU et nœuds. Elles incluent :

  • DeepSpeed ZeRO Stage 3 : ZeRO exploite le parallélisme de données pour distribuer les états du modèle (poids, gradients, états de l’optimiseur) sur plusieurs GPU et CPU, réduisant ainsi les besoins en mémoire et en calcul sur chaque appareil. Comme les grands modèles ne peuvent pas tenir sur un seul GPU, l’utilisation de ZeRO Stage 3 est requise pour entraîner ce type de modèles. Pour plus de détails, voir DeepSpeed Integration.
  • Accelerate : Accelerate est une bibliothèque qui simplifie l’entraînement distribué sur plusieurs GPU et nœuds. Elle fournit une API simple pour lancer l’entraînement distribué et gère les complexités de l’entraînement distribué, telles que le parallélisme de données, l’accumulation de gradients et le chargement distribué des données. Pour plus de détails, voir Distributing Training.
  • vLLM : Reportez-vous à la section précédente pour savoir comment utiliser vLLM afin d’accélérer la génération.

Voici un exemple de script SLURM pour entraîner un modèle de 70B avec RLOO sur plusieurs nœuds. Ce script entraîne un modèle sur 4 nœuds et utilise le 5e nœud pour la génération propulsée par 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}" accelerate launch \
     --config_file examples/accelerate_configs/deepspeed_zero3.yaml \
     --num_processes 32 \
     --num_machines 4 \
     --main_process_ip ${NODELIST[0]} \
     --machine_rank $SLURM_PROCID \
     --rdzv_backend c10d \
     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,
        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()

Utiliser une fonction de récompense personnalisée

Le RLOOTrainer prend en charge l'utilisation de fonctions de récompense personnalisées à la place des modèles de récompense denses. Pour garantir la compatibilité, votre fonction de récompense doit satisfaire aux exigences suivantes :

Les fonctions de récompense peuvent être des fonctions Python synchrones ou des coroutines asynchrones async def. Lorsque vous fournissez plusieurs fonctions de récompense asynchrones, elles sont attendues de manière concurrente (exécutées en parallèle via asyncio.gather) afin que leur latence se superpose.

  1. Arguments d'entrée :

    • La fonction doit accepter les éléments suivants en tant qu'arguments nommés :

      • prompts (contient les prompts),
      • completions (contient les complétions générées),
      • completion_ids (contient les complétions tokenisées),
      • trainer_state (TrainerState) : l'état actuel du formateur. Il peut être utilisé pour implémenter des fonctions de récompense dynamiques, comme l'apprentissage par curriculum, où la récompense est ajustée en fonction de la progression de l'entraînement.
      • log_extra : un appelable log_extra(column: str, values: list) pour ajouter des colonnes supplémentaires au tableau des complétions. Voir l’exemple 6.
      • log_metric : un appelable log_metric(name: str, value: float) pour journaliser des métriques scalaires sous forme de graphiques aux côtés de kl, entropy, etc. Voir l’exemple 6.
      • Tous les noms de colonnes (sauf prompt) que le jeu de données peut contenir. Par exemple, si le jeu de données contient une colonne nommée ground_truth, la fonction sera appelée avec ground_truth en tant qu'argument par mot-clé.

      La façon la plus simple de respecter cette exigence est d'utiliser **kwargs dans la signature de la fonction.

    • Selon le format du jeu de données, l'entrée variera :

      • Pour le format standard, prompts et completions seront des listes de chaînes de caractères.
      • Pour le format conversationnel, prompts et completions seront des listes de dictionnaires de messages.
  2. Valeur de retour : La fonction doit retourner une liste de flottants. Chaque flottant représente la récompense correspondant à une seule complétion.

Exemple 1 : Récompenser les complétions plus longues

Voici un exemple de fonction de récompense pour un format standard qui récompense les complétions plus longues :

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]

Vous pouvez le tester comme suit :

>>> 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]

Exemple 1.1 : Récompenser les complétions plus longues (en fonction du nombre de caractères)

Identique à l'exemple précédent, mais cette fois la fonction de récompense est basée sur le nombre de caractères plutôt que sur les jetons.

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]

Vous pouvez le tester comme suit :

>>> 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]

Exemple 2 : Récompenser les complétions avec un format spécifique

Voici un exemple de fonction de récompense qui vérifie si l’achèvement respecte un format spécifique. Cet exemple s’inspire de la fonction format reward utilisée dans l’article DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning. Elle est conçue pour un format conversationnel, où les invites et les achèvements sont constitués de messages structurés.

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]

Vous pouvez tester cette fonction comme suit :

>>> 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]

Exemple 3 : Récompenser les complétions sur la base d'une référence

Voici un exemple de fonction de récompense qui vérifie si la complétion est correcte. Cet exemple s’inspire de la fonction accuracy reward utilisée dans l’article DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning. Cet exemple est conçu pour le format standard, où le jeu de données contient une colonne nommée 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)]

Vous pouvez tester cette fonction comme suit :

>>> 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]

Exemple 4 : Fonctions de récompense multi-tâches

Voici un exemple d'utilisation de plusieurs fonctions de récompense dans le RLOOTrainer. Dans cet exemple, nous définissons deux fonctions de récompense spécifiques à la tâche : math_reward_func et coding_reward_func. La fonction math_reward_func attribue une récompense aux problèmes de mathématiques en fonction de leur exactitude, tandis que la fonction coding_reward_func attribue une récompense aux problèmes de codage en fonction du fait que la solution fonctionne.

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()

Dans cet exemple, math_reward_func et coding_reward_func sont conçus pour fonctionner avec un jeu de données mixte contenant à la fois des problèmes de mathématiques et de codage. La colonne task du jeu de données est utilisée pour déterminer quelle fonction de récompense appliquer à chaque problème. S'il n'existe pas de fonction de récompense pertinente pour un échantillon du jeu de données, la fonction de récompense renvoie None, et le RLOOTrainer poursuit avec les fonctions et tâches valides. Cela permet au RLOOTrainer de gérer plusieurs fonctions de récompense avec des domaines d'application différents.

Notez que le RLOOTrainer ignorera les récompenses None renvoyées par les fonctions de récompense et ne tiendra compte que des récompenses renvoyées par les fonctions pertinentes. Cela garantit que le modèle est entraîné sur les tâches pertinentes et ignore les tâches pour lesquelles il n’existe pas de fonction de récompense pertinente.

Exemple 5 : Fonctions de récompense asynchrones

Les fonctions de récompense personnalisées peuvent également être définies sous forme de coroutines async def. Cela est utile si votre récompense dépend d'une E/S lente (par exemple, l'appel d'un service distant). Lorsque vous passez plusieurs fonctions de récompense asynchrones, RLOOTrainer les exécute de manière concurrente afin que leur latence se superpose.

Voici un exemple minimal d'une fonction de récompense asynchrone qui simule une opération limitée par l'E/S :

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]

Exemple 6 : Journalisation de colonnes et de métriques supplémentaires

Voici un exemple de fonction de récompense qui journalise des colonnes supplémentaires dans le tableau des complétions et des métriques scalaires sous forme de graphiques.

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

Passer la fonction de récompense à l'entraîneur

Pour utiliser votre fonction de récompense personnalisée, transmettez-la au RLOOTrainer comme suit :

from trl import RLOOTrainer

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

Vous pouvez passer plusieurs fonctions de récompense sous forme de liste ; cette liste peut inclure à la fois des fonctions synchrones et asynchrones :

from trl import RLOOTrainer

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

et la récompense sera calculée comme la somme des récompenses de chaque fonction, ou comme la somme pondérée si reward_weights est fourni dans la configuration.

Notez que RLOOTrainer prend en charge plusieurs fonctions de récompense de types différents. Consultez la documentation des paramètres pour plus de détails.

Entraînement des modèles de vision et de langage

RLOO prend en charge l’entraînement de modèles Vision-Language (VLM) sur des jeux de données multimodaux contenant à la fois du texte et des images.

Modèles pris en charge

Testé avec :

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

La compatibilité avec tous les VLM n'est pas garantie. Si vous pensez qu'un modèle devrait être pris en charge, n'hésitez pas à ouvrir un ticket sur GitHub — ou mieux encore, à soumettre une pull request avec les modifications nécessaires.

Démarrage rapide

Utilisez rloo_vlm.py pour l’ajustement fin d’un VLM. Exemple de commande pour l’entraînement sur lmms-lab/multimodal-open-r1-8k-verified :

accelerate launch \
  --config_file=examples/accelerate_configs/deepspeed_zero3.yaml \
  examples/rloo_visual_math/rloo_visual_math.py \
  --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

Conseils de configuration

  • Utiliser LoRA sur les couches de projection vision-langage
  • Activer la quantification 4 bits pour réduire l'utilisation de la mémoire
  • Les VLM sont gourmands en mémoire — commencez par des tailles de lot plus petites
  • La plupart des modèles sont compatibles avec vLLM (modes server et colocate)

Format du jeu de données

Chaque exemple d'entraînement doit inclure :

  • prompt : texte formaté via le modèle de discussion du processeur
  • image/images : Image PIL ou liste d'Images PIL

Le formateur gère automatiquement la conversion des images en tenseurs via le processeur d'images du modèle.

RLOOTrainer

classe 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 )

Paramètres

  • model (str ou PreTrainedModel ou PeftModel) — Modèle à entraîner. Peut être l'un des éléments suivants :
    • Une chaîne de caractères, correspondant à l'identifiant du modèle d'un modèle pré-entraîné hébergé dans un dépôt de modèles sur huggingface.co, ou à un chemin vers un dossier contenant les poids du modèle enregistrés à l'aide de save_pretrained, par exemple './my_model_directory/'. Le modèle est chargé à l'aide de <ModelArchitecture>.from_pretrained (où <ModelArchitecture> est dérivé de la configuration du modèle) avec les arguments nommés de args.model_init_kwargs. Si dtype n'est pas spécifié dans args.model_init_kwargs, la valeur par défaut est float32. Cela diffère de from_pretrained, où (depuis Transformers v5) le type de données est déduit de la configuration du modèle.
    • Un objet PreTrainedModel. Seuls les modèles de langage causaux sont pris en charge.
    • Un objet PeftModel. Seuls les modèles de langage causaux sont pris en charge.
  • reward_funcs (RewardFunc | list[RewardFunc]) — Fonctions de récompense à utiliser pour le calcul des récompenses. Pour calculer les récompenses, nous appelons toutes les fonctions de récompense avec les prompts et les complétions, puis nous additionnons les récompenses. Peut être soit :
    • Une fonction de récompense unique, telle que :

      • Une chaîne de caractères : l'identifiant du modèle d'un modèle pré-entraîné hébergé dans un dépôt de modèles sur huggingface.co, ou un chemin vers un dossier contenant les poids du modèle enregistrés à l'aide de save_pretrained, par exemple './my_model_directory/'. Le modèle est chargé à l'aide de from_pretrained avec num_labels=1 et les arguments de mot-clé de args.model_init_kwargs.

      • Un objet PreTrainedModel : seuls les modèles de classification de séquence sont pris en charge.

      • Une fonction de récompense personnalisée : la fonction reçoit les prompts et les complétions générées, ainsi que les colonnes supplémentaires du jeu de données. Elle doit renvoyer une liste de récompenses. Les fonctions de récompense personnalisées peuvent être synchrones ou asynchrones et peuvent également renvoyer None lorsque la récompense ne s'applique pas à ces échantillons. Cela est utile pour l'entraînement multi-tâches où différentes fonctions de récompense s'appliquent à différents types d'échantillons. Lorsqu'une fonction de récompense renvoie None pour un échantillon, cette fonction de récompense est exclue du calcul de la récompense pour cet échantillon. Pour plus de détails, voir Utiliser une fonction de récompense personnalisée.

        L’état du formateur est également transmis à la fonction de récompense. L’état du formateur est une instance de TrainerState et peut être consulté en accédant à l’argument trainer_state de la signature de la fonction de récompense.

    • Une liste de fonctions de récompense, où chaque élément peut indépendamment être de l'un des types ci-dessus. Il est possible de mélanger différents types au sein de la liste (par exemple, un identifiant de modèle sous forme de chaîne et une fonction de récompense personnalisée).

  • args (RLOOConfig, facultatif) — Configuration de ce formateur. Si None, une configuration par défaut est utilisée.
  • train_dataset (Dataset ou IterableDataset) — Jeu de données à utiliser pour l'entraînement. Il doit inclure une colonne "prompt". Toute colonne supplémentaire dans le jeu de données est ignorée. Le format des échantillons peut être l'un des deux suivants :
    • Standard : chaque échantillon contient du texte brut.
    • Conversationnel : chaque échantillon contient des messages structurés (par exemple, le rôle et le contenu).

    Lorsque train_dataset est un IterableDataset (par exemple un jeu de données en streaming), max_steps doit être défini dans les arguments d’entraînement, car sa longueur ne peut pas être déduite et le nombre total d’étapes d’entraînement est nécessaire pour limiter la boucle d’entraînement et configurer le planificateur de taux d’apprentissage.

  • eval_dataset (Dataset, IterableDataset, DatasetDict, IterableDatasetDict ou dict[str, Dataset | IterableDataset]) — Jeu de données à utiliser pour l'évaluation. Il doit répondre aux mêmes exigences que train_dataset.
  • processing_class (PreTrainedTokenizerBase, ProcessorMixin, facultatif) — Classe de traitement utilisée pour traiter les données. Le côté de bourrage doit être défini sur « left ». Si None, la classe de traitement est chargée à partir du nom du modèle avec from_pretrained. Un jeton de bourrage, tokenizer.pad_token, doit être défini. Si la classe de traitement n’a pas défini de jeton de bourrage, tokenizer.eos_token sera utilisé par défaut.
  • reward_processing_classes (PreTrainedTokenizerBase ou list[PreTrainedTokenizerBase], facultatif) — Classes de traitement correspondant aux fonctions de récompense spécifiées dans reward_funcs. Peut être soit :
    • Une seule classe de traitement : utilisée lorsque reward_funcs ne contient qu'une seule fonction de récompense.
    • Une liste de classes de traitement : doit correspondre à l’ordre et à la longueur des fonctions de récompense dans reward_funcs. Si défini sur None, ou si un élément de la liste correspondant à un PreTrainedModel est None, le tokenizer du modèle est chargé automatiquement à l’aide de from_pretrained. Pour les éléments de reward_funcs qui sont des fonctions de récompense personnalisées (non PreTrainedModel), les entrées correspondantes dans reward_processing_classes sont ignorées.
  • callbacks (liste de TrainerCallback, facultatif) — Liste de callbacks pour personnaliser la boucle d'entraînement. Ils seront ajoutés à la liste des callbacks par défaut détaillée ici.

    Si vous souhaitez supprimer l'un des callbacks par défaut utilisés, utilisez la méthode remove_callback.

  • optimizers (tuple[torch.optim.Optimizer | None, torch.optim.lr_scheduler.LambdaLR | None], facultatif, par défaut (None, None)) — Un tuple contenant l'optimiseur et le planificateur à utiliser. Par défaut, une instance de AdamW est utilisée sur votre modèle ainsi qu'un planificateur défini par get_linear_schedule_with_warmup et contrôlé par args.
  • quantization_config (BitsAndBytesConfig, facultatif) — Configuration de quantification utilisée lors du chargement du modèle à partir d'un identifiant de modèle. À combiner avec peft_config pour l'entraînement QLoRA. Ignoré si le modèle est déjà instancié.
  • peft_config (PeftConfig, facultatif) — Configuration PEFT utilisée pour envelopper le modèle. Si None, le modèle n'est pas enveloppé.

Trainer pour la méthode Reinforce Leave One Out (RLOO). Cet algorithme a été initialement proposé dans l'article Back to Basics: Revisiting REINFORCE Style Optimization for Learning from Human Feedback in LLMs.

Exemple :

>>> 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()

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

Paramètres

  • resume_from_checkpoint (str ou bool, facultatif) — Si c'est une str, chemin local d'un point de contrôle enregistré par une instance précédente de Trainer. Si c'est un bool et vaut True, charge le dernier point de contrôle dans args.output_dir enregistré par une instance précédente de Trainer. S'il est présent, l'entraînement reprendra à partir des états du modèle/optimiseur/planificateur chargés ici.
  • trial (optuna.Trial ou dict[str, Any], facultatif) — L’exécution de l’essai ou le dictionnaire d’hyperparamètres pour la recherche d’hyperparamètres.
  • ignore_keys_for_eval (list[str], facultatif) — Une liste de clés dans la sortie de votre modèle (s’il s’agit d’un dictionnaire) qui doivent être ignorées lors de la collecte des prédictions pour l’évaluation pendant l’entraînement.

Retourne

~trainer_utils.TrainOutput

Objet contenant le nombre d'étapes globales, la perte d'entraînement et les métriques.

Point d'entrée principal de l'entraînement.

save_model

< >

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

Enregistrera le modèle, ce qui vous permettra de le recharger à l'aide de from_pretrained().

N'enregistrera que depuis le processus principal.

push_to_hub

< >

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

Paramètres

  • commit_message (str, facultatif, par défaut "End of training") — Message à committer lors de la poussée.
  • blocking (bool, facultatif, par défaut True) — Indique si la fonction doit ne renvoyer que lorsque le git push est terminé.
  • token (str, facultatif, par défaut None) — Jeton avec permission d’écriture pour écraser les arguments originaux du Trainer.
  • revision (str, facultatif) — La révision git à partir de laquelle effectuer le commit. Par défaut, il s'agit de la tête de la branche « main ».
  • kwargs (dict[str, Any], facultatif) — Arguments supplémentaires passés à ~Trainer.create_model_card.

Téléchargez self.model et self.processing_class sur le hub de modèles 🤗 dans le dépôt self.args.hub_model_id.

RLOOConfig

classe 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 )

Paramètres qui contrôlent le modèle et le modèle de référence

  • model_init_kwargs (str ou dict[str, Any], facultatif) — Arguments par mot-clé pour from_pretrained, utilisés lorsque l’argument model du RLOOTrainer est fourni sous forme de chaîne de caractères. La valeur de revision est également utilisée lors du chargement des classes de traitement.
  • trust_remote_code (bool, facultatif, valeur par défaut False) — Indique s'il faut autoriser le chargement de modèles et de tokenizers qui incluent du code Python personnalisé depuis le Hub. Transmis à from_pretrained et from_pretrained. S'applique également au chargement des modèles de récompense et des tokenizers de récompense.
  • router_aux_loss_coef (float, facultatif) — Coefficient de la perte auxiliaire d’équilibrage de charge pour les modèles Mixture-of-Experts (MoE), ajoutée à la perte d’entraînement avec ce poids. Si non défini, la valeur déclarée par la configuration du modèle est utilisée. Définissez-le à 0.0 pour le désactiver. Échoue s’il est utilisé avec un modèle non-MoE.
  • disable_dropout (bool, facultatif, valeur par défaut False) — Indique s'il faut désactiver le dropout dans le modèle. Cela est utile pour l'entraînement avec un modèle de référence, car cela empêche le modèle de générer des logprobs différents pour la même entrée.

Paramètres qui contrôlent la prétraitement des données

  • remove_unused_columns (bool, facultatif, valeur par défaut False) — Indique s'il faut ne conserver que la colonne "prompt" dans le jeu de données. Si vous utilisez une fonction de récompense personnalisée qui nécessite une colonne autre que "prompts" et "completions", vous devez laisser cette valeur à False.
  • num_generations (int, facultatif, par défaut 2) — Nombre de générations à échantillonner par invite. La taille de lot effective (num_processes * per_device_batch_size
    • gradient_accumulation_steps) doit être divisible par cette valeur.
  • num_generations_eval (int, facultatif) — Nombre de générations à échantillonner lors de l'évaluation. Cela permet d'utiliser moins de générations pendant l'évaluation afin d'économiser des calculs. Si None, utilise la valeur de num_generations.
  • max_completion_length (int ou None, facultatif, par défaut 512) — Longueur maximale de la complétion générée.
  • ds3_gather_for_generation (bool, facultatif, valeur par défaut True) — Ce paramètre s’applique à DeepSpeed ZeRO-3. S’il est activé, les poids du modèle de politique sont regroupés pour la génération, ce qui améliore la vitesse de génération. Toutefois, désactiver cette option permet d’entraîner des modèles dont la taille dépasse la capacité VRAM d’une seule GPU, au prix d’une génération plus lente. La désactivation de cette option n’est pas compatible avec la génération vLLM.
  • shuffle_dataset (bool, facultatif, par défaut True) — Indique s'il faut mélanger le jeu de données d'entraînement.
  • pad_to_multiple_of (int ou str, facultatif, valeur par défaut "auto") — Si défini, les identifiants des prompts et des complétions seront rembourrés jusqu'à un multiple de cette valeur. "auto" rembourde jusqu'à un multiple de 128 sur Neuron (AWS Trainium), qui compile une fois par forme d'entrée, et ne rembourde pas ailleurs.

Paramètres qui contrôlent la génération

  • generation_batch_size (int, facultatif) — Taille de lot à utiliser pour la génération. Si None, la valeur par défaut est la taille de lot effective d’entraînement : per_device_train_batch_size * num_processes * steps_per_generation. Autrement dit, un lot de génération est traité par étape d’optimisation. Mutuellement exclusif avec steps_per_generation.
  • steps_per_generation (int, facultatif) — Nombre d'étapes par génération. Si None, la valeur par défaut est gradient_accumulation_steps. Mutuellement exclusif avec generation_batch_size.
  • temperature (float, facultatif, par défaut 1.0) — Température pour l'échantillonnage. Plus la température est élevée, plus les complétions sont aléatoires.
  • top_p (float, facultatif, par défaut 1.0) — Flottant qui contrôle la probabilité cumulée des jetons de tête à prendre en compte. Doit être dans (0, 1]. Définissez-le à 1.0 pour prendre en compte tous les jetons.
  • top_k (int, facultatif, par défaut 0) — Nombre de jetons du vocabulaire ayant la plus haute probabilité à conserver pour le filtrage top-k. Si la valeur est 0, le filtrage top-k est désactivé et tous les jetons sont pris en compte.
  • min_p (float, facultatif) — Probabilité minimale du jeton, qui sera mise à l'échelle par la probabilité du jeton le plus probable. Elle doit être une valeur comprise entre 0.0 et 1.0. Les valeurs typiques se situent dans la plage 0.01-0.2.
  • generation_kwargs (dict[str, Any], facultatif) — Arguments supplémentaires à passer à GenerationConfig (si vous utilisez transformers) ou SamplingParams (si vous utilisez vLLM) lors de l'échantillonnage des complétions. Cela permet de personnaliser davantage le comportement de la génération, par exemple en définissant suppress_tokens, num_beams, etc. S'il contient des clés qui entrent en conflit avec les autres paramètres de génération (comme min_p, top_p, etc.), ils les remplaceront.
  • chat_template_kwargs (dict[str, Any], facultatif) — Arguments supplémentaires à passer à la fonction apply_chat_template lors de la génération des complétions.
  • repetition_penalty (float, facultatif, par défaut 1.0) — Flottant qui pénalise les nouveaux jetons en fonction de leur présence dans le prompt et dans le texte généré jusqu’à présent. Les valeurs > 1.0 encouragent le modèle à utiliser de nouveaux jetons, tandis que les valeurs < 1.0 encouragent le modèle à répéter les jetons.
  • cache_implementation (str, facultatif) — Implémentation de la méthode de cache pour une génération plus rapide lorsque use_vllm est défini sur False.

Paramètres qui contrôlent l'accélération de la génération propulsée par vLLM

  • use_vllm (bool, facultatif, valeur par défaut False) — Indique s'il faut utiliser vLLM pour générer des complétions. Si défini sur True, l'entraîneur utilisera vLLM pour la génération au lieu de model.generate() par défaut. Nécessite l'installation de vllm.
  • vllm_mode (str, facultatif, valeur par défaut "colocate") — Mode à utiliser pour l'intégration vLLM lorsque use_vllm est défini sur True. Doit être l'un des "server" ou "colocate".
    • "server" : le formateur enverra les requêtes de génération à un serveur vLLM distinct. Assurez-vous qu’un serveur vLLM est en cours d’exécution (à démarrer avec vllm serve).
    • "colocate" : vLLM s’exécute dans le même processus et partage les GPU d’entraînement. Cela évite d’avoir besoin d’un serveur séparé, mais peut provoquer une concurrence pour les ressources avec l’entraînement.
  • vllm_model_impl (str, facultatif, par défaut "vllm") — Implémentation du modèle à utiliser pour vLLM. Doit être l'une des valeurs "transformers" ou "vllm". "transformers" : utiliser le backend transformers pour l'implémentation du modèle. "vllm" : utiliser la bibliothèque vllm pour l'implémentation du modèle.
  • vllm_enable_sleep_mode (bool, facultatif, valeur par défaut False) — Active le mode veille de vLLM pour décharger les poids et le cache pendant l'étape d'optimisation. Cela maintient l'utilisation de la mémoire GPU au plus bas, mais le réveil du moteur ajoute une latence de transfert hôte-appareil.
  • vllm_structured_outputs_regex (str, facultatif) — Expression régulière pour les sorties structurées de vLLM. Si None (par défaut), les sorties structurées sont désactivées.

Paramètres qui contrôlent le serveur vLLM (utilisés uniquement lorsque `vllm_mode` est `"server"`)

  • vllm_server_base_url (str, facultatif) — URL de base du serveur vLLM (par exemple, "http://localhost:8000"). Si fournie, vllm_server_host et vllm_server_port sont ignorés.
  • vllm_server_host (str, facultatif, par défaut "0.0.0.0") — Hôte du serveur vLLM auquel se connecter. Ignoré si vllm_server_base_url est fourni.
  • vllm_server_port (int, facultatif, par défaut 8000) — Port du serveur vLLM auquel se connecter. Ignoré si vllm_server_base_url est fourni.
  • vllm_server_timeout (float, facultatif, valeur par défaut 240.0) — Durée totale de temporisation, en secondes, à attendre pour que le serveur vLLM soit opérationnel. Si le serveur n'est pas opérationnel après le dépassement de ce délai, une ConnectionError est levée.
  • vllm_group_port (int, facultatif, valeur par défaut 51216) — Numéro de port du groupe de mise à jour des poids. Il est utilisé pour communiquer avec le serveur vLLM. Sauf si le port est occupé, il n'est pas nécessaire de le modifier.

Paramètres qui contrôlent l'exécution de vLLM co-localisée (utilisés uniquement lorsque `vllm_mode` est `"colocate"`)

  • vllm_gpu_memory_utilization (float, facultatif, valeur par défaut 0.3) — Contrôle l'utilisation de la mémoire GPU pour vLLM. Ce paramètre ne s'applique que lorsque vllm_mode est défini sur "colocate". Si vous utilisez vllm_mode="server", ce paramètre doit être transmis séparément lors du démarrage du serveur vLLM via le drapeau --vllm_gpu_memory_utilization.
  • vllm_max_model_length (int, facultatif) — Fenêtre de contexte pour vLLM. Réglez-la sur au moins la longueur maximale des prompts dans le jeu de données plus max_completion_length ; si elle est omise, elle est déduite de la configuration du modèle.
  • vllm_tensor_parallel_size (int, facultatif, valeur par défaut 1) — Contrôle la taille du parallélisme de tenseurs pour vLLM. Ce paramètre ne s’applique que lorsque vllm_mode est défini sur "colocate". Si vous utilisez vllm_mode="server", ce paramètre doit être transmis séparément lors du démarrage du serveur vLLM via le drapeau --vllm_tensor_parallel_size.

Paramètres qui contrôlent l'entraînement

  • beta (float, facultatif, par défaut 0.05) — Coefficient KL. Si 0.0, le modèle de référence n’est pas chargé, ce qui réduit l’utilisation de la mémoire et améliore la vitesse d’entraînement.
  • num_iterations (int, facultatif, par défaut 1) — Nombre d'itérations par lot (noté μ dans l'algorithme).
  • epsilon (float, facultatif, valeur par défaut 0.2) — Valeur pour le clipping.
  • epsilon_high (float, facultatif) — Valeur epsilon supérieure pour le rognage. Si elle n'est pas spécifiée, elle prend par défaut la même valeur que la borne inférieure spécifiée dans l'argument epsilon. L'article DAPO recommande 0.28.
  • reward_weights (list[float], facultatif) — Poids pour chaque fonction de récompense. Doit correspondre au nombre de fonctions de récompense. Si None, toutes les récompenses sont pondérées également avec un poids de 1.0.
  • normalize_advantages (bool, facultatif, par défaut False) — Indique s'il faut normaliser les avantages. La normalisation est effectuée par lot de génération pour obtenir une moyenne de 0.0 et un écart-type de 1.0.
  • reward_clip_range (tuple[float, float], facultatif) — Plage de rognage des récompenses sous la forme (min, max). Si None, aucun rognage n'est appliqué.
  • mask_truncated_completions (bool, facultatif, valeur par défaut False) — Lorsqu’il est activé, les complétions tronquées sont exclues du calcul de la perte, ce qui empêche qu’elles soient pénalisées à tort et introduisent du bruit pendant l’entraînement. Selon l’article DAPO, il s’agit d’une bonne pratique pour la stabilité de l’entraînement.
  • sync_ref_model (bool, facultatif, par défaut False) — Indique s'il faut synchroniser le modèle de référence avec le modèle actif toutes les ref_model_sync_steps étapes, en utilisant le paramètre ref_model_mixup_alpha. Cette synchronisation provient de l'article TR-DPO.
  • ref_model_mixup_alpha (float, facultatif, valeur par défaut 0.6) — Paramètre α issu de l'article TR-DPO, qui contrôle le mélange entre la politique actuelle et la politique de référence précédente lors des mises à jour. La politique de référence est mise à jour selon l'équation : π_ref = α * π_θ + (1 - α) * π_ref_prev. Pour utiliser ce paramètre, vous devez définir sync_ref_model=True.
  • ref_model_sync_steps (int, facultatif, valeur par défaut 512) — paramètre τ issu de l’article TR-DPO, qui détermine la fréquence à laquelle la politique actuelle est synchronisée avec la politique de référence. Pour utiliser ce paramètre, vous devez définir sync_ref_model=True.

Paramètres qui contrôlent la journalisation

  • log_completions (bool, facultatif, par défaut False) — Indique s'il faut consigner un échantillon de paires (prompt, completion) toutes les logging_steps étapes. Si rich est installé, l'échantillon est affiché. Si la journalisation wandb et/ou trackio est activée, il est consigné dans wandb et/ou trackio.
  • log_multimodal (bool, facultatif, valeur par défaut True) — Indique s'il faut journaliser le contenu multimodal (images, vidéos, etc.) avec les complétions. Désactivez cette option pour réduire la taille des journaux lors de l'utilisation de données multimodales haute résolution.
  • num_completions_to_print (int, facultatif) — Nombre de complétions à afficher avec rich. Si None, toutes les complétions sont journalisées.
  • log_unique_prompts (bool, facultatif, valeur par défaut False) — Indique s'il faut journaliser les prompts uniques. Si True, seuls les prompts uniques sont journalisés. Si False, tous les prompts sont journalisés.

Paramètres qui contrôlent l'accélération de la génération grâce au batch continu de Transformers

  • use_transformers_continuous_batching (bool, facultatif, valeur par défaut False) — Indique s'il faut utiliser le moteur de continuous batching de transformers pour générer des complétions. Nécessite transformers>=5.8.0.
  • transformers_continuous_batching_config (dict, facultatif) — Arguments par mot-clé pour ContinuousBatchingConfig.

Paramètres obsolètes

  • use_transformers_paged —

    Le paramètre use_transformers_paged est obsolète et sera supprimé dans la version v2.0.0. Utilisez use_transformers_continuous_batching à la place.

Classe de configuration pour le RLOOTrainer.

Cette classe ne contient que les paramètres spécifiques à l’entraînement RLOO. Pour la liste complète des arguments d’entraînement, veuillez consulter la documentation de TrainingArguments. Notez que les valeurs par défaut de cette classe peuvent différer de celles de TrainingArguments.

À l’aide de HfArgumentParser, nous pouvons transformer cette classe en arguments argparse qui peuvent être spécifiés sur la ligne de commande.

Ces paramètres ont des valeurs par défaut différentes de TrainingArguments :

  • logging_steps : la valeur par défaut est 10 au lieu de 500.
  • gradient_checkpointing : la valeur par défaut est True au lieu de False.
  • bf16 : passe à True par défaut si fp16 n’est pas défini, au lieu de False.
  • learning_rate : la valeur par défaut est 1e-6 au lieu de 5e-5.

Références

  1. Article RLOO
  2. Retour aux bases : réexamen de l’optimisation de type REINFORCE pour l’apprentissage à partir des retours humains dans les LLM
  3. Article - REINFORCE++ : une approche simple et efficace pour l’alignement des grands modèles de langage
  4. Article de blogue - Remettre le RL dans le RLHF
  5. Article de blog – Déchiffrer le RLHF et ses variantes : progrès et retours d’expérience en ingénierie pratique
  6. YouTube - RLOO : une optimisation rentable pour l'apprentissage à partir des retours humains sur les LLM

Guide de migration depuis l'ancienne implémentation (0.21 et versions antérieures)

Avec la publication de la version 0.22.0, nous avons repensé le RLOOTrainer pour qu'il soit plus aligné avec les autres entraîneurs en ligne de la bibliothèque, comme GRPOTrainer. Cette nouvelle implémentation introduit plusieurs modifications aux paramètres de configuration et à la structure générale de l'entraîneur. Voici un résumé des principales modifications pour RLOOConfig :

TRL ≤ 0.21.xTRL ≥ 0.22.0
rloo_krenommé en num_generations
cliprangerenommé en epsilon
kl_coefrenommé en beta
exp_namerenommé en run_name. Utilisez run_name = f"{exp_name}__{seed}__{int(time.time())}" pour reproduire l'ancien comportement
normalize_rewardrenommé en normalize_advantages. Remarque : cela normalisait toujours les avantages (malgré l'ancien nom)
num_ppo_epochsrenommé en num_iterations (valeur par défaut : 1)
token_level_klsupprimé – KL est désormais calculé uniquement au niveau de la séquence
dataset_num_procsupprimé – il n’était pas utilisé
num_mini_batchesrenommé en steps_per_generation
total_episodesutilisez max_steps=total_episodes / gradient_accumulation_steps à la place
local_rollout_forward_batch_sizesupprimé – désormais automatiquement défini sur per_device_train_batch_size (ou per_device_eval_batch_size lors de l'évaluation)
num_sample_generationssupprimé – utilisez logging_steps pour contrôler la fréquence de la journalisation de la génération
response_lengthrenommé en max_completion_length (valeur par défaut : 512)
stop_tokensupprimé
stop_token_idsupprimé – utilisez processing_class.eos_token_id à la place
missing_eos_penaltysupprimé – à reproduire avec une fonction de récompense personnalisée vérifiant si eos_token_id est présent dans completion_ids

Voici un résumé des principales modifications apportées à RLOOTrainer :

TRL ≤ 0.21.xTRL ≥ 0.22.0
configrenommé en args
reward_modelrenommé en reward_funcs, qui prend désormais en charge à la fois les modèles de récompense et les fonctions de récompense personnalisées
policyrenommé en model
ref_policysupprimé – le modèle de référence est désormais créé automatiquement à partir de model
data_collatorsupprimé
Update on GitHub