RLHF : Guide Complet — Apprentissage par Renforcement à partir de Retour Humain

RLHF : Guide Complet — Apprentissage par Renforcement à partir de Retour Humain

RLHF — Apprentissage par Renforcement à partir de Retour Humain

Résumé

Le RLHF (Reinforcement Learning from Human Feedback, ou apprentissage par renforcement à partir de retour humain) est une technique d’alignement qui permet de fine-tuner un modèle de langage pré-entraîné afin qu’il produise des réponses conformes aux préférences humaines. Cette méthode repose sur trois piliers fondamentaux : la collecte de données de préférence auprès d’annotateurs humains, l’entraînement d’un modèle de récompense capable de scorer n’importe quelle réponse, et l’optimisation du modèle de langage via un algorithme de renforcement tel que PPO (Proximal Policy Optimization), régularisé par une pénalité de divergence KL pour éviter la dégénérescence du modèle.

Le RLHF est au cœur du succès de modèles fondateurs tels que ChatGPT, Claude et Gemini. Sans cette technique, ces systèmes produiraient des réponses certes fluides mais potentiellement toxiques, biaisées, ou inutiles — car un modèle pré-entraîné uniquement sur du texte internet n’a aucune notion de ce qui est “poli”, “utile” ou “sûr”. Le RLHF comble précisément cette lacune.


Principe Mathématique du RLHF

Le RLHF se déroule en trois étapes distinctes, chacune reposant sur des fondements mathématiques rigoureux.

Étape 1 — Entraînement du modèle de récompense r_φ(x, y)

La première étape consiste à collecter des comparaisons humaines par paires. Pour une même question (ou prompt) x, on demande à des annotateurs humains de comparer deux réponses y₁ et y₂ générées par le modèle, et de désigner la réponse préférée y_w (winner) et la réponse rejetée y_l (loser).

Avec ces paires (x, y_w, y_l), on entraîne un modèle de récompense paramétré par φ, noté r_φ(x, y), dont la fonction de perte s’écrit :

L_RM(φ) = -E_[x,y_w,y_l)[log(σ(r_φ(x, y_w) - r_φ(x, y_l)))]

σ désigne la fonction sigmoïde. Cette loss est une version réécrite de la régression logistique par paires (Bradley-Terry model). Intuitivement, le modèle de récompense apprend à attribuer un score plus élevé aux réponses préférées par les humains. Plus la différence r_φ(x, y_w) – r_φ(x, y_l) est grande, plus la probabilité prédite est proche de 1, et plus la loss est faible.

Le modèle de récompense est typiquement initialisé à partir du même modèle de langage pré-entraîné, avec la couche de tête (head) remplacée par un scalaire de régression. On retire la couche de langage final et on ajoute un classifieur linéaire qui prend la représentation du dernier token et produit un score scalaire.

Étape 2 — Gel du modèle de référence π_ref

Une fois le modèle de récompense entraîné, on gèle (freeze) le modèle de langage initial. Ce modèle gelé, noté π_ref, servira de référence durant l’étape de fine-tuning. Son rôle est crucial : il empêche le modèle fine-tuné de trop s’éloigner de son comportement initial, ce qui éviterait la dégénérescence (le modèle pourrait apprendre à maximiser la récompense de manière pathologique, produisant des réponses absurdes mais bien scorées).

Étape 3 — Fine-tuning par PPO avec pénalité KL

C’est l’étape centrale du RLHF. On optimise le modèle de langage π_θ (paramétré par θ) en utilisant l’algorithme PPO (Proximal Policy Optimization). L’objectif à maximiser est le suivant :

J(θ) = E_[x~D, y~π_θ(·|x)][r_φ(x, y) - β · KL(π_θ(·|x) || π_ref(·|x))]

