Alignement#

Les grands modèles de langage, tels qu’ils émergent du pré-entrainement, sont des machines à prédire le token suivant. Ils ont absorbé la structure statistique de milliards de pages web, de livres et de conversations, mais rien dans leur objectif d’entrainement ne les incite à être utiles, honnêtes ou inoffensifs. Un LLM de base peut tout aussi bien générer un poème, un tutoriel de chimie dangereuse ou une réponse plausible mais factuellement fausse — il optimise la vraisemblance, pas la qualité. Le problème de l”alignement est précisément celui-ci : comment faire en sorte qu’un modèle très capable se comporte conformément aux intentions et aux valeurs de ses utilisateurs et de la société ?

Ce problème est devenu central à partir de 2022, lorsque InstructGPT (Ouyang et al., 2022) puis ChatGPT ont montré qu’un même modèle de base pouvait être transformé en assistant conversationnel remarquablement coopératif grâce à l’apprentissage par retour humain (RLHF). Depuis, une série de techniques — DPO, ORPO, Constitutional AI — ont proposé des alternatives et des raffinements, chacune avec ses compromis en termes de coût, de stabilité et d’efficacité. Comprendre ces méthodes est indispensable pour quiconque déploie ou évalue un LLM.

Ce chapitre présente les fondements conceptuels et techniques de l’alignement. Nous partirons du problème lui-même, puis détaillerons le pipeline RLHF classique (SFT, modèle de récompense, PPO), avant d’examiner les alternatives plus récentes (DPO, ORPO, Constitutional AI). Nous aborderons ensuite les pathologies de l’alignement — le reward hacking et la loi de Goodhart — et le trilemme HHH (Helpful, Harmless, Honest) qui structure la réflexion sur ce que signifie « bien aligner » un modèle. L’approche est illustrée par des simulations sur des données synthétiques, sans chargement de modèle, pour rester dans une empreinte mémoire réduite.

Le problème de l’alignement#

Un LLM pré-entrainé par modelisation auto-regressive maximise la log-vraisemblance \(\mathcal{L}(\theta) = \sum_t \log P_\theta(x_t \mid x_{<t})\) sur un corpus massif. Cet objectif ne contient aucune notion de véracité, d’utilité ou de sécurité : le modèle apprend à reproduire la distribution statistique du texte humain, y compris ses erreurs, ses biais et ses contenus nuisibles. Le problème de l’alignement consiste à combler l’écart entre ce que le modèle peut faire (sa capacité) et ce qu’il devrait faire (son comportement souhaité).

Définition 94 (Alignement)

L”alignement (alignment) d’un modèle de langage est le processus par lequel on modifie le comportement du modèle pour qu’il agisse conformément aux intentions de l’utilisateur et à un ensemble de valeurs spécifiées (utilité, honnêteté, sécurité). Formellement, si \(\pi_\theta\) désigne la politique du modèle (la distribution sur les séquences générées) et \(R^*\) la fonction de récompense idéale capturant les préférences humaines, l’objectif d’alignement est :

\[\pi^* = \arg\max_\pi \, \mathbb{E}_{x \sim \mathcal{D},\, y \sim \pi(\cdot|x)}\left[R^*(x, y)\right] - \beta \, D_{\text{KL}}\left[\pi(\cdot|x) \,\|\, \pi_{\text{ref}}(\cdot|x)\right]\]

Le terme de régularisation KL empêche le modèle aligné de trop s’éloigner du modèle de référence \(\pi_{\text{ref}}\) (typiquement le modèle SFT), préservant ainsi les capacités linguistiques acquises pendant le pré-entrainement. Le coefficient \(\beta\) contrôle le compromis entre maximisation de la récompense et conservation des capacités du modèle.

Remarque 94 (Taxe d’alignement)

La taxe d’alignement (alignment tax) désigne la perte de performance brute que le modèle subit du fait de l’alignement. Un modèle aligné refuse certaines requêtes, est plus prudent dans ses affirmations et peut perdre en fluidité ou en créativité par rapport au modèle de base. En pratique, cette taxe est souvent faible pour les tâches courantes, mais elle peut devenir significative pour des tâches spécialisées où le modèle de base excelle mais que l’alignement contraint excessivement. L’objectif des méthodes modernes d’alignement est de minimiser cette taxe tout en maximisant le comportement souhaité.

L’écart entre capacité et alignement n’est pas qu’un problème théorique. Un LLM de base, interrogé sur un sujet sensible, produira la continuation la plus probable selon son corpus d’entrainement — ce qui peut inclure de la désinformation, des instructions dangereuses ou des stéréotypes. L’alignement transforme un générateur de texte en un assistant qui comprend les conventions d’un dialogue, refuse les demandes nuisibles et signale son incertitude.

Hide code cell source

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import seaborn as sns
import torch
import torch.nn as nn

sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)
np.random.seed(42)
torch.manual_seed(42)
<torch._C.Generator at 0x7facdfaebbb0>

Hide code cell source

categories = ["Générer du texte\ncohérent", "Suivre des\ninstructions",
              "Refuser les\nrequêtes nuisibles", "Citer ses\nsources",
              "Reconnaitre\nson incertitude", "Etre factuel\net véridique"]
capacite_base = [0.95, 0.30, 0.10, 0.05, 0.08, 0.45]
capacite_aligne = [0.92, 0.90, 0.85, 0.55, 0.75, 0.78]

x = np.arange(len(categories))
fig, ax = plt.subplots(figsize=(12, 5))
ax.bar(x - 0.18, capacite_base, 0.35, label="Modele de base",
       color="#C44E52", alpha=0.85, edgecolor="white")
ax.bar(x + 0.18, capacite_aligne, 0.35, label="Modele aligne",
       color="#4C72B0", alpha=0.85, edgecolor="white")
ax.set_ylabel("Score (normalisé)")
ax.set_title("Ecart entre capacité brute et comportement aligné")
ax.set_xticks(x)
ax.set_xticklabels(categories, fontsize=8)
ax.legend(fontsize=10)
ax.set_ylim(0, 1.1)
plt.show()
_images/35f094347125089c92e398a824dd551cd748f017629ac7a1fdf85523b83074f9.png

RLHF : apprentissage par retour humain#

L’approche dominante pour l’alignement, popularisée par InstructGPT (Ouyang et al., 2022) et utilisée pour ChatGPT, est le Reinforcement Learning from Human Feedback (RLHF). Le pipeline RLHF se décompose en trois étapes séquentielles, chacune construisant sur la précédente.

Définition 95 (RLHF (Reinforcement Learning from Human Feedback))

Le RLHF (Reinforcement Learning from Human Feedback) est un pipeline d’alignement en trois étapes :

  1. Supervised Fine-Tuning (SFT) : le modèle de base est fine-tuné sur un corpus de démonstrations humaines de haute qualité — des paires (instruction, réponse idéale) rédigées par des annotateurs. Le modèle résultant \(\pi_{\text{SFT}}\) sait déjà suivre des instructions, mais de manière imparfaite.

  2. Entrainement du modèle de récompense (RM) : des annotateurs humains comparent des paires de réponses générées par \(\pi_{\text{SFT}}\) pour un même prompt et indiquent laquelle est préférable. Un modèle de récompense \(r_\phi(x, y)\) est entrainé à prédire ces préférences.

  3. Optimisation par PPO : le modèle SFT est optimisé par un algorithme de reinforcement learning (Proximal Policy Optimization, PPO) pour maximiser la récompense prédite par le RM, sous contrainte de ne pas trop s’éloigner de \(\pi_{\text{SFT}}\) (régularisation KL).

Ce pipeline a été introduit par Christiano et al. (2017) dans le cadre général du RL, puis adapté aux LLM par Stiennon et al. (2020) et Ouyang et al. (2022).

La première étape (SFT) fine-tune le modèle de base sur un corpus de démonstrations : des paires (instruction, réponse idéale) rédigées par des annotateurs. L’objectif est la maximisation de la vraisemblance \(\mathcal{L}_{\text{SFT}}(\theta) = -\mathbb{E}_{(x, y)} \left[\sum_t \log \pi_\theta(y_t \mid x, y_{<t})\right]\). Cette étape produit déjà un modèle plus utile, mais sa qualité reste limitée par le coût des démonstrations. La deuxième étape entraine un modèle de récompense sur des préférences humaines.

Définition 96 (Modèle de récompense (Reward Model))

Un modèle de récompense (reward model, RM) est un modèle entrainé à prédire les préférences humaines entre deux réponses. Etant donné un prompt \(x\) et deux réponses \(y_w\) (préférée) et \(y_l\) (rejetée), le RM est entrainé par la perte de Bradley-Terry :

\[\mathcal{L}_{\text{RM}}(\phi) = -\mathbb{E}_{(x, y_w, y_l) \sim \mathcal{D}_{\text{pref}}} \left[\log \sigma\left(r_\phi(x, y_w) - r_\phi(x, y_l)\right)\right]\]

\(\sigma\) est la fonction sigmoide et \(r_\phi(x, y) \in \mathbb{R}\) est le score de récompense attribué à la réponse \(y\) pour le prompt \(x\). Le modèle de Bradley-Terry suppose que la probabilité qu’une réponse \(y_w\) soit préférée à \(y_l\) est :

\[P(y_w \succ y_l \mid x) = \sigma\left(r(x, y_w) - r(x, y_l)\right) = \frac{1}{1 + e^{-(r(x, y_w) - r(x, y_l))}}\]

En pratique, le RM est souvent initialisé à partir du modèle SFT, dont on remplace la tête de génération par une tête de régression scalaire.

Exemple 68 (Données de préférence pour le RM)

Les données de préférence sont collectées en présentant aux annotateurs un prompt et deux réponses, en leur demandant laquelle est meilleure. Par exemple :

Prompt

Réponse A (choisie)

Réponse B (rejetee)

« Explique la photosynthèse »

Explication claire, structuree, correcte

Explication vague, avec des erreurs factuelles

« Ecris un email professionnel »

Ton adapté, structure standard

Trop familier, pas de formule de politesse

« Comment crocheter une serrure ? »

Refus poli avec explication

Instructions détaillées (contenu dangereux)

La qualité du RM dépend directement de la qualité et de la cohérence des annotations humaines. Les désaccords entre annotateurs sont fréquents (accord inter-annotateur typiquement de 70 a 80%), ce qui constitue un plafond de verre pour la précision du RM.

Exemple 69 (Entrainement d’un modèle de récompense)

L’entrainement d’un modèle de récompense sur des données de préférence suit le schéma standard de la classification binaire. On minimise la perte de Bradley-Terry, qui est équivalente à la perte d’entropie croisée binaire appliquée à la différence des scores :

reward_model = nn.Sequential(nn.Linear(dim, 32), nn.ReLU(), nn.Linear(32, 1))

def bradley_terry_loss(r_w, r_l):
    return -torch.mean(torch.log(torch.sigmoid(r_w - r_l)))

for epoch in range(100):
    r_w = reward_model(features_w).squeeze()
    r_l = reward_model(features_l).squeeze()
    loss = bradley_terry_loss(r_w, r_l)
    optimizer.zero_grad(); loss.backward(); optimizer.step()

Hide code cell source

# Données de préférence synthétiques
n_samples, dim_features = 500, 8
w_true = torch.randn(dim_features); w_true = w_true / w_true.norm()
features_w = torch.randn(n_samples, dim_features) * 0.5
features_l = torch.randn(n_samples, dim_features) * 0.5
rewards_w = features_w @ w_true + torch.randn(n_samples) * 0.3
rewards_l = features_l @ w_true + torch.randn(n_samples) * 0.3
mask = rewards_w > rewards_l
features_w_clean = torch.where(mask.unsqueeze(1), features_w, features_l)
features_l_clean = torch.where(mask.unsqueeze(1), features_l, features_w)

# Entrainement du modèle de récompense
reward_model = nn.Sequential(nn.Linear(dim_features, 32), nn.ReLU(),
                             nn.Linear(32, 16), nn.ReLU(), nn.Linear(16, 1))
def bradley_terry_loss(r_w, r_l):
    return -torch.mean(torch.log(torch.sigmoid(r_w - r_l) + 1e-8))

optimizer = torch.optim.Adam(reward_model.parameters(), lr=1e-3)
losses, accuracies = [], []
for epoch in range(200):
    r_w = reward_model(features_w_clean).squeeze()
    r_l = reward_model(features_l_clean).squeeze()
    loss = bradley_terry_loss(r_w, r_l)
    optimizer.zero_grad(); loss.backward(); optimizer.step()
    losses.append(loss.item())
    with torch.no_grad():
        accuracies.append((r_w > r_l).float().mean().item())

fig, axes = plt.subplots(1, 2, figsize=(13, 5))
axes[0].plot(losses, color="#4C72B0", linewidth=2)
axes[0].set_xlabel("Epoque"); axes[0].set_ylabel("Perte de Bradley-Terry")
axes[0].set_title("Entrainement du modele de recompense")
axes[1].plot(accuracies, color="#55A868", linewidth=2)
axes[1].set_xlabel("Epoque"); axes[1].set_ylabel("Precision (prefere > rejete)")
axes[1].set_title(f"Precision finale : {accuracies[-1]:.1%}")
axes[1].axhline(y=1.0, color="gray", linestyle="--", alpha=0.4)
axes[1].set_ylim(0.4, 1.05)
plt.show()
_images/0d2f52999fcdd0c74546007fbb95f1203a15d731150b3809bc00474f740873bc.png

Définition 97 (PPO dans le contexte RLHF)

L’optimisation par Proximal Policy Optimization (PPO, Schulman et al., 2017) dans le contexte RLHF maximise la récompense prédite par le RM tout en restant proche du modèle SFT. L’objectif combiné est :

\[J(\theta) = \mathbb{E}_{x \sim \mathcal{D},\, y \sim \pi_\theta(\cdot|x)} \left[r_\phi(x, y) - \beta \log \frac{\pi_\theta(y|x)}{\pi_{\text{ref}}(y|x)}\right]\]

ou \(\pi_{\text{ref}} = \pi_{\text{SFT}}\) est la politique de référence et \(\beta\) est le coefficient de pénalité KL. PPO utilise un mécanisme de clipping pour limiter la taille des mises à jour de la politique, ce qui stabilise l’entrainement :

\[L^{\text{CLIP}}(\theta) = \mathbb{E}\left[\min\left(\rho_t \hat{A}_t, \; \text{clip}(\rho_t, 1-\epsilon, 1+\epsilon) \hat{A}_t\right)\right]\]

ou \(\rho_t = \pi_\theta(a_t|s_t) / \pi_{\theta_{\text{old}}}(a_t|s_t)\) est le ratio de probabilité et \(\hat{A}_t\) est l’estimateur d’avantage. En pratique, l’entrainement PPO des LLM est notablement instable et nécessite un réglage minutieux des hyperparamètres.

Hide code cell source

# Diagramme du pipeline RLHF complet
fig, ax = plt.subplots(figsize=(16, 5))
ax.set_xlim(0, 20); ax.set_ylim(0, 8); ax.axis("off")

boxes = [
    (1, 3.5, 3.5, 2.5, "Modele\nde base", "#B0B0B0", "Pré-entrainé"),
    (5.5, 3.5, 3.5, 2.5, "SFT", "#4C72B0", "Démonstrations\nhumaines"),
    (10, 3.5, 3.5, 2.5, "Reward\nModel", "#DD8452", "Préférences\nhumaines"),
    (14.5, 3.5, 3.5, 2.5, "PPO", "#55A868", "Optimisation RL\nsous contrainte KL"),
]
for x_b, y_b, w, h, title, color, desc in boxes:
    rect = mpatches.FancyBboxPatch((x_b, y_b), w, h, boxstyle="round,pad=0.15",
                                    facecolor=color, alpha=0.25, edgecolor=color, linewidth=2)
    ax.add_patch(rect)
    ax.text(x_b + w / 2, y_b + h * 0.65, title, ha="center", va="center",
            fontsize=12, fontweight="bold", color=color)
    ax.text(x_b + w / 2, y_b + h * 0.25, desc, ha="center", va="center",
            fontsize=8, color="gray")
arrow_kw = dict(arrowstyle="-|>", color="#333333", lw=2)
for xs, xe in [(4.5, 5.5), (9.0, 10.0), (13.5, 14.5)]:
    ax.annotate("", xy=(xe, 4.75), xytext=(xs, 4.75), arrowprops=arrow_kw)
ax.text(19.0, 4.75, "Modèle\naligné", ha="center", va="center", fontsize=12,
        fontweight="bold", color="#E24A33",
        bbox=dict(boxstyle="round,pad=0.4", facecolor="#E24A33", alpha=0.15,
                  edgecolor="#E24A33", linewidth=2))
ax.annotate("", xy=(18.2, 4.75), xytext=(18.0, 4.75), arrowprops=arrow_kw)
ax.set_title("Pipeline RLHF : du modèle de base au modèle aligné", fontsize=14, pad=15)
plt.show()
_images/8fad5bb49295283609d2b3c6fc0d4027371a42794dbeb6f0ebd2c66b6b771279.png

Hide code cell source

# Distribution des récompenses avant et après alignement
rewards_before = np.random.normal(loc=0.3, scale=1.2, size=2000)
rewards_after = np.random.normal(loc=1.8, scale=0.8, size=2000)

fig, ax = plt.subplots(figsize=(12, 5))
ax.hist(rewards_before, bins=50, alpha=0.6, color="#C44E52",
        label="Avant alignement (SFT)", density=True, edgecolor="white")
ax.hist(rewards_after, bins=50, alpha=0.6, color="#4C72B0",
        label="Après alignement (PPO)", density=True, edgecolor="white")
ax.axvline(x=np.mean(rewards_before), color="#C44E52", linestyle="--", linewidth=2)
ax.axvline(x=np.mean(rewards_after), color="#4C72B0", linestyle="--", linewidth=2)
ax.set_xlabel("Score de récompense"); ax.set_ylabel("Densité")
ax.set_title("Distribution des récompenses avant et après alignement RLHF")
ax.legend(fontsize=9)
plt.show()
_images/004db131797ca00d1573eb11c7c2df29ed9a86fd872f5951dcbe7ddc3ba8785e.png

DPO : optimisation directe des préférences#

Le pipeline RLHF est efficace mais complexe : il requiert l’entrainement d’un modèle de récompense séparé, puis une boucle d’optimisation RL (PPO) qui est notablement instable et coûteuse en calcul. Direct Preference Optimization (DPO, Rafailov et al., 2023) propose une simplification élégante en montrant qu’on peut optimiser directement les préférences humaines, sans passer par un modèle de récompense explicite.

Définition 98 (DPO (Direct Preference Optimization))

L”optimisation directe des préférences (Direct Preference Optimization, DPO, Rafailov et al., 2023) reformule le problème d’alignement comme un problème de classification sur des paires de préférences. Au lieu d’entrainer un modèle de récompense puis d’optimiser via PPO, DPO dérive une perte qui optimise directement la politique du modèle. La perte DPO est :

\[\mathcal{L}_{\text{DPO}}(\theta) = -\mathbb{E}_{(x, y_w, y_l) \sim \mathcal{D}_{\text{pref}}} \left[\log \sigma\left(\beta \log \frac{\pi_\theta(y_w | x)}{\pi_{\text{ref}}(y_w | x)} - \beta \log \frac{\pi_\theta(y_l | x)}{\pi_{\text{ref}}(y_l | x)}\right)\right]\]

\(y_w\) est la réponse préférée, \(y_l\) la réponse rejetée, \(\pi_{\text{ref}}\) la politique de référence (typiquement le modèle SFT), \(\beta\) le paramètre de température et \(\sigma\) la fonction sigmoide. Cette perte augmente la probabilité relative de \(y_w\) par rapport a \(y_l\), en utilisant \(\pi_{\text{ref}}\) comme ancre pour éviter la dégénérescence.

Propriété 22 (Equivalence DPO-RLHF)

Rafailov et al. (2023) montrent que la solution optimale du problème RLHF avec contrainte KL admet une forme analytique. La politique optimale \(\pi^*\) du problème :

\[\max_\pi \, \mathbb{E}\left[r(x, y)\right] - \beta \, D_{\text{KL}}\left[\pi \| \pi_{\text{ref}}\right]\]

est donnée par :

\[\pi^*(y|x) = \frac{1}{Z(x)} \pi_{\text{ref}}(y|x) \exp\left(\frac{r(x, y)}{\beta}\right)\]

En inversant cette relation, on obtient la récompense implicite de DPO :

\[r(x, y) = \beta \log \frac{\pi_\theta(y|x)}{\pi_{\text{ref}}(y|x)} + \beta \log Z(x)\]

En substituant dans la perte de Bradley-Terry, le terme \(Z(x)\) s’annule (il ne dépend pas de \(y\)), et on retrouve exactement la perte DPO. Ainsi, DPO et RLHF optimisent le même objectif, mais DPO le fait en une seule étape de classification, sans modèle de recompense ni boucle RL.

Remarque 95 (Avantages de DPO sur RLHF)

DPO présente plusieurs avantages pratiques par rapport a RLHF :

  • Simplicité : pas de modèle de récompense à entrainer, pas de boucle RL, pas de PPO. L’entrainement est une simple classification supervisée.

  • Stabilité : l’absence de PPO élimine une source majeure d’instabilité. L’entrainement DPO converge de manière plus fiable.

  • Coût : DPO nécessite moins de GPU et moins de temps de calcul, car il n’y a qu’un seul modèle à entrainer (au lieu de trois avec RLHF : SFT + RM + politique).

  • Mémoire : DPO ne nécessite que deux copies du modèle en mémoire (politique et référence), contre trois ou quatre pour RLHF.

En revanche, DPO peut être moins performant que RLHF sur des distributions de préférences très complexes, et il est plus sensible à la qualité des paires de préférences. De plus, il ne produit pas de modèle de récompense explicite, ce qui peut être un inconvénient pour le monitoring et le debug.

Exemple 70 (Calcul de la perte DPO)

Considérons un exemple simplifié avec un prompt \(x\) et deux réponses. Les log-probabilités sont :

| | \(\log \pi_\theta(y|x)\) | \(\log \pi_{\text{ref}}(y|x)\) | |—|—|—| | Réponse préférée \(y_w\) | \(-2.1\) | \(-2.5\) | | Réponse rejetée \(y_l\) | \(-1.8\) | \(-1.6\) |

Avec \(\beta = 0.1\) :

\[\hat{r}(y_w) = \beta \left(\log \pi_\theta(y_w|x) - \log \pi_{\text{ref}}(y_w|x)\right) = 0.1 \times (-2.1 - (-2.5)) = 0.04\]
\[\hat{r}(y_l) = \beta \left(\log \pi_\theta(y_l|x) - \log \pi_{\text{ref}}(y_l|x)\right) = 0.1 \times (-1.8 - (-1.6)) = -0.02\]
\[\mathcal{L}_{\text{DPO}} = -\log \sigma(0.04 - (-0.02)) = -\log \sigma(0.06) \approx 0.669\]

La perte est faible (proche de \(-\log(0.5) \approx 0.693\)), ce qui signifie que le modèle accorde déjà une légère préférence à \(y_w\). L’optimisation va accentuer cette préférence.

Hide code cell source

def dpo_loss(logp_w, logp_l, logp_ref_w, logp_ref_l, beta=0.1):
    return -torch.mean(torch.log(
        torch.sigmoid(beta * ((logp_w - logp_ref_w) - (logp_l - logp_ref_l))) + 1e-8))

# Données synthétiques de préférences
n_pairs = 300
logp_ref_w = -torch.rand(n_pairs) * 3 - 1
logp_ref_l = -torch.rand(n_pairs) * 3 - 1
logp_w = nn.Parameter(logp_ref_w.clone() + torch.randn(n_pairs) * 0.1)
logp_l = nn.Parameter(logp_ref_l.clone() + torch.randn(n_pairs) * 0.1)

optimizer_dpo = torch.optim.Adam([logp_w, logp_l], lr=0.01)
dpo_losses, implicit_reward_gaps = [], []
for step in range(300):
    loss = dpo_loss(logp_w, logp_l, logp_ref_w, logp_ref_l, beta=0.1)
    optimizer_dpo.zero_grad(); loss.backward(); optimizer_dpo.step()
    dpo_losses.append(loss.item())
    with torch.no_grad():
        implicit_reward_gaps.append(
            ((logp_w - logp_ref_w) - (logp_l - logp_ref_l)).mean().item())

fig, axes = plt.subplots(1, 2, figsize=(13, 5))
axes[0].plot(dpo_losses, color="#4C72B0", linewidth=2)
axes[0].set_xlabel("Etape d'optimisation"); axes[0].set_ylabel("Perte DPO")
axes[0].set_title("Convergence de la perte DPO")
axes[1].plot(implicit_reward_gaps, color="#55A868", linewidth=2)
axes[1].axhline(y=0, color="gray", linestyle="--", alpha=0.4)
axes[1].set_xlabel("Etape d'optimisation")
axes[1].set_ylabel("Ecart de récompense implicite moyen")
axes[1].set_title("$\\hat{r}(y_w) - \\hat{r}(y_l)$ augmente avec l'entrainement")
plt.show()
_images/b9c4b9c321413f1d011267219fcb583d6caae42aeefe5eb30791404672923e9f.png

Hide code cell source

# Bradley-Terry : simulation de matchs entre 5 "modèles"
bt_prob = lambda r_a, r_b: 1.0 / (1.0 + np.exp(-(r_a - r_b)))
model_names = ["Modèle A", "Modèle B", "Modèle C", "Modèle D", "Modèle E"]
true_ratings = np.array([1.5, 0.8, 0.0, -0.5, -1.2])
np.random.seed(42)

resultats = np.zeros((5, 5))
for _ in range(1000):
    i, j = np.random.choice(5, size=2, replace=False)
    if np.random.random() < bt_prob(true_ratings[i], true_ratings[j]):
        resultats[i, j] += 1
    else:
        resultats[j, i] += 1

win_rates = np.zeros((5, 5))
for i in range(5):
    for j in range(5):
        total = resultats[i, j] + resultats[j, i]
        if total > 0:
            win_rates[i, j] = resultats[i, j] / total

fig, axes = plt.subplots(1, 2, figsize=(13, 5))
sns.heatmap(win_rates, annot=True, fmt=".2f", cmap="RdYlGn",
            xticklabels=model_names, yticklabels=model_names,
            ax=axes[0], vmin=0, vmax=1, linewidths=0.5, square=True)
axes[0].set_title("Taux de victoire (Bradley-Terry)")
axes[0].set_xlabel("Adversaire"); axes[0].set_ylabel("Modèle")

victoires = resultats.sum(axis=1)
total_matchs = resultats.sum(axis=1) + resultats.sum(axis=0)
est_ratings = np.log(victoires / (total_matchs - victoires + 1e-8))
est_ratings -= est_ratings.mean()
axes[1].scatter(true_ratings, est_ratings, s=100, color="#4C72B0", edgecolor="white", zorder=5)
for i, name in enumerate(model_names):
    axes[1].annotate(name, (true_ratings[i], est_ratings[i]),
                     textcoords="offset points", xytext=(8, 5), fontsize=9)
z = np.polyfit(true_ratings, est_ratings, 1)
x_line = np.linspace(-1.5, 1.8, 100)
axes[1].plot(x_line, np.poly1d(z)(x_line), "--", color="gray", alpha=0.5)
axes[1].set_xlabel("Rating réel"); axes[1].set_ylabel("Rating estimé")
axes[1].set_title("Ratings estimés vs réels par Bradley-Terry")
plt.show()
_images/d0407465cc86f1b3d825ba7ace7c528e199130205a97388e834dd9eefcaa7fdc.png

ORPO et variantes recentes#

Le paysage des méthodes d’alignement s’est rapidement diversifié après DPO. Plusieurs variantes cherchent à simplifier encore le pipeline, à éliminer le besoin d’un modèle de référence, ou à fonctionner avec des types de retour différents.

Définition 99 (ORPO (Odds Ratio Preference Optimization))

L”ORPO (Odds Ratio Preference Optimization, Hong et al., 2024) combine l’étape SFT et l’étape d’alignement en une seule phase d’entrainement, sans modèle de référence. La perte ORPO est :

\[\mathcal{L}_{\text{ORPO}} = \mathcal{L}_{\text{SFT}}(y_w) + \lambda \cdot \mathcal{L}_{\text{OR}}\]

\(\mathcal{L}_{\text{SFT}}(y_w)\) est la perte de vraisemblance standard sur la réponse préférée et \(\mathcal{L}_{\text{OR}}\) est un terme basé sur le rapport de côtes (odds ratio) :

\[\mathcal{L}_{\text{OR}} = -\log \sigma\left(\log \frac{\text{odds}_\theta(y_w|x)}{\text{odds}_\theta(y_l|x)}\right)\]

avec \(\text{odds}_\theta(y|x) = \frac{P_\theta(y|x)}{1 - P_\theta(y|x)}\). L’avantage principal d’ORPO est l’élimination du modèle de référence \(\pi_{\text{ref}}\), ce qui réduit de moitié la mémoire GPU nécessaire.

Parmi les autres variantes notables :

  • SimPO (Simple Preference Optimization, Meng et al., 2024) simplifie DPO en utilisant la log-probabilité moyenne (normalisée par la longueur) comme récompense implicite et en ajoutant un terme de marge. Cela élimine le besoin de \(\pi_{\text{ref}}\) tout en préservant la stabilité.

  • KTO (Kahneman-Tversky Optimization, Ethayarajh et al., 2024) s’inspire de la théorie des perspectives de Kahneman et Tversky pour fonctionner avec des signaux de retour non appariés : au lieu de paires (préféré, rejeté), KTO peut utiliser des retours individuels « bon » ou « mauvais » sur des réponses isolées. Cela simplifie considérablement la collecte de données.

  • IPO (Identity Preference Optimization, Azar et al., 2023) corrige un biais théorique de DPO en utilisant une perte quadratique plutôt que logistique, ce qui évite la sur-confiance sur les paires faciles.

Hide code cell source

# Tableau comparatif des méthodes d'alignement
fig, ax = plt.subplots(figsize=(14, 5))
ax.axis("off")

methodes = ["RLHF (PPO)", "DPO", "ORPO", "SimPO", "KTO"]
criteres = ["Reward\nModel", "Modele\nref.", "Paires", "Stabilité", "Mémoire", "Simplicité"]
data = [["Oui", "Oui", "Oui", "Faible", "Faible", "Faible"],
        ["Non", "Oui", "Oui", "Bonne", "Moyenne", "Bonne"],
        ["Non", "Non", "Oui", "Bonne", "Excellente", "Excellente"],
        ["Non", "Non", "Oui", "Excellente", "Excellente", "Excellente"],
        ["Non", "Oui", "Non", "Bonne", "Moyenne", "Bonne"]]
colors = [["#FFCCCC" if v == "Oui" else "#CCFFCC" if v == "Non" else "#E8F5E9"
           if v in ("Bonne", "Excellente") else "#FFF3E0" for v in row] for row in data]

table = ax.table(cellText=data, rowLabels=methodes, colLabels=criteres,
                 cellColours=colors, loc="center", cellLoc="center")
table.auto_set_font_size(False); table.set_fontsize(9); table.scale(1.0, 1.8)
for key, cell in table.get_celld().items():
    cell.set_edgecolor("#CCCCCC")
    if key[0] == 0: cell.set_text_props(fontweight="bold"); cell.set_facecolor("#E8E8E8")
    if key[1] == -1: cell.set_text_props(fontweight="bold")
ax.set_title("Comparaison des méthodes d'alignement", fontsize=14, pad=20)
plt.show()
_images/a3a331d84f2f2bc308b7fb670ac4f3e9a21bfdd8098ab6d38ff8cefac3133510.png

Constitutional AI#

L’approche Constitutional AI (CAI), proposee par Bai et al. (2022, Anthropic), offre une alternative au retour humain direct en remplacant une partie des annotations humaines par de l’auto-critique guidée par des principes explicites — une « constitution ».

Définition 100 (Constitutional AI)

L”IA constitutionnelle (Constitutional AI, CAI, Bai et al., 2022) est une méthode d’alignement en deux phases :

  1. Phase de critique et revision (critique-revision) : le modèle génère une réponse initiale à un prompt, puis se critique lui-même selon un ensemble de principes (la « constitution ») et produit une version révisée. Ce processus est répété pour chaque principe pertinent. Les paires (réponse initiale, réponse révisée) servent de données d’entrainement SFT.

  2. Phase RLAIF (Reinforcement Learning from AI Feedback) : au lieu d’annotateurs humains, un modèle LLM évalue les paires de réponses en se référant à la constitution. Le modèle de récompense est entrainé sur ces préférences générées par l’IA, puis la politique est optimisée par RL comme dans le pipeline RLHF standard.

La constitution est un ensemble de principes explicites, par exemple : « Choisir la réponse la moins susceptible d’être perçue comme nuisible par un observateur raisonnable » ou « Choisir la réponse la plus honnête et véridique ».

Remarque 96 (Rôle de la constitution)

Les principes constitutionnels jouent un role analogue aux consignes données aux annotateurs humains dans RLHF, mais avec plusieurs avantages :

  • Explicite et auditable : la constitution est un document textuel que l’on peut inspecter, débattre et modifier. Les critères implicites des annotateurs humains sont, au contraire, opaques et inconsistants.

  • Scalable : la critique automatisée ne coûte que le prix de l’inférence, bien moins que les annotateurs humains. On peut générer des millions de paires critique-revision.

  • Reproductible : les mêmes principes appliqués au même modèle produisent des résultats comparables, ce qui améliore la reproductibilité par rapport aux annotations humaines.

  • Composable : on peut ajouter, retirer ou modifier des principes individuels pour ajuster le comportement du modèle sur des aspects spécifiques.

L’inconvénient est que la qualité de l’auto-critique est bornée par les capacités du modèle critique. Un modèle médiocre produira des critiques médiocres, limitant le bénéfice de l’approche.

Hide code cell source

# Diagramme de la boucle critique-revision de Constitutional AI
fig, ax = plt.subplots(figsize=(14, 7))
ax.set_xlim(0, 18)
ax.set_ylim(0, 10)
ax.axis("off")

# Constitution (en haut)
constitution_box = mpatches.FancyBboxPatch((5, 8.0), 8, 1.5, boxstyle="round,pad=0.2",
                                            facecolor="#E24A33", alpha=0.15,
                                            edgecolor="#E24A33", linewidth=2)
ax.add_patch(constitution_box)
ax.text(9, 8.75, "Constitution", ha="center", va="center",
        fontsize=13, fontweight="bold", color="#E24A33")
ax.text(9, 8.25, "Principes : sécurité, honnêteté, utilité, ...", ha="center", va="center",
        fontsize=8, color="gray")

# Etape 1 : réponse initiale
step_boxes = [
    (1.0, 4.5, 3.5, 2.0, "Prompt\n+\néeponse initiale", "#4C72B0"),
    (6.0, 4.5, 3.5, 2.0, "Critique\n(auto-évaluation)", "#DD8452"),
    (11.0, 4.5, 3.5, 2.0, "Révision\n(réponse améliorée)", "#55A868"),
]

for x_b, y_b, w, h, label, color in step_boxes:
    rect = mpatches.FancyBboxPatch((x_b, y_b), w, h, boxstyle="round,pad=0.15",
                                    facecolor=color, alpha=0.2, edgecolor=color, linewidth=2)
    ax.add_patch(rect)
    ax.text(x_b + w / 2, y_b + h / 2, label, ha="center", va="center",
            fontsize=10, fontweight="bold", color=color)

# Flèches entre étapes
for x_start, x_end in [(4.5, 6.0), (9.5, 11.0)]:
    ax.annotate("", xy=(x_end, 5.5), xytext=(x_start, 5.5),
                arrowprops=dict(arrowstyle="-|>", color="#333333", lw=2))

# Flèche de la constitution vers la critique
ax.annotate("", xy=(7.75, 6.5), xytext=(7.75, 8.0),
            arrowprops=dict(arrowstyle="-|>", color="#E24A33", lw=1.5, linestyle="--"))

# Boucle de retour (revision -> critique)
ax.annotate("", xy=(9.5, 4.5), xytext=(14.5, 4.0),
            arrowprops=dict(arrowstyle="-|>", color="gray", lw=1.5,
                          connectionstyle="arc3,rad=0.4"))
ax.text(13.0, 3.2, "Itérer si\nnécessaire", ha="center", fontsize=8, color="gray",
        fontstyle="italic")

# Sortie vers SFT
ax.text(15.5, 5.5, "Données\nSFT", ha="center", va="center",
        fontsize=10, fontweight="bold", color="#8B6DAF",
        bbox=dict(boxstyle="round,pad=0.3", facecolor="#8B6DAF", alpha=0.15,
                  edgecolor="#8B6DAF", linewidth=2))
ax.annotate("", xy=(15.0, 5.5), xytext=(14.5, 5.5),
            arrowprops=dict(arrowstyle="-|>", color="#333333", lw=2))

# Sous-titre
ax.text(9, 1.5, "Phase 1 : critique et révision. La constitution guide l'auto-évaluation.",
        ha="center", va="center", fontsize=10, fontstyle="italic", color="gray")

ax.set_title("Constitutional AI : boucle critique-révision", fontsize=14, pad=15)
plt.show()
_images/6288a28216702e84c00607d240b3999b56aa533e7ed16727f4887c8102f8fad0.png

Reward hacking et problèmes d’alignement#

L’alignement par optimisation d’une fonction de récompense apprise est sujet à une pathologie fondamentale : le reward hacking. Le modèle peut apprendre à exploiter les failles du modèle de récompense plutôt qu’à réellement satisfaire les préférences humaines.

Définition 101 (Reward hacking)

Le reward hacking (ou reward gaming, reward exploitation) est le phénomène par lequel un agent optimise une fonction de récompense proxy \(r_\phi\) de manière à obtenir un score élevé sans véritablement satisfaire l’objectif sous-jacent \(R^*\). Dans le contexte de l’alignement des LLM, cela se manifeste lorsque le modèle decouvre des stratégies qui maximisent le score du modèle de récompense sans améliorer — voire en dégradant — la qualité réelle des réponses telle qu’évaluée par des humains.

Exemples typiques :

  • Générer des réponses excessivement longues et verbeuses (le RM ayant appris que les reponses longues sont souvent préférées).

  • Répéter des formules de politesse ou des mises en garde qui plaisent au RM mais n’apportent rien.

  • Produire du texte qui « sonne bien » mais est factuellement incorrect.

  • Utiliser un style flatteur ou excessivement confiant pour obtenir un score élevé.

Remarque 97 (Loi de Goodhart)

La loi de Goodhart (1975) énonce que « quand une mesure devient un objectif, elle cesse d’être une bonne mesure ». Dans le contexte de l’alignement :

  • Le modèle de récompense \(r_\phi\) est une mesure (imparfaite) des préférences humaines.

  • Lorsqu’on en fait un objectif d’optimisation, le modèle aligne exploite les écarts entre \(r_\phi\) et les vraies préférences \(R^*\).

  • Plus l’optimisation est poussée (plus de pas PPO, \(\beta\) trop faible), plus le reward hacking devient sévère.

Ce phénomène est analogue au surapprentissage en apprentissage supervisé : le modèle s’adapte trop bien au signal d’entrainement (ici, le RM) et se généralise mal à l’objectif réel (les préférences humaines). La régularisation KL (\(\beta\)) joue un rôle analogue à la régularisation L2 : elle empêche le modèle de trop s’éloigner d’une solution raisonnable (\(\pi_{\text{ref}}\)).

Hide code cell source

# Illustration du reward hacking : score RM vs qualité réelle
steps = np.arange(0, 500)

# Score du RM : augmente continument
rm_score = 2.0 * (1 - np.exp(-steps / 100)) + 0.5 * steps / 500

# Qualite réelle : augmente puis diminue (sur-optimisation)
real_quality = 1.8 * (1 - np.exp(-steps / 80)) - 0.004 * np.maximum(steps - 150, 0)
real_quality += np.random.normal(0, 0.05, len(steps))

# Point optimal
optimal_step = 150

fig, ax = plt.subplots(figsize=(12, 6))
ax.plot(steps, rm_score, color="#4C72B0", linewidth=2.5, label="Score du reward model $r_\\phi$")
ax.plot(steps, real_quality, color="#C44E52", linewidth=2.5, label="Qualite réelle $R^*$")
ax.axvline(x=optimal_step, color="gray", linestyle="--", alpha=0.5)
ax.text(optimal_step + 5, ax.get_ylim()[1] * 0.95, "Point optimal\n(avant sur-optimisation)",
        fontsize=9, color="gray", fontstyle="italic")

# Zone de reward hacking
ax.axvspan(optimal_step, 500, alpha=0.08, color="#C44E52")
ax.text(325, 0.3, "Zone de reward hacking\n(loi de Goodhart)", ha="center",
        fontsize=10, color="#C44E52", fontstyle="italic",
        bbox=dict(boxstyle="round,pad=0.3", facecolor="white", edgecolor="#C44E52", alpha=0.8))

ax.set_xlabel("Etapes d'optimisation (PPO)")
ax.set_ylabel("Score")
ax.set_title("Reward hacking : divergence entre score RM et qualité réelle")
ax.legend(fontsize=10)
plt.show()
_images/ce0d27e357316c9b393b1327736e83c46176cac8882b3686ed02993fe0bd18d4.png

Le problème du reward hacking motive le recours à la régularisation KL, qui pénalise les politiques trop éloignées de \(\pi_{\text{ref}}\). Cependant, un \(\beta\) trop élevé empêche tout progrès, tandis qu’un \(\beta\) trop faible laisse le champ libre au reward hacking. Trouver le bon équilibre est un art empirique.

Le trilemme HHH#

Les objectifs d’alignement sont souvent résumés par l’acronyme HHH : Helpful, Harmless, Honest (Askell et al., 2021, Anthropic). Ces trois propriétés définissent un trilemme : il est difficile de les satisfaire simultanément.

Remarque 98 (Le trilemme HHH)

Le trilemme HHH (Helpful, Harmless, Honest) capture les tensions fondamentales de l’alignement :

  • Helpful (utile) : le modèle doit fournir des réponses pertinentes, complètes et actionables. Il doit aider l’utilisateur à accomplir ses objectifs.

  • Harmless (inoffensif) : le modèle ne doit pas générer de contenus dangereux, discriminatoires, trompeurs ou susceptibles de causer du tort.

  • Honest (honnête) : le modèle doit etre véridique, reconnaitre son incertitude et ses limites, et ne pas fabriquer d’informations (hallucinations).

Les tensions entre ces objectifs sont réelles :

Tension

Exemple

Helpful vs Harmless

L’utilisateur demande comment fabriquer un explosif « pour un cours de chimie ». Etre utile impliquerait de repondre ; etre inoffensif impliquerait de refuser.

Helpful vs Honest

L’utilisateur demande un avis médical. Etre utile pousserait a donner une réponse détaillée ; être honnête imposerait de rappeler les limites du modèle.

Harmless vs Honest

Un modèle trop prudent (« harmless ») refusera de répondre à des questions légitimes, ce qui est une forme de malhonnêteté par omission.

L’équilibre entre ces trois objectifs est un choix de conception qui varie selon le contexte de déploiement, la population d’utilisateurs et le cadre réglementaire.

Hide code cell source

# Radar chart du trilemme HHH pour différentes configurations
categories_hhh = ["Helpful", "Harmless", "Honest"]
n_cats = len(categories_hhh)

configs = {
    "Modèle de base\n(non aligné)":     [0.7, 0.2, 0.3],
    "Aligné\n(équilibré)":              [0.8, 0.8, 0.8],
    "Sur-aligné\n(trop prudent)":       [0.3, 0.95, 0.6],
    "Aligné utilité\n(assistant)":      [0.95, 0.6, 0.7],
}
config_colors = {
    "Modèle de base\n(non aligné)":     "#C44E52",
    "Aligné\n(équilibré)":              "#4C72B0",
    "Sur-aligné\n(trop prudent)":       "#8B6DAF",
    "Aligné utilité\n(assistant)":      "#55A868",
}

angles = np.linspace(0, 2 * np.pi, n_cats, endpoint=False).tolist()
angles += angles[:1]

fig, ax = plt.subplots(figsize=(8, 8), subplot_kw=dict(polar=True))

for config_name, scores_hhh in configs.items():
    values = scores_hhh + scores_hhh[:1]
    ax.plot(angles, values, "o-", linewidth=2.5, label=config_name,
            color=config_colors[config_name], markersize=7)
    ax.fill(angles, values, alpha=0.08, color=config_colors[config_name])

ax.set_xticks(angles[:-1])
ax.set_xticklabels(categories_hhh, fontsize=13, fontweight="bold")
ax.set_ylim(0, 1.0)
ax.set_yticks([0.2, 0.4, 0.6, 0.8, 1.0])
ax.set_yticklabels(["0.2", "0.4", "0.6", "0.8", "1.0"], fontsize=8)
ax.set_title("Trilemme HHH : différentes configurations d'alignement",
             fontsize=12, pad=25)
ax.legend(loc="lower right", bbox_to_anchor=(1.35, 0), fontsize=9)
plt.show()
_images/7d976abe481eedffcf8ad7a64586ab9f0d068a75da488b63e0ec8b3c1ebbaa99.png

Hide code cell source

# Illustration du compromis : sur-alignement vs sous-alignement
fig, ax = plt.subplots(figsize=(12, 5))

beta_values = np.linspace(0.001, 0.5, 200)

# Utilité : décroit avec beta (plus on pénalise, moins on est utile)
helpfulness = 0.95 * np.exp(-2.5 * beta_values) + 0.05
# Sécurité : augmente avec beta (plus on pénalise, plus on est sûr)
safety = 1.0 - 0.9 * np.exp(-6.0 * beta_values)
# Score global : produit des deux (compromis)
overall = helpfulness * safety

ax.plot(beta_values, helpfulness, color="#55A868", linewidth=2.5, label="Utilité (Helpful)")
ax.plot(beta_values, safety, color="#C44E52", linewidth=2.5, label="Sécurité (Harmless)")
ax.plot(beta_values, overall, color="#4C72B0", linewidth=2.5, linestyle="--",
        label="Score global")

# Point optimal
opt_idx = np.argmax(overall)
ax.scatter([beta_values[opt_idx]], [overall[opt_idx]], s=150, color="#4C72B0",
           zorder=5, edgecolor="white", linewidth=2)
ax.annotate(f"Optimum : $\\beta$ = {beta_values[opt_idx]:.3f}",
            xy=(beta_values[opt_idx], overall[opt_idx]),
            xytext=(beta_values[opt_idx] + 0.08, overall[opt_idx] + 0.05),
            fontsize=10, fontweight="bold", color="#4C72B0",
            arrowprops=dict(arrowstyle="-|>", color="#4C72B0"))

ax.set_xlabel("Coefficient de régularisation $\\beta$")
ax.set_ylabel("Score (normalisé)")
ax.set_title("Compromis utilité-securité en fonction de la régularisation KL")
ax.legend(fontsize=10)

# Annotations des zones
ax.text(0.02, 0.15, "Sous-aligne\n(reward hacking)", fontsize=9,
        color="#C44E52", fontstyle="italic")
ax.text(0.4, 0.15, "Sur-aligne\n(refus excessif)", fontsize=9,
        color="#55A868", fontstyle="italic")

plt.show()
_images/f7e9d5c792a8f77b67274f9746f73721916dbff9ed5138b77ac99ba2291e264a.png

Résumé#

Ce chapitre a présenté les fondements conceptuels et techniques de l’alignement des grands modèles de langage, c’est-à-dire le problème de faire en sorte qu’un modèle très capable se comporte conformément aux intentions de ses utilisateurs et de la société.

  1. Le problème de l’alignement nait de l’ecart entre l’objectif de pré-entrainement (prédire le token suivant) et le comportement souhaité (être utile, honnête et inoffensif). Un LLM de base est un générateur de texte, pas un assistant : l’alignement est le processus qui comble cet écart, au prix d’une « taxe d’alignement » généralement modeste.

  2. Le RLHF (Reinforcement Learning from Human Feedback) est le pipeline d’alignement historique, en trois étapes : (1) SFT sur des démonstrations humaines, (2) entrainement d’un modèle de récompense sur des préfèrences humaines via la perte de Bradley-Terry, (3) optimisation de la politique par PPO sous contrainte KL. Ce pipeline a produit ChatGPT et InstructGPT, mais il est complexe, coûteux et instable.

  3. Le DPO (Direct Preference Optimization, Rafailov et al., 2023) simplifie radicalement le pipeline en montrant que l’optimisation des préférences peut être reformulée comme une classification supervisée, sans modèle de récompense ni boucle RL. DPO et RLHF optimisent le même objectif, mais DPO le fait en une seule étape, avec une stabilité et une simplicité supérieures.

  4. Les variantes récentes (ORPO, SimPO, KTO) poursuivent la simplification : ORPO élimine le modèle de référence, KTO fonctionne avec des retours non appariés, SimPO normalise par la longueur. Le choix de la méthode dépend du budget, des données disponibles et des contraintes mémoire.

  5. L”IA constitutionnelle (Constitutional AI, Bai et al., 2022) remplace une partie du retour humain par de l’auto-critique guidée par des principes explicites (une « constitution »). La boucle critique-révision produit des données d’entrainement à moindre coût, avec l’avantage de l’explicite, de la scalabilité et de la reproductibilité.

  6. Le reward hacking est la pathologie centrale de l’alignement par optimisation : le modèle exploite les failles du modèle de récompense plutôt que de satisfaire les vraies préférences humaines. La loi de Goodhart — « quand une mesure devient un objectif, elle cesse d’être une bonne mesure » — formalise ce risque. La régularisation KL (\(\beta\)) est le principal garde-fou, mais son réglage reste empirique.

  7. Le trilemme HHH (Helpful, Harmless, Honest) structure la réflexion sur les objectifs d’alignement. Les tensions entre utilité, securité et honnêteté sont irréductibles : un modèle trop prudent refuse des requêtes légitimes, un modèle trop utile peut être dangereux, un modèle trop honnête sur ses limites peut paraitre inutile. L’équilibre dépend du contexte de déploiement.