Compter les Paramètres de BERT

Boulier, BERT, et IA.
Boulier, BERT, et IA – Image générée par l’auteur et par IA.

Plus les modèles de langage sont larges, et plus chaque paramètre semble noyé dans une masse de laquelle on n’ose pas prendre la peine de le considérer dans son individualité.

GPT3 contient 175 milliards de paramètres, tandis que son « petit frère » GPT4 en contiendrait dix fois plus selon les rumeurs. Lorsque l’on contemple de telles quantités, on est tenté de se dire que cela ne vaut pas la peine de compter à l’unité près le nombre de poids du modèle. On peut bien se contenter d’un arrondi au milliard près.

Le macro et le micro

L’inconvénient de cette pensée est que l’on tend à ne plus s’intéresser au comportement des modèles de langage à l’échelle microscopique et que l’on reste à l’échelle macroscopique. On reste dans du haut niveau, dans une vue d’ensemble, qui ne s’incommode pas des détails, ni de la précision.

Or, pour comprendre méthodiquement le comportement des réseaux de neurones utilisés, il est indispensable de regarder chaque couche dans son individualité et de regarder ses cellules, une à une.

Ici, nous allons nous retrousser les manches l’histoire de quelques instants pour montrer que l’on peut dénombrer les paramètres d’un modèle de deep learning sans problème.

Nous allons compter ensemble les paramètres d’un modèle BERT (Bidirectional Encoder Representations from Transformers).

Charger un modèle BERT

Pour commencer, mais cette étape n’est pas nécessaire à reproduire, chargeons un modèle BERT avec Python.

from transformers import BertConfig, BertForMaskedLM

vocab_size= 30_000
max_length = 512
model_config = BertConfig(vocab_size=vocab_size, max_position_embeddings=max_length)
model = BertForMaskedLM(config=model_config)

Dans un notebook sur Colab, le code précédent devrait tourner sans problème et charger un modèle BERT de base pour faire du Masked-Language Modeling, qui est la tâche de modélisation du langage sur laquelle BERT a été particulièrement pré-entraîné.

Afficher le modèle

Ensuite, pour afficher le modèle, il suffit de l’appeler dans un interpréteur ou une cellule de code :

model

Ceci devrait afficher l’architecture du modèle ainsi :

Architecture du modèle
Architecture du modèle

Pour le lecteur non-averti, cela peut faire beaucoup de caractères et de lignes.

Cependant, cet affichage a beaucoup de valeur car il nous permet de voir, de l’entrée en haut jusqu’à la sortie en bas, la liste exhausive des couches du modèle.

Entre autres, les couches qui vont le plus nous intéresser sont celles qui contiennent des paramètres. Toutes les couches ne contiennent pas des paramètres ! Celles qui nous intéressent sont ici :

  • Embedding
  • Linear
  • LayerNorm

À chaque fois que l’on voit un de ces noms, il faudra compter des paramètres.

Les autres n’en ont pas.

Résumé du modèle

Avant de compter par nous-mêmes le nombre de paramètres de ce modèle BERT, utilisons un outil à disposition pour compter à notre place cette quantité et qui nous servira de référence.

Faisons d’abord un pip install torchinfo :

!pip install torchinfo

Puis, utilisons la fonction summary de torchinfo pour nous afficher un résumé du modèle.

from torchinfo import summary
summary(model)

On obtient alors la chose suivante :

Résumé du modèle BERT

La ligne qui nous intéresse est vers la toute fin : Total params, qui nous indique le nombre total de paramètres du modèle.

Ici, on nous indique 132,152,880 paramètres.

Regardons à présent si l’on peut retrouver cette valeur.

Dénombrement des paramètres

Maintenant que tout est fermement en place, on peut s’attaquer à la partie qui nous intéresse : compter « à la main » le nombre de paramètres de notre modèle.

L’objectif va être de retrouver la même valeur que celle indiquée précédemment.

