Les modèles
Dans cette section, nous allons examiner de plus près la création et l’utilisation d’un modèle. Nous utiliserons la classe AutoModel
qui est pratique lorsque vous voulez instancier n’importe quel modèle à partir d’un checkpoint.
La classe AutoModel
et toutes les classes apparentées sont en fait de simples wrappers sur la grande variété de modèles disponibles dans la bibliothèque. C’est une enveloppe intelligente car elle peut automatiquement deviner l’architecture appropriée pour votre checkpoint et ensuite instancier un modèle avec cette architecture.
Cependant, si vous connaissez le type de modèle que vous voulez utiliser, vous pouvez utiliser directement la classe qui définit son architecture. Voyons comment cela fonctionne avec un modèle BERT.
Création d’un <i> transformer </i>
La première chose que nous devons faire pour initialiser un modèle BERT est de charger un objet configuration :
from transformers import BertConfig, BertModel
# Construire la configuration
config = BertConfig()
# Construire le modèle à partir de la configuration
model = BertModel(config)
La configuration contient de nombreux attributs qui sont utilisés pour construire le modèle :
print(config)
BertConfig {
[...]
"hidden_size": 768,
"intermediate_size": 3072,
"max_position_embeddings": 512,
"num_attention_heads": 12,
"num_hidden_layers": 12,
[...]
}
Bien que vous n’ayez pas encore vu ce que font tous ces attributs, vous devriez en reconnaître certains : l’attribut hidden_size
définit la taille du vecteur hidden_states
, et num_hidden_layers
définit le nombre de couches que le transformer possède.
Différentes méthodes de chargement
Un modèle créé à partir de la configuration par défaut est initialisé avec des valeurs aléatoires :
from transformers import BertConfig, BertModel
config = BertConfig()
model = BertModel(config)
# Le modèle est initialisé de façon aléatoire !
Le modèle peut être utilisé tel quel mais il produira du charabia. En effet, il doit d’abord être entraîné. Nous pourrions entraîner le modèle à partir de zéro sur la tâche qui nous intéresse mais comme vous l’avez vu dans le chapitre 1 cela nécessiterait beaucoup de temps et de données. De plus cela aurait un impact environnemental non négligeable. Pour éviter les efforts inutiles et redondants, il est impératif de pouvoir partager et réutiliser les modèles qui ont déjà été entraînés.
Charger un transformer qui a déjà été entraîné est simple : nous pouvons le faire en utilisant la méthode from_pretrained()
:
from transformers import BertModel
model = BertModel.from_pretrained("bert-base-cased")
Comme vu précédemment, nous pouvons remplacer BertModel
par la classe équivalente AutoModel
. A partir de maintenant nous ferons cela car cela produit un code agnostique checkpoint, c’est-à-dire que si votre code fonctionne pour un checkpoint donné, il devrait fonctionner sans problème avec un autre. Cela s’applique même si l’architecture est différente du moment que le checkpoint a été entraîné pour une tâche similaire (par exemple, une tâche d’analyse de sentiments).
Dans l’exemple de code ci-dessus, nous n’avons pas utilisé BertConfig
et avons à la place chargé un modèle pré-entraîné via l’identifiant bert-base-cased
. Il s’agit d’un checkpoint qui a été entraîné par les auteurs de BERT eux-mêmes. Vous pouvez trouver davantage de détails à son sujet dans la fiche du modèle.
Ce modèle est maintenant initialisé avec tous les poids du checkpoint. Il peut être utilisé directement pour l’inférence sur les tâches sur lesquelles il a été entraîné. Il peut également être finetuné sur une nouvelle tâche. En entraînant avec des poids pré-entraînés plutôt qu’à partir de zéro, nous pouvons rapidement obtenir de bons résultats.
Les poids ont été téléchargés et mis en cache (afin que les futurs appels à la méthode from_pretrained()
ne les retéléchargent pas) dans le dossier cache, qui est par défaut ~/.cache/huggingface/transformers. Vous pouvez personnaliser votre dossier de cache en définissant la variable d’environnement HF_HOME
.
L’identifiant utilisé pour charger le modèle peut être l’identifiant de n’importe quel modèle sur le Model Hub du moment qu’il est compatible avec l’architecture BERT. La liste complète des checkpoints de BERT disponibles peut être trouvée ici.
Méthodes de sauvegarde
Sauvegarder un modèle est aussi facile que d’en charger un. Nous utilisons la méthode save_pretrained()
, qui est analogue à la méthode from_pretrained()
:
model.save_pretrained("directory_on_my_computer")
Cela enregistre deux fichiers sur votre disque :
ls directory_on_my_computer
config.json pytorch_model.bin
Si vous jetez un coup d’œil au fichier config.json, vous reconnaîtrez les attributs nécessaires pour construire l’architecture du modèle. Ce fichier contient également certaines métadonnées, comme l’origine du checkpoint et la version de la bibliothèque 🤗 Transformers que vous utilisiez lors du dernier enregistrement du point checkpoint.
Le fichier pytorch_model.bin est connu comme le dictionnaire d’état. Il contient tous les poids de votre modèle. Les deux fichiers vont de pair : la configuration est nécessaire pour connaître l’architecture de votre modèle, tandis que les poids du modèle sont les paramètres de votre modèle.
Utilisation d’un <i> transformer </i> pour l’inférence
Maintenant que vous savez comment charger et sauvegarder un modèle, essayons de l’utiliser pour faire quelques prédictions. Les transformers ne peuvent traiter que des nombres. Des nombres que le tokenizer génère. Mais avant de parler des tokenizers, explorons les entrées que le modèle accepte.
Les tokenizers se chargent de passer les entrées vers les tenseurs du framework approprié. Pour vous aider à comprendre ce qui se passe, jetons un coup d’œil rapide à ce qui doit être fait avant d’envoyer les entrées au modèle.
Disons que nous avons les séquences suivantes :
sequences = ["Hello!", "Cool.", "Nice!"]
Le tokenizer les convertit en indices de vocabulaire qui sont généralement appelés input IDs. Chaque séquence est maintenant une liste de nombres ! La sortie résultante est :
encoded_sequences = [
[101, 7592, 999, 102],
[101, 4658, 1012, 102],
[101, 3835, 999, 102],
]
Il s’agit d’une liste de séquences encodées : une liste de listes. Les tenseurs n’acceptent que des formes rectangulaires (pensez aux matrices). Ce « tableau » est déjà de forme rectangulaire, donc le convertir en tenseur est facile :
import torch
model_inputs = torch.tensor(encoded_sequences)
Utilisation des tenseurs comme entrées du modèle
L’utilisation des tenseurs avec le modèle est extrêmement simple, il suffit d’appeler le modèle avec les entrées :
output = model(model_inputs)
Bien que le modèle accepte un grand nombre d’arguments différents, seuls les identifiants d’entrée sont nécessaires. Nous expliquerons plus tard ce que font les autres arguments et quand ils sont nécessaires. Avant cela, regardons de plus près les tokenizers, cet outil qui construit les entrées qu’un transformer peut comprendre.