Buckets:

rtrm's picture
|
download
raw
55 kB
# Fine-tuningul la un masked language model[[fine-tuning-a-masked-language-model]]
{#if fw === 'pt'}
<CourseFloatingBanner chapter={7}
classNames="absolute z-10 right-0 top-0"
notebooks={[
{label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/en/chapter7/section3_pt.ipynb"},
{label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter7/section3_pt.ipynb"},
]} />
{:else}
<CourseFloatingBanner chapter={7}
classNames="absolute z-10 right-0 top-0"
notebooks={[
{label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/en/chapter7/section3_tf.ipynb"},
{label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter7/section3_tf.ipynb"},
]} />
{/if}
Pentru multe aplicații NLP care implică modele Transformer, puteți lua pur și simplu un model preantrenat de pe Hugging Face Hub și să îl faceți fine-tune direct pe datele voastre pentru sarcina dată. Cu condiția că corpusul utilizat pentru preantrenare să nu fie prea diferit de corpusul utilizat pentru fine-tuning, învățarea prin transfer va produce de obicei rezultate bune.
Cu toate acestea, există câteva cazuri în care veți dori să faceți fine-tune mai întâi modelelor lingvistice pe datele voastre, înainte de a antrena un head specific sarcinii. De exemplu, dacă datasetul vostru conține contracte juridice sau articole științifice, un model Transformer obișnuit, precum BERT, va trata de obicei cuvintele specifice domeniului din corpus ca pe niște tokeni rari, iar performanța rezultată poate fi mai puțin satisfăcătoare. Prin fine-tuningul modelului lingvistic pe baza datelor din domeniu, puteți crește performanța multor sarcini, ceea ce înseamnă că, de obicei, trebuie să efectuați acest pas o singură dată!
Acest proces de fine-tuning a unui model lingvistic preantrenat pe date din domeniu se numește de obicei _adaptare la domeniu_. Acesta a fost popularizat în 2018 de [ULMFiT](https://arxiv.org/abs/1801.06146), care a fost una dintre primele arhitecturi neuronale (bazate pe LSTM-uri) care a făcut ca învățarea prin transfer să funcționeze cu adevărat pentru NLP. Un exemplu de adaptare la domeniu cu ULMFiT este prezentat în imaginea de mai jos; în această secțiune vom face ceva similar, dar cu un Transformer în loc de un LSTM!
<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/ulmfit.svg" alt="ULMFiT."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/ulmfit-dark.svg" alt="ULMFiT."/>
</div>
Până la sfârșitul acestei secțiuni, veți avea un [model de limbaj mascat](https://huggingface.co/huggingface-course/distilbert-base-uncased-finetuned-imdb?text=This+is+a+great+%5BMASK%5D.) pe Hub, care poate completa automat propoziții, după cum se poate vedea mai jos:
<iframe src="https://course-demos-distilbert-base-uncased-finetuned-imdb.hf.space" frameBorder="0" height="300" title="Gradio app" class="block dark:hidden container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>
Hai să începem!
<Youtube id="mqElG5QJWUg"/>
> [!TIP]
> 🙋 Dacă termenii "masked language modeling" și "pretrained model" nu vă sună familiar, mergeți să verificați [Capitolul 1](/course/chapter1), unde vă explicăm toate aceste concepte de bază, cu videoclipuri!
## Alegerea unui model preantrenat pentru masked language modeling[[picking-a-pretrained-model-for-masked-language-modeling]]
Pentru a începe, să alegem un model preantrenat adecvat pentru modelarea limbajului mascat. După cum se vede în următoarea captură de ecran, puteți găsi o listă de candidați prin aplicarea filtrului "Fill-Mask" pe [Hugging Face Hub](https://huggingface.co/models?pipeline_tag=fill-mask&sort=downloads):
<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/mlm-models.png" alt="Hub models." width="80%"/>
</div>
Deși modelele din familia BERT și RoBERTa sunt cele mai descărcate, vom utiliza un model numit [DistilBERT](https://huggingface.co/distilbert-base-uncased)
care poate fi antrenat mult mai rapid, cu o pierdere mică sau nulă a performanței în aval. Acest model a fost antrenat folosind o tehnică specială numită [_knowledge distillation_](https://en.wikipedia.org/wiki/Knowledge_distillation), în care un "model profesor" mare, precum BERT, este folosit pentru a ghida antrenarea unui "model elev" care are mult mai puțini parametrii. O explicație a detaliilor privind distilarea cunoștințelor ne-ar duce prea departe în această secțiune, dar dacă ești interesat, poți citi totul despre aceasta în [_Natural Language Processing with Transformers_](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/) (cunoscut sub numele colocvial de Transformers textbooks).
{#if fw === 'pt'}
Să continuăm și să descărcăm modelul DistilBERT folosind clasa `AutoModelForMaskedLM`:
```python
from transformers import AutoModelForMaskedLM
model_checkpoint = "distilbert-base-uncased"
model = AutoModelForMaskedLM.from_pretrained(model_checkpoint)
```
Putem vedea câți parametri are acest model prin apelarea metodei `num_parameters()`:
```python
distilbert_num_parameters = model.num_parameters() / 1_000_000
print(f"'>>> DistilBERT number of parameters: {round(distilbert_num_parameters)}M'")
print(f"'>>> BERT number of parameters: 110M'")
```
```python out
'>>> DistilBERT number of parameters: 67M'
'>>> BERT number of parameters: 110M'
```
{:else}
Să continuăm și să descărcăm modelul DistilBERT folosind clasa `TFAutoModelForMaskedLM`:
```python
from transformers import TFAutoModelForMaskedLM
model_checkpoint = "distilbert-base-uncased"
model = TFAutoModelForMaskedLM.from_pretrained(model_checkpoint)
```
Putem vedea câți parametri are acest model prin apelarea metodei `summary()`:
```python
model.summary()
```
```python out
Model: "tf_distil_bert_for_masked_lm"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
distilbert (TFDistilBertMain multiple 66362880
_________________________________________________________________
vocab_transform (Dense) multiple 590592
_________________________________________________________________
vocab_layer_norm (LayerNorma multiple 1536
_________________________________________________________________
vocab_projector (TFDistilBer multiple 23866170
=================================================================
Total params: 66,985,530
Trainable params: 66,985,530
Non-trainable params: 0
_________________________________________________________________
```
{/if}
Cu aproximativ 67 de milioane de parametri, DistilBERT este de aproximativ două ori mai mic decât modelul de bază BERT, ceea ce se traduce aproximativ printr-o creștere de două ori a vitezei de antrenare - super! Să vedem acum ce tipuri de tokeni prezice acest model ca fiind cele mai probabile completări ale unui mic sample de text:
```python
text = "This is a great [MASK]."
```
Ca oameni, ne putem imagina multe posibilități pentru tokenul `[MASK]`, cum ar fi "day", "ride" sau "painting". Pentru modelele preantrenate, predicțiile depind de corpusul pe care modelul a fost antrenat, deoarece acesta învață să detecteze tiparele statistice prezente în date. La fel ca BERT, DistilBERT a fost preantrenat pe dataseturile [English Wikipedia](https://huggingface.co/datasets/wikipedia) și [BookCorpus](https://huggingface.co/datasets/bookcorpus), astfel încât ne așteptăm ca predicțiile pentru `[MASK]` să reflecte aceste domenii. Pentru a prezice masca, avem nevoie de tokenizerul DistilBERT pentru a produce inputurile pentru model, deci hai să-l descărcăm și pe acesta din Hub:
```python
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
```
Cu un tokenizer și un model, putem acum să transmitem exemplul nostru de text modelului, să extragem logiturile și să tipărim primii 5 candidați:
{#if fw === 'pt'}
```python
import torch
inputs = tokenizer(text, return_tensors="pt")
token_logits = model(**inputs).logits
# Find the location of [MASK] and extract its logits
mask_token_index = torch.where(inputs["input_ids"] == tokenizer.mask_token_id)[1]
mask_token_logits = token_logits[0, mask_token_index, :]
# Pick the [MASK] candidates with the highest logits
top_5_tokens = torch.topk(mask_token_logits, 5, dim=1).indices[0].tolist()
for token in top_5_tokens:
print(f"'>>> {text.replace(tokenizer.mask_token, tokenizer.decode([token]))}'")
```
{:else}
```python
import numpy as np
import tensorflow as tf
inputs = tokenizer(text, return_tensors="np")
token_logits = model(**inputs).logits
# Find the location of [MASK] and extract its logits
mask_token_index = np.argwhere(inputs["input_ids"] == tokenizer.mask_token_id)[0, 1]
mask_token_logits = token_logits[0, mask_token_index, :]
# Pick the [MASK] candidates with the highest logits
# We negate the array before argsort to get the largest, not the smallest, logits
top_5_tokens = np.argsort(-mask_token_logits)[:5].tolist()
for token in top_5_tokens:
print(f">>> {text.replace(tokenizer.mask_token, tokenizer.decode([token]))}")
```
{/if}
```python out
'>>> This is a great deal.'
'>>> This is a great success.'
'>>> This is a great adventure.'
'>>> This is a great idea.'
'>>> This is a great feat.'
```
Putem vedea din rezultate că predicțiile modelului se referă la termeni din viața de zi cu zi, ceea ce poate că nu este surprinzător având în vedere fundamentul Wikipedia în limba engleză. Să vedem cum putem schimba acest domeniu în ceva puțin mai nișat - recenzii de filme foarte polarizate!
## Datasetul[[the-dataset]]
Pentru a prezenta adaptarea la domeniu, vom utiliza faimosul [Large Movie Review Dataset] (https://huggingface.co/datasets/imdb) (sau IMDb pe scurt), care este un corpus de recenzii de filme care este adesea utilizat pentru a evalua modelele de analiză a sentimentelor. Prin fine-tuningul aplicat asupra DistilBERT pe acest corpus, ne așteptăm ca modelul de limbaj să își adapteze vocabularul de la datele factuale din Wikipedia pe care a fost antrenat în prealabil la elementele mai subiective ale recenziilor de film. Putem obține datele din Hugging Face Hub cu funcția `load_dataset()` din 🤗 Datasets:
```python
from datasets import load_dataset
imdb_dataset = load_dataset("imdb")
imdb_dataset
```
```python out
DatasetDict({
train: Dataset({
features: ['text', 'label'],
num_rows: 25000
})
test: Dataset({
features: ['text', 'label'],
num_rows: 25000
})
unsupervised: Dataset({
features: ['text', 'label'],
num_rows: 50000
})
})
```
Putem vedea că segmentele `train` și `test` conțin fiecare 25.000 de recenzii, în timp ce există un segment fără label numită `unsupervised` care conține 50.000 de recenzii. Să aruncăm o privire la câteva sampleuri pentru a ne face o idee despre tipul de text cu care avem de-a face. Așa cum am făcut în capitolele anterioare ale cursului, vom combina funcțiile `Dataset.shuffle()` și `Dataset.select()` pentru a crea un sample aleatoriu:
```python
sample = imdb_dataset["train"].shuffle(seed=42).select(range(3))
for row in sample:
print(f"\n'>>> Review: {row['text']}'")
print(f"'>>> Label: {row['label']}'")
```
```python out
'>>> Review: This is your typical Priyadarshan movie--a bunch of loony characters out on some silly mission. His signature climax has the entire cast of the film coming together and fighting each other in some crazy moshpit over hidden money. Whether it is a winning lottery ticket in Malamaal Weekly, black money in Hera Pheri, "kodokoo" in Phir Hera Pheri, etc., etc., the director is becoming ridiculously predictable. Don\'t get me wrong; as clichéd and preposterous his movies may be, I usually end up enjoying the comedy. However, in most his previous movies there has actually been some good humor, (Hungama and Hera Pheri being noteworthy ones). Now, the hilarity of his films is fading as he is using the same formula over and over again.<br /><br />Songs are good. Tanushree Datta looks awesome. Rajpal Yadav is irritating, and Tusshar is not a whole lot better. Kunal Khemu is OK, and Sharman Joshi is the best.'
'>>> Label: 0'
'>>> Review: Okay, the story makes no sense, the characters lack any dimensionally, the best dialogue is ad-libs about the low quality of movie, the cinematography is dismal, and only editing saves a bit of the muddle, but Sam" Peckinpah directed the film. Somehow, his direction is not enough. For those who appreciate Peckinpah and his great work, this movie is a disappointment. Even a great cast cannot redeem the time the viewer wastes with this minimal effort.<br /><br />The proper response to the movie is the contempt that the director San Peckinpah, James Caan, Robert Duvall, Burt Young, Bo Hopkins, Arthur Hill, and even Gig Young bring to their work. Watch the great Peckinpah films. Skip this mess.'
'>>> Label: 0'
'>>> Review: I saw this movie at the theaters when I was about 6 or 7 years old. I loved it then, and have recently come to own a VHS version. <br /><br />My 4 and 6 year old children love this movie and have been asking again and again to watch it. <br /><br />I have enjoyed watching it again too. Though I have to admit it is not as good on a little TV.<br /><br />I do not have older children so I do not know what they would think of it. <br /><br />The songs are very cute. My daughter keeps singing them over and over.<br /><br />Hope this helps.'
'>>> Label: 1'
```
Da, acestea sunt cu siguranță recenzii de film și, dacă sunteți suficient de bătrâni, ați putea chiar înțelege comentariul din ultima recenzie despre deținerea unei versiuni VHS 😜! Deși nu vom avea nevoie de labeluri pentru modelarea limbajului, putem vedea deja că un `0` denotă o recenzie negativă, în timp ce un `1` corespunde uneia pozitive.
> [!TIP]
> ✏️ **Încearcă!** Creați un sample aleatoriu din segmentul `unsupervised` și verificați că labelurile nu sunt nici `0`, nici `1`. În același timp, ați putea verifica și dacă labelurile din segmentele `train` și `test` sunt într-adevăr `0` sau `1` - aceasta este o verificare utilă pe care orice practicant NLP ar trebui să o efectueze la începutul unui nou proiect!
Acum că am aruncat o privire rapidă asupra datelor, să ne apucăm să le pregătim pentru modelarea limbajului mascat. După cum vom vedea, există câteva etape suplimentare pe care trebuie să le parcurgem în comparație cu sarcinile de clasificare a secvențelor pe care le-am văzut în [Capitolul 3](/course/chapter3). Să începem!
## Preprocesarea datelor[[preprocessing-the-data]]
<Youtube id="8PmhEIXhBvI"/>
Atât pentru auto-regressive cât și pentru masked language modeling, un pas comun de preprocesare este concatenarea tuturor exemplelor și apoi împărțirea întregului corpus în bucăți de dimensiuni egale. Acest lucru este destul de diferit de abordarea noastră obișnuită, în care pur și simplu tokenizăm exemplele individuale. De ce să concatenăm totul împreună? Motivul este că exemplele individuale ar putea fi trunchiate dacă sunt prea lungi, ceea ce ar duce la pierderea de informații care ar putea fi utile pentru sarcina de modelare a limbajului!
Deci, pentru a începe, vom tokeniza mai întâi corpusul nostru ca de obicei, dar _fără_ a seta opțiunea `truncation=True` în tokenizerul nostru. De asemenea, vom prelua ID-urile cuvintelor dacă acestea sunt disponibile (ceea ce va fi cazul dacă folosim un tokenizer rapid, așa cum este descris în [Capitolul 6](/course/chapter6/3)), deoarece vom avea nevoie de ele mai târziu pentru a face mascarea întregului cuvânt. Vom include acest lucru într-o funcție simplă și, în același timp, vom elimina coloanele `text` și `label`, deoarece nu mai avem nevoie de ele:
```python
def tokenize_function(examples):
result = tokenizer(examples["text"])
if tokenizer.is_fast:
result["word_ids"] = [result.word_ids(i) for i in range(len(result["input_ids"]))]
return result
# Utilizați batched=True pentru a activa multithreadingul!
tokenized_datasets = imdb_dataset.map(
tokenize_function, batched=True, remove_columns=["text", "label"]
)
tokenized_datasets
```
```python out
DatasetDict({
train: Dataset({
features: ['attention_mask', 'input_ids', 'word_ids'],
num_rows: 25000
})
test: Dataset({
features: ['attention_mask', 'input_ids', 'word_ids'],
num_rows: 25000
})
unsupervised: Dataset({
features: ['attention_mask', 'input_ids', 'word_ids'],
num_rows: 50000
})
})
```
Deoarece DistilBERT este un model de tip BERT, putem vedea că textele codate constau din `input_ids` și `attention_mask` pe care le-am văzut în alte capitole, precum și din `word_ids` pe care le-am adăugat.
Acum că am tokenizat recenziile de filme, următorul pas este să le grupăm pe toate și să împărțim rezultatul în chunkuri. Dar cât de mari ar trebui să fie aceste chunkuri? Acest lucru va fi determinat în cele din urmă de cantitatea de memorie GPU pe care o aveți disponibilă, dar un bun punct de plecare este să vedeți care este dimensiunea maximă a contextului modelului. Aceasta poate fi dedusă prin inspectarea atributului `model_max_length` al tokenizerului:
```python
tokenizer.model_max_length
```
```python out
512
```
Această valoare este derivată din fișierul *tokenizer_config.json* asociat cu un checkpoint; în acest caz putem vedea că dimensiunea contextului este de 512 tokeni, la fel ca în cazul BERT.
> [!TIP]
> ✏️ **Încearcă!** Unele modele Transformer, precum [BigBird](https://huggingface.co/google/bigbird-roberta-base) și [Longformer](hf.co/allenai/longformer-base-4096), au o lungime de context mult mai mare decât BERT și alte modele Transformer mai vechi. Inițializați tokenizerul pentru unul dintre aceste checkpointuri și verificați dacă `model_max_length` este în concordanță cu ceea ce este menționat pe model card.
Prin urmare, pentru a derula experimentele pe GPU-uri precum cele de pe Google Colab, vom alege ceva mai mic care să încapă în memorie:
```python
chunk_size = 128
```
> [!WARNING]
> Rețineți că utilizarea unei dimensiuni mici a chunkurilor poate fi dăunător în scenariile din lumea reală, astfel încât ar trebui să utilizați o dimensiune care corespunde cazului de utilizare la care veți aplica modelul.
Acum vine partea distractivă. Pentru a arăta cum funcționează concatenarea, să luăm câteva recenzii din setul nostru de antrenare tokenizat și să imprimăm numărul de tokeni per recenzie:
```python
# Slicingul produce o listă de liste pentru fiecare caracteristică
tokenized_samples = tokenized_datasets["train"][:3]
for idx, sample in enumerate(tokenized_samples["input_ids"]):
print(f"'>>> Review {idx} length: {len(sample)}'")
```
```python out
'>>> Review 0 length: 200'
'>>> Review 1 length: 559'
'>>> Review 2 length: 192'
```
Putem apoi concatena toate aceste exemple cu un dictionary comprehension, după cum urmează:
```python
concatenated_examples = {
k: sum(tokenized_samples[k], []) for k in tokenized_samples.keys()
}
total_length = len(concatenated_examples["input_ids"])
print(f"'>>> Concatenated reviews length: {total_length}'")
```
```python out
'>>> Concatenated reviews length: 951'
```
Minunat, lungimea totală se verifică - așa că acum să împărțim recenziile concatenate în chunkuri de dimensiunea dată de `chunk_size`. Pentru a face acest lucru, iterăm peste caracteristicile din `concatenated_examples` și folosim un list comprehension pentru a crea slice-uri ale fiecărei caracteristici. Rezultatul este un dicționar de chunkuri pentru fiecare caracteristică:
```python
chunks = {
k: [t[i : i + chunk_size] for i in range(0, total_length, chunk_size)]
for k, t in concatenated_examples.items()
}
for chunk in chunks["input_ids"]:
print(f"'>>> Chunk length: {len(chunk)}'")
```
```python out
'>>> Chunk length: 128'
'>>> Chunk length: 128'
'>>> Chunk length: 128'
'>>> Chunk length: 128'
'>>> Chunk length: 128'
'>>> Chunk length: 128'
'>>> Chunk length: 128'
'>>> Chunk length: 55'
```
După cum puteți vedea în acest exemplu, ultimul fragment va fi în general mai mic decât dimensiunea maximă a fragmentului. Există două strategii principale pentru a face față acestei situații:
* Aruncați ultimul chunk dacă este mai mic decât `chunk_size`.
* Faceți padding ultimului chunk până când lungimea sa este egală cu `chunk_size`.
Vom adopta prima abordare aici, așa că hai să încorporăm toată logica de mai sus într-o singură funcție pe care o putem aplica dataseturilor tokenizate:
```python
def group_texts(examples):
# Concatenarea tuturor textelor
concatenated_examples = {k: sum(examples[k], []) for k in examples.keys()}
# Calcularea lungimii textelor concatenate
total_length = len(concatenated_examples[list(examples.keys())[0]])
# Renunțăm la ultimul chunk dacă este mai mic decât chunk_size
total_length = (total_length // chunk_size) * chunk_size
# Împărțiți pe bucăți de max_len
result = {
k: [t[i : i + chunk_size] for i in range(0, total_length, chunk_size)]
for k, t in concatenated_examples.items()
}
# Creați o nouă coloană de labeluri
result["labels"] = result["input_ids"].copy()
return result
```
Observați că în ultimul pas al `group_texts()` creăm o nouă coloană `labels` care este o copie a coloanei `input_ids`. După cum vom vedea în curând, acest lucru se datorează faptului că în modelarea limbajului mascat obiectivul este de a prezice tokeni mascați aleatoriu în input batch, iar prin crearea unei coloane `labels` furnizăm adevărul de bază din care modelul nostru de limbaj poate să învețe.
Să aplicăm acum funcția `group_texts()` dataseturilor tokenizate folosind funcția noastră de încredere `Dataset.map()`:
```python
lm_datasets = tokenized_datasets.map(group_texts, batched=True)
lm_datasets
```
```python out
DatasetDict({
train: Dataset({
features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
num_rows: 61289
})
test: Dataset({
features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
num_rows: 59905
})
unsupervised: Dataset({
features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
num_rows: 122963
})
})
```
Puteți vedea că gruparea și apoi fragmentarea textelor a produs mult mai multe exemple decât cele 25.000 inițiale pentru spliturile `train` și `test`. Acest lucru se datorează faptului că acum avem exemple care implică _contigous tokens_ care se întind pe mai multe exemple din corpusul original. Puteți vedea acest lucru în mod explicit căutând tokenii speciali `[SEP]` și `[CLS]` într-unul dintre chunkuri:
```python
tokenizer.decode(lm_datasets["train"][1]["input_ids"])
```
```python out
".... at.......... high. a classic line : inspector : i'm here to sack one of your teachers. student : welcome to bromwell high. i expect that many adults of my age think that bromwell high is far fetched. what a pity that it isn't! [SEP] [CLS] homelessness ( or houselessness as george carlin stated ) has been an issue for years but never a plan to help those on the street that were once considered human who did everything from going to school, work, or vote for the matter. most people think of the homeless"
```
În acest exemplu puteți vedea două recenzii de film care se suprapun, una despre un film de liceu și cealaltă despre persoanele fără adăpost. Să verificăm, de asemenea, cum arată labelurile pentru modelarea limbajului mascat:
```python out
tokenizer.decode(lm_datasets["train"][1]["labels"])
```
```python out
".... at.......... high. a classic line : inspector : i'm here to sack one of your teachers. student : welcome to bromwell high. i expect that many adults of my age think that bromwell high is far fetched. what a pity that it isn't! [SEP] [CLS] homelessness ( or houselessness as george carlin stated ) has been an issue for years but never a plan to help those on the street that were once considered human who did everything from going to school, work, or vote for the matter. most people think of the homeless"
```
Așa cum era de așteptat de la funcția noastră `group_texts()` de mai sus, acest lucru pare identic cu `input_ids` decodificat - dar atunci cum poate modelul nostru să învețe ceva? Ne lipsește un pas cheie: inserarea tokenilor `[MASK]` în poziții aleatorii în inputuri! Să vedem cum putem face acest lucru din mers, în timpul fine-tuningului, folosind un data collator special.
## Fine-tuningul asupra DistilBERT cu API-ul `Trainer`[[fine-tuning-distilbert-with-the-trainer-api]]
Fine-tuningul unui model lingvistic mascat este aproape identic cu fine-tuningul a unui model de clasificare a secvențelor, așa cum am făcut în [Capitolul 3](/course/chapter3). Singura diferență este că avem nevoie de un data collator care poate masca aleatoriu o parte dintre tokeni din fiecare batch de texte. Din fericire, 🤗 Transformers vine pregătit cu un `DataCollatorForLanguageModeling` dedicat tocmai pentru această sarcină. Trebuie doar să îi transmitem tokenizerul și un argument `mlm_probability` care specifică ce fracțiune din tokeni trebuie mascată. Vom alege 15%, care este cantitatea utilizată pentru BERT și o alegere comună în literatura de specialitate:
```python
from transformers import DataCollatorForLanguageModeling
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm_probability=0.15)
```
Pentru a vedea cum funcționează mascarea aleatorie, să introducem câteva exemple în data collator. Deoarece se așteaptă la o listă de "dict"-uri, în care fiecare "dict" reprezintă un singur chunk de text continuu, mai întâi iterăm peste dataset înainte de a trimite batchul către data collator. Eliminăm cheia `"word_ids"` pentru acest data collator, deoarece acesta nu o așteaptă:
```python
samples = [lm_datasets["train"][i] for i in range(2)]
for sample in samples:
_ = sample.pop("word_ids")
for chunk in data_collator(samples)["input_ids"]:
print(f"\n'>>> {tokenizer.decode(chunk)}'")
```
```python output
'>>> [CLS] bromwell [MASK] is a cartoon comedy. it ran at the same [MASK] as some other [MASK] about school life, [MASK] as " teachers ". [MASK] [MASK] [MASK] in the teaching [MASK] lead [MASK] to believe that bromwell high\'[MASK] satire is much closer to reality than is " teachers ". the scramble [MASK] [MASK] financially, the [MASK]ful students whogn [MASK] right through [MASK] pathetic teachers\'pomp, the pettiness of the whole situation, distinction remind me of the schools i knew and their students. when i saw [MASK] episode in [MASK] a student repeatedly tried to burn down the school, [MASK] immediately recalled. [MASK]...'
'>>> .... at.. [MASK]... [MASK]... high. a classic line plucked inspector : i\'[MASK] here to [MASK] one of your [MASK]. student : welcome to bromwell [MASK]. i expect that many adults of my age think that [MASK]mwell [MASK] is [MASK] fetched. what a pity that it isn\'t! [SEP] [CLS] [MASK]ness ( or [MASK]lessness as george 宇in stated )公 been an issue for years but never [MASK] plan to help those on the street that were once considered human [MASK] did everything from going to school, [MASK], [MASK] vote for the matter. most people think [MASK] the homeless'
```
Frumos, a funcționat! Putem vedea că tokenul `[MASK]` a fost inserat aleatoriu în diferite locuri din textul nostru. Acestea vor fi tokenii pe care modelul nostru va trebui să le prezică în timpul antrenamentului - iar frumusețea data collatorului este că va introduce aleatoriu tokenul `[MASK]` cu fiecare batch!
> [!TIP]
> ✏️ **Încercați!** Rulați fragmentul de cod de mai sus de mai multe ori pentru a vedea cum se întâmplă mascarea aleatorie în fața ochilor voștri! De asemenea, înlocuiți metoda `tokenizer.decode()` cu `tokenizer.convert_ids_to_tokens()` pentru a vedea că uneori un singur token dintr-un cuvânt dat este mascat, și nu celelalte.
{#if fw === 'pt'}
Un efect secundar al mascării aleatorii este faptul că metricile noastre de evaluare nu vor fi deterministe atunci când folosim `Trainer`, deoarece folosim același data collator pentru seturile de antrenare și testare. Vom vedea mai târziu, când ne vom uita la aplicarea fine-tuningului cu 🤗 Accelerate, cum putem folosi flexibilitatea unei bucle de evaluare personalizate pentru a îngheța caracterul aleatoriu.
{/if}
La antrenarea modelelor pentru modelarea limbajului mascat, o tehnică care poate fi utilizată este mascarea cuvintelor întregi împreună, nu doar a tokenilor individuali. Această abordare se numește _whole word masking_. Dacă dorim să utilizăm mascarea întregului cuvânt, va trebui să construim noi înșine un data collator. Un data collator este doar o funcție care preia o listă de sampleuri și le convertește într-un batch, așa că hai să facem asta acum! Vom utiliza ID-urile cuvintelor calculate mai devreme pentru a realiza o hartă între indicii cuvintelor și tokenii corespunzători, apoi vom decide aleatoriu ce cuvinte să mascăm și vom aplica masca respectivă asupra inputurilor. Rețineți că labelurile sunt toate `-100`, cu excepția celor care corespund cuvintelor mascate.
{#if fw === 'pt'}
```py
import collections
import numpy as np
from transformers import default_data_collator
wwm_probability = 0.2
def whole_word_masking_data_collator(features):
for feature in features:
word_ids = feature.pop("word_ids")
# Create a map between words and corresponding token indices
mapping = collections.defaultdict(list)
current_word_index = -1
current_word = None
for idx, word_id in enumerate(word_ids):
if word_id is not None:
if word_id != current_word:
current_word = word_id
current_word_index += 1
mapping[current_word_index].append(idx)
# Randomly mask words
mask = np.random.binomial(1, wwm_probability, (len(mapping),))
input_ids = feature["input_ids"]
labels = feature["labels"]
new_labels = [-100] * len(labels)
for word_id in np.where(mask)[0]:
word_id = word_id.item()
for idx in mapping[word_id]:
new_labels[idx] = labels[idx]
input_ids[idx] = tokenizer.mask_token_id
feature["labels"] = new_labels
return default_data_collator(features)
```
{:else}
```py
import collections
import numpy as np
from transformers.data.data_collator import tf_default_data_collator
wwm_probability = 0.2
def whole_word_masking_data_collator(features):
for feature in features:
word_ids = feature.pop("word_ids")
# Create a map between words and corresponding token indices
mapping = collections.defaultdict(list)
current_word_index = -1
current_word = None
for idx, word_id in enumerate(word_ids):
if word_id is not None:
if word_id != current_word:
current_word = word_id
current_word_index += 1
mapping[current_word_index].append(idx)
# Randomly mask words
mask = np.random.binomial(1, wwm_probability, (len(mapping),))
input_ids = feature["input_ids"]
labels = feature["labels"]
new_labels = [-100] * len(labels)
for word_id in np.where(mask)[0]:
word_id = word_id.item()
for idx in mapping[word_id]:
new_labels[idx] = labels[idx]
input_ids[idx] = tokenizer.mask_token_id
feature["labels"] = new_labels
return tf_default_data_collator(features)
```
{/if}
În continuare, îl putem încerca pe aceleași sampleuri ca înainte:
```py
samples = [lm_datasets["train"][i] for i in range(2)]
batch = whole_word_masking_data_collator(samples)
for chunk in batch["input_ids"]:
print(f"\n'>>> {tokenizer.decode(chunk)}'")
```
```python out
'>>> [CLS] bromwell high is a cartoon comedy [MASK] it ran at the same time as some other programs about school life, such as " teachers ". my 35 years in the teaching profession lead me to believe that bromwell high\'s satire is much closer to reality than is " teachers ". the scramble to survive financially, the insightful students who can see right through their pathetic teachers\'pomp, the pettiness of the whole situation, all remind me of the schools i knew and their students. when i saw the episode in which a student repeatedly tried to burn down the school, i immediately recalled.....'
'>>> .... [MASK] [MASK] [MASK] [MASK]....... high. a classic line : inspector : i\'m here to sack one of your teachers. student : welcome to bromwell high. i expect that many adults of my age think that bromwell high is far fetched. what a pity that it isn\'t! [SEP] [CLS] homelessness ( or houselessness as george carlin stated ) has been an issue for years but never a plan to help those on the street that were once considered human who did everything from going to school, work, or vote for the matter. most people think of the homeless'
```
> [!TIP]
> ✏️ **Încercați!** Rulați fragmentul de cod de mai sus de mai multe ori pentru a vedea cum se întâmplă mascarea aleatorie în fața ochilor voștri! De asemenea, înlocuiți metoda `tokenizer.decode()` cu `tokenizer.convert_ids_to_tokens()` pentru a vedea că tokenii dintr-un cuvânt dat sunt întotdeauna mascați împreună.
Acum, că avem două data collators, restul pașilor fine-tuning sunt standard. Pregătirea poate dura ceva timp pe Google Colab dacă nu sunteți suficient de norocos să obțineți un GPU P100 mitic 😭, așa că vom reduce mai întâi dimensiunea setului de antrenare la câteva mii de exemple. Nu vă faceți griji, vom obține în continuare un model lingvistic destul de decent! O modalitate rapidă de a reduce sampleurile unui dataset în 🤗 Datasets este prin intermediul funcției `Dataset.train_test_split()` pe care am văzut-o în [Capitolul 5](/course/chapter5):
```python
train_size = 10_000
test_size = int(0.1 * train_size)
downsampled_dataset = lm_datasets["train"].train_test_split(
train_size=train_size, test_size=test_size, seed=42
)
downsampled_dataset
```
```python out
DatasetDict({
train: Dataset({
features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
num_rows: 10000
})
test: Dataset({
features: ['attention_mask', 'input_ids', 'labels', 'word_ids'],
num_rows: 1000
})
})
```
Acest lucru a creat în mod automat noi splituri de `train` și `test`, cu dimensiunea setului de antrenare setată la 10.000 de exemple și a setului de validare la 10% din aceasta - nu ezitați să măriți această valoare dacă aveți un GPU puternic! Următorul lucru pe care trebuie să îl facem este să ne conectăm la Hugging Face Hub. Dacă executați acest cod într-un notebook, puteți face acest lucru cu următoarea funcție utilitară:
```python
from huggingface_hub import notebook_login
notebook_login()
```
care va afișa un widget în care vă puteți introduce credențialele. Alternativ, puteți rula:
```
huggingface-cli login
```
in your favorite terminal and log in there.
{#if fw === 'tf'}
Odată ce ne-am conectat, putem crea dataseturile `tf.data`. Pentru a face acest lucru, vom utiliza metoda `prepare_tf_dataset()`, care utilizează modelul nostru pentru a deduce automat ce coloane ar trebui să intre în dataset. Dacă doriți să controlați exact ce coloane să utilizați, puteți folosi în schimb metoda `Dataset.to_tf_dataset()`. Pentru a simplifica lucrurile, vom utiliza aici doar data collatorul standard, dar puteți încerca și whole word masking collator și puteți compara rezultatele ca un exercițiu:
```python
tf_train_dataset = model.prepare_tf_dataset(
downsampled_dataset["train"],
collate_fn=data_collator,
shuffle=True,
batch_size=32,
)
tf_eval_dataset = model.prepare_tf_dataset(
downsampled_dataset["test"],
collate_fn=data_collator,
shuffle=False,
batch_size=32,
)
```
În continuare, setăm hiperparametrii de antrenare și compilăm modelul nostru. Utilizăm funcția `create_optimizer()` din biblioteca 🤗 Transformers, care ne oferă un optimizator `AdamW` cu o scădere liniară a ratei de învățare. Utilizăm, de asemenea, pierderea încorporată în model, care este cea implicită atunci când nu este specificată nicio pierdere ca argument pentru `compile()`, și setăm precizia de antrenare la `"mixed_float16"`. Rețineți că, dacă utilizați un GPU Colab sau alt GPU care nu are suport accelerat pentru float16, ar trebui probabil să comentați această linie.
În plus, am configurat un `PushToHubCallback` care va salva modelul în Hub după fiecare epocă. Puteți specifica numele repositoriului către care doriți să faceți push cu argumentul `hub_model_id` (în special, va trebui să utilizați acest argument pentru a face push către o organizație). De exemplu, pentru a trimite modelul către organizația [`huggingface-course`](https://huggingface.co/huggingface-course), am adăugat `hub_model_id="huggingface-course/distilbert-finetuned-imdb"`. În mod implicit, repositoriul utilizat va fi în namespaceul vostru și numit după output directory-ul pe care l-ați stabilit, deci în cazul nostru va fi `"lewtun/distilbert-finetuned-imdb"`.
```python
from transformers import create_optimizer
from transformers.keras_callbacks import PushToHubCallback
import tensorflow as tf
num_train_steps = len(tf_train_dataset)
optimizer, schedule = create_optimizer(
init_lr=2e-5,
num_warmup_steps=1_000,
num_train_steps=num_train_steps,
weight_decay_rate=0.01,
)
model.compile(optimizer=optimizer)
# Train in mixed-precision float16
tf.keras.mixed_precision.set_global_policy("mixed_float16")
model_name = model_checkpoint.split("/")[-1]
callback = PushToHubCallback(
output_dir=f"{model_name}-finetuned-imdb", tokenizer=tokenizer
)
```
Acum suntem gata să executăm `model.fit()` - dar înainte de a face acest lucru, să ne uităm pe scurt la _perplexitate_, care este o metrică comună pentru a evalua performanța modelelor de limbaj.
{:else}
Odată ce suntem conectați, putem specifica argumentele pentru `Trainer`:
```python
from transformers import TrainingArguments
batch_size = 64
# Show the training loss with every epoch
logging_steps = len(downsampled_dataset["train"]) // batch_size
model_name = model_checkpoint.split("/")[-1]
training_args = TrainingArguments(
output_dir=f"{model_name}-finetuned-imdb",
overwrite_output_dir=True,
evaluation_strategy="epoch",
learning_rate=2e-5,
weight_decay=0.01,
per_device_train_batch_size=batch_size,
per_device_eval_batch_size=batch_size,
push_to_hub=True,
fp16=True,
logging_steps=logging_steps,
)
```
Aici am modificat câteva dintre opțiunile implicite, inclusiv `logging_steps` pentru a ne asigura că urmărim pierderea de antrenare cu fiecare epocă. De asemenea, am folosit `fp16=True` pentru a activa antrenarea cu precizie mixtă, ceea ce ne oferă un alt impuls vitezei. În mod implicit, `Trainer` va elimina toate coloanele care nu fac parte din metoda `forward()` a modelului. Aceasta înseamnă că, dacă utilizați whole word masking collator, va trebui să setați și `remove_unused_columns=False` pentru a vă asigura că nu pierdem coloana `word_ids` în timpul antrenamentului.
Rețineți că puteți specifica numele repositoriului către care doriți să faceți push cu argumentul `hub_model_id` (în special, va trebui să utilizați acest argument pentru a face push către o organizație). De exemplu, atunci când am făcut push modelului către organizația [`huggingface-course`](https://huggingface.co/huggingface-course), am adăugat `hub_model_id="huggingface-course/distilbert-finetuned-imdb"` la `TrainingArguments`. În mod implicit, repositoriul utilizat va fi în namespaceul vostru și denumit după output directory-ul pe care l-ați stabilit, deci în cazul nostru va fi `"lewtun/distilbert-finetuned-imdb"`.
Acum avem toate ingredientele pentru inițializarea `Trainer`. Aici folosim doar `data_collator` standard, dar puteți încerca whole word masking collator și să comparați rezultatele ca un exercițiu:
```python
from transformers import Trainer
trainer = Trainer(
model=model,
args=training_args,
train_dataset=downsampled_dataset["train"],
eval_dataset=downsampled_dataset["test"],
data_collator=data_collator,
tokenizer=tokenizer,
)
```
Acum suntem gata să rulăm `trainer.train()` - dar înainte de a face acest lucru, să analizăm pe scurt _perplexitatea_, care este o metrică comună de evaluare a performanței modelelor de limbaj.
{/if}
### Perplexity pentru language models[[perplexity-for-language-models]]
<Youtube id="NURcDHhYe98"/>
Spre deosebire de alte sarcini, cum ar fi clasificarea textului sau răspunderea la întrebări, unde ni se oferă un corpus labeled pe care să antrenăm, cu modelarea limbajului nu avem labeluri explicite. Așadar, cum determinăm ce face un model lingvistic bun? La fel ca în cazul funcției de autocorectare din telefon, un model lingvistic bun este unul care atribuie probabilități ridicate propozițiilor corecte din punct de vedere gramatical și probabilități scăzute propozițiilor fără sens. Pentru a vă face o idee mai bună despre cum arată acest lucru, puteți găsi online seturi întregi de "autocorrect fails", în care modelul din telefonul unei persoane a produs niște completări destul de amuzante (și adesea nepotrivite)!
{#if fw === 'pt'}
Presupunând că setul nostru de testare constă în cea mai mare parte din propoziții corecte din punct de vedere gramatical, atunci o modalitate de a măsura calitatea modelului nostru lingvistic este de a calcula probabilitățile pe care le atribuie următorului cuvânt în toate propozițiile din setul de testare. Probabilitatea ridicată indică faptul că modelul nu este "surprins" sau "perplex" de exemplele nevăzute și sugerează că a învățat tiparele gramaticale de bază ale limbii. Există diverse definiții matematice ale perplexității, dar cea pe care o vom utiliza o definește ca the exponential of the cross-entropy loss. Astfel, putem calcula perplexitatea modelului nostru preantrenat utilizând funcția `Trainer.evaluate()` pentru a calcula pierderea de cross-entropy pe setul de testare și apoi luând exponențiala rezultatului:
```python
import math
eval_results = trainer.evaluate()
print(f">>> Perplexity: {math.exp(eval_results['eval_loss']):.2f}")
```
{:else}
Presupunând că setul nostru de testare constă în cea mai mare parte din propoziții corecte din punct de vedere gramatical, atunci o modalitate de a măsura calitatea modelului nostru lingvistic este de a calcula probabilitățile pe care le atribuie următorului cuvânt în toate propozițiile din setul de testare. Probabilitățile ridicate indică faptul că modelul indică faptul că modelul nu este "surprins" sau "perplex" de exemplele nevăzute și sugerează că a învățat modelele de bază ale gramaticii limbii. Există diverse definiții matematice ale perplexității, dar cea pe care o vom folosi o definește ca the exponential of the cross-entropy loss. Astfel, putem calcula perplexitatea modelului nostru preantrenat folosind metoda `model.evaluate()` pentru a calcula pierderea de entropie încrucișată pe setul de testare și apoi luând exponențiala rezultatului:
```python
import math
eval_loss = model.evaluate(tf_eval_dataset)
print(f"Perplexity: {math.exp(eval_loss):.2f}")
```
{/if}
```python out
>>> Perplexity: 21.75
```
Un scor de perplexitate mai mic înseamnă un model lingvistic mai bun, iar aici putem vedea că modelul nostru inițial are o valoare oarecum mare. Să vedem dacă o putem reduce prin fine-tuning! Pentru a face acest lucru, vom rula mai întâi bucla de antrenare:
{#if fw === 'pt'}
```python
trainer.train()
```
{:else}
```python
model.fit(tf_train_dataset, validation_data=tf_eval_dataset, callbacks=[callback])
```
{/if}
și apoi calculați perplexitatea rezultată pe setul de testare ca înainte:
{#if fw === 'pt'}
```python
eval_results = trainer.evaluate()
print(f">>> Perplexity: {math.exp(eval_results['eval_loss']):.2f}")
```
{:else}
```python
eval_loss = model.evaluate(tf_eval_dataset)
print(f"Perplexity: {math.exp(eval_loss):.2f}")
```
{/if}
```python out
>>> Perplexity: 11.32
```
Grozav - aceasta este o reducere destul de mare a perplexității, ceea ce ne spune că modelul a învățat ceva despre domeniul recenziilor de filme!
{#if fw === 'pt'}
Odată ce antrenarea este finalizată, putem trimite cardul modelului cu informațiile de antrenare către Hub (checkpointurile sunt salvate în timpul antrenare):
```python
trainer.push_to_hub()
```
{/if}
> [!TIP]
> ✏️ **Rândul tău!** Rulați antrenamentul de mai sus după schimbarea data collatorului cu whole word masking collator. Obțineți rezultate mai bune?
{#if fw === 'pt'}
În cazul nostru de utilizare, nu a fost nevoie să facem nimic special cu bucla de antrenare, dar în unele cazuri s-ar putea să fie nevoie să implementați o logică personalizată. Pentru aceste aplicații, puteți utiliza 🤗 Accelerate -- să aruncăm o privire!
## Fine-tuningul DistilBERT cu 🤗 Accelerate[[fine-tuning-distilbert-with-accelerate]]
Așa cum am văzut cu `Trainer`, fine-tuningul unui model de limbaj mascat este foarte asemănător cu exemplul de clasificare a textului din [Capitolul 3](/course/chapter3). De fapt, singura subtilitate este utilizarea unui data collator special, pe care l-am abordat mai devreme în această secțiune!
Cu toate acestea, am văzut că `DataCollatorForLanguageModeling` aplică, de asemenea, o mascare aleatorie cu fiecare evaluare, astfel încât vom vedea unele fluctuații în scorurile noastre de perplexitate cu fiecare rulare de antrenament. O modalitate de a elimina această sursă de dezordine este de a aplica mascarea _o singură dată_ pe întregul set de teste și apoi de a utiliza data collatorul implicit din 🤗 Transformers pentru a colecta batch-urile în timpul evaluării. Pentru a vedea cum funcționează acest lucru, să implementăm o funcție simplă care aplică mascarea pe un batch, similară cu prima noastră întâlnire cu `DataCollatorForLanguageModeling`:
```python
def insert_random_mask(batch):
features = [dict(zip(batch, t)) for t in zip(*batch.values())]
masked_inputs = data_collator(features)
# Create a new "masked" column for each column in the dataset
return {"masked_" + k: v.numpy() for k, v in masked_inputs.items()}
```
În continuare, vom aplica această funcție setului nostru de testare și vom elimina coloanele nemascate pentru a le putea înlocui cu cele mascate. Puteți utiliza whole word masking prin înlocuirea `data_collator` de mai sus cu cel corespunzător, caz în care trebuie să eliminați prima linie de aici:
```py
downsampled_dataset = downsampled_dataset.remove_columns(["word_ids"])
eval_dataset = downsampled_dataset["test"].map(
insert_random_mask,
batched=True,
remove_columns=downsampled_dataset["test"].column_names,
)
eval_dataset = eval_dataset.rename_columns(
{
"masked_input_ids": "input_ids",
"masked_attention_mask": "attention_mask",
"masked_labels": "labels",
}
)
```
Putem configura apoi dataloaderele ca de obicei, dar vom folosi `default_data_collator` de la 🤗 Transformers pentru setul de evaluare:
```python
from torch.utils.data import DataLoader
from transformers import default_data_collator
batch_size = 64
train_dataloader = DataLoader(
downsampled_dataset["train"],
shuffle=True,
batch_size=batch_size,
collate_fn=data_collator,
)
eval_dataloader = DataLoader(
eval_dataset, batch_size=batch_size, collate_fn=default_data_collator
)
```
De aici, vom urma pașii standard cu 🤗 Accelerate. În primul rând, se încarcă o versiune nouă a modelului antrenat:
```
model = AutoModelForMaskedLM.from_pretrained(model_checkpoint)
```
Apoi trebuie să specificăm optimizatorul; vom folosi standardul `AdamW`:
```python
from torch.optim import AdamW
optimizer = AdamW(model.parameters(), lr=5e-5)
```
Cu aceste obiecte, acum putem pregăti totul pentru antrenare cu obiectul `Accelerator`:
```python
from accelerate import Accelerator
accelerator = Accelerator()
model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
model, optimizer, train_dataloader, eval_dataloader
)
```
Acum că modelul, optimizatorul și dataloaderul sunt configurate, putem specifica learning rate schedulerul cum urmează:
```python
from transformers import get_scheduler
num_train_epochs = 3
num_update_steps_per_epoch = len(train_dataloader)
num_training_steps = num_train_epochs * num_update_steps_per_epoch
lr_scheduler = get_scheduler(
"linear",
optimizer=optimizer,
num_warmup_steps=0,
num_training_steps=num_training_steps,
)
```
Mai este un singur lucru de făcut înainte de antrenare: creați un repositoriu de modele pe Hugging Face Hub! Putem utiliza biblioteca 🤗 Hub pentru a genera mai întâi numele complet al repositoriul nostru:
```python
from huggingface_hub import get_full_repo_name
model_name = "distilbert-base-uncased-finetuned-imdb-accelerate"
repo_name = get_full_repo_name(model_name)
repo_name
```
```python out
'lewtun/distilbert-base-uncased-finetuned-imdb-accelerate'
```
apoi creați și clonați repositoriul folosind clasa `Repository` din 🤗 Hub:
```python
from huggingface_hub import Repository
output_dir = model_name
repo = Repository(output_dir, clone_from=repo_name)
```
Odată ce acest lucru este făcut, trebuie doar să scriem ciclul complet de antrenare și evaluare:
```python
from tqdm.auto import tqdm
import torch
import math
progress_bar = tqdm(range(num_training_steps))
for epoch in range(num_train_epochs):
# Training
model.train()
for batch in train_dataloader:
outputs = model(**batch)
loss = outputs.loss
accelerator.backward(loss)
optimizer.step()
lr_scheduler.step()
optimizer.zero_grad()
progress_bar.update(1)
# Evaluation
model.eval()
losses = []
for step, batch in enumerate(eval_dataloader):
with torch.no_grad():
outputs = model(**batch)
loss = outputs.loss
losses.append(accelerator.gather(loss.repeat(batch_size)))
losses = torch.cat(losses)
losses = losses[: len(eval_dataset)]
try:
perplexity = math.exp(torch.mean(losses))
except OverflowError:
perplexity = float("inf")
print(f">>> Epoch {epoch}: Perplexity: {perplexity}")
# Save and upload
accelerator.wait_for_everyone()
unwrapped_model = accelerator.unwrap_model(model)
unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
if accelerator.is_main_process:
tokenizer.save_pretrained(output_dir)
repo.push_to_hub(
commit_message=f"Training in progress epoch {epoch}", blocking=False
)
```
```python out
>>> Epoch 0: Perplexity: 11.397545307900472
>>> Epoch 1: Perplexity: 10.904909330983092
>>> Epoch 2: Perplexity: 10.729503505340409
```
Mișto, am reușit să evaluăm perplexitatea cu fiecare epocă și să ne asigurăm că mai multe runde de antrenament sunt reproductibile!
{/if}
## Utilizarea modelului fine-tuned[[using-our-fine-tuned-model]]
Puteți interacționa cu modelul vostru fine-tuned utilizând widgetul său de pe Hub, fie local cu `pipeline` din 🤗 Transformers. Să folosim aceasta din urmă pentru a descărca modelul nostru folosind pipelineuul `fill-mask`:
```python
from transformers import pipeline
mask_filler = pipeline(
"fill-mask", model="huggingface-course/distilbert-base-uncased-finetuned-imdb"
)
```
Putem apoi să alimentăm pipelineul cu exemplul nostru de text "This is a hrea [MASK]" și să vedem care sunt primele 5 predicții:
```python
preds = mask_filler(text)
for pred in preds:
print(f">>> {pred['sequence']}")
```
```python out
'>>> this is a great movie.'
'>>> this is a great film.'
'>>> this is a great story.'
'>>> this is a great movies.'
'>>> this is a great character.'
```
Frumos - modelul nostru și-a adaptat în mod clar weighturile pentru a prezice cuvintele care sunt asociate mai puternic cu filmele!
<Youtube id="0Oxphw4Q9fo"/>
Acest lucru încheie primul nostru experiment de antrenare a unui model lingvistic. În [secțiunea 6](/course/ro/chapter7/6) veți învăța cum să antrenați de la zero un model auto-regressive precum GPT-2; mergeți acolo dacă doriți să vedeți cum vă puteți preantrena propriul model Transformer!
> [!TIP]
> ✏️ **Încercați!** Pentru a cuantifica beneficiile adaptării domeniului, faceți fine-tune unui clasificator pe labelurile IMDb atât pentru checkpointurile DistilBERT preantrenate, cât și pentru cele fine-tuned. Dacă aveți nevoie de o recapitulare a clasificării textului, consultați [Capitolul 3](/course/chapter3).
<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter7/3.mdx" />

Xet Storage Details

Size:
55 kB
·
Xet hash:
58d81fa6630389bc4311d1cd7c050a755698e12b827ccb80792ed97bc029a00e

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.