Etape 1 : Construire le transformer

Au niveau des données, nous allons prendre des fichiers txt brut assez volumineux, qu’il faudra traiter, tokenizer etc…

Pour construire le modèle, il faut commencer par déterminer les hyperparamètres, tels que la taille du vocabulaire (vocab_size)(ensemble des mots et symboles uniques présent dans l’ensemble des données), la taille de l’embedding (d_model)(le vecteur de nombres réels issus de la transformations des données complexe), le nombre de têtes d’attention (nhead)(pour la pertinence de la séquence de données), le nombre de couches du modèle (num_layers) (les couches de neurones hyperconnectés), et le taux de dropout (dropout)(régularisation du modèle pour éviter l’overfitting ou le sur-apprentissage).

Ensuite, on créé une instance de la classe TransformerModel en passant ces paramètres comme arguments. Une fois le modèle construit, on passera à l’entrainement.

La classe TransformerModel hérite de la classe nn.Module de PyTorch pour créer un modèle de Transformer pour la classification de texte.

Le constructeur de la classe TransformerModel prend en entrée les paramètres suivants :

vocab_size : la taille du vocabulaire utilisé dans l’embedding
d_model : la dimension de l’espace d’embedding
nhead : le nombre de têtes d’attention
num_layers : le nombre de couches d’encodeurs de Transformer
dropout : le taux de dropout utilisé

Dans la fonction init, les étapes suivantes sont effectuées :

super(TransformerModel, self).init() appelle le constructeur de la classe mère nn.Module
self.model_type = ‘Transformer’ définit le type de modèle comme « Transformer »
self.src_mask = None initialise la variable self.src_mask à None, elle sera utilisée plus tard dans la fonction forward
self.pos_encoder = PositionalEncoding(d_model, dropout) crée une instance de la classe PositionalEncoding et la stocke dans self.pos_encoder

encoder_layers = TransformerEncoderLayer(d_model, nhead, dim_feedforward=2048, dropout=dropout, activation=’relu’) crée une instance de la classe TransformerEncoderLayer avec les paramètres d_model, nhead, dim_feedforward, dropout et activation.

Cette classe représente une seule couche d’un encodeur de Transformer. La variable encoder_layers stocke ensuite cette instance.
self.transformer_encoder = TransformerEncoder(encoder_layers, num_layers) crée une instance de la classe TransformerEncoder avec les paramètres encoder_layers et num_layers. Cette classe représente l’ensemble des couches d’un encodeur de Transformer. La variable self.transformer_encoder stocke ensuite cette instance.
self.encoder = nn.Embedding(vocab_size, d_model) crée une instance de la classe nn.Embedding avec les paramètres vocab_size et d_model. Cette classe est utilisée pour créer une table de lookup pour les embeddings de chaque token. La variable self.encoder stocke ensuite cette instance.
self.d_model = d_model définit la variable self.d_model à d_model.
self.decoder = nn.Linear(d_model, vocab_size) crée une instance de la classe nn.Linear avec les paramètres d_model et vocab_size. Cette classe est utilisée pour projeter la sortie du modèle dans un espace de dimension égale à vocab_size, qui correspond au nombre de classes de la tâche de classification de texte. La variable self.decoder stocke ensuite cette instance.
En appelant self.init_weights(), nous nous assurons que les poids et les biais sont initialisés correctement lors de la création d’une instance de la classe TransformerModel.

La fonction init_weights est appelée lors de l’initialisation des poids des couches du modèle.

La première instruction dans cette fonction initrange = 0.1 initialise la variable initrange à 0.1, qui sera utilisée pour initialiser les poids des couches.

La ligne suivante self.encoder.weight.data.uniform_(-initrange, initrange) initialise les poids de la couche d’embedding en utilisant une distribution uniforme entre -initrange et initrange. La variable self.encoder.weight représente la matrice de poids de la couche d’embedding. L’attribut .data est utilisé pour accéder aux données sous-jacentes de la matrice et .uniform_() est utilisé pour initialiser ces données avec une distribution uniforme.

