L’auto-attention causale est le mécanisme à l’origine de la plupart des avancées en IA depuis 2017. Dans cet article, je vais détailler le calcul étape par étape pour, je l’espère, acquérir une meilleure intuition de son fonctionnement.
À un haut niveau, cette fonction prend une séquence et la transforme en une autre. Une séquence est une liste d’embeddings de tokens, un tenseur de forme , où est la longueur de la séquence d’entrée et la dimension de l’embedding. Chaque ligne de cette matrice correspond à un token d’entrée, représenté par un vecteur de dimension .
Mais alors, pourquoi y a-t-il 3 entrées à ? C’est parce que, dans l’architecture Transformer, la séquence d’entrée est projetée par 3 couches linéaires différentes. Si est la séquence d’entrée,
où sont de taille . Ainsi, sont simplement des représentations différentes de la même séquence d’entrée.
Calculons étape par étape. D’abord, nous effectuons , qui est un produit scalaire par , donnant un résultat . Que fait cette opération ?
Le résultat de est un scalaire ( point ), et c’est le produit scalaire vectoriel entre et . Si l’on se souvient de la formule
on voit que le produit scalaire est positif lorsque , l’angle entre et , est proche de 0º, et négatif lorsque l’angle est de 180º, c’est-à-dire lorsqu’ils pointent dans des directions opposées. On peut interpréter le produit scalaire comme une mesure de similarité, où les valeurs positives indiquent des vecteurs similaires et les valeurs négatives indiquent l’opposé.
Ainsi, notre matrice finale est remplie de scores de similarité entre chaque paire de tokens et . Le résultat est divisé par pour éviter que la variance n’explose pour de grandes dimensions d’embedding. Voir l’annexe pour plus de détails.
L’étape suivante consiste à appliquer la fonction , qui définit toutes les valeurs qui ne sont pas dans la section triangulaire inférieure de la matrice d’entrée à .
À cela, nous appliquons , qui convertit chaque ligne de valeurs de la matrice en une distribution de probabilité. La fonction est définie comme une application de , où le ème élément de sortie est donné par
Deux choses à noter ici :
- La somme de tous les éléments de sortie est , comme attendu pour une distribution de probabilité.
- Si un élément d’entrée est , alors .
Après avoir appliqué la fonction aux scores de similarité masqués, nous obtenons :
Où les entrées sont définies comme :
La matrice résultante a des lignes qui sont des distributions de probabilité de longueur . La dernière étape consiste à mapper notre matrice de valeurs par ces distributions de probabilité pour obtenir notre nouvelle séquence.
Notez que est un scalaire et est un vecteur d’embedding . Visuellement, on observe que SelfAttention combine sélectivement les tokens de Valeur, pondérés par une distribution de probabilité générée par la façon dont les requêtes et les clés s’attendent mutuellement, c’est-à-dire ont un grand produit scalaire. On voit aussi que le poids d’un token de sortie à l’indice ne dépend que des tokens d’entrée d’indice , en raison du masque causal appliqué précédemment. Ceci est basé sur l’hypothèse causale, qu’un token de sortie ne dépend pas des tokens futurs, ce qui est requis lors de l’entraînement de modèles autorégressifs (c’est-à-dire de prédiction du token suivant).
J’espère que vous avez trouvé cela utile !
Annexe
Pourquoi diviser par ?
Nous faisons cela pour éviter que la variance n’explose lorsque augmente.
Supposons que et soient i.i.d. Calculons l’espérance et la variance du produit scalaire non normalisé .
L’espérance est trivialement nulle :
Et la variance est :
car
ce qui vaut pour (puisque et sont i.i.d). Pour ,
puisque .
Ainsi, si nous divisons par , notre nouvelle variance est
comme souhaité.
Attention Multi-Têtes
La plupart des systèmes modernes utilisent l’attention multi-têtes, qui calcule en parallèle sur plusieurs “têtes”. On pose généralement , où est le nombre de têtes.