Tutoriel : Comment former un modèle linguistique RoBERTa pour l'espagnol

SpanBERTa : Comment nous avons formé le modèle linguistique RoBERTa pour l'espagnol à partir de zéro

    Originally published by Skim AI's Machine Learning Research Intern, Chris Tran.



spanberta_pretraining_bert_from_scratch





Exécuter dans Google Colab

Introduction

Les méthodes d'auto-apprentissage avec des modèles de transformateurs ont atteint des performances de pointe pour la plupart des tâches de TAL. Cependant, comme leur entraînement est coûteux en termes de calcul, la plupart des modèles de transformateurs pré-entraînés actuellement disponibles ne concernent que l'anglais. C'est pourquoi, afin d'améliorer les performances des tâches de TAL dans le cadre de nos projets sur l'espagnol, mon équipe à la Skim AI a décidé de former un RoBERTa pour l'espagnol à partir de zéro et l'appeler SpanBERTa.

SpanBERTa a la même taille que RoBERTa-base. Nous avons suivi le schéma d'entraînement de RoBERTa pour entraîner le modèle sur 18 Go de OSCARen 8 jours en utilisant 4 GPU Tesla P100.

Dans cet article de blog, nous allons parcourir un processus de bout en bout pour former un modèle de langage de type BERT à partir de zéro en utilisant transformateurs et les tokenizers par Hugging Face. Il existe également un notebook Google Colab pour exécuter directement les codes de cet article. Vous pouvez également modifier le notebook en conséquence pour entraîner un modèle de type BERT pour d'autres langues ou l'affiner sur votre ensemble de données personnalisé.

Avant de poursuivre, je tiens à remercier chaleureusement l'équipe de Hugging Face pour avoir mis à la portée de tous des modèles de PNL à la pointe de la technologie.

Mise en place

1. Installer les dépendances

Dans [0] :

%%capture
!pip uninstall -y tensorflow
!pip install transformers==2.8.0

2. Données

Nous avons pré-entraîné SpanBERTa sur OSCARen espagnol. La taille totale de l'ensemble de données est de 150 Go et nous avons utilisé une partie de 18 Go pour l'entraînement.

Dans cet exemple, pour des raisons de simplicité, nous utiliserons un ensemble de données de sous-titres de films espagnols provenant de OpenSubtitles. Cet ensemble de données a une taille de 5,4 Go et nous nous entraînerons sur un sous-ensemble de ~300 Mo.
Dans [0] :

import os
# Download and unzip movie substitle dataset
if not os.path.exists('data/dataset.txt'):
  !wget "https://object.pouta.csc.fi/OPUS-OpenSubtitles/v2016/mono/es.txt.gz" -O dataset.txt.gz
  !gzip -d dataset.txt.gz
  !mkdir data
  !mv dataset.txt data
--2020-04-06 15:53:04-- https://object.pouta.csc.fi/OPUS-OpenSubtitles/v2016/mono/es.txt.gz
Résolution de object.pouta.csc.fi (object.pouta.csc.fi)... 86.50.254.18, 86.50.254.19
Connexion à object.pouta.csc.fi (object.pouta.csc.fi)|86.50.254.18|:443... connecté.
Requête HTTP envoyée, réponse attendue... 200 OK
Longueur : 1859673728 (1.7G) [application/gzip]
Sauvegarde dans : 'dataset.txt.gz'

dataset.txt.gz 100%[===================>] 1.73G 17.0MB/s en 1m 46s

2020-04-06 15:54:51 (16.8 MB/s) - 'dataset.txt.gz' sauvegardé [1859673728/1859673728]

Dans [0] :

# Total number of lines and some random lines
!wc -l data/dataset.txt
!shuf -n 5 data/dataset.txt
179287150 data/dataset.txt
Sabes, pensé que tenías más pelotas que para enfrentarme a través de mi hermano.
Les enfants de l'école sont des enfants de la rue, des enfants de la rue, des enfants de la rue, des enfants de la rue, des enfants de la rue, des enfants de la rue, des enfants de la rue, des enfants de la rue, des enfants de la rue et des enfants de la rue.
Précédemment dans Blue Bloods :
Y quiero que prometas que no habrá ningún trato con Daniel Stafford.
Fue comiquísimo.