Décomposons cet objectif :

  • r_φ(x, y) : la récompense attribuée par le modèle de récompense à la réponse y pour le prompt x. Le modèle de langage est incité à générer des réponses qui reçoivent des scores élevés.
  • KL(π_θ(·|x) || π_ref(·|x)) : la divergence de Kullback-Leibler entre la distribution du modèle fine-tuné et celle du modèle de référence. Cette mesure quantifie à quel point π_θ s’est éloigné de π_ref. Elle agit comme un terme de régularisation : elle pénalise les modifications trop brutales du comportement du modèle.
  • β (bêta) : l’hyperparamètre qui contrôle le trade-off entre alignement et fidélité au modèle original.
  • β élevé (ex: 0.2–0.5) : le modèle reste très proche du comportement de référence. Il est stable mais peut être moins bien aligné sur les préférences humaines.
  • β faible (ex: 0.01–0.05) : le modèle est fortement poussé à maximiser la récompense. Il peut mieux s’aligner mais risque la dégénérescence : produire des réponses étranges ou répétitives qui exploitent le modèle de récompense (phénomène appelé reward hacking).

Le choix de PPO plutôt que d’autres algorithmes de renforcement (comme REINFORCE ou SAC) s’explique par sa stabilité : PPO utilise un ratio de probabilités tronqué (clipping) qui empêche les mises à jour trop agressives, ce qui est essentiel lorsqu’on entraîne un modèle de plusieurs milliards de paramètres.


Intuition Analogique

Imaginons un LLM pré-entraîné comme un étudiant brillant qui a lu tous les livres de la bibliothèque mais qui n’a jamais appris les bonnes manières. Il peut réciter des faits, écrire des dissertations, et même faire de l’humour — mais il ne sait pas quand il faut se taire, quand il faut nuancer, ou quand une réponse pourrait être offensante.

Le RLHF, c’est un coach qui va lui apprendre à se comporter en société :

  1. Le modèle de récompense, c’est le coach qui observe l’étudiant et lui dit : “Cette réponse est polie, celle-là est arrogante.” Il apprend à juger la qualité des réponses selon les critères humains.
  2. La régularisation KL, c’est le coach qui rappelle à l’étudiant : “Ne perds pas ta personnalité. Tu peux être plus poli sans devenir robotique.” Elle empêche le modèle d’oublier ce qu’il a appris pendant le pré-entraînement.
  3. Le PPO, c’est la pratique guidée : l’étudiant essaie de répondre, le coach le corrige immédiatement, et il ajuste progressivement son comportement. Pas de changement brutal — des petites corrections itératives, comme un musicien qui affine son interprétation mesure par mesure.

Sans ce coaching, le modèle reste un érudit maladroit. Avec le RLHF, il devient un interlocuteur compétent et agréable.


Implémentation Python avec PyTorch

Voici une implémentation simplifiée mais complète du pipeline RLHF, simulant un modèle de récompense sur de la classification de texte et une boucle PPO avec pénalité KL.

"""
RLHF Simplifié — Implémentation avec PyTorch
Modèle de récompense + Boucle PPO + Pénalité KL
"""

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader, TensorDataset
import numpy as np


# ============================================================
# 1. Modèle de Récompense (Reward Model)
# ============================================================
class RewardModel(nn.Module):
    """
    Modèle de récompense basé sur un encodeur de texte simple.
    Prend une séquence d'embeddings et retourne un score scalaire.
    """
    def __init__(self, vocab_size=30522, embed_dim=128, hidden_dim=256):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        self.encoder = nn.TransformerEncoder(
            nn.TransformerEncoderLayer(
                d_model=embed_dim,
                nhead=4,
                dim_feedforward=hidden_dim,
                batch_first=True
            ),
            num_layers=2
        )
        self.regression_head = nn.Sequential(
            nn.Linear(embed_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, 1)
        )

    def forward(self, x, attention_mask=None):
        """
        x: tensor de tokens [batch_size, seq_len]
        Retourne un score scalaire par échantillon.
        """
        emb = self.embedding(x)  # [B, T, E]
        encoded = self.encoder(emb)  # [B, T, E]
        # On utilise le vecteur du dernier token
        if attention_mask is not None:
            lengths = attention_mask.sum(dim=1)  # [B]
            batch_indices = torch.arange(x.size(0))
            last_tokens = encoded[batch_indices, lengths - 1]
        else:
            last_tokens = encoded[:, -1, :]  # [B, E]
        score = self.regression_head(last_tokens).squeeze(-1)  # [B]
        return score