1. Embedding

Commençons avec les couches Embedding.

Couches Embedding

Pour compter le nombre de paramètres de ces couches, il suffit de mutliplier le nombre d’entrées avec le nombre de sorties.

Imaginons que l’on ait une couche Embedding(n_entrees, n_sorties), alors le nombre de paramètres associés sera n_entrees * n_sorties.

Ainsi, le nombre de paramètres dans ces couches est :

n_param_embeddings = 30_000 * 768 + 512 * 768 + 2 * 768

Après calcul, cela fait déjà 23,434,752 paramètres, juste pour ces trois couches d’embeddings.

2. Linear

Dénombrons à présent les paramètres des couches Linear.

Encoder

Le gros des troupes se situe dans l’encodeur. On note qu’il y a 12 modules BertLayer qui ont tous la même architecture. Ainsi, pour compter le nombre de paramères de l’encodeur, on comptera les paramères dans chaque BertLayer et on multipliera à la fin par 12.

BertLayer

Dans une BertLayer, on note qu’il y a tout d’abord trois couches Linear.

Pour chacune de ces couches, le nombre de paramètres est donné par la multiplication du nombre d’entrées avec le nombre de sorties, à laquelle on rajoute une fois le nombre de sorties s’il y a un biais.

Ainsi, pour une couche Linear(in_features=n_entrees, out_features=n_sorties, bias=True), le nombre de paramètres sera n_entrees * n_sorties + n_sorties.

Pour le module self de attention, on a ainsi :

n_param_attention_self = (768 * 768 + 768) + (768 * 768 + 768) + (768 * 768 + 768)

Ce qui fait un petit total de 1,771,776 paramètres.

Pour le module output de attention, on a :

n_param_attention_output = 768 * 768 + 768

Ce qui donne 590,592 paramètres.

Pour les modules intermediate et output, si on les traite ensemble, cela nous donne :

n_param_intermediate_and_output = (768 * 3_072 + 3_072) + (3_072 * 768 + 768)

Ce qui donne alors 4,722,432 paramètres.

Sommons ces trois précédentes quantités que nous avons calculées et multiplions la somme par 12 (le nombre de BertLayer dans l’encodeur) :

n_param_encoder = 12 * (n_param_attention_self + n_param_attention_output + n_param_intermediate_and_output)

On obtient alors un total de 85,017,600 paramètres dans les couches linéaires de l’encodeur.

Il nous reste encore quelques paramètres de couches Linear à compter dans le module cls du modèle.

cls

Le calcul est simplement :

n_param_cls = (768 * 768 + 768) + (768 * 30_000 + 30_000)

Ce qui donne 23,660,592 paramètres supplémentaires.

Sommons à présent tous les paramètres des couches Linear :

n_param_linear = n_param_encoder + n_param_cls

On a, en tout, 108,678,192 paramètres dans ces couches.

3. LayerNorm

Finalement, il ne faut pas oublier que des couches de normalisation peuvent également présenter des paramètres.

Ainsi, les couches LayerNorm de ce modèle ont chacune deux paramètres pour chaque sortie qu’elles ont. En effet, la documentation de Pytorch indique la chose suivante :

Documentation PyTorch sur la LayerNorm

Alors, pour compter le nombre de paramètres liés aux couches LayerNorm, il suffit de reparcourir l’architecture du modèle et de compter deux fois le nombre de canaux qu’elles ont :

n_param_layernorm = 2 * (768 + 12 * (768 + 768) + 768)

On trouve alors seulement un appoint de 39,936 paramètres.

4. Passage à l’addition

Nous avons compté chaque paramètre du modèle. Il est l’heure de passer à l’addition et voir si le compte est bon.

n_param_total = n_param_embeddings + n_param_linear + n_param_layernorm

On trouve alors 132,152,880 paramètres !

C’est exactement ce que l’on avait au début, quel heureux hasard.

Laisser un commentaire