Dans [0] :

# Get a subset of first 1,000,000 lines for training
TRAIN_SIZE = 1000000 #@param {type:"integer"}
!(head -n $TRAIN_SIZE data/dataset.txt) > data/train.txt

Dans [0] :

# Get a subset of next 10,000 lines for validation
VAL_SIZE = 10000 #@param {type:"integer"}
!(sed -n {TRAIN_SIZE + 1},{TRAIN_SIZE + VAL_SIZE}p data/dataset.txt) > data/dev.txt

3. Former un tokeniseur

L'implémentation originale de l'ORET utilise un tokenizer WordPiece avec un vocabulaire de 32K unités de sous-mots. Cette méthode peut toutefois introduire des jetons "inconnus" lors du traitement de mots rares.

Dans cette implémentation, nous utilisons un tokenizer BPE au niveau de l'octet avec un vocabulaire de 50 265 unités de sous-mots (le même que RoBERTa-base). L'utilisation de BPE au niveau de l'octet permet d'apprendre un vocabulaire de sous-mots de taille modeste qui peut encoder n'importe quelle entrée sans obtenir de jetons "inconnus".

Parce que ByteLevelBPETokenizer produit 2 fichiers ["vocab.json", "merges.txt"] alors que BertWordPieceTokenizer ne produit qu'un seul fichier vocab.txtil y aura une erreur si nous utilisons BertWordPieceTokenizer pour charger les sorties d'un tokenizer BPE.
Dans [0] :

%%time
from tokenizers import ByteLevelBPETokenizer
path = "data/train.txt"
# Initialize a tokenizer
tokenizer = ByteLevelBPETokenizer()
# Customize training
tokenizer.train(files=path,
                vocab_size=50265,
                min_frequency=2,
                special_tokens=["<s>", "<pad>", "</s>", "<unk>", "<mask>"])
# Save files to disk
!mkdir -p "models/roberta"
tokenizer.save("models/roberta")
Temps CPU : utilisateur 1min 37s, sys : 1.02 s, total : 1min 38s
Temps au mur : 1min 38s

Super rapide ! Il ne faut que 2 minutes pour s'entraîner sur 10 millions de lignes.

Modèle linguistique de formation à partir de zéro

1. Architecture du modèle

RoBERTa a exactement la même architecture que BERT. Les seules différences sont les suivantes :

  • RoBERTa utilise un tokenizer BPE au niveau de l'octet avec un vocabulaire de sous-mots plus important (50k contre 32k).
  • RoBERTa met en œuvre un masquage dynamique des mots et abandonne la tâche de prédiction de la phrase suivante.
  • Les hyperparamètres de formation de RoBERTa.

D'autres configurations d'architecture peuvent être trouvées dans la documentation (RoBERTa, BERT).
Dans [0] :

import json
config = {
    "architectures": [
        "RobertaForMaskedLM"
    ],
    "attention_probs_dropout_prob": 0.1,
    "hidden_act": "gelu",
    "hidden_dropout_prob": 0.1,
    "hidden_size": 768,
    "initializer_range": 0.02,
    "intermediate_size": 3072,
    "layer_norm_eps": 1e-05,
    "max_position_embeddings": 514,
    "model_type": "roberta",
    "num_attention_heads": 12,
    "num_hidden_layers": 12,
    "type_vocab_size": 1,
    "vocab_size": 50265
}
with open("models/roberta/config.json", 'w') as fp:
    json.dump(config, fp)
tokenizer_config = {"max_len": 512}
with open("models/roberta/tokenizer_config.json", 'w') as fp:
    json.dump(tokenizer_config, fp)

2. Hyperparamètres de formation