La ligne suivante self.decoder.bias.data.zero_() initialise les biais de la couche de projection à zéro. La variable self.decoder.bias représente le vecteur de biais de la couche de projection. L’attribut .data est utilisé pour accéder aux données sous-jacentes du vecteur et .zero_() est utilisé pour initialiser ces données à zéro.

Enfin, la ligne self.decoder.weight.data.uniform_(-initrange, initrange) initialise les poids de la couche de projection en utilisant une distribution uniforme entre -initrange et initrange. La variable self.decoder.weight représente la matrice de poids de la couche de projection. L’attribut .data est utilisé pour accéder aux données sous-jacentes de la matrice et .uniform_() est utilisé pour initialiser ces données avec une distribution uniforme.

En résumé, la fonction init_weights est utilisée pour initialiser les poids des couches du modèle avec une distribution uniforme et les biais avec des zéros. Cela permet d’initialiser le modèle de manière aléatoire et de faciliter l’apprentissage lors de l’entraînement.

La méthode principale de la classe est la fonction forward, qui prend en entrée un tenseur src de forme (seq_len, batch_size) qui contient les tokens d’une séquence de texte.

Si self.src_mask est None ou si sa taille ne correspond pas à la longueur de la séquence src, une matrice de masquage pour les séquences de texte est créée en utilisant self._generate_square_subsequent_mask(len(src)). Cette matrice de masquage est stockée dans self.src_mask.
src est passé à l’instance de la classe nn.Embedding stockée dans self.encoder pour obtenir les embeddings. Les embeddings sont multipliés par la racine carrée de la dimension d’embedding self.d_model et passent ensuite à travers l’instance de la classe PositionalEncoding stockée dans self.pos_encoder. Cette étape ajoute des informations de position aux embeddings en utilisant une formule mathématique spécifique.
src est passé à travers l’instance de la classe TransformerEncoder stockée dans self.transformer_encoder. Cette étape effectue plusieurs itérations de l’encodeur de Transformer pour obtenir une représentation globale de la séquence.
La sortie de l’encodeur de Transformer est passée à l’instance de la classe nn.Linear stockée dans self.decoder. Cette étape projette la sortie du modèle dans un espace de dimension égale à vocab_size, qui correspond au nombre de classes de la tâche de classification de texte.
La sortie finale du modèle est renvoyée.

La fonction _generate_square_subsequent_mask est utilisée pour créer une matrice de masquage carrée pour les séquences de texte.

La première ligne de la fonction crée une matrice carrée de taille sz x sz contenant des 1 sur la diagonale et des 0 en dessous en utilisant torch.triu(torch.ones(sz, sz)). La fonction triu renvoie la partie triangulaire supérieure d’une matrice et la fonction ones(sz, sz) crée une matrice de sz x sz contenant des 1. En combinant les deux, nous obtenons une matrice contenant des 1 sur la diagonale et des 0 en dessous.

La deuxième ligne convertit la matrice en float en utilisant .float(). Elle utilise ensuite la méthode .masked_fill pour remplacer les 0 de la matrice par -inf et les 1 par 0.0. Cette étape est nécessaire pour empêcher l’attention de regarder en avant. En effet, les 0 de la matrice indiquent les positions qui ne doivent pas être utilisées par l’attention, tandis que les -inf permettent de masquer ces positions.

Et, la troisième ligne renvoie la matrice de masquage.

En gros, la fonction _generate_square_subsequent_mask est utilisée pour créer une matrice de masquage carrée pour les séquences de texte en empêchant l’attention de regarder en avant(attention mechanism to look ahead). Cette fonction est utilisée dans la méthode forward pour initialiser la variable self.src_mask.

Donc voici la fonction personnalisé de notre transformer maintenant allons l’entrainer !!