Tutorial: Wie man ein RoBERTa-Sprachmodell für Spanisch trainiert

SpanBERTa: Wie wir das RoBERTa-Sprachmodell für Spanisch von Grund auf neu trainiert haben

    

Ursprünglich veröffentlicht von Skim AI's Machine Learning Research Intern, Chris Tran.



spanberta_vorbildung_bert_von_der_kratz





In Google Colab ausführen

Einführung

Selbsttrainierende Methoden mit Transformatormodellen haben bei den meisten NLP-Aufgaben Spitzenleistungen erzielt. Da das Training dieser Modelle jedoch sehr rechenintensiv ist, sind die meisten derzeit verfügbaren vortrainierten Transformer-Modelle nur für Englisch verfügbar. Um die Leistung bei NLP-Aufgaben in unseren Projekten auf Spanisch zu verbessern, hat mein Team bei Skim AI beschlossen, eine RoBERTa Sprachmodell für Spanisch von Grund auf neu zu entwickeln und es SpanBERTa zu nennen.

SpanBERTa hat die gleiche Größe wie RoBERTa-base. Wir folgten dem Trainingsschema von RoBERTa und trainierten das Modell auf 18 GB von OSCARspanischen Korpus in 8 Tagen mit 4 Tesla P100 GPUs.

In diesem Blogbeitrag werden wir einen durchgängigen Prozess durchlaufen, um ein BERT-ähnliches Sprachmodell von Grund auf zu trainieren. Transformatoren und Tokenizer Bibliotheken von Hugging Face. Es gibt auch ein Google Colab-Notebook, mit dem Sie die Codes in diesem Artikel direkt ausführen können. Sie können das Notebook auch entsprechend anpassen, um ein BERT-ähnliches Modell für andere Sprachen zu trainieren oder eine Feinabstimmung für Ihren individuellen Datensatz vorzunehmen.

Bevor ich fortfahre, möchte ich dem Team von Hugging Face einen großen Dank dafür aussprechen, dass es modernste NLP-Modelle für jedermann zugänglich macht.

Einrichtung

1. Abhängigkeiten installieren

In [0]:

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

2. Daten

Wir haben SpanBERTa vortrainiert auf OSCARdem spanischen Korpus. Die Gesamtgröße des Datensatzes beträgt 150 GB und wir haben einen Teil von 18 GB zum Training verwendet.

In diesem Beispiel werden wir der Einfachheit halber einen Datensatz mit spanischen Filmuntertiteln aus OpenSubtitles. Dieser Datensatz hat eine Größe von 5,4 GB und wir werden mit einer Teilmenge von ~300 MB trainieren.

In [0]:

os importieren
# Herunterladen und Entpacken des Filmsubstitutionsdatensatzes
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 datensatz.txt.gz
  !mkdir daten
  !mv dataset.txt daten
--2020-04-06 15:53:04-- https://object.pouta.csc.fi/OPUS-OpenSubtitles/v2016/mono/es.txt.gz
Auflösen von object.pouta.csc.fi (object.pouta.csc.fi)... 86.50.254.18, 86.50.254.19
Verbindung zu object.pouta.csc.fi (object.pouta.csc.fi)|86.50.254.18|:443... hergestellt.
HTTP-Anfrage gesendet, warte auf Antwort... 200 OK
Länge: 1859673728 (1.7G) [application/gzip]
Speichern in: 'dataset.txt.gz'

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

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

In [0]:

# Gesamtzahl der Zeilen und einige zufällige Zeilen
!wc -l Daten/Datensatz.txt
!shuf -n 5 daten/datensatz.txt
179287150 data/dataset.txt
Sabes, pensé que tenías más pelotas que para enfrentarme a través de mi hermano.
Supe todos los encantamientos en todas las lenguas de los Elfos hombres y Orcos.
Anteriormente en Blue Bloods:
Y quiero que prometas que no habrá ningún trato con Daniel Stafford.
Fue comiquísimo.

In [0]:

# Abrufen einer Teilmenge der ersten 1.000.000 Zeilen für das Training
TRAIN_SIZE = 1000000 #@param {Typ: "Ganzzahl"}
!(head -n $TRAIN_SIZE data/dataset.txt) > data/train.txt

In [0]:

# Abrufen einer Teilmenge der nächsten 10.000 Zeilen für die Validierung
VAL_SIZE = 10000 #@param {Typ: "Ganzzahl"}
!(sed -n {TRAIN_SIZE + 1},{TRAIN_SIZE + VAL_SIZE}p data/dataset.txt) > data/dev.txt

3. Einen Tokenizer trainieren

Die ursprüngliche BERT-Implementierung verwendet einen WordPiece-Tokenizer mit einem Vokabular von 32K Teilworteinheiten. Diese Methode kann jedoch bei der Verarbeitung seltener Wörter "unbekannte" Token einführen.

In dieser Implementierung verwenden wir einen BPE-Tokenizer auf Byte-Ebene mit einem Vokabular von 50.265 Teilworteinheiten (wie bei RoBERTa-base). Die Verwendung von BPE auf Byte-Ebene macht es möglich, ein Unterwortvokabular von bescheidener Größe zu lernen, das jede Eingabe kodieren kann, ohne "unbekannte" Token zu erhalten.

Denn ByteLevelBPETokenizer erzeugt 2 Dateien ["vocab.json", "merges.txt"] während BertWordPieceTokenizer erzeugt nur 1 Datei vocab.txtwird es einen Fehler verursachen, wenn wir BertWordPieceTokenizer um Ausgaben eines BPE-Tokenizers zu laden.

In [0]:

%%zeit
from tokenizers import ByteLevelBPETokenizer
pfad = "daten/train.txt"
# Initialisieren eines Tokenizers
tokenizer = ByteLevelBPETokenizer()
# Anpassen des Trainings
tokenizer.train(files=path,
                vocab_size=50265,
                min_frequency=2,
                special_tokens=["", "", "", "", ""])
# Dateien auf der Festplatte speichern
!mkdir -p "models/roberta"
tokenizer.save("models/roberta")
CPU-Zeiten: user 1min 37s, sys: 1.02 s, gesamt: 1min 38s
Wandzeit: 1min 38s

Superschnell! Es dauert nur 2 Minuten, um auf 10 Millionen Zeilen zu trainieren.

Traing Language Model from Scratch

1. Modell Architektur

RoBERTa hat genau die gleiche Architektur wie BERT. Die einzigen Unterschiede sind:

  • RoBERTa verwendet einen BPE-Tokenizer auf Byte-Ebene mit einem größeren Unterwort-Vokabular (50k gegenüber 32k).
  • RoBERTa implementiert eine dynamische Wortmaskierung und lässt die Aufgabe der Vorhersage des nächsten Satzes fallen.
  • Die Trainings-Hyperparameter von RoBERTa.

Weitere Architekturkonfigurationen finden Sie in der Dokumentation (RoBERTa, BERT).

In [0]:

json importieren
config = {
    "architekturen": [
        "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. Training der Hyperparameter

HyperparamBERT-BasisRoBERTa-Basis
Sequenz Länge128, 512512
Größe der Charge2568K
Maximale Lernrate1e-46e-4
Max. Schritte1M500K
Warmup-Schritte10K24K
Gewichtsabnahme0.010.01
Adam $epsilon$1e-61e-6
Adam $beta_1$0.90.9
Adam $beta_2$0.9990.98
Gradient Clipping0.00.0

Beachten Sie, dass die Stapelgröße beim Training von RoBERTa 8000 beträgt. Obwohl RoBERTa-base für 500K Schritte trainiert wurde, ist der Rechenaufwand für das Training daher 16-mal so hoch wie bei BERT-base. In der RoBERTa-PapierEs wird gezeigt, dass das Training mit großen Stapeln die Komplexität für das Ziel der maskierten Sprachmodellierung sowie die Genauigkeit am Ende der Aufgabe verbessert. Eine größere Stapelgröße kann durch Optimieren erreicht werden gradient_accumulation_steps.

Aufgrund von Rechenbeschränkungen folgten wir dem Trainingsschema von BERT-base und trainierten unser SpanBERTa-Modell mit 4 Tesla P100 GPUs für 200K Schritte in 8 Tagen.

3. Ausbildung beginnen

Wir werden unser Modell von Grund auf trainieren, indem wir run_language_modeling.pyein von Hugging Face bereitgestelltes Skript, das den Korpus vorverarbeitet, tokenisiert und das Modell auf Modellierung von maskierter Sprache Aufgabe. Das Skript ist für das Training auf einem einzigen großen Korpus optimiert. Wenn Ihr Datensatz also groß ist und Sie ihn aufteilen möchten, um nacheinander zu trainieren, müssen Sie das Skript ändern oder sich einen Monsterrechner mit viel Speicherplatz zulegen.

In [0]:

# Update 22. April 2020: Hugging Face aktualisiert run_language_modeling.py Skript.
# Bitte verwenden Sie diese Version vor dem 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
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...
Verbindung zu raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... hergestellt.
HTTP-Anfrage gesendet, erwarte Antwort... 200 OK
Länge: 34328 (34K) [text/plain]
Speichern in: 'run_language_modeling.py'

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

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

Wichtige Argumente

  • --Zeile_für_Zeile Ob unterschiedliche Textzeilen im Datensatz als unterschiedliche Sequenzen behandelt werden sollen. Wenn jede Zeile in Ihrem Datensatz lang ist und ~512 Token oder mehr enthält, sollten Sie diese Einstellung verwenden. Wenn jede Zeile kurz ist, werden bei der Standard-Textvorverarbeitung alle Zeilen verkettet, mit Token versehen und die mit Token versehenen Ausgaben in Blöcke von 512 Token unterteilt. Sie können Ihre Datensätze auch in kleine Abschnitte aufteilen und diese separat vorverarbeiten. Die Verarbeitung von 3 GB Text dauert mit der Standardeinstellung ~50 Minuten. TextDatensatz Klasse.
  • --sollte_weitergehen Ob vom letzten Prüfpunkt in output_dir fortgefahren werden soll.
  • --modell_name_oder_pfad Der Modellprüfpunkt für die Initialisierung der Gewichte. Lassen Sie "Keine" stehen, wenn Sie ein Modell von Grund auf trainieren wollen.
  • --mlm Trainieren Sie mit maskiertem Sprachmodellierungsverlust anstelle von Sprachmodellierung.
  • --config_name, --tokenizer_name Optionale vortrainierte Konfiguration und Tokenizer-Name oder -Pfad, falls nicht identisch mit model_name_or_path. Wenn beides nicht vorhanden ist, wird eine neue Konfiguration initialisiert.
  • --per_gpu_train_batch_size Stapelgröße pro GPU/CPU für das Training. Wählen Sie die größte Anzahl, die Sie auf Ihren GPUs unterbringen können. Sie erhalten eine Fehlermeldung, wenn die Stapelgröße zu groß ist.
  • --gradient_accumulation_steps Anzahl der Aktualisierungsschritte, die kumuliert werden sollen, bevor ein Rückwärts-/Aktualisierungsdurchlauf durchgeführt wird. Sie können diesen Trick verwenden, um die Stapelgröße zu erhöhen. Zum Beispiel, wenn per_gpu_train_batch_size = 16 und gradient_accumulation_steps = 4beträgt die gesamte Zugstapelgröße 64.
  • --overwrite_output_dir Überschreiben Sie den Inhalt des Ausgabeverzeichnisses.
  • --no_cuda, --fp16, --fp16_opt_level Argumente für das Training auf GPU/CPU.
  • Andere Argumente sind Modellpfade und Trainingshyperparameter.

Es wird dringend empfohlen, den Modelltyp (z. B. "roberta", "bert", "gpt2" usw.) in den Modellpfad aufzunehmen, da das Skript die AutoModelle Klasse, um die Konfiguration des Modells mithilfe eines Mustervergleichs für den angegebenen Pfad zu erraten.

In [0]:

# Modellpfade
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 = "daten/dev.txt" #@param {Typ: "string"}

Für dieses Beispiel werden wir nur 25 Schritte auf einem Tesla P4 Grafikprozessor trainieren, der von Colab zur Verfügung gestellt wurde.

In [0]:

!nvidia-smi
Mon Apr 6 15:59:35 2020
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.64.00 Treiber Version: 418.67 CUDA Version: 10.1 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 Tesla P4 Aus | 00000000:00:04.0 Aus | 0 |
| N/A 31C P8 7W / 75W | 0MiB / 7611MiB | 0% Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Prozesse:                                                       GPU-Speicher |
| GPU PID Typ Prozessname Nutzung |
|=============================================================================|
| Keine laufenden Prozesse gefunden |
+-----------------------------------------------------------------------------+

In [0]:

# Befehlszeile
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}
    {Modell_name_oder_pfad}
    --train_data_file {train_path}
    --eval_data_file {eval_path}
    --do_train
    {do_eval}
    {auswerten_während_des_Trainings}
    --overwrite_output_dir
    --block_size 512
    --max_step 25
    --warmup_steps 10
    ---Lernrate 5e-5
    --pro_gpu_train_batch_size 4
    --gradient_accumulation_steps 4
    --weight_decay 0.01
    --adam_epsilon 1e-6
    -max_grad_norm 100.0
    --speichern_gesamte_grenze 10
    --sichern_Schritte 10
    --logging_steps 2
    --seed 42
"""

In [0]:

# Argumente für die Ausbildung von Grund auf. Ich schalte evaluate_during_training aus,
# line_by_line, should_continue, und 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": "",
}

Wenn Sie auf einer virtuellen Maschine trainieren, können Sie Tensorboard installieren, um den Trainingsprozess zu überwachen. Hier ist unser Tensorboard für die Ausbildung von SpanBERTa.

pip install tensorboard==2.1.0
tensorboard dev upload --logdir läuft

Nach 200k Schritten erreichte der Verlust 1,8 und die Perplexität 5,2.

Und jetzt lass uns mit dem Training beginnen!

In [ ]:

!{cmd.format(**train_params)}
    04/06/2020 15:59:55 - INFO - __main__ - Erstellen von Merkmalen aus der Datensatzdatei data
    04/06/2020 16:04:43 - INFO - __main__ - Speichern von Merkmalen in die zwischengespeicherte Datei data/roberta_cached_lm_510_train.txt
    04/06/2020 16:04:46 - INFO - __main__ - ***** Training läuft *****
    04/06/2020 16:04:46 - INFO - __main__ - Anzahl der Beispiele = 165994
    04/06/2020 16:04:46 - INFO - __main__ - Anzahl Epochen = 1
    04/06/2020 16:04:46 - INFO - __main__ - Momentane Stapelgröße pro GPU = 4
    04/06/2020 16:04:46 - INFO - __main__ - Gesamte Zugstapelgröße (parallel, verteilt & akkumuliert) = 16
    04/06/2020 16:04:46 - INFO - __main__ - Gradienten Akkumulationsschritte = 4
    04/06/2020 16:04:46 - INFO - __main__ - Gesamte Optimierungsschritte = 25
    Epoche: 0% 0/1 [00:00<?, ?it/s]
    Iteration:   0% 0/41499 [00:00<?, ?it/s]
    Iteration:   0% 1/41499 [00:01<13:18:02, 1.15s/it]
    Iteration:   0% 2/41499 [00:01<11:26:47, 1.01it/s]
    Iteration:   0% 3/41499 [00:02<10:10:30, 1.13it/s]
    Iteration:   0% 4/41499 [00:03<9:38:10, 1.20it/s]
    Iteration:   0% 5/41499 [00:03<8:52:44, 1.30it/s]
    Iteration:   0% 6/41499 [00:04<8:22:47, 1.38it/s]
    Iteration:   0% 7/41499 [00:04<8:00:55, 1.44it/s]
    Iteration:   0% 8/41499 [00:05<8:03:40, 1.43it/s]
    Iteration:   0% 9/41499 [00:06<7:46:57, 1.48it/s]
    Iteration:   0% 10/41499 [00:06<7:35:35, 1.52it/s]
    Iteration:   0% 11/41499 [00:07<7:28:29, 1.54it/s]
    Iteration:   0% 12/41499 [00:08<7:41:41, 1.50it/s]
    Iteration:   0% 13/41499 [00:08<7:34:28, 1.52it/s]
    Iteration:   0% 14/41499 [00:09<7:28:46, 1.54it/s]
    Iteration:   0% 15/41499 [00:10<7:23:29, 1.56it/s]
    Iteration:   0% 16/41499 [00:10<7:38:06, 1.51it/s]
    Iteration:   0% 17/41499 [00:11<7:29:13, 1.54it/s]
    Iteration:   0% 18/41499 [00:12<7:24:04, 1.56it/s]
    Iteration:   0% 19/41499 [00:12<7:21:59, 1.56it/s]
    Iteration:   0% 20/41499 [00:13<7:38:06, 1.51it/s]
    04/06/2020 16:06:23 - INFO - __main__ - ***** Laufende Auswertung *****
    04/06/2020 16:06:23 - INFO - __main__ - Anzahl der Beispiele = 156
    04/06/2020 16:06:23 - INFO - __main__ - Stapelgröße = 4
    Auswerten: 100% 39/39 [00:08<00:00, 4.41it/s]
    04/06/2020 16:06:32 - INFO - __main__ - ***** Auswertungsergebnisse *****
    04/06/2020 16:06:32 - INFO - __main__ - Komplexität = Tensor(6077.6812)

4. Maskierte Wörter vorhersagen

Nachdem Sie Ihr Sprachmodell trainiert haben, können Sie es hochladen und mit der Community teilen. Wir haben unser SpanBERTa-Modell auf den Server von Hugging Face hochgeladen. Bevor wir das Modell an nachgelagerten Aufgaben evaluieren, wollen wir sehen, wie es gelernt hat, maskierte Wörter in einem bestimmten Kontext zu füllen.

In [0]:

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

Ich greife einen Satz aus dem Wikipedia-Artikel über COVID-19 heraus.

Der ursprüngliche Satz lautet: "Lavarse frecuentemente las manos con agua y jabón," bedeutet "Waschen Sie sich häufig die Hände mit Wasser und Seife.

Das maskierte Wort lautet "jabón" (Seife) und die 5 besten Prognosen sind Seife, Salz, Dampf, Zitrone und Essig. Es ist interessant, dass das Modell irgendwie lernt, dass wir unsere Hände mit Dingen waschen sollten, die Bakterien abtöten können oder Säure enthalten.

In [0]:

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

Out[0]:

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

Schlussfolgerung

Wir haben gezeigt, wie man ein BERT-Sprachmodell für Spanisch von Grund auf trainiert und gesehen, dass das Modell Eigenschaften der Sprache gelernt hat, indem es versucht hat, maskierte Wörter in einem bestimmten Kontext vorherzusagen. Sie können diesem Artikel auch folgen, um ein vortrainiertes BERT-ähnliches Modell auf Ihren eigenen Datensatz abzustimmen.

Als Nächstes implementieren wir die vortrainierten Modelle für nachgelagerte Aufgaben wie Sequenzklassifikation, NER, POS-Tagging und NLI und vergleichen die Leistung des Modells mit einigen Nicht-BERT-Modellen.

Bleiben Sie dran für unsere nächsten Beiträge!


Lassen Sie uns Ihre Idee besprechen

    Verwandte Beiträge

    Bereit, Ihr Geschäft aufzuladen

    LASST UNS
    TALK
    de_DEDeutsch