Hyperparam BERT-base RoBERTa-base
Longueur de la séquence 128, 512 512
Taille du lot 256 8K
Taux d'apprentissage maximal 1e-4 6e-4
Nombre maximal de pas 1M 500K
Étapes de l'échauffement 10K 24K
Diminution du poids 0.01 0.01
Adam $epsilon$ 1e-6 1e-6
Adam $beta_1$ 0.9 0.9
Adam $beta_2$ 0.999 0.98
Découpage des dégradés 0.0 0.0

Notez que la taille du lot lors de l'apprentissage de RoBERTa est de 8000. Par conséquent, bien que la base RoBERTa ait été entraînée pour 500 000 étapes, son coût de calcul est 16 fois supérieur à celui de la base BERT. Dans le Papier RoBERTaIl est démontré que l'entraînement avec de grands lots améliore la perplexité pour l'objectif de modélisation du langage masqué, ainsi que la précision à la fin de la tâche. Il est possible d'obtenir une taille de lot plus importante en modifiant les paramètres suivants gradient_accumulation_steps.

En raison des contraintes de calcul, nous avons suivi le schéma d'entraînement de BERT-base et entraîné notre modèle SpanBERTa en utilisant 4 GPU Tesla P100 pour 200K étapes en 8 jours.

3. Début de la formation

Nous allons former notre modèle à partir de zéro en utilisant run_language_modeling.pyun script fourni par Hugging Face, qui va prétraiter, tokeniser le corpus et entraîner le modèle sur Modélisation du langage masqué tâche. Le script est optimisé pour s'entraîner sur un seul grand corpus. Par conséquent, si votre ensemble de données est volumineux et que vous souhaitez le diviser pour l'entraîner de manière séquentielle, vous devrez modifier le script, ou être prêt à obtenir une machine monstre avec une mémoire élevée.
Dans [0] :

# Update April 22, 2020: Hugging Face updated run_language_modeling.py script.
# Please use this version before the update.
!wget -c https://raw.githubusercontent.com/chriskhanhtran/spanish-bert/master/run_language_modeling.py
--2020-04-24 02:28:21-- https://raw.githubusercontent.com/chriskhanhtran/spanish-bert/master/run_language_modeling.py
Résolution de raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...
Connexion à raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connecté.
Requête HTTP envoyée, en attente de réponse... 200 OK
Longueur : 34328 (34K) [text/plain]
Sauvegarde dans : 'run_language_modeling.py'

run_language_modeli 100%[===================>] 33.52K --.-KB/s en 0.003s

2020-04-24 02:28:21 (10.1 MB/s) - 'run_language_modeling.py' sauvegardé [34328/34328]

Arguments importants

  • --line_by_line Indique si les lignes de texte distinctes de l'ensemble de données doivent être traitées comme des séquences distinctes. Si chaque ligne de votre jeu de données est longue et contient ~512 tokens ou plus, vous devriez utiliser ce paramètre. Si chaque ligne est courte, le prétraitement de texte par défaut concatène toutes les lignes, les tokenise et découpe les sorties tokenisées en blocs de 512 tokens. Vous pouvez également diviser vos ensembles de données en petits morceaux et les prétraiter séparément. Le traitement de 3 Go de texte prendra environ 50 minutes avec le traitement par défaut. TextDataset classe.
  • --doit_continuer Continuer ou non à partir du dernier point de contrôle dans output_dir.
  • --nom_du_modèle_ou_chemin Le point de contrôle du modèle pour l'initialisation des poids. Laissez None si vous voulez former un modèle à partir de zéro.
  • --mlm S'entraîner avec une perte de modélisation de la langue masquée au lieu d'une modélisation de la langue.
  • --config_name, --tokenizer_name Configuration préformée optionnelle et nom du tokenizer ou chemin d'accès s'il n'est pas le même que model_name_ou_path. Si les deux sont None, une nouvelle configuration est initialisée.
  • --per_gpu_train_batch_size Taille du lot par GPU/CPU pour l'entraînement. Choisissez le plus grand nombre de lots que vous pouvez faire tenir sur vos GPU. Une erreur s'affiche si la taille du lot est trop importante.
  • --gradient_accumulation_steps Nombre d'étapes de mise à jour à accumuler avant d'effectuer une passe de retour en arrière/mise à jour. Vous pouvez utiliser cette astuce pour augmenter la taille des lots. Par exemple, si per_gpu_train_batch_size = 16 et gradient_accumulation_steps = 4la taille totale du lot de train sera de 64.
  • --overwrite_output_dir Remplacer le contenu du répertoire de sortie.
  • --no_cuda, --fp16, --fp16_opt_level Arguments en faveur de l'entraînement sur GPU/CPU.
  • Les autres arguments sont les chemins du modèle et les hyperparamètres d'apprentissage.

