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.
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.txt
wird 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¶
Hyperparam | BERT-Basis | RoBERTa-Basis |
---|---|---|
Sequenz Länge | 128, 512 | 512 |
Größe der Charge | 256 | 8K |
Maximale Lernrate | 1e-4 | 6e-4 |
Max. Schritte | 1M | 500K |
Warmup-Schritte | 10K | 24K |
Gewichtsabnahme | 0.01 | 0.01 |
Adam $epsilon$ | 1e-6 | 1e-6 |
Adam $beta_1$ | 0.9 | 0.9 |
Adam $beta_2$ | 0.999 | 0.98 |
Gradient Clipping | 0.0 | 0.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.py
ein 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, wennper_gpu_train_batch_size = 16
undgradient_accumulation_steps = 4
beträ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!