Pre-Allenare BERT con Hugging Face Transformers e Habana Gaudi

'Allenare BERT con Hugging Face Transformers e Habana Gaudi'

In questo tutorial, imparerai come pre-addestrare BERT-base da zero utilizzando un’istanza DL1 basata su Habana Gaudi su AWS per sfruttare i vantaggi di costo e prestazioni di Gaudi. Utilizzeremo le librerie Hugging Face Transformers, Optimum Habana e Datasets per pre-addestrare un modello BERT-base utilizzando il masked-language modeling, uno dei due compiti originali di pre-addestramento di BERT. Prima di iniziare, è necessario configurare l’ambiente di deep learning.

Visualizza Codice

Imparerai come:

  1. Preparare il dataset
  2. Addestrare un Tokenizer
  3. Preprocessare il dataset
  4. Pre-addestrare BERT su Habana Gaudi

Nota: I passaggi da 1 a 3 possono/devono essere eseguiti su una diversa dimensione dell’istanza in quanto sono compiti intensivi per la CPU.

Requisiti

Prima di iniziare, assicurati di soddisfare i seguenti requisiti:

  • Account AWS con quota per il tipo di istanza DL1
  • AWS CLI installata
  • Utente AWS IAM configurato in CLI con il permesso di creare e gestire istanze ec2

Risorse utili

  • Configurazione dell’ambiente di deep learning per Hugging Face Transformers con Habana Gaudi su AWS
  • Configurazione semplificata del deep learning con EC2 Remote Runner e Habana Gaudi
  • Documentazione di Optimum Habana
  • Script di pre-addestramento
  • Codice: pre-training-bert.ipynb

Cos’è BERT?

BERT, acronimo di Bidirectional Encoder Representations from Transformers, è un modello di Machine Learning (ML) per l’elaborazione del linguaggio naturale. È stato sviluppato nel 2018 dai ricercatori di Google AI Language e serve come soluzione multiuso per oltre 11 compiti linguistici comuni, come l’analisi del sentiment e il riconoscimento delle entità nominate.

Leggi di più su BERT nel nostro blog BERT 101 🤗 State Of The Art NLP Model Explained.

Cos’è un Masked Language Modeling (MLM)?

MLM consente/impone l’apprendimento bidirezionale dal testo tramite la mascheratura (nascosta) di una parola in una frase e costringendo BERT a utilizzare in modo bidirezionale le parole ai lati della parola coperta per prevedere la parola mascherata.

Esempio di Masked Language Modeling:

"Accidente! Sto pescando e una enorme trota ha [MASK] la mia lenza!"

Leggi di più su Masked Language Modeling qui.


Iniziamo. 🚀

Nota: I passaggi da 1 a 3 sono stati eseguiti su un’istanza AWS c6i.12xlarge.

1. Prepara il dataset

Il tutorial è “diviso” in due parti. La prima parte (passaggi 1-3) riguarda la preparazione del dataset e del tokenizer. La seconda parte (passaggio 4) riguarda il pre-addestramento di BERT sul dataset preparato. Prima di poter iniziare con la preparazione del dataset, è necessario configurare il nostro ambiente di sviluppo. Come menzionato nell’introduzione, non è necessario preparare il dataset sull’istanza DL1 e potresti utilizzare il tuo notebook o computer desktop.

Prima di tutto installeremo transformers, datasets e git-lfs per caricare il nostro tokenizer e dataset nell’Hugging Face Hub per un uso successivo.

!pip install transformers datasets
!sudo apt-get install git-lfs

Per completare la nostra configurazione, accediamo all’Hugging Face Hub per caricare il nostro dataset, tokenizer, artefatti del modello, registri e metriche durante l’addestramento e successivamente nell’Hub.

Per poter caricare il nostro modello nell’Hub, è necessario registrarsi sull’Hugging Face Hub.

Utilizzeremo l’utilità notebook_login del pacchetto huggingface_hub per accedere al nostro account. Puoi ottenere il tuo token nelle impostazioni di Access Tokens.

from huggingface_hub import notebook_login

notebook_login()

Ora che siamo connessi, otteniamo l’user_id che verrà utilizzato per caricare gli artefatti.

from huggingface_hub import HfApi

user_id = HfApi().whoami()["name"]

print(f"l'id utente '{user_id}' sarà utilizzato durante l'esempio")

Il BERT originale è stato preaddestrato sui dataset Wikipedia e BookCorpus. Entrambi i dataset sono disponibili su Hugging Face Hub e possono essere caricati con datasets.

Nota: Per Wikipedia utilizzeremo il 20220301, che è diverso dalla suddivisione originale.

Come primo passaggio stiamo caricando i dataset e li stiamo unendo insieme per creare un unico dataset.

from datasets import concatenate_datasets, load_dataset

bookcorpus = load_dataset("bookcorpus", split="train")
wiki = load_dataset("wikipedia", "20220301.en", split="train")
wiki = wiki.remove_columns([col for col in wiki.column_names if col != "text"])  # manteniamo solo la colonna 'text'

assert bookcorpus.features.type == wiki.features.type
raw_datasets = concatenate_datasets([bookcorpus, wiki])

Non faremo alcuna preparazione avanzata del dataset, come deduplicazione, filtraggio o qualsiasi altro pre-processing. Se hai intenzione di applicare questo notebook per addestrare il tuo modello BERT da zero, ti consiglio vivamente di includere queste fasi di preparazione dei dati nel tuo workflow. Questo ti aiuterà a migliorare il tuo Language Model.

2. Allenare un Tokenizer

Per poter addestrare il nostro modello, dobbiamo convertire il nostro testo in un formato tokenizzato. La maggior parte dei modelli Transformer viene fornita con un tokenizer preaddestrato, ma poiché stiamo preaddestrando il nostro modello da zero, dobbiamo anche addestrare un Tokenizer sui nostri dati. Possiamo addestrare un tokenizer sui nostri dati con transformers e la classe BertTokenizerFast.

Maggiori informazioni sull’addestramento di un nuovo tokenizer possono essere trovate nel nostro Corso Hugging Face.

from tqdm import tqdm
from transformers import BertTokenizerFast

# id del repository per salvare il tokenizer
tokenizer_id="bert-base-uncased-2022-habana"

# crea un generatore python per caricare dinamicamente i dati
def batch_iterator(batch_size=10000):
    for i in tqdm(range(0, len(raw_datasets), batch_size)):
        yield raw_datasets[i : i + batch_size]["text"]

# crea un tokenizer da uno già esistente per riutilizzare i token speciali
tokenizer = BertTokenizerFast.from_pretrained("bert-base-uncased")

Possiamo iniziare ad addestrare il tokenizer con train_new_from_iterator().

bert_tokenizer = tokenizer.train_new_from_iterator(text_iterator=batch_iterator(), vocab_size=32_000)
bert_tokenizer.save_pretrained("tokenizer")

Pushiamo il tokenizer su Hugging Face Hub per addestrare successivamente il nostro modello.

# devi essere loggato per pushare il tokenizer
bert_tokenizer.push_to_hub(tokenizer_id)

3. Preprocessare il dataset

Prima di poter iniziare ad addestrare il nostro modello, l’ultimo passo è pre-processare/tokenizzare il nostro dataset. Utilizzeremo il nostro tokenizer addestrato per tokenizzare il nostro dataset e successivamente pusharlo nell’hub per caricarlo facilmente in seguito nel nostro addestramento. Il processo di tokenizzazione viene mantenuto anche piuttosto semplice, se i documenti sono più lunghi di 512 token, vengono troncati e non divisi in diversi documenti.

from transformers import AutoTokenizer
import multiprocessing

# carica il tokenizer
# tokenizer = AutoTokenizer.from_pretrained(f"{user_id}/{tokenizer_id}")
tokenizer = AutoTokenizer.from_pretrained("tokenizer")
num_proc = multiprocessing.cpu_count()
print(f"La lunghezza massima per il tokenizer è: {tokenizer.model_max_length}")

def group_texts(examples):
    tokenized_inputs = tokenizer(
       examples["text"], return_special_tokens_mask=True, truncation=True, max_length=tokenizer.model_max_length
    )
    return tokenized_inputs

# preprocessa il dataset
tokenized_datasets = raw_datasets.map(group_texts, batched=True, remove_columns=["text"], num_proc=num_proc)
tokenized_datasets.features

Come funzione di elaborazione dei dati, concateniamo tutti i testi dal nostro dataset e generiamo chunk di lunghezza tokenizer.model_max_length (512).

from itertools import chain

# Funzione principale di elaborazione dei dati che concatena tutti i testi dal nostro dataset e genera chunk di
# max_seq_length.
def group_texts(examples):
    # Concatena tutti i testi.
    concatenated_examples = {k: list(chain(*examples[k])) for k in examples.keys()}
    total_length = len(concatenated_examples[list(examples.keys())[0]])
    # Eliminiamo il resto piccolo, potremmo aggiungere padding se il modello lo supportasse invece di eliminare, puoi
    # personalizzare questa parte secondo le tue esigenze.
    if total_length >= tokenizer.model_max_length:
        total_length = (total_length // tokenizer.model_max_length) * tokenizer.model_max_length
    # Dividiamo in chunk di max_len.
    result = {
        k: [t[i : i + tokenizer.model_max_length] for i in range(0, total_length, tokenizer.model_max_length)]
        for k, t in concatenated_examples.items()
    }
    return result

tokenized_datasets = tokenized_datasets.map(group_texts, batched=True, num_proc=num_proc)
# mescoliamo il dataset
tokenized_datasets = tokenized_datasets.shuffle(seed=34)

print(f"il dataset contiene in totale {len(tokenized_datasets)*tokenizer.model_max_length} token")
# il dataset contiene in totale 3417216000 token

Il passaggio finale prima di poter iniziare il nostro addestramento è quello di caricare il nostro dataset preparato nel hub.

# carica il dataset nel hub
dataset_id=f"{user_id}/processed_bert_dataset"
tokenized_datasets.push_to_hub(f"{user_id}/processed_bert_dataset")

4. Pre-allena BERT su Habana Gaudi

In questo esempio, utilizzeremo Habana Gaudi su AWS utilizzando l’istanza DL1 per eseguire la pre-allenamento. Utilizzeremo il toolkit Remote Runner per lanciare facilmente il nostro pre-allenamento su un’istanza DL1 remota dalla nostra configurazione locale. Se vuoi saperne di più su come funziona, puoi consultare Configurazione di Deep Learning semplificata con EC2 Remote Runner e Habana Gaudi.

!pip install rm-runner

Quando si utilizzano le GPU, si utilizzerebbe il Trainer e TrainingArguments . Poiché eseguiremo il nostro addestramento su Habana Gaudi, faremo uso della libreria optimum-habana, quindi useremo invece GaudiTrainer e GaudiTrainingArguments. Il GaudiTrainer è un wrapper attorno al Trainer che consente di pre-allenare o affinare un modello trasformatore su istanze di Habana Gaudi.

-from transformers import Trainer, TrainingArguments
+from optimum.habana import GaudiTrainer, GaudiTrainingArguments

# definisci gli argomenti di addestramento
-training_args = TrainingArguments(
+training_args = GaudiTrainingArguments(
+  use_habana=True,
+  use_lazy_mode=True,
+  gaudi_config_name=path_to_gaudi_config,
  ...
)

# Inizializza il nostro Trainer
-trainer = Trainer(
+trainer = GaudiTrainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset
    ... # altri argomenti
)

L’istanza DL1 che utilizziamo ha 8 core HPU disponibili, il che significa che possiamo sfruttare l’addestramento parallelo dei dati distribuiti per il nostro modello. Per eseguire il nostro addestramento come addestramento distribuito, è necessario creare uno script di addestramento, che può essere utilizzato con multiprocessing per eseguirlo su tutti gli HPUs. Abbiamo creato uno script run_mlm.py che implementa il masked-language modeling utilizzando il GaudiTrainer. Per eseguire il nostro addestramento distribuito, utilizziamo il runner DistributedRunner da optimum-habana e passiamo i nostri argomenti. In alternativa, potresti consultare gaudi_spawn.py nel repository optimum-habana.

Prima di poter avviare il nostro addestramento, dobbiamo definire gli iperparametri che vogliamo utilizzare per il nostro addestramento. Stiamo sfruttando l’integrazione di Hugging Face Hub del GaudiTrainer per caricare automaticamente i nostri checkpoint, i log e le metriche durante l’addestramento in un repository.

from huggingface_hub import HfFolder

# iperparametri
hyperparameters = {
    "model_config_id": "bert-base-uncased",
    "dataset_id": "philschmid/processed_bert_dataset",
    "tokenizer_id": "philschmid/bert-base-uncased-2022-habana",
    "gaudi_config_id": "philschmid/bert-base-uncased-2022-habana",
    "repository_id": "bert-base-uncased-2022",
    "hf_hub_token": HfFolder.get_token(),  # devi effettuare l'accesso con `huggingface-cli login`
    "max_steps": 100_000,
    "per_device_train_batch_size": 32,
    "learning_rate": 5e-5,
}
hyperparameters_string = " ".join(f"--{key} {value}" for key, value in hyperparameters.items())

Possiamo iniziare il nostro addestramento creando un EC2RemoteRunner e quindi launcharlo. Questo avvierà quindi la nostra istanza AWS EC2 DL1 e eseguirà il nostro script run_mlm.py su di essa utilizzando il container huggingface/optimum-habana:latest.

from rm_runner import EC2RemoteRunner
# crea l'ec2 remote runner
runner = EC2RemoteRunner(
  instance_type="dl1.24xlarge",
  profile="hf-sm",  # adatta al tuo profilo
  region="us-east-1",
  container="huggingface/optimum-habana:4.21.1-pt1.11.0-synapse1.5.0"
  )

# lancia il mio script con gaudi_spawn per l'addestramento distribuito
runner.launch(
    command=f"python3 gaudi_spawn.py --use_mpi --world_size=8 run_mlm.py {hyperparameters_string}",
    source_dir="scripts",
)

Questo esperimento è stato eseguito per 60k passi.

Nelle nostre iperparametri abbiamo definito una proprietà max_steps, che ha limitato il pre-training a soli 100_000 passi. I 100_000 passi con una dimensione di batch globale di 256 hanno richiesto circa 12,5 ore.

BERT è stato originariamente pre-trainato su 1 milione di passi con una dimensione di batch globale di 256:

Alleniamo con una dimensione di batch di 256 sequenze (256 sequenze * 512 token = 128.000 token/batch) per 1.000.000 di passi, che corrisponde a circa 40 epoche sul corpus di 3,3 miliardi di parole.

Ciò significa che se volessimo eseguire un pre-training completo ci vorrebbero circa 125 ore (12,5 ore * 10) e ci costerebbe circa ~$1.650 utilizzando Habana Gaudi su AWS, il che è estremamente conveniente.

A titolo di confronto, il team DeepSpeed, che detiene il record per il pre-training BERT più veloce, ha riportato che il pre-training di BERT su 1 DGX-2 (alimentato da 16 GPU NVIDIA V100 con 32GB di memoria ciascuna) richiede circa 33,25 ore.

Per confrontare i costi possiamo utilizzare come riferimento il p3dn.24xlarge, che è dotato di 8 GPU NVIDIA V100 32GB e costa circa ~31,22$/h. Avremmo bisogno di due di queste istanze per avere lo stesso “setup” di quello riportato da DeepSpeed, per ora stiamo ignorando qualsiasi overhead creato dalla configurazione multi-nodo (I/O, rete, ecc.). Ciò porterebbe il costo del training basato su GPU DeepSpeed su AWS a circa ~$2.075, che è il 25% in più rispetto a quanto offre attualmente Habana Gaudi.

Da notare qui è che l’utilizzo di DeepSpeed in generale migliora le prestazioni di un fattore di ~1,5 – 2. Un fattore di ~1,5 – 2x significa che lo stesso lavoro di pre-training senza DeepSpeed richiederebbe probabilmente il doppio del tempo e costerebbe il doppio o ~$3-4k.

Non vediamo l’ora di ripetere l’esperimento una volta che l’integrazione di Gaudi DeepSpeed sarà più ampiamente disponibile.

Conclusioni

Ecco tutto per questo tutorial. Ora conosci le basi su come pre-trainare BERT da zero utilizzando Hugging Face Transformers e Habana Gaudi. Hai anche visto quanto sia facile migrare dal Trainer al GaudiTrainer.

Abbiamo confrontato la nostra implementazione con i risultati più veloci del pre-training di BERT e abbiamo visto che Habana Gaudi offre ancora una riduzione dei costi del 25% e ci consente di pre-trainare BERT per ~$1.650.

Questi risultati sono incredibili poiché consentiranno alle aziende di adattare i loro modelli pre-trainati alla loro lingua e al loro dominio per migliorare l’accuratezza fino al 10% rispetto ai modelli BERT generali.

Se sei interessato a allenare il tuo BERT o altri modelli Transformers da zero per ridurre i costi e migliorare l’accuratezza, contatta i nostri esperti per saperne di più sul nostro Programma di Accelerazione degli Esperti. Per saperne di più sulle soluzioni Habana, leggi la nostra partnership e come contattarli.

Codice: pre-training-bert.ipynb


Grazie per la lettura! Se hai domande, non esitare a contattarmi tramite Github o sul forum. Puoi anche connetterti con me su Twitter o LinkedIn.