Du Transformer au LLM#

Le chapitre 23 du volume Apprentissage automatique a introduit l’architecture Transformer : auto-attention multi-têtes, encodage positionnel, blocs encodeur-décodeur. Ce mécanisme, concu initialement pour la traduction automatiquée, s’est révélé être une brique de base extraordinairement polyvalente. Mais l’histoire ne s’arrête pas là. Entre 2018 et 2025, une série de découvertes empiriques a montré que l’augmentation simultanée de la taille des modèles, du volume de données et de la puissance de calcul faisait émerger des capacités qualitativement nouvelles, imprévisibles à petite échelle.

Ce passage à l’échelle — le scaling — constitue le fil conducteur de ce premier chapitre. Comprendre pourquoi un Transformer de 175 milliards de paramètres (GPT-3) se comporte différemment d’un Transformer de 117 millions (GPT-2), alors que l’architecture est essentiellement la même, est la question fondatrice du domaine des grands modèles de langage (Large Language Models, LLM). Les lois d’échelle (scaling laws) fournissent un cadre quantitatif pour y répondre, tandis que le débat sur l’émergence des capacités interroge la nature même de ce que ces modèles « apprennent ».

Ce chapitre pose les fondations conceptuelles du livre. Nous partirons de l’architecture Transformer pour expliquer ce qui change lorsqu’on augmente l’échelle de plusieurs ordres de grandeur. Nous formaliserons les lois de puissance qui relient performance, paramètres, données et calcul. Nous examinerons les principales familles de modèles et les benchmarks utilisés pour les évaluer. L’objectif est de donner au lecteur une carte mentale solide avant d’entrer dans les détails techniques des chapitres suivants.

Du Transformer au LLM : le passage à l’échelle#

L’architecture Transformer originale de Vaswani et al. (2017) comptait environ 65 millions de paramètres et était entrainée sur un corpus de traduction de quelques millions de paires de phrases. En l’espace de six ans, les modèles fondés sur cette même architecture ont vu leur taille multipliée par un facteur \(10\,000\) et leur corpus d’entrainement par un facteur comparable. Ce changement d’échelle n’est pas simplement quantitatif : il a engendre des comportements qualitativement différents.

Définition 1 (Grand modèle de langage (LLM))

Un grand modèle de langage (Large Language Model, LLM) est un modèle de langage neuronal fondé sur l’architecture Transformer (généralement dans sa variante décodeur seul), pré-entrainé sur un corpus textuel massif (typiquement des centaines de milliards à plusieurs milliers de milliards de tokens) par modélisation auto-regressive du langage. Les LLM se distinguent des modèles de langage antérieurs par leur taille (de l’ordre de \(10^{10}\) a \(10^{12}\) paramètres) et par les capacités émergentes que cette échelle leur confère, notamment le raisonnement en contexte (in-context learning), le suivi d’instructions et la génération de texte cohérent sur de longues séquences.

Définition 2 (Pré-entrainement)

Le pré-entrainement (pretraining) est la phase initiale d’entrainement d’un LLM, au cours de laquelle le modèle apprend è predire le token suivant è partir des tokens précédents sur un corpus textuel massif et non annoté. L’objectif est la maximisation de la log-vraisemblance auto-regressive :

\[\mathcal{L}(\theta) = \sum_{t=1}^{T} \log P_\theta(x_t \mid x_1, \ldots, x_{t-1})\]

Cette phase, extrêmement coûteuse en calcul (de l’ordre de \(10^{23}\) a \(10^{25}\) FLOPs pour les plus grands modèles), produit un modèle de base (base model) qui capture la structure statistique du langage et encode une vaste quantité de connaissances factuelles.

Le mécanisme central est simple : on empile davantage de blocs Transformer (profondeur), on augmente la dimension cachée \(d_{\text{model}}\) (largeur), et on entraine sur plus de texte pendant plus longtemps. Le nombre de paramètres d’un Transformer décodeur seul scale approximativement comme \(N \approx 12 \, L \, d_{\text{model}}^2\), où \(L\) est le nombre de couches. Passer de \(d_{\text{model}} = 768\) et \(L = 12\) (BERT-base, 110M paramètres) a \(d_{\text{model}} = 12\,288\) et \(L = 96\) (GPT-3, 175B paramètres) represente un facteur \(\times 1\,600\) en nombre de paramètres.

Remarque 1 (Budget de calcul)

Le coût d’entrainement d’un LLM est dominé par les multiplications matricielles dans les couches d’attention et les couches feed-forward. Pour un modèle de \(N\) paramètres entrainé sur \(D\) tokens, le nombre d’opérations en virgule flottante est approximativement \(C \approx 6ND\) (en comptant le passage avant et le passage arrière). Ainsi, entrainer GPT-3 (\(N = 175 \times 10^9\), \(D \approx 300 \times 10^9\) tokens) a necessité environ \(C \approx 3{,}14 \times 10^{23}\) FLOPs, soit plusieurs semaines sur des milliers de GPU.

