Work in progress

Contenu rédigé à partir des diapositives coursTransformers.pdf, faute de source préexistante pour cette séance. Susceptible d’être ajusté.

Les informations pratiques concernant le déroulement de l’unité d’enseignement RCP209 « Apprentissage, réseaux de neurones et modèles graphiques » au Cnam se trouvent dans ce préambule.

XIII.1 - Cours#

[Diapositives du cours]

Pourquoi le Transformer ?#

Dans le chapitre précédent, nous avons vu que les réseaux récurrents permettent de traiter des séquences de longueur arbitraire, au prix d’un calcul intrinsèquement séquentiel : l’état interne \(\mathbf h_t\) ne peut être calculé qu’après \(\mathbf h_{t-1}\), ce qui empêche toute parallélisation le long de l’axe temporel et rend l’entraînement lent sur de longues séquences.

L’architecture Transformer (Vaswani et al., 2017) répond à ce problème en abandonnant totalement la récurrence, au profit d’un mécanisme d”attention : chaque position de la séquence est mise à jour simultanément en fonction de toutes les autres positions, ce qui se ramène à des produits matriciels, parfaitement parallélisables sur GPU. Contrairement à un RNN, dont le champ réceptif à un pas de temps donné ne s’étend que progressivement (au fil des mises à jour successives de l’état interne), l’attention offre dès la première couche un accès direct de chaque position à toutes les autres.

L’architecture Transformer ne fait aucune hypothèse sur la nature des éléments de la séquence : elle a initialement été conçue pour le traitement du langage (traduction automatique), mais s’applique tout aussi bien à des séries temporelles ou, comme nous le verrons en fin de séance, à des images. C’est cette généralité que nous exploiterons dans le TP de cette séance, exclusivement sur des séries temporelles.

Attention#

Intuition#

L’attention permet de construire une représentation contextuelle de chaque élément d’une séquence en intégrant, de façon sélective, l’information des éléments voisins : chaque élément « regarde » les autres avec un poids différent, plus ou moins important selon leur pertinence.

Requêtes, clés et valeurs#

Pour chaque élément \(x_i\) de la séquence, on définit trois vecteurs, obtenus par projection linéaire de son plongement (embedding) :

  • une query (requête) \(q_i\), qui encode ce que cet élément cherche comme information,

  • une key (clé) \(k_i\), qui encode ce que cet élément a à offrir aux autres,

  • une value (valeur) \(v_i\), le contenu qui sera effectivement agrégé.

Formellement, si \(x_i \in \mathbb R^d\) est le plongement de l’élément \(i\), ces trois vecteurs s’obtiennent par trois matrices apprises \(\mathbf W^Q \in \mathbb R^{d \times d_q}\), \(\mathbf W^K \in \mathbb R^{d \times d_k}\) et \(\mathbf W^V \in \mathbb R^{d \times d_v}\) :

\[q_i = x_i \mathbf W^Q~, \qquad k_i = x_i \mathbf W^K~, \qquad v_i = x_i \mathbf W^V~.\]

Le calcul suivant (produit scalaire entre requête et clé) impose \(d_q = d_k\) ; en pratique, on choisit le plus souvent \(d_q = d_k = d_v\).

Calcul de l’attention#

Étant donné la requête \(q_i\) d’un élément \(i\), on calcule son attention sur chaque élément \(j\) (clé, valeur) de la séquence en trois temps :

  1. calculer un score de similarité entre \(q_i\) et chaque clé \(k_j\), par produit scalaire normalisé par \(\sqrt{d_k}\) (pour éviter que les scores ne deviennent trop grands en valeur absolue et ne saturent le softmax) :

    \[\mathrm{score}(x_i, x_j) = \frac{q_i \cdot k_j}{\sqrt{d_k}}~;\]
  2. normaliser ces scores par un softmax le long de \(j\), pour obtenir les poids d’attention \(\alpha_{ij}\) :

    \[\alpha_{ij} = \mathrm{softmax}_j\big(\mathrm{score}(x_i, x_j)\big)~;\]
  3. agréger les valeurs \(v_j\) pondérées par ces poids, pour obtenir la sortie de l’attention pour l’élément \(i\) :

    \[z_i = \sum_j \alpha_{ij} v_j~.\]

Calcul matriciel#

En pratique, on empile les vecteurs \(q_i\), \(k_i\), \(v_i\) de tous les éléments de la séquence dans des matrices \(\mathbf Q\), \(\mathbf K\), \(\mathbf V\) (une ligne par élément), ce qui permet de calculer l’attention de tous les éléments simultanément :

\[\mathrm{Attention}(\mathbf Q, \mathbf K, \mathbf V) = \mathrm{softmax}\left(\frac{\mathbf Q \mathbf K^T}{\sqrt{d_k}}\right) \mathbf V~.\]

La matrice \(\mathbf Q \mathbf K^T\), de dimensions \((L, L)\) pour une séquence de longueur \(L\), contient pour chaque paire d’éléments \((i,j)\) leur score de similarité ; après softmax le long de chaque ligne, la ligne \(i\) devient la distribution de poids d’attention \(\alpha_{ij}\) de l’élément \(i\) sur tous les éléments \(j\).

Attention multi-tête#

