Architecture et fonctionnement interne des modèles Transformer

L'architecture Transformer, introduite initialement par Vaswani et al. dans l'article "Attention is All You Need", a radicalement transformé le traitement automatique du langage naturel (NLP). Contrairement aux architectures récurrentes (RNN) ou convolutionnelles (CNN), le Transformer repose entièrement sur des mécanismes d'attention pour modéliser les dépendances globales au sein d'une séquence, indépendamment de la distance entre les éléments.

Structure globale de l'architecture

Le modèle suit une structure de type encodeur-décodeur :

  • L'Encodeur : Son rôle est de transformer une séquence d'entrée en une représentation vectorielle continue. Il est composé de plusieurs couches identiques, chacune intégrant un mécanisme d'attention multi-têtes suivi d'un réseau de neurones à propagation avant (Feed-Forward).
  • Le Décodeur : Il génère la séquence de sortie étape par étape. En plus des composants présents dans l'encodeur, il insère une troisième sous-couche d'attention qui se focalise sur les sorties de l'encodeur, permettant ainsi de lier les informatoins d'entrée et de sortie.

Le mécanisme d'Auto-Attention (Self-Attention)

Le cœur du Transformer est l'auto-attention. Pour chaque élément de la séquence, le modèle calcule trois vecteurs : Requête (Query), Clé (Key) et Valeur (Value).

Le score d'attention est déterminé par le produit scalaire entre la Requête d'un mot et les Clés de tous les autres mots de la séquence. Ces scores sont ensuite normalisés via une fonction Softmax pour obtenir des poids, qui sont finalement appliqués aux vecteurs Valeurs. Mathématiquement, cela se traduit par :

Attention(Q, K, V) = softmax(QKᵀ / √dₖ)V

Attention Multi-Têtes

Plutôt que d'effectuer un seul calcul d'attention, le modèle projette les vecteurs dans plusieurs sous-espaces de représentation. Chaque "tête" peut ainsi apprendre des relations différentes (syntaxiques, sémantiques, temporelles). Les résultats sont ensuite concaténés et projetés à nouveau pour retrouver la dimension d'origine.

Composants techniques essentiels

Encodage Positionnel

Comme le Transformer ne traite pas les données de manière séquentielle, il n'a aucune connaissance intrinsèque de l'ordre des mots. Pour pallier cela, on ajoute des vecteurs d'encodage positionnel aux plongements (embeddings) d'entrée. Ces vecteurs utilisent des fonctions sinusoïdales de fréquences variées pour coder la position relative ou absolue des jetons.

Réseau Feed-Forward (FFN)

Chaque couche d'attention est suivie d'un réseau de neurones totalement connecté apliqué de manière indépendante à chaque position. Il se compose généralement de deux transformations linéaires séparées par une activation non linéaire (souvent ReLU ou GELU).

Implémenattion avec PyTorch

Voici une approche simplifiée pour construire les briques fondamentales d'un Transformer.

1. Mécanisme d'Attention Multi-Têtes

import torch
import torch.nn as nn
import math

class AttentionMultiTetes(nn.Module):
   def __init__(self, d_modele, nb_tetes):
       super(AttentionMultiTetes, self).__init__()
       self.d_modele = d_modele
       self.nb_tetes = nb_tetes
       self.d_tete = d_modele // nb_tetes

       assert self.d_tete * nb_tetes == d_modele, "La dimension doit être divisible par le nombre de têtes"

       self.projection_q = nn.Linear(d_modele, d_modele)
       self.projection_k = nn.Linear(d_modele, d_modele)
       self.projection_v = nn.Linear(d_modele, d_modele)
       self.sortie_lineaire = nn.Linear(d_modele, d_modele)

   def forward(self, q, k, v, masque=None):
       taille_batch = q.size(0)

       # Transformation et division en têtes
       q = self.projection_q(q).view(taille_batch, -1, self.nb_tetes, self.d_tete).transpose(1, 2)
       k = self.projection_k(k).view(taille_batch, -1, self.nb_tetes, self.d_tete).transpose(1, 2)
       v = self.projection_v(v).view(taille_batch, -1, self.nb_tetes, self.d_tete).transpose(1, 2)

       # Calcul du produit scalaire entrelacé
       scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.d_tete)

       if masque is not None:
           scores = scores.masked_fill(masque == 0, -1e9)

       poids_attention = torch.softmax(scores, dim=-1)
       contexte = torch.matmul(poids_attention, v)

       # Recombinaison des têtes
       contexte = contexte.transpose(1, 2).contiguous().view(taille_batch, -1, self.d_modele)
       return self.sortie_lineaire(contexte)

2. Couche d'Encodage

class BlocEncodeur(nn.Module):
   def __init__(self, d_modele, nb_tetes, d_ff, dropout=0.1):
       super(BlocEncodeur, self).__init__()
       self.attention = AttentionMultiTetes(d_modele, nb_tetes)
       self.norm1 = nn.LayerNorm(d_modele)
       self.norm2 = nn.LayerNorm(d_modele)
       
       self.feed_forward = nn.Sequential(
           nn.Linear(d_modele, d_ff),
           nn.ReLU(),
           nn.Linear(d_ff, d_modele)
       )
       self.dropout = nn.Dropout(dropout)

   def forward(self, x, masque):
       # Sous-couche d'attention avec connexion résiduelle
       attn_res = self.attention(x, x, x, masque)
       x = self.norm1(x + self.dropout(attn_res))
       
       # Sous-couche Feed-Forward avec connexion résiduelle
       ff_res = self.feed_forward(x)
       x = self.norm2(x + self.dropout(ff_res))
       return x

Évolutions et cas d'usage

L'efficacité du Transformer repose sur sa capacité de parallélisation massive, contrairement aux RNN qui sont intrinsèquement séquentiels. Cette propriété a permis l'entraînement de modèles géants sur des corpus de données colossaux.

Aujourd'hui, cette architecture ne se limite plus au texte. Elle est déclinée dans le domaine de la vision par ordinateur (Vision Transformers - ViT), de la reconnaissance vocale et même de la génération de protéines. Des modèles célèbres comme GPT (basé sur le décodeur) et BERT (basé sur l'encodeur) illustrent la polyvalence de ce paradigme.

Étiquettes: transformer PyTorch attention-mechanism deep-learning nlp

Publié le 17 juillet à 22h35