def train_reward_model(
    reward_model,
    pairs_y_w, pairs_y_l,
    lr=1e-4, epochs=10, batch_size=32
):
    """
    Entraîne le modèle de récompense sur des paires de comparaison.
    pairs_y_w: réponses préférées (winner) [N, seq_len]
    pairs_y_l: réponses rejetées (loser) [N, seq_len]
    """
    optimizer = torch.optim.Adam(reward_model.parameters(), lr=lr)
    dataset = TensorDataset(pairs_y_w, pairs_y_l)
    dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)

    reward_model.train()
    for epoch in range(epochs):
        total_loss = 0.0
        for y_w, y_l in dataloader:
            r_w = reward_model(y_w)  # score des réponses préférées
            r_l = reward_model(y_l)  # score des réponses rejetées

            # Loss Bradley-Terry
            diff = r_w - r_l
            loss = -F.logsigmoid(diff).mean()

            optimizer.zero_grad()
            loss.backward()
            torch.nn.utils.clip_grad_norm_(reward_model.parameters(), max_norm=1.0)
            optimizer.step()

            total_loss += loss.item()

        avg_loss = total_loss / len(dataloader)
        print(f"  Époque {epoch+1}/{epochs} — Récompense loss: {avg_loss:.4f}")

    return reward_model


# ============================================================
# 2. Modèle de Politique (Policy — le LLM à fine-tuner)
# ============================================================
class PolicyModel(nn.Module):
    """
    Modèle de politique simplifié pour la génération de texte.
    Utilise la même architecture que le modèle de récompense
    mais avec une tête de génération (classification par token).
    """
    def __init__(self, vocab_size=30522, embed_dim=128, hidden_dim=256, max_len=32):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        self.encoder = nn.TransformerEncoder(
            nn.TransformerEncoderLayer(
                d_model=embed_dim,
                nhead=4,
                dim_feedforward=hidden_dim,
                batch_first=True
            ),
            num_layers=2
        )
        self.lm_head = nn.Linear(embed_dim, vocab_size)
        self.max_len = max_len
        self.vocab_size = vocab_size

    def forward(self, x, attention_mask=None):
        emb = self.embedding(x)
        encoded = self.encoder(emb)
        logits = self.lm_head(encoded)
        return logits  # [B, T, V]

    def generate(self, prompt, max_len=16):
        """Génération autoregressive échantillonnée."""
        self.eval()
        generated = prompt.clone()
        for _ in range(max_len):
            logits = self.forward(generated)
            next_token_logits = logits[:, -1, :]
            probs = F.softmax(next_token_logits, dim=-1)
            next_token = torch.multinomial(probs, num_samples=1)
            generated = torch.cat([generated, next_token], dim=1)
        return generated

    def compute_log_probs(self, tokens):
        """Calcule les log-probabilités token par token."""
        logits = self.forward(tokens)  # [B, T, V]
        log_probs = F.log_softmax(logits, dim=-1)  # [B, T, V]
        return log_probs


# ============================================================
# 3. Calcul de la Divergence KL
# ============================================================
def compute_kl_divergence(log_pi_theta, log_pi_ref):
    """
    Calcule KL(π_θ || π_ref) token par token.
    log_pi_theta: [B, T, V] — log-probs du modèle fine-tuné
    log_pi_ref: [B, T, V] — log-probs du modèle de référence

    KL = sum_y π_θ(y|x) * log(π_θ(y|x) / π_ref(y|x))
    Approximation par échantillonnage :
    KL ≈ log_pi_theta - log_pi_ref (moyenné sur les tokens générés)
    """
    # On utilise l'approximation: KL ≈ E_{y~pi_theta}[log pi_theta - log pi_ref]
    # Pour chaque token généré, on prend la log-prob du token sous pi_theta
    # et on soustrait la log-prob du même token sous pi_ref
    pi_theta_probs = torch.exp(log_pi_theta)
    kl = (pi_theta_probs * (log_pi_theta - log_pi_ref)).sum(dim=-1)  # [B, T]
    return kl.sum(dim=1)  # [B] — somme sur la séquence