En pratique, on n’utilise pas une seule projection \((\mathbf W^Q, \mathbf W^K, \mathbf W^V)\) mais plusieurs en parallèle : c’est l”attention multi-tête (multi-head attention). Chaque tête dispose de ses propres matrices de projection et peut ainsi apprendre à se concentrer sur des aspects différents de la séquence (par exemple, une tête pourrait apprendre des relations syntaxiques locales, une autre des dépendances à longue portée). Les sorties \(\mathbf Z_1, \dots, \mathbf Z_h\) des \(h\) têtes sont concaténées puis reprojetées par une dernière matrice apprise \(\mathbf W^O\) pour produire la sortie finale de la couche.

Encodage positionnel#

Contrairement à une couche récurrente ou convolutive, l’attention ne fait aucune différence entre un élément et un autre : permuter l’ordre des éléments en entrée donnerait exactement la même sortie (à la permutation près). Pourtant, l’ordre est évidemment important pour une séquence (temporelle ou textuelle). Le Transformer original ajoute donc à chaque vecteur d’entrée un encodage positionnel (positional encoding), un vecteur qui ne dépend que de la position \(i\) dans la séquence, construit à partir de fonctions sinus et cosinus de fréquences différentes :

\[\begin{split}p_i = \begin{pmatrix} \sin(i / 10000^{2\cdot 1/d}) \\ \cos(i / 10000^{2\cdot 1/d}) \\ \vdots \\ \sin(i / 10000^{2 \cdot (d/2)/d}) \\ \cos(i / 10000^{2 \cdot (d/2)/d}) \end{pmatrix}~.\end{split}\]

En pratique, il est souvent aussi simple, et souvent préférable, d”apprendre directement un vecteur par position (un embedding de position), au même titre que les autres paramètres du réseau, par descente de gradient – c’est cette seconde solution que nous utiliserons dans le TP de cette séance.

Architecture Transformer#

Bloc Transformer#

Un bloc Transformer (encodeur) complet combine l’attention multi-tête avec deux ingrédients supplémentaires, déjà rencontrés dans les architectures modernes de réseaux profonds :

  • des connexions résiduelles (residual connections), qui ajoutent l’entrée d’un sous-module à sa sortie :

    \[\text{sortie} = \text{sous-module}(x) + x~;\]
  • une normalisation par couche (layer normalization), qui normalise les activations en sortie de chaque sous-module :

    \[y = \frac{x - \mathbb E[x]}{\sqrt{\mathrm{Var}(x) + \epsilon}} * \gamma + \beta~,\]

    avec \(\gamma\) et \(\beta\) deux paramètres appris (à ne pas confondre avec la batch normalization, qui normalise le long du batch : la layer normalization normalise chaque exemple indépendamment, ce qui la rend utilisable même pour des séquences de longueur variable).

Un bloc encodeur applique ainsi, dans l’ordre : attention multi-tête en self-attention (c’est-à-dire que la même séquence sert de query, key et value) puis connexion résiduelle et layer normalization ; puis un petit réseau feed-forward, appliqué indépendamment à chaque position, suivi à nouveau d’une connexion résiduelle et d’une layer normalization.

Encodeur-décodeur#

L’architecture Transformer originale suit un schéma encodeur-décodeur :

  • l”encodeur empile plusieurs blocs Transformer identiques (self-attention) pour transformer la séquence d’entrée en une représentation contextuelle ;

  • le décodeur empile des blocs similaires, mais avec une self-attention masquée (chaque position ne peut attendre que les positions précédentes, pour respecter la causalité lors de la génération autorégressive de la sortie) suivie d’une attention croisée (cross-attention) sur la sortie de l’encodeur (les queries proviennent du décodeur, les keys et values de l’encodeur).

Il n’y a donc, à aucun moment, de calcul récurrent : l’ensemble du modèle ne repose que sur des couches d’attention et des couches entièrement connectées, ce qui permet de paralléliser massivement le calcul, aussi bien à l’entraînement qu’à l’inférence (hormis la génération autorégressive du décodeur, intrinsèquement séquentielle).

Ce que fait ce TP

Le TP de cette séance travaille sur une tâche de régression (prédiction d’une valeur future à partir d’un historique), qui ne nécessite qu’un encodeur Transformer : pas de décodeur, pas de self-attention masquée, pas d’attention croisée.

Vision Transformer (ViT)#

L’architecture Transformer, initialement conçue pour le texte, a également été appliquée avec succès aux images (Dosovitskiy et al., 2021), sous le nom de Vision Transformer (ViT). L’idée est de traiter une image comme une séquence : on la découpe en une grille de patchs (par exemple, une image \(224\times224\times3\) en patchs de \(16\times16\times3\)), chaque patch étant aplati puis projeté linéairement pour produire un vecteur, comme le serait le plongement d’un mot. Un encodage positionnel (2D) est ajouté, puis la séquence de patchs est traitée par un encodeur Transformer classique, en tout point identique à celui décrit plus haut.

Cette généralité radicale – la même architecture pour du texte, des séries temporelles ou des images – a un coût : contrairement à un CNN, dont le a priori de structure spatiale (champ réceptif local, équivariance à la translation) facilite l’apprentissage sur des jeux de données de taille modeste, le ViT n’a a priori aucune connaissance de la structure de l’image (chaque patch peut attendre n’importe quel autre patch dès la première couche). Ce biais inductif plus faible rend le ViT plus flexible, mais nécessite en général un pré-entraînement sur de très grands jeux de données pour être compétitif avec les architectures convolutives.

CNN

ViT

Champ réceptif local

Champ réceptif global

Fort a priori structurel (équivariance à la translation)

Faible a priori structurel

Apprend une hiérarchie avec la profondeur

Apprend les relations par attention

Efficace sur des jeux de données modestes

Nécessite un pré-entraînement à grande échelle