Exemple 1 (Ordres de grandeur)

Pour fixer les idées, voici quelques ordres de grandeur représentatifs :

Modèle

Année

Paramètres

Tokens d’entrainement

Compute (FLOPs)

Transformer original

2017

65M

~100M

~\(10^{18}\)

BERT-base

2018

110M

~3,3B

~\(10^{19}\)

GPT-2

2019

1,5B

~10B

~\(10^{20}\)

GPT-3

2020

175B

300B

~\(3 \times 10^{23}\)

Chinchilla

2022

70B

1,4T

~\(5 \times 10^{23}\)

LLaMA 2

2023

70B

2T

~\(10^{24}\)

GPT-4

2023

~1,8T (estimé)

~13T (estimé)

~\(10^{25}\)

On constate que la taille des modèles et des corpus a augmenté de manière exponentielle, avec un doublement tous les 6 a 12 mois — un rythme bien plus rapide que la loi de Moore.

Hide code cell source

# Import des librairies Python
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import seaborn as sns

sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)
np.random.seed(42)

Hide code cell source

# Chronologie des principaux LLM (2017-2025)
models = [
    ("Transformer", 2017.5, 0.065, "#8B6DAF"),
    ("BERT", 2018.8, 0.34, "#4C72B0"),
    ("GPT-2", 2019.2, 1.5, "#DD8452"),
    ("T5-11B", 2019.8, 11, "#55A868"),
    ("GPT-3", 2020.5, 175, "#DD8452"),
    ("PaLM", 2022.3, 540, "#C44E52"),
    ("Chinchilla", 2022.4, 70, "#55A868"),
    ("LLaMA", 2023.2, 65, "#4C72B0"),
    ("GPT-4*", 2023.3, 1800, "#DD8452"),
    ("Mixtral", 2023.9, 47, "#8B6DAF"),
    ("LLaMA 3", 2024.3, 405, "#4C72B0"),
    ("Claude 3.5", 2024.5, None, "#E24A33"),
    ("DeepSeek-V3", 2024.9, 671, "#55A868"),
]

fig, ax = plt.subplots(figsize=(14, 5))
ax.set_xlim(2017, 2025.5)

for i, (name, year, params, color) in enumerate(models):
    if params is not None:
        ax.scatter(year, params, s=120, color=color, zorder=5, edgecolors="white", linewidth=0.8)
        offset_y = 1.3 if i % 2 == 0 else 0.7
        ax.annotate(f"{name}\n({params}B)" if params >= 1 else f"{name}\n({params*1000:.0f}M)",
                    xy=(year, params), fontsize=7, ha="center",
                    xytext=(0, 12 if i % 2 == 0 else -16),
                    textcoords="offset points", color=color, fontweight="bold")
    else:
        ax.axvline(year, color=color, alpha=0.3, linestyle="--")
        ax.text(year, ax.get_ylim()[1] * 0.5, name, fontsize=7, ha="center",
                color=color, fontweight="bold", rotation=90)

ax.set_yscale("log")
ax.set_xlabel("Année")
ax.set_ylabel("Nombre de paramètres (milliards)")
ax.set_title("Chronologie des principaux LLM (2017--2025)")
plt.show()
_images/6f9eada50c3a3f6e5fe26ff3f99755b082eccd53793126041f3e47415bfb9b8a.png

Lois d’échelle (Scaling Laws)#

L’une des découvertes les plus importantes de la recherche sur les LLM est que la performance des modèles de langage suit des lois de puissance (power laws) remarquablement regulières lorsqu’on augmente la taille du modèle, le volume de données ou le budget de calcul. Ces relations empiriques, appelées lois d’échelle (scaling laws), permettent de prédire la performance d’un modèle avant même de l’entrainer.

Définition 3 (Lois d’échelle (Scaling Laws))

Les lois d’échelle (scaling laws) des modèles de langage sont des relations empiriques de type loi de puissance reliant la perte du modèle (entropie croisée sur un ensemble de test) à trois facteurs :

  1. Le nombre de paramètres \(N\) : \(L(N) = \left(\frac{N_c}{N}\right)^{\alpha_N}\), avec \(\alpha_N \approx 0{,}076\)

  2. Le volume de données \(D\) (en tokens) : \(L(D) = \left(\frac{D_c}{D}\right)^{\alpha_D}\), avec \(\alpha_D \approx 0{,}095\)

  3. Le budget de calcul \(C\) (en FLOPs) : \(L(C) = \left(\frac{C_c}{C}\right)^{\alpha_C}\), avec \(\alpha_C \approx 0{,}050\)

ou \(N_c\), \(D_c\), \(C_c\) sont des constantes et \(\alpha_N\), \(\alpha_D\), \(\alpha_C\) sont les exposants de la loi de puissance. Ces relations sont observées sur plusieurs ordres de grandeur et semblent indépendantes de détails architecturaux (nombre de couches vs largeur, type de tokenisation, etc.).

Propriété 1 (Loi de puissance et linéarité en log-log)