# ============================================================
# 4. Boucle PPO pour le RLHF
# ============================================================
class PPOTrainer:
    """
    Entraîneur PPO simplifié pour le RLHF.
    Implémente la boucle d'optimisation avec clipping et pénalité KL.
    """
    def __init__(
        self,
        policy,
        ref_policy,
        reward_model,
        beta=0.1,
        clip_epsilon=0.2,
        ppo_epochs=4,
        lr=1e-5,
        gamma=0.99,
        gae_lambda=0.95,
        max_grad_norm=1.0
    ):
        self.policy = policy
        self.ref_policy = ref_policy
        self.reward_model = reward_model
        self.beta = beta            # Coefficient KL
        self.clip_epsilon = clip_epsilon  # Seuil de clipping PPO
        self.ppo_epochs = ppo_epochs
        self.gamma = gamma
        self.gae_lambda = gae_lambda
        self.max_grad_norm = max_grad_norm

        self.optimizer = torch.optim.Adam(policy.parameters(), lr=lr)

    def compute_advantages(self, rewards, values):
        """Calcule les avantages via GAE (Generalized Advantage Estimation)."""
        advantages = []
        gae = 0.0
        for t in reversed(range(len(rewards))):
            if t == len(rewards) - 1:
                delta = rewards[t] - values[t]
            else:
                delta = rewards[t] + self.gamma * values[t+1] - values[t]
            gae = delta + self.gamma * self.gae_lambda * gae
            advantages.insert(0, gae)
        return torch.tensor(advantages, dtype=torch.float32)

    def ppo_update(
        self,
        prompts,
        generated_responses,
        rewards,
        old_log_probs
    ):
        """
        Une étape de mise à jour PPO.

        prompts: [B, prompt_len]
        generated_responses: [B, gen_len]
        rewards: [B] — récompenses scalaires
        old_log_probs: [B] — log-probs anciennes (pour le ratio PPO)
        """
        for _ in range(self.ppo_epochs):
            self.policy.train()

            # Générer à nouveau et calculer les log-probs
            with torch.no_grad():
                # On réutilise les réponses générées
                full_responses = torch.cat([prompts, generated_responses], dim=1)
                new_log_probs_full = self.policy.compute_log_probs(full_responses)

            # Extraire les log-probs pour la partie générée uniquement
            prompt_len = prompts.size(1)
            gen_len = generated_responses.size(1)

            # Log-prob des tokens générés sous la politique actuelle
            new_log_probs = []
            for i in range(prompts.size(0)):
                token_log_probs_i = 0.0
                for t in range(gen_len):
                    token_idx = prompt_len + t
                    token_id = generated_responses[i, t].item()
                    if token_id < new_log_probs_full.size(2):
                        token_log_probs_i += new_log_probs_full[i, token_idx, token_id]
                new_log_probs.append(token_log_probs_i)
            new_log_probs = torch.stack(new_log_probs)

            # Ratio PPO: π_θ / π_ancien
            ratio = torch.exp(new_log_probs - old_log_probs)

            # Objectif PPO avec clipping
            surr1 = ratio * rewards
            surr2 = torch.clamp(ratio, 1 - self.clip_epsilon, 1 + self.clip_epsilon) * rewards
            policy_loss = -torch.min(surr1, surr2).mean()

            # Pénalité KL
            with torch.no_grad():
                log_pi_theta = self.policy.compute_log_probs(full_responses)
                log_pi_ref = self.ref_policy.compute_log_probs(full_responses)
                kl_penalty = compute_kl_divergence(log_pi_theta, log_pi_ref).mean()

            # Objectif total
            total_loss = policy_loss + self.beta * kl_penalty

            # Mise à jour des gradients
            self.optimizer.zero_grad()
            total_loss.backward()
            torch.nn.utils.clip_grad_norm_(
                self.policy.parameters(),
                max_norm=self.max_grad_norm
            )
            self.optimizer.step()

            return total_loss.item(), policy_loss.item(), kl_penalty.item()

    def train_step(self, prompts, reward_model=None):
        """
        Une étape complète de RLHF:
        1. Générer des réponses
        2. Scorer avec le modèle de récompense
        3. Mettre à jour la politique avec PPO
        """
        rm = reward_model if reward_model is not None else self.reward_model
        self.ref_policy.eval()
        rm.eval()

        # Phase de génération
        with torch.no_grad():
            responses = self.policy.generate(prompts, max_len=16)
            # Calculer les log-probs de référence
            ref_full = torch.cat([prompts, responses], dim=1)
            ref_log_probs = self.ref_policy.compute_log_probs(ref_full)

            # Scorer avec le modèle de récompense
            rewards = rm(responses)  # [B]

            # Appliquer la pénalité KL à la récompense
            prompt_len = prompts.size(1)
            kl_per_sample = compute_kl_divergence(
                self.policy.compute_log_probs(ref_full),
                ref_log_probs
            )
            adjusted_rewards = rewards - self.beta * kl_per_sample

            # Log-probs anciennes
            old_log_probs = adjusted_rewards.detach().clone()

        # Phase d'optimisation PPO
        total_loss, pol_loss, kl_val = self.ppo_update(
            prompts, responses, adjusted_rewards, old_log_probs
        )

        return {
            "total_loss": total_loss,
            "policy_loss": pol_loss,
            "kl_penalty": kl_val,
            "mean_reward": rewards.mean().item()
        }


