Module 6 — L'encodeur : BERT et les modèles de compréhension
Empilez la couche d'encodeur du module 5 douze fois de suite, ajoutez un encodage de position à l'entrée, et vous avez la moitié « compréhension » du Transformer. Cette moitié seule, préentraînée à grande échelle, est ce que BERT a popularisé en 2018 : un modèle qui lit un texte sans le générer et qui alimente ensuite des classifieurs, des extracteurs d'entités, des systèmes de recherche. Ce module explique la mécanique du préentraînement, ce que le jeton [CLS] transporte, et comment affiner un tel modèle sur une tâche cible.
Une pile d'encodeurs, rien de plus
BERT-Base est simple à décrire : douze couches identiques du bloc étudié au module 5, une dimension , douze têtes d'attention, et une dimension feed-forward . À l'entrée, chaque jeton reçoit trois plongements additionnés : le plongement de mot, le plongement de position, et un plongement de segment qui distingue phrase A et phrase B pour les tâches à deux entrées.
Aucun masque causal n'est appliqué : chaque jeton peut regarder à gauche et à droite. C'est précisément ce qui fait la valeur d'un encodeur pour la compréhension — il dispose du contexte complet pour représenter chaque position, alors qu'un décodeur (module 7) doit se contenter de ce qui précède.
Le préentraînement masqué
L'idée décisive de BERT n'est pas dans l'architecture, elle est dans l'objectif d'entraînement. Devlin et ses coauteurs remarquent qu'on peut apprendre une représentation puissante à partir de texte brut, sans étiquettes, en cachant environ 15 % des jetons et en demandant au modèle de les retrouver. C'est la modélisation du langage masqué.
Concrètement, pour chaque phrase :
- 15 % des jetons sont sélectionnés au hasard ;
- 80 % de ces jetons sont remplacés par
[MASK], 10 % par un jeton aléatoire, 10 % laissés intacts ; - le modèle produit une distribution sur le vocabulaire à chaque position sélectionnée, et l'entropie croisée est calculée uniquement sur ces positions.
Cet objectif oblige le modèle à s'appuyer sur le contexte à gauche et à droite pour deviner chaque jeton caché, ce qui aligne parfaitement la tâche avec l'architecture. À l'issue du préentraînement, les représentations produites par les dernières couches contiennent une information linguistique riche, transférable à toute une famille de tâches en aval.
Masquer trop peu ne fournit pas assez de signal ; masquer trop enlève tellement de contexte que la tâche devient impossible. 15 % est le compromis empirique qui a survécu à cinq ans de variantes.
Le jeton [CLS] et la tête de classification
Chaque séquence commence par un jeton spécial [CLS], ajouté artificiellement. Ce jeton n'a aucune signification lexicale ; son rôle apparaît à l'entraînement. Comme l'attention est bidirectionnelle, la représentation finale de [CLS] a regardé toute la séquence et sert donc de vecteur récapitulatif.
Pour une tâche de classification — analyse de sentiment, détection de spam, tri de tickets — on entraîne un classifieur linéaire au-dessus de la représentation finale de [CLS] :
import torch
from transformers import AutoTokenizer, AutoModel
tokenizer = AutoTokenizer.from_pretrained("camembert-base")
modele = AutoModel.from_pretrained("camembert-base")
phrase = "3 mars 2026 est une date bien formee."
entrees = tokenizer(phrase, return_tensors="pt")
sorties = modele(**entrees)
# La representation finale de [CLS] est le premier vecteur.
cls = sorties.last_hidden_state[:, 0, :]
print(cls.shape) # (1, 768)
Pour une extraction d'entités ou un étiquetage au niveau du jeton, on utilise à la place la représentation finale de chaque jeton individuel et l'on entraîne une tête qui prédit une classe par position.
Affinage : geler ou tout entraîner
Il existe deux régimes d'affinage. Dans le premier, on gèle le corps du modèle et on n'entraîne que la tête ajoutée. C'est rapide, économique, adapté aux jeux de données modestes. Dans le second, on entraîne toutes les couches conjointement, avec un taux d'apprentissage très faible (typiquement pour l'AdamW) sur trois à cinq époques. Ce second régime donne de meilleurs résultats quand les données le permettent, mais réclame plus de RAM et attention à ne pas oublier catastrophiquement ce que le préentraînement a appris.
Sur un jeu de données spécialisé, cinq époques sont un maximum. Au-delà, le modèle « oublie » les régularités générales apprises au préentraînement et sur-ajuste la niche. Le symptôme est classique : les scores en distribution montent, la robustesse hors distribution chute.
from transformers import AutoModelForSequenceClassification, Trainer, TrainingArguments
modele = AutoModelForSequenceClassification.from_pretrained(
"camembert-base", num_labels=2
)
args = TrainingArguments(
output_dir="./runs/sentiment",
num_train_epochs=3,
per_device_train_batch_size=16,
learning_rate=2e-5,
weight_decay=0.01,
logging_steps=50,
eval_strategy="epoch",
)
# entraineur = Trainer(model=modele, args=args, train_dataset=..., eval_dataset=...)
# entraineur.train()
Le code ci-dessus ne s'exécute pas tel quel — il manque le jeu de données — mais il montre le squelette minimal d'un affinage en trois époques avec Hugging Face.
Le zoo des variantes
BERT a engendré une famille dont il faut connaître les grandes lignes, pour choisir vite le modèle adapté :
| Modèle | Ce qu'il change | Quand le préférer |
|---|---|---|
| RoBERTa | Préentraînement plus long, sans la tâche « prédire la phrase suivante » | Meilleure performance à taille égale |
| DistilBERT | Distillation en une version deux fois plus légère | Latence contrainte, mobile ou périphérie |
| CamemBERT | Préentraîné sur du texte français (OSCAR) | Toute tâche en langue française |
| FlauBERT | Autre BERT français, entraîné indépendamment | Alternative à CamemBERT selon la tâche |
| AraBERT | Préentraîné sur de l'arabe | Toute tâche en langue arabe |
| DeBERTa | Attention désenchevêtrée et positions relatives | Meilleur score sur les jeux publics à taille égale |
Sur le fil rouge, pour une tâche jouet en français, CamemBERT est le choix naturel dès qu'on souhaite comparer notre Transformer maison à un modèle préentraîné. Sur un projet réel de production française, la question de départ est presque toujours « CamemBERT ou DistilCamemBERT ? », et la latence tranche.
Ce qu'un encodeur ne fait pas
Un encodeur seul ne génère pas de texte. Il produit une représentation par position ; pour générer, il faudrait un mécanisme autorégressif, absent ici. Un projet qui demande un texte libre en sortie — résumé, réponse ouverte, code — n'utilisera pas un encodeur nu, mais un décodeur (module 7) ou un encodeur-décodeur (module 8).
Cette limite est aussi une force : parce qu'il ne génère pas, un encodeur est déterministe, plus rapide à évaluer, plus simple à déployer. Pour tout ce qui est classement, recherche par similarité, extraction d'entités ou tri, il est le bon outil.
En résumé
- Un encodeur BERT est une pile de couches d'encodeur de type module 5, avec attention bidirectionnelle et sans masque causal.
- Le préentraînement masqué cache 15 % des jetons et demande au modèle de les retrouver, ce qui aligne l'objectif avec l'architecture bidirectionnelle.
- La représentation finale du jeton
[CLS]sert de vecteur récapitulatif pour une classification en aval ; pour un étiquetage, on utilise chaque jeton individuel. - Les variantes utiles à connaître : RoBERTa (préentraînement amélioré), DistilBERT (compression), CamemBERT (français), AraBERT (arabe), DeBERTa (positions relatives).
Module suivant : le décodeur, l'autre moitié du Transformer, qui génère un mot après l'autre et qui donne GPT.