Une loi de puissance \(L(x) = a \, x^{-\alpha}\) se traduit par une relation linéaire en échelle logarithmique :

\[\log L = \log a - \alpha \log x\]

C’est cette linéarité en graphe log-log qui rend les lois d’échelle si frappantes et si utiles : en mesurant la perte pour quelques valeurs de \(x\) (paramètres, données ou compute), on peut extrapoler à des échelles bien plus grandes par simple régression linéaire. La pente de la droite donne directement l’exposant \(\alpha\), qui mesure la « rentabilité » de l’augmentation de l’échelle.

Hide code cell source

fig, axes = plt.subplots(1, 3, figsize=(14, 5))

# Paramètres simulés (loi de puissance)
N_vals = np.logspace(7, 11, 50)
D_vals = np.logspace(8, 12, 50)
C_vals = np.logspace(17, 24, 50)

alpha_N, alpha_D, alpha_C = 0.076, 0.095, 0.050
Nc, Dc, Cc = 8.8e13, 5.4e13, 3.1e8

L_N = (Nc / N_vals) ** alpha_N
L_D = (Dc / D_vals) ** alpha_D
L_C = (Cc / C_vals) ** alpha_C

# Ajout de bruit pour réalisme
noise = lambda n: np.exp(np.random.normal(0, 0.01, n))

axes[0].loglog(N_vals, L_N * noise(len(N_vals)), 'o', color='#4C72B0',
               markersize=3, alpha=0.6)
axes[0].loglog(N_vals, L_N, '-', color='#4C72B0', linewidth=2)
axes[0].set_xlabel("Nombre de paramètres $N$")
axes[0].set_ylabel("Perte $L$")
axes[0].set_title("$L(N) \\propto N^{-0{,}076}$")

axes[1].loglog(D_vals, L_D * noise(len(D_vals)), 'o', color='#55A868',
               markersize=3, alpha=0.6)
axes[1].loglog(D_vals, L_D, '-', color='#55A868', linewidth=2)
axes[1].set_xlabel("Tokens d'entrainement $D$")
axes[1].set_ylabel("Perte $L$")
axes[1].set_title("$L(D) \\propto D^{-0{,}095}$")

axes[2].loglog(C_vals, L_C * noise(len(C_vals)), 'o', color='#E24A33',
               markersize=3, alpha=0.6)
axes[2].loglog(C_vals, L_C, '-', color='#E24A33', linewidth=2)
axes[2].set_xlabel("Compute $C$ (FLOPs)")
axes[2].set_ylabel("Perte $L$")
axes[2].set_title("$L(C) \\propto C^{-0{,}050}$")

fig.suptitle("Lois d'échelle de Kaplan et al. (2020) --- données simulées", fontsize=13, y=1.02)
plt.show()
_images/f4c79190f8c6f0dd069c75d0c2004896375ab9642716a7ee527aafe7ee74e293.png

L’optimum de Chinchilla#

Les travaux de Kaplan et al. (2020) suggéraient que, pour un budget de calcul fixe, il valait mieux investir dans un modèle plus grand entrainé sur relativement peu de données. Hoffmann et al. (2022), dans l’article dit « Chinchilla », ont contesté cette conclusion en montrant que les modèles existants étaient significativement sous-entrainés : pour un budget de calcul optimal, le nombre de paramètres et le nombre de tokens doivent croitre au même rythme.

Définition 4 (Loi de Chinchilla (ratio optimal))

Le résultat central de Hoffmann et al. (2022) est que, pour un budget de calcul \(C\) donné, la perte minimale est atteinte lorsque le nombre de paramètres \(N\) et le nombre de tokens \(D\) sont choisis de manière proportionnelle :

\[D_{\text{opt}} \approx 20 \, N\]

Autrement dit, un modèle de \(N\) paramètres devrait être entrainé sur environ \(20N\) tokens pour exploiter optimalement le budget de calcul. Cette relation implique que \(N_{\text{opt}} \propto C^{0{,}5}\) et \(D_{\text{opt}} \propto C^{0{,}5}\) : paramètres et données doivent croître à parts égales avec le compute.

Remarque 2 (Chinchilla vs Kaplan)

La différence entre les recommandations de Kaplan et celles de Chinchilla est considérable en pratique. Kaplan suggérait d’allouer la majeure partie du budget à la taille du modèle (\(N \propto C^{0{,}73}\)), tandis que Chinchilla montre que l’allocation optimale est \(N \propto C^{0{,}5}\). Concrètement, GPT-3 (175B parametres, 300B tokens) était largement sous-entrainé selon le critère de Chinchilla : il aurait fallu environ \(20 \times 175B = 3{,}5T\) tokens. Le modèle Chinchilla (70B parametres, 1,4T tokens) atteint des performances comparables a Gopher (280B parametres, 300B tokens) avec quatre fois moins de paramètres, simplement en équilibrant le ratio données/paramètres.

Hide code cell source

fig, ax = plt.subplots(figsize=(10, 7))

# Courbes iso-compute
N_range = np.logspace(9, 12, 200)

compute_budgets = [1e21, 1e22, 1e23, 1e24, 1e25]
colors_iso = plt.cm.viridis(np.linspace(0.2, 0.9, len(compute_budgets)))

for C_budget, col in zip(compute_budgets, colors_iso):
    # C = 6 * N * D => D = C / (6N)
    D_range = C_budget / (6 * N_range)
    valid = D_range > 1e8
    ax.loglog(N_range[valid], D_range[valid], '--', color=col, alpha=0.5, linewidth=1.2)
    # Etiquette
    idx = len(N_range[valid]) // 3
    if np.sum(valid) > idx:
        ax.text(N_range[valid][idx], D_range[valid][idx] * 1.3,
                f"$C = 10^{{{int(np.log10(C_budget))}}}$",
                fontsize=7, color=col, alpha=0.8)

# Ligne optimale de Chinchilla : D = 20N
D_chinchilla = 20 * N_range
ax.loglog(N_range, D_chinchilla, '-', color='#E24A33', linewidth=2.5,
          label="Optimum Chinchilla : $D = 20N$")

# Modèles existants
models_pts = [
    ("GPT-3", 175e9, 300e9, "#DD8452"),
    ("Chinchilla", 70e9, 1.4e12, "#55A868"),
    ("LLaMA", 65e9, 1.4e12, "#4C72B0"),
    ("LLaMA 2", 70e9, 2e12, "#4C72B0"),
    ("Gopher", 280e9, 300e9, "#8B6DAF"),
    ("PaLM", 540e9, 780e9, "#C44E52"),
]

for name, n, d, col in models_pts:
    ax.scatter(n, d, s=100, color=col, zorder=5, edgecolors="white", linewidth=0.8)
    ax.annotate(name, (n, d), fontsize=8, fontweight="bold", color=col,
                xytext=(8, 5), textcoords="offset points")

ax.set_xlabel("Nombre de paramètres $N$")
ax.set_ylabel("Tokens d'entrainement $D$")
ax.set_title("Ratio optimal données/paramètres et courbes iso-compute")
ax.legend(fontsize=10)
ax.set_xlim(1e9, 2e12)
ax.set_ylim(1e9, 1e13)
plt.show()
_images/2db226324b6be6857def3ece45457c3a547cccd98c8cafa2c3c335bbc100f6db.png

Emergence de capacités#

L’un des phénomènes les plus débattus dans la recherche sur les LLM est l”émergence de capacités à grande échelle. Certaines tâches semblent impossibles pour des modèles de petite taille, puis deviennent soudainement réalisables lorsque le modèle dépasse un seuil critique de paramètres ou de compute.

Définition 5 (Capacités émergentes)

Une capacité émergente (emergent ability) d’un LLM est une capacité qui est absente dans les modèles de petite taille mais qui apparait dans les modèles de grande taille. Plus formellement, Wei et al. (2022) définissent une capacité comme émergente si sa performance est proche du hasard pour des modèles en dessous d’un certain seuil d’échelle, puis augmente brusquement au-dela de ce seuil. Cette transition evoque les transitions de phase en physique statistique, ou un changement quantitatif (la température) engendre un changement qualitatif (le passage de l’état liquide à l’état gazeux).

Exemple 2 (Exemples de capacités émergentes)

Parmi les capacités fréquemment citées comme émergentes :

  • Raisonnement en chaine de pensée (Chain-of-Thought, CoT) : la capacité à décomposer un problème en étapes intermediaires n’apparait de manière fiable qu’au-delà d’environ \(10^{11}\) paramètres (Wei et al., 2022). Les petits modèles ne bénéficient pas du prompt « Réflechissons étape par étape ».

  • Apprentissage en contexte (in-context learning) : la capacité à réaliser une tâche à partir de quelques exemples dans le prompt, sans mise à jour des poids. Ce comportement, documenté par Brown et al. (2020) pour GPT-3, est quasi absent dans GPT-2.

  • Suivi d’instructions (instruction following) : la capacité à comprendre et exécuter des instructions en langue naturelle, qui émerge après un alignement par RLHF sur des modèles suffisamment grands.

  • Arithmétique multi-chiffres : la multiplication de nombres à 3+ chiffres échoue à petite echelle puis réussit de manière fiable au-delà d’un seuil de taille.

Remarque 3 (Modèle de fondation)

Le terme modèle de fondation (foundation model), introduit par Bommasani et al. (2021), désigne un modèle pré-entrainé à grande échelle sur des données larges et non spécifiques, qui peut ensuite être adapté à une varieté de tâches en aval. Les LLM sont les exemples les plus emblématiques de modèles de fondation, mais le concept s’étend aux modèles multimodaux (texte + image), aux modèles de code et aux modèles scientifiques. Le terme souligne que ces modèles servent de « fondation » a un écosystème entier d’applications, et que leurs biais et limitations se propagent à toutes les utilisations en aval.

Le débat sur la réalité de l’émergence reste ouvert. Schaeffer et al. (2023) ont montré que l’émergence apparente peut être un artefact du choix de la métrique : lorsqu’on remplace une métrique discontinue (exact match) par une metrique continue (log-vraisemblance du token correct), la transition brusque disparait au profit d’une amélioration graduelle. Néanmoins, même si l’émergence « au sens strict » est contestée, il reste indéniable que les grands modèles manifestent des capacités absentes chez les petits — la question est de savoir si cette transition est réellement abrupte ou simplement graduelle et amplifiée par le choix de métrique.

Hide code cell source

fig, axes = plt.subplots(1, 2, figsize=(13, 5))

# Gauche : émergence apparente (métrique discontinue, exact match)
params_log = np.linspace(7, 12, 200)
params = 10 ** params_log

tasks = [
    ("Arithmétique 3 chiffres", 10.0, 0.6),
    ("CoT reasoning", 10.5, 0.5),
    ("Instruction following", 10.8, 0.4),
    ("Traduction zero-shot", 9.5, 0.7),
]
colors_tasks = ["#4C72B0", "#DD8452", "#55A868", "#E24A33"]

for (name, threshold, steepness), col in zip(tasks, colors_tasks):
    perf = 1 / (1 + np.exp(-steepness * (params_log * 2 - threshold * 2)))
    noise_t = np.random.normal(0, 0.02, len(perf))
    axes[0].plot(params, np.clip(perf + noise_t, 0, 1), '-', label=name,
                 color=col, linewidth=2)

axes[0].set_xscale("log")
axes[0].set_xlabel("Nombre de paramètres $N$")
axes[0].set_ylabel("Performance (exact match)")
axes[0].set_title("Emergence apparente (métrique discontinue)")
axes[0].legend(fontsize=8, loc="upper left")
axes[0].set_ylim(-0.05, 1.1)
axes[0].axhline(y=0, color='gray', linestyle='-', alpha=0.2)

# Droite : même données en log-vraisemblance (métrique continue)
for (name, threshold, steepness), col in zip(tasks, colors_tasks):
    # En log-likelihood, la progression est plus graduelle
    ll = -3.0 + 3.0 * (params_log - 7) / 5 + np.random.normal(0, 0.08, len(params_log))
    ll = np.clip(ll, -3.5, 0.2)
    axes[1].plot(params, ll, '-', label=name, color=col, linewidth=2)

axes[1].set_xscale("log")
axes[1].set_xlabel("Nombre de paramètres $N$")
axes[1].set_ylabel("Log-vraisemblance (token correct)")
axes[1].set_title("Amélioration graduelle (métrique continue)")
axes[1].legend(fontsize=8, loc="lower right")

fig.suptitle("Emergence : transition de phase ou artefact de métrique ?", fontsize=13, y=1.02)
plt.show()
_images/98403d7140368d0f057165dd2bfffcf02b268a85b18abd6a335f3c40c64a64ef.png

Familles de modèles#

Le paysage des LLM est structuré autour de quelques grandes familles, distinguées par leur architecture (encodeur, décodeur, ou les deux), leur régime d’accès (ouvert ou fermé) et leur stratégie d’échelle (dense ou par mélange d’experts).

Architecture : encodeur, décodeur, encodeur-décodeur#

Le Transformer original comportait un encodeur et un décodeur, mais les LLM modernes utilisent majoritairement le décodeur seul (decoder-only). Ce choix est motivé par sa simplicité et son adéquation à la génération auto-régressive.

  • Encodeur seul : BERT, RoBERTa. L’attention est bidirectionnelle. Adapté à la classification, l’extraction d’entités, etc. Ces modèles ne sont généralement pas classés parmi les LLM au sens strict.

  • Décodeur seul : GPT, Claude, LLaMA, Mistral. L’attention est causale (masque triangulaire). C’est l’architecture dominante pour les LLM génératifs.

  • Encodeur-decodeur : T5, BART, UL2. L’encodeur utilise une attention bidirectionnelle, le décodeur une attention causale. Adapté aux tâches de transformation texte-a-texte.

Dense vs Mixture of Experts (MoE)#

Définition 6 (Mélange d’experts (MoE))

Une architecture Mixture of Experts (MoE) remplace les couches feed-forward denses du Transformer par un ensemble de \(E\) sous-réseaux « experts », parmi lesquels seuls \(k \ll E\) sont actives pour chaque token. Un réseau de routage (gating network) détermine quels experts sont actifs :

\[\begin{split}y = \sum_{i=1}^{E} g_i(x) \cdot \text{Expert}_i(x), \quad \text{avec} \quad g_i(x) = \begin{cases} \text{softmax}(W_g \, x)_i & \text{si } i \in \text{Top-}k \\ 0 & \text{sinon} \end{cases}\end{split}\]

L’avantage est que le nombre total de paramètres est très grand (capacité du modèle), mais le coût de calcul par token ne dépend que de \(k\) experts actifs. Ainsi, Mixtral 8x7B possède 47B de paramètres totaux mais n’en active qu’environ 13B par token, offrant un compromis favorable entre capacité et efficacité.

Remarque 4 (Dense vs MoE)

Les modèles denses activent tous leurs paramètres pour chaque token, ce qui rend le coût de calcul proportionnel a \(N\). Les modèles MoE décorrellent la taille du modèle du coût de calcul : un MoE de 1 000B de paramètres totaux peut avoir un coût d’inférence comparable à un modèle dense de 100B. C’est cette propriété qui a motivé le développement de modèles comme Switch Transformer (Google, 2021), Mixtral (Mistral AI, 2023) et DeepSeek-V3 (2024). Le principal defi des MoE est l’équilibrage de la charge (load balancing) : il faut éviter que certains experts soient systématiquement sous-utilisés tandis que d’autres sont surchargés.

Principales familles#

Exemple 3 (Familles de modèles)

Famille

Organisation

Architecture

Accès

Tailles notables

GPT

OpenAI

Décodeur dense

Fermé (API)

GPT-3 (175B), GPT-4 (~1,8T MoE*)

Claude

Anthropic

Décodeur dense

Fermé (API)

Claude 3 Opus, Claude 3.5 Sonnet

LLaMA

Meta

Décodeur dense

Ouvert (poids)

LLaMA 2 (7/13/70B), LLaMA 3 (8/70/405B)

Mistral

Mistral AI

Décodeur dense + MoE

Ouvert (poids)

Mistral 7B, Mixtral 8x7B, Mixtral 8x22B

Gemini

Google

Décodeur dense (MoE*)

Fermé (API)

Gemini 1.5 Pro, Gemini Ultra

DeepSeek

DeepSeek

Décodeur MoE

Ouvert (poids)

DeepSeek-V2 (236B), DeepSeek-V3 (671B)

Qwen

Alibaba

Décodeur dense + MoE

Ouvert (poids)

Qwen 2.5 (7/72B), Qwen-MoE

*Les détails architecturaux exacts de GPT-4 et Gemini ne sont pas publiés ; les valeurs sont des estimations de la communauté.

Remarque 5 (Modèles ouverts vs fermés)

La distinction entre modèles ouverts (open-weight) et fermés (closed) est structurante pour le domaine. Les modèles ouverts (LLaMA, Mistral, DeepSeek) publient leurs poids et permettent l’inspection, le fine-tuning et le déploiement local. Les modèles fermés (GPT-4, Claude, Gemini) ne sont accessibles que via des API et leurs détails internes sont confidentiels. Cette tension entre ouverture et contrôle a des implications majeures pour la reproductibilité scientifique, la sécurité et l’innovation. On notera que « open-weight » n’est pas synonyme de « open-source » : les poids sont publiés mais les données d’entrainement et le code restent souvent propriétaires.

Hide code cell source

# Comparaison des tailles de modèles par famille
families = {
    "GPT": [("GPT-2", 1.5), ("GPT-3", 175), ("GPT-4*", 1800)],
    "Claude": [("Claude 2", 130), ("Claude 3\nSonnet", 70), ("Claude 3\nOpus", 200)],
    "LLaMA": [("LLaMA\n7B", 7), ("LLaMA\n70B", 70), ("LLaMA 3\n405B", 405)],
    "Mistral": [("Mistral\n7B", 7), ("Mixtral\n8x7B", 47), ("Mixtral\n8x22B", 141)],
    "DeepSeek": [("V2", 236), ("V2.5", 236), ("V3", 671)],
}

fig, ax = plt.subplots(figsize=(14, 6))
family_colors = {"GPT": "#DD8452", "Claude": "#E24A33", "LLaMA": "#4C72B0",
                 "Mistral": "#8B6DAF", "DeepSeek": "#55A868"}

x_pos = 0
x_ticks, x_labels = [], []
for family, models_list in families.items():
    for name, params in models_list:
        ax.bar(x_pos, params, color=family_colors[family], edgecolor="white",
               width=0.7, alpha=0.85)
        if params > 50:
            ax.text(x_pos, params + 20, f"{params}B", ha="center", fontsize=7, fontweight="bold")
        else:
            ax.text(x_pos, params + 20, f"{params}B", ha="center", fontsize=7)
        x_ticks.append(x_pos)
        x_labels.append(name)
        x_pos += 1
    x_pos += 0.5  # espace entre familles

ax.set_xticks(x_ticks)
ax.set_xticklabels(x_labels, fontsize=7, rotation=0)
ax.set_ylabel("Nombre de parametres (milliards)")
ax.set_title("Taille des modeles par famille (* = estimation)")
ax.set_yscale("log")
ax.set_ylim(1, 5000)

# Légende
handles = [mpatches.Patch(color=c, label=f) for f, c in family_colors.items()]
ax.legend(handles=handles, fontsize=9, loc="upper left")
plt.show()
_images/ae192e4d072173f23b754af6ce948fbb1f775dc8d2f4354c51bacebafc5d6c2e.png

Hide code cell source

# Radar chart : comparaison de modèles sur différents benchmarks (données simulées)
categories = ["MMLU", "HumanEval", "GSM8K", "MATH", "TruthfulQA", "MT-Bench"]
n_cats = len(categories)

# Scores simulés (normalisés sur 100)
model_scores = {
    "GPT-4":       [86, 82, 92, 68, 78, 92],
    "Claude 3.5":  [88, 85, 90, 71, 82, 90],
    "LLaMA 3 70B": [79, 72, 82, 52, 63, 78],
    "Mixtral 8x22B": [77, 68, 78, 48, 66, 76],
}
model_colors = {"GPT-4": "#DD8452", "Claude 3.5": "#E24A33",
                "LLaMA 3 70B": "#4C72B0", "Mixtral 8x22B": "#8B6DAF"}

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 model_name, scores in model_scores.items():
    values = scores + scores[:1]
    ax.plot(angles, values, 'o-', linewidth=2, label=model_name,
            color=model_colors[model_name], markersize=5)
    ax.fill(angles, values, alpha=0.1, color=model_colors[model_name])

ax.set_xticks(angles[:-1])
ax.set_xticklabels(categories, fontsize=10)
ax.set_ylim(0, 100)
ax.set_yticks([20, 40, 60, 80, 100])
ax.set_yticklabels(["20", "40", "60", "80", "100"], fontsize=7)
ax.set_title("Comparaison des modèles sur les principaux benchmarks\n(données illustratives)",
             fontsize=12, pad=20)
ax.legend(loc="lower right", bbox_to_anchor=(1.25, 0), fontsize=9)
plt.show()
_images/6cfb81eaa153dec98bc8a0fcf6b9c5434cf95ef78c1ea0ad72c868843c8a901c.png

Benchmarks et évaluation#

L’évaluation des LLM est un problème ouvert et fondamental. A mesure que les modèles progressent, les benchmarks existants saturent et de nouveaux sont créés, dans une dynamique de « course aux armements » entre capacités des modèles et difficulté des évaluations.

Définition 7 (Benchmark)

Un benchmark est un ensemble de tâches standardisées, accompagne d’un protocole d’évaluation et d’une métrique, utilisé pour mesurer et comparer les performances des modèles. Un bon benchmark doit être discriminant (séparer les modèles de niveaux différents), reproductible (protocole fixé et données publiques), représentatif (couvrir des capacités pertinentes) et résistant à la contamination (les données de test ne doivent pas se retrouver dans les données d’entrainement).

Exemple 4 (Principaux benchmarks pour LLM)

Benchmark

Domaine

Metrique

Description

MMLU

Connaissances générales

Accuracy (QCM)

57 matières, du lycée au doctorat

HumanEval

Code

pass@k

Génération de fonctions Python

GSM8K

Mathématiques (primaire)

Exact match

Problèmes de maths a 2-8 étapes

MATH

Mathématiques (avancé)

Exact match

Problèmes de compétitions mathématiques

TruthfulQA

Véracité

BLEURT / juge

Résistance aux idees reçues fausses

MT-Bench

Conversation

Score LLM-juge

Evaluation multi-tour par un LLM juge

Chatbot Arena

Général

Elo (votes humains)

Comparaisons par paires par des utilisateurs

GPQA

Sciences (expert)

Accuracy (QCM)

Questions de niveau doctorat en sciences

ARC-AGI

Raisonnement abstrait

Exact match

Analogies visuelles de type QI

Chaque benchmark éclaire une facette différente des capacités du modèle. Aucun benchmark unique ne suffit à capturer la « qualité » globale d’un LLM.

Remarque 6 (Contamination des données)

La contamination des données (data contamination) est un problème majeur pour l’évaluation des LLM. Comme les corpus d’entrainement contiennent une part substantielle du web, il est possible — voire probable — que des questions et réponses de benchmarks s’y trouvent. Un modèle qui a « vu » les réponses pendant l’entrainement obtiendra un score artificiellement élevé. Ce problème est aggravé par l’opacité des corpus d’entrainement des modèles fermés. Des stratégies d’atténuation existent (déduplication, canary strings, benchmarks dynamiques comme Chatbot Arena), mais aucune n’est parfaite. La contamination est l’une des raisons pour lesquelles les classements sur les benchmarks statiques doivent être interprétés avec prudence.

Un autre phénomène préoccupant est la saturation des benchmarks. MMLU, longtemps considéré comme un objectif ambitieux, est désormais approché ou dépassé par les meilleurs modèles (>90% de precision). GSM8K, conçu pour être difficile en 2021, est aujourd’hui résolu à plus de 95% par GPT-4 et Claude 3.5. Cette saturation nécessite la création continue de benchmarks plus difficiles (GPQA, ARC-AGI, Frontier Math) et alimente le debat sur ce que les scores mesurent réellement.

Hide code cell source

# Evolution des scores sur MMLU au fil des modèles
models_mmlu = [
    ("GPT-3\n(2020)", 43.9),
    ("Chinchilla\n(2022)", 67.6),
    ("GPT-4\n(2023)", 86.4),
    ("Claude 3\nOpus (2024)", 86.8),
    ("GPT-4o\n(2024)", 88.7),
    ("Claude 3.5\nSonnet (2024)", 88.7),
    ("LLaMA 3\n405B (2024)", 88.6),
]

fig, axes = plt.subplots(1, 2, figsize=(13, 5))

names = [m[0] for m in models_mmlu]
scores = [m[1] for m in models_mmlu]
colors_bar = plt.cm.viridis(np.linspace(0.3, 0.9, len(models_mmlu)))

axes[0].barh(range(len(names)), scores, color=colors_bar, edgecolor="white", height=0.6)
axes[0].set_yticks(range(len(names)))
axes[0].set_yticklabels(names, fontsize=8)
axes[0].set_xlabel("Score MMLU (%)")
axes[0].set_title("Progression des scores MMLU (2020--2024)")
axes[0].set_xlim(0, 100)
axes[0].axvline(x=90, color='gray', linestyle='--', alpha=0.4, label="Seuil de saturation")
axes[0].legend(fontsize=8)
for i, s in enumerate(scores):
    axes[0].text(s + 0.5, i, f"{s:.1f}%", va="center", fontsize=8)

# Droite : saturation schématique
years = np.array([2020, 2021, 2022, 2023, 2024, 2025])
benchmarks_sat = {
    "MMLU": [44, 55, 68, 86, 89, 91],
    "GSM8K": [15, 35, 58, 92, 96, 97],
    "HumanEval": [20, 30, 48, 67, 85, 92],
    "MATH": [5, 8, 15, 42, 68, 78],
}
bench_colors = {"MMLU": "#4C72B0", "GSM8K": "#55A868",
                "HumanEval": "#DD8452", "MATH": "#E24A33"}

for bench, vals in benchmarks_sat.items():
    axes[1].plot(years, vals, 'o-', label=bench, color=bench_colors[bench],
                 linewidth=2, markersize=5)

axes[1].axhline(y=95, color='gray', linestyle='--', alpha=0.4, label="Zone de saturation")
axes[1].set_xlabel("Annee")
axes[1].set_ylabel("Score (%)")
axes[1].set_title("Saturation progressive des benchmarks")
axes[1].legend(fontsize=8)
axes[1].set_ylim(0, 105)
plt.show()
_images/33fb5cc7f4337cc2e4930550cda3949c1dad111543f075360a82f8adad1b8ea2.png

Résumé#

Ce chapitre a tracé le chemin qui mène de l’architecture Transformer (chapitre 23 du volume précédent) aux grands modèles de langage qui définissent l’état de l’art actuel.

  1. Un LLM est un Transformer décodeur seul de grande taille (\(\geq 10^{10}\) paramètres), pré-entrainé par modélisation auto-régressive du langage sur un corpus massif. L’architecture reste fondamentalement celle de Vaswani et al. (2017) ; c’est l”échelle qui change tout.

  2. Les lois d’échelle de Kaplan et al. (2020) montrent que la perte suit des lois de puissance en fonction du nombre de paramètres (\(L \propto N^{-0{,}076}\)), du volume de données (\(L \propto D^{-0{,}095}\)) et du budget de calcul (\(L \propto C^{-0{,}050}\)). Ces relations, linéaires en échelle log-log, permettent de prédire la performance à grande échelle.

  3. La loi de Chinchilla (Hoffmann et al., 2022) établit que le ratio optimal est \(D \approx 20N\) : un modèle doit être entrainé sur environ 20 tokens par paramètre. Ce résultat a montré que GPT-3 était sous-entrainé et a réorienté les stratégies d’entrainement vers un meilleur équilibre données/paramètres.

  4. Les capacités émergentes — raisonnement en chaine de pensée, apprentissage en contexte, suivi d’instructions — apparaissent au-delà de certains seuils d’échelle. Le débat sur la realité de l’émergence (transition de phase vs artefact de métrique) reste ouvert mais stimulant.

  5. Le paysage des LLM est structuré autour de grandes familles (GPT, Claude, LLaMA, Mistral, Gemini, DeepSeek), distinguées par leur architecture (décodeur dense vs MoE), leur régime d’accès (ouvert vs fermé) et leur stratégie d’échelle.

  6. L”évaluation des LLM repose sur une batterie de benchmarks (MMLU, HumanEval, GSM8K, MATH, TruthfulQA, MT-Bench, Chatbot Arena) dont aucun ne capture à lui seul la qualité globale d’un modèle. Les problèmes de contamination des données et de saturation des benchmarks imposent une interprétation prudente des classements.

  7. Le coût d’entrainement, approximativement \(C \approx 6ND\) FLOPs, est un facteur déterminant qui structure la recherche : seules quelques organisations disposent des ressources nécessaires pour entrainer les plus grands modèles, ce qui souleve des questions d’accès, de reproductibilité et de concentration du pouvoir technologique.