Il est fortement recommandé d'inclure le type de modèle (par exemple "roberta", "bert", "gpt2", etc.) dans le chemin d'accès au modèle car le script utilise la fonction AutoModels pour deviner la configuration du modèle à l'aide d'une recherche de motifs sur le chemin fourni.
Dans [0] :

# Model paths
MODEL_TYPE = "roberta" #@param ["roberta", "bert"]
MODEL_DIR = "models/roberta" #@param {type: "string"}
OUTPUT_DIR = "models/roberta/output" #@param {type: "string"}
TRAIN_PATH = "data/train.txt" #@param {type: "string"}
EVAL_PATH = "data/dev.txt" #@param {type: "string"}

Pour cet exemple, nous nous entraînerons pour 25 étapes seulement sur un GPU Tesla P4 fourni par Colab.
Dans [0] :

!nvidia-smi
Mon Apr 6 15:59:35 2020
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.64.00 Version du pilote : 418.67 CUDA Version : 10.1 |
|-------------------------------+----------------------+----------------------+
| Nom du GPU Persistance-M| Bus-Id Disp.A | Volatile Uncorr. ECC
| Temp. ventilateur Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. 
|===============================+======================+======================|
| 0 Tesla P4 Off | 00000000:00:04.0 Off | 0 |
| N/A 31C P8 7W / 75W | 0MiB / 7611MiB | 0% Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processus :                                                       Mémoire GPU
| GPU PID Type Nom du processus Utilisation
|=============================================================================|
| Aucun processus en cours n'a été trouvé.
+-----------------------------------------------------------------------------+

Dans [0] :

# Command line
cmd = """python run_language_modeling.py 
    --output_dir {output_dir} 
    --model_type {model_type} 
    --mlm 
    --config_name {config_name} 
    --tokenizer_name {tokenizer_name} 
    {line_by_line} 
    {should_continue} 
    {model_name_or_path} 
    --train_data_file {train_path} 
    --eval_data_file {eval_path} 
    --do_train 
    {do_eval} 
    {evaluate_during_training} 
    --overwrite_output_dir 
    --block_size 512 
    --max_step 25 
    --warmup_steps 10 
    --learning_rate 5e-5 
    --per_gpu_train_batch_size 4 
    --gradient_accumulation_steps 4 
    --weight_decay 0.01 
    --adam_epsilon 1e-6 
    --max_grad_norm 100.0 
    --save_total_limit 10 
    --save_steps 10 
    --logging_steps 2 
    --seed 42
"""

Dans [0] :

# Arguments for training from scratch. I turn off evaluate_during_training,
#   line_by_line, should_continue, and model_name_or_path.
train_params = {
    "output_dir": OUTPUT_DIR,
    "model_type": MODEL_TYPE,
    "config_name": MODEL_DIR,
    "tokenizer_name": MODEL_DIR,
    "train_path": TRAIN_PATH,
    "eval_path": EVAL_PATH,
    "do_eval": "--do_eval",
    "evaluate_during_training": "",
    "line_by_line": "",
    "should_continue": "",
    "model_name_or_path": "",
}

