Tutorial: Come addestrare un modello linguistico RoBERTa per lo spagnolo

SpanBERTa: Come abbiamo addestrato da zero il modello linguistico RoBERTa per lo spagnolo

    

Pubblicato originariamente da Chris Tran, stagista di ricerca sull'apprendimento automatico di Skim AI.



spanberta_preparazione_di_bert_dal_graffio





Eseguire in Google Colab

Introduzione

I metodi di autoformazione con modelli di trasformatori hanno raggiunto lo stato dell'arte nella maggior parte dei compiti di NLP. Tuttavia, poiché il loro addestramento è costoso dal punto di vista computazionale, la maggior parte dei modelli trasformatori pre-addestrati attualmente disponibili riguarda solo l'inglese. Pertanto, per migliorare le prestazioni nei compiti di NLP nei nostri progetti sullo spagnolo, il mio team di Skim AI ha deciso di formare un RoBERTa modello linguistico per lo spagnolo da zero e chiamarlo SpanBERTa.

SpanBERTa ha le stesse dimensioni di RoBERTa-base. Abbiamo seguito lo schema di addestramento di RoBERTa per addestrare il modello su 18 GB di OSCARdel corpus spagnolo in 8 giorni utilizzando 4 GPU Tesla P100.

In questo post del blog, illustreremo un processo end-to-end per l'addestramento di un modello linguistico simile a BERT partendo da zero utilizzando trasformatori e tokenizer biblioteche di Hugging Face. È disponibile anche un notebook di Google Colab per eseguire direttamente i codici di questo articolo. È inoltre possibile modificare il notebook per addestrare un modello simile a BERT per altre lingue o perfezionarlo su un set di dati personalizzato.

Prima di proseguire, desidero esprimere un enorme ringraziamento al team di Hugging Face per aver reso accessibili a tutti i modelli NLP all'avanguardia.

Impostazione

1. Installare le dipendenze

In [0]:

%pture
!pip disinstallare -y tensorflow
!pip installare transformers==2.8.0

2. I dati

Abbiamo preaddestrato SpanBERTa su OSCARdel corpus spagnolo. L'intera dimensione del dataset è di 150 GB e per l'addestramento abbiamo utilizzato una porzione di 18 GB.

In questo esempio, per semplicità, si utilizzerà un set di dati di sottotitoli di film spagnoli provenienti da Sottotitoli aperti. Questo dataset ha una dimensione di 5,4 GB e ci alleneremo su un sottoinsieme di ~300 MB.

In [0]:

importare os
# Scaricare e decomprimere il set di dati dei sottotitoli dei film
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 dati
  mv dataset.txt dati
--2020-04-06 15:53:04-- https://object.pouta.csc.fi/OPUS-OpenSubtitles/v2016/mono/es.txt.gz
Risoluzione di object.pouta.csc.fi (object.pouta.csc.fi)... 86.50.254.18, 86.50.254.19
Connessione a object.pouta.csc.fi (object.pouta.csc.fi)|86.50.254.18|:443... connesso.
Richiesta HTTP inviata, in attesa di risposta... 200 OK
Lunghezza: 1859673728 (1.7G) [application/gzip]
Salvataggio 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' salvato [1859673728/1859673728]

In [0]:

# Numero totale di linee e alcune linee casuali
wc -l dati/dataset.txt
!shuf -n 5 dati/dataset.txt
179287150 data/dataset.txt
Sai, credevo di avere più armi per affrontarmi a causa di mio fratello.
Tutti i dettagli in tutte le lingue degli uomini elfi e degli orchi.
Precedentemente in Blue Bloods:
Voglio promettere che non c'è stato alcun contatto con Daniel Stafford.
È stato comico.

In [0]:

# Ottenere un sottoinsieme delle prime 1.000.000 di righe per l'addestramento
TRAIN_SIZE = 1000000 #@param {type: "integer"}
!(head -n $TRAIN_SIZE data/dataset.txt) > data/train.txt

In [0]:

# Ottenere un sottoinsieme delle prossime 10.000 righe per la convalida
VAL_SIZE = 10000 #@param {type: "integer"}
!(sed -n {TRAIN_SIZE + 1},{TRAIN_SIZE + VAL_SIZE}p dati/dataset.txt) > dati/dev.txt

3. Addestrare un tokenizzatore

L'implementazione originale del BERT utilizza un tokenizer WordPiece con un vocabolario di 32K unità di sottoparole. Questo metodo, tuttavia, può introdurre token "sconosciuti" quando si elaborano parole rare.

In questa implementazione, utilizziamo un tokenizer BPE a livello di byte con un vocabolario di 50.265 unità subword (lo stesso di RoBERTa-base). L'uso di BPE a livello di byte consente di apprendere un vocabolario di sottoparole di dimensioni modeste, in grado di codificare qualsiasi input senza ottenere token "sconosciuti".

Perché ByteLevelBPETokenizer produce 2 file ["vocab.json", "merges.txt"]. mentre BertWordPieceTokenizer produce solo 1 file vocab.txtcauserà un errore se utilizziamo BertWordPieceTokenizer per caricare le uscite di un tokenizzatore BPE.

In [0]:

%%time
da tokenizers import ByteLevelBPETokenizer
percorso = "dati/allenamento.txt"
# Inizializzazione di un tokenizer
tokenizer = ByteLevelBPETokenizer()
# Personalizzare l'addestramento
tokenizer.train(file=percorso,
                vocab_size=50265,
                min_frequency=2,
                special_tokens=["", "", "", "", ""])
# Salvare i file su disco
mkdir -p "modelli/roberta"
tokenizer.save("models/roberta")
Tempi della CPU: utente 1min 37s, sys: 1,02 s, totale: 1min 38s
Tempo di parete: 1min 38s

Super veloce! Ci vogliono solo 2 minuti per allenarsi su 10 milioni di linee.

Modello linguistico da zero

1. Architettura del modello

RoBERTa ha esattamente la stessa architettura di BERT. Le uniche differenze sono:

  • RoBERTa utilizza un tokenizzatore BPE a livello di byte con un vocabolario di sottoparole più ampio (50k contro 32k).
  • RoBERTa implementa il mascheramento dinamico delle parole e abbandona il compito di predizione della frase successiva.
  • Iperparametri di addestramento di RoBERTa.

Altre configurazioni dell'architettura sono disponibili nella documentazione (RoBERTa, BERT).

In [0]:

importare json
config = {
    "architetture": [
        "RobertaForMaskedLM"
    ],
    "attention_probs_dropout_prob": 0.1,
    "atto_nascosto": "gelu",
    "nascosto_prob": "nascosto_prob": 0.1,
    "hidden_size": 768,
    "initializer_range": 0.02,
    "dimensione_intermedia": 3072,
    "layer_norm_eps": 1e-05,
    "max_position_embeddings": 514,
    "model_type": "roberta",
    "num_teste_di_attenzione": 12,
    "num_strati_nascosti": 12,
    "dimensione_tipo_vocabolo": 1,
    "vocab_size": 50265
}
con open("models/roberta/config.json", 'w') come fp:
    json.dump(config, fp)
tokenizer_config = {"max_len": 512}
con open("models/roberta/tokenizer_config.json", 'w') come fp:
    json.dump(tokenizer_config, fp)

2. Iperparametri di formazione

IperparametroBERT-baseRoBERTa-base
Lunghezza della sequenza128, 512512
Dimensione del lotto2568K
Velocità di apprendimento di picco1e-46e-4
Passi massimi1M500K
Passi di riscaldamento10K24K
Decadimento del peso0.010.01
Adamo $epsilon$1e-61e-6
Adamo $beta_1$0.90.9
Adamo $beta_2$0.9990.98
Ritaglio di gradiente0.00.0

Si noti che la dimensione del batch durante l'addestramento di RoBERTa è di 8000. Pertanto, sebbene RoBERTa-base sia stato addestrato per 500K passi, il suo costo computazionale di addestramento è 16 volte quello di BERT-base. Nel Carta RoBERTaÈ stato dimostrato che l'addestramento con batch di grandi dimensioni migliora la perplessità per l'obiettivo di modellazione del linguaggio mascherato e l'accuratezza del compito finale. Una maggiore dimensione dei batch può essere ottenuta modificando passi_di_accumulo_di_gradiente.

A causa dei vincoli computazionali, abbiamo seguito lo schema di addestramento di BERT-base e abbiamo addestrato il nostro modello SpanBERTa utilizzando 4 GPU Tesla P100 per 200K passi in 8 giorni.

3. Avvio della formazione

Addestreremo il nostro modello da zero utilizzando run_language_modeling.py, uno script fornito da Hugging Face, che preprocesserà, tokenizzerà il corpus e addestrerà il modello su Modellazione linguistica mascherata compito. Lo script è ottimizzato per l'addestramento su un unico grande corpus. Pertanto, se il vostro set di dati è grande e volete dividerlo per addestrarlo in modo sequenziale, dovrete modificare lo script o essere pronti a procurarvi una macchina mostruosa con una memoria elevata.

In [0]:

# Aggiornamento del 22 aprile 2020: Lo script run_language_modeling.py di Hugging Face è stato aggiornato.
# Utilizzare questa versione prima dell'aggiornamento.
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
Risoluzione di raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...
Connessione a raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connesso.
Richiesta HTTP inviata, in attesa di risposta... 200 OK
Lunghezza: 34328 (34K) [text/plain]
Salvataggio su: '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' salvato [34328/34328]

Argomenti importanti

  • --linea_per_linea Se le righe di testo distinte nel set di dati devono essere gestite come sequenze distinte. Se ogni riga del dataset è lunga e ha ~512 token o più, si dovrebbe usare questa impostazione. Se ogni riga è corta, la preelaborazione predefinita del testo concatena tutte le righe, le tokenizza e divide i risultati tokenizzati in blocchi di 512 tokens. È anche possibile dividere i set di dati in piccoli pezzi e preelaborarli separatamente. Per elaborare 3 GB di testo ci vorranno circa 50 minuti con l'elaborazione predefinita. Set di dati di testo classe.
  • --dovrebbe_continuare Se continuare dall'ultimo checkpoint in output_dir.
  • -nome_modello_o_percorso Il checkpoint del modello per l'inizializzazione dei pesi. Lasciare Nessuno se si vuole addestrare un modello da zero.
  • --mlm Addestramento con perdita di modellazione linguistica mascherata invece che con modellazione linguistica.
  • --nome_configurazione, -nome_tokenizer Configurazione preaddestrata opzionale e nome del tokenizzatore o percorso, se non è lo stesso di nome_modello_o_percorso. Se entrambi sono None, inizializzare una nuova configurazione.
  • --per_gpu_train_batch_size Dimensione del batch per GPU/CPU per l'addestramento. Scegliere il numero più grande possibile per le GPU. Se la dimensione del batch è troppo grande, verrà visualizzato un errore.
  • --gradiente_di_accumulazione_passi Numero di passi di aggiornamento da accumulare prima di eseguire un passaggio indietro/aggiornamento. È possibile utilizzare questo trucco per aumentare le dimensioni del batch. Ad esempio, se per_gpu_train_batch_size = 16 e gradiente_accumulo_passi = 4, la dimensione totale del batch del treno sarà di 64 unità.
  • --sovrascrivere_uscita_dir Sovrascrive il contenuto della directory di output.
  • --no_cuda, --fp16, --fp16_opt_level Argomenti per la formazione su GPU/CPU.
  • Gli altri argomenti sono i percorsi del modello e gli iperparametri di addestramento.

Si raccomanda vivamente di includere il tipo di modello (ad es. "roberta", "bert", "gpt2", ecc.) nel percorso del modello, perché lo script utilizza il parametro Modelli di auto per indovinare la configurazione del modello, utilizzando la corrispondenza dei pattern sul percorso fornito.

In [0]:

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

Per questo esempio, ci alleneremo per soli 25 passi su una GPU Tesla P4 fornita da Colab.

In [0]:

!nvidia-smi
Mon Apr 6 15:59:35 2020
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.64.00 Versione del driver: 418.67 Versione CUDA: 10.1 |
|-------------------------------+----------------------+----------------------+
| Nome GPU Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Temp. ventola 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 |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processi:                                                       Memoria GPU |
| GPU PID Tipo Nome del processo Utilizzo
|=============================================================================|
| Nessun processo in esecuzione trovato
+-----------------------------------------------------------------------------+

In [0]:

Linea di comando #
cmd = """python run_language_modeling.py
    --output_dir {output_dir}
    --model_type {model_type}
    --mlm
    --nome_configurazione {nome_configurazione}
    -nome_tokenizer {nome_tokenizer}
    {linea_per_linea}
    {Dovrebbe_continuare}
    {nome_modello_o_percorso}
    --train_data_file {train_path}
    --file_dati_di_valutazione {percorso_di_valutazione}
    --do_train
    {do_eval}
    {valutazione_durante_l'allenamento}
    --sovrascrivere_uscita_dir
    -dimensione blocco 512
    --max_step 25
    -passi di riscaldamento 10
    --rate di apprendimento 5e-5
    --per_gpu_train_batch_size 4
    --gradiente_accumulo_passi 4
    --peso_decadimento 0.01
    --adam_epsilon 1e-6
    --max_grad_norm 100.0
    -salvataggio_totale_limite 10
    --salvataggio_passi 10
    -passi di registrazione 2
    --seed 42
"""

In [0]:

# Argomenti per la formazione da zero. Disattivo evaluate_during_training,
# line_by_line, should_continue e model_name_or_path.
train_params = {
    "output_dir": OUTPUT_DIR,
    "model_type": MODEL_TYPE,
    "nome_configurazione": MODEL_DIR,
    "nome_tokenizer": MODEL_DIR,
    "train_path": TRAIN_PATH,
    "eval_path": EVAL_PATH,
    "do_eval": "--do_eval",
    "evaluate_during_training": "",
    "linea_per_linea": "",
    "dovrebbe_continuare": "",
    "nome_modello_o_percorso": "",
}

Se ci si allena su una macchina virtuale, è possibile installare tensorboard per monitorare il processo di allenamento. Ecco il nostro Tensorboard per la formazione di SpanBERTa.

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

Dopo 200k passi, la perdita ha raggiunto 1,8 e la perplessità ha raggiunto 5,2.

Ora iniziamo ad allenarci!

In [ ]:

!{cmd.format(**train_params)}
    04/06/2020 15:59:55 - INFO - __main__ - Creazione di caratteristiche dal file dataset in data
    04/06/2020 16:04:43 - INFO - __main__ - Salvataggio delle caratteristiche nel file in cache data/roberta_cached_lm_510_train.txt
    04/06/2020 16:04:46 - INFO - __main__ - ***** Esecuzione dell'allenamento *****
    04/06/2020 16:04:46 - INFO - __main__ - Num esempi = 165994
    04/06/2020 16:04:46 - INFO - __main__ - Num Epochs = 1
    04/06/2020 16:04:46 - INFO - __main__ - Dimensione istantanea del batch per GPU = 4
    04/06/2020 16:04:46 - INFO - __main__ - Dimensione totale del batch del treno (con parallelo, distribuito e accumulo) = 16
    04/06/2020 16:04:46 - INFO - __main__ - Fasi di accumulo del gradiente = 4
    04/06/2020 16:04:46 - INFO - __main__ - Totale passi di ottimizzazione = 25
    Epoca: 0% 0/1 [00:00<?, ?it/s]
    Iterazione:   0% 0/41499 [00:00<?, ?it/s]
    Iterazione:   0% 1/41499 [00:01<13:18:02, 1.15s/it]
    Iterazione:   0% 2/41499 [00:01<11:26:47, 1.01it/s]
    Iterazione:   0% 3/41499 [00:02<10:10:30, 1.13it/s]
    Iterazione:   0% 4/41499 [00:03<9:38:10, 1.20it/s]
    Iterazione:   0% 5/41499 [00:03<8:52:44, 1.30it/s]
    Iterazione:   0% 6/41499 [00:04<8:22:47, 1.38it/s]
    Iterazione:   0% 7/41499 [00:04<8:00:55, 1.44it/s]
    Iterazione:   0% 8/41499 [00:05<8:03:40, 1.43it/s]
    Iterazione:   0% 9/41499 [00:06<7:46:57, 1.48it/s]
    Iterazione:   0% 10/41499 [00:06<7:35:35, 1.52it/s]
    Iterazione:   0% 11/41499 [00:07<7:28:29, 1.54it/s]
    Iterazione:   0% 12/41499 [00:08<7:41:41, 1.50it/s]
    Iterazione:   0% 13/41499 [00:08<7:34:28, 1.52it/s]
    Iterazione:   0% 14/41499 [00:09<7:28:46, 1.54it/s]
    Iterazione:   0% 15/41499 [00:10<7:23:29, 1.56it/s]
    Iterazione:   0% 16/41499 [00:10<7:38:06, 1,51it/s]
    Iterazione:   0% 17/41499 [00:11<7:29:13, 1,54it/s]
    Iterazione:   0% 18/41499 [00:12<7:24:04, 1,56it/s]
    Iterazione:   0% 19/41499 [00:12<7:21:59, 1,56it/s]
    Iterazione:   0% 20/41499 [00:13<7:38:06, 1,51it/s]
    04/06/2020 16:06:23 - INFO - __main__ - ***** Esecuzione della valutazione *****
    04/06/2020 16:06:23 - INFO - __main__ - Num esempi = 156
    04/06/2020 16:06:23 - INFO - __main__ - Dimensione del lotto = 4
    Valutazione: 100% 39/39 [00:08<00:00, 4.41it/s]
    04/06/2020 16:06:32 - INFO - __main__ - ***** Risultati della valutazione *****
    04/06/2020 16:06:32 - INFO - __main__ - perplessità = tensore(6077.6812)

4. Prevedere le parole mascherate

Dopo aver addestrato il modello linguistico, è possibile caricarlo e condividerlo con la comunità. Abbiamo caricato il nostro modello SpanBERTa sul server di Hugging Face. Prima di valutare il modello su compiti a valle, vediamo come ha imparato a riempire parole mascherate date da un contesto.

In [0]:

%pture
%%tempo
da transformers import pipeline
fill_mask = pipeline(
 "fill-mask",
 model="chriskhanhtran/spanberta",
 tokenizer="chriskhanhtran/spanberta"
)

Prendo una frase dall'articolo di Wikipedia su COVID-19.

La frase originale è "Lavare frequentemente le mani con agua e jabón," che significa "Lavarsi spesso le mani con acqua e sapone.

La parola mascherata è "jabón" (sapone) e i primi 5 pronostici sono sapone, sale, vapore, limone e aceto. È interessante che il modello impari in qualche modo che dovremmo lavarci le mani con oggetti che possono uccidere i batteri o che contengono acido.

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}]

Conclusione

Abbiamo visto come addestrare un modello linguistico BERT per lo spagnolo partendo da zero e abbiamo visto che il modello ha appreso le proprietà della lingua cercando di prevedere le parole mascherate date da un contesto. Potete anche seguire questo articolo per mettere a punto un modello BERT pre-addestrato sul vostro set di dati personalizzato.

Successivamente, implementeremo i modelli preaddestrati su compiti a valle, tra cui la classificazione delle sequenze, il NER, il POS tagging e l'NLI, e confronteremo le prestazioni del modello con alcuni modelli non BERT.

Restate sintonizzati per i prossimi post!


Discutiamo la vostra idea

    Messaggi correlati

    Pronti a potenziare la vostra attività

    LET'S
    PARLARE
    it_ITItaliano