# ============================================================
# 5. Exemple d'Utilisation Complète du Pipeline RLHF
# ============================================================
if __name__ == "__main__":
    torch.manual_seed(42)
    np.random.seed(42)

    print("=" * 60)
    print("Pipeline RLHF — Implémentation Complète")
    print("=" * 60)

    # Configuration
    VOCAB_SIZE = 1000
    EMBED_DIM = 64
    HIDDEN_DIM = 128
    SEQ_LEN = 20
    BATCH_SIZE = 8
    NUM_PAIRS = 200

    # --- Étape 1: Créer et entraîner le modèle de récompense ---
    print("\n[Étape 1] Entraînement du modèle de récompense...")
    reward_model = RewardModel(
        vocab_size=VOCAB_SIZE,
        embed_dim=EMBED_DIM,
        hidden_dim=HIDDEN_DIM
    )

    # Données simulées: réponses préférées vs rejetées
    pairs_y_w = torch.randint(0, VOCAB_SIZE, (NUM_PAIRS, SEQ_LEN))
    pairs_y_l = torch.randint(0, VOCAB_SIZE, (NUM_PAIRS, SEQ_LEN))
    # On rend les "winners" légèrement différents des "losers"
    pairs_y_w[:, :5] = 1  # Pattern distinctif pour les réponses préférées

    reward_model = train_reward_model(
        reward_model,
        pairs_y_w, pairs_y_l,
        lr=5e-4, epochs=5, batch_size=BATCH_SIZE
    )

    # --- Étape 2: Créer la politique et la référence gelée ---
    print("\n[Étape 2] Création du modèle de politique et de la référence...")
    policy = PolicyModel(
        vocab_size=VOCAB_SIZE,
        embed_dim=EMBED_DIM,
        hidden_dim=HIDDEN_DIM,
        max_len=SEQ_LEN
    )
    # Copier les poids initiaux comme référence
    ref_policy = PolicyModel(
        vocab_size=VOCAB_SIZE,
        embed_dim=EMBED_DIM,
        hidden_dim=HIDDEN_DIM,
        max_len=SEQ_LEN
    )
    ref_policy.load_state_dict(policy.state_dict())
    ref_policy.eval()  # Gelée — pas d'entraînement

    # --- Étape 3: Boucle PPO (RLHF) ---
    print("\n[Étape 3] Fine-tuning par PPO avec pénalité KL...")
    trainer = PPOTrainer(
        policy=policy,
        ref_policy=ref_policy,
        reward_model=reward_model,
        beta=0.05,            # Coefficient KL modéré
        clip_epsilon=0.2,     # Clipping PPO standard
        ppo_epochs=3,         # Itérations PPO par batch
        lr=1e-5,              # Taux d'apprentissage faible pour la stabilité
        gamma=0.99,
        gae_lambda=0.95,
        max_grad_norm=1.0
    )

    # Prompts simulés pour l'entraînement
    prompts = torch.randint(0, VOCAB_SIZE, (BATCH_SIZE, 8))

    print(f"\n  Configuration PPO:")
    print(f"    β (KL coeff)    = {trainer.beta}")
    print(f"    clip_epsilon    = {trainer.clip_epsilon}")
    print(f"    ppo_epochs      = {trainer.ppo_epochs}")
    print(f"    lr              = {trainer.optimizer.param_groups[0]['lr']}")
    print()

    # Boucle d'entraînement
    num_steps = 20
    for step in range(num_steps):
        metrics = trainer.train_step(prompts)
        if (step + 1) % 5 == 0:
            print(f"  Étape {step+1}/{num_steps} — "
                  f"Loss totale: {metrics['total_loss']:.4f} | "
                  f"Policy loss: {metrics['policy_loss']:.4f} | "
                  f"KL: {metrics['kl_penalty']:.4f} | "
                  f"Récompense moy.: {metrics['mean_reward']:.4f}")

    print("\n[Réussi] Pipeline RLHF terminé avec succès!")
    print("=" * 60)

Hyperparamètres Clés

Le succès du RLHF dépend fortement du réglage des hyperparamètres suivants :

Hyperparamètre Valeur typique Rôle
β (KL coeff) 0.05 – 0.2 Contrôle le trade-off alignement vs fidélité. Un β trop faible entraîne du reward hacking ; un β trop fort empêche tout ajustement utile.
ppo_epochs 3 – 4 Nombre de passes PPO sur chaque batch de données générées. Plus d’époques accélère la convergence mais risque le surajustement sur les données simulées.
clip_epsilon 0.2 Seuil de clipping dans PPO. Empêche les mises à jour trop agressives qui déstabiliseraient l’entraînement.
reward_model_lr 1e-4 – 5e-4 Taux d’apprentissage pour l’entraînement du modèle de récompense. Un apprentissage trop élevé peut empêcher la convergence ; trop lent, le modèle ne capture pas les nuances des préférences.
batch_size 32 – 256 Taille du batch pour le PPO. Des batchs plus grands stabilisent les gradients mais consomment plus de mémoire GPU.
gamma (γ) 0.99 Facteur d’actualisation. Détermine l’importance des récompenses futures.
GAE lambda 0.95 Paramètre du Generalized Advantage Estimation. Contrôle le compromis biais-variance de l’estimation des avantages.

Recommandation pratique : Commencez avec β = 0.1, ppo_epochs = 3, clip_epsilon = 0.2. Surveillez la divergence KL à chaque étape : si elle dépasse 5–10 nats, augmentez β. Si elle reste sous 0.5, diminuez β pour un alignement plus agressif.


Avantages et Limites du RLHF

Avantages

  • Alignement supérieur : Le RLHF produit des modèles dont les réponses sont nettement plus utiles, inoffensives et honnêtes que celles d’un modèle simplement fine-tuné par instruction (SFT seul). C’est la raison pour laquelle ChatGPT a surpassé ses concurrents en 2022.
  • Capture de préférences complexes : Les préférences humaines sont souvent subtiles et difficiles à formaliser dans une fonction de récompense manuelle. Le RLHF les apprend implicitement à travers les comparaisons humaines, capturant des nuances comme le ton, la structure, ou la pertinence contextuelle.
  • Flexibilité : On peut entraîner plusieurs modèles de récompense pour différents critères (utilité, sécurité, concision) et les combiner. On peut aussi utiliser des récompenses artificielles (règles heuristiques, vérificateurs automatiques) en complément des annotations humaines.

Limites

  • Coût élevé : Collecter des dizaines de milliers de comparaisons humaines est long et coûteux. Le processus nécessite des annotateurs qualifiés, et la qualité des annotations détermine directement la qualité du modèle final.
  • Biais des annotateurs : Les préférences capturées reflètent les biais culturels, linguistiques et subjectifs des annotateurs. Un modèle RLHF entraîné avec des annotateurs occidentaux aura des comportements différents d’un modèle entraîné avec des annotateurs asiatiques — et les deux seront biaisés à leur manière.
  • Reward hacking et dégénérescence : Si β est mal réglé, le modèle peut apprendre à « jouer » le modèle de récompense plutôt qu’à produire de bonnes réponses. Des travaux récents (Gao et al., 2022) ont montré que les modèles RLHF peuvent produire des réponses répétitives ou des boucles de tokens pour maximiser le score sans améliorer la qualité réelle.
  • Échelle limitée des préférences : Les comparaisons par paires ne capturent pas bien les préférences multidimensionnelles. Une réponse peut être plus informative mais moins concise — comment les annotateurs tranchent-ils ? Le modèle de récompense apprendra une moyenne qui ne satisfait personne.
  • Instabilité de l’entraînement : Le PPO peut être instable à grande échelle. Des bugs subtils dans l’implémentation (notamment le calcul de la KL) peuvent mener à un entraînement silencieux qui semble converger mais produit un modèle dégradé.

4 Cas d’Usage Concrets du RLHF

1. Assistants Conversationnels (ChatGPT, Claude, Gemini)

C’est le cas d’usage le plus connu. Le RLHF transforme un modèle de langage brut — capable de générer du texte cohérent mais potentiellement toxique ou inutile — en un assistant utile et sûr. Les annotateurs humains comparent des réponses sur des critères d’utilité, de véracité et d’inoffensivité. Le modèle final est capable de refuser poliment de générer du contenu dangereux, de reformuler des réponses ambiguës, et de structurer des explications complexes de manière pédagogique.

2. Modération de Contenu Automatisée

Les plateformes comme Reddit, Stack Overflow ou les forums d’entreprise utilisent le RLHF pour entraîner des classifieurs de modération plus nuancés. Au lieu de règles binaires (ce mot est interdit / autorisé), le modèle apprend des gradations de sévérité à partir des décisions des modérateurs humains. Il peut ainsi distinguer une insulte directe d’une critique acerbe mais légitime, un spam d’un enthousiasme excessif.

3. Résumé de Documents avec Préférences de Format

Un modèle de résumé RLHF peut apprendre non seulement quoi résumer, mais comment le faire : longueur souhaitée, niveau de détail, ton formel ou informel, inclusion ou exclusion de certains types d’information. Les annotateurs comparent des résumés selon des critères stylistiques et informatifs, et le modèle fine-tuné produit des résumés adaptés au contexte d’utilisation — qu’il s’agisse d’un rapport exécutif ou d’une note rapide.

4. Agents de Code Intelligents (GitHub Copilot, Code Llama)

Le RLHF s’applique aussi à la génération de code. Les programmeurs comparent des suggestions de code selon la lisibilité, l’efficacité, la sécurité et la conformité aux conventions du projet. Le modèle apprend ainsi à privilégier des solutions non seulement correctes mais aussi bien structurées et maintenables — une nuance que l’apprentissage supervisé seul ne capture pas.


Voir Aussi


Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur la façon dont les données de vos commentaires sont traitées.