Come addestrare BERT per compiti di modellazione del linguaggio mascherato

Come addestrare BERT per la modellazione del linguaggio mascherato nel campo della moda

Guida pratica per la costruzione di un modello di lingua per compiti di Masked Language Model da zero utilizzando Python e la libreria Transformers

Introduzione

Negli ultimi anni, i grandi modelli di lingua (LLM) hanno attirato tutta l’attenzione della comunità di machine learning. Prima dell’introduzione dei LLM, abbiamo avuto una fase di ricerca cruciale su varie tecniche di modellazione del linguaggio, tra cui masked language modeling, causal language modeling e sequence-to-sequence language modeling.

Dalla lista sopra, i masked language models come BERT sono diventati più utilizzabili in compiti di NLP successivi come classificazione e clustering. Grazie alle librerie come Hugging Face Transformers, l’adattamento di questi modelli per compiti successivi è diventato più accessibile e gestibile. Grazie anche alla comunità open-source, abbiamo molteplici modelli di lingua tra cui scegliere che coprono linguaggi e domini ampiamente utilizzati.

Affinare o costruire da zero?

Quando si adattano modelli di lingua esistenti ai casi d’uso specifici, a volte possiamo utilizzare modelli esistenti senza ulteriori affinamenti (il cosiddetto fine-tuning). Ad esempio, se si desidera un modello di rilevamento sentimentale/intent in lingua inglese, è possibile consultare HuggingFace.co e trovare un modello adatto per il proprio caso d’uso.

Tuttavia, questo si può applicare solo a alcuni dei compiti incontrati nel mondo reale. Qui è dove abbiamo bisogno di una tecnica aggiuntiva chiamata fine-tuning. Prima di tutto, è necessario scegliere un modello di base che verrà affinato. A questo punto, è necessario prestare attenzione al modello selezionato e alla similarità lessicale della lingua di destinazione.

Tuttavia, se non si trova un modello adatto riaddestrato nella lingua desiderata, si può considerare la costruzione di uno da zero. In questo tutorial, implementeremo il modello BERT per il masked language model.

Architettura BERT

Anche se descrivere l’architettura BERT esce dallo scopo di questo tutorial, per una questione di chiarezza, diamo un’occhiata molto ampia. BERT, o Bidirectional Encoder Representations from Transformers, fa parte della famiglia di transformer basata solo sulla codifica. È stato introdotto nel 2018 da ricercatori di Google.

Abstract del paper:

Introduciamo un nuovo modello di rappresentazione del linguaggio chiamato BERT, che sta per Bidirectional Encoder Representations from Transformers. A differenza dei recenti modelli di rappresentazione del linguaggio, BERT è progettato per pre-addestrare rappresentazioni bidirezionali profonde a partire da testo non etichettato, condizionando contemporaneamente sia il contesto a sinistra che a destra in tutti i layer. Di conseguenza, il modello BERT pre-addestrato può essere affinato con un solo strato di output aggiuntivo per creare modelli all’avanguardia per una vasta gamma di compiti, come la risposta alle domande e l’inferenza del linguaggio, senza modifiche sostanziali all’architettura specifica del compito. BERT è concettualmente semplice ed empiricamente potente. Ottiene nuovi risultati all’avanguardia in undici compiti di elaborazione del linguaggio naturale, portando il punteggio GLUE all’80,5% (miglioramento assoluto di 7,7 punti percentuali), l’accuratezza di MultiNLI all’86,7% (miglioramento assoluto del 4,6%), il punteggio F1 per la risposta alle domande SQuAD v1.1 al 93,2 (miglioramento assoluto di 1,5 punti) e il punteggio F1 per la risposta alle domande SQuAD v2.0 al 83,1 (miglioramento assoluto di 5,1 punti). Paper: https://arxiv.org/abs/1810.04805

Nel paragrafo precedente, possiamo vedere una parola chiave interessante, Bidirectional. La natura bidirezionale dà a BERT un potere simile a quello umano. Supponiamo di dover riempire uno spazio vuoto come nel seguente esempio:

“La guerra può talvolta essere un male necessario. Ma non importa quanto necessaria, è sempre un _____, mai una cosa buona.”

Per indovinare la parola che va nel vuoto, è necessario tenere conto di alcune cose: le parole prima dello spazio vuoto, le parole dopo il vuoto e il contesto generale della frase. Adattando questa natura umana, BERT funziona allo stesso modo. Durante l’addestramento, nascondiamo alcune parole e chiediamo a BERT di cercare di predirle. Quando l’addestramento è finito, BERT può prevedere i token nascosti in base alle parole che li precedono e a quelle che li seguono. Per fare ciò, il modello deve allocare una diversa attenzione alle parole presentate nella sequenza di input, che può influire in modo significativo sulla previsione dei token nascosti.

Image by Author via https://huggingface.co/spaces/exbert-project/exbert

Come puoi vedere qui, il modello vede una parola adatta per la posizione nascosta come male e il male della prima frase come necessario per fare questa previsione. Questo è un punto notevole e implica che il modello comprende il contesto della sequenza di input. Questa consapevolezza del contesto consente a BERT di generare embedding significativi per le frasi per le diverse attività. Inoltre, questi embedding possono essere utilizzati in attività successive come clustering e classificazione. Abbiamo parlato abbastanza di BERT, ora costruiamone uno da zero.

Definire il modello BERT

In generale, abbiamo BERT(base) e BERT(large). Entrambi hanno 64 dimensioni per ciascuna testa. La variante large contiene 24 livelli di codifica, mentre la variante base ne ha solo 12. Tuttavia, non siamo limitati a queste configurazioni. Sorprendentemente, abbiamo il completo controllo sulla definizione del modello utilizzando la libreria Hugging Face Transformers. Tutto ciò che dobbiamo fare è definire le configurazioni desiderate del modello utilizzando la classe BertConfig.

Ho scelto 6 testate e una dimensione totale del modello di 384 per essere conformi all’implementazione originale. In questo modo, ogni testata ha 64 dimensioni, simili all’implementazione originale. Iniziamo ad inizializzare il nostro modello BERT.

from transformers import BertConfig, BertForMaskedLMconfig = BertConfig(    hidden_size = 384,    vocab_size= tokenizer.vocab_size,    num_hidden_layers = 6,    num_attention_heads = 6,    intermediate_size = 1024,    max_position_embeddings = 256)model = BertForMaskedLM(config=config)print(model.num_parameters()) #10457864

Allenare un tokenizzatore

Qui, non descriverò come funziona la tokenizzazione sotto al cofano. Invece, alleniamone uno da zero utilizzando la libreria Hugging Face tokenizers. Si noti che il tokenizzatore utilizzato nell’implementazione originale di BERT è il tokenizzatore WordPiece, un altro metodo di sottoparole basato sulla tokenizzazione. Puoi approfondire questa tokenizzazione utilizzando la risorsa HuggingFace utile di seguito.

Tokenizzazione WordPiece – Corso di formazione Hugging Face NLP

Siamo in viaggio per avanzare e democratizzare l’intelligenza artificiale attraverso l’open source e la scienza aperta.

huggingface.co

Il dataset utilizzato qui è il dataset Sinhala-400M (sotto licenza apache-2.0). Puoi seguire lo stesso processo con qualsiasi dataset tu abbia.

Come potrai notare, alcune parole singalese sono state digitate anche in inglese. Alleniamo un tokenizzatore per questo corpus.

Iniziamo importando i moduli necessari. La cosa buona di allenare tokenizzatori utilizzando la libreria Hugging Face Tokenizers è che possiamo utilizzare tokenizzatori esistenti e sostituire solo il vocabolario (e unire dove applicabile) per il nostro corpus di allenamento. Questo significa che le fasi di tokenizzazione come la pre-tokenizzazione e la post-tokenizzazione saranno preservate. Per fare ciò, possiamo utilizzare un metodo, train_new_from_iterator, della classe BertTokenizer.

from tokenizers.implementations import ByteLevelBPETokenizerfrom tokenizers.processors import BertProcessingfrom transformers import AutoTokenizerfrom datasets import Datasetimport pandas as pd# carica il tokenizzatore di base per allenarlo sul datasettokenizer_base = AutoTokenizer.from_pretrained("bert-base-cased")# converti il dataset pandas in un dataset HFdataset = Dataset.from_pandas(df.rename(columns={"comment":'text'}))# definisci l'iteratoretraining_corpus = (    dataset[i : i + 1000]["text"]    for i in range(0, len(dataset), 1000))# allena il nuovo tokenizzatore per il datasettokenizer = tokenizer_base.train_new_from_iterator(training_corpus, 5000)# testa il tokenizzatore allenato su un testo di esempiotesto = dataset['text'][123]print(text)

# verifichiamo il processo di tokenizzazioneinput_ids = tokenizer(text).input_idssubword_view = [tokenizer.convert_ids_to_tokens(id) for id in input_ids]np.array(subword_view)

Puoi vedere parole come ‘giocatore di cricket’ decomposte in cricket e ##er, indicando che il tokenizer è stato adeguatamente addestrato. Tuttavia, prova diverse dimensioni di vocabolario; il mio è 5000, che è relativamente piccolo ma adatto a questo esempio di prova.

Infine, possiamo salvare il tokenizer addestrato nella nostra directory.

tokenizer.save_pretrained("tokenizer/sinhala-wordpiece-yt-comments")

Definire il collatore dei dati e tokenizzare il dataset.

Definiamo un collatore per i task MLM. Qui, mascheriamo il 15% dei token. In ogni caso, possiamo impostare diverse probabilità di mascheramento.

from transformers import DataCollatorForLanguageModelingdata_collator = DataCollatorForLanguageModeling(    tokenizer=tokenizer, mlm=True, mlm_probability=0.15)

Tokenizziamo il dataset utilizzando un tokenizer creato in precedenza. Sto sostituendo il dataset originale LineByLineTextDataset con la mia classe personalizzata che utilizza Hugging Face accelerate.

import torchfrom torch.utils.data import Datasetfrom accelerate import Accelerator, DistributedTypeclass LineByLineTextDataset(Dataset):    def __init__(self, tokenizer, raw_datasets, max_length: int):        self.padding = "max_length"        self.text_column_name = 'text'        self.max_length = max_length        self.accelerator = Accelerator(gradient_accumulation_steps=1)        self.tokenizer = tokenizer        with self.accelerator.main_process_first():            self.tokenized_datasets = raw_datasets.map(                self.tokenize_function,                batched=True,                num_proc=4,                remove_columns=[self.text_column_name],                desc="Running tokenizer on dataset line_by_line",            )            self.tokenized_datasets.set_format('torch',columns=['input_ids'],dtype=torch.long)                def tokenize_function(self,examples):        examples[self.text_column_name] = [            line for line in examples[self.text_column_name] if len(line[0]) > 0 and not line[0].isspace()        ]        return self.tokenizer(            examples[self.text_column_name],            padding=self.padding,            truncation=True,            max_length=self.max_length,            return_special_tokens_mask=True,        )    def __len__(self):        return len(self.tokenized_datasets)    def __getitem__(self, i):        return self.tokenized_datasets[i]

Tokenizziamo il dataset.

tokenized_dataset_train = LineByLineTextDataset(    tokenizer= tokenizer,    raw_datasets = dataset,    max_length=256,)

Bene, codifichiamo il nostro ciclo di addestramento.

from transformers import Trainer, TrainingArgumentstraining_args = TrainingArguments(    output_dir="./model",    overwrite_output_dir=True,    push_to_hub=True,    hub_model_id="Ransaka/sinhala-bert-yt-comments",    num_train_epochs=2,    per_device_train_batch_size=32,    save_steps=5_000,    logging_steps = 1000,    save_total_limit=2,    use_mps_device = True, # disabilita questo se si sta eseguendo un ambiente non-Mac    hub_private_repo = False, # impostare su true se si desidera salvare il modello in modo privato    save_safetensors= True,    learning_rate = 1e-4,    report_to='wandb')trainer = Trainer(    model=model,    args=training_args,    data_collator=data_collator,    train_dataset=tokenized_dataset_train)trainer.train()

Possiamo invocare il trainer utilizzando il suo metodo train().

trainer.train()

Dopo un addestramento sufficiente, il nostro modello può essere utilizzato per compiti successivi come la classificazione a prova di zero e il clustering. Puoi trovare l’esempio che utilizza questo spazio Hugging Face per ulteriori dettagli.

Spazio di incorporamento sinhala – uno spazio di Hugging Face di Ransaka

Scopri incredibili app di apprendimento automatico realizzate dalla community

huggingface.co

Conclusione

Con risorse limitate, i modelli pre-addestrati possono riconoscere solo determinati pattern linguistici, ma possono comunque essere utili per casi d’uso specifici. Si consiglia vivamente di eseguire il fine-tuning quando possibile.

In questo articolo, tutte le immagini, salvo diversa indicazione, sono dell’autore.

Riferimenti

  1. Un BERT esplorabile – https://huggingface.co/spaces/exbert-project/exbert
  2. Documento BERT – https://arxiv.org/abs/1810.04805
  3. Dataset – https://huggingface.co/datasets/Ransaka/Sinhala-400M