Si vous vous entraînez sur une machine virtuelle, vous pouvez installer tensorboard pour surveiller le processus d'entraînement. Voici notre Tensorboard pour la formation de SpanBERTa.

pip install tensorboard==2.1.0
tensorboard dev upload --logdir runs

Après 200 000 étapes, la perte a atteint 1,8 et la perplexité 5,2.

Maintenant, commençons l'entraînement !
Dans [ ] :

!{cmd.format(**train_params)}
    04/06/2020 15:59:55 - INFO - __main__ - Création de caractéristiques à partir du fichier data/roberta_cached_lm_510_train
    04/06/2020 16:04:43 - INFO - __main__ - Enregistrement des caractéristiques dans le fichier en cache data/roberta_cached_lm_510_train.txt
    04/06/2020 16:04:46 - INFO - __main__ - ***** Exécution de l'entraînement *****
    04/06/2020 16:04:46 - INFO - __main__ - Nombre d'exemples = 165994
    04/06/2020 16:04:46 - INFO - __main__ - Num Epochs = 1
    04/06/2020 16:04:46 - INFO - __main__ - Taille instantanée du lot par GPU = 4
    04/06/2020 16:04:46 - INFO - __main__ - Taille totale du lot de train (avec parallèle, distribué et accumulation) = 16
    04/06/2020 16:04:46 - INFO - __main__ - Pas d'accumulation de gradient = 4
    04/06/2020 16:04:46 - INFO - __main__ - Nombre total d'étapes d'optimisation = 25
    Epoque : 0% 0/1 [00:00< ??, ?it/s]
    Itération :   0% 0/41499 [00:00< ?, ?it/s]
    Itération :   0% 1/41499 [00:01<13:18:02, 1.15s/it]
    Itération :   0% 2/41499 [00:01<11:26:47, 1.01it/s]
    Itération :   0% 3/41499 [00:02<10:10:30, 1.13it/s]
    Itération :   0% 4/41499 [00:03<9:38:10, 1.20it/s]
    Itération :   0% 5/41499 [00:03<8:52:44, 1.30it/s]
    Itération :   0% 6/41499 [00:04<8:22:47, 1.38it/s]
    Itération :   0% 7/41499 [00:04<8:00:55, 1.44it/s]
    Itération :   0% 8/41499 [00:05<8:03:40, 1.43it/s]
    Itération :   0% 9/41499 [00:06<7:46:57, 1.48it/s]
    Itération :   0% 10/41499 [00:06<7:35:35, 1.52it/s]
    Itération :   0% 11/41499 [00:07<7:28:29, 1.54it/s]
    Itération :   0% 12/41499 [00:08<7:41:41, 1.50it/s]
    Itération :   0% 13/41499 [00:08<7:34:28, 1.52it/s]
    Itération :   0% 14/41499 [00:09<7:28:46, 1.54it/s]
    Itération :   0% 15/41499 [00:10<7:23:29, 1.56it/s]
    Itération :   0% 16/41499 [00:10<7:38:06, 1.51it/s]
    Itération :   0% 17/41499 [00:11<7:29:13, 1.54it/s]
    Itération :   0% 18/41499 [00:12<7:24:04, 1.56it/s]
    Itération :   0% 19/41499 [00:12<7:21:59, 1.56it/s]
    Itération :   0% 20/41499 [00:13<7:38:06, 1.51it/s]
    04/06/2020 16:06:23 - INFO - __main__ - ***** Exécution de l'évaluation *****
    04/06/2020 16:06:23 - INFO - __main__ - Nombre d'exemples = 156
    04/06/2020 16:06:23 - INFO - __main__ - Taille du lot = 4
    Évaluation : 100% 39/39 [00:08<00:00, 4.41it/s]
    04/06/2020 16:06:32 - INFO - __main__ - ***** Résultats de l'évaluation *****
    04/06/2020 16:06:32 - INFO - __main__ - perplexité = tenseur(6077.6812)

4. Prédire les mots masqués

Après avoir entraîné votre modèle linguistique, vous pouvez le télécharger et le partager avec la communauté. Nous avons téléchargé notre modèle SpanBERTa sur le serveur de Hugging Face. Avant d'évaluer le modèle sur des tâches en aval, voyons comment il a appris à remplir des mots masqués en fonction du contexte.
Dans [0] :

%%capture
%%time
from transformers import pipeline
fill_mask = pipeline(
    "fill-mask",
    model="chriskhanhtran/spanberta",
    tokenizer="chriskhanhtran/spanberta"
)

Je choisis une phrase de l'article de Wikipédia sur le COVID-19.

La phrase originale est "Laver fréquemment les mains avec de l'eau et du savon," signifiant "Se laver fréquemment les mains avec de l'eau et du savon."

Le mot masqué est "jabón" (savon) et les 5 premiers pronostics sont savon, sel, vapeur, citron et vinaigre. Il est intéressant de constater que le modèle apprend en quelque sorte qu'il faut se laver les mains avec des produits qui peuvent tuer les bactéries ou contenir de l'acide.
Dans [0] :

fill_mask("Lavarse frecuentemente las manos con agua y <mask>.")

Out[0] :

[{'score': 0.6469631195068359,
  'sequence' : 'Lavarse frecuentemente las manos con agua y jabón.&#39 ;,
  'token': 18493},
 {'score': 0.06074320897459984,
  'sequence' : 'Lavarse frecuentemente las manos con agua y sal.&#39 ;,
  'token' : 619},
 {'score': 0.029787985607981682,
  'sequence' : 'Lavarse frecuentemente las manos con agua y vapor.&#39 ;,
  'token': 11079},
 {'score': 0.026410052552819252,
  'sequence' : 'Lavarse frecuentemente las manos con agua y limón.&#39 ;,
  'token': 12788},
 {'score': 0.017029203474521637,
  'sequence' : 'Lavarse frecuentemente las manos con agua y vinagre.&#39 ;,
  'token': 18424}]

Conclusion

Nous avons vu comment entraîner un modèle de langue BERT pour l'espagnol à partir de zéro et constaté que le modèle a appris des propriétés de la langue en essayant de prédire des mots masqués en fonction d'un contexte. Vous pouvez également suivre cet article pour affiner un modèle BERT pré-entraîné sur votre ensemble de données personnalisé.

Ensuite, nous mettrons en œuvre les modèles pré-entraînés sur des tâches en aval, notamment la classification des séquences, le NER, l'étiquetage POS et le NLI, et nous comparerons les performances du modèle à celles de certains modèles non-BERT.

Restez à l'écoute de nos prochains articles !

Discutons de votre idée

    Articles connexes

    • Conception sans titre (23)

      Les grands modèles de langage (LLM) sont apparus comme la clé de la construction d'applications d'entreprise intelligentes. Cependant, l'exploitation de la puissance de ces modèles de langage nécessite une pile d'applications LLM robuste et efficace. Chez Skim AI, notre pile d'applications LLM nous permet de

      LLMs / NLP
    • Dessin sans titre (20)

      Les entreprises se tournent de plus en plus vers les grands modèles de langage (LLM), et celles qui ne le font pas sont en train de prendre du retard. Les API de LLM, telles que GPT d'OpenAI et Claude d'Anthropic, offrent aux entreprises des possibilités inégalées d'intégrer des capacités linguistiques avancées dans leurs systèmes et leurs applications.

      Non classé
    • notre stack llm

      Les grands modèles de langage (LLM) open-source sont devenus un outil puissant pour les entreprises en 2024. Ils offrent aux entreprises des possibilités sans précédent d'exploiter le potentiel du traitement du langage naturel basé sur l'IA, ce qui leur permet d'améliorer leurs opérations, d'améliorer l'expérience des clients et d'accroître leur productivité.

      LLMs / NLP

    Prêt à donner un coup de fouet à votre entreprise

    LAISSONS
    PARLER
    fr_FRFrançais