# Course

## Docs

- [Quiz de sfârșit de capitol[[end-of-chapter-quiz]]](https://huggingface.co/learn/course/ro/chapter7/9.md)
- [Sumarizare[[summarization]]](https://huggingface.co/learn/course/ro/chapter7/5.md)
- [Introducere[[introduction]]](https://huggingface.co/learn/course/ro/chapter7/1.md)
- [Fine-tuningul la un masked language model[[fine-tuning-a-masked-language-model]]](https://huggingface.co/learn/course/ro/chapter7/3.md)
- [Antrenarea de la zero a unui model de limbaj cauzal[[training-a-causal-language-model-from-scratch]]](https://huggingface.co/learn/course/ro/chapter7/6.md)
- [Înțelegerea LLM-urilor[[mastering-llms]]](https://huggingface.co/learn/course/ro/chapter7/8.md)
- [Traducere[[translation]]](https://huggingface.co/learn/course/ro/chapter7/4.md)
- [Răspuns la întrebări[[question-answering]]](https://huggingface.co/learn/course/ro/chapter7/7.md)
- [Clasificarea tokenilor[[token-classification]]](https://huggingface.co/learn/course/ro/chapter7/2.md)
- [Partea 1 este completă!](https://huggingface.co/learn/course/ro/chapter4/5.md)
- [Platforma Hugging Face Hub[[the-hugging-face-hub]]](https://huggingface.co/learn/course/ro/chapter4/1.md)
- [Partajarea modelelor preantrenate[[sharing-pretrained-models]]](https://huggingface.co/learn/course/ro/chapter4/3.md)
- [Quiz la final de capitol[[end-of-chapter-quiz]]](https://huggingface.co/learn/course/ro/chapter4/6.md)
- [Crearea unui card de model[[building-a-model-card]]](https://huggingface.co/learn/course/ro/chapter4/4.md)
- [Utilizarea modelelor preantrenate[[using-pretrained-models]]](https://huggingface.co/learn/course/ro/chapter4/2.md)
- [Exercițiu Practic: Ajustează fin un model cu GRPO](https://huggingface.co/learn/course/ro/chapter12/5.md)
- [Open R1 pentru Studenți](https://huggingface.co/learn/course/ro/chapter12/1.md)
- [Înțelegerea Lucrării DeepSeek R1](https://huggingface.co/learn/course/ro/chapter12/3.md)
- [Exercițiu Practic: GRPO cu Unsloth](https://huggingface.co/learn/course/ro/chapter12/6.md)
- [Înțelegerea Avansată a Optimizării Relative a Politicii de Grup (GRPO) în DeepSeekMath](https://huggingface.co/learn/course/ro/chapter12/3a.md)
- [Implementarea GRPO în TRL](https://huggingface.co/learn/course/ro/chapter12/4.md)
- [În curând...](https://huggingface.co/learn/course/ro/chapter12/7.md)
- [Introducere în Învățarea prin Întărire și rolul acesteia în LLM-uri](https://huggingface.co/learn/course/ro/chapter12/2.md)
- [Fine-tuning, Check![[fine-tuning-check]]](https://huggingface.co/learn/course/ro/chapter3/5.md)
- [Introducere[[introducere]]](https://huggingface.co/learn/course/ro/chapter3/1.md)
- [Ajustarea unui model folosind Trainer API[[ajustarea-unui-model-folosind-trainer-api]]](https://huggingface.co/learn/course/ro/chapter3/3.md)
- [End-of-chapter quiz[[end-of-chapter-quiz]]](https://huggingface.co/learn/course/ro/chapter3/6.md)
- [Ajustarea unui model cu Keras[[ajustarea-unui-model-cu-keras]]](https://huggingface.co/learn/course/ro/chapter3/3_tf.md)
- [O instruire completă[[o-instruire-completă]]](https://huggingface.co/learn/course/ro/chapter3/4.md)
- [Prelucrarea datelor[[prelucrarea-datelor]]](https://huggingface.co/learn/course/ro/chapter3/2.md)
- [Folosiți setul de date adnotat[[folosiți-setul-de-date-adnotat]]](https://huggingface.co/learn/course/ro/chapter10/5.md)
- [Introducere în Argilla[[introducere-în-argilla]]](https://huggingface.co/learn/course/ro/chapter10/1.md)
- [Încărcați setul de date în Argilla[[încărcați-setul-de-date-în-argilla]]](https://huggingface.co/learn/course/ro/chapter10/3.md)
- [Argilla, terminat![[argilla-terminat]]](https://huggingface.co/learn/course/ro/chapter10/6.md)
- [Adnotați setul de date[[adnotați-setul-de-date]]](https://huggingface.co/learn/course/ro/chapter10/4.md)
- [Quiz de final de capitol[[quiz-de-final-de-capitol]]](https://huggingface.co/learn/course/ro/chapter10/7.md)
- [Configurați-vă instanța Argilla[[configurați-vă-instanța-argilla]]](https://huggingface.co/learn/course/ro/chapter10/2.md)
- [Cum să scrieți un issue bun[[cum-sa-scrieti-un-issue-bun]]](https://huggingface.co/learn/course/ro/chapter8/5.md)
- [Introducere[[introducere]]](https://huggingface.co/learn/course/ro/chapter8/1.md)
- [Cum să ceri ajutor pe forumuri[[cum-sa-ceri-ajutor-pe-forumuri]]](https://huggingface.co/learn/course/ro/chapter8/3.md)
- [Partea 2 completă![[partea-2-completa]]](https://huggingface.co/learn/course/ro/chapter8/6.md)
- [Debugging-ul pipeline-ului de antrenament[[debugging-pipeline-ului-de-antrenament]]](https://huggingface.co/learn/course/ro/chapter8/4.md)
- [Quiz de sfârșit de capitol[[quiz-de-sfarsit-de-capitol]]](https://huggingface.co/learn/course/ro/chapter8/7.md)
- [Debugging-ul pipeline-ului de antrenament[[debugging-pipeline-ului-de-antrenament]]](https://huggingface.co/learn/course/ro/chapter8/4_tf.md)
- [Ce să faci când primești o eroare[[ce-sa-fac-cand-primesti-o-eroare]]](https://huggingface.co/learn/course/ro/chapter8/2.md)
- [Quiz de final de capitol[[end-of-chapter-quiz]]](https://huggingface.co/learn/course/ro/chapter9/9.md)
- [Integrări cu Hugging Face Hub[[integrations-with-the-hugging-face-hub]]](https://huggingface.co/learn/course/ro/chapter9/5.md)
- [Introducere în Gradio[[introduction-to-gradio]]](https://huggingface.co/learn/course/ro/chapter9/1.md)
- [Înțelegerea clasei Interface[[understanding-the-interface-class]]](https://huggingface.co/learn/course/ro/chapter9/3.md)
- [Caracteristici avansate ale Interface[[advanced-interface-features]]](https://huggingface.co/learn/course/ro/chapter9/6.md)
- [Gradio, verificat![[gradio-check]]](https://huggingface.co/learn/course/ro/chapter9/8.md)
- [Partajarea demo-urilor cu alții[[sharing-demos-with-others]]](https://huggingface.co/learn/course/ro/chapter9/4.md)
- [Introducere în Gradio Blocks[[introduction-to-gradio-blocks]]](https://huggingface.co/learn/course/ro/chapter9/7.md)
- [Construirea primului tău demo[[building-your-first-demo]]](https://huggingface.co/learn/course/ro/chapter9/2.md)
- [Sesiuni live și workshop-uri[[sesiuni-live-si-workshop-uri]]](https://huggingface.co/learn/course/ro/events/1.md)
- [Petrecerea Gradio Blocks[[petrecerea-gradio-blocks]]](https://huggingface.co/learn/course/ro/events/3.md)
- [Evenimentul de lansare a părții 2[[evenimentul-de-lansare-a-partii-2]]](https://huggingface.co/learn/course/ro/events/2.md)
- [Tokenizeri, verificare![[tokenizers-check]]](https://huggingface.co/learn/course/ro/chapter6/9.md)
- [Tokenizarea Byte-Pair Encoding[[byte-pair-encoding-tokenization]]](https://huggingface.co/learn/course/ro/chapter6/5.md)
- [Introducere[[introducere]]](https://huggingface.co/learn/course/ro/chapter6/1.md)
- [Superputerile tokenizerilor rapizi[[fast-tokenizers-special-powers]]](https://huggingface.co/learn/course/ro/chapter6/3.md)
- [Tokenizarea WordPiece[[wordpiece-tokenization]]](https://huggingface.co/learn/course/ro/chapter6/6.md)
- [Quiz de sfârșit de capitol[[end-of-chapter-quiz]]](https://huggingface.co/learn/course/ro/chapter6/10.md)
- [Construirea unui tokenizer, bloc cu bloc[[building-a-tokenizer-block-by-block]]](https://huggingface.co/learn/course/ro/chapter6/8.md)
- [Normalizare și pre-tokenizare[[normalization-and-pre-tokenization]]](https://huggingface.co/learn/course/ro/chapter6/4.md)
- [Tokenizarea Unigram[[unigram-tokenization]]](https://huggingface.co/learn/course/ro/chapter6/7.md)
- [Tokenizerii rapizi în pipeline-ul de QA[[fast-tokenizers-in-the-qa-pipeline]]](https://huggingface.co/learn/course/ro/chapter6/3b.md)
- [Antrenarea unui nou tokenizer dintr-unul vechi[[training-a-new-tokenizer-from-an-old-one]]](https://huggingface.co/learn/course/ro/chapter6/2.md)
- [Evaluarea](https://huggingface.co/learn/course/ro/chapter11/5.md)
- [Fine-tuning supervizat](https://huggingface.co/learn/course/ro/chapter11/1.md)
- [Fine-tuning supervizat](https://huggingface.co/learn/course/ro/chapter11/3.md)
- [Concluzie](https://huggingface.co/learn/course/ro/chapter11/6.md)
- [LoRA (Adaptarea de rang scăzut)](https://huggingface.co/learn/course/ro/chapter11/4.md)
- [E timpul examenului!](https://huggingface.co/learn/course/ro/chapter11/7.md)
- [Template-uri de chat](https://huggingface.co/learn/course/ro/chapter11/2.md)
- [Crearea propriului tău dataset[[creating-your-own-dataset]]](https://huggingface.co/learn/course/ro/chapter5/5.md)
- [Introducere[[introduction]]](https://huggingface.co/learn/course/ro/chapter5/1.md)
- [E timpul să tăiem și să analizăm datele[[time-to-slice-and-dice]]](https://huggingface.co/learn/course/ro/chapter5/3.md)
- [Căutare semantică cu FAISS[[semantic-search-with-faiss]]](https://huggingface.co/learn/course/ro/chapter5/6.md)
- [Quiz de final de capitol[[end-of-chapter-quiz]]](https://huggingface.co/learn/course/ro/chapter5/8.md)
- [Big data? 🤗 Datasets vine în ajutor![[big-data-datasets-to-the-rescue]]](https://huggingface.co/learn/course/ro/chapter5/4.md)
- [🤗 Datasets, verificare![[datasets-check]]](https://huggingface.co/learn/course/ro/chapter5/7.md)
- [Ce fac dacă dataset-ul meu nu este pe Hub?[[what-if-my-dataset-isnt-on-the-hub]]](https://huggingface.co/learn/course/ro/chapter5/2.md)
- [Quiz la final de capitol [[quiz-la-final-de-capitol]]](https://huggingface.co/learn/course/ro/chapter2/9.md)
- [Gestionarea secvențelor multiple[[gestionarea-secvențelor-multiple]]](https://huggingface.co/learn/course/ro/chapter2/5.md)
- [Introducere[[introducere]]](https://huggingface.co/learn/course/ro/chapter2/1.md)
- [Modele[[modele]]](https://huggingface.co/learn/course/ro/chapter2/3.md)
- [Să punem totul cap la cap[[să-punem-totul-cap-la-cap]]](https://huggingface.co/learn/course/ro/chapter2/6.md)
- [Implementarea Optimizată a Inferenței](https://huggingface.co/learn/course/ro/chapter2/8.md)
- [Tokenizatoare[[tokenizatoare]]](https://huggingface.co/learn/course/ro/chapter2/4.md)
- [Utilizarea de bază este completă![[utilizarea-de-bază-este-completă]]](https://huggingface.co/learn/course/ro/chapter2/7.md)
- [În spatele pipeline-ului[[în-spatele-pipeline-ului]]](https://huggingface.co/learn/course/ro/chapter2/2.md)
- [Introducere[[introducere]]](https://huggingface.co/learn/course/ro/chapter0/1.md)
- [Sumar[[sumar]]](https://huggingface.co/learn/course/ro/chapter1/9.md)
- [Modele Encoder[[modele-encoder]]](https://huggingface.co/learn/course/ro/chapter1/5.md)
- [Introducere[[introducere]]](https://huggingface.co/learn/course/ro/chapter1/1.md)
- [Ce pot face modelele Transformer[[ce-pot-face-modelele-transformer]]](https://huggingface.co/learn/course/ro/chapter1/3.md)
- [Modele Decoder[[modele-decoder]]](https://huggingface.co/learn/course/ro/chapter1/6.md)
- [Quiz de sfârșit de capitol[[test-de-sfârșit-de-capitol]]](https://huggingface.co/learn/course/ro/chapter1/10.md)
- [Prejudecăți și limitări[[prejudecăți-și-limitări]]](https://huggingface.co/learn/course/ro/chapter1/8.md)
- [Cum funcționează Transformers?[[cum-funcționează-transformers]]](https://huggingface.co/learn/course/ro/chapter1/4.md)
- [Modele Sequence-to-sequence[modele-sequence-to-sequence]](https://huggingface.co/learn/course/ro/chapter1/7.md)
- [Procesarea limbajului natural[[procesarea-limbajului-natural]]](https://huggingface.co/learn/course/ro/chapter1/2.md)

### Quiz de sfârșit de capitol[[end-of-chapter-quiz]]
https://huggingface.co/learn/course/ro/chapter7/9.md

# Quiz de sfârșit de capitol[[end-of-chapter-quiz]]

<CourseFloatingBanner
    chapter={7}
    classNames="absolute z-10 right-0 top-0"
/>

Să testăm ce ați învățat în acest capitol!

### 1. Care dintre următoarele sarcini pot fi încadrate ca o problemă de clasificare a tokenilor?

<Question
	choices={[
		{
			text: "Găsirea componentelor gramaticale ale unei propoziții.",
			explain: "Corect! Apoi putem eticheta fiecare cuvânt ca substantiv, verb etc.",
			correct: true
		},
		{
			text: "Verificarea dacă o propoziție este corectă gramatical sau nu.",
			explain: "Nu, aceasta este o problemă de clasificare a secvențelor."
		},
		{
			text: "Găsirea persoanelor menționate într-o propoziție.",
			explain: "Corect! Putem eticheta fiecare cuvânt ca persoană sau nu ca o persoană.",
            correct: true
		},
        {
			text: "Găsirea grupul de cuvinte dintr-o propoziție care răspunde la o întrebare.",
			explain: "Nu, aceasta ar fi o problemă de răspundere a unei întrebări."
		}
	]}
/>

### 2. Ce parte a preprocesării pentru clasificarea tokenilor diferă de celelalte pipelineuri de preprocesare?

<Question
	choices={[
		{
			text: "Nu este nevoie să faceți nimic; textele sunt deja tokenizate.",
			explain: "Textele sunt într-adevăr date ca cuvinte separate, dar tot trebuie să aplicăm modelul de tokenizare a sub-cuvintelor."
		},
		{
			text: "Textele sunt date sub formă de cuvinte, astfel încât trebuie să aplicăm doar tokenizarea sub-cuvintelor.",
			explain: "Corect! Acest lucru este diferit de preprocesarea obișnuită, în care trebuie să aplicăm întregul proces de tokenizare. Vă puteți gândi la o altă diferență?",
			correct: true
		},
		{
			text: "Folosim <code>-100</code> pentru a eticheta tokenii speciali.",
			explain: "Acest lucru nu este specific clasificării tokenilor - folosim întotdeauna <code>-100</code> ca label pentru tokenii pe care dorim să îi ignorăm în pierdere."
		},
		{
			text: "Trebuie să ne asigurăm că labelurile sunt trunchiate sau padded la aceeași dimensiune ca și inputurile, atunci când aplicăm trunchierea/paddingul.",
			explain: "Într-adevăr! Totuși, aceasta nu este singura diferență.",
			correct: true
		}
	]}
/>

### 3. Ce problemă apare atunci când tokenizăm cuvintele într-o problemă de clasificare a tokenilor și dorim să etichetăm tokenii?

<Question
	choices={[
		{
			text: "Tokenizerul adaugă tokeni speciali și nu avem labeluri pentru ele.",
			explain: "Le etichetăm pe acestea cu <code>-100</code>, astfel încât acestea să fie ignorate în pierdere."
		},
		{
			text: "Fiecare cuvânt poate produce mai mulți tokeni, astfel încât ajungem să avem mai multe tokeni decât labeluri.",
			explain: "Aceasta este problema principală, iar noi trebuie să aliniem labelurile originale cu tokenii.",
			correct: true
		},
		{
			text: "Tokenii adăugați nu au etichete, deci nu există nicio problemă.",
			explain: "Incorect; avem nevoie de atâtea etichete câți tokeni avem, altfel modelele noastre vor da erori."
		}
	]}
/>

### 4. Ce înseamnă "domain adaptation"??

<Question
	choices={[
		{
			text: "Este atunci când rulăm un model pe un dataset și obținem predicțiile pentru fiecare sample din acel dataset.",
			explain: "Nu, aceasta este doar o inferență."
		},
		{
			text: "Este atunci când antrenăm un model pe un dataset.",
			explain: "Nu, acesta este antrenarea unui model; nu există nicio adaptare aici."
		},
		{
			text: "Este atunci când facem fine-tune unui model preantrenat pe un nou dataset, iar acesta oferă predicții care sunt mai bine adaptate la datasetul respectiv",
			explain: "Corect! Modelul și-a adaptat cunoștințele la noul dataset.",
            correct: true
		},
        {
			text: "Este atunci când adăugăm sampleuri neclasificate la un dataset pentru a face modelul nostru mai robust.",
			explain: "Cu siguranță este ceva ce ar trebui să faceți dacă vă reantrenați modelul în mod regulat, dar nu este o adaptare la domeniu."
		}
	]}
/>

### 5. Ce sunt labelurile într-o problemă de modelare a limbajului mascat?

<Question
	choices={[
		{
			text: "Unii dintre tokenii din propoziția de intrare sunt mascate aleatoriu, iar labelurile sunt tokenii de input originali.",
			explain: "Corect!",
            correct: true
		},
		{
			text: "Unii dintre tokenii din propoziția de intrare sunt mascate aleatoriu, iar labelurile sunt tokenii de input originali, deplasate spre stânga.",
			explain: "Nu, deplasarea labelurilor spre stânga corespunde prezicerii următorului cuvânt, ceea ce este modelarea cauzală a limbajului."
		},
		{
			text: "Unele dintre tokenii din propoziția de intrare sunt mascate aleatoriu, iar labelul este dacă propoziția este pozitivă sau negativă.",
			explain: "Aceasta este o problemă de clasificare a secvențelor cu o anumită creștere a datelor, nu de modelare a limbajului mascat."
		},
        {
			text: "Unii dintre tokenii din cele două propoziții de intrare sunt mascate aleatoriu, iar labelul este dacă cele două propoziții sunt similare sau nu.",
			explain: "Aceasta este o problemă de clasificare a secvențelor cu o anumită creștere a datelor, nu de modelare a limbajului mascat."
		}
	]}
/>

### 6. Care dintre aceste sarcini poate fi văzută ca o problemă de sequence-to-sequence?

<Question
	choices={[
		{
			text: "Scrierea de recenzii scurte ale documentelor lungi",
			explain: "Da, aceasta este o problemă de sumarizare. Încercați un alt răspuns!",
            correct: true
		},
		{
			text: "Răspunderea întrebărilor despre un document",
			explain: "Acest lucru poate fi încadrat ca o problemă de sequence-to-sequence. Totuși, acesta nu este singurul răspuns corect.",
            correct: true
		},
		{
			text: "Traducerea unui text din chineză în engleză",
			explain: "Aceasta este cu siguranță o problemă de sequence-to-sequence. Mai puteți găsi alta?",
            correct: true
		},
        {
			text: "Corectarea mesajelor trimise de nepotul/prietenul meu pentru ca acestea să fie în limba engleză, scrise gramatical",
			explain: "Aceasta este un fel de problemă de traducere, deci cu siguranță o sarcină de la sequence-to-sequence. Totuși, acesta nu este singurul răspuns corect!",
			correct: true
		}
	]}
/>

### 7. Care este modalitatea corectă de preprocesare a datelor pentru o problemă de sequence-to-sequence?

<Question
	choices={[
		{
			text: "Inputurile și targeturile trebuie trimise împreună la tokenizer cu <code>inputs=...</code> și <code>targets=...</code>.",
			explain: "Acesta ar putea fi un API pe care îl vom adăuga în viitor, dar nu este disponibil acum."
		},
		{
			text: "Inputurile și targeturile trebuie preprocesate, în două apeluri separate către tokenizer.",
			explain: "Acest lucru este adevărat, dar incomplet. Trebuie să faceți ceva pentru a vă asigura că tokenizerul le procesează pe ambele în mod corespunzător."
		},
		{
			text: "Ca de obicei, trebuie doar să tokenizăm inputurile.",
			explain: "Nu într-o problemă de clasificare a secvențelor; targeturile sunt de asemenea texte pe care trebuie să le convertim în numere!"
		},
        {
			text: "Inputurile trebuie să fie trimise către tokenizer, la fel și targeturile, dar în cadrul unui manager de context special.",
			explain: "Corect, tokenizerul trebuie să fie pus în target mode de către acel context manager.",
			correct: true
		}
	]}
/>

{#if fw === 'pt'}

### 8. De ce există o subclasă specifică a `Trainer` pentru problemele sequence-to-sequence?

<Question
	choices={[
		{
			text: "Deoarece problemele de sequence-to-sequence utilizează o pierdere personalizată, pentru a ignora labelurile setate la <code>-100</code>",
			explain: "Aceasta nu este deloc o pierdere personalizată, ci modul în care pierderea este întotdeauna calculată."
		},
		{
			text: "Deoarece problemele de sequence-to-sequence la secvență necesită o buclă de evaluare specială",
			explain: "Acest lucru este corect. Predicțiile modelelor sequence-to-sequence sunt de obicei rulate cu metoda <code>generate()</code>.",
			correct: true
		},
		{
			text: "Deoarece targeturile sunt texte în probleme sequence-to-sequence",
			explain: "<code>Trainer</code>-ului nu prea îi pasă de asta, deoarece acestea au fost preprocesate înainte."
		},
        {
			text: "Deoarece folosim două modele în problemele sequence-to-sequence",
			explain: "Într-un fel, folosim două modele, un codificator și un decodificator, dar acestea sunt grupate într-un singur model."
		}
	]}
/>

{:else}

### 9. De ce este adesea inutil să se specifice o pierdere atunci când se apelează `compile()` pe un model Transformer?

<Question
	choices={[
		{
			text: "Deoarece modelele Transformer sunt antrenate cu unsupervised learning",
			explain: "Nu chiar - chiar și unsupervised learning are nevoie de o funcție de pierdere!"
		},
		{
			text: "Deoarece, în mod prestabilit, se utilizează valoarea pierderilor interne ale modelului",
			explain: "Corect!",
			correct: true
		},
		{
			text: "Deoarece calculăm metricele după antrenare",
			explain: "Facem adesea acest lucru, dar nu explică de unde obținem valoarea pierderii pe care o optimizăm în formare."
		},
        {
			text: "Deoarece pierderea este specificată în `model.fit()`",
			explain: "Nu, funcția de pierdere este întotdeauna fixată odată ce rulați `model.compile()`, și nu poate fi modificată în `model.fit()`."
		}
	]}
/>

{/if}

### 10. Când ar trebui să preantrenați un model nou?

<Question
	choices={[
		{
			text: "Atunci când nu există un model preantrenat disponibil pentru limba dorită.",
			explain: "Corect.",
			correct: true
		},
		{
			text: "Atunci când aveți o mulțime de date disponibile, chiar dacă există un model preantrenat care ar putea funcționa pe ele",
			explain: "În acest caz, ar trebui, probabil, să utilizați modelul preantrenat și să îi faceți fine-tune pe datele voastre, pentru a evita costurile uriașe de calcul."
		},
		{
			text: "Atunci când aveți îndoieli cu privire la biasul modelului preantrenat pe care îl utilizați",
			explain: "Este adevărat, dar trebuie să vă asigurați că datele pe care le veți utiliza pentru antrenare sunt cu adevărat mai bune.",
			correct: true
		},
        {
			text: "Atunci când modelele preantrenate disponibile nu sunt suficient de bune",
			explain: "Sunteți sigur că ați făcut debbuging în mod corespunzător antrenării?"
		}
	]}
/>

### 11. De ce este ușor să preantrenăm un model lingvistic pe o mulțime de texte?

<Question
	choices={[
		{
			text: "Deoarece există o mulțime de texte disponibile pe internet",
			explain: "Deși este adevărat, acest lucru nu răspunde cu adevărat la întrebare. Încercați din nou!"
		},
		{
			text: "Deoarece obiectivul de preantrenare nu necesită ca oamenii să eticheteze datele",
			explain: "Acest lucru este corect, modelarea limbajului este o problemă autosupravegheată.",
			correct: true
		},
		{
			text: "Deoarece biblioteca 🤗 Transformers necesită doar câteva linii de cod pentru a începe antrenarea",
			explain: "Deși adevărat, acest lucru nu răspunde cu adevărat la întrebarea pusă. Încercați un alt răspuns!"
		}
	]}
/>

### 12. Care sunt principalele provocări la preprocesarea datelor pentru o sarcină de răspundere a întrebărilor?

<Question
	choices={[
		{
			text: "Trebuie să tokenizați inputurile.",
			explain: "Este corect, dar este cu adevărat o provocare principală?"
		},
		{
			text: "Trebuie să aveți de-a face cu contexte foarte lungi, care oferă mai multe caracteristici de antrenare care pot sau nu să conțină răspunsul.",
			explain: "Aceasta este cu siguranță una dintre provocări.",
			correct: true
		},
		{
			text: "Trebuie să tokenizați răspunsurile la întrebări, precum și inputurile.",
			explain: "Nu, cu excepția cazului în care vă încadrați problema de răspundere a întrebărilor ca o sarcină de sequence-to-sequence."
		},
       {
			text: "Din intervalul răspunsului din text, trebuie să găsiți tokenul de început și de sfârșit în inputul tokenizat.",
			explain: "Aceasta este una dintre părțile dificile!",
			correct: true
		}
	]}
/>

### 13. Cum se face de obicei post-procesarea în răspunderea la întrebări?

<Question
	choices={[
		{
			text: "Modelul vă oferă pozițiile de început și de sfârșit ale răspunsului, iar voi trebuie doar să decodificați intervalul corespunzător de tokeni.",
			explain: "Acesta ar putea fi un mod de a face acest lucru, dar este un pic prea simplist."
		},
		{
			text: "Modelul vă oferă pozițiile de început și de sfârșit ale răspunsului pentru fiecare caracteristică creată de un exemplu, iar voi trebuie doar să decodificați intervalul corespunzător de tokeni în cel care are cel mai bun scor.",
			explain: "Acest lucru este apropiat de post-procesarea pe care am studiat-o, dar nu este complet corect."
		},
		{
			text: "Modelul vă oferă pozițiile de început și de sfârșit ale răspunsului pentru fiecare caracteristică creată de un exemplu, iar voi trebuie doar să le potriviți cu intervalul din context pentru cel care are cel mai bun scor.",
			explain: "Asta e corect!",
			correct: true
		},
        {
			text: "Modelul generează un răspuns, iar tu trebuie doar să îl decodifici.",
			explain: "Nu, cu excepția cazului în care vă încadrați problema de răspundere a întrebării ca o sarcină de sequence-to-sequence."
		}
	]}
/>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter7/9.mdx" />

### Sumarizare[[summarization]]
https://huggingface.co/learn/course/ro/chapter7/5.md

# Sumarizare[[summarization]]

{#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/section5_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter7/section5_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/section5_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter7/section5_tf.ipynb"},
]} />

{/if}


În această secțiune vom analiza modul în care modelele Transformer pot fi utilizate pentru a condensa documente lungi în rezumate, o sarcină cunoscută sub numele de _text summarization_. Aceasta este una dintre cele mai dificile sarcini NLP, deoarece necesită o gamă largă de abilități, cum ar fi înțelegerea pasajelor lungi și generarea unui text coerent care integrează principalele subiecte dintr-un document. Cu toate acestea, atunci când este bine realizată, rezumarea textului este un instrument puternic care poate accelera diverse procese de business prin scutirea experților într-un anumit domeniu de a citi documente lungi în detaliu.

<Youtube id="yHnr5Dk2zCI"/>

Deși există deja diverse modele bine puse la punct pentru sumarizare pe [Hugging Face Hub] (https://huggingface.co/models?pipeline_tag=summarization&sort=downloads), aproape toate acestea sunt potrivite numai pentru documentele în limba engleză. Prin urmare, pentru a adăuga o întorsătură în această secțiune, vom antrena un model bilingv pentru engleză și spaniolă. Până la sfârșitul acestei secțiuni, veți avea un [model](https://huggingface.co/huggingface-course/mt5-small-finetuned-amazon-en-es) care poate rezuma recenziile clienților precum cel prezentat aici:


<iframe src="https://course-demos-mt5-small-finetuned-amazon-en-es.hf.space" frameBorder="0" height="400" 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>

După cum vom vedea, aceste rezumate sunt concise deoarece sunt învățate din titlurile pe care clienții le furnizează în recenziile lor despre produse. Să începem prin alcătuirea unui corpus bilingv adecvat pentru această sarcină.

## Pregătirea unui corpus multilingv[[preparing-a-multilingual-corpus]]

Vom utiliza [Multilingual Amazon Reviews Corpus] (https://huggingface.co/datasets/amazon_reviews_multi) pentru a crea bilingv summarizerul nostru. Acest corpus este format din recenzii ale produselor Amazon în șase limbi și este utilizat de obicei pentru a evalua clasificatoarele multilingve. Cu toate acestea, deoarece fiecare recenzie este însoțită de un titlu scurt, putem folosi titlurile ca rezumate țintă din care modelul nostru să învețe! Pentru a începe, să descărcăm subseturile în engleză și spaniolă de la Hugging Face Hub:

```python
from datasets import load_dataset

spanish_dataset = load_dataset("amazon_reviews_multi", "es")
english_dataset = load_dataset("amazon_reviews_multi", "en")
english_dataset
```

```python out
DatasetDict({
    train: Dataset({
        features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'],
        num_rows: 200000
    })
    validation: Dataset({
        features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'],
        num_rows: 5000
    })
    test: Dataset({
        features: ['review_id', 'product_id', 'reviewer_id', 'stars', 'review_body', 'review_title', 'language', 'product_category'],
        num_rows: 5000
    })
})
```

După cum puteți vedea, pentru fiecare limbă există 200.000 de recenzii pentru splitul `train` și 5.000 de recenzii pentru fiecare dintre spliturile `validation` și `test`. Informațiile despre recenzii care ne interesează sunt conținute în coloanele `review_body` și `review_title`. Să analizăm câteva exemple prin crearea unei funcții simple care preia un sample aleatoriu din setul de antrenare cu ajutorul tehnicilor învățate în [Capitolul 5](/course/chapter5):

```python
def show_samples(dataset, num_samples=3, seed=42):
    sample = dataset["train"].shuffle(seed=seed).select(range(num_samples))
    for example in sample:
        print(f"\n'>> Title: {example['review_title']}'")
        print(f"'>> Review: {example['review_body']}'")


show_samples(english_dataset)
```

```python out
'>> Title: Worked in front position, not rear'
'>> Review: 3 stars because these are not rear brakes as stated in the item description. At least the mount adapter only worked on the front fork of the bike that I got it for.'

'>> Title: meh'
'>> Review: Does it’s job and it’s gorgeous but mine is falling apart, I had to basically put it together again with hot glue'

'>> Title: Can\'t beat these for the money'
'>> Review: Bought this for handling miscellaneous aircraft parts and hanger "stuff" that I needed to organize; it really fit the bill. The unit arrived quickly, was well packaged and arrived intact (always a good sign). There are five wall mounts-- three on the top and two on the bottom. I wanted to mount it on the wall, so all I had to do was to remove the top two layers of plastic drawers, as well as the bottom corner drawers, place it when I wanted and mark it; I then used some of the new plastic screw in wall anchors (the 50 pound variety) and it easily mounted to the wall. Some have remarked that they wanted dividers for the drawers, and that they made those. Good idea. My application was that I needed something that I can see the contents at about eye level, so I wanted the fuller-sized drawers. I also like that these are the new plastic that doesn\'t get brittle and split like my older plastic drawers did. I like the all-plastic construction. It\'s heavy duty enough to hold metal parts, but being made of plastic it\'s not as heavy as a metal frame, so you can easily mount it to the wall and still load it up with heavy stuff, or light stuff. No problem there. For the money, you can\'t beat it. Best one of these I\'ve bought to date-- and I\'ve been using some version of these for over forty years.'
```

<Tip>

✏️ **Încercați!** Schimbați seedul aleatoriu în comanda `Dataset.shuffle()` pentru a explora alte recenzii din corpus. Dacă sunteți vorbitor de spaniolă, aruncați o privire la unele dintre recenziile din `spanish_dataset` pentru a vedea dacă și titlurile par a fi rezumate rezonabil.

</Tip>

Acest sample arată diversitatea recenziilor pe care le găsim de obicei online, variind de la pozitive la negative (și totul între ele!). Deși exemplul cu titlul "meh" nu este foarte informativ, celelalte titluri par a fi rezumate decente ale recenziilor în sine. Antrenarea unui model de rezumare pe toate cele 400 000 de recenzii ar dura mult prea mult pe un singur GPU, așa că ne vom concentra pe generarea de rezumate pentru un singur domeniu de produse. Pentru a avea o idee despre domeniile din care putem alege, să convertim `english_dataset` într-un `pandas.DataFrame` și să calculăm numărul de recenzii per categorie de produse:

```python
english_dataset.set_format("pandas")
english_df = english_dataset["train"][:]
# Show counts for top 20 products
english_df["product_category"].value_counts()[:20]
```

```python out
home                      17679
apparel                   15951
wireless                  15717
other                     13418
beauty                    12091
drugstore                 11730
kitchen                   10382
toy                        8745
sports                     8277
automotive                 7506
lawn_and_garden            7327
home_improvement           7136
pet_products               7082
digital_ebook_purchase     6749
pc                         6401
electronics                6186
office_product             5521
shoes                      5197
grocery                    4730
book                       3756
Name: product_category, dtype: int64
```

Cele mai populare produse din datasetul în limba engleză sunt despre articole de uz casnic, îmbrăcăminte și electronice fără fir. Cu toate acestea, pentru a rămâne la Amazontheme, să ne concentrăm pe rezumatul recenziilor de cărți - la urma urmei, acesta este motivul pentru care compania a fost fondată! Putem vedea două categorii de produse care se potrivesc (`book` și `digital_ebook_purchase`), deci să filtrăm dataseturile în ambele limbi doar pentru aceste produse. După cum am văzut în [Capitolul 5](/course/chapter5), funcția `Dataset.filter()` ne permite să tăiem un dataset foarte eficient, deci putem defini o funcție simplă pentru a face acest lucru:

```python
def filter_books(example):
    return (
        example["product_category"] == "book"
        or example["product_category"] == "digital_ebook_purchase"
    )
```

Acum, când aplicăm această funcție la `english_dataset` și `spanish_dataset`, rezultatul va conține doar acele rânduri care implică categoriile de cărți. Înainte de a aplica filtrul, să schimbăm formatul din `english_dataset` din `"pandas"` înapoi în `"arrow"`:

```python
english_dataset.reset_format()
```

Putem aplica apoi funcția de filtrare și, ca o verificare a corectitudinii, să inspectăm un sample de recenzii pentru a vedea dacă acestea sunt într-adevăr despre cărți:

```python
spanish_books = spanish_dataset.filter(filter_books)
english_books = english_dataset.filter(filter_books)
show_samples(english_books)
```

```python out
'>> Title: I\'m dissapointed.'
'>> Review: I guess I had higher expectations for this book from the reviews. I really thought I\'d at least like it. The plot idea was great. I loved Ash but, it just didnt go anywhere. Most of the book was about their radio show and talking to callers. I wanted the author to dig deeper so we could really get to know the characters. All we know about Grace is that she is attractive looking, Latino and is kind of a brat. I\'m dissapointed.'

'>> Title: Good art, good price, poor design'
'>> Review: I had gotten the DC Vintage calendar the past two years, but it was on backorder forever this year and I saw they had shrunk the dimensions for no good reason. This one has good art choices but the design has the fold going through the picture, so it\'s less aesthetically pleasing, especially if you want to keep a picture to hang. For the price, a good calendar'

'>> Title: Helpful'
'>> Review: Nearly all the tips useful and. I consider myself an intermediate to advanced user of OneNote. I would highly recommend.'
```

Bine, putem vedea că recenziile nu sunt strict despre cărți și se pot referi la lucruri precum calendare și aplicații electronice precum OneNote. Cu toate acestea, domeniul pare potrivit pentru a antrena un model de sumarizare. Înainte de a analiza diferitele modele care sunt potrivite pentru această sarcină, trebuie să mai pregătim puțin datele: să combinăm recenziile în engleză și spaniolă ca un singur obiect `DatasetDict`. 🤗 Datasets oferă o funcție utilă `concatenate_datasets()` care (după cum sugerează și numele) va concatena două obiecte `Dataset` unul peste celălalt. Așadar, pentru a crea datasetul nostru bilingv, vom parcurge în buclă fiecare împărțire, vom concatena dataseturile pentru acel split și vom amesteca rezultatul pentru a ne asigura că modelul nostru nu se adaptează excesiv la o singură limbă:

```python
from datasets import concatenate_datasets, DatasetDict

books_dataset = DatasetDict()

for split in english_books.keys():
    books_dataset[split] = concatenate_datasets(
        [english_books[split], spanish_books[split]]
    )
    books_dataset[split] = books_dataset[split].shuffle(seed=42)

# Peek at a few examples
show_samples(books_dataset)
```

```python out
'>> Title: Easy to follow!!!!'
'>> Review: I loved The dash diet weight loss Solution. Never hungry. I would recommend this diet. Also the menus are well rounded. Try it. Has lots of the information need thanks.'

'>> Title: PARCIALMENTE DAÑADO'
'>> Review: Me llegó el día que tocaba, junto a otros libros que pedí, pero la caja llegó en mal estado lo cual dañó las esquinas de los libros porque venían sin protección (forro).'

'>> Title: no lo he podido descargar'
'>> Review: igual que el anterior'
```

Acest lucru arată cu siguranță ca un amestec de recenzii în engleză și spaniolă! Acum că avem un corpus de antrenament, un ultim lucru de verificat este distribuția cuvintelor în recenzii și în titlurile acestora. Acest lucru este deosebit de important pentru sarcinile de sumarizare, în cazul în care rezumatele scurte de referință din date pot influența modelul să producă doar unul sau două cuvinte în rezumatele generate. Graficele de mai jos arată distribuția cuvintelor și putem observa că titlurile sunt puternic înclinate spre 1-2 cuvinte:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/review-lengths.svg" alt="Distribuția numărului de cuvinte pentru titlurile și textele recenziei."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/review-lengths-dark.svg" alt="Distribuția numărului de cuvinte pentru titlurile și textele recenziei."/>
</div>

Pentru a rezolva acest lucru, vom filtra exemplele cu titluri foarte scurte, astfel încât modelul nostru să poată produce rezumate mai interesante. Deoarece avem de-a face cu texte în engleză și spaniolă, putem folosi rough heuristic pentru a face split titlurilor pe baza spațiului alb și apoi să folosim metoda noastră de încredere `Dataset.filter()` după cum urmează:

```python
books_dataset = books_dataset.filter(lambda x: len(x["review_title"].split()) > 2)
```

Now that we've prepared our corpus, let's take a look at a few possible Transformer models that one might fine-tune on it!

## Modele pentru sumarizarea textului[[models-for-text-summarization]]

Dacă vă gândiți bine, rezumarea textului este o sarcină similară cu machine translation: avem un corp de text, cum ar fi o recenzie, pe care am dori să o "traducem" într-o versiune mai scurtă care să capteze caracteristicile principale ale datelor de intrare. În consecință, majoritatea modelelor Transformer pentru rezumare adoptă arhitectura codificator-decodificator pe care am întâlnit-o pentru prima dată în [Capitolul 1](/course/chapter1), deși există unele excepții, cum ar fi familia de modele GPT, care poate fi, de asemenea, utilizată pentru rezumare în few-shot settings. Tabelul de mai jos enumeră câteva modele preantrenate populare care pot fi ajustate pentru sumarizare.

| Transformer model | Description                                                                                                                                                                                                    | Multilingual? |
| :---------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----------: |
|    [GPT-2](https://huggingface.co/gpt2-xl)    | Deși este antrenat ca un model lingvistic autoregresiv, puteți face GPT-2 să genereze rezumate prin adăugarea "TL;DR" la sfârșitul textului de intrare.                                                                          |      ❌       |
|   [PEGASUS](https://huggingface.co/google/pegasus-large)   | Utilizează un obiectiv de preantrenare pentru a prezice propoziții mascate în texte cu mai multe propoziții. Acest obiectiv de preantrenare este mai apropiat de rezumare decât de vanilla language modeling și obține scoruri ridicate la standardele populare. |      ❌       |
|     [T5](https://huggingface.co/t5-base)      | O arhitectură Transformer universală care formulează toate sarcinile într-un framework text-text; de exemplu, formatul de intrare pentru modelul de rezumare a unui document este `summarize: ARTICOL`.                              |      ❌       |
|     [mT5](https://huggingface.co/google/mt5-base)     | O versiune multilingvă a T5, preantrenată pe corpusul multilingv Common Crawl (mC4), care acoperă 101 limbi.                                                                                                |      ✅       |
|    [BART](https://huggingface.co/facebook/bart-base)     | O nouă arhitectură Transformer cu un encoder și un stack de decodere antrenate pentru a reconstrui intrarea coruptă care combină schemele de preantrenare ale BERT și GPT-2.                                   |      ❌       |
|  [mBART-50](https://huggingface.co/facebook/mbart-large-50)   | O versiune multilingvă a BART, preantrenată pe 50 de limbi.                                                                                                                                                     |      ✅       |

După cum puteți vedea din acest tabel, majoritatea modelelor Transformer pentru rezumare (și, într-adevăr, majoritatea sarcinilor NLP) sunt monolingve. Acest lucru este grozav dacă sarcina voastrăeste într-o limbă cu  multe resurse precum engleza sau germana, dar mai puțin pentru miile de alte limbi utilizate în întreaga lume. Din fericire, există o clasă de modele Transformer multilingve, precum mT5 și mBART, care vin în ajutor. Aceste modele sunt preantrenate folosind modelarea limbajului, dar cu o întorsătură: în loc să fie antrenate pe un corpus dintr-o singură limbă, ele sunt antrenate împreună pe texte în peste 50 de limbi deodată!

Ne vom concentra asupra mT5, o arhitectură interesantă bazată pe T5, care a fost preantrenată într-un cadru text-to-text. În T5, fiecare sarcină NLP este formulată în termenii unui prompt prefix precum `summarize:` care condiționează modelul să adapteze textul generat la prompt. După cum se arată în figura de mai jos, acest lucru face T5 extrem de versatil, deoarece puteți rezolva multe sarcini cu un singur model!

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/t5.svg" alt="Diferite sarcini îndeplinite de arhitectura T5."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/t5-dark.svg" alt="Diferite sarcini îndeplinite de arhitectura T5."/>
</div>

mT5 nu utilizează prefixe, dar împărtășește o mare parte din versatilitatea T5 și are avantajul de a fi multilingv. Acum că am ales un model, să aruncăm o privire la pregătirea datelor noastre pentru antrenare.


<Tip>

✏️ **Încercați!** După ce ați parcurs această secțiune, vedeți cât de bine se compară mT5 cu mBART prin aplicarea fine-tuningului acestuia din urmă cu aceleași tehnici. Pentru puncte bonus, puteți încerca, de asemenea, fine-tuningul a T5 doar pe recenziile în limba engleză. Deoarece T5 are un prefix prompt special, va trebui să adăugați `summarize:` la exemplele de intrare în pașii de preprocesare de mai jos.

</Tip>

## Preprocessing the data[[preprocessing-the-data]]

<Youtube id="1m7BerpSq8A"/>

Următoarea noastră sarcină este să tokenizăm și să codificăm recenziile și titlurile acestora. Ca de obicei, începem prin încărcarea tokenizelui asociat cu checkpointul modelului preantrenat. Vom folosi `mt5-small` ca checkpoint, astfel încât să putem face fine-tune modelului într-un timp rezonabil:

```python
from transformers import AutoTokenizer

model_checkpoint = "google/mt5-small"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
```

<Tip>

💡 În stadiile inițiale ale proiectelor NLP, o bună practică este de a antrena o clasă de modele "mici" pe un sample mic de date. Acest lucru vă permite să faceți debug și să iterați mai rapid către un flux de lucru end-to-end. Odată ce sunteți încrezător în rezultate, puteți oricând să măriți modelul prin simpla schimbare a checkpointului modelului!

</Tip>

Să testăm tokenizerul mT5 pe un mic exemplu:

```python
inputs = tokenizer("I loved reading the Hunger Games!")
inputs
```

```python out
{'input_ids': [336, 259, 28387, 11807, 287, 62893, 295, 12507, 1], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}
```

Aici putem vedea binecunoscutele `input_ids` și `attention_mask` pe care le-am întâlnit în primele noastre experimente de fine-tuning în [Capitolul 3](/course/chapter3). Să decodificăm aceste ID-uri de intrare cu funcția `convert_ids_to_tokens()` a tokenizerului pentru a vedea cu ce fel de tokenizer avem de-a face:

```python
tokenizer.convert_ids_to_tokens(inputs.input_ids)
```

```python out
['▁I', '▁', 'loved', '▁reading', '▁the', '▁Hung', 'er', '▁Games', '</s>']
```

Caracterul Unicode special `▁` și tokenul de sfârșit de secvență `</s>` indică faptul că avem de-a face cu tokenizeerul SentencePiece, care se bazează pe algoritmul de segmentare Unigram discutat în [Capitolul 6](/course/chapter6). Unigram este deosebit de util pentru corpusurile multilingve, deoarece permite SentencePiece să fie agnostic în ceea ce privește accentele, punctuația și faptul că multe limbi, precum japoneza, nu au caractere de spațiu alb.

Pentru a tokeniza corpusul nostru, trebuie să ne ocupăm de o subtilitate asociată cu rezumarea: deoarece labelurile noastre sunt, de asemenea, text, este posibil ca acestea să depășească dimensiunea maximă a contextului modelului. Acest lucru înseamnă că trebuie să aplicăm trunchierea atât a recenziilor, cât și a titlurilor acestora, pentru a ne asigura că nu trecem inputuri excesiv de lungi modelului nostru. Tokenizerele din 🤗 Transformers oferă un argument ingenios, `text_target`,  care vă permite să tokenizați labelurile în paralel cu inputurile. Iată un exemplu al modului în care inputurile și targeturile sunt procesate pentru mT5:

```python
max_input_length = 512
max_target_length = 30


def preprocess_function(examples):
    model_inputs = tokenizer(
        examples["review_body"],
        max_length=max_input_length,
        truncation=True,
    )
    labels = tokenizer(
        examples["review_title"], max_length=max_target_length, truncation=True
    )
    model_inputs["labels"] = labels["input_ids"]
    return model_inputs
```

Să parcurgem acest cod pentru a înțelege ce se întâmplă. Primul lucru pe care l-am făcut a fost să definim valorile pentru `max_input_length` și `max_target_length`, care stabilesc limitele superioare pentru cât de lungi pot fi recenziile și titlurile noastre. Deoarece corpul recenziei este de obicei mult mai mare decât titlul, am mărit aceste valori în consecință.

Cu ajutorul funcției `preprocess_function()`, este simplu să tokenizăm întregul corpus cu ajutorul funcției practice `Dataset.map()` pe care am folosit-o la greu pe parcursul acestui curs:

```python
tokenized_datasets = books_dataset.map(preprocess_function, batched=True)
```

Acum că corpusul a fost preprocesat, să aruncăm o privire asupra unor metrici care sunt utilizate în mod obișnuit pentru sumarizare. După cum vom vedea, nu există un glonț de argint atunci când vine vorba de măsurarea calității textului generat de calculator.

<Tip>

💡 Poate ați observat că am folosit `batched=True` în funcția noastră `Dataset.map()` de mai sus. Aceasta codifică exemplele în batchuri de 1.000 (implicit) și vă permite să utilizați capacitățile multithreading ale tokenizerilor rapizi din 🤗 Transformers. Atunci când este posibil, încercați să utilizați `batched=True` pentru a profita la maximum de preprocesare!

</Tip>


## Metrice pentru sumarizare[[metrics-for-text-summarization]]

<Youtube id="TMshhnrEXlg"/>

În comparație cu majoritatea celorlalte sarcini pe care le-am abordat în acest curs, măsurarea performanței sarcinilor de generare a textului, precum sumarizare sau traducerea, nu este la fel de simplă. De exemplu, având în vedere o recenzie precum "Mi-a plăcut să citesc Hunger Games", există mai multe rezumate valide, precum "Mi-a plăcut Hunger Games" sau "Hunger Games este o lectură excelentă". În mod clar, aplicarea unui exact match între rezumatul generat și label nu este o soluție bună - chiar și oamenii s-ar descurca prost cu un astfel de metric, deoarece toți avem propriul nostru stil de scriere.

Pentru rezumare, una dintre cele mai frecvent utilizate metrici este [ROUGE score](https://en.wikipedia.org/wiki/ROUGE_(metric)) (prescurtarea de la Recall-Oriented Understudy for Gisting Evaluation). Ideea de bază din spatele acestei metrici este de a compara un rezumat generat cu un set de rezumate de referință care sunt de obicei create de oameni. Pentru a face acest lucru mai precis, să presupunem că dorim să comparăm următoarele două rezumate:

```python
generated_summary = "I absolutely loved reading the Hunger Games"
reference_summary = "I loved reading the Hunger Games"
```

O modalitate de a le compara ar fi să numărați numărul de cuvinte care se suprapun, care în acest caz ar fi 6. Cu toate acestea, acest lucru este un pic crud, astfel încât, în schimb, ROUGE se bazează pe calcularea scorurilor _preicision_ și _recall_ pentru suprapunere.

<Tip>

🙋 Nu vă faceți griji dacă aceasta este prima dată când auziți de precision și recall - vom trece împreună prin câteva exemple explicite pentru a clarifica totul. Aceste metrici sunt de obicei întâlnite în sarcinile de clasificare, deci dacă doriți să înțelegeți cum sunt definite precizia și recallul în acest context, vă recomandăm să consultați [ghidurile `scikit-learn`] (https://scikit-learn.org/stable/auto_examples/model_selection/plot_precision_recall.html).

</Tip>

Pentru ROUGE, recall măsoară cât de mult din rezumatul de referință este capturat de cel generat. Dacă comparăm doar cuvinte, recall poate fi calculată conform următoarei formule:

$$ \mathrm{Recall} = \frac{\mathrm{Number\,of\,overlapping\, words}}{\mathrm{Total\, number\, of\, words\, in\, reference\, summary}} $$

Pentru exemplul nostru simplu de mai sus, această formulă oferă un recall perfect de 6/6 = 1; adică, toate cuvintele din rezumatul de referință au fost produse de model. Acest lucru poate părea grozav, dar imaginați-vă dacă rezumatul nostru generat ar fi fost "Mi-a plăcut foarte mult să citesc Jocurile Foamei toată noaptea". Aceasta ar avea, de asemenea, o reamintire perfectă, dar este, fără îndoială, un rezumat mai prost, deoarece are mai multe cuvinte. Pentru a face față acestor scenarii, calculăm și precizia, care, în contextul ROUGE, măsoară cât de mult din rezumatul generat a fost relevant:


$$ \mathrm{Precision} = \frac{\mathrm{Number\,of\,overlapping\, words}}{\mathrm{Total\, number\, of\, words\, in\, generated\, summary}} $$

Aplicând acest lucru la rezumatul nostru cu mai multe cuvinte, se obține o precizie de 6/10 = 0,6, ceea ce este considerabil mai rău decât precizia de 6/7 = 0,86 obținută de rezumatul nostru mai scurt. În practică, se calculează de obicei atât precision, cât și recallul, iar apoi se raportează scorul F1 (media armonică a precision și recall). Putem face acest lucru cu ușurință în 🤗 Datasets instalând mai întâi biblioteca `rouge_score`:

```py
!pip install rouge_score
```

și apoi încărcând metrica ROUGE după cum urmează:

```python
import evaluate

rouge_score = evaluate.load("rouge")
```

Apoi, putem utiliza funcția `rouge_score.compute()` pentru a calcula toate metricile odată:

```python
scores = rouge_score.compute(
    predictions=[generated_summary], references=[reference_summary]
)
scores
```

```python out
{'rouge1': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92)),
 'rouge2': AggregateScore(low=Score(precision=0.67, recall=0.8, fmeasure=0.73), mid=Score(precision=0.67, recall=0.8, fmeasure=0.73), high=Score(precision=0.67, recall=0.8, fmeasure=0.73)),
 'rougeL': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92)),
 'rougeLsum': AggregateScore(low=Score(precision=0.86, recall=1.0, fmeasure=0.92), mid=Score(precision=0.86, recall=1.0, fmeasure=0.92), high=Score(precision=0.86, recall=1.0, fmeasure=0.92))}
```

Whoa, există o mulțime de informații în aceast output - ce înseamnă toate acestea? În primul rând, 🤗 Datasets calculează de fapt confidence intervalurile pentru precision, recall și scorul F1; acestea sunt atributele `low`, `mid` și `high` pe care le puteți vedea aici. În plus, 🤗 Datasets calculează o varietate de scoruri ROUGE care se bazează pe diferite tipuri de granularitate a textului atunci când compară rezumatele generate și de referință. Varianta `rouge1` este suprapunerea unigramelor - acesta este doar un mod elegant de a spune suprapunerea cuvintelor și este exact metrica pe care am discutat-o mai sus. Pentru a verifica acest lucru, să extragem valoarea `mid` a scorurilor noastre:

```python
scores["rouge1"].mid
```

```python out
Score(precision=0.86, recall=1.0, fmeasure=0.92)
```

Grozav, numerele de precision și de recall se potrivesc! Acum ce se întâmplă cu celelalte scoruri ROUGE? `rouge2` măsoară suprapunerea dintre bigrame (suprapunerea perechilor de cuvinte), în timp ce `rougeL` și `rougeLsum` măsoară cele mai lungi secvențe de cuvinte care se potrivesc, căutând cele mai lungi substraturi comune în rezumatele generate și de referință. Termenul "sum" din `rougeLsum` se referă la faptul că această metrică este calculată pentru un rezumat întreg, în timp ce `rougeL` este calculată ca medie a propozițiilor individuale.

<Tip>

✏️ **Încercați!** Creați propriul exemplu de rezumat generat și de referință și vedeți dacă scorurile ROUGE rezultate sunt în concordanță cu un calcul manual bazat pe formulele de precision și recall. Pentru puncte bonus, împărțiți textul în bigrame și comparați precizia și recallul pentru metrica `rouge2`.

</Tip>

Vom folosi aceste scoruri ROUGE pentru a urmări performanța modelului nostru, dar înainte de a face acest lucru, să facem ceva ce orice bun practician NLP ar trebui să facă: să creăm un baseline puternic, dar simplu!

### Crearea unui baseline bun[[creating-a-strong-baseline]]

un baseline obișnuit pentru rezumarea textului este de a lua pur și simplu primele trei propoziții ale unui articol, adesea numit _lead-3_ baseline. Am putea folosi puncte de oprire pentru a urmări limitele propoziției, dar acest lucru va eșua în cazul acronimelor precum "U.S." sau "U.N." - așa că vom folosi în schimb biblioteca `nltk`, care include un algoritm mai bun pentru a gestiona aceste cazuri. Puteți instala pachetul folosind `pip` după cum urmează:

```python
!pip install nltk
```

și apoi descărcați regulile de punctuație:

```python
import nltk

nltk.download("punkt")
```

În continuare, importăm tokenizerul de propoziții din `nltk` și creăm o funcție simplă pentru a extrage primele trei propoziții dintr-o recenzie. Convenția în rezumarea textului este de a separa fiecare rezumat cu o linie nouă, deci să includem și aceasta și să o testăm pe un exemplu de antrenare:

```python
from nltk.tokenize import sent_tokenize


def three_sentence_summary(text):
    return "\n".join(sent_tokenize(text)[:3])


print(three_sentence_summary(books_dataset["train"][1]["review_body"]))
```

```python out
'I grew up reading Koontz, and years ago, I stopped,convinced i had "outgrown" him.'
'Still,when a friend was looking for something suspenseful too read, I suggested Koontz.'
'She found Strangers.'
```

Acest lucru pare să funcționeze, deci să implementăm acum o funcție care extrage aceste "rezumate" dintr-un dataset și calculează scorurile ROUGE pentru baseline:

```python
def evaluate_baseline(dataset, metric):
    summaries = [three_sentence_summary(text) for text in dataset["review_body"]]
    return metric.compute(predictions=summaries, references=dataset["review_title"])
```

Putem folosi apoi această funcție pentru a calcula scorurile ROUGE pe setul de validare și pentru a le înfrumuseța puțin folosind Pandas:

```python
import pandas as pd

score = evaluate_baseline(books_dataset["validation"], rouge_score)
rouge_names = ["rouge1", "rouge2", "rougeL", "rougeLsum"]
rouge_dict = dict((rn, round(score[rn].mid.fmeasure * 100, 2)) for rn in rouge_names)
rouge_dict
```

```python out
{'rouge1': 16.74, 'rouge2': 8.83, 'rougeL': 15.6, 'rougeLsum': 15.96}
```

Putem vedea că scorul `rouge2` este semnificativ mai mic decât restul; acest lucru reflectă probabil faptul că titlurile recenziilor sunt de obicei concise și, prin urmare, baselineul lead-3 are prea multe cuvinte. Acum, că avem un baseline bun de lucru, să ne îndreptăm atenția către fine-tuningul mT5!

{#if fw === 'pt'}

## Fine-tuningul mT5 cu API-ul `Trainer`[[fine-tuning-mt5-with-the-trainer-api]]

Fine-tuningul unui model pentru rezumare este foarte asemănător cu celelalte sarcini pe care le-am acoperit în acest capitol. Primul lucru pe care trebuie să îl facem este să încărcăm modelul preantrenat din checkpointul `mt5-small`. Deoarece sumarizarea este o sarcină de la secvență la secvență, putem încărca modelul cu clasa `AutoModelForSeq2SeqLM`, care va descărca automat și va stoca în cache weighturile:

```python
from transformers import AutoModelForSeq2SeqLM

model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
```

{:else}

## Fine-tuningul mT5 cu Keras[[fine-tuning-mt5-with-keras]]

Fine-tuningul unui model pentru sumarizare este foarte asemănătoare cu celelalte sarcini pe care le-am acoperit în acest capitol. Primul lucru pe care trebuie să îl facem este să încărcăm modelul preantrenat din punctul de control `mt5-small`. Deoarece rezumarea este o sarcină de la secvență la secvență, putem încărca modelul cu clasa `TFAutoModelForSeq2SeqLM`, care va descărca și va stoca în cache weighturile:

```python
from transformers import TFAutoModelForSeq2SeqLM

model = TFAutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
```

{/if}

<Tip>

💡 Dacă vă întrebați de ce nu vedeți niciun avertisment cu privire la fine-tuningul modelului pe un downstream task, acest lucru se datorează faptului că pentru sarcinile secvență-la-secvență păstrăm toate weighturile rețelei. Comparați acest lucru cu modelul nostru de clasificare a textului din [Capitolul 3](/course/chapter3), unde headul modelului preantrenat a fost înlocuit cu o rețea inițializată aleatoriu.

</Tip>

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:

```python
from huggingface_hub import notebook_login

notebook_login()
```

care va afișa un widget în care puteți introduce credențialele. Alternativ, puteți rula această comandă în terminal și să vă conectați acolo:

```
huggingface-cli login
```

{#if fw === 'pt'}

Va trebui să generăm rezumate pentru a calcula scorurile ROUGE în timpul antrenării. Din fericire, 🤗 Transformers oferă clase dedicate `Seq2SeqTrainingArguments` și `Seq2SeqTrainer` care pot face acest lucru pentru noi în mod automat! Pentru a vedea cum funcționează acest lucru, să definim mai întâi hiperparametrii și alte argumente pentru experimentele noastre:

```python
from transformers import Seq2SeqTrainingArguments

batch_size = 8
num_train_epochs = 8
# Show the training loss with every epoch
logging_steps = len(tokenized_datasets["train"]) // batch_size
model_name = model_checkpoint.split("/")[-1]

args = Seq2SeqTrainingArguments(
    output_dir=f"{model_name}-finetuned-amazon-en-es",
    evaluation_strategy="epoch",
    learning_rate=5.6e-5,
    per_device_train_batch_size=batch_size,
    per_device_eval_batch_size=batch_size,
    weight_decay=0.01,
    save_total_limit=3,
    num_train_epochs=num_train_epochs,
    predict_with_generate=True,
    logging_steps=logging_steps,
    push_to_hub=True,
)
```

Aici, argumentul `predict_with_generate` a fost setat pentru a indica faptul că ar trebui să generăm rezumate în timpul evaluării, astfel încât să putem calcula scorurile ROUGE pentru fiecare epocă. După cum s-a discutat în [Capitolul 1](/course/chapter1), decodificatorul realizează inference-ul prin prezicerea tokenilor unul câte unul, iar acest lucru este implementat de metoda `generate()` a modelului. Setarea `predict_with_generate=True` îi spune lui `Seq2SeqTrainer` să utilizeze această metodă pentru evaluare. Am ajustat, de asemenea, unii dintre hiperparametrii impliciți, cum ar fi rata de învățare, numărul de epoci și scăderea weighturilor și am setat opțiunea `save_total_limit` pentru a salva numai până la 3 checkpointuri în timpul antrenamentului - acest lucru se datorează faptului că chiar și versiunea "mică" a mT5 utilizează aproximativ 1 GB de spațiu pe hard disk și putem economisi puțin spațiu prin limitarea numărului de copii salvate.

Argumentul `push_to_hub=True` ne va permite să trimitem modelul în Hub după antrenare; veți găsi repositoriul în profilul vostru de utilizator, în locația definită de `output_dir`. Rețineți că puteți specifica numele repositoriului către care doriți să trimiteți modelul cu argumentul `hub_model_id` (în special, va trebui să utilizați acest argument pentru a trimite modelul către o organizație). De exemplu, atunci când am trimis modelul către organizația [`huggingface-course`](https://huggingface.co/huggingface-course), am adăugat `hub_model_id="huggingface-course/mt5-finetuned-amazon-en-es"` la `Seq2SeqTrainingArguments`.

Următorul lucru pe care trebuie să îl facem este să oferim trainerului o funcție `compute_metrics()`, astfel încât să ne putem evalua modelul în timpul antrenării. Pentru sumarizare, acest lucru este un pic mai complicat decât simpla apelare a funcției `rouge_score.compute()` pentru predicțiile modelului, deoarece trebuie să _decodăm_ rezultatele și labelurile din text înainte de a putea calcula scorurile ROUGE. Următoarea funcție face exact acest lucru și, de asemenea, utilizează funcția `sent_tokenize()` din `nltk` pentru a separa propozițiile rezumate cu linii noi:

```python
import numpy as np


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    # Decode generated summaries into text
    decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
    # Replace -100 in the labels as we can't decode them
    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
    # Decode reference summaries into text
    decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
    # ROUGE expects a newline after each sentence
    decoded_preds = ["\n".join(sent_tokenize(pred.strip())) for pred in decoded_preds]
    decoded_labels = ["\n".join(sent_tokenize(label.strip())) for label in decoded_labels]
    # Compute ROUGE scores
    result = rouge_score.compute(
        predictions=decoded_preds, references=decoded_labels, use_stemmer=True
    )
    # Extract the median scores
    result = {key: value.mid.fmeasure * 100 for key, value in result.items()}
    return {k: round(v, 4) for k, v in result.items()}
```

{/if}

În continuare, trebuie să definim un data collator pentru sarcina noastră secvență-la-secvență. Deoarece mT5 este un transformer model encoder-decoder, o subtilitate a pregătirii batch-urilor noastre este că, în timpul decodificării, trebuie să deplasăm labelurile la dreapta cu una. Acest lucru este necesar pentru a ne asigura că decodificatorul vede doar labelurile anterioare ale adevărului de bază și nu pe cele actuale sau viitoare, care ar fi ușor de memorat de către model. Acest lucru este similar cu modul în care masked self-attention este aplicată inputurilor într-o sarcină precum [causal language modeling](/course/chapter7/6).

Din fericire, 🤗 Transformers oferă un collator `DataCollatorForSeq2Seq` care va face padding dinamic inputurilor și labelurilor pentru noi. Pentru a inițializa acest collator, trebuie doar să furnizăm `tokenizer` și `model`:

{#if fw === 'pt'}

```python
from transformers import DataCollatorForSeq2Seq

data_collator = DataCollatorForSeq2Seq(tokenizer, model=model)
```

{:else}

```python
from transformers import DataCollatorForSeq2Seq

data_collator = DataCollatorForSeq2Seq(tokenizer, model=model, return_tensors="tf")
```

{/if}

Să vedem ce produce acest collator atunci când este alimentat cu un mic batch de exemple. În primul rând, trebuie să eliminăm coloanele cu șiruri de caractere, deoarece collatorul nu va ști cum să completeze aceste elemente:

```python
tokenized_datasets = tokenized_datasets.remove_columns(
    books_dataset["train"].column_names
)
```

Deoarece collatorul așteaptă o listă de `dict`, unde fiecare `dict` reprezintă un singur exemplu din dataset, trebuie să transformăm datele în formatul așteptat înainte de a le transmite data collatorului:

```python
features = [tokenized_datasets["train"][i] for i in range(2)]
data_collator(features)
```

```python out
{'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]), 'input_ids': tensor([[  1494,    259,   8622,    390,    259,    262,   2316,   3435,    955,
            772,    281,    772,   1617,    263,    305,  14701,    260,   1385,
           3031,    259,  24146,    332,   1037,    259,  43906,    305,    336,
            260,      1,      0,      0,      0,      0,      0,      0],
        [   259,  27531,  13483,    259,   7505,    260, 112240,  15192,    305,
          53198,    276,    259,  74060,    263,    260,    459,  25640,    776,
           2119,    336,    259,   2220,    259,  18896,    288,   4906,    288,
           1037,   3931,    260,   7083, 101476,   1143,    260,      1]]), 'labels': tensor([[ 7483,   259,  2364, 15695,     1,  -100],
        [  259, 27531, 13483,   259,  7505,     1]]), 'decoder_input_ids': tensor([[    0,  7483,   259,  2364, 15695,     1],
        [    0,   259, 27531, 13483,   259,  7505]])}
```

Principalul lucru care trebuie observat aici este că primul exemplu este mai lung decât al doilea, astfel încât `input_ids` și `attention_mask` din al doilea exemplu au primit padding în dreapta cu un simbol `[PAD]` (al cărui ID este `0`). În mod similar, putem vedea că `labels` au primit padding cu `-100`, pentru a ne asigura că tokenii de padding sunt ignorați de funcția de pierdere. În sfârșit, putem vedea un nou `decoder_input_ids` care a deplasat labelurile spre dreapta prin inserarea unui simbol `[PAD]` în prima intrare.

{#if fw === 'pt'}

Avem în sfârșit toate ingredientele de care avem nevoie pentru a antrenare! Acum trebuie doar să inițializăm trainerul cu argumentele standard:

```python
from transformers import Seq2SeqTrainer

trainer = Seq2SeqTrainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    tokenizer=tokenizer,
    compute_metrics=compute_metrics,
)
```

și să lansăm cursa noastră de antrenare:

```python
trainer.train()
```

În timpul antrenamentului, ar trebui să vedeți cum training loss scade și scorurile ROUGE cresc cu fiecare epocă. După ce antrenamentul este complet, puteți vedea scorurile ROUGE finale executând `Trainer.evaluate()`:

```python
trainer.evaluate()
```

```python out
{'eval_loss': 3.028524398803711,
 'eval_rouge1': 16.9728,
 'eval_rouge2': 8.2969,
 'eval_rougeL': 16.8366,
 'eval_rougeLsum': 16.851,
 'eval_gen_len': 10.1597,
 'eval_runtime': 6.1054,
 'eval_samples_per_second': 38.982,
 'eval_steps_per_second': 4.914}
```

Din scorurile obținute, putem vedea că modelul nostru a depășit cu mult modelul nostru de bază lead-3 - frumos! Ultimul lucru de făcut este să introducem weighturile modelului în Hub, după cum urmează:

```
trainer.push_to_hub(commit_message="Training complete", tags="summarization")
```

```python out
'https://huggingface.co/huggingface-course/mt5-finetuned-amazon-en-es/commit/aa0536b829b28e73e1e4b94b8a5aacec420d40e0'
```

Acest lucru va salva checkpointul și fișierele de configurare în `output_dir`, înainte de a încărca toate fișierele în Hub. Specificând argumentul `tags`, ne asigurăm, de asemenea, că widgetul de pe Hub va fi unul pentru un pipeline de rezumare în locul celui implicit de generare de text asociat arhitecturii mT5 (pentru mai multe informații despre labelurile modelului, consultați [🤗Documentația Hub](https://huggingface.co/docs/hub/main#how-is-a-models-type-of-inference-api-and-widget-determined)). Rezultatul din `trainer.push_to_hub()` este o adresă URL către hash-ul Git commit, astfel încât să puteți vedea cu ușurință modificările care au fost făcute în repositoriul modelului!

Pentru a încheia această secțiune, să aruncăm o privire la modul în care putem, de asemenea, să facem fine-tune la mT5 folosind featururile de nivel scăzut oferite de 🤗 Accelerate.

{:else}

Suntem aproape gata de antrenament! Trebuie doar să convertim dataseturile în `tf.data.Dataset`s folosind data collatorul pe care l-am definit mai sus, iar apoi aplicarea `compil()` și `fit()` modelul. Mai întâi, dataseturile:

```python
tf_train_dataset = model.prepare_tf_dataset(
    tokenized_datasets["train"],
    collate_fn=data_collator,
    shuffle=True,
    batch_size=8,
)
tf_eval_dataset = model.prepare_tf_dataset(
    tokenized_datasets["validation"],
    collate_fn=data_collator,
    shuffle=False,
    batch_size=8,
)
```

Acum, ne definim hiperparametrii de antrenare și compilare:

```python
from transformers import create_optimizer
import tensorflow as tf

# The number of training steps is the number of samples in the dataset, divided by the batch size then multiplied
# by the total number of epochs. Note that the tf_train_dataset here is a batched tf.data.Dataset,
# not the original Hugging Face Dataset, so its len() is already num_samples // batch_size.
num_train_epochs = 8
num_train_steps = len(tf_train_dataset) * num_train_epochs
model_name = model_checkpoint.split("/")[-1]

optimizer, schedule = create_optimizer(
    init_lr=5.6e-5,
    num_warmup_steps=0,
    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")
```

În cele din urmă, facem fit modelului modelului. Folosim un `PushToHubCallback` pentru a salva modelul în Hub după fiecare epocă, ceea ce ne va permite să îl folosim ulterior pentru inference:

```python
from transformers.keras_callbacks import PushToHubCallback

callback = PushToHubCallback(
    output_dir=f"{model_name}-finetuned-amazon-en-es", tokenizer=tokenizer
)

model.fit(
    tf_train_dataset, validation_data=tf_eval_dataset, callbacks=[callback], epochs=8
)
```

Am obținut unele valori ale pierderilor în timpul antrenării, dar am dori să vedem metricile ROUGE pe care le-am calculat mai devreme. Pentru a obține aceste metrici, va trebui să generăm outputuri din model și să le convertim în șiruri de caractere. Să construim câteva liste de labels și predicții pentru a compara metrica ROUGE (rețineți că, dacă obțineți erori de import pentru această secțiune, este posibil să fie necesar să faceți `!pip install tqdm`). De asemenea, vom utiliza un truc care crește dramatic performanța - compilarea codului nostru de generare cu [XLA](https://www.tensorflow.org/xla), compilatorul accelerat de algebră liniară al TensorFlow. XLA aplică diverse optimizări graficului de calcul al modelului și are ca rezultat îmbunătățiri semnificative ale vitezei și utilizării memoriei. După cum se descrie în [blogul Hugging Face](https://huggingface.co/blog/tf-xla-generate), XLA funcționează cel mai bine atunci când formele noastre de input nu variază prea mult. Pentru a face față acestui lucru, vom face padding inputurilor la multipli a 128 și vom crea un nou dataset cu padding collatorul, iar apoi vom aplica decoratorul `@tf.function(jit_compile=True)` funcției noastre de generare, care marchează întreaga funcție pentru compilare cu XLA.

```python
from tqdm import tqdm
import numpy as np

generation_data_collator = DataCollatorForSeq2Seq(
    tokenizer, model=model, return_tensors="tf", pad_to_multiple_of=320
)

tf_generate_dataset = model.prepare_tf_dataset(
    tokenized_datasets["validation"],
    collate_fn=generation_data_collator,
    shuffle=False,
    batch_size=8,
    drop_remainder=True,
)


@tf.function(jit_compile=True)
def generate_with_xla(batch):
    return model.generate(
        input_ids=batch["input_ids"],
        attention_mask=batch["attention_mask"],
        max_new_tokens=32,
    )


all_preds = []
all_labels = []
for batch, labels in tqdm(tf_generate_dataset):
    predictions = generate_with_xla(batch)
    decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
    labels = labels.numpy()
    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
    decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
    decoded_preds = ["\n".join(sent_tokenize(pred.strip())) for pred in decoded_preds]
    decoded_labels = ["\n".join(sent_tokenize(label.strip())) for label in decoded_labels]
    all_preds.extend(decoded_preds)
    all_labels.extend(decoded_labels)
```

Odată ce avem listele noastre de labeluri și de șiruri de predicție, calcularea scorului ROUGE este ușoară:

```python
result = rouge_score.compute(
    predictions=decoded_preds, references=decoded_labels, use_stemmer=True
)
result = {key: value.mid.fmeasure * 100 for key, value in result.items()}
{k: round(v, 4) for k, v in result.items()}
```

```
{'rouge1': 31.4815, 'rouge2': 25.4386, 'rougeL': 31.4815, 'rougeLsum': 31.4815}
```


{/if}

{#if fw === 'pt'}

## Fine-tuningul mT5 cu 🤗 Accelerate[[fine-tuning-mt5-with-accelerate]]

Fine-tuningul nostru cu 🤗 Accelerate este foarte asemănător cu exemplul de clasificare a textului pe care l-am întâlnit în [Capitolul 3](/course/chapter3). Principalele diferențe vor fi necesitatea de a genera în mod explicit rezumatele noastre în timpul antrenării și de a defini modul în care calculăm scorurile ROUGE (reamintim că `Seq2SeqTrainer` a avut grijă de generare pentru noi). Să aruncăm o privire la modul în care putem implementa aceste două cerințe în cadrul 🤗 Accelerate!

### Pregătirea pentru antrenare[[preparing-everything-for-training]]

Primul lucru pe care trebuie să-l facem este să creăm un `DataLoader` pentru fiecare dintre spliturile noastre. Deoarece dataloaders PyTorch așteaptă batchuri de tensori, trebuie să setăm formatul la `"torch"` în dataseturile noastre:

```python
tokenized_datasets.set_format("torch")
```

Acum că avem dataseturi formate doar din tensori, următorul lucru este inițializarea `DataCollatorForSeq2Seq`. Pentru aceasta trebuie să furnizăm o versiune nouă a modelului, așa că hai să îl încărcăm din nou din cache:

```python
model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
```

Putem apoi să instanțiem data collatorul și să îl folosim pentru a ne defini dataloaders:

```python
from torch.utils.data import DataLoader

batch_size = 8
train_dataloader = DataLoader(
    tokenized_datasets["train"],
    shuffle=True,
    collate_fn=data_collator,
    batch_size=batch_size,
)
eval_dataloader = DataLoader(
    tokenized_datasets["validation"], collate_fn=data_collator, batch_size=batch_size
)
```

Următorul lucru este definirea optimizatorului pe care dorim să îl utilizăm. Ca și în celelalte exemple, vom folosi `AdamW`, care funcționează bine pentru majoritatea problemelor:

```python
from torch.optim import AdamW

optimizer = AdamW(model.parameters(), lr=2e-5)
```

În cele din urmă, introducem modelul, optimizatorul și dataloaders în metoda `accelerator.prepare()`:

```python
from accelerate import Accelerator

accelerator = Accelerator()
model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
    model, optimizer, train_dataloader, eval_dataloader
)
```

<Tip>

🚨 Dacă faceți antrenarea pe un TPU, va trebui să mutați tot codul de mai sus într-o funcție de antrenare aparte. Consultați [Capitolul 3](/course/chapter3) pentru mai multe detalii.

</Tip>

Acum că ne-am pregătit obiectele, mai avem trei lucruri de făcut:

* Definirea learning rate schedule.
* Implementarea unei funcții de post-procesare a rezumatelor pentru evaluare.
* Crearea unui repositoriu pe Hub în care să putem trimite modelul nostru.

Pentru learning rate schedule, îl vom utiliza pe cel liniar standard din secțiunile anterioare:

```python
from transformers import get_scheduler

num_train_epochs = 10
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,
)
```

Pentru post-procesare, avem nevoie de o funcție care să împartă rezumatele generate în propoziții separate prin linii noi. Acesta este formatul pe care îl așteaptă metrica ROUGE, iar noi putem realiza acest lucru cu următorul fragment de cod:

```python
def postprocess_text(preds, labels):
    preds = [pred.strip() for pred in preds]
    labels = [label.strip() for label in labels]

    # ROUGE expects a newline after each sentence
    preds = ["\n".join(nltk.sent_tokenize(pred)) for pred in preds]
    labels = ["\n".join(nltk.sent_tokenize(label)) for label in labels]

    return preds, labels
```

Acest lucru ar trebui să vă pară familiar dacă vă amintiți cum am definit funcția `compute_metrics()` a `Seq2SeqTrainer`.

În cele din urmă, trebuie să creăm un repositoriu de modele pe Hugging Face Hub. Pentru aceasta, putem utiliza biblioteca 🤗 Hub intitulată corespunzător . Trebuie doar să definim un nume pentru repositoriul nostru, iar biblioteca are o funcție utilitară pentru a combina ID-ul repositoriul cu profilul utilizatorului:

```python
from huggingface_hub import get_full_repo_name

model_name = "test-bert-finetuned-squad-accelerate"
repo_name = get_full_repo_name(model_name)
repo_name
```

```python out
'lewtun/mt5-finetuned-amazon-en-es-accelerate'
```

Acum putem folosi numele repositoriului pentru a clona o versiune locală în folderul nostru cu rezultate care va stoca artefactele de antrenare:

```python
from huggingface_hub import Repository

output_dir = "results-mt5-finetuned-squad-accelerate"
repo = Repository(output_dir, clone_from=repo_name)
```

Acest lucru ne va permite să trimitem artefactele înapoi la Hub prin apelarea metodei `repo.push_to_hub()` în timpul antrenării! Să încheiem acum analiza noastră prin scrierea buclei de antrenare.

### Bucla de antrenare[[training-loop]]

Bucla de formare pentru sumarizare este destul de asemănătoare cu celelalte exemple 🤗 Accelerate pe care le-am întâlnit și este împărțită aproximativ în patru etape principale:

1. Antrenarea modelului prin iterarea peste toate exemplele din `train_dataloader` pentru fiecare epocă.
2. Generarea rezumatelor modelului la sfârșitul fiecărei epoci, mai întâi prin generarea token-urilor și apoi prin decodarea lor (și a rezumatelor de referință) în text.
3. Calcularea scorurilor ROUGE folosind aceleași tehnici pe care le-am văzut mai devreme.
4. Salvați checkpointurile și încărcați totul pe Hub. Aici ne bazăm pe argumentul `blocking=False` al obiectului `Repository` astfel încât să putem împinge checkointurile pentru fiecare epocă _asincron_. Acest lucru ne permite să continuăm antrenamentul fără a fi nevoiți să așteptăm încărcarea oarecum lentă asociată cu un model de dimensiunea unui GB!

Acești pași pot fi observați în următorul bloc de cod:

```python
from tqdm.auto import tqdm
import torch
import numpy as np

progress_bar = tqdm(range(num_training_steps))

for epoch in range(num_train_epochs):
    # Training
    model.train()
    for step, batch in enumerate(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()
    for step, batch in enumerate(eval_dataloader):
        with torch.no_grad():
            generated_tokens = accelerator.unwrap_model(model).generate(
                batch["input_ids"],
                attention_mask=batch["attention_mask"],
            )

            generated_tokens = accelerator.pad_across_processes(
                generated_tokens, dim=1, pad_index=tokenizer.pad_token_id
            )
            labels = batch["labels"]

            # If we did not pad to max length, we need to pad the labels too
            labels = accelerator.pad_across_processes(
                batch["labels"], dim=1, pad_index=tokenizer.pad_token_id
            )

            generated_tokens = accelerator.gather(generated_tokens).cpu().numpy()
            labels = accelerator.gather(labels).cpu().numpy()

            # Replace -100 in the labels as we can't decode them
            labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
            if isinstance(generated_tokens, tuple):
                generated_tokens = generated_tokens[0]
            decoded_preds = tokenizer.batch_decode(
                generated_tokens, skip_special_tokens=True
            )
            decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)

            decoded_preds, decoded_labels = postprocess_text(
                decoded_preds, decoded_labels
            )

            rouge_score.add_batch(predictions=decoded_preds, references=decoded_labels)

    # Compute metrics
    result = rouge_score.compute()
    # Extract the median ROUGE scores
    result = {key: value.mid.fmeasure * 100 for key, value in result.items()}
    result = {k: round(v, 4) for k, v in result.items()}
    print(f"Epoch {epoch}:", result)

    # 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: {'rouge1': 5.6351, 'rouge2': 1.1625, 'rougeL': 5.4866, 'rougeLsum': 5.5005}
Epoch 1: {'rouge1': 9.8646, 'rouge2': 3.4106, 'rougeL': 9.9439, 'rougeLsum': 9.9306}
Epoch 2: {'rouge1': 11.0872, 'rouge2': 3.3273, 'rougeL': 11.0508, 'rougeLsum': 10.9468}
Epoch 3: {'rouge1': 11.8587, 'rouge2': 4.8167, 'rougeL': 11.7986, 'rougeLsum': 11.7518}
Epoch 4: {'rouge1': 12.9842, 'rouge2': 5.5887, 'rougeL': 12.7546, 'rougeLsum': 12.7029}
Epoch 5: {'rouge1': 13.4628, 'rouge2': 6.4598, 'rougeL': 13.312, 'rougeLsum': 13.2913}
Epoch 6: {'rouge1': 12.9131, 'rouge2': 5.8914, 'rougeL': 12.6896, 'rougeLsum': 12.5701}
Epoch 7: {'rouge1': 13.3079, 'rouge2': 6.2994, 'rougeL': 13.1536, 'rougeLsum': 13.1194}
Epoch 8: {'rouge1': 13.96, 'rouge2': 6.5998, 'rougeL': 13.9123, 'rougeLsum': 13.7744}
Epoch 9: {'rouge1': 14.1192, 'rouge2': 7.0059, 'rougeL': 14.1172, 'rougeLsum': 13.9509}
```

Și asta e tot! După ce executați acest lucru, veți avea un model și rezultate care sunt destul de asemănătoare cu cele obținute cu `Trainer`.

{/if}

## Utilizarea modelului fine-tuned[[using-your-fine-tuned-model]]

Odată ce ați încărcat modelul în Hub, vă puteți juca cu el prin widgetul de inference, fie cu un obiect `pipeline`, după cum urmează:

```python
from transformers import pipeline

hub_model_id = "huggingface-course/mt5-small-finetuned-amazon-en-es"
summarizer = pipeline("summarization", model=hub_model_id)
```

Putem introduce câteva exemple din setul de testare (pe care modelul nu le-a văzut) în pipelineul noastru pentru a avea o idee despre calitatea rezumatelor. Mai întâi, să implementăm o funcție simplă pentru a afișa împreună recenzia, titlul și rezumatul generat:

```python
def print_summary(idx):
    review = books_dataset["test"][idx]["review_body"]
    title = books_dataset["test"][idx]["review_title"]
    summary = summarizer(books_dataset["test"][idx]["review_body"])[0]["summary_text"]
    print(f"'>>> Review: {review}'")
    print(f"\n'>>> Title: {title}'")
    print(f"\n'>>> Summary: {summary}'")
```

Să aruncăm o privire la unul dintre exemplele englezești pe care le primim:

```python
print_summary(100)
```

```python out
'>>> Review: Nothing special at all about this product... the book is too small and stiff and hard to write in. The huge sticker on the back doesn’t come off and looks super tacky. I would not purchase this again. I could have just bought a journal from the dollar store and it would be basically the same thing. It’s also really expensive for what it is.'

'>>> Title: Not impressed at all... buy something else'

'>>> Summary: Nothing special at all about this product'
```

This is not too bad! We can see that our model has actually been able to perform _abstractive_ summarization by augmenting parts of the review with new words. And perhaps the coolest aspect of our model is that it is bilingual, so we can also generate summaries of Spanish reviews:

```python
print_summary(0)
```

```python out
'>>> Review: Es una trilogia que se hace muy facil de leer. Me ha gustado, no me esperaba el final para nada'

'>>> Title: Buena literatura para adolescentes'

'>>> Summary: Muy facil de leer'
```

Rezumatul se traduce prin "Very easy to read" în limba engleză, ceea ce putem vedea că în acest caz a fost extras direct din recenzie. Cu toate acestea, acest lucru arată versatilitatea modelului mT5 și v-a dat o idee despre cum este să aveți de-a face cu un corpus multilingv!

În continuare, ne vom îndrepta atenția către o sarcină puțin mai complexă: antrenarea unui model lingvistic de la zero.

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter7/5.mdx" />

### Introducere[[introduction]]
https://huggingface.co/learn/course/ro/chapter7/1.md

# Introducere[[introduction]]

<CourseFloatingBanner
    chapter={7}
    classNames="absolute z-10 right-0 top-0"
/>

În [Capitolul 3](/course/chapter3), ați văzut cum să faceți fine-tune unui model pentru clasificarea textului. În acest capitol, vom aborda următoarele sarcini NLP:

- Clasificarea tokenilor
- Masked language modeling (precum BERT)
- Sumarizare
- Traducere
- Preantrenare pentru `causal language modeling` (precum GPT-2)
- Răspunsul la întrebări

{#if fw === 'pt'}

Pentru a face acest lucru, va trebui să valorificați tot ceea ce ați învățat despre API-ul `Trainer` și biblioteca 🤗 Accelerate în [Capitolul 3](/course/chapter3), biblioteca 🤗 Datasets în [Capitolul 5](/course/chapter5) și biblioteca 🤗 Tokenizers în [Capitolul 6](/course/chapter6). De asemenea, vom încărca rezultatele noastre în Model Hub, așa cum am făcut în [Capitolul 4](/course/chapter4), astfel încât acesta este cu adevărat capitolul în care totul se unește!

Fiecare secțiune poate fi citită independent și vă va arăta cum să antrenați un model cu API-ul `Trainer` sau cu propria buclă de antrenament, utilizând 🤗 Accelerate. Nu ezitați să săriți peste oricare dintre cele două părți și să vă concentrați pe cea care vă interesează cel mai mult: API-ul `Trainer` este excelent pentru fine-tuning sau antrenarea modelului vostru fără a vă face griji cu privire la ceea ce se întâmplă în spatele scenei, în timp ce bucla de antrenament cu `Accelerate` vă va permite să personalizați mai ușor orice parte doriți.

{:else}

Pentru a face acest lucru, va trebui să valorificați tot ceea ce ați învățat despre API-ul `Trainer` și biblioteca 🤗 Accelerate în [Capitolul 3](/course/chapter3), biblioteca 🤗 Datasets în [Capitolul 5](/course/chapter5) și biblioteca 🤗 Tokenizers în [Capitolul 6](/course/chapter6). De asemenea, vom încărca rezultatele noastre în Model Hub, așa cum am făcut în [Capitolul 4](/course/chapter4), astfel încât acesta este cu adevărat capitolul în care totul se unește!

Fiecare secțiune poate fi citită independent.

{/if}


<Tip>

Dacă citiți secțiunile în succesiune, veți observa că acestea au destul de mult cod și proză în comun. Repetarea este intenționată, pentru a vă permite să intrați (sau să reveniți mai târziu) la orice sarcină care vă interesează și să găsiți un exemplu.

</Tip>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter7/1.mdx" />

### Fine-tuningul la un masked language model[[fine-tuning-a-masked-language-model]]
https://huggingface.co/learn/course/ro/chapter7/3.md

# 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!

</Tip>

## 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!

</Tip>

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.

</Tip>

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
```

<Tip warning={true}>

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.

</Tip>

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.

</Tip>

{#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ă.

</Tip>

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?

</Tip>

{#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).

</Tip>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter7/3.mdx" />

### Antrenarea de la zero a unui model de limbaj cauzal[[training-a-causal-language-model-from-scratch]]
https://huggingface.co/learn/course/ro/chapter7/6.md

# Antrenarea de la zero a unui model de limbaj cauzal[[training-a-causal-language-model-from-scratch]]

{#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/section6_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter7/section6_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/section6_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter7/section6_tf.ipynb"},
]} />

{/if}

Până acum, am folosit în principal modele preantrenate și le-am făcut fine-tuning pentru noi cazuri de utilizare prin reutilizarea weighturilor din preantrenare. După cum am văzut în [Capitolul 1](/course/chapter1), acest lucru este denumit în mod obișnuit _învățare prin transfer_ și este o strategie foarte reușită pentru aplicarea modelelor Transformer la majoritatea cazurilor de utilizare din lumea reală în care datele etichetate sunt puține. În acest capitol, vom adopta o abordare diferită și vom antrena un model complet nou de la zero. Aceasta este o abordare bună dacă aveți multe date și este foarte diferită de datele de preantrenare utilizate pentru modelele disponibile. Cu toate acestea, preantrenarea unui model lingvistic necesită, de asemenea, mult mai multe resurse de calcul decât fine-tuningul unui model existent. Printre exemplele în care poate fi utilă antrenarea unui nou model se numără dataseturile formate din note muzicale, secvențe moleculare precum ADN sau limbaje de programare. Acestea din urmă au câștigat recent teren datorită unor instrumente precum TabNine și Copilot de la GitHub, alimentate de modelul Codex al OpenAI, care pot genera secvențe lungi de cod. Această sarcină de generare a textului este cel mai bine abordată cu modele de limbaj autoregresive sau cauzale, cum ar fi GPT-2.

În această secțiune vom construi o versiune la scară redusă a unui model de generare a codului: ne vom concentra pe completări de o linie în loc de funcții sau clase complete, folosind un subset de cod Python. Atunci când lucrați cu date în Python, sunteți în contact frecvent Python data science stack, formată din bibliotecile `matplotlib`, `seaborn`, `pandas` și `scikit-learn`. Atunci când se utilizează aceste cadre, este frecvent să fie nevoie să se caute comenzi specifice, astfel încât ar fi bine dacă am putea utiliza un model care să efectueze aceste apeluri pentru noi.

<Youtube id="Vpjb1lu0MDk"/>

În [Capitolul 6](/course/chapter6) am creat un tokenizer eficient pentru a procesa codul sursă Python, dar avem nevoie de un dataset la scară largă pe care să preantrenăm un model. Aici, vom aplica tokenizerul nostru la un corpus de cod Python derivat din repositoriile GitHub. Vom utiliza apoi API-ul `Trainer` și 🤗 Accelerate pentru a antrena modelul. Să trecem la treabă!

<iframe src="https://course-demos-codeparrot-ds.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>

Aceasta este de fapt o prezentare a modelului care a fost antrenat și încărcat în Hub folosind codul prezentat în această secțiune. Îl puteți găsi [aici](https://huggingface.co/huggingface-course/codeparrot-ds?text=plt.imshow%28). Rețineți că, deoarece are loc o anumită randomizare în generarea textului, veți obține probabil un rezultat ușor diferit.
 
## Colectarea datelor[[gathering-the-data]]

Codul Python este disponibil din abundență în repositorii de cod, cum ar fi GitHub, pe care le putem utiliza pentru a crea un dataset prin scraping pentru fiecare repositoriu Python. Aceasta a fost abordarea adoptată în [Transformers textbook](https://learning.oreilly.com/library/view/natural-language-processing/9781098136789/) pentru a preantrena un model GPT-2. Folosind o descărcare GitHub de aproximativ 180 GB care conține aproximativ 20 de milioane de fișiere Python numită `codeparrot`, autorii au construit un dataset pe care l-au oferit apoi pe [Hugging Face Hub](https://huggingface.co/datasets/transformersbook/codeparrot).

Cu toate acestea, antrenarea pe întregul corpus consumă timp și puterea calculatorului, iar noi avem nevoie doar de subsetul datasetului referitor la Python data science stack. Așadar, să începem prin filtrarea datasetului `codeparrot` pentru toate fișierele care includ oricare dintre bibliotecile din aceast stack. Din cauza dimensiunii datasetului, dorim să evităm descărcarea acestuia; în schimb, vom utiliza funcția de streaming pentru a-l filtra din mers. Pentru a ne ajuta să filtrăm exemplele de cod care utilizează bibliotecile pe care le-am menționat mai devreme, vom utiliza următoarea funcție:

```py
def any_keyword_in_string(string, keywords):
    for keyword in keywords:
        if keyword in string:
            return True
    return False
```

Să-l testăm pe două exemple:

```py
filters = ["pandas", "sklearn", "matplotlib", "seaborn"]
example_1 = "import numpy as np"
example_2 = "import pandas as pd"

print(
    any_keyword_in_string(example_1, filters), any_keyword_in_string(example_2, filters)
)
```

```python out
False True
```

Putem folosi acest lucru pentru a crea o funcție care va transmite în flux datasetul și va filtra elementele dorite:

```py
from collections import defaultdict
from tqdm import tqdm
from datasets import Dataset


def filter_streaming_dataset(dataset, filters):
    filtered_dict = defaultdict(list)
    total = 0
    for sample in tqdm(iter(dataset)):
        total += 1
        if any_keyword_in_string(sample["content"], filters):
            for k, v in sample.items():
                filtered_dict[k].append(v)
    print(f"{len(filtered_dict['content'])/total:.2%} of data after filtering.")
    return Dataset.from_dict(filtered_dict)
```

Apoi, putem aplica pur și simplu această funcție pe datasetului din flux:

```py
# This cell will take a very long time to execute, so you should skip it and go to
# the next one!
from datasets import load_dataset

split = "train"  # "valid"
filters = ["pandas", "sklearn", "matplotlib", "seaborn"]

data = load_dataset(f"transformersbook/codeparrot-{split}", split=split, streaming=True)
filtered_data = filter_streaming_dataset(data, filters)
```

```python out
3.26% of data after filtering.
```

Acest lucru ne lasă cu aproximativ 3% din setul de date original, care este încă destul de mare - datasetul rezultat este de 6 GB și constă din 600.000 de scripturi Python!

Filtrarea datasetului complet poate dura 2-3 ore, în funcție de calculator și de bandwidth. Dacă nu doriți să parcurgeți singur acest proces îndelungat, vă punem la dispoziție datasetul filtrat pe Hub pentru a-l descărca:

```py
from datasets import load_dataset, DatasetDict

ds_train = load_dataset("huggingface-course/codeparrot-ds-train", split="train")
ds_valid = load_dataset("huggingface-course/codeparrot-ds-valid", split="validation")

raw_datasets = DatasetDict(
    {
        "train": ds_train,  # .shuffle().select(range(50000)),
        "valid": ds_valid,  # .shuffle().select(range(500))
    }
)

raw_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['repo_name', 'path', 'copies', 'size', 'content', 'license'],
        num_rows: 606720
    })
    valid: Dataset({
        features: ['repo_name', 'path', 'copies', 'size', 'content', 'license'],
        num_rows: 3322
    })
})
```

<Tip>

Preantrenarea modelului de limbaj va dura ceva timp. Vă sugerăm să rulați mai întâi bucla de antrenare pe un sample de date prin decomentarea celor două linii parțiale de mai sus și să vă asigurați că antrenarea se finalizează cu succes și că modelele sunt stocate. Nimic nu este mai frustrant decât o rulare de antrenare care eșuează la ultimul pas pentru că ați uitat să creați un folder sau pentru că există o greșeală de tipar la sfârșitul buclei de antrenare!

</Tip>

Să ne uităm la un exemplu din dataset. Vom arăta doar primele 200 de caractere din fiecare câmp:

```py
for key in raw_datasets["train"][0]:
    print(f"{key.upper()}: {raw_datasets['train'][0][key][:200]}")
```

```python out
'REPO_NAME: kmike/scikit-learn'
'PATH: sklearn/utils/__init__.py'
'COPIES: 3'
'SIZE: 10094'
'''CONTENT: """
The :mod:`sklearn.utils` module includes various utilites.
"""

from collections import Sequence

import numpy as np
from scipy.sparse import issparse
import warnings

from .murmurhash import murm
LICENSE: bsd-3-clause'''
```

Putem vedea căci câmpul `content` conține codul pe care dorim ca modelul nostru să se antreneze. Acum că avem un dataset, trebuie să pregătim textele astfel încât acestea să fie într-un format adecvat pentru preantrenare.

## Pregătirea datasetului[[preparing-the-dataset]]

<Youtube id="ma1TrR7gE7I"/>

Primul pas va fi tokenizarea datelor, astfel încât să le putem utiliza pentru antrenare. Deoarece obiectivul nostru este de a autocompleta în principal apeluri scurte de funcții, putem păstra dimensiunea contextului relativ mică. Acest lucru are avantajul că putem antrena modelul mult mai rapid și că necesită semnificativ mai puțină memorie. Dacă este important pentru aplicația voastră să aveți mai mult context (de exemplu, dacă doriți ca modelul să scrie teste unitare pe baza unui fișier cu definiția funcției), asigurați-vă că măriți acest număr, dar rețineți, de asemenea, că acest lucru vine cu utilizare mai mare de memorie GPU. Pentru moment, să fixăm dimensiunea contextului la 128 de tokeni, spre deosebire de 1 024 sau 2 048 utilizate în GPT-2 sau respectiv GPT-3.

Majoritatea documentelor conțin mult mai mult de 128 de cuvinte, astfel încât trunchierea simplă a inputurilor la lungimea maximă ar elimina o mare parte din datasetul nostru. În schimb, vom utiliza opțiunea `return_overflowing_tokens` pentru a tokeniza întreagul input și a o împărți în mai multe bucăți, așa cum am făcut în [Capitolul 6](/course/chapter6/4). De asemenea, vom utiliza opțiunea `return_length` pentru a returna automat lungimea fiecărui fragment creat. Adesea, ultimul fragment va fi mai mic decât dimensiunea contextului, iar noi vom scăpa de aceste bucăți pentru a evita problemele de padding; nu avem nevoie de ele, deoarece oricum avem o mulțime de date.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/chunking_texts.svg" alt="Fragmentarea unui text mare în mai multe bucăți."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/chunking_texts-dark.svg" alt="Fragmentarea unui text mare în mai multe bucăți."/>
</div>

Să vedem exact cum funcționează acest lucru analizând primele două exemple:

```py
from transformers import AutoTokenizer

context_length = 128
tokenizer = AutoTokenizer.from_pretrained("huggingface-course/code-search-net-tokenizer")

outputs = tokenizer(
    raw_datasets["train"][:2]["content"],
    truncation=True,
    max_length=context_length,
    return_overflowing_tokens=True,
    return_length=True,
)

print(f"Input IDs length: {len(outputs['input_ids'])}")
print(f"Input chunk lengths: {(outputs['length'])}")
print(f"Chunk mapping: {outputs['overflow_to_sample_mapping']}")
```

```python out
Input IDs length: 34
Input chunk lengths: [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 117, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 41]
Chunk mapping: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
```

Putem vedea că obținem 34 de segmente în total din aceste două exemple. Uitându-ne la lungimea segmentelor, putem vedea că segmentele de la sfârșitul ambelor documente au mai puțin de 128 de token-uri (117 și, respectiv, 41). Acestea reprezintă doar o mică parte din totalul segmentelor pe care le avem, așa că le putem șterge în siguranță. Cu ajutorul câmpului `overflow_to_sample_mapping`, putem, de asemenea, să reconstituim care segmente au aparținut căror probe de intrare.

Cu această operațiune folosim o caracteristică utilă a funcției `Dataset.map()` din 🤗 Datasets, și anume că nu necesită one-to-one maps; așa cum am văzut în [secțiunea 3](/course/chaptero7/3), putem crea batch-uri cu mai multe sau mai puține elemente decât batch-ul de intrare. Acest lucru este util atunci când efectuăm operațiuni precum augmentarea sau filtrarea datelor care modifică numărul de elemente. În cazul nostru, atunci când tokenizăm fiecare element în segmente de dimensiunea contextului specificat, creăm multe probe din fiecare document. Trebuie doar să ne asigurăm că ștergem coloanele existente, deoarece acestea au o dimensiune conflictuală. Dacă am dori să le păstrăm, am putea să le repetăm în mod corespunzător și să le returnăm în cadrul apelului `Dataset.map()`:

```py
def tokenize(element):
    outputs = tokenizer(
        element["content"],
        truncation=True,
        max_length=context_length,
        return_overflowing_tokens=True,
        return_length=True,
    )
    input_batch = []
    for length, input_ids in zip(outputs["length"], outputs["input_ids"]):
        if length == context_length:
            input_batch.append(input_ids)
    return {"input_ids": input_batch}


tokenized_datasets = raw_datasets.map(
    tokenize, batched=True, remove_columns=raw_datasets["train"].column_names
)
tokenized_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['input_ids'],
        num_rows: 16702061
    })
    valid: Dataset({
        features: ['input_ids'],
        num_rows: 93164
    })
})
```

Avem acum 16,7 milioane de exemple cu 128 de tokenii fiecare, ceea ce corespunde unui total de aproximativ 2,1 miliarde de tokeni. Ca referință, modelele GPT-3 și Codex ale OpenAI sunt antrenate pe 300 și, respectiv, 100 de miliarde de tokeni, unde modelele Codex sunt inițializate din checkpointurile GPT-3. Scopul nostru în această secțiune nu este de a concura cu aceste modele, care pot genera texte lungi și coerente, ci de a crea o versiune la scară redusă care să ofere o funcție rapidă de autocompletare pentru data scientists.

Acum că avem datasetul gata, hai să configurăm modelul!

<Tip>

✏️ **Încercați!** Eliminarea tuturor bucăților care sunt mai mici decât dimensiunea contextului nu a fost o problemă majoră aici, deoarece folosim ferestre de context mici. Pe măsură ce creșteți dimensiunea contextului (sau dacă aveți un corpus de documente scurte), fracțiunea de segmente care sunt aruncate va crește și ea. O modalitate mai eficientă de a pregăti datele este de a uni toate sampleurile tokenizate într-un batch cu un token `eos_token_id` între ele, iar apoi de a efectua chunkingul pe secvențele concatenate. Ca exercițiu, modificați funcția `tokenize()` pentru a utiliza această abordare. Rețineți că veți dori să setați `truncation=False` și să eliminați celelalte argumente din tokenizer pentru a obține secvența completă de token IDs.

</Tip>


## Inițializarea unui nou model[[initializing-a-new-model]]

Primul nostru pas este să inițializăm un model GPT-2. Vom utiliza aceeași configurație pentru modelul nostru ca și pentru modelul GPT-2 mic, deci încărcăm configurația preantrenată, ne asigurăm că dimensiunea tokenizerlui corespunde cu dimensiunea vocabularului modelului și transmitem ID-urile tokenilor `bos` și `eos` (începutul și sfârșitul secvenței):

{#if fw === 'pt'}

```py
from transformers import AutoTokenizer, GPT2LMHeadModel, AutoConfig

config = AutoConfig.from_pretrained(
    "gpt2",
    vocab_size=len(tokenizer),
    n_ctx=context_length,
    bos_token_id=tokenizer.bos_token_id,
    eos_token_id=tokenizer.eos_token_id,
)
```

Cu această configurație, putem încărca un nou model. Rețineți că aceasta este prima dată când nu folosim funcția `from_pretrained()`, deoarece inițializăm noi înșine un model:

```py
model = GPT2LMHeadModel(config)
model_size = sum(t.numel() for t in model.parameters())
print(f"GPT-2 size: {model_size/1000**2:.1f}M parameters")
```

```python out
GPT-2 size: 124.2M parameters
```

{:else}

```py
from transformers import AutoTokenizer, TFGPT2LMHeadModel, AutoConfig

config = AutoConfig.from_pretrained(
    "gpt2",
    vocab_size=len(tokenizer),
    n_ctx=context_length,
    bos_token_id=tokenizer.bos_token_id,
    eos_token_id=tokenizer.eos_token_id,
)
```

Cu această configurație, putem încărca un nou model. Rețineți că aceasta este prima dată când nu folosim funcția `from_pretrained()`, deoarece inițializăm noi înșine un model:

```py
model = TFGPT2LMHeadModel(config)
model(model.dummy_inputs)  # Builds the model
model.summary()
```

```python out
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
transformer (TFGPT2MainLayer multiple                  124242432 
=================================================================
Total params: 124,242,432
Trainable params: 124,242,432
Non-trainable params: 0
_________________________________________________________________
```

{/if}

Modelul nostru are 124 milioane de parametri pe care va trebui să le facem tune. Înainte de a începe antrenarea, trebuie să configurăm un data collator care se va ocupa de crearea batch-urilor. Putem utiliza colatorul `DataCollatorForLanguageModeling`, care este conceput special pentru modelarea limbajului (după cum sugerează subtil numele). Pe lângă stacking și paddingul batchurilor, acesta se ocupă și de crearea labelurilor modelului lingvistic - în modelarea cauzală a limbajului, inputurile servesc și ca labels (doar că sunt decalate cu un element), iar acest data collator le creează din mers în timpul antrenării, astfel încât să nu fie nevoie să duplicăm `input_ids`.

Rețineți că `DataCollatorForLanguageModeling` acceptă atât masked language masking (MLM), cât și causal language modeling(CLM). În mod implicit, acesta pregătește datele pentru MLM, dar putem trece la CLM prin setarea argumentului `mlm=False`:

{#if fw === 'pt'}

```py
from transformers import DataCollatorForLanguageModeling

tokenizer.pad_token = tokenizer.eos_token
data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False)
```

{:else}

```py
from transformers import DataCollatorForLanguageModeling

tokenizer.pad_token = tokenizer.eos_token
data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False, return_tensors="tf")
```

{/if}

Să aruncăm o privire la un exemplu:

```py
out = data_collator([tokenized_datasets["train"][i] for i in range(5)])
for key in out:
    print(f"{key} shape: {out[key].shape}")
```

{#if fw === 'pt'}

```python out
input_ids shape: torch.Size([5, 128])
attention_mask shape: torch.Size([5, 128])
labels shape: torch.Size([5, 128])
```

{:else}

```python out
input_ids shape: (5, 128)
attention_mask shape: (5, 128)
labels shape: (5, 128)
```

{/if}

Putem vedea că exemplele sunt stacked și că toți tensorii au aceeași formă.

{#if fw === 'tf'}

Acum putem utiliza metoda `prepare_tf_dataset()` pentru a converti dataseturile noastre în dataseturi TensorFlow cu ajutorul data collatorului pe care l-am creat mai sus:

```python
tf_train_dataset = model.prepare_tf_dataset(
    tokenized_datasets["train"],
    collate_fn=data_collator,
    shuffle=True,
    batch_size=32,
)
tf_eval_dataset = model.prepare_tf_dataset(
    tokenized_datasets["valid"],
    collate_fn=data_collator,
    shuffle=False,
    batch_size=32,
)
```

{/if}

<Tip warning={true}>

⚠️ Schimbarea inputurilor și a labelurilor pentru a le alinia are loc în interiorul modelului, astfel încât data collatorului doar copiază inputurile pentru a crea labeluri.

</Tip>


Acum avem totul pregătit pentru a ne antrena modelul - până la urmă nu a fost atât de greu! Înainte de a începe antrenamentul, trebuie să ne conectăm la Hugging Face. Dacă lucrați într-un notebook, puteți face acest lucru cu următoarea funcție de utilitate:

```python
from huggingface_hub import notebook_login

notebook_login()
```

Aceasta va afișa un widget în care puteți introduce datele voastre de autentificare Hugging Face.

Dacă nu lucrați într-un notebook, tastați următoarea linie în terminal:

```bash
huggingface-cli login
```

{#if fw === 'pt'}

Tot ce a mai rămas de făcut este să configurăm argumentele de antrenare și să pornim `Trainer`-ul. Vom utiliza un cosine learning rate schedule cu un warmup și o dimensiune efectivă a batch-ului de 256 (`per_device_train_batch_size` * `gradient_accumulation_steps`). Acumularea gradientului este utilizată atunci când un singur batch nu încape în memorie și construiește treptat gradientul prin mai multe treceri înainte/înapoi. Vom vedea acest lucru în acțiune atunci când vom crea bucla de antrenare cu 🤗 Accelerate.

```py
from transformers import Trainer, TrainingArguments

args = TrainingArguments(
    output_dir="codeparrot-ds",
    per_device_train_batch_size=32,
    per_device_eval_batch_size=32,
    evaluation_strategy="steps",
    eval_steps=5_000,
    logging_steps=5_000,
    gradient_accumulation_steps=8,
    num_train_epochs=1,
    weight_decay=0.1,
    warmup_steps=1_000,
    lr_scheduler_type="cosine",
    learning_rate=5e-4,
    save_steps=5_000,
    fp16=True,
    push_to_hub=True,
)

trainer = Trainer(
    model=model,
    tokenizer=tokenizer,
    args=args,
    data_collator=data_collator,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["valid"],
)
```

Acum putem doar să pornim `Trainer`-ul și să așteptăm ca antrenamentul să se termine. În funcție de executarea antrenării pe întregul set de antrenarea sau pe un subset al acestuia, va dura 20 minute, sau respectiv 2 ore, așa că luați câteva cafeluțe și o carte bună de citit!

```py
trainer.train()
```

După finalizarea antrenării, putem trimite modelul și tokenizerul către Hub:

```py
trainer.push_to_hub()
```

{:else}

Tot ce rămâne de făcut este să configurați hiperparametrii de antrenament și să apelați `compile()` și `fit()`. Vom utiliza un program al learning rate cu un anumit warmup pentru a îmbunătăți stabilitatea antrenării:

```py
from transformers import create_optimizer
import tensorflow as tf

num_train_steps = len(tf_train_dataset)
optimizer, schedule = create_optimizer(
    init_lr=5e-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")
```

Acum putem apela `model.fit()` și să așteptăm ca antrenarea să se încheie. În funcție de executarea antrenării pe întregul set de antrenarea sau pe un subset al acestuia, va dura 20 minute, sau respectiv 2 ore, așa că luați câteva cafeluțe și o carte bună de citit!

```py
from transformers.keras_callbacks import PushToHubCallback

callback = PushToHubCallback(output_dir="codeparrot-ds", tokenizer=tokenizer)

model.fit(tf_train_dataset, validation_data=tf_eval_dataset, callbacks=[callback])
```

{/if}

<Tip>

✏️ **Try it out!** Ne-a luat doar aproximativ 30 de linii de cod în plus față de `TrainingArguments` pentru a ajunge de la texte brute la antrenarea GPT-2. Încercați antrenarea cu propriul dataset și vedeți dacă puteți obține rezultate bune!

</Tip>

<Tip>

{#if fw === 'pt'}

💡 Dacă aveți acces la un calculator cu mai multe GPU-uri, încercați să rulați codul acolo. `Trainer` gestionează automat mai multe calculatoare, iar acest lucru poate accelera foarte mult antrenamentul.

{:else}

💡 Dacă aveți acces la un calculator cu mai multe GPU-uri, puteți încerca să utilizați un context `MirroredStrategy` pentru a accelera substanțial antrenarea. Va trebui să creați un obiect `tf.distribute.MirroredStrategy` și să vă asigurați că toate metodele `to_tf_dataset()` sau `prepare_tf_dataset()`, precum și crearea modelului și apelul la `fit()` sunt rulate în contextul său `scope()`. Puteți vedea documentația despre acest lucru [aici] (https://www.tensorflow.org/guide/distributed_training#use_tfdistributestrategy_with_keras_modelfit).

{/if}

</Tip>

## Generarea codului cu un pipeline[[code-generation-with-a-pipeline]]

Acum este momentul adevărului: să vedem cât de bine funcționează de fapt modelul antrenat! Putem vedea în loguri că pierderea a scăzut în mod constant, dar pentru a testa modelul, hai să vedem cât de bine funcționează la câteva încerări. Pentru a face acest lucru, vom încorpora modelul într-un `pipeline` de generare a textului și îl vom pune pe un GPU pentru generații rapide, dacă există unul disponibil:

{#if fw === 'pt'}

```py
import torch
from transformers import pipeline

device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
pipe = pipeline(
    "text-generation", model="huggingface-course/codeparrot-ds", device=device
)
```

{:else}

```py
from transformers import pipeline

course_model = TFGPT2LMHeadModel.from_pretrained("huggingface-course/codeparrot-ds")
course_tokenizer = AutoTokenizer.from_pretrained("huggingface-course/codeparrot-ds")
pipe = pipeline(
    "text-generation", model=course_model, tokenizer=course_tokenizer, device=0
)
```

{/if}

Să începem cu sarcina simplă de a crea un scatter plot:

```py
txt = """\
# create some data
x = np.random.randn(100)
y = np.random.randn(100)

# crearea unui scatter plot cu x, y
"""
print(pipe(txt, num_return_sequences=1)[0]["generated_text"])
```

```python out
# crearea unor date
x = np.random.randn(100)
y = np.random.randn(100)

# crearea scatter plot cu x, y
plt.scatter(x, y)

# crearea scatter
```

Rezultatul pare corect. Funcționează și pentru o operație `pandas`? Să vedem dacă putem crea un `DataFrame` din două array-uri:

```py
txt = """\
# create some data
x = np.random.randn(100)
y = np.random.randn(100)

# creați dataframeul din x și y
"""
print(pipe(txt, num_return_sequences=1)[0]["generated_text"])
```

```python out
# create some data
x = np.random.randn(100)
y = np.random.randn(100)

# crează un dataframe din x și y
df = pd.DataFrame({'x': x, 'y': y})
df.insert(0,'x', x)
for
```

Excelent, acesta este răspunsul corect - deși apoi introduce din nou coloana `x`. Deoarece numărul de tokeni generate este limitat, următoarea buclă `for` este întreruptă. Să vedem dacă putem face ceva un pic mai complex și dacă modelul ne ajută să folosim operația `groupby`:

```py
txt = """\
# dataframe with profession, income and name
df = pd.DataFrame({'profession': x, 'income':y, 'name': z})

# calculați venitul mediu pe profesie
"""
print(pipe(txt, num_return_sequences=1)[0]["generated_text"])
```

```python out
# dataframe with profession, income and name
df = pd.DataFrame({'profession': x, 'income':y, 'name': z})

# calculeză venitul mediu pe profesie
profession = df.groupby(['profession']).mean()

# calculează
```

Nu este rău; acesta este modul corect de a face acest lucru. În cele din urmă, să vedem dacă îl putem folosi și pentru `scikit-learn` și să configurăm un model Random Forest:

```py
txt = """
# import random forest regressor from scikit-learn
from sklearn.ensemble import RandomForestRegressor

# ajustați modelul Random Forest cu 300 de estimatori pe X, y:
"""
print(pipe(txt, num_return_sequences=1)[0]["generated_text"])
```

```python out
# import random forest regressor from scikit-learn
from sklearn.ensemble import RandomForestRegressor

# ajustați modelul Random Forest cu 300 de estimatori pe X, y:
rf = RandomForestRegressor(n_estimators=300, random_state=random_state, max_depth=3)
rf.fit(X, y)
rf
```

{#if fw === 'tf'}

Privind la aceste câteva exemple, se pare că modelul a învățat o parte din sintaxa Python data science stack. Desigur, ar trebui să evaluăm modelul mai amănunțit înainte de a-l implementa în lumea reală, totuși acesta este un prototip impresionant.

{:else}

Privind aceste câteva exemple, se pare că modelul a învățat o parte din sintaxa Python data science stack(desigur, ar trebui să o evaluăm mai bine înainte de a implementa modelul în lumea reală). Cu toate acestea, uneori este nevoie de o mai mare personalizare a antrenării modelului pentru a obține performanța necesară pentru un anumit caz de utilizare. De exemplu, dacă am dori să actualizăm dinamic dimensiunea batch-ului sau să avem o buclă de antrenare condiționată care trece peste exemplele proaste din mers? O opțiune ar fi să facem subclass la `Trainer` și să adăugăm modificările necesare, dar uneori este mai simplu să scriem bucla de antrenare de la zero. Aici intervine 🤗 Accelerate.

{/if}

{#if fw === 'pt'}

## Antrenarea cu 🤗 Accelerate[[training-with-accelerate]]

Am văzut cum să antrenăm un model cu `Trainer`, care poate permite o anumită personalizare. Cu toate acestea, uneori dorim control deplin asupra buclei de antrenare sau dorim să facem unele schimbări exotice. În acest caz, 🤗 Accelerate este o alegere excelentă, iar în această secțiune vom parcurge pașii de utilizare a acestuia pentru a ne antrena modelul. Pentru a face lucrurile mai interesante, vom adăuga și un twist buclei de antrenare.

<Youtube id="Hm8_PgVTFuc"/>

Deoarece suntem interesați în principal de o autocompletare sensibilă pentru bibliotecile din domeniul data science, este logic să acordăm mai multă importanță exemplelor de antrenare care utilizează mai mult aceste biblioteci. Putem identifica cu ușurință aceste exemple prin utilizarea unor cuvinte-cheie precum `plt`, `pd`, `sk`, `fit` și `predict`, care sunt cele mai frecvente nume de import pentru `matplotlib.pyplot`, `pandas` și `sklearn`, precum și modelul fit/predict al acestora din urmă. Dacă acestea sunt reprezentate fiecare ca un singur simbol, putem verifica cu ușurință dacă apar în secvența de input. Tokenii pot avea un prefix de spațiu, deci vom verifica și aceste versiuni în vocabularul tokenizerului. Pentru a verifica dacă funcționează, vom adăuga un token de test care ar trebui să fie împărțit în mai mulți tokeni:

```py
keytoken_ids = []
for keyword in [
    "plt",
    "pd",
    "sk",
    "fit",
    "predict",
    " plt",
    " pd",
    " sk",
    " fit",
    " predict",
    "testtest",
]:
    ids = tokenizer([keyword]).input_ids[0]
    if len(ids) == 1:
        keytoken_ids.append(ids[0])
    else:
        print(f"Keyword has not single token: {keyword}")
```

```python out
'Keyword has not single token: testtest'
```

Grozav, se pare că funcționează bine! Acum putem scrie o funcție de pierdere personalizată care ia ca secvența de input, logurile și tokenii cheie pe care tocmai le-am selectat. În primul rând, trebuie să aliniem logurile și inputurile: secvența de intrare deplasată cu o unitate la dreapta formează labeluri, deoarece următorul tokenul este labelul pentru tokenul curent. Putem realiza acest lucru începând cu labelurile de la al doilea token al secvenței de intrare, deoarece modelul nu face o predicție pentru primul token în orice caz. Apoi tăiem ultimul logit, deoarece nu avem un label pentru tokenul care urmează secvenței complete de intrare. Astfel, putem calcula pierderea per sample și putem număra aparițiile tuturor cuvintelor-cheie în fiecare sample. În cele din urmă, calculăm media weighturilor pe fiecare sample folosind aparițiile ca weighturi. Deoarece nu dorim să eliminăm toate sampleurile care nu au cuvinte-cheie, adăugăm 1 la weighturi:

```py
from torch.nn import CrossEntropyLoss
import torch


def keytoken_weighted_loss(inputs, logits, keytoken_ids, alpha=1.0):
    # Shift so that tokens < n predict n
    shift_labels = inputs[..., 1:].contiguous()
    shift_logits = logits[..., :-1, :].contiguous()
    # Calculate per-token loss
    loss_fct = CrossEntropyLoss(reduce=False)
    loss = loss_fct(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1))
    # Resize and average loss per sample
    loss_per_sample = loss.view(shift_logits.size(0), shift_logits.size(1)).mean(axis=1)
    # Calculate and scale weighting
    weights = torch.stack([(inputs == kt).float() for kt in keytoken_ids]).sum(
        axis=[0, 2]
    )
    weights = alpha * (1.0 + weights)
    # Calculate weighted average
    weighted_loss = (loss_per_sample * weights).mean()
    return weighted_loss
```

Înainte de a începe antrenamentul cu această nouă funcție de pierdere minunată, trebuie să pregătim câteva lucruri:

- Avem nevoie de dataloaders pentru a încărca datele în batch-uri.
- Trebuie să configurăm parametrii de scădere a weighturilor.
- Din când în când, dorim să evaluăm, astfel încât este logic să includem codul de evaluare într-o funcție.

Să începem cu dataloaders. Trebuie doar să setăm formatul datasetului la `"torch"`, iar apoi îl putem trece la un `DataLoader` PyTorch cu dimensiunea corespunzătoare a batch-lui:

```py
from torch.utils.data.dataloader import DataLoader

tokenized_datasets.set_format("torch")
train_dataloader = DataLoader(tokenized_datasets["train"], batch_size=32, shuffle=True)
eval_dataloader = DataLoader(tokenized_datasets["valid"], batch_size=32)
```

În continuare, grupăm parametrii astfel încât optimizatorul să știe care dintre aceștia vor primi o scădere suplimentară a weighturilor. De obicei, toți termenii weighturilor bias și LayerNorm sunt scutiți de acest lucru; iată cum putem face acest lucru:

```py
weight_decay = 0.1


def get_grouped_params(model, no_decay=["bias", "LayerNorm.weight"]):
    params_with_wd, params_without_wd = [], []
    for n, p in model.named_parameters():
        if any(nd in n for nd in no_decay):
            params_without_wd.append(p)
        else:
            params_with_wd.append(p)
    return [
        {"params": params_with_wd, "weight_decay": weight_decay},
        {"params": params_without_wd, "weight_decay": 0.0},
    ]
```

Deoarece dorim să evaluăm modelul în mod regulat pe setul de validare în timpul antrenării, trebuie să scriem o funcție și pentru acest lucru. Aceasta rulează pur și simplu prin dataloaderul de evaluare și adună toate pierderile în cadrul proceselor:

```py
def evaluate():
    model.eval()
    losses = []
    for step, batch in enumerate(eval_dataloader):
        with torch.no_grad():
            outputs = model(batch["input_ids"], labels=batch["input_ids"])

        losses.append(accelerator.gather(outputs.loss))
    loss = torch.mean(torch.cat(losses))
    try:
        perplexity = torch.exp(loss)
    except OverflowError:
        perplexity = float("inf")
    return loss.item(), perplexity.item()
```

Cu funcția `evaluate()` putem raporta pierderile și [perplexitatea](/course/chapter7/3) la intervale regulate. În continuare, redefinim modelul nostru pentru a ne asigura că antrenăm din nou de la zero:

```py
model = GPT2LMHeadModel(config)
```

Apoi putem defini optimizatorul nostru, folosind funcția de mai devreme pentru a împărți parametrii pentru scăderea weighturilor:

```py
from torch.optim import AdamW

optimizer = AdamW(get_grouped_params(model), lr=5e-4)
```

Acum să pregătim modelul, optimizatorul și dataloaderurile, astfel încât să putem începe antrenamentul:

```py
from accelerate import Accelerator

accelerator = Accelerator(fp16=True)

model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
    model, optimizer, train_dataloader, eval_dataloader
)
```

<Tip>

🚨 Dacă antrenați pe un TPU, va trebui să mutați tot codul începând cu celula de mai sus într-o funcție de antrenare dedicată. Consultați [Capitolul 3](/course/chapter3) pentru mai multe detalii.

</Tip>

Acum că am trimis `train_dataloader` la `accelerator.prepare()`, putem utiliza lungimea acestuia pentru a calcula numărul de pași de antrenare. Rețineți că ar trebui să facem acest lucru întotdeauna după ce pregătim dataloaderurile, deoarece această metodă îi va modifica lungimea. Utilizăm un program liniar clasic de la rata de învățare la 0:

```py
from transformers import get_scheduler

num_train_epochs = 1
num_update_steps_per_epoch = len(train_dataloader)
num_training_steps = num_train_epochs * num_update_steps_per_epoch

lr_scheduler = get_scheduler(
    name="linear",
    optimizer=optimizer,
    num_warmup_steps=1_000,
    num_training_steps=num_training_steps,
)
```

În cele din urmă, pentru a trimite modelul nostru către Hub, va trebui să creăm un obiect `Repository` într-un folder de lucru. În primul rând, conectați-vă la Hugging Face Hub, dacă nu sunteți deja conectat. Vom determina numele repositoriului pornind de la ID-ul modelului pe care dorim să îl atribuim modelului nostru (nu ezitați să înlocuiți `repo_name` cu propria alegere; acesta trebuie doar să conțină numele vostru de utilizator, ceea ce face funcția `get_full_repo_name()`):

```py
from huggingface_hub import Repository, get_full_repo_name

model_name = "codeparrot-ds-accelerate"
repo_name = get_full_repo_name(model_name)
repo_name
```

```python out
'sgugger/codeparrot-ds-accelerate'
```

Apoi putem clona acel repositoriu într-un folder local. Dacă există deja, acest folder local ar trebui să fie o clonă existentă a repositoriul cu care lucrăm:

```py
output_dir = "codeparrot-ds-accelerate"
repo = Repository(output_dir, clone_from=repo_name)
```

Acum putem încărca orice salvăm în `output_dir` prin apelarea metodei `repo.push_to_hub()`. Acest lucru ne va ajuta să încărcăm modelele intermediare la sfârșitul fiecărei epoci.

Înainte de antrenament, să efectuăm un test rapid pentru a vedea dacă funcția de evaluare funcționează corect:

```py
evaluate()
```

```python out
(10.934126853942871, 56057.14453125)
```

Acestea sunt valori foarte ridicate pentru pierdere și perplexitate, dar acest lucru nu este surprinzător, deoarece nu am antrenat încă modelul. Astfel, avem totul pregătit pentru a scrie partea principală a scriptului de antrenare: bucla de antrenare. În bucla de antrenare, iterăm peste dataloader și transmitem batch-urile către model. Cu logurile, putem apoi evalua funcția noastră de pierdere personalizată. Redimensionăm pierderea în funcție de numărul de etape de acumulare a gradientului pentru a nu crea pierderi mai mari atunci când agregăm mai multe etape. Înainte de a optimiza, comprimăm, de asemenea, gradienții pentru o mai bună convergență. În cele din urmă, la fiecare câțiva pași, evaluăm modelul pe setul de evaluare cu noua noastră funcție `evaluate()`:

```py
from tqdm.notebook import tqdm

gradient_accumulation_steps = 8
eval_steps = 5_000

model.train()
completed_steps = 0
for epoch in range(num_train_epochs):
    for step, batch in tqdm(
        enumerate(train_dataloader, start=1), total=num_training_steps
    ):
        logits = model(batch["input_ids"]).logits
        loss = keytoken_weighted_loss(batch["input_ids"], logits, keytoken_ids)
        if step % 100 == 0:
            accelerator.print(
                {
                    "samples": step * samples_per_step,
                    "steps": completed_steps,
                    "loss/train": loss.item() * gradient_accumulation_steps,
                }
            )
        loss = loss / gradient_accumulation_steps
        accelerator.backward(loss)
        if step % gradient_accumulation_steps == 0:
            accelerator.clip_grad_norm_(model.parameters(), 1.0)
            optimizer.step()
            lr_scheduler.step()
            optimizer.zero_grad()
            completed_steps += 1
        if (step % (eval_steps * gradient_accumulation_steps)) == 0:
            eval_loss, perplexity = evaluate()
            accelerator.print({"loss/eval": eval_loss, "perplexity": perplexity})
            model.train()
            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 step {step}", blocking=False
                )
```

Și asta e tot - acum aveți propria buclă de antrenare personalizată pentru modele de limbaj cauzal, cum ar fi GPT-2, pe care o puteți personaliza în continuare în funcție de nevoile voastre.

<Tip>

✏️ **încercați!** Fie vă creați propria funcție de pierdere personalizată, adaptată la cazul vostru de utilizare, fie adăugați un alt pas personalizat în bucla de antrenare.

</Tip>

<Tip>

✏️ **încercați!** Atunci când efectuați experimente de antrenare de lungă durată, este o idee bună să înregistrați parametrii importanți utilizând instrumente precum TensorBoard sau Weights & Biases. Adăugați o logare adecvată la bucla de antrenare, astfel încât să puteți verifica întotdeauna cum decurge antrenarea.

</Tip>

{/if}


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter7/6.mdx" />

### Înțelegerea LLM-urilor[[mastering-llms]]
https://huggingface.co/learn/course/ro/chapter7/8.md

# Înțelegerea LLM-urilor[[mastering-llms]]

<CourseFloatingBanner
    chapter={7}
    classNames="absolute z-10 right-0 top-0"
/>

Dacă ați ajuns până aici în curs, felicitări – acum aveți toate cunoștințele și instrumentele necesare pentru a aborda (aproape) orice sarcină de procesare a limbajului cu 🤗 Transformers și ecosistemul Hugging Face!

## De la NLP la LLM-uri

Deși am acoperit multe sarcini tradiționale de NLP în acest curs, domeniul a fost revoluționat de Modelele Mari de Limbaj (LLM-uri). Aceste modele au extins dramatic ceea ce este posibil în procesarea limbajului:

- Pot gestiona mai multe sarcini fără fine-tuning specific pentru fiecare sarcină
- Excelează la urmarea instrucțiunilor și adaptarea la contexte diferite
- Pot genera text coerent și adecvat contextului pentru diverse aplicații
- Pot realiza raționamente și rezolva probleme complexe prin tehnici precum chain-of-thought prompting

Abilitățile fundamentale de NLP pe care le-ați învățat sunt în continuare esențiale pentru a lucra eficient cu LLM-urile. Înțelegerea tokenizării, a arhitecturilor de modele, a metodelor de fine-tuning și a metricilor de evaluare vă oferă cunoștințele necesare pentru a valorifica la maximum potențialul LLM-urilor.

Am văzut o mulțime de data collators, așa că am făcut acest mic videoclip pentru a vă ajuta să găsiți cel pe care să îl utilizați pentru fiecare sarcină:

<Youtube id="-RPeakdlHYo"/>

După finalizarea acestui tur fulger prin sarcinile de bază ale procesării limbajului, ar trebui să:

* Știți care arhitecturi (encoder, decoder sau encoder-decoder) sunt cele mai potrivite pentru fiecare sarcină
* Înțelegeți diferența dintre preantrenarea și fine-tuning-ul unui model lingvistic
* Știți cum să antrenați modele Transformer folosind fie API-ul `Trainer` și funcționalitățile de antrenare distribuită ale 🤗 Accelerate, fie TensorFlow și Keras, în funcție de traseul pe care l-ați urmat
* Înțelegeți semnificația și limitele metricilor precum ROUGE și BLEU pentru sarcinile de generare de text
* Știți cum să interacționați cu modelele voastre ajustate, atât pe Hub, cât și folosind `pipeline` din 🤗 Transformers
* Să apreciați modul în care LLM-urile se bazează pe și extind tehnicile tradiționale de NLP

În ciuda tuturor acestor cunoștințe, va veni un moment în care fie veți întâlni un bug dificil în codul vostru, fie veți avea o întrebare despre cum să rezolvați o anumită problemă de procesare a limbajului. Din fericire, comunitatea Hugging Face este aici pentru a vă ajuta! În ultimul capitol al acestei părți a cursului, vom explora cum puteți depana modelele Transformer și cum puteți solicita ajutor în mod eficient.

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter7/8.mdx" />

### Traducere[[translation]]
https://huggingface.co/learn/course/ro/chapter7/4.md

# Traducere[[translation]]

{#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/section4_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter7/section4_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/section4_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter7/section4_tf.ipynb"},
]} />

{/if}

Să trecem acum la traducere. Aceasta este o altă [sarcină de sequence-to-sequence](/course/chapter1/7), ceea ce înseamnă că este o problemă care poate fi formulată ca trecerea de la o secvență la alta. În acest sens, problema este destul de apropiată de [sumarizare](/course/chapter7/6) și ați putea adapta ceea ce vom vedea aici la alte probleme de la sequence-to-sequence, cum ar fi:

- **Style transfer**: Crearea unui model care *traduce* texte scrise într-un anumit stil în altul (de exemplu, din formal în casual sau din engleza Shakespeariană în engleza modernă)
- **Generative question answering**: Crearea unui model care generează răspunsuri la întrebări, fiind dat un context

<Youtube id="1JvfrvZgi6c"/>

Dacă aveți un corpus suficient de mare de texte în două (sau mai multe) limbi, puteți antrena un nou model de traducere de la zero, așa cum vom face în secțiunea despre [causal language modeling] (/course/chapter7/6). Cu toate acestea, va fi mai rapid să faceți fine-tune unui model de traducere existent, fie că este vorba de un model multilingv precum mT5 sau mBART, pe care doriți să îi faceți fine-tune pentru o anumită pereche de limbi, sau chiar un model specializat pentru traducerea dintr-o limbă în alta, pe care doriți să îl perfecționați pentru corpusul vostru specific.

În această secțiune, vom pune face fine-tune unui model Marian preantrenat pentru a traduce din engleză în franceză (deoarece mulți dintre angajații Hugging Face vorbesc ambele limbi) pe [datasetul KDE4](https://huggingface.co/datasets/kde4), care este un set de fișiere localizate pentru [KDE apps](https://apps.kde.org/). Modelul pe care îl vom utiliza a fost preantrenat pe un corpus mare de texte în franceză și engleză preluate din [datasetul Opus](https://opus.nlpl.eu/), care conține de fapt datasetul KDE4. Dar chiar dacă modelul preantrenat pe care îl folosim a văzut aceste date în timpul preantrenării sale, vom vedea că putem obține o versiune mai bună a acestuia după fine-tuning.

Odată ce am terminat, vom avea un model capabil să facă predicții ca acesta:

<iframe src="https://course-demos-marian-finetuned-kde4-en-to-fr.hf.space" frameBorder="0" height="350" 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>

<a class="flex justify-center" href="/huggingface-course/marian-finetuned-kde4-en-to-fr">
<img class="block dark:hidden lg:w-3/5" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/modeleval-marian-finetuned-kde4-en-to-fr.png" alt="One-hot encoded labels pentru răspunderea la întrebări."/>
<img class="hidden dark:block lg:w-3/5" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/modeleval-marian-finetuned-kde4-en-to-fr-dark.png" alt="One-hot encoded labels răspunderea la întrebări."/>
</a>

Ca și în secțiunile anterioare, puteți găsi modelul pe care îl vom antrena și încărca în Hub utilizând codul de mai jos și puteți verifica predicțiile acestuia [aici](https://huggingface.co/huggingface-course/marian-finetuned-kde4-en-to-fr?text=This+plugin+allows+you+to+automatically+translate+web+pages+between+several+languages.).

## Pregătirea datelor[[preparing-the-data]]

Pentru a face fine-tune sau a antrena un model de traducere de la zero, vom avea nevoie de un dataset adecvat pentru această sarcină. După cum am menționat anterior, vom utiliza [datasetul KDE4](https://huggingface.co/datasets/kde4) în această secțiune, dar puteți adapta codul pentru a utiliza propriile date destul de ușor, atâta timp cât aveți perechi de propoziții în cele două limbi din și în care doriți să traduceți. Consultați [Capitolul 5](/course/chapter5) dacă aveți nevoie de o reamintire a modului de încărcare a datelor personalizate într-un `Dataset`.

### Datasetul KDE4[[the-kde4-dataset]]

Ca de obicei, descărcăm datasetul nostru folosind funcția `load_dataset()`:

```py
from datasets import load_dataset

raw_datasets = load_dataset("kde4", lang1="en", lang2="fr")
```

Dacă doriți să lucrați cu o pereche de limbi diferită, le puteți specifica prin codurile lor. Un total de 92 de limbi sunt disponibile pentru acest dataset; le puteți vedea pe toate prin extinderea labelurilor de limbă pe [dataset cardul acesteia](https://huggingface.co/datasets/kde4).

<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/language_tags.png" alt="Limba disponibilă pentru datasetul KDE4." width="100%">

Să aruncăm o privire la dataset:

```py
raw_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['id', 'translation'],
        num_rows: 210173
    })
})
```

Avem 210 173 de perechi de propoziții, dar într-o singură împărțire, deci va trebui să ne creăm propriul set de validare. Așa cum am văzut în [Capitolul 5](/course/chapter5), un `Dataset` are o metodă `train_test_split()` care ne poate ajuta. Vom furniza un seed pentru reproductibilitate:

```py
split_datasets = raw_datasets["train"].train_test_split(train_size=0.9, seed=20)
split_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['id', 'translation'],
        num_rows: 189155
    })
    test: Dataset({
        features: ['id', 'translation'],
        num_rows: 21018
    })
})
```

Putem redenumi cheia `"test"` în `"validation"` astfel:

```py
split_datasets["validation"] = split_datasets.pop("test")
```

Acum să aruncăm o privire la un element al datasetului:

```py
split_datasets["train"][1]["translation"]
```

```python out
{'en': 'Default to expanded threads',
 'fr': 'Par défaut, développer les fils de discussion'}
```

Obținem un dicționar cu două propoziții în perechea de limbi solicitate. O particularitate a acestui dataset plin de termeni tehnici din domeniul informaticii este că toate sunt traduse integral în franceză. Cu toate acestea, inginerii francezi lasă majoritatea cuvintelor specifice informaticii în engleză atunci când vorbesc. Aici, de exemplu, cuvântul "threads" ar putea foarte bine să apară într-o propoziție în limba franceză, în special într-o conversație tehnică; dar în acest dataset a fost tradus în "fils de discussion". Modelul preantrenat pe care îl folosim, care a fost preantrenat pe un corpus mai mare de propoziții în franceză și engleză, alege opțiunea mai ușoară de a lăsa cuvântul așa cum este:

```py
from transformers import pipeline

model_checkpoint = "Helsinki-NLP/opus-mt-en-fr"
translator = pipeline("translation", model=model_checkpoint)
translator("Default to expanded threads")
```

```python out
[{'translation_text': 'Par défaut pour les threads élargis'}]
```

Un alt exemplu al acestui comportament poate fi văzut cu cuvântul "plugin", care nu este oficial un cuvânt francez, dar pe care majoritatea vorbitorilor nativi îl vor înțelege și nu se vor obosi să îl traducă.
În datasetul KDE4, acest cuvânt a fost tradus în franceză în termenul puțin mai oficial "module d'extension":

```py
split_datasets["train"][172]["translation"]
```

```python out
{'en': 'Unable to import %1 using the OFX importer plugin. This file is not the correct format.',
 'fr': "Impossible d'importer %1 en utilisant le module d'extension d'importation OFX. Ce fichier n'a pas un format correct."}
```

Modelul nostru preantrenat, cu toate acestea, rămâne la cuvântul englez compact și familiar:

```py
translator(
    "Unable to import %1 using the OFX importer plugin. This file is not the correct format."
)
```

```python out
[{'translation_text': "Impossible d'importer %1 en utilisant le plugin d'importateur OFX. Ce fichier n'est pas le bon format."}]
```

Va fi interesant de văzut dacă modelul nostru fine-tuned reține aceste particularități ale datasetului(spoiler alert: va reține).

<Youtube id="0Oxphw4Q9fo"/>

<Tip>

✏️ **Rândul tău!** Un alt cuvânt englezesc care este adesea folosit în franceză este "email". Găsiți primul sample din datasetul de antrenare care utilizează acest cuvânt. Cum este tradus? Cum traduce modelul preantrenat aceeași propoziție în limba engleză?

</Tip>

### Procesarea datelor[[processing-the-data]]

<Youtube id="XAR8jnZZuUs"/>

Ar trebui să știți deja cum stă treaba: toate textele trebuie convertite în seturi de ID-uri token, astfel încât modelul să le poată înțelege. Pentru această sarcină, va trebui să tokenizăm atât inputurile, cât și targeturile. Prima noastră sarcină este să creăm obiectul `tokenizer`. După cum am menționat mai devreme, vom utiliza un model Marian preantrenat din engleză în franceză. Dacă încercați acest cod cu o altă pereche de limbi, asigurați-vă că adaptați checkpointul modelului. Organizația [Helsinki-NLP](https://huggingface.co/Helsinki-NLP) oferă mai mult de o mie de modele în mai multe limbi.

```python
from transformers import AutoTokenizer

model_checkpoint = "Helsinki-NLP/opus-mt-en-fr"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint, return_tensors="pt")
```

De asemenea, puteți înlocui `model_checkpoint` cu orice alt model preferat din [Hub](https://huggingface.co/models) sau cu un folder local în care ați salvat un model preantrenat și un tokenizer.

<Tip>

💡 Dacă utilizați un tokenizer multilingv, cum ar fi mBART, mBART-50 sau M2M100, va trebui să setați codurile de limbă ale inputurilor și targeturilor în tokenizer prin setarea `tokenizer.src_lang` și `tokenizer.tgt_lang` la valorile corecte.

</Tip>

Pregătirea datelor noastre este destul de simplă. Trebuie să rețineți un singur lucru; trebuie să vă asigurați că tokenizerul procesează targeturile în limba de ieșire (aici, franceză). Puteți face acest lucru trecând targeturile la argumentul `text_targets` al metodei `__call__` a tokenizerului.

Pentru a vedea cum funcționează acest lucru, să procesăm un sample din fiecare limbă din setul de antrenare:

```python
en_sentence = split_datasets["train"][1]["translation"]["en"]
fr_sentence = split_datasets["train"][1]["translation"]["fr"]

inputs = tokenizer(en_sentence, text_target=fr_sentence)
inputs
```

```python out
{'input_ids': [47591, 12, 9842, 19634, 9, 0], 'attention_mask': [1, 1, 1, 1, 1, 1], 'labels': [577, 5891, 2, 3184, 16, 2542, 5, 1710, 0]}
```

După cum putem vedea, rezultatul conține ID-urile de input asociate cu propoziția în limba engleză, în timp ce ID-urile asociate cu cea în limba franceză sunt stocate în câmpul `labels`. Dacă uitați să indicați că tokenizați labelurile, acestea vor fi tokenizate de către tokenizerul de input, ceea ce în cazul unui model Marian nu va merge deloc bine:

```python
wrong_targets = tokenizer(fr_sentence)
print(tokenizer.convert_ids_to_tokens(wrong_targets["input_ids"]))
print(tokenizer.convert_ids_to_tokens(inputs["labels"]))
```

```python out
['▁Par', '▁dé', 'f', 'aut', ',', '▁dé', 've', 'lop', 'per', '▁les', '▁fil', 's', '▁de', '▁discussion', '</s>']
['▁Par', '▁défaut', ',', '▁développer', '▁les', '▁fils', '▁de', '▁discussion', '</s>']
```

După cum se poate observa, utilizarea tokenizerului englez pentru preprocesarea unei propoziții franceze are ca rezultat mult mai mulți tokeni, deoarece tokenizerul nu cunoaște niciun cuvânt francez (cu excepția celor care apar și în limba engleză, precum "discussion").

Deoarece `inputs` este un dicționar cu cheile noastre obișnuite (ID-uri de input, attention mask etc.), ultimul pas este să definim funcția de preprocesare pe care o vom aplica dataseturilor:

```python
max_length = 128


def preprocess_function(examples):
    inputs = [ex["en"] for ex in examples["translation"]]
    targets = [ex["fr"] for ex in examples["translation"]]
    model_inputs = tokenizer(
        inputs, text_target=targets, max_length=max_length, truncation=True
    )
    return model_inputs
```

Rețineți că am stabilit aceeași lungime maximă pentru inputurile și outputurile noastre. Deoarece textele cu care avem de-a face par destul de scurte, vom folosi 128.

<Tip>

💡 Dacă utilizați un model T5 (mai precis, unul dintre checkpointurile `t5-xxx`), modelul se va aștepta ca inputurile text să aibă un prefix care să indice sarcina în cauză, cum ar fi `translate: din engleză în franceză:`.

</Tip>

<Tip warning={true}>

⚠️ Nu acordăm atenție attention maskului a targeturilor, deoarece modelul nu se va aștepta la aceasta. În schimb, labelurile corespunzătoare unui padding token trebuie setate la `-100`, astfel încât acestea să fie ignorate în calculul pierderilor. Acest lucru va fi făcut mai târziu de data collatorul nostru, deoarece aplicăm padding dinamic, dar dacă utilizați padding aici, ar trebui să adaptați funcția de preprocesare pentru a seta toate labelurile care corespund simbolului de padding la `-100`.

</Tip>

Acum putem aplica această preprocesare dintr-o singură dată pe toate diviziunile datasetului nostru:

```py
tokenized_datasets = split_datasets.map(
    preprocess_function,
    batched=True,
    remove_columns=split_datasets["train"].column_names,
)
```

Acum că datele au fost preprocesate, suntem pregătiți să facem fine-tune modelul nostru preantrenat!

{#if fw === 'pt'}

## Fine-tuningul modelului cu API-ul `Trainer`[[fine-tuning-the-model-with-the-trainer-api]]

Codul real care utilizează `Trainer` va fi același ca înainte, cu o singură mică schimbare: folosim aici un [`Seq2SeqTrainer`](https://huggingface.co/transformers/main_classes/trainer.html#seq2seqtrainer), care este o subclasă a `Trainer` care ne va permite să ne ocupăm în mod corespunzător de evaluare, folosind metoda `generate()` pentru a prezice rezultatele din inputuri. Vom analiza acest aspect mai în detaliu atunci când vom vorbi despre calculul metricelor.

În primul rând, avem nevoie de un model pe care să îl aplicăm fine-tuningul. Vom utiliza API-ul obișnuit `AutoModel`:

```py
from transformers import AutoModelForSeq2SeqLM

model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
```

{:else}

## Fine-tuningul modelului cu Keras[[fine-tuning-the-model-with-keras]]

În primul rând, avem nevoie de un model pe care să îl aplicăm fine-tuningul. Vom folosi API-ul obișnuit `AutoModel`:

```py
from transformers import TFAutoModelForSeq2SeqLM

model = TFAutoModelForSeq2SeqLM.from_pretrained(model_checkpoint, from_pt=True)
```

<Tip warning={false}>

💡 Checkpointul `Helsinki-NLP/opus-mt-en-fr` are doar weighturi PyTorch, deci
veți primi o eroare dacă încercați să încărcați modelul fără a utiliza argumentul
`from_pt=True` în metoda `from_pretrained()`. Atunci când specificați
`from_pt=True`, biblioteca va descărca și va converti automat
weighturile PyTorch pentru voi. După cum puteți vedea, este foarte simplu să vă schimbați între
frameworkuri în 🤗 Transformers!

</Tip>

{/if}

Rețineți că de data aceasta folosim un model care a fost antrenat pe o sarcină de traducere și care poate fi utilizat deja, astfel încât nu există niciun avertisment cu privire la weighturile lipsă sau la cele nou inițializate.

### Data collation[[data-collation]]

Vom avea nevoie de un data collator care să se ocupe de paddingul pentru batching-ul dinamic. Nu putem folosi un `DataCollatorWithPadding` ca în [Capitolul 3](/course/chapter3) în acest caz, pentru că acesta doar face padding inputurilor (input Is, attention mask și token type IDs). Labelurile noastre ar trebui, de asemenea, să fie padded la lungimea maximă întâlnită în labeluri. Și, așa cum am menționat anterior, valoarea de padding utilizată pentru a face padding labelurilor ar trebui să fie `-100` și nu padding tokenul tokenizerului, pentru a ne asigura că aceste valori de padding sunt ignorate în calculul pierderilor.

Toate acestea sunt realizate de un [`DataCollatorForSeq2Seq`](https://huggingface.co/transformers/main_classes/data_collator.html#datacollatorforseq2seq). Ca și `DataCollatorWithPadding`, acesta preia `tokenizer` utilizat pentru preprocesarea inputurilor, dar preia și `modelul`. Acest lucru se datorează faptului că acest data collator va fi, de asemenea, responsabil de pregătirea ID-urilor de input ale decoderului, care sunt versiuni schimbate ale labelurilor cu un token special la început. Deoarece această schimbare se face ușor diferit pentru diferite arhitecturi, `DataCollatorForSeq2Seq` trebuie să cunoască obiectul `model`:

{#if fw === 'pt'}

```py
from transformers import DataCollatorForSeq2Seq

data_collator = DataCollatorForSeq2Seq(tokenizer, model=model)
```

{:else}

```py
from transformers import DataCollatorForSeq2Seq

data_collator = DataCollatorForSeq2Seq(tokenizer, model=model, return_tensors="tf")
```

{/if}

Pentru a testa acest lucru pe câteva sampleuri, îl apelăm doar pe o listă de sampleuri din setul nostru de antrenare tokenizat:

```py
batch = data_collator([tokenized_datasets["train"][i] for i in range(1, 3)])
batch.keys()
```

```python out
dict_keys(['attention_mask', 'input_ids', 'labels', 'decoder_input_ids'])
```

Putem verifica dacă labelurile noastre au fost padded la lungimea maximă a batchului, folosind `-100`:

```py
batch["labels"]
```

```python out
tensor([[  577,  5891,     2,  3184,    16,  2542,     5,  1710,     0,  -100,
          -100,  -100,  -100,  -100,  -100,  -100],
        [ 1211,     3,    49,  9409,  1211,     3, 29140,   817,  3124,   817,
           550,  7032,  5821,  7907, 12649,     0]])
```

De asemenea, putem arunca o privire la ID-urile de input ale decoderului, pentru a vedea că acestea sunt versiuni schimbate ale labelurilor:

```py
batch["decoder_input_ids"]
```

```python out
tensor([[59513,   577,  5891,     2,  3184,    16,  2542,     5,  1710,     0,
         59513, 59513, 59513, 59513, 59513, 59513],
        [59513,  1211,     3,    49,  9409,  1211,     3, 29140,   817,  3124,
           817,   550,  7032,  5821,  7907, 12649]])
```

Iată labelurile pentru primul și al doilea element din datasetul nostru:

```py
for i in range(1, 3):
    print(tokenized_datasets["train"][i]["labels"])
```

```python out
[577, 5891, 2, 3184, 16, 2542, 5, 1710, 0]
[1211, 3, 49, 9409, 1211, 3, 29140, 817, 3124, 817, 550, 7032, 5821, 7907, 12649, 0]
```

{#if fw === 'pt'}

Vom transmite acest `data_collator` către `Seq2SeqTrainer`. În continuare, să aruncăm o privire la metrice.

{:else}

Acum putem folosi acest `data_collator` pentru a converti fiecare dintre dataseturile într-un `tf.data.Dataset`, gata pentru antrenare:

```python
tf_train_dataset = model.prepare_tf_dataset(
    tokenized_datasets["train"],
    collate_fn=data_collator,
    shuffle=True,
    batch_size=32,
)
tf_eval_dataset = model.prepare_tf_dataset(
    tokenized_datasets["validation"],
    collate_fn=data_collator,
    shuffle=False,
    batch_size=16,
)
```

{/if}


### Metrice[[metrics]]

<Youtube id="M05L1DhFqcw"/>

{#if fw === 'pt'}

Caracteristica pe care `Seq2SeqTrainer` o adaugă superclasei sale `Trainer` este capacitatea de a utiliza metoda `generate()` în timpul evaluării sau predicției. În timpul antrenării, modelul va utiliza `decoder_input_ids` cu un attention mask care asigură că nu utilizează tokenii de după tokenul pe care încearcă să îl prezică, pentru a accelera antrenarea. În timpul inferenței, nu le vom putea utiliza deoarece nu vom avea labeluri, deci este o idee bună să ne evaluăm modelul cu aceeași configurație.

După cum am văzut în [Capitolul 1](/course/chapter1/6), decoderul realizează inferența prin prezicerea tokenilor unul câte unul - lucru care este implementat behind the scenes în 🤗 Transformers prin metoda `generate()`. `Seq2SeqTrainer` ne va permite să folosim această metodă pentru evaluare dacă setăm `predict_with_generate=True`.

{/if}

Metricele tradiționale utilizate pentru traducere sunt [scorul BLEU](https://en.wikipedia.org/wiki/BLEU), introdus în [un articol din 2002](https://aclanthology.org/P02-1040.pdf) de Kishore Papineni et al. Scorul BLEU evaluează cât de apropiate sunt traducerile de labelurile lor. Acesta nu măsoară inteligibilitatea sau corectitudinea gramaticală a rezultatelor generate de model, ci utilizează reguli statistice pentru a se asigura că toate cuvintele din rezultatele generate apar și în targets. În plus, există reguli care penalizează repetițiile acelorași cuvinte dacă acestea nu sunt repetate și în targets(pentru a evita ca modelul să producă propoziții de tipul `"the the the the the the"`) și să producă propoziții care sunt mai scurte decât cele din targets(pentru a evita ca modelul să producă propoziții de tipul `"the"`).

Un punct slab al BLEU este că se așteaptă ca textul să fie deja tokenizat, ceea ce face dificilă compararea scorurilor între modele care utilizează tokenizere diferite. În schimb, cea mai frecvent utilizată măsură pentru evaluarea comparativă a modelelor de traducere este [SacreBLEU](https://github.com/mjpost/sacrebleu), care abordează acest punct slab (și altele) prin standardizarea etapei de tokenizare. Pentru a utiliza această metrică, trebuie mai întâi să instalăm biblioteca SacreBLEU:

```py
!pip install sacrebleu
```

Îl putem încărca apoi prin `evaluate.load()` așa cum am făcut în [Capitolul 3](/course/chapter3):

```py
import evaluate

metric = evaluate.load("sacrebleu")
```

Această metrică va lua texte ca inputuri și targeturi. Este conceput pentru a lua mai multe obiective acceptabile, deoarece există adesea mai multe traduceri acceptabile ale aceleiași propoziții - datasetul pe care îl folosim oferă doar una, dar nu este neobișnuit în NLP să găsim dataseturi care oferă mai multe propoziții ca labeluri. Deci, predicțiile ar trebui să fie o listă de propoziții, dar referințele ar trebui să fie o listă de liste de propoziții.

Hai să încercăm acest exemplu:

```py
predictions = [
    "This plugin lets you translate web pages between several languages automatically."
]
references = [
    [
        "This plugin allows you to automatically translate web pages between several languages."
    ]
]
metric.compute(predictions=predictions, references=references)
```

```python out
{'score': 46.750469682990165,
 'counts': [11, 6, 4, 3],
 'totals': [12, 11, 10, 9],
 'precisions': [91.67, 54.54, 40.0, 33.33],
 'bp': 0.9200444146293233,
 'sys_len': 12,
 'ref_len': 13}
```

Se obține un scor BLEU de 46,75, ceea ce este destul de bine - ca referință, modelul original Transformer din lucrarea ["Attention Is All You Need"](https://arxiv.org/pdf/1706.03762.pdf) a obținut un scor BLEU de 41,8 la o sarcină similară de traducere între engleză și franceză! (Pentru mai multe informații despre parametrii individuali, precum `counts` și `bp`, consultați [repositoriul SacreBLEU](https://github.com/mjpost/sacrebleu/blob/078c440168c6adc89ba75fe6d63f0d922d42bcfe/sacrebleu/metrics/bleu.py#L74).) Pe de altă parte, dacă încercăm cu cele două tipuri de predicții proaste (multe repetări sau prea scurte) care rezultă adesea din modelele de traducere, vom obține scoruri BLEU destul de proaste:

```py
predictions = ["This This This This"]
references = [
    [
        "This plugin allows you to automatically translate web pages between several languages."
    ]
]
metric.compute(predictions=predictions, references=references)
```

```python out
{'score': 1.683602693167689,
 'counts': [1, 0, 0, 0],
 'totals': [4, 3, 2, 1],
 'precisions': [25.0, 16.67, 12.5, 12.5],
 'bp': 0.10539922456186433,
 'sys_len': 4,
 'ref_len': 13}
```

```py
predictions = ["This plugin"]
references = [
    [
        "This plugin allows you to automatically translate web pages between several languages."
    ]
]
metric.compute(predictions=predictions, references=references)
```

```python out
{'score': 0.0,
 'counts': [2, 1, 0, 0],
 'totals': [2, 1, 0, 0],
 'precisions': [100.0, 100.0, 0.0, 0.0],
 'bp': 0.004086771438464067,
 'sys_len': 2,
 'ref_len': 13}
```

Scorul poate varia de la 0 la 100, iar mai mare înseamnă un scor mai bun.

{#if fw === 'tf'}

Pentru a trece de la rezultatele modelului la texte pe care metricele le pot utiliza, vom utiliza metoda `tokenizer.batch_decode()`. Trebuie doar să curățăm toate `-100` din labeluri; tokenizerul va face automat același lucru pentru padding token. Să definim o funcție care să preia modelul nostru și un dataset și să calculeze metrici pe acesta. De asemenea, vom utiliza un truc care crește dramatic performanța - compilarea codului nostru de generare cu [XLA](https://www.tensorflow.org/xla), compilatorul accelerat de algebră liniară al TensorFlow. XLA aplică diverse optimizări graficului de calcul al modelului și are ca rezultat îmbunătățiri semnificative ale vitezei și utilizării memoriei. După cum se descrie în [blogul](https://huggingface.co/blog/tf-xla-generate) Hugging Face, XLA funcționează cel mai bine atunci când shaperulire noastre de input nu variază prea mult. Pentru a face față acestui lucru, vom aplica padding inputurilor cu multipli ai 128 și vom crea un nou dataset cu padding collatorul, iar apoi vom aplica decoratorul `@tf.function(jit_compile=True)` funcției noastre de generare, care marchează întreaga funcție pentru compilare cu XLA.

```py
import numpy as np
import tensorflow as tf
from tqdm import tqdm

generation_data_collator = DataCollatorForSeq2Seq(
    tokenizer, model=model, return_tensors="tf", pad_to_multiple_of=128
)

tf_generate_dataset = model.prepare_tf_dataset(
    tokenized_datasets["validation"],
    collate_fn=generation_data_collator,
    shuffle=False,
    batch_size=8,
)


@tf.function(jit_compile=True)
def generate_with_xla(batch):
    return model.generate(
        input_ids=batch["input_ids"],
        attention_mask=batch["attention_mask"],
        max_new_tokens=128,
    )


def compute_metrics():
    all_preds = []
    all_labels = []

    for batch, labels in tqdm(tf_generate_dataset):
        predictions = generate_with_xla(batch)
        decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
        labels = labels.numpy()
        labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
        decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
        decoded_preds = [pred.strip() for pred in decoded_preds]
        decoded_labels = [[label.strip()] for label in decoded_labels]
        all_preds.extend(decoded_preds)
        all_labels.extend(decoded_labels)

    result = metric.compute(predictions=all_preds, references=all_labels)
    return {"bleu": result["score"]}
```

{:else}

Pentru a trece de la rezultatele modelului la textele pe care metricele le pot utiliza, vom utiliza metoda `tokenizer.batch_decode()`. Trebuie doar să curățăm toate `-100` din labeluri(tokenizerul va face automat același lucru pentru padding token):

```py
import numpy as np


def compute_metrics(eval_preds):
    preds, labels = eval_preds
    # In case the model returns more than the prediction logits
    if isinstance(preds, tuple):
        preds = preds[0]

    decoded_preds = tokenizer.batch_decode(preds, skip_special_tokens=True)

    # Replace -100s in the labels as we can't decode them
    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
    decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)

    # Some simple post-processing
    decoded_preds = [pred.strip() for pred in decoded_preds]
    decoded_labels = [[label.strip()] for label in decoded_labels]

    result = metric.compute(predictions=decoded_preds, references=decoded_labels)
    return {"bleu": result["score"]}
```

{/if}

Acum că am făcut acest lucru, suntem gata să facem fine-tune modelului!


### Fine-tuningul modelului[[fine-tuning-the-model]]

Primul pas pe care trebuie să îl faceți este să vă conectați la Hugging Face, astfel încât să vă puteți încărca rezultatele în Model Hub. Există o funcție convenabilă care vă ajuta cu acest lucru într-un notebook:

```python
from huggingface_hub import notebook_login

notebook_login()
```

Aceasta va afișa un widget în care puteți introduce datele voastre de autentificare Hugging Face.

Dacă nu lucrați într-un notebook, tastați următoarea linie în terminal:

```bash
huggingface-cli login
```

{#if fw === 'tf'}

Înainte de a începe, să vedem ce fel de rezultate obținem de la modelul nostru fără niciun antrenament:

```py
print(compute_metrics())
```

```
{'bleu': 33.26983701454733}
```

Odată făcut acest lucru, putem pregăti tot ce avem nevoie pentru a compila și antrena modelul nostru. Observați utilizarea `tf.keras.mixed_precision.set_global_policy("mixed_float16")` -- aceasta îi va spune lui Keras să se antreneze folosind float16, ceea ce poate oferi o creștere semnificativă a vitezei pe GPU-urile care o acceptă (Nvidia 20xx/V100 sau mai noi).

```python
from transformers import create_optimizer
from transformers.keras_callbacks import PushToHubCallback
import tensorflow as tf

# Numărul etapelor de antrenare este numărul de sampleuri din dataset, împărțit la dimensiunea batchului, apoi înmulțit
# cu numărul total de epoci. Rețineți că datasetul tf_train_dataset de aici este un dataset tf.data.Dataset în batchuri,
# nu datasetul original Hugging Face, deci len() este deja num_samples // batch_size.
num_epochs = 3
num_train_steps = len(tf_train_dataset) * num_epochs

optimizer, schedule = create_optimizer(
    init_lr=5e-5,
    num_warmup_steps=0,
    num_train_steps=num_train_steps,
    weight_decay_rate=0.01,
)
model.compile(optimizer=optimizer)

# Antrenarea în float16 cu precizie mixtă
tf.keras.mixed_precision.set_global_policy("mixed_float16")
```

În continuare, definim un `PushToHubCallback` pentru a încărca modelul nostru în Hub în timpul antrenamentului, așa cum am văzut în [secțiunea 2]((/course/chapter7/2)), iar apoi pur și simplu facem fit modelului cu acel callback:

```python
from transformers.keras_callbacks import PushToHubCallback

callback = PushToHubCallback(
    output_dir="marian-finetuned-kde4-en-to-fr", tokenizer=tokenizer
)

model.fit(
    tf_train_dataset,
    validation_data=tf_eval_dataset,
    callbacks=[callback],
    epochs=num_epochs,
)
```

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 trimis modelul către organizația [`huggingface-course`](https://huggingface.co/huggingface-course), am adăugat `hub_model_id="huggingface-course/marian-finetuned-kde4-en-to-fr"` la `Seq2SeqTrainingArguments`. În mod implicit, repositoriul utilizat va fi în namespaceul vostru și denumit după output directory-ul pe care l-ați stabilit, deci aici va fi `"sgugger/marian-finetuned-kde4-en-to-fr"` (care este modelul la care am făcut legătura la începutul acestei secțiuni).

<Tip>

💡 Dacă output directory-ul pe care îl utilizați există deja, acesta trebuie să fie o clonă locală a repositoriului către care doriți să faceți push. Dacă nu este, veți primi o eroare atunci când apelați `model.fit()` și va trebui să setați un nume nou.

</Tip>

În cele din urmă, hai să vedem cum arată metricele noastre acum că antrenarea s-a încheiat:

```py
print(compute_metrics())
```

```
{'bleu': 57.334066271545865}
```

În acest moment, puteți utiliza widgetul de inferență de pe Model Hub pentru a testa modelul și pentru a-l partaja cu prietenii voștrii. Ați făcut fine-tune cu succes unui model pentru o sarcină de traducere - felicitări!

{:else}

Odată făcut acest lucru, putem defini `Seq2SeqTrainingArguments`. Ca și pentru `Trainer`, folosim o subclasă a `TrainingArguments` care conține câteva câmpuri suplimentare:

```python
from transformers import Seq2SeqTrainingArguments

args = Seq2SeqTrainingArguments(
    f"marian-finetuned-kde4-en-to-fr",
    evaluation_strategy="no",
    save_strategy="epoch",
    learning_rate=2e-5,
    per_device_train_batch_size=32,
    per_device_eval_batch_size=64,
    weight_decay=0.01,
    save_total_limit=3,
    num_train_epochs=3,
    predict_with_generate=True,
    fp16=True,
    push_to_hub=True,
)
```

În afară de hiperparametrii obișnuiți (cum ar fi rata de învățare, numărul de epoci, dimensiunea batch-ului și o anumită scădere a weighturilor), aici sunt câteva schimbări în comparație cu ceea ce am văzut în secțiunile anterioare:

- Nu setăm nicio evaluare periodică, deoarece evaluarea durează; ne vom evalua modelul doar o dată înainte și după antrenare.
- Setăm `fp16=True`, care accelerează antrenarea pe GPU-urile moderne.
- Setăm `predict_with_generate=True`, așa cum am discutat mai sus.
- Utilizăm `push_to_hub=True` pentru a încărca modelul în Hub la sfârșitul fiecărei epoci.

Rețineți că puteți specifica numele complet al 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 trimis modelul către organizația [`huggingface-course`](https://huggingface.co/huggingface-course), am adăugat `hub_model_id="huggingface-course/marian-finetuned-kde4-en-to-fr"` la `Seq2SeqTrainingArguments`. Î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 `"sgugger/marian-finetuned-kde4-en-to-fr"` (care este modelul la care am făcut legătura la începutul acestei secțiuni).

<Tip>

💡 Dacă output directory-ul pe care îl utilizați există deja, acesta trebuie să fie o clonă locală a repositoriului către care doriți să faceți push. În caz contrar, veți primi o eroare atunci când vă definiți `Seq2SeqTrainer` și va trebui să stabiliți un nume nou.

</Tip>

În final, transmitem totul către `Seq2SeqTrainer`:

```python
from transformers import Seq2SeqTrainer

trainer = Seq2SeqTrainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    tokenizer=tokenizer,
    compute_metrics=compute_metrics,
)
```

Înainte de antrenare, ne vom uita mai întâi la scorul obținut de modelul nostru, pentru a verifica dacă nu înrăutățim lucrurile prin fine-tuning. Această comandă va dura ceva timp, așa că puteți lua o cafea sau două în timp ce se execută:

```python
trainer.evaluate(max_length=max_length)
```

```python out
{'eval_loss': 1.6964408159255981,
 'eval_bleu': 39.26865061007616,
 'eval_runtime': 965.8884,
 'eval_samples_per_second': 21.76,
 'eval_steps_per_second': 0.341}
```

Un scor BLEU de 39 nu este prea rău, ceea ce reflectă faptul că modelul nostru este deja bun la traducerea propozițiilor din engleză în franceză.

Urmează antrenarea, care va necesita, de asemenea, puțin timp:

```python
trainer.train()
```

Rețineți că, în timpul antrenamentului, de fiecare dată când modelul este salvat (aici, la fiecare epocă), acesta este încărcat în Hub în fundal. În acest fel, veți putea să reluați antrenarea pe o altă mașină, dacă este necesar.

Odată ce formarea este terminată, evaluăm din nou modelul nostru - sperăm că vom vedea o îmbunătățire a scorului BLEU!

```py
trainer.evaluate(max_length=max_length)
```

```python out
{'eval_loss': 0.8558505773544312,
 'eval_bleu': 52.94161337775576,
 'eval_runtime': 714.2576,
 'eval_samples_per_second': 29.426,
 'eval_steps_per_second': 0.461,
 'epoch': 3.0}
```

Aceasta este o îmbunătățire de aproape 14 puncte, ceea ce este minunat.

În final, folosim metoda `push_to_hub()` pentru a ne asigura că încărcăm cea mai recentă versiune a modelului. De asemenea, `Trainer` redactează un model card cu toate rezultatele evaluării și o încarcă. Acest model card conține metadate care ajută Model Hub să aleagă widgetul pentru demonstrația de inferență. De obicei, nu este nevoie să se menționeze nimic, deoarece poate deduce widgetul potrivit din clasa modelului, dar în acest caz, aceeași clasă de model poate fi utilizată pentru toate tipurile de probleme de tip sequence-to-sequence, așa că specificăm că este un model de traducere:

```py
trainer.push_to_hub(tags="translation", commit_message="Training complete")
```

Această comandă returnează URL-ul comitului pe care tocmai l-am făcut, dacă doriți să îl inspectați:

```python out
'https://huggingface.co/sgugger/marian-finetuned-kde4-en-to-fr/commit/3601d621e3baae2bc63d3311452535f8f58f6ef3'
```

În această etapă, puteți utiliza widget-ul de inferență de pe Model Hub pentru a vă testa modelul și pentru a-l partaja cu prietenii. Ați făcut fine-tune cu succes un model pentru o sarcină de traducere - felicitări!

Dacă doriți să pătrundeți puțin mai adânc în bucla de antrenare, vă vom arăta acum să faceți același lucru folosind 🤗 Accelerate.

{/if}

{#if fw === 'pt'}

## O buclă de antrenare personalizată[[a-custom-training-loop]]

Să aruncăm acum o privire la bucla de antrenare completă, astfel încât să puteți personaliza cu ușurință părțile de care aveți nevoie. Va arăta foarte asemănător cu ceea ce am făcut în [secțiunea 2](/course/chapter7/2) și [capitolul 3](/course/chapter7/4).

### Pregătirea tuturor lucrulilor pentru antrenare[[preparing-everything-for-training]]

Ați văzut toate acestea de câteva ori până acum, așa că vom trece prin cod destul de repede. Mai întâi vom construi `DataLoader`s din dataseturile noastre, după ce vom seta dataseturile în formatul `"torch"` astfel încât să obținem tensori PyTorch:

```py
from torch.utils.data import DataLoader

tokenized_datasets.set_format("torch")
train_dataloader = DataLoader(
    tokenized_datasets["train"],
    shuffle=True,
    collate_fn=data_collator,
    batch_size=8,
)
eval_dataloader = DataLoader(
    tokenized_datasets["validation"], collate_fn=data_collator, batch_size=8
)
```

În continuare, reinițializăm modelul, pentru a ne asigura că nu continuăm fine-tuningul de dinainte, ci pornim din nou de la modelul preantrenat:

```py
model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
```

Pe urmă vom avea nevoie de un optimizator:

```py
from transformers import AdamW

optimizer = AdamW(model.parameters(), lr=2e-5)
```

Odată ce avem toate aceste obiecte, le putem trimite la metoda `accelerator.prepare()`. Amintiți-vă că, dacă doriți să vă antrenați pe un TPU într-un notebook Colab, va trebui să mutați tot acest cod într-o funcție de antrenare, care nu ar trebui să execute nicio celulă care inițializează un `Accelerator`.

```py
from accelerate import Accelerator

accelerator = Accelerator()
model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
    model, optimizer, train_dataloader, eval_dataloader
)
```

Acum că am trimis `train_dataloader` la `accelerator.prepare()`, putem utiliza lungimea acestuia pentru a calcula numărul de pași de antrenare. Amintiți-vă că trebuie să facem acest lucru întotdeauna după pregătirea data loaderului, deoarece metoda respectivă va modifica lungimea `DataLoader`. Utilizăm un program liniar clasic de la rata de învățare la 0:

```py
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,
)
```

În cele din urmă, pentru a trimite modelul nostru către Hub, va trebui să creăm un obiect `Repository` într-un folder de lucru. În primul rând, conectați-vă la Hugging Face Hub, dacă nu sunteți deja conectat. Vom determina numele repositoriului pornind de la ID-ul modelului pe care dorim să îl atribuim modelului nostru (nu ezitați să înlocuiți `repo_name` cu propria alegere; acesta trebuie doar să conțină numele vostru de utilizator, ceea ce face funcția `get_full_repo_name()`):

```py
from huggingface_hub import Repository, get_full_repo_name

model_name = "marian-finetuned-kde4-en-to-fr-accelerate"
repo_name = get_full_repo_name(model_name)
repo_name
```

```python out
'sgugger/marian-finetuned-kde4-en-to-fr-accelerate'
```

Apoi putem clona acel repositoriu într-un folder local. Dacă există deja, acest folder local ar trebui să fie o clonă a repositoriului cu care lucrăm:

```py
output_dir = "marian-finetuned-kde4-en-to-fr-accelerate"
repo = Repository(output_dir, clone_from=repo_name)
```

Acum putem încărca orice salvăm în `output_dir` prin apelarea metodei `repo.push_to_hub()`. Acest lucru ne va ajuta să încărcăm modelele intermediare la sfârșitul fiecărei epoci.

### Bucla de antrenare[[training-loop]]

Acum suntem pregătiți să scriem bucla de antrenare completă. Pentru a simplifica partea de evaluare, definim această funcție `postprocess()` care preia predicțiile și labelurile și le convertește în liste de stringuri pe care obiectul nostru `metric` le va aștepta:

```py
def postprocess(predictions, labels):
    predictions = predictions.cpu().numpy()
    labels = labels.cpu().numpy()

    decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)

    # Replace -100 in the labels as we can't decode them.
    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
    decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)

    # Some simple post-processing
    decoded_preds = [pred.strip() for pred in decoded_preds]
    decoded_labels = [[label.strip()] for label in decoded_labels]
    return decoded_preds, decoded_labels
```

Bucla de antrenare seamănă foarte mult cu cele din [secțiunea 2](/course/chapter7/2) și [capitolul 3](/course/chapter3), cu câteva diferențe în partea de evaluare - așa că hai să ne concentrăm pe asta!

Primul lucru pe care îl putem remarca este că folosim metoda `generate()` pentru a calcula predicțiile, dar aceasta este o metodă a modelul nostru de bază, nu pe modelul wrapped 🤗 Accelerate creat în metoda `prepare()`. De aceea, mai întâi facem unwrap modelului, apoi apelăm această metodă.

Al doilea lucru este că, la fel ca în cazul [token classification](/course/chapter7/2), este posibil ca două procese să fi făcut padding inputurilor și labelurilor cu forme diferite, așa că folosim `accelerator.pad_across_processes()` pentru a face predicțiile și labelurile de aceeași formă înainte de a apela metoda `gather()`. Dacă nu facem acest lucru, evaluarea va da o eroare sau se va bloca pentru totdeauna.

```py
from tqdm.auto import tqdm
import torch

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()
    for batch in tqdm(eval_dataloader):
        with torch.no_grad():
            generated_tokens = accelerator.unwrap_model(model).generate(
                batch["input_ids"],
                attention_mask=batch["attention_mask"],
                max_length=128,
            )
        labels = batch["labels"]

        # Necessary to pad predictions and labels for being gathered
        generated_tokens = accelerator.pad_across_processes(
            generated_tokens, dim=1, pad_index=tokenizer.pad_token_id
        )
        labels = accelerator.pad_across_processes(labels, dim=1, pad_index=-100)

        predictions_gathered = accelerator.gather(generated_tokens)
        labels_gathered = accelerator.gather(labels)

        decoded_preds, decoded_labels = postprocess(predictions_gathered, labels_gathered)
        metric.add_batch(predictions=decoded_preds, references=decoded_labels)

    results = metric.compute()
    print(f"epoch {epoch}, BLEU score: {results['score']:.2f}")

    # 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, BLEU score: 53.47
epoch 1, BLEU score: 54.24
epoch 2, BLEU score: 54.44
```

Odată făcut acest lucru, ar trebui să aveți un model care are rezultate destul de asemănătoare cu cel antrenat cu `Seq2SeqTrainer`. Îl puteți verifica pe cel pe care l-am antrenat folosind acest cod la [*huggingface-course/marian-finetuned-kde4-en-to-fr-accelerate*] (https://huggingface.co/huggingface-course/marian-finetuned-kde4-en-to-fr-accelerate). Și dacă doriți să testați orice modificări ale buclei de antrenare, le puteți implementa direct prin editarea codului prezentat mai sus!

{/if}

## Utilizarea modelului fine-tuned[[using-the-fine-tuned-model]]

V-am arătat deja cum puteți utiliza modelul pe căruia i-am aplicat fine-tune pe Model Hub cu widgetul de inferență. Pentru a-l utiliza la nivel local într-un `pipeline`, trebuie doar să specificăm identificatorul de model corespunzător:

```py
from transformers import pipeline

# Replace this with your own checkpoint
model_checkpoint = "huggingface-course/marian-finetuned-kde4-en-to-fr"
translator = pipeline("translation", model=model_checkpoint)
translator("Default to expanded threads")
```

```python out
[{'translation_text': 'Par défaut, développer les fils de discussion'}]
```

Așa cum era de așteptat, modelul nostru preantrenat și-a adaptat cunoștințele la corpusul pe care i-am făcut fine-tune și, în loc să lase cuvântul englezesc "threads", acum îl traduce în versiunea oficială franceză. Același lucru este valabil și pentru "plugin":

```py
translator(
    "Unable to import %1 using the OFX importer plugin. This file is not the correct format."
)
```

```python out
[{'translation_text': "Impossible d'importer %1 en utilisant le module externe d'importation OFX. Ce fichier n'est pas le bon format."}]
```

Un alt exemplu excelent de adaptare a domeniului!

<Tip>

✏️ **E rândul tău!** Care este rezultatul modelului pentru sampleul cuvântul "email" pe care l-ai identificat mai devreme?

</Tip>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter7/4.mdx" />

### Răspuns la întrebări[[question-answering]]
https://huggingface.co/learn/course/ro/chapter7/7.md

# Răspuns la întrebări[[question-answering]]

{#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/section7_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter7/section7_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/section7_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter7/section7_tf.ipynb"},
]} />

{/if}

Este timpul să analizăm răspunsul la întrebări! Această sarcină are mai multe variante, dar cea pe care ne vom concentra în această secțiune se numește răspuns *extractiv* la întrebări. Aceasta presupune formularea de întrebări cu privire la un document și identificarea răspunsurilor ca _intervale de text_ în documentul în sine.

<Youtube id="ajPx5LwJD-I"/>

Vom face fine-tuning unui-model BERT pe [datasetul SQuAD] (https://rajpurkar.github.io/SQuAD-explorer/), care constă din întrebări adresate de mulțimea de lucrători pe un set de articole Wikipedia. Acest lucru ne va oferi un model capabil să calculeze predicții precum aceasta:

<iframe src="https://course-demos-bert-finetuned-squad.hf.space" frameBorder="0" height="450" 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>

Aceasta este de fapt o prezentare a modelului care a fost antrenat și încărcat în Hub folosind codul prezentat în această secțiune. Puteți să-l găsiți și să verificați predicțiile [aici](https://huggingface.co/huggingface-course/bert-finetuned-squad?context=%F0%9F%A4%97+Transformers+is+backed+by+the+three+most+popular+deep+learning+libraries+%E2%80%94+Jax%2C+PyTorch+and+TensorFlow+%E2%80%94+with+a+seamless+integration+between+them.+It%27s+straightforward+to+train+your+models+with+one+before+loading+them+for+inference+with+the+other.&question=Which+deep+learning+libraries+back+%F0%9F%A4%97+Transformers%3F).

<Tip>

💡 Modelele bazate doar pe encoding, cum ar fi BERT, tind să fie foarte bune la extragerea răspunsurilor la întrebări de tip factoid, cum ar fi "Cine a inventat arhitectura Transformer?", dar nu se descurcă prea bine atunci când primesc întrebări deschise, cum ar fi "De ce este cerul albastru?" În aceste cazuri mai dificile, modelele encoder-decoder precum T5 și BART sunt utilizate de obicei pentru a sintetiza informațiile într-un mod destul de similar cu [rezumarea textului](/course/chapter7/5). Dacă sunteți interesat de acest tip de răspuns *generativ* la întrebări, vă recomandăm să consultați [demo-ul](https://yjernite.github.io/lfqa.html) nostru bazat pe [datasetul ELI5](https://huggingface.co/datasets/eli5).

</Tip>

## Pregătirea datelor[[preparing-the-data]]

Datasetul care este cel mai utilizat ca referință academică pentru răspunderea extractivă la întrebări este [SQuAD](https://rajpurkar.github.io/SQuAD-explorer/), deci acesta este cel pe care îl vom utiliza aici. Există, de asemenea, un benchmark mai dificil [SQuAD v2](https://huggingface.co/datasets/squad_v2), care include întrebări care nu au un răspuns. Atât timp cât propriul dataset conține o coloană pentru contexte, o coloană pentru întrebări și o coloană pentru răspunsuri, ar trebui să puteți adapta pașii de mai jos.

### Datasetul SQuD[[the-squad-dataset]]

Ca de obicei, putem descărca și stoca în cache datasetul într-un singur pas datorită funcției `load_dataset()`:

```py
from datasets import load_dataset

raw_datasets = load_dataset("squad")
```

Ne putem uita apoi la acest obiect pentru a afla mai multe despre datasetul SQuAD:

```py
raw_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['id', 'title', 'context', 'question', 'answers'],
        num_rows: 87599
    })
    validation: Dataset({
        features: ['id', 'title', 'context', 'question', 'answers'],
        num_rows: 10570
    })
})
```

Se pare că avem tot ce ne trebuie cu câmpurile `context`, `question` și `answers`, așa că să le afișăm pentru primul element al datasetului nostru de antrenare:

```py
print("Context: ", raw_datasets["train"][0]["context"])
print("Question: ", raw_datasets["train"][0]["question"])
print("Answer: ", raw_datasets["train"][0]["answers"])
```

```python out
Context: 'Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend "Venite Ad Me Omnes". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive (and in a direct line that connects through 3 statues and the Gold Dome), is a simple, modern stone statue of Mary.'
Question: 'To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France?'
Answer: {'text': ['Saint Bernadette Soubirous'], 'answer_start': [515]}
```

Câmpurile `context` și `question` sunt foarte simplu de utilizat. Câmpul `answers` este un pic mai complicat, deoarece conține un dicționar cu două câmpuri care sunt ambele liste. Acesta este formatul care va fi așteptat de metrica `squad` în timpul evaluării; dacă utilizați propriile date, nu trebuie neapărat să vă faceți griji cu privire la plasarea răspunsurilor în același format. Câmpul `text` este destul de evident, iar câmpul `answer_start` conține indicele caracterului de început al fiecărui răspuns din context.

În timpul antrenamentului, există un singur răspuns posibil. Putem verifica acest lucru folosind metoda `Dataset.filter()`:

```py
raw_datasets["train"].filter(lambda x: len(x["answers"]["text"]) != 1)
```

```python out
Dataset({
    features: ['id', 'title', 'context', 'question', 'answers'],
    num_rows: 0
})
```

Cu toate acestea, pentru evaluare, există mai multe răspunsuri posibile pentru fiecare sample, care pot fi identice sau diferite:

```py
print(raw_datasets["validation"][0]["answers"])
print(raw_datasets["validation"][2]["answers"])
```

```python out
{'text': ['Denver Broncos', 'Denver Broncos', 'Denver Broncos'], 'answer_start': [177, 177, 177]}
{'text': ['Santa Clara, California', "Levi's Stadium", "Levi's Stadium in the San Francisco Bay Area at Santa Clara, California."], 'answer_start': [403, 355, 355]}
```

Nu ne vom aprofunda în scriptul de evaluare, deoarece totul va fi încorporat de o metrică 🤗 Datasets pentru noi, dar versiunea scurtă este că unele dintre întrebări au mai multe răspunsuri posibile, iar acest script va compara un răspuns prezis cu toate răspunsurile acceptabile și va lua cel mai bun scor. Dacă ne uităm la sampleul de la indexul 2, de exemplu:

```py
print(raw_datasets["validation"][2]["context"])
print(raw_datasets["validation"][2]["question"])
```

```python out
'Super Bowl 50 was an American football game to determine the champion of the National Football League (NFL) for the 2015 season. The American Football Conference (AFC) champion Denver Broncos defeated the National Football Conference (NFC) champion Carolina Panthers 24–10 to earn their third Super Bowl title. The game was played on February 7, 2016, at Levi\'s Stadium in the San Francisco Bay Area at Santa Clara, California. As this was the 50th Super Bowl, the league emphasized the "golden anniversary" with various gold-themed initiatives, as well as temporarily suspending the tradition of naming each Super Bowl game with Roman numerals (under which the game would have been known as "Super Bowl L"), so that the logo could prominently feature the Arabic numerals 50.'
'Where did Super Bowl 50 take place?'
```

putem vedea că răspunsul poate fi într-adevăr una dintre cele trei posibilități pe care le-am văzut anterior.

### Procesarea datelor de antrenare[[processing-the-training-data]]

<Youtube id="qgaM0weJHpA"/>

Să începem cu preprocesarea datelor de antrenare. Partea dificilă va fi generarea labelurilor pentru răspunsul la întrebare, care vor fi pozițiile de început și de sfârșit ale tokenilor corespunzătoare răspunsului în context.

Dar să nu ne grăbim. În primul rând, trebuie să convertim textul din datele de intrare în ID-uri pe care modelul să le poată înțelege, utilizând un tokenizer:

```py
from transformers import AutoTokenizer

model_checkpoint = "bert-base-cased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
```

După cum am menționat anterior, vom face fine-tune unui model BERT, dar puteți utiliza orice alt tip de model, atâta timp cât are implementat un tokenizer rapid. Puteți vedea toate arhitecturile care vin cu o versiune rapidă în [acest tabel mare] (https://huggingface.co/transformers/#supported-frameworks), iar pentru a verifica dacă obiectul `tokenizer` pe care îl utilizați este într-adevăr susținut de 🤗 Tokenizers, vă puteți uita la atributul său `is_fast`:

```py
tokenizer.is_fast
```

```python out
True
```

Putem transmite împreună întrebarea și contextul către tokenizerul nostru, iar acesta va introduce în mod corespunzător tokenii speciali pentru a forma o propoziție ca aceasta:

```
[CLS] question [SEP] context [SEP]
```

Hai să verificăm de două ori:

```py
context = raw_datasets["train"][0]["context"]
question = raw_datasets["train"][0]["question"]

inputs = tokenizer(question, context)
tokenizer.decode(inputs["input_ids"])
```

```python out
'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] Architecturally, '
'the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin '
'Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms '
'upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred '
'Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a '
'replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette '
'Soubirous in 1858. At the end of the main drive ( and in a direct line that connects through 3 statues '
'and the Gold Dome ), is a simple, modern stone statue of Mary. [SEP]'
```

Labelurile vor fi apoi indexul tokenilor care încep și termină răspunsul, iar modelul va fi însărcinat să prezică un logit de început și de sfârșit pentru fiecare token din intrare, labelurile teoretice fiind următoarele:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/qa_labels.svg" alt="One-hot encoded label pentru răspunderea la întrebări."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/qa_labels-dark.svg" alt="One-hot encoded label pentru răspunderea la întrebări."/>
</div>

În acest caz, contextul nu este prea lung, dar unele dintre exemplele din dataset au contexte foarte lungi care vor depăși lungimea maximă pe care am stabilit-o (care este de 384 în acest caz). După cum am văzut în [Capitolul 6](/course/chapter6/4) când am explorat elementele interne ale pipelineului `question-answering`, vom trata contextele lungi prin crearea mai multor caracteristici de antrenare dintr-un sample din datasetul nostru, cu un sliding window între ele.

Pentru a vedea cum funcționează acest lucru folosind exemplul curent, putem limita lungimea la 100 și putem utiliza un sliding window de 50 de tokeni. Vă reamintim că folosim:

- `max_length` pentru a stabili lungimea maximă (aici 100)
- `truncation="only_second"` pentru a trunchia contextul (care este în poziția a doua) atunci când întrebarea cu contextul său este prea lungă
- `stride` pentru a seta numărul de tokeni care se suprapun între două bucăți succesive (aici 50)
- `return_overflowing_tokens=True` pentru ca tokenizerul să știe că dorim tokenii care se suprapun

```py
inputs = tokenizer(
    question,
    context,
    max_length=100,
    truncation="only_second",
    stride=50,
    return_overflowing_tokens=True,
)

for ids in inputs["input_ids"]:
    print(tokenizer.decode(ids))
```

```python out
'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basi [SEP]'
'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin [SEP]'
'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP] Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive ( and in a direct line that connects through 3 [SEP]'
'[CLS] To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France? [SEP]. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive ( and in a direct line that connects through 3 statues and the Gold Dome ), is a simple, modern stone statue of Mary. [SEP]'
```

După cum se poate observa, exemplul nostru a fost împărțit în patru inputuri, fiecare dintre acestea conținând întrebarea și o parte din context. Rețineți că răspunsul la întrebare ("Bernadette Soubirous") apare doar în al treilea și ultimul input, astfel încât, prin tratarea contextelor lungi în acest mod, vom crea câteva exemple de antrenament în care răspunsul nu este inclus în context. Pentru aceste exemple, labelurile vor fi `start_position = end_position = 0` (deci vom prezice tokenul `[CLS]`). Vom seta aceste labeluri și în cazul nefericit în care răspunsul a fost trunchiat, astfel încât avem doar începutul (sau sfârșitul) acestuia. Pentru exemplele în care răspunsul este complet în context, labelurile vor fi indicele tokenului în care începe răspunsul și indicele tokenului în care se termină răspunsul.

Datasetul ne oferă caracterul de început al răspunsului în context, iar prin adăugarea lungimii răspunsului, putem găsi caracterul de sfârșit în context. Pentru a le corela cu indicii tokenilor, va trebui să folosim offset mapping pe care le-am studiat în [Capitolul 6](/course/chapter6/4). Putem face ca tokenizatorul nostru să le returneze trecând `return_offsets_mapping=True`:

```py
inputs = tokenizer(
    question,
    context,
    max_length=100,
    truncation="only_second",
    stride=50,
    return_overflowing_tokens=True,
    return_offsets_mapping=True,
)
inputs.keys()
```

```python out
dict_keys(['input_ids', 'token_type_ids', 'attention_mask', 'offset_mapping', 'overflow_to_sample_mapping'])
```

După cum putem vedea, primim înapoi ID-urile obișnuite de intrare, ID-urile tipului de token și attention maskul, precum și offset mapping necesar și o cheie suplimentară, `overflow_to_sample_mapping`. Valoarea corespunzătoare ne va fi de folos atunci când vom tokeniza mai multe texte în același timp (ceea ce ar trebui să facem pentru a beneficia de faptul că tokenizerul nostru este susținut de Rust). Deoarece un sample poate oferi mai multe caracteristici, aceasta mapează fiecare caracteristică la exemplul din care provine. Deoarece aici am tokenizat un singur exemplu, obținem o listă de `0`:

```py
inputs["overflow_to_sample_mapping"]
```

```python out
[0, 0, 0, 0]
```

Dar dacă vom tokeniza mai multe exemple, acest lucru va deveni mai util:

```py
inputs = tokenizer(
    raw_datasets["train"][2:6]["question"],
    raw_datasets["train"][2:6]["context"],
    max_length=100,
    truncation="only_second",
    stride=50,
    return_overflowing_tokens=True,
    return_offsets_mapping=True,
)

print(f"The 4 examples gave {len(inputs['input_ids'])} features.")
print(f"Here is where each comes from: {inputs['overflow_to_sample_mapping']}.")
```

```python out
'The 4 examples gave 19 features.'
'Here is where each comes from: [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3].'
```

După cum se poate observa, primele trei exemple (la indicii 2, 3 și 4 din setul de antrenare) au dat fiecare câte patru caracteristici, iar ultimul exemplu (la indicele 5 din setul de antrenare) a dat 7 caracteristici.

Aceste informații vor fi utile pentru a corela fiecare caracteristică obținută cu labeul corespunzător. După cum am menționat anterior, aceste labelurile sunt:

- `(0, 0)` dacă răspunsul nu se află în intervalul corespunzător al contextului
- `(start_position, end_position)` dacă răspunsul se află în intervalul corespunzător al contextului, cu `start_position` fiind indicele tokenului (în ID-urile de intrare) la începutul răspunsului și `end_position` fiind indicele tokenului (în ID-urile de intrare) unde se termină răspunsul

Pentru a determina care dintre acestea este cazul și, dacă este relevant, pozițiile tokenilor, vom găsi mai întâi indicii care încep și termină contextul în ID-urile de intrare. Am putea folosi ID-urile tipului de token pentru a face acest lucru, dar deoarece acestea nu există neapărat pentru toate modelele (DistilBERT nu le solicită, de exemplu), vom folosi în schimb metoda `sequence_ids()` a `BatchEncoding` pe care tokenizerul nostru o returnează.

Odată ce avem indicii tokenilor, ne uităm la offseturile corespunzătoare, care sunt tupeluri de două numere întregi reprezentând intervalul de caractere din contextul original. Astfel, putem detecta dacă bucățica de context din această caracteristică începe după răspuns sau se termină înainte de începerea răspunsului (caz în care eticheta este `(0, 0)`). Dacă nu este cazul, facem o buclă pentru a găsi primul și ultimul token al răspunsului:

```py
answers = raw_datasets["train"][2:6]["answers"]
start_positions = []
end_positions = []

for i, offset in enumerate(inputs["offset_mapping"]):
    sample_idx = inputs["overflow_to_sample_mapping"][i]
    answer = answers[sample_idx]
    start_char = answer["answer_start"][0]
    end_char = answer["answer_start"][0] + len(answer["text"][0])
    sequence_ids = inputs.sequence_ids(i)

    # Find the start and end of the context
    idx = 0
    while sequence_ids[idx] != 1:
        idx += 1
    context_start = idx
    while sequence_ids[idx] == 1:
        idx += 1
    context_end = idx - 1

    # If the answer is not fully inside the context, label is (0, 0)
    if offset[context_start][0] > start_char or offset[context_end][1] < end_char:
        start_positions.append(0)
        end_positions.append(0)
    else:
        # Otherwise it's the start and end token positions
        idx = context_start
        while idx <= context_end and offset[idx][0] <= start_char:
            idx += 1
        start_positions.append(idx - 1)

        idx = context_end
        while idx >= context_start and offset[idx][1] >= end_char:
            idx -= 1
        end_positions.append(idx + 1)

start_positions, end_positions
```

```python out
([83, 51, 19, 0, 0, 64, 27, 0, 34, 0, 0, 0, 67, 34, 0, 0, 0, 0, 0],
 [85, 53, 21, 0, 0, 70, 33, 0, 40, 0, 0, 0, 68, 35, 0, 0, 0, 0, 0])
```

Să aruncăm o privire la câteva rezultate pentru a verifica dacă abordarea noastră este corectă. Pentru prima caracteristică găsim `(83, 85)` ca labeluri, așa că comparăm răspunsul teoretic cu intervalul decodat de tokeni de la 83 la 85 (inclusiv):

```py
idx = 0
sample_idx = inputs["overflow_to_sample_mapping"][idx]
answer = answers[sample_idx]["text"][0]

start = start_positions[idx]
end = end_positions[idx]
labeled_answer = tokenizer.decode(inputs["input_ids"][idx][start : end + 1])

print(f"Theoretical answer: {answer}, labels give: {labeled_answer}")
```

```python out
'Theoretical answer: the Main Building, labels give: the Main Building'
```

Deci, asta e o potrivire! Acum să verificăm indexul 4, unde am setat labelurile la `(0, 0)`, ceea ce înseamnă că răspunsul nu se află în chunkul de context al acelei caracteristici:

```py
idx = 4
sample_idx = inputs["overflow_to_sample_mapping"][idx]
answer = answers[sample_idx]["text"][0]

decoded_example = tokenizer.decode(inputs["input_ids"][idx])
print(f"Theoretical answer: {answer}, decoded example: {decoded_example}")
```

```python out
'Theoretical answer: a Marian place of prayer and reflection, decoded example: [CLS] What is the Grotto at Notre Dame? [SEP] Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend " Venite Ad Me Omnes ". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grot [SEP]'
```

Într-adevăr, nu vedem răspunsul în interiorul contextului.

<Tip>

✏️ **E rândul tău!** Atunci când se utilizează arhitectura XLNet, paddingul este aplicat la stânga, iar întrebarea și contextul sunt schimbate. Adaptați tot codul pe care tocmai l-am văzut la arhitectura XLNet (și adăugați `padding=True`). Fiți conștienți de faptul că tokenul `[CLS]` ar putea să nu se afle la poziția 0 în cazul aplicării paddingului.

</Tip>

Acum că am văzut pas cu pas cum să preprocesăm datele de antrenare, le putem grupa într-o funcție pe care o vom aplica întregului dataset de antrenare. Vom umple fiecare caracteristică la lungimea maximă pe care am stabilit-o, deoarece majoritatea contextelor vor fi lungi (iar sampleurile corespunzătoare vor fi împărțite în mai multe caracteristici), astfel încât nu există niciun beneficiu real pentru aplicarea paddingului dinamic aici:

```py
max_length = 384
stride = 128


def preprocess_training_examples(examples):
    questions = [q.strip() for q in examples["question"]]
    inputs = tokenizer(
        questions,
        examples["context"],
        max_length=max_length,
        truncation="only_second",
        stride=stride,
        return_overflowing_tokens=True,
        return_offsets_mapping=True,
        padding="max_length",
    )

    offset_mapping = inputs.pop("offset_mapping")
    sample_map = inputs.pop("overflow_to_sample_mapping")
    answers = examples["answers"]
    start_positions = []
    end_positions = []

    for i, offset in enumerate(offset_mapping):
        sample_idx = sample_map[i]
        answer = answers[sample_idx]
        start_char = answer["answer_start"][0]
        end_char = answer["answer_start"][0] + len(answer["text"][0])
        sequence_ids = inputs.sequence_ids(i)

        # Find the start and end of the context
        idx = 0
        while sequence_ids[idx] != 1:
            idx += 1
        context_start = idx
        while sequence_ids[idx] == 1:
            idx += 1
        context_end = idx - 1

        # If the answer is not fully inside the context, label is (0, 0)
        if offset[context_start][0] > start_char or offset[context_end][1] < end_char:
            start_positions.append(0)
            end_positions.append(0)
        else:
            # Otherwise it's the start and end token positions
            idx = context_start
            while idx <= context_end and offset[idx][0] <= start_char:
                idx += 1
            start_positions.append(idx - 1)

            idx = context_end
            while idx >= context_start and offset[idx][1] >= end_char:
                idx -= 1
            end_positions.append(idx + 1)

    inputs["start_positions"] = start_positions
    inputs["end_positions"] = end_positions
    return inputs
```

Rețineți că am definit două constante pentru a determina lungimea maximă utilizată, precum și lungimea al sliding window, și că am adăugat o mică curățare înainte de tokenizare: unele dintre întrebările din datasetul SQuAD au spații suplimentare la început și la sfârșit care nu adaugă nimic (și ocupă spațiu atunci când sunt tokenizate dacă utilizați un model precum RoBERTa), așa că am eliminat aceste spații suplimentare.

Pentru a aplica această funcție întregului set de antrenare, folosim metoda `Dataset.map()` cu flagul `batched=True`. Acesta este necesar aici, deoarece modificăm lungimea datasetului (deoarece un exemplu poate oferi mai multe caracteristici de antrenare):

```py
train_dataset = raw_datasets["train"].map(
    preprocess_training_examples,
    batched=True,
    remove_columns=raw_datasets["train"].column_names,
)
len(raw_datasets["train"]), len(train_dataset)
```

```python out
(87599, 88729)
```

După cum putem vedea, preprocesarea a adăugat aproximativ 1.000 de caracteristici. Setul nostru de antrenare este acum gata de utilizare - să trecem la preprocesarea setului de validare!

### Procesarea datelor de validare[[processing-the-validation-data]]

Preprocesarea datelor de validare va fi puțin mai ușoară, deoarece nu trebuie să generăm labeluri (cu excepția cazului în care dorim să calculăm o pierdere de validare, dar acest număr nu ne va ajuta să înțelegem cât de bun este modelul). Adevărata bucurie va fi să interpretăm predicțiile modelului în intervale ale contextului original. Pentru aceasta, va trebui doar să stocăm atât offset mappings, cât și o modalitate de a corela fiecare caracteristică creată cu exemplul original din care provine. Deoarece există o coloană ID în datasetul original, vom utiliza acel ID.

Singurul lucru pe care îl vom adăuga aici este o mică curățare a offset mappings. Acestea vor conține offseturi pentru întrebare și context, dar odată ajunși în etapa de postprocesare nu vom avea nicio modalitate de a ști care parte a ID-urilor de intrare corespunde contextului și care parte este întrebarea (metoda `sequence_ids()` pe care am folosit-o este disponibilă doar pentru ieșirea tokenizerului). Prin urmare, vom seta offseturile corespunzătoare întrebării la `None`:


```py
def preprocess_validation_examples(examples):
    questions = [q.strip() for q in examples["question"]]
    inputs = tokenizer(
        questions,
        examples["context"],
        max_length=max_length,
        truncation="only_second",
        stride=stride,
        return_overflowing_tokens=True,
        return_offsets_mapping=True,
        padding="max_length",
    )

    sample_map = inputs.pop("overflow_to_sample_mapping")
    example_ids = []

    for i in range(len(inputs["input_ids"])):
        sample_idx = sample_map[i]
        example_ids.append(examples["id"][sample_idx])

        sequence_ids = inputs.sequence_ids(i)
        offset = inputs["offset_mapping"][i]
        inputs["offset_mapping"][i] = [
            o if sequence_ids[k] == 1 else None for k, o in enumerate(offset)
        ]

    inputs["example_id"] = example_ids
    return inputs
```

Putem aplica această funcție pe întregul dataset de validare, ca și înainte:

```py
validation_dataset = raw_datasets["validation"].map(
    preprocess_validation_examples,
    batched=True,
    remove_columns=raw_datasets["validation"].column_names,
)
len(raw_datasets["validation"]), len(validation_dataset)
```

```python out
(10570, 10822)
```

În acest caz, am adăugat doar câteva sute de sampleuri, astfel încât se pare că contextele din datasetul de validare sunt un pic mai scurte.

Acum că am preprocesat toate datele, putem trece la antrenare.

{#if fw === 'pt'}

## Fine-tuningul modelului cu API-ul `Trainer`[[fine-tuning-the-model-with-the-trainer-api]]

Codul de antrenare pentru acest exemplu va semăna foarte mult cu codul din secțiunile anterioare - cel mai greu lucru va fi să scriem funcția `compute_metrics()`. Deoarece am făcut padding tuturor sampleurilor la lungimea maximă pe care am stabilit-o, nu trebuie definit niciun data collator, astfel încât acest calcul al metricii este singurul lucru de care trebuie să ne facem griji. Partea dificilă va fi să postprocesăm predicțiile modelului în intervale de text în exemplele originale; odată ce am făcut acest lucru, metrica din biblioteca 🤗 Datasets va face cea mai mare parte a muncii pentru noi.

{:else}

## Fine-tuningul unui model cu Keras[[fine-tuning-the-model-with-keras]]

Codul de antrenare pentru acest exemplu va semăna foarte mult cu codul din secțiunile anterioare, dar calcularea metricilor va fi o provocare unică. Din moment ce am făcut padding tuturor sampleurilor la lungimea maximă pe care am stabilit-o, nu există niciun data collator care să fie definit, astfel încât acest calcul al metricii este singurul lucru de care trebuie să ne facem griji. Partea dificilă va fi să postprocesăm predicțiile modelului în intervale de text în exemplele originale; odată ce am făcut acest lucru, metrica din biblioteca 🤗 Datasets va face cea mai mare parte a muncii pentru noi.

{/if}

### Post-procesare[[post-processing]]

{#if fw === 'pt'}

<Youtube id="BNy08iIWVJM"/>

{:else}

<Youtube id="VN67ZpN33Ss"/>

{/if}

Modelul va produce logits pentru pozițiile de început și de sfârșit ale răspunsului în ID-urile de intrare, așa cum am văzut în timpul explorării [`question-answering` pipeline](/course/chapter6/3b). Etapa de post-procesare va fi similară cu ceea ce am făcut acolo, așa că iată vă reamintim ce acțiuni am luat:

- Am mascat logiturile de început și de sfârșit corespunzătoare tokenilor din afara contextului.
- Am convertit apoi logiturile de început și de sfârșit în probabilități utilizând un softmax.
- Am atribuit un scor fiecărei perechi `(start_token, end_token)` prin calcularea produsului celor două probabilități corespunzătoare.
- Am căutat perechea cu scorul maxim care a dat un răspuns valid (de exemplu, `start_token` mai mic decât `end_token`).

Aici vom schimba ușor acest proces, deoarece nu trebuie să calculăm scorurile reale (doar răspunsul prezis). Aceasta înseamnă că putem sări peste etapa softmax. De asemenea, pentru a merge mai repede, nu vom puncta toate perechile posibile `(start_token, end_token)`, ci doar pe cele care corespund celor mai mari `n_best` logits (cu `n_best=20`). Deoarece vom trece peste softmax, aceste scoruri vor fi scoruri logit și vor fi obținute prin însumarea logiturilor de început și de sfârșit (în loc de produs, datorită regulii \\(\log(ab) = \log(a) + \log(b)\\)).

Pentru a demonstra toate acestea, vom avea nevoie de un fel de predicții. Deoarece nu ne-am antrenat încă modelul, vom utiliza modelul implicit pentru pipelineuri QA pentru a genera unele predicții pe o mică parte a setului de validare. Putem utiliza aceeași funcție de procesare ca înainte; deoarece se bazează pe constanta globală `tokenizer`, trebuie doar să schimbăm acest obiect cu tokenizerul modelului pe care dorim să îl utilizăm temporar:

```python
small_eval_set = raw_datasets["validation"].select(range(100))
trained_checkpoint = "distilbert-base-cased-distilled-squad"

tokenizer = AutoTokenizer.from_pretrained(trained_checkpoint)
eval_set = small_eval_set.map(
    preprocess_validation_examples,
    batched=True,
    remove_columns=raw_datasets["validation"].column_names,
)
```

Acum că preprocesarea este terminată, schimbăm tokenizerul înapoi la cel pe care l-am ales inițial:

```python
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
```

Apoi eliminăm coloanele din `eval_set` care nu sunt așteptate de model, construim un batch cu întregul set de validare și îl trecem prin model. Dacă este disponibil un GPU, îl folosim pentru a merge mai repede:

{#if fw === 'pt'}

```python
import torch
from transformers import AutoModelForQuestionAnswering

eval_set_for_model = eval_set.remove_columns(["example_id", "offset_mapping"])
eval_set_for_model.set_format("torch")

device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
batch = {k: eval_set_for_model[k].to(device) for k in eval_set_for_model.column_names}
trained_model = AutoModelForQuestionAnswering.from_pretrained(trained_checkpoint).to(
    device
)

with torch.no_grad():
    outputs = trained_model(**batch)
```

Deoarece `Trainer` ne va oferi predicții sub formă de matrici NumPy, preluăm logiturile de început și de sfârșit și le convertim în acest format:

```python
start_logits = outputs.start_logits.cpu().numpy()
end_logits = outputs.end_logits.cpu().numpy()
```

{:else}

```python
import tensorflow as tf
from transformers import TFAutoModelForQuestionAnswering

eval_set_for_model = eval_set.remove_columns(["example_id", "offset_mapping"])
eval_set_for_model.set_format("numpy")

batch = {k: eval_set_for_model[k] for k in eval_set_for_model.column_names}
trained_model = TFAutoModelForQuestionAnswering.from_pretrained(trained_checkpoint)

outputs = trained_model(**batch)
```

Pentru ușurința experimentelor, să convertim aceste rezultate în array-uri NumPy:

```python
start_logits = outputs.start_logits.numpy()
end_logits = outputs.end_logits.numpy()
```

{/if}

Acum, trebuie să găsim răspunsul prezis pentru fiecare exemplu din `small_eval_set`. Este posibil ca un exemplu să fi fost împărțit în mai multe caracteristici în `eval_set`, astfel încât primul pas constă în maparea fiecărui exemplu din `small_eval_set` la caracteristicile corespunzătoare din `eval_set`:

```python
import collections

example_to_features = collections.defaultdict(list)
for idx, feature in enumerate(eval_set):
    example_to_features[feature["example_id"]].append(idx)
```

Cu acest lucru în mână, ne putem apuca de treabă trecând prin toate exemplele și, pentru fiecare exemplu, prin toate caracteristicile asociate. Așa cum am spus mai devreme, ne vom uita la scorurile logit pentru `n_cele mai bune` logits de început și de sfârșit, excluzând pozițiile care dau:

- Un răspuns care nu ar fi în interiorul contextului
- Un răspuns cu lungime negativă
- Un răspuns care este prea lung (limităm posibilitățile la `max_answer_length=30`)

Odată ce avem toate răspunsurile posibile scored pentru un exemplu, îl alegem pe cel cu cel mai bun scor logit:

```python
import numpy as np

n_best = 20
max_answer_length = 30
predicted_answers = []

for example in small_eval_set:
    example_id = example["id"]
    context = example["context"]
    answers = []

    for feature_index in example_to_features[example_id]:
        start_logit = start_logits[feature_index]
        end_logit = end_logits[feature_index]
        offsets = eval_set["offset_mapping"][feature_index]

        start_indexes = np.argsort(start_logit)[-1 : -n_best - 1 : -1].tolist()
        end_indexes = np.argsort(end_logit)[-1 : -n_best - 1 : -1].tolist()
        for start_index in start_indexes:
            for end_index in end_indexes:
                # Skip answers that are not fully in the context
                if offsets[start_index] is None or offsets[end_index] is None:
                    continue
                # Skip answers with a length that is either < 0 or > max_answer_length.
                if (
                    end_index < start_index
                    or end_index - start_index + 1 > max_answer_length
                ):
                    continue

                answers.append(
                    {
                        "text": context[offsets[start_index][0] : offsets[end_index][1]],
                        "logit_score": start_logit[start_index] + end_logit[end_index],
                    }
                )

    best_answer = max(answers, key=lambda x: x["logit_score"])
    predicted_answers.append({"id": example_id, "prediction_text": best_answer["text"]})
```

Formatul final al răspunsurilor prezise este cel care va fi așteptat de metrica pe care o vom utiliza. Ca de obicei, o putem încărca cu ajutorul bibliotecii 🤗 Evaluate:

```python
import evaluate

metric = evaluate.load("squad")
```

Această metrică așteaptă răspunsurile prezise în formatul pe care l-am văzut mai sus (o listă de dicționare cu o cheie pentru ID-ul exemplului și o cheie pentru textul prezis) și răspunsurile teoretice în formatul de mai jos (o listă de dicționare cu o cheie pentru ID-ul exemplului și o cheie pentru răspunsurile posibile):

```python
theoretical_answers = [
    {"id": ex["id"], "answers": ex["answers"]} for ex in small_eval_set
]
```

Acum putem verifica dacă obținem rezultate rezonabile analizând primul element din ambele liste:

```python
print(predicted_answers[0])
print(theoretical_answers[0])
```

```python out
{'id': '56be4db0acb8001400a502ec', 'prediction_text': 'Denver Broncos'}
{'id': '56be4db0acb8001400a502ec', 'answers': {'text': ['Denver Broncos', 'Denver Broncos', 'Denver Broncos'], 'answer_start': [177, 177, 177]}}
```

Nu-i deloc rău! Acum putem arunca o privire la scorul pe care ni-l oferă metrica:

```python
metric.compute(predictions=predicted_answers, references=theoretical_answers)
```

```python out
{'exact_match': 83.0, 'f1': 88.25}
```

Din nou, acest lucru este destul de bun, având în vedere că, în conformitate cu [documentul acestuia] (https://arxiv.org/abs/1910.01108v2), DistilBERT fine-tuned pe SQuAD obține 79,1 și 86,9 pentru aceste scoruri pe întregul dataset.

{#if fw === 'pt'}

Acum să punem tot ce am făcut într-o funcție `compute_metrics()` pe care o vom folosi în `Trainer`. În mod normal, această funcție `compute_metrics()` primește doar un tuple `eval_preds` cu logiți și labels. Aici vom avea nevoie de ceva mai mult, deoarece trebuie să căutăm în dataset de caracteristici pentru offset și în datasetul de exemple pentru contextele originale, astfel încât nu vom putea utiliza această funcție pentru a obține rezultate de evaluare regulate în timpul antrenării. O vom utiliza doar la sfârșitul antrenamentului pentru a verifica rezultatele.

Funcția `compute_metrics()` grupează aceiași pași ca înainte; adăugăm doar o mică verificare în cazul în care nu obținem niciun răspuns valid (caz în care prezicem un șir gol).

{:else}

Acum să punem tot ce tocmai am făcut într-o funcție `compute_metrics()` pe care o vom folosi după antrenarea modelului nostru. Va trebui să transmitem puțin mai mult decât output logits, deoarece trebuie să căutăm în datasetul de caracteristici pentru offset și în datasetul de exemple pentru contextele originale:

{/if}

```python
from tqdm.auto import tqdm


def compute_metrics(start_logits, end_logits, features, examples):
    example_to_features = collections.defaultdict(list)
    for idx, feature in enumerate(features):
        example_to_features[feature["example_id"]].append(idx)

    predicted_answers = []
    for example in tqdm(examples):
        example_id = example["id"]
        context = example["context"]
        answers = []

        # Loop through all features associated with that example
        for feature_index in example_to_features[example_id]:
            start_logit = start_logits[feature_index]
            end_logit = end_logits[feature_index]
            offsets = features[feature_index]["offset_mapping"]

            start_indexes = np.argsort(start_logit)[-1 : -n_best - 1 : -1].tolist()
            end_indexes = np.argsort(end_logit)[-1 : -n_best - 1 : -1].tolist()
            for start_index in start_indexes:
                for end_index in end_indexes:
                    # Skip answers that are not fully in the context
                    if offsets[start_index] is None or offsets[end_index] is None:
                        continue
                    # Skip answers with a length that is either < 0 or > max_answer_length
                    if (
                        end_index < start_index
                        or end_index - start_index + 1 > max_answer_length
                    ):
                        continue

                    answer = {
                        "text": context[offsets[start_index][0] : offsets[end_index][1]],
                        "logit_score": start_logit[start_index] + end_logit[end_index],
                    }
                    answers.append(answer)

        # Select the answer with the best score
        if len(answers) > 0:
            best_answer = max(answers, key=lambda x: x["logit_score"])
            predicted_answers.append(
                {"id": example_id, "prediction_text": best_answer["text"]}
            )
        else:
            predicted_answers.append({"id": example_id, "prediction_text": ""})

    theoretical_answers = [{"id": ex["id"], "answers": ex["answers"]} for ex in examples]
    return metric.compute(predictions=predicted_answers, references=theoretical_answers)
```

Putem verifica dacă funcționează pe baza predicțiilor noastre:

```python
compute_metrics(start_logits, end_logits, eval_set, small_eval_set)
```

```python out
{'exact_match': 83.0, 'f1': 88.25}
```

Arată bine! Acum să folosim acest lucru pentru a face fine-tune modelului nostru.

### Fine-tuningul modelului[[fine-tuning-the-model]]

{#if fw === 'pt'}

Acum suntem gata să antrenăm modelul. Să-l creăm mai întâi, folosind clasa `AutoModelForQuestionAnswering` ca și înainte:

```python
model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
```

{:else}

Acum suntem gata să antrenăm modelul. Să-l creăm mai întâi, folosind clasa `TFAutoModelForQuestionAnswering` ca mai înainte:

```python
model = TFAutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
```

{/if}

Ca de obicei, primim un avertisment că unele weighturi nu sunt utilizate (cele din headul de preantrenare), iar altele sunt inițializate aleatoriu (cele pentru headul pentru răspuns la întrebări). Ar trebui să fiți obișnuiți cu acest lucru până acum, dar înseamnă că acest model nu este încă pregătit pentru a fi utilizat și trebuie să fie fine-tuned - bine că suntem pe cale să facem asta!

Pentru a putea trimite modelul nostru către Hub, va trebui să ne conectăm la Hugging Face. Dacă executați acest cod într-un notebook, puteți face acest lucru cu următoarea funcție utilitară, care afișează un widget în care puteți introduce datele voastre de autentificare:


```python
from huggingface_hub import notebook_login

notebook_login()
```

Dacă nu lucrați într-un notebook, tastați următoarea linie în terminal:

```bash
huggingface-cli login
```

{#if fw === 'pt'}

Odată făcut acest lucru, ne putem defini `TrainingArguments`. Așa cum am spus atunci când am definit funcția noastră pentru a calcula metrica, nu vom putea avea o buclă de evaluare obișnuită din cauza parametrilor funcției `compute_metrics()`. Am putea scrie propria noastră subclasă a `Trainer` pentru a face acest lucru (o abordare pe care o puteți găsi în [scriptul exemplu pentru răspunderea la întrebări](https://github.com/huggingface/transformers/blob/master/examples/pytorch/question-answering/trainer_qa.py)), dar este un pic prea lung pentru această secțiune. În schimb, aici vom evalua modelul doar la sfârșitul antrenării și vă vom arăta cum să efectuați o evaluare obișnuită în secțiunea "O buclă de antrenare personalizată" de mai jos.

Acesta este într-adevăr locul în care API-ul `Trainer` își arată limitele și biblioteca 🤗 Accelerate strălucește: personalizarea clasei pentru un caz de utilizare specific poate fi greu de implementat, dar modificarea unei bucle de antrenare complet expuse este ușoară.

Să aruncăm o privire la `TrainingArguments`:

```python
from transformers import TrainingArguments

args = TrainingArguments(
    "bert-finetuned-squad",
    evaluation_strategy="no",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
    fp16=True,
    push_to_hub=True,
)
```

Am mai văzut cele mai multe dintre acestea: stabilim niște hiperparametrii (cum ar fi learning rate, numărul de epoci pentru antrenament și o anumită scădere a weighturilor) și indicăm că dorim să salvăm modelul la sfârșitul fiecărei epoci, să sărim peste evaluare și să încărcăm rezultatele noastre în Model Hub. De asemenea, activăm antrenarea cu precizie mixtă cu `fp16=True`, deoarece aceasta poate accelera foarte mult antrenarea pe un GPU recent.

{:else}

Acum, putem crea dataseturile TF. De data aceasta, putem utiliza data collatorul de bază:

```python
from transformers import DefaultDataCollator

data_collator = DefaultDataCollator(return_tensors="tf")
```

Și acum creăm dataseturile ca de obicei.

```python
tf_train_dataset = model.prepare_tf_dataset(
    train_dataset,
    collate_fn=data_collator,
    shuffle=True,
    batch_size=16,
)
tf_eval_dataset = model.prepare_tf_dataset(
    validation_dataset,
    collate_fn=data_collator,
    shuffle=False,
    batch_size=16,
)
```

În continuare, stabilim hiperparametrii de antrenament și compilăm modelul nostru:

```python
from transformers import create_optimizer
from transformers.keras_callbacks import PushToHubCallback
import tensorflow as tf

# Numărul etapelor de antrenare este numărul de sampleuri din dataset, împărțit la dimensiunea batch-ului, apoi înmulțit
# cu numărul total de epoci. Rețineți că datasetul tf_train_dataset de aici este un batched tf.data.Dataset,
# nu datasetul original Hugging Face, deci len() este deja num_samples // batch_size.
num_train_epochs = 3
num_train_steps = len(tf_train_dataset) * num_train_epochs
optimizer, schedule = create_optimizer(
    init_lr=2e-5,
    num_warmup_steps=0,
    num_train_steps=num_train_steps,
    weight_decay_rate=0.01,
)
model.compile(optimizer=optimizer)

# Antrenarea în mixed-precision float16
tf.keras.mixed_precision.set_global_policy("mixed_float16")
```

În cele din urmă, suntem gata să ne antrenăm cu `model.fit()`. Folosim un `PushToHubCallback` pentru a încărca modelul în Hub după fiecare epocă.

{/if}

În mod implicit, repositoriul utilizat va fi în namespaceul vostru și numit după foldrul de ieșire pe care l-ați stabilit, deci în cazul nostru va fi în `"sgugger/bert-finetuned-squad"`. Putem trece peste acest lucru prin trecerea unui `hub_model_id`; de exemplu, pentru a încărca modelul în organizația `huggingface_course`, am folosit `hub_model_id="huggingface_course/bert-finetuned-squad"` (care este modelul la care am făcut legătura la începutul acestei secțiuni).


{#if fw === 'pt'}

<Tip>

💡 Dacă folderul de ieșire pe care îl utilizați există, acesta trebuie să fie o clonă locală a repositoriul în care doriți să faceți push (deci setați un nume nou dacă primiți o eroare la definirea `Trainer`).

</Tip>

În cele din urmă, trecem totul în clasa `Trainer` și lansăm antrenarea:

```python
from transformers import Trainer

trainer = Trainer(
    model=model,
    args=args,
    train_dataset=train_dataset,
    eval_dataset=validation_dataset,
    tokenizer=tokenizer,
)
trainer.train()
```

{:else}

```python
from transformers.keras_callbacks import PushToHubCallback

callback = PushToHubCallback(output_dir="bert-finetuned-squad", tokenizer=tokenizer)

# Vom face validarea după aceea, deci nu vom face o validare în timpul antrenării
model.fit(tf_train_dataset, callbacks=[callback], epochs=num_train_epochs)
```

{/if}

Rețineți că, în timpul antrenamentului, de fiecare dată când modelul este salvat (aici, la fiecare epocă), acesta este încărcat în Hub pe fundal. În acest fel, veți putea să reluați antrenarea pe un alt device, dacă este necesar. Întreaga pregătire durează ceva timp (puțin peste o oră pe un Titan RTX), așa că puteți să vă luați o cafea sau să recitiți unele dintre părțile cursului care vi s-au părut mai dificile în timp ce se desfășoară. De asemenea, rețineți că, de îndată ce se termină prima epocă, veți vedea câteva weighturu încărcate în Hub și puteți începe să vă jucați cu modelul vostru pe pagina acestuia.

{#if fw === 'pt'}

Odată ce antrenamentul este complet, putem în cele din urmă să evaluăm modelul (și să ne rugăm să nu fi petrecut tot timpul de calcul degeaba). Metoda `predict()` a `Trainer` va returna un tuple în care primele elemente vor fi predicțiile modelului (aici o pereche cu logiturile de început și de sfârșit). Trimitem acest rezultat funcției noastre `compute_metrics()`:

```python
predictions, _, _ = trainer.predict(validation_dataset)
start_logits, end_logits = predictions
compute_metrics(start_logits, end_logits, validation_dataset, raw_datasets["validation"])
```

{:else}

Odată ce antrenamentul este complet, putem în sfârșit să ne evaluăm modelul (și să ne rugăm să nu fi cheltuit tot timpul de calcul degeaba). Metoda `predict()` a modelului nostru `model` se va ocupa de obținerea predicțiilor și, deoarece am făcut toată munca grea de definire a unei funcții `compute_metrics()` mai devreme, putem obține rezultatele noastre într-o singură linie:

```python
predictions = model.predict(tf_eval_dataset)
compute_metrics(
    predictions["start_logits"],
    predictions["end_logits"],
    validation_dataset,
    raw_datasets["validation"],
)
```

{/if}

```python out
{'exact_match': 81.18259224219489, 'f1': 88.67381321905516}
```

Super! Ca o comparație, scorurile de bază raportate în articolul BERT pentru acest model sunt 80,8 și 88,5, deci suntem exact unde ar trebui să fim.

{#if fw === 'pt'}

În final, folosim metoda `push_to_hub()` pentru a ne asigura că încărcăm cea mai recentă versiune a modelului:

```py
trainer.push_to_hub(commit_message="Training complete")
```

Aceasta returnează URL-ul commit-ului pe care tocmai l-a făcut, dacă doriți să îl inspectați:

```python out
'https://huggingface.co/sgugger/bert-finetuned-squad/commit/9dcee1fbc25946a6ed4bb32efb1bd71d5fa90b68'
```

De asemenea, `Trainer` redactează un model card cu toate rezultatele evaluării și o încarcă.

{/if}

În această etapă, puteți utiliza widgetul de inferență de pe Model Hub pentru a testa modelul și pentru a-l oferi prietenilor, familia și animalele de companie preferate. Ați făcut fine-tune cu succes unui model pentru o sarcină de răspundere a unei întrebari - felicitări!

<Tip>

✏️ **E rândul tău!** Încearcă un alt model de arhitectură pentru a vedea dacă are performanțe mai bune la această sarcină!

</Tip>

{#if fw === 'pt'}

Dacă doriți să pătrundeți puțin mai adânc în bucla de antrenare, vă vom arăta acum cum să faceți același lucru folosind 🤗 Accelerate.

## O buclă de antrenare personalizată[[a-custom-training-loop]]

Să aruncăm acum o privire la bucla de antrenare completă, astfel încât să puteți personaliza cu ușurință părțile de care aveți nevoie. Aceasta va semăna foarte mult cu bucla de antrenare din [Capitolul 3](/course/chapter3/4), cu excepția buclei de evaluare. Vom putea evalua modelul în mod regulat, deoarece nu mai suntem constrânși de clasa `Trainer`.

### Pregătirea pentru antrenament[[preparing-everything-for-training]]

Mai întâi trebuie să construim `DataLoader`s din dataseturile noastre. Am setat formatul acestor dataseturi la `"torch"` și am eliminat coloanele din setul de validare care nu sunt utilizate de model. Apoi, putem utiliza `default_data_collator` furnizat de Transformers ca un `collate_fn` și să amestecăm setul de antrenare, dar nu și setul de validare:

```py
from torch.utils.data import DataLoader
from transformers import default_data_collator

train_dataset.set_format("torch")
validation_set = validation_dataset.remove_columns(["example_id", "offset_mapping"])
validation_set.set_format("torch")

train_dataloader = DataLoader(
    train_dataset,
    shuffle=True,
    collate_fn=default_data_collator,
    batch_size=8,
)
eval_dataloader = DataLoader(
    validation_set, collate_fn=default_data_collator, batch_size=8
)
```

În continuare, reinițializăm modelul, pentru a ne asigura că nu continuăm fine-tuningul de dinainte, ci pornim din nou de la modelul preantrenat BERT:

```py
model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
```

Atunci vom avea nevoie de un optimizator. Ca de obicei, folosim clasicul `AdamW`, care este ca Adam, dar cu o corecție în modul în care se aplică scăderea weighturilor:

```py
from torch.optim import AdamW

optimizer = AdamW(model.parameters(), lr=2e-5)
```

Odată ce avem toate aceste obiecte, le putem trimite metodei `accelerator.prepare()`. Amintiți-vă că, dacă doriți să vă antrenați pe un TPU într-un notebook Colab, va trebui să mutați tot acest cod într-o funcție de antrenament, care nu ar trebui să execute nicio celulă care inițializează un `Accelerator`. Putem forța antrenarea cu precizie mixtă trecând `fp16=True` la `Accelerator` (sau, dacă executați codul ca un script, asigurați-vă că completați `config` în 🤗 Accelerate în mod corespunzător).

```py
from accelerate import Accelerator

accelerator = Accelerator(fp16=True)
model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
    model, optimizer, train_dataloader, eval_dataloader
)
```

După cum ar trebui să știți din secțiunile anterioare, putem utiliza lungimea `train_dataloader` pentru a calcula numărul de pași de antrenare numai după ce a trecut prin metoda `accelerator.prepare()`. Utilizăm același program liniar ca în secțiunile anterioare:

```py
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,
)
```

Pentru a trimite modelul nostru către Hub, va trebui să creăm un obiect `Repository` într-un folder de lucru. În primul rând, conectați-vă la Hugging Face Hub, dacă nu sunteți deja conectat. Vom determina numele repositoriul pornind de la ID-ul modelului pe care dorim să îl atribuim modelului nostru (nu ezitați să înlocuiți `repo_name` cu propria alegere; acesta trebuie doar să conțină numele vostru de utilizator, ceea ce face funcția `get_full_repo_name()`):

```py
from huggingface_hub import Repository, get_full_repo_name

model_name = "bert-finetuned-squad-accelerate"
repo_name = get_full_repo_name(model_name)
repo_name
```

```python out
'sgugger/bert-finetuned-squad-accelerate'
```

Apoi putem clona acel repositoriu într-un folder local. Dacă există deja, acest folder local ar trebui să fie o clonă a repositoriului cu care lucrăm:

```py
output_dir = "bert-finetuned-squad-accelerate"
repo = Repository(output_dir, clone_from=repo_name)
```

Acum putem încărca orice salvăm în `output_dir` prin apelarea metodei `repo.push_to_hub()`. Acest lucru ne va ajuta să încărcăm modelele intermediare la sfârșitul fiecărei epoci.

## Bucla de antrenare[[training-loop]]

Acum suntem pregătiți să scriem bucla de antrenare completă. După definirea unei bare de progres pentru a urmări modul în care decurge antrenamentul, bucla are trei părți:

- Pregătirea în sine, care este iterația clasică peste `train_dataloader`, trecerea înainte prin model, apoi trecerea înapoi și pasul optimizatorului.
- Evaluarea, în care adunăm toate valorile pentru `start_logits` și `end_logits` înainte de a le converti în matrici NumPy. Odată ce bucla de evaluare este terminată, concatenăm toate rezultatele. Rețineți că trebuie să truncăm deoarece `Accelerator` ar fi putut adăuga câteva exemple la sfârșit pentru a ne asigura că avem același număr de exemple în fiecare proces.
- Salvarea și încărcarea, unde mai întâi salvăm modelul și tokenizatorul, apoi apelăm `repo.push_to_hub()`. Ca și înainte, folosim argumentul `blocking=False` pentru a spune bibliotecii 🤗 Hub să efectueze push-ul într-un proces asincron. În acest fel, antrenamentul continuă normal, iar această instrucțiune (lungă) este executată pe fundal.

Iată codul complet pentru bucla de antrenare:

```py
from tqdm.auto import tqdm
import torch

progress_bar = tqdm(range(num_training_steps))

for epoch in range(num_train_epochs):
    # Training
    model.train()
    for step, batch in enumerate(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()
    start_logits = []
    end_logits = []
    accelerator.print("Evaluation!")
    for batch in tqdm(eval_dataloader):
        with torch.no_grad():
            outputs = model(**batch)

        start_logits.append(accelerator.gather(outputs.start_logits).cpu().numpy())
        end_logits.append(accelerator.gather(outputs.end_logits).cpu().numpy())

    start_logits = np.concatenate(start_logits)
    end_logits = np.concatenate(end_logits)
    start_logits = start_logits[: len(validation_dataset)]
    end_logits = end_logits[: len(validation_dataset)]

    metrics = compute_metrics(
        start_logits, end_logits, validation_dataset, raw_datasets["validation"]
    )
    print(f"epoch {epoch}:", metrics)

    # 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
        )
```

În cazul în care este prima dată când vedeți un model salvat cu 🤗 Accelerate, să ne oprim puțin pentru a inspecta cele trei linii de cod care îl însoțesc:

```py
accelerator.wait_for_everyone()
unwrapped_model = accelerator.unwrap_model(model)
unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
```

Prima linie se explică de la sine: aceasta spune tuturor proceselor să aștepte până când toată lumea se află în etapa respectivă înainte de a continua. Acest lucru are rolul de a ne asigura că avem același model în fiecare proces înainte de salvare. Apoi luăm `unwrapped_model`, care este modelul de bază pe care l-am definit. Metoda `accelerator.prepare()` modifică modelul pentru a funcționa în antrenarea distribuită, deci nu va mai avea metoda `save_pretrained()`; metoda `accelerator.unwrap_model()` anulează acest pas. În cele din urmă, apelăm metoda `save_pretrained()`, dar îi spunem să folosească metoda `accelerator.save()` în loc de `torch.save()`.

Odată făcut acest lucru, ar trebui să aveți un model care produce rezultate destul de asemănătoare cu cel antrenat cu `Trainer`. Puteți verifica modelul pe care l-am antrenat folosind acest cod pe [*huggingface-course/bert-finetuned-squad-accelerate*] (https://huggingface.co/huggingface-course/bert-finetuned-squad-accelerate). Și dacă doriți să testați orice modificări ale buclei de antrenare, le puteți implementa direct prin editarea codului prezentat mai sus!

{/if}

## Utilizarea modelului fine-tuned[[using-the-fine-tuned-model]]

V-am arătat deja cum puteți utiliza modelul căruia i-am făcut fine-tune pe Model Hub cu widgetul de inferență. Pentru a-l utiliza local într-un `pipeline`, trebuie doar să specificați identificatorul modelului:

```py
from transformers import pipeline

# Înlocuiți acest checkpoint cu propriul checkpoint
model_checkpoint = "huggingface-course/bert-finetuned-squad"
question_answerer = pipeline("question-answering", model=model_checkpoint)

context = """
🤗 Transformers is backed by the three most popular deep learning libraries — Jax, PyTorch and TensorFlow — with a seamless integration
between them. It's straightforward to train your models with one before loading them for inference with the other.
"""
question = "Which deep learning libraries back 🤗 Transformers?"
question_answerer(question=question, context=context)
```

```python out
{'score': 0.9979003071784973,
 'start': 78,
 'end': 105,
 'answer': 'Jax, PyTorch and TensorFlow'}
```

Grozav! Modelul nostru funcționează la fel de bine ca cel implicit pentru aceast pipeline!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter7/7.mdx" />

### Clasificarea tokenilor[[token-classification]]
https://huggingface.co/learn/course/ro/chapter7/2.md

# Clasificarea tokenilor[[token-classification]]

{#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/section2_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter7/section2_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/section2_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter7/section2_tf.ipynb"},
]} />

{/if}

Prima aplicație pe care o vom explora este clasificarea tokenilor. Această sarcină generică cuprinde orice problemă care poate fi formulată ca "atribuirea unui label fiecărui token dintr-o propoziție", cum ar fi:

- **Named entity recognition(NER)**: Găsirea entităților (cum ar fi persoane, locații sau organizații) într-o propoziție. Acest lucru poate fi formulat ca atribuirea unui label fiecărui token, având o clasă pentru fiecare entitate și o clasă pentru "nicio entitate".
- **Part-of-speech tagging (POS)**: Marchează fiecare cuvânt dintr-o propoziție ca corespunzând unei anumite părți de vorbire (cum ar fi substantiv, verb, adjectiv etc.).
- **Chunking**: Găsirea tokenilor care aparțin aceleiași entități. Această sarcină (care poate fi combinată cu POS sau NER) poate fi formulată ca atribuirea unui label (de obicei `B-`) tuturor tokenilor care se află la începutul unui chunk, a unui alt label (de obicei `I-`) la tokeni care se află în interiorul unui chunk și a unui al treilea label (de obicei `O`) la tokeni care nu aparțin niciunui chunk.

<Youtube id="wVHdVlPScxA"/>

Desigur, există multe alte tipuri de probleme de clasificare a tokenilor; acestea sunt doar câteva exemple reprezentative. În această secțiune, vom pune la punct un model (BERT) pe o sarcină NER, care va fi apoi capabil să calculeze predicții precum aceasta:

<iframe src="https://course-demos-bert-finetuned-ner.hf.space" frameBorder="0" height="350" 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>

<a class="flex justify-center" href="/huggingface-course/bert-finetuned-ner">
<img class="block dark:hidden lg:w-3/5" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/model-eval-bert-finetuned-ner.png" alt="One-hot encoding labels pentru răspunderea la întrebări."/>
<img class="hidden dark:block lg:w-3/5" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/model-eval-bert-finetuned-ner-dark.png" alt="One-hot encoded labels pentru răspunderea la întrebări."/>
</a>

Puteți găsi modelul pe care îl vom antrena și încărca în Hub și puteți verifica de două ori predicțiile sale [aici](https://huggingface.co/huggingface-course/bert-finetuned-ner?text=My+name+is+Sylvain+and+I+work+at+Hugging+Face+in+Brooklyn).

## Pregătirea datelor[[preparing-the-data]]

În primul rând, avem nevoie de un dataset adecvat pentru clasificarea simbolurilor. În această secțiune vom utiliza [datasetul CoNLL-2003] (https://huggingface.co/datasets/conll2003), care conține știri de la Reuters.

<Tip>

💡 Atât timp cât datasetul vostru constă în texte împărțite în cuvinte cu labelurile corespunzătoare, veți putea adapta procedurile de preprocesare a datelor descrise aici la propriul dataset. Consultați [Capitolul 5](/course/chapter5) dacă aveți nevoie de o recapitulare a modului de încărcare a propriilor date personalizate într-un `Dataset`.

</Tip>

### Datasetul CoNLL-2003 [[the-conll-2003-dataset]]

Pentru a încărca datasetul CoNLL-2003, folosim metoda `load_dataset()` din biblioteca 🤗 Datasets:

```py
from datasets import load_dataset

raw_datasets = load_dataset("conll2003")
```

Acest lucru va descărca și va stoca în cache datasetul, așa cum am văzut în [Capitolul 3](/course/chapter3) pentru setul de date GLUE MRPC. Inspectarea acestui obiect ne arată coloanele prezente și împărțirea între seturile de antrenare, validare și testare:

```py
raw_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'],
        num_rows: 14041
    })
    validation: Dataset({
        features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'],
        num_rows: 3250
    })
    test: Dataset({
        features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'],
        num_rows: 3453
    })
})
```

În special, putem vedea că datasetul conține labeluri pentru cele trei sarcini pe care le-am menționat mai devreme: NER, POS și Chunking. O mare diferență față de alte datseturi este că textele de intrare nu sunt prezentate ca propoziții sau documente, ci ca liste de cuvinte (ultima coloană se numește `tokens`, dar conține cuvinte în sensul că acestea sunt inputuri pre-tokenizate care mai trebuie să treacă prin tokenizer pentru tokenizarea subcuvintelor).

Să aruncăm o privire la primul element al setului de formare:

```py
raw_datasets["train"][0]["tokens"]
```

```python out
['EU', 'rejects', 'German', 'call', 'to', 'boycott', 'British', 'lamb', '.']
```

Deoarece dorim să efectuăm named entity recognition, ne vom uita la etichetele NER:

```py
raw_datasets["train"][0]["ner_tags"]
```

```python out
[3, 0, 7, 0, 0, 0, 7, 0, 0]
```

Acestea sunt labelurile ca numere întregi pregătite pentru antrenare, dar nu sunt neapărat utile atunci când dorim să inspectăm datele. La fel ca în cazul clasificării textului, putem accesa corespondența dintre aceste numere întregi și numele labelurilor consultând atributul `features` al datasetului nostru:

```py
ner_feature = raw_datasets["train"].features["ner_tags"]
ner_feature
```

```python out
Sequence(feature=ClassLabel(num_classes=9, names=['O', 'B-PER', 'I-PER', 'B-ORG', 'I-ORG', 'B-LOC', 'I-LOC', 'B-MISC', 'I-MISC'], names_file=None, id=None), length=-1, id=None)
```

Deci, această coloană conține elemente care sunt secvențe a `ClassLabel`. Tipul elementelor din secvență se află în atributul `feature` al acestei `ner_feature`, iar noi putem accesa lista de nume consultând atributul `names` al acelei `feature`:

```py
label_names = ner_feature.feature.names
label_names
```

```python out
['O', 'B-PER', 'I-PER', 'B-ORG', 'I-ORG', 'B-LOC', 'I-LOC', 'B-MISC', 'I-MISC']
```

Am văzut deja aceste labeluri atunci când am cercetat pipelineul `token-classification` în [Capitolul 6](/course/chapter6/3), dar pentru o reîmprospătare rapidă:

- `O` înseamnă că cuvântul nu corespunde niciunei entități.
- `B-PER`/`I-PER` înseamnă că cuvântul corespunde începutului/este în interiorul unei entități de tip *persoană*.
- `B-ORG`/`I-ORG` înseamnă că cuvântul corespunde începutului/este în interiorul unei entități de tip *organizaționale*.
- `B-LOC`/`I-LOC` înseamnă că cuvântul corespunde începutului/este în interiorul unei entități de tip  *locație*.
- `B-MISC`/`I-MISC` înseamnă că cuvântul corespunde începutului/este în interiorul unei entități de tip *miscellaneous.

Acum, decodificarea labelurilor pe care le-am văzut mai devreme ne dă următorul rezultat:

```python
words = raw_datasets["train"][0]["tokens"]
labels = raw_datasets["train"][0]["ner_tags"]
line1 = ""
line2 = ""
for word, label in zip(words, labels):
    full_label = label_names[label]
    max_length = max(len(word), len(full_label))
    line1 += word + " " * (max_length - len(word) + 1)
    line2 += full_label + " " * (max_length - len(full_label) + 1)

print(line1)
print(line2)
```

```python out
'EU    rejects German call to boycott British lamb .'
'B-ORG O       B-MISC O    O  O       B-MISC  O    O'
```

Și pentru un exemplu de amestecare a labelurilor `B-` și `I-`, iată ce ne oferă același cod pentru elementul din setul de antrenare de la indexul 4:

```python out
'Germany \'s representative to the European Union \'s veterinary committee Werner Zwingmann said on Wednesday consumers should buy sheepmeat from countries other than Britain until the scientific advice was clearer .'
'B-LOC   O  O              O  O   B-ORG    I-ORG O  O          O         B-PER  I-PER     O    O  O         O         O      O   O         O    O         O     O    B-LOC   O     O   O          O      O   O       O'
```

După cum putem vedea, entităților care cuprind două cuvinte, precum "Uniunea Europeană" și "Werner Zwingmann", li se atribuie un label `B-` pentru primul cuvânt și un label `I-` pentru al doilea.

<Tip>

✏️ **E rândul tău!** Afișați aceleași două propoziții cu labelurile POS sau chunking.

</Tip>

### Procesarea datelor[[processing-the-data]]

<Youtube id="iY2AZYdZAr0"/>

Ca de obicei, textele noastre trebuie să fie convertite în ID-uri token înainte ca modelul să le poată înțelege. După cum am văzut în [Capitolul 6](/course/chapter6/), o mare diferență în cazul sarcinilor de clasificare a tokenilor este că avem inputuri pre-tokenizate. Din fericire, API-ul tokenizerului poate face față acestei situații destul de ușor; trebuie doar să avertizăm `tokenizerul` cu un indicator special.

Pentru început, hai să creăm obiectul `tokenizer`. După cum am mai spus, vom utiliza un model BERT preantrenat, deci vom începe prin a descărca și a stoca în cache tokenizerul asociat:

```python
from transformers import AutoTokenizer

model_checkpoint = "bert-base-cased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
```

Puteți înlocui `model_checkpoint` cu orice alt model preferat din [Hub](https://huggingface.co/models) sau cu un folder local în care ați salvat un model preantreant și un tokenizer. Singura constrângere este că tokenizerul trebuie să fie susținut de biblioteca 🤗 Tokenizers, pentru ca să existe o versiune "rapidă" disponibilă. Puteți vedea toate arhitecturile care vin cu o versiune rapidă în [acest tabel mare] (https://huggingface.co/transformers/#supported-frameworks), iar pentru a verifica dacă obiectul `tokenizer` pe care îl utilizați este într-adevăr susținut de 🤗 Tokenizers, vă puteți uita la atributul său `is_fast`:

```py
tokenizer.is_fast
```

```python out
True
```

Pentru a tokeniza un input pre-tokenizat, putem utiliza `tokenizer` ca de obicei și să adăugăm `is_split_into_words=True`:

```py
inputs = tokenizer(raw_datasets["train"][0]["tokens"], is_split_into_words=True)
inputs.tokens()
```

```python out
['[CLS]', 'EU', 'rejects', 'German', 'call', 'to', 'boycott', 'British', 'la', '##mb', '.', '[SEP]']
```

După cum putem vedea, tokenizerul a adăugat tokenii speciali utilizați de model (`[CLS]` la început și `[SEP]` la sfârșit) și a lăsat majoritatea cuvintelor neatinse. Cu toate acestea, cuvântul `lamb` a fost tokenizat în două subcuvinte, `la` și `##mb`. Acest lucru introduce o neconcordanță între inputurile noastre și labeluri: lista de labeluri are doar 9 elemente, în timp ce inputurile noastre au acum 12 tokeni. Să luăm în considerare tokenii speciali este ușor (știm că sunt la început și la sfârșit), dar trebuie să ne asigurăm că aliniem toate labelurile cu cuvintele corespunzătoare.

Din fericire, pentru că folosim un tokenizer rapid, avem acces la superputerile 🤗 Tokenizers, ceea ce înseamnă că putem mapa cu ușurință fiecare token la cuvântul corespunzător (după cum se vede în [Capitolul 6](/course/chapter6/3)):

```py
inputs.word_ids()
```

```python out
[None, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, None]
```

Cu puțină muncă, putem apoi extinde lista de labeluri pentru a se potrivi cu tokenii. Prima regulă pe care o vom aplica este că tokenii special primesc un label `-100`. Acest lucru se datorează faptului că în mod implicit `-100` este un indice care este ignorat în funcția de pierdere pe care o vom utiliza (cross-entropy). Apoi, fiecare token primește același label ca și tokenul care a inițiat cuvântul în care se află, deoarece fac parte din aceeași entitate. Pentru tokenii din interiorul unui cuvânt, dar care nu se află la început, înlocuim `B-` cu `I-` (deoarece tokenul nu începe entitatea):


```python
def align_labels_with_tokens(labels, word_ids):
    new_labels = []
    current_word = None
    for word_id in word_ids:
        if word_id != current_word:
            # Start of a new word!
            current_word = word_id
            label = -100 if word_id is None else labels[word_id]
            new_labels.append(label)
        elif word_id is None:
            # Special token
            new_labels.append(-100)
        else:
            # Same word as previous token
            label = labels[word_id]
            # If the label is B-XXX we change it to I-XXX
            if label % 2 == 1:
                label += 1
            new_labels.append(label)

    return new_labels
```

Hai să îl încercăm cu prima noastră propoziție:

```py
labels = raw_datasets["train"][0]["ner_tags"]
word_ids = inputs.word_ids()
print(labels)
print(align_labels_with_tokens(labels, word_ids))
```

```python out
[3, 0, 7, 0, 0, 0, 7, 0, 0]
[-100, 3, 0, 7, 0, 0, 0, 7, 0, 0, 0, -100]
```

După cum putem vedea, funcția noastră a adăugat `-100` pentru cei doi tokeni speciali de la început și de la sfârșit, și un nou `0` pentru cuvântul nostru care a fost împărțit în doi tokeni.

<Tip>

✏️ ** Rândul tău!** Unii cercetători preferă să atribuie un singur label pe cuvânt și să atribuie `-100` celorlalți subtokeni dintr-un cuvânt dat. Aceasta are loc pentru a evita ca cuvintele lungi care se împart în mai mulți subtokeni să contribuie puternic la pierdere. Modificați funcția anterioară pentru a alinia labelurile cu ID-urile de input urmând această regulă.

</Tip>

Pentru a preprocesa întregul nostru dataset, trebuie să tokenizăm toate inputurile și să aplicăm `align_labels_with_tokens()` pe toate labelurile. Pentru a profita de viteza tokenizerului nostru rapid, este mai bine să tokenizăm multe texte în același timp, așa că vom scrie o funcție care procesează o listă de exemple și vom folosi metoda `Dataset.map()` cu opțiunea `batched=True`. Singurul lucru diferit față de exemplul nostru anterior este că funcția `word_ids()` trebuie să obțină indexul exemplului din care dorim ID-urile cuvintelor atunci când inputurile către tokenizer sunt liste de texte (sau, în cazul nostru, liste de liste de cuvinte), așa că adăugăm și acest lucru:

```py
def tokenize_and_align_labels(examples):
    tokenized_inputs = tokenizer(
        examples["tokens"], truncation=True, is_split_into_words=True
    )
    all_labels = examples["ner_tags"]
    new_labels = []
    for i, labels in enumerate(all_labels):
        word_ids = tokenized_inputs.word_ids(i)
        new_labels.append(align_labels_with_tokens(labels, word_ids))

    tokenized_inputs["labels"] = new_labels
    return tokenized_inputs
```

Rețineți că nu am completat încă inputurile; vom face acest lucru mai târziu, atunci când vom crea batchurile cu un data collator.

Acum putem aplica toate aceste preprocesări dintr-o dată celorlalte fracțiuni ale datasetului nostru:

```py
tokenized_datasets = raw_datasets.map(
    tokenize_and_align_labels,
    batched=True,
    remove_columns=raw_datasets["train"].column_names,
)
```

Am făcut partea cea mai grea! Acum, că datele au fost preprocesate, antrenarea efectivă va semăna mult cu ceea ce am făcut în [Capitolul 3](/course/chapter3).

{#if fw === 'pt'}

## Fine-tuningul modelului cu `Trainer` API[[fine-tuning-the-model-with-the-trainer-api]]

Codul real care utilizează `Trainer` va fi același ca înainte; singurele modificări sunt modul în care datele sunt collated într-un batch și metric computation function.

{:else}

## Fine-tuningul modelului cu Keras[[fine-tuning-the-model-with-keras]]

Codul real care utilizează `Keras` va fi același ca înainte; singurele modificări sunt modul în care datele sunt collated într-un batch și metric computation function.

{/if}


### Data collation[[data-collation]]

Nu putem folosi doar un `DataCollatorWithPadding` ca în [Capitolul 3](/course/chapter3) deoarece acesta doar adaugă padding inputurilor(input IDs, attention mask, and token type IDs). Aici labelurile noastre ar trebui să fie padded exact în același mod ca și inputurile, astfel încât să rămână de aceeași dimensiune, folosind `-100` ca valoare, astfel încât predicțiile corespunzătoare să fie ignorate în calculul pierderilor.

Toate acestea sunt realizate de un [`DataCollatorForTokenClassification`](https://huggingface.co/transformers/main_classes/data_collator.html#datacollatorfortokenclassification). La fel ca `DataCollatorWithPadding`, acesta ia `tokenizer`-ul folosit pentru preprocesarea inputurilor:

{#if fw === 'pt'}

```py
from transformers import DataCollatorForTokenClassification

data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer)
```

{:else}

```py
from transformers import DataCollatorForTokenClassification

data_collator = DataCollatorForTokenClassification(
    tokenizer=tokenizer, return_tensors="tf"
)
```

{/if}

Pentru a testa acest lucru pe câteva sampleuri, îl putem apela doar pe o listă de exemple din setul nostru de antrenat tokenizat:

```py
batch = data_collator([tokenized_datasets["train"][i] for i in range(2)])
batch["labels"]
```

```python out
tensor([[-100,    3,    0,    7,    0,    0,    0,    7,    0,    0,    0, -100],
        [-100,    1,    2, -100, -100, -100, -100, -100, -100, -100, -100, -100]])
```

Hai să comparăm acest lucru cu labelurile pentru primul și al doilea element din datasetul nostru:

```py
for i in range(2):
    print(tokenized_datasets["train"][i]["labels"])
```

```python out
[-100, 3, 0, 7, 0, 0, 0, 7, 0, 0, 0, -100]
[-100, 1, 2, -100]
```

{#if fw === 'pt'}

După cum se poate observa, al doilea set de labeluri a fost padded la lungimea primului folosind `-100`.

{:else}

Data collatorul nostru de date este gata de utilizare! Acum să îl folosim pentru a crea un `tf.data.Dataset` cu metoda `to_tf_dataset()`. De asemenea, puteți utiliza `model.prepare_tf_dataset()` pentru a face acest lucru cu un pic mai puțin cod repetitiv - veți vedea acest lucru în unele dintre celelalte secțiuni ale acestui capitol.

```py
tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
    columns=["attention_mask", "input_ids", "labels", "token_type_ids"],
    collate_fn=data_collator,
    shuffle=True,
    batch_size=16,
)

tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset(
    columns=["attention_mask", "input_ids", "labels", "token_type_ids"],
    collate_fn=data_collator,
    shuffle=False,
    batch_size=16,
)
```


Următoarea oprire: modelul în sine.

{/if}

{#if fw === 'tf'}

### Definirea modelului[[defining-the-model]]

Deoarece lucrăm la o problemă de clasificare a tokenilor, vom utiliza clasa `TFAutoModelForTokenClassification`. Principalul lucru de care trebuie să ne amintim atunci când definim acest model este să transmitem informații privind numărul de labeluri pe care le avem. Cel mai simplu mod de a face acest lucru este să transmiteți acest număr cu argumentul `num_labels`, dar dacă dorim un inference widget frumos care să funcționeze ca cel pe care l-am văzut la începutul acestei secțiuni, este mai bine să setați în schimb corespondențele corecte ale labelurilor.

Acestea ar trebui să fie stabilite de două dicționare, `id2label` și `label2id`, care conțin corespondența de la ID la label și viceversa:

```py
id2label = {i: label for i, label in enumerate(label_names)}
label2id = {v: k for k, v in id2label.items()}
``` 

Acum putem să le transmitem metodei `TFAutoModelForTokenClassification.from_pretrained()`, iar acestea vor fi setate în configurația modelului, apoi salvate corespunzător și încărcate în Hub:

```py
from transformers import TFAutoModelForTokenClassification

model = TFAutoModelForTokenClassification.from_pretrained(
    model_checkpoint,
    id2label=id2label,
    label2id=label2id,
)
```

La fel ca atunci când am definit `TFAutoModelForSequenceClassification` în [Capitolul 3](/course/chapter3), crearea modelului emite un avertisment că unele weights nu au fost utilizate (cele din headul de preantrenare) și că alte weights sunt inițializate aleatoriu (cele din headul de clasificare a tokenilor noi) și că acest model ar trebui să fie antrenat. Vom face acest lucru într-un minut, dar mai întâi să verificăm de două ori că modelul nostru are numărul corect de labeluri:

```python
model.config.num_labels
```

```python out
9
```

<Tip warning={true}>

⚠️ Dacă aveți un model cu un număr greșit de labeluri, veți primi o eroare obscură atunci când apelați `model.fit()` mai târziu. Acest lucru poate fi enervant pentru debbuging, așa că asigurați-vă că faceți această verificare pentru a confirma că aveți numărul așteptat de labeluri.

</Tip>

### Fine-tuningul modelului[[fine-tuning-the-model]]

Acum suntem gata să ne antrenăm modelul! Totuși, mai avem doar câteva lucruri de făcut mai întâi: ar trebui să ne conectăm la Hugging Face și să definim hiperparametrii noștri de antrenare. Dacă lucrați într-un notebook, există o funcție convenabilă pentru a vă ajuta cu acest lucru:

```python
from huggingface_hub import notebook_login

notebook_login()
```

Aceasta va afișa un widget în care puteți introduce datele tale de autentificare Hugging Face.

Dacă nu lucrați într-un notebook, tastați următoarea linie în terminal:

```bash
huggingface-cli login
```

După autentificare, putem pregăti tot ce avem nevoie pentru a compila modelul nostru. 🤗 Transformers oferă o funcție convenabilă `create_optimizer()` care vă va oferi un optimizator `AdamW` cu setări adecvate pentru weight decay și learning rate decay, ambele îmbunătățind performanța modelului vsotru în comparație cu optimizatorul `Adam` încorporat:

```python
from transformers import create_optimizer
import tensorflow as tf

# Antrenarea în mixed-precision float16
# Comentați această linie dacă utilizați un GPU care nu va beneficia de acest lucru
tf.keras.mixed_precision.set_global_policy("mixed_float16")

# Numărul de etape de antrenare este numărul de sampleuri din dataset, împărțit la dimensiunea batch-ului, apoi multiplicat
# cu numărul total de epoci. Rețineți că tf_train_dataset de aici este un batched tf.data.Dataset,
# nu este originalul Hugging Face Dataset, deci len() este deja num_samples // batch_size.
num_epochs = 3
num_train_steps = len(tf_train_dataset) * num_epochs

optimizer, schedule = create_optimizer(
    init_lr=2e-5,
    num_warmup_steps=0,
    num_train_steps=num_train_steps,
    weight_decay_rate=0.01,
)
model.compile(optimizer=optimizer)
```

Rețineți, de asemenea, că nu furnizăm un argument `loss` la `compile()`. Acest lucru se datorează faptului că modelele pot calcula de fapt pierderea intern - dacă compilați fără o pierdere și furnizați labelurile în dicționarul de intrare (așa cum facem în dataseturile noastre), atunci modelul se va antrena folosind acea pierdere internă, care va fi adecvată pentru sarcina și tipul de model pe care le-ați ales.

În continuare, definim un `PushToHubCallback` pentru a încărca modelul nostru în Hub în timpul antrenării și pentru a potrivi modelul cu acel callback:

```python
from transformers.keras_callbacks import PushToHubCallback

callback = PushToHubCallback(output_dir="bert-finetuned-ner", tokenizer=tokenizer)

model.fit(
    tf_train_dataset,
    validation_data=tf_eval_dataset,
    callbacks=[callback],
    epochs=num_epochs,
)
```

Puteți specifica numele complet al repositoriului către care doriți să efectuați push cu argumentul `hub_model_id` (în special, va trebui să utilizați acest argument pentru a efectua push către o organizație). De exemplu, atunci când am trimis modelul către organizația [`huggingface-course`](https://huggingface.co/huggingface-course), am adăugat `hub_model_id="huggingface-course/bert-finetuned-ner"`. În mod implicit, repositoriul utilizat va fi în namespace-ul denumit după directory output pe care l-ați stabilit, de exemplu `"cool_huggingface_user/bert-finetuned-ner"`.

<Tip>

💡 Dacă directory output pe care îl utilizați există deja, acesta trebuie să fie o clonă locală a repositoriului către care doriți să faceți push. Dacă nu este, veți primi o eroare atunci când apelați `model.fit()` și va trebui să setați un nume nou.

</Tip>

Rețineți că, în timpul antrenamentului, de fiecare dată când modelul este salvat (aici, la fiecare epocă), acesta este încărcat pe Hub în fundal. În acest fel, veți putea să reluați formarea pe o altă mașină, dacă este necesar.

În acest stadiu, puteți utiliza inference widget de pe Model Hub pentru a testa modelul vostru și pentru a-l partaja cu prietenii. Ați făcut fine-tune cu succes unui model pentru o sarcină de clasificare a tokenilor - felicitări! Dar cât de bun este modelul nostru, de fapt? Ar trebui să evaluăm anumiți parametri pentru a afla.

{/if}


### Metrici[[metrics]]

{#if fw === 'pt'}

Pentru ca `Trainer` să calculeze o metrică în fiecare epocă, va trebui să definim o funcție `compute_metrics()` care primește matricele de predicții și labelurile și returnează un dicționar cu numele și valorile metricilor.

Frameworkul tradițional utilizat pentru a evalua predicția clasificării a tokenilor este [*seqeval*](https://github.com/chakki-works/seqeval). Pentru a utiliza această metrică, trebuie mai întâi să instalăm biblioteca *seqeval*:

```py
!pip install seqeval
```

Apoi îl putem încărca prin intermediul funcției `evaluate.load()` așa cum am făcut în [Capitolul 3](/course/chapter3):

{:else}

Frameworkul tradițional utilizat pentru a evalua predicția clasificării tokenilor este [*seqeval*](https://github.com/chakki-works/seqeval). Pentru a utiliza această metrică, trebuie mai întâi să instalăm biblioteca *seqeval*:

```py
!pip install seqeval
```

Apoi îl putem încărca prin intermediul funcției `evaluate.load()` așa cum am făcut în [Capitolul 3](/course/chapter3):

{/if}

```py
import evaluate

metric = evaluate.load("seqeval")
```

Această metrică nu se comportă ca precizia standard: de fapt, va lua listele de labeluri ca șiruri de caractere, nu ca numere întregi, deci va trebui să decodificăm complet predicțiile și labelurile înainte de a le trece în metrice. Să vedem cum funcționează. În primul rând, vom obține labelurile pentru primul nostru exemplu de antrenare:

```py
labels = raw_datasets["train"][0]["ner_tags"]
labels = [label_names[i] for i in labels]
labels
```

```python out
['B-ORG', 'O', 'B-MISC', 'O', 'O', 'O', 'B-MISC', 'O', 'O']
```

Putem apoi crea predicții false pentru acestea prin simpla schimbare a valorii la indexul 2:

```py
predictions = labels.copy()
predictions[2] = "O"
metric.compute(predictions=[predictions], references=[labels])
```

Rețineți că metrica ia o listă de predicții (nu doar una) și o listă de labels. Iată rezultatul:

```python out
{'MISC': {'precision': 1.0, 'recall': 0.5, 'f1': 0.67, 'number': 2},
 'ORG': {'precision': 1.0, 'recall': 1.0, 'f1': 1.0, 'number': 1},
 'overall_precision': 1.0,
 'overall_recall': 0.67,
 'overall_f1': 0.8,
 'overall_accuracy': 0.89}
```

{#if fw === 'pt'}

Acesta trimite înapoi o mulțime de informații! Noi obținem precizia, recallul și scorul F1 pentru fiecare entitate în parte, precum și scorul general. Pentru calculul nostru metric, vom păstra doar scorul global, dar nu ezitați să modificați funcția `compute_metrics()` pentru a returna toate metricile pe care doriți să le raportați.

Această funcție `compute_metrics()` ia mai întâi argmaxul logiturilor pentru a le converti în predicții (ca de obicei, logiturile și probabilitățile sunt în aceeași ordine, deci nu trebuie să aplicăm softmaxul). Apoi trebuie să convertim atât labelurile, cât și predicțiile din numere întregi în șiruri de caractere. Eliminăm toate valorile în care labelul este `-100`, apoi transmitem rezultatele metodei `metric.compute()`:

```py
import numpy as np


def compute_metrics(eval_preds):
    logits, labels = eval_preds
    predictions = np.argmax(logits, axis=-1)

    # Remove ignored index (special tokens) and convert to labels
    true_labels = [[label_names[l] for l in label if l != -100] for label in labels]
    true_predictions = [
        [label_names[p] for (p, l) in zip(prediction, label) if l != -100]
        for prediction, label in zip(predictions, labels)
    ]
    all_metrics = metric.compute(predictions=true_predictions, references=true_labels)
    return {
        "precision": all_metrics["overall_precision"],
        "recall": all_metrics["overall_recall"],
        "f1": all_metrics["overall_f1"],
        "accuracy": all_metrics["overall_accuracy"],
    }
```

Acum că acest lucru este făcut, suntem aproape gata să definim `Trainer`-ul nostru. Avem nevoie doar de un `model` pentru a face fine-tune!

{:else}

Aceasta trimite înapoi o mulțime de informații! Obținem precizia, recallul și scorul F1 pentru fiecare entitate în parte, precum și în ansamblu. Acum să vedem ce se întâmplă dacă încercăm să folosim predicțiile modelului nostru real pentru a calcula niște scoruri reale.

TensorFlow nu apreciază concatenarea predicțiilor noastre, deoarece acestea au lungimi de secvențe variabile. Aceasta înseamnă că nu putem folosi pur și simplu `model.predict()` - dar asta nu ne va opri. Vom obține unele predicții pe rând și le vom concatena într-o singură listă mare și lungă, eliminând simbolurile `-100` care indică mascarea/paddingul, apoi vom calcula metrici pe lista de la sfârșit:

```py
import numpy as np

all_predictions = []
all_labels = []
for batch in tf_eval_dataset:
    logits = model.predict_on_batch(batch)["logits"]
    labels = batch["labels"]
    predictions = np.argmax(logits, axis=-1)
    for prediction, label in zip(predictions, labels):
        for predicted_idx, label_idx in zip(prediction, label):
            if label_idx == -100:
                continue
            all_predictions.append(label_names[predicted_idx])
            all_labels.append(label_names[label_idx])
metric.compute(predictions=[all_predictions], references=[all_labels])
```


```python out
{'LOC': {'precision': 0.91, 'recall': 0.92, 'f1': 0.91, 'number': 1668},
 'MISC': {'precision': 0.70, 'recall': 0.79, 'f1': 0.74, 'number': 702},
 'ORG': {'precision': 0.85, 'recall': 0.90, 'f1': 0.88, 'number': 1661},
 'PER': {'precision': 0.95, 'recall': 0.95, 'f1': 0.95, 'number': 1617},
 'overall_precision': 0.87,
 'overall_recall': 0.91,
 'overall_f1': 0.89,
 'overall_accuracy': 0.97}
```

Cum s-a descurcat modelul tău, comparativ cu al nostru? Dacă ați obținut cifre similare, antrenarea a fost un succes!

{/if}

{#if fw === 'pt'}

### Definirea modelului[[defining-the-model]]

Deoarece lucrăm la o problemă de clasificare a tokenilor, vom utiliza clasa `AutoModelForTokenClassification`. Principalul lucru de care trebuie să ne amintim atunci când definim acest model este să transmitem informații privind numărul de labeluri pe care le avem. Cel mai simplu mod de a face acest lucru este să transmiteți acest număr cu argumentul `num_labels`, dar dacă dorim un inferencea widget frumos care să funcționeze ca cel pe care l-am văzut la începutul acestei secțiuni, este mai bine să setați în schimb corespondențele corecte ale labelurilor.

Acestea ar trebui să fie stabilite de două dicționare, `id2label` și `label2id`, care conțin corespondențele de la ID la labeluri și viceversa:

```py
id2label = {i: label for i, label in enumerate(label_names)}
label2id = {v: k for k, v in id2label.items()}
```

Acum putem doar să le transmitem metodei `AutoModelForTokenClassification.from_pretrained()`, iar acestea vor fi setate în configurația modelului și apoi salvate și încărcate corespunzător în Hub:

```py
from transformers import AutoModelForTokenClassification

model = AutoModelForTokenClassification.from_pretrained(
    model_checkpoint,
    id2label=id2label,
    label2id=label2id,
)
```

La fel ca atunci când am definit `AutoModelForSequenceClassification` în [Capitolul 3](/course/chapter3), crearea modelului emite un avertisment că unele weights nu au fost utilizate (cele din headul de antrenare) și alte weights sunt inițializate aleatoriu (cele din headul de clasificare a tokenilor noi) și că acest model ar trebui să fie format. Vom face acest lucru într-un minut, dar mai întâi să verificăm de două ori că modelul nostru are numărul corect de labeluri:

```python
model.config.num_labels
```

```python out
9
```

<Tip warning={true}>

⚠️ Dacă aveți un model cu un număr greșit de labeluri, veți primi o eroare obscură atunci când apelați metoda `Trainer.train()` mai târziu (ceva de genul "CUDA error: device-side assert triggered"). Aceasta este cauza numărul unu a erorilor raportate de utilizatori pentru astfel de erori, așa că asigurați-vă că faceți această verificare pentru a confirma că aveți numărul de labeluri așteptat.

</Tip>

### Fine-tuningul modelului[[fine-tuning-the-model]]

Acum suntem gata să ne antrenăm modelul! Trebuie doar să facem ultimele două lucruri înainte de a defini modelul nostru `Trainer`: să ne conectăm la Hugging Face și să definim argumentele de antrenare. Dacă lucrați într-un notebook, există o funcție convenabilă pentru a vă ajuta cu acest lucru:

```python
from huggingface_hub import notebook_login

notebook_login()
```

Aceasta va afișa un widget în care puteți introduce datele tale de autentificare Hugging Face.

Dacă nu lucrați într-un notebook, tastați următoarea linie în terminal:

```bash
huggingface-cli login
```

Odată făcut acest lucru, putem defini`TrainingArguments`:

```python
from transformers import TrainingArguments

args = TrainingArguments(
    "bert-finetuned-ner",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
    push_to_hub=True,
)
```

Ați mai văzut cele mai multe dintre acestea: stabilim niște hiperparametri (cum ar fi learning rate, numărul de epoci pentru care să ne antrenăm și weights decay) și specificăm `push_to_hub=True` pentru a indica faptul că dorim să salvăm modelul și să îl evaluăm la sfârșitul fiecărei epoci și că dorim să încărcăm rezultatele noastre în Model Hub. 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 trimis modelul către organizația [`huggingface-course`](https://huggingface.co/huggingface-course), am adăugat `hub_model_id="huggingface-course/bert-finetuned-ner"` la `TrainingArguments`. În mod implicit, repositoriul utilizat va fi în namespaceul tău și denumit după output directory-ul pe care l-ați setat, deci în cazul nostru va fi `"sgugger/bert-finetuned-ner"`.

<Tip>

💡 Dacă output directory-ul pe care îl utilizați există deja, acesta trebuie să fie o clonă locală a repositoriul către care doriți să faceți push. Dacă nu este așa, veți primi o eroare la definirea `Trainer` și va trebui să setați un nume nou.

</Tip>

În final, transmitem totul către `Trainer` și lansăm antrenarea:

```python
from transformers import Trainer

trainer = Trainer(
    model=model,
    args=args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    compute_metrics=compute_metrics,
    tokenizer=tokenizer,
)
trainer.train()
```

Rețineți că, în timpul antrenării, de fiecare dată când modelul este salvat (aici, la fiecare epocă), acesta este încărcat pe Hub în fundal. În acest fel, veți putea să reluați antrenareape o altă mașină, dacă este necesar.

Odată ce antrenamentul este complet, folosim metoda `push_to_hub()` pentru a ne asigura că încărcăm cea mai recentă versiune a modelului:

```py
trainer.push_to_hub(commit_message="Training complete")
```

Această comandă returnează URL-ul comitului pe care tocmai l-ai făcut, dacă doriți să o inspectați:

```python out
'https://huggingface.co/sgugger/bert-finetuned-ner/commit/26ab21e5b1568f9afeccdaed2d8715f571d786ed'
```

De asemenea, `Trainer` redactează un model card cu toate rezultatele evaluării și o încarcă. În acest stadiu, puteți utiliza inference widgetul de pe Model Hub pentru a testa modelul și a-l partaja cu prietenii. Ați făcut fine-tune cu succes un model pentru o sarcină de clasificare a tokenilor - felicitări!

Dacă doriți să vă scufundați puțin mai profund în bucla de antrenare, vă vom arăta acum cum să faceți același lucru utilizând 🤗 Accelerate.

## Un training loop personalizat[[a-custom-training-loop]]

Să aruncăm acum o privire la training loopul complet, astfel încât să puteți personaliza cu ușurință părțile de care aveți nevoie. Va semăna foarte mult cu ceea ce am făcut în [Capitolul 3](/course/chapter3/4), cu câteva modificări pentru evaluare.

### Pregătiți totul pentru antrenare[[preparing-everything-for-training]]

Mai întâi trebuie să construim `DataLoader`s din dataseturile noastre. Vom reutiliza `data_collator` ca un `collate_fn` și vom amesteca setul de antrenare, dar nu și setul de validare:

```py
from torch.utils.data import DataLoader

train_dataloader = DataLoader(
    tokenized_datasets["train"],
    shuffle=True,
    collate_fn=data_collator,
    batch_size=8,
)
eval_dataloader = DataLoader(
    tokenized_datasets["validation"], collate_fn=data_collator, batch_size=8
)
```

În continuare, reinstanțiem modelul, pentru a ne asigura că nu continuăm fine-tuningul de dinainte, ci pornim din nou de la modelul preantrenat BERT:

```py
model = AutoModelForTokenClassification.from_pretrained(
    model_checkpoint,
    id2label=id2label,
    label2id=label2id,
)
```

Atunci vom avea nevoie de un optimizator. Vom folosi clasicul `AdamW`, care este ca `Adam`, dar cu o corecție în modul în care se aplică weight decay-ul:

```py
from torch.optim import AdamW

optimizer = AdamW(model.parameters(), lr=2e-5)
```

Odată ce avem toate aceste obiecte, le putem trimite la metoda `accelerator.prepare()`:

```py
from accelerate import Accelerator

accelerator = Accelerator()
model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
    model, optimizer, train_dataloader, eval_dataloader
)
```

<Tip>

🚨 Dacă vă antrenați pe un TPU, va trebui să mutați tot codul începând de la celula de mai sus într-o funcție de antrenament dedicată. Consultați [Capitolul 3](/course/chapter3) pentru mai multe detalii.

</Tip>

Acum că am trimis `train_dataloader` la `accelerator.prepare()`, putem utiliza lungimea acestuia pentru a calcula numărul de pași de antrenare. Rețineți că ar trebui să facem întotdeauna acest lucru după ce pregătim dataloaderul, deoarece această metodă îi va modifica lungimea. Utilizăm un classic liner schedule de la rata de învățare la 0:

```py
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,
)
```

În cele din urmă, pentru a trimite modelul nostru către Hub, va trebui să creăm un obiect `Repository` într-un folder de lucru. În primul rând, conectați-vă la Hugging Face, dacă nu sunteți deja conectat. Vom determina numele repositoriului pornind de la ID-ul modelului pe care dorim să îl dăm modelului nostru (nu ezitați să înlocuiți `repo_name` cu propriul nume; trebuie doar să conțină numele vostru de utilizator, ceea ce face funcția `get_full_repo_name()`):

```py
from huggingface_hub import Repository, get_full_repo_name

model_name = "bert-finetuned-ner-accelerate"
repo_name = get_full_repo_name(model_name)
repo_name
```

```python out
'sgugger/bert-finetuned-ner-accelerate'
```

Apoi putem clona acel repositoriu într-un folder local. Dacă există deja, acest folder local ar trebui să fie o clonă existentă a repositoriului cu care lucrăm:

```py
output_dir = "bert-finetuned-ner-accelerate"
repo = Repository(output_dir, clone_from=repo_name)
```

Acum putem încărca orice salvăm în `output_dir` prin apelarea metodei `repo.push_to_hub()`. Acest lucru ne va ajuta să încărcăm modelele intermediare la sfârșitul fiecărei epoci.

### Loopul de antrenare[[training-loop]]

Acum suntem pregătiți să scriem bucla de antrenare completă. Pentru a simplifica partea sa de evaluare, definim funcția `postprocess()` care preia predicțiile și labelurile și le convertește în liste de șiruri de caractere, așa cum se așteaptă obiectul nostru `metric`:

```py
def postprocess(predictions, labels):
    predictions = predictions.detach().cpu().clone().numpy()
    labels = labels.detach().cpu().clone().numpy()

    # Remove ignored index (special tokens) and convert to labels
    true_labels = [[label_names[l] for l in label if l != -100] for label in labels]
    true_predictions = [
        [label_names[p] for (p, l) in zip(prediction, label) if l != -100]
        for prediction, label in zip(predictions, labels)
    ]
    return true_labels, true_predictions
```

Apoi putem scrie bucla de antrenare. După definirea unei bare de progres pentru a urmări modul în care decurge antrenarea, bucla are trei părți:

- Antrenarea în sine, care este iterația clasică peste `train_dataloader`, trecerea înainte prin model, apoi trecerea înapoi și pasul optimizatorului.
- Evaluarea, în care există o noutate după obținerea outputurilor modelului nostru pe un batch: din moment ce două procese pot ar fi putut face padding inputurilor și labelurile la forme diferite, trebuie să folosim `accelerator.pad_across_processes()` pentru a face predicțiile și labelurile să aibă aceeași formă înainte de a apela metoda `gather()`. Dacă nu facem acest lucru, evaluarea va da eroare sau se va bloca pentru totdeauna. Apoi trimitem rezultatele la `metric.add_batch()` și apelăm `metric.compute()` odată ce bucla de evaluare s-a încheiat.
- Salvarea și încărcarea, unde mai întâi salvăm modelul și tokenizerul, apoi apelăm `repo.push_to_hub()`. Observați că folosim argumentul `blocking=False` pentru a spune bibliotecii 🤗 Hub să efectueze push-ul într-un proces asincron. În acest fel, antrenamentul continuă normal, iar această instrucțiune (lungă) este executată în fundal.

Iată codul complet pentru bucla de antrenare:

```py
from tqdm.auto import tqdm
import torch

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()
    for batch in eval_dataloader:
        with torch.no_grad():
            outputs = model(**batch)

        predictions = outputs.logits.argmax(dim=-1)
        labels = batch["labels"]

        # Necessary to pad predictions and labels for being gathered
        predictions = accelerator.pad_across_processes(predictions, dim=1, pad_index=-100)
        labels = accelerator.pad_across_processes(labels, dim=1, pad_index=-100)

        predictions_gathered = accelerator.gather(predictions)
        labels_gathered = accelerator.gather(labels)

        true_predictions, true_labels = postprocess(predictions_gathered, labels_gathered)
        metric.add_batch(predictions=true_predictions, references=true_labels)

    results = metric.compute()
    print(
        f"epoch {epoch}:",
        {
            key: results[f"overall_{key}"]
            for key in ["precision", "recall", "f1", "accuracy"]
        },
    )

    # 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
        )
```

În cazul în care este prima dată când vedeți un model salvat cu 🤗 Accelerate, să ne oprim puțin pentru a inspecta cele trei linii de cod care îl însoțesc:

```py
accelerator.wait_for_everyone()
unwrapped_model = accelerator.unwrap_model(model)
unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
```

Prima linie se explică de la sine: aceasta spune tuturor proceselor să aștepte până când toată lumea se află în etapa respectivă înainte de a continua. Acest lucru are rolul de a ne asigura că avem același model în fiecare proces înainte de a salva. Apoi luăm `unwrapped_model`, care este modelul de bază pe care l-am definit. Metoda `accelerator.prepare()` modifică modelul pentru a funcționa în antrenarea distribuită, deci nu va mai avea metoda `save_pretrained()`; metoda `accelerator.unwrap_model()` anulează acest pas. În cele din urmă, apelăm metoda `save_pretrained()`, dar îi spunem să folosească metoda `accelerator.save()` în loc de `torch.save()`.

Odată făcut acest lucru, ar trebui să aveți un model care produce rezultate destul de asemănătoare cu cel antrenat cu `Trainer`. Puteți verifica modelul pe care l-am antrenat folosind acest cod la [*huggingface-course/bert-finetuned-ner-accelerate*] (https://huggingface.co/huggingface-course/bert-finetuned-ner-accelerate). Și dacă doriți să testați orice modificări ale buclei de antrenare, le puteți implementa direct prin editarea codului prezentat mai sus!


{/if}

## Utilizarea model fine-tuned[[using-the-fine-tuned-model]]

V-am arătat deja cum puteți utiliza modelul pe care l-am ajustat pe Model Hub cu inference widget. Pentru a-l utiliza la nivel local într-un `pipeline`, trebuie doar să specificați identificatorul de model corespunzător:

```py
from transformers import pipeline

# Replace this with your own checkpoint
model_checkpoint = "huggingface-course/bert-finetuned-ner"
token_classifier = pipeline(
    "token-classification", model=model_checkpoint, aggregation_strategy="simple"
)
token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
```

```python out
[{'entity_group': 'PER', 'score': 0.9988506, 'word': 'Sylvain', 'start': 11, 'end': 18},
 {'entity_group': 'ORG', 'score': 0.9647625, 'word': 'Hugging Face', 'start': 33, 'end': 45},
 {'entity_group': 'LOC', 'score': 0.9986118, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
```

Grozav! Modelul nostru funcționează la fel de bine ca cel implicit pentru aceast pipeline!

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter7/2.mdx" />

### Partea 1 este completă!
https://huggingface.co/learn/course/ro/chapter4/5.md

# Partea 1 este completă!

<CourseFloatingBanner
    chapter={4}
    classNames="absolute z-10 right-0 top-0"
/>

Acesta este sfârșitul primei părți a cursului! Partea 2 va fi lansată pe data de 15 noiembrie, cu un mare eveniment comunitar, mai multe informații găsiți [aici](https://huggingface.co/blog/course-launch-event).

Acum, sunteți capabili să faceți fine-tune unui model preantrenat pe o problemă de clasificare a textului (o singură sau perechi de propoziții) și să încarci rezultatele la Model Hub. Să fii sigur că ai învățat această primă secțiune, ar trebui să faci exact acest lucru pe o problemă care ți se pare interesantă (și nu neapărat în limba engleză dacă vorbiți altă limbă)! Puteți găsi ajutor în [forumurile Hugging Face](https://discuss.huggingface.co/) și să împărtășiți proiectul dvs. pe [acest subiect](https://discuss.huggingface.co/t/share-your-projects/6803) odată ce ați terminat.

Ne așteptăm cu nerăbdare să vedem ceea ce veți construi cu aceste cunoștințe :)!

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter4/5.mdx" />

### Platforma Hugging Face Hub[[the-hugging-face-hub]]
https://huggingface.co/learn/course/ro/chapter4/1.md

# Platforma Hugging Face Hub[[the-hugging-face-hub]]

<CourseFloatingBanner
    chapter={4}
    classNames="absolute z-10 right-0 top-0"
/>

Platforma [Hugging Face Hub](https://huggingface.co/) –- site-ul nostru principal –- reprezintă o platformă care permite oricărui utilizator să descopere, utilizeze și contribuie la noi modele și dataseturi. Acesta găzduiește o varietate vastă de modele, cu peste 10.000 disponibile public. Ne vom concentra discuția asupra modelelor în acest capitol, iar pentru dataseturi vom vorbi în Capitolul 5.

Modelele din Hub nu sunt limitate la 🤗 Transformers sau chiar și NLP. Există și alte modele, cum ar fi cele de la [Flair](https://github.com/flairNLP/flair) și [AllenNLP](https://github.com/allenai/allennlp) pentru NLP, [Asteroid](https://github.com/asteroid-team/asteroid) și [pyannote](https://github.com/pyannote/pyannote-audio) pentru speech, precum și cele de la [timm](https://github.com/rwightman/pytorch-image-models) pentru vision, doar să menționăm câteva.

Fiecare dintre aceste modele este găzduit ca un repository Git, ceea ce permite crearea versiunilor și reproducerea acestuia. Oferirea modelului pe Hub înseamnă deschiderea sa comunității, făcându-l accesibil oricărui utilizator care dorește să îl utilizeze cu ușurință, eliminând astfel nevoia de a-și antrena propriul model și simplificând utilizarea acestora. 

În plus, oferirea unui model pe Hub face deploy automat la un Inference API pentru acel model. Orice membru al comunității poate testa direct pe pagina modelului cu inputuri personalizate și widget-uri corespunzătoare.

Cel mai bun lucru este că oferirea și utilizarea oricărui model public pe Hub este complet gratuit! Existează și [planuri plătite](https://huggingface.co/pricing) dacă doriți să oferiți modele în privat.

Video-ul de mai jos arată modul în care puteți naviga prin Hub.

<Youtube id="XvSGPZFEjDY"/>

Crearea unui cont huggingface.co este necesară pentru a continua această parte, deoarece vom crea și gestiona repositorii pe Platforma Hugging Face Hub: [creați un cont](https://huggingface.co/join)

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter4/1.mdx" />

### Partajarea modelelor preantrenate[[sharing-pretrained-models]]
https://huggingface.co/learn/course/ro/chapter4/3.md

# Partajarea modelelor preantrenate[[sharing-pretrained-models]]

{#if fw === 'pt'}

<CourseFloatingBanner chapter={4}
  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/chapter4/section3_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter4/section3_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={4}
  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/chapter4/section3_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter4/section3_tf.ipynb"},
]} />

{/if}

În următoarele etape, vom explora modalitățile cele mai ușoare de a partaja modelele preantrenate pe 🤗 Hub. Există tooluri și utilities disponibile care fac posibilă partajarea și actualizarea modelelor direct pe Hub, despre care vom vorbi mai jos.

<Youtube id="9yY3RB_GSPM"/>

Recomandăm tuturor utilizatorilor care antrenează modele să contribuie prin partajarea lor cu comunitatea - partajarea modelelor, chiar și atunci când sunt antrenate pe dateseturi foarte specifice, va ajuta la economisind timpul altor utilizatori și resurselor și va oferi acces la artefacte antrenate folositoare. În schimb, puteți beneficia de munca altora!

Există trei moduri de a crea noi repositorii cu modele:

- Utilizând API-ul `push_to_hub`
- Utilizând biblioteca Python `huggingface_hub`
- Utilizând interfața web

Odată ce aveți creat un repository, puteți încărca fișiere în el folosind git și git-lfs. Vom trece împreună prin crearea model repositories și încărcarea fișierelor în el în următoarele secțiuni.


## Utilizând API-ul `push_to_hub`[[using-the-pushtohub-api]]

{#if fw === 'pt'}

<Youtube id="Zh0FfmVrKX0"/>

{:else}

<Youtube id="pUh5cGmNV8Y"/>

{/if}

Cea mai simplă modalitate de a încărca fișiere pe Hub este utilizând API-ul `push_to_hub`.

Înainte de a continua, aveți nevoie să creați un token de autentificare astfel încât API-ul `huggingface_hub` să știe cine sunteți și care sunt namespaces la care aveți acces de scriere. Asigurați-vă că sunteți într-un environment unde aveți instalat `transformers` (vedeti [Configurarea](/course/chapter0)). Dacă sunteți într-un notebook, puteți folosi următoarea funcție pentru a vă autentifica:

```python
from huggingface_hub import notebook_login

notebook_login()
```

În terminal, puteți rula următoarea comandă:

```bash
huggingface-cli login
```

În ambele cazuri, veți fi rugați să introduceți numele utilizatorului și parola, care sunt aceleași cu cele folosite la autentificare pe Hub. Dacă nu aveți încă un profil pe Hub, vă rugăm să creați unul [aici](https://huggingface.co/join).

Excelent! Acum aveți tokenul de autentificare stocat în folder-ul cache. Vom crea acum niște repositorii!

{#if fw === 'pt'}

Dacă ați lucrat cu API-ul `Trainer` pentru a antrena un model, cea mai ușoară modalitate de a o încărca pe Hub este să setați `push_to_hub=True` când definiți `TrainingArguments`:

```py
from transformers import TrainingArguments

training_args = TrainingArguments(
    "bert-finetuned-mrpc", save_strategy="epoch", push_to_hub=True
)
```

Când chemați `trainer.train()`, `Trainer` va încărca modelul pe Hub de fiecare dată când este salvat (aici, la fiecare epocă) într-un repository namespace-ul tău. Acest repository va fi numit după output directory-ul ales (aici `bert-finetuned-mrpc`), dar puteți alege un alt nume cu `hub_model_id = "un_nume_diferit"`.

Pentru a încărca modelul într-o organizație din care faceți parte, este suficient să introduceți aceasta în `hub_model_id = "organizația_mea/numele_repositoriului"`.

Odată ce antrenarea s-a terminat, trebuie să faceți `trainer.push_to_hub()` pentru a încărca ultima versiune a modelului. Acesta va genera apoi un model card cu toate informațiile relevante, inclusiv metadatele despre hyperparametrii utilizați și rezultatele evaluării! Aici este exemplul unui astfel de model card:

<div class="flex justify-center">
  <img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/model_card.png" alt="Un exemplu de un card auto-generat." width="100%"/>
</div>

{:else}

Dacă utilizați Keras pentru a antrena modelul dumneavoastră, cea mai ușoară modalitate de a încărca-o pe Hub este să transmiteți un `PushToHubCallback` când chemați `model.fit()`:

```py
from transformers import PushToHubCallback

callback = PushToHubCallback(
    "bert-finetuned-mrpc", save_strategy="epoch", tokenizer=tokenizer
)
```

Apoi, adăugați `callbacks=[callback]` în apelul la `model.fit()`. Callback-ul va încărca apoi modelul pe Hub de fiecare dată când se salvează (aici, la fiecare epocă) într-un repository din namespace-ul tău. Acest repository va fi numit după output directory ales (aici `bert-finetuned-mrpc`), dar puteți alege un alt nume cu `hub_model_id = "un_nume_diferit"`.

Pentru a încărca modelul într-o organizație din care faceți parte, este suficient să introduceți aceasta în `hub_model_id = "organizația_mea/numele_repositoriului"`.

{/if}

La un nivel inferior, accesarea Hub-ului se poate face direct pe modele, tokenizers și configurations objects prin metoda `push_to_hub()`. Această metodă se ocupă atât de crearea repositoriului cât si de încărcarea modelului și tokenizerului direct în el. Nu este nevoie de o interacțiune manuală, precum fața de API-ul pe care îl vom vedea mai jos.

Pentru a înțelege cum funcționează, luați în considerare inițializarea unui model și a unui tokenizer:

{#if fw === 'pt'}
```py
from transformers import AutoModelForMaskedLM, AutoTokenizer

checkpoint = "camembert-base"

model = AutoModelForMaskedLM.from_pretrained(checkpoint)
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
```
{:else}
```py
from transformers import TFAutoModelForMaskedLM, AutoTokenizer

checkpoint = "camembert-base"

model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
```
{/if}

Sunteți liberi să faceți orice ați vrea cu acestea - adăugați tokens la tokenizer, antrenați modelul sau faceți fine-tune. Odată ce sunteți mulțumiți de modelul obținut, weighturile și tokenizerul acestuia , puteți folosi metoda `push_to_hub()` disponibilă direct pe obiectul `model`:

```py
model.push_to_hub("dummy-model")
```
Acest lucru va crea un nou repository `dummy-model` în profilul tău și o va popula cu fișierele modelului tău.
Încercați același lucru cu tokenizerul, astfel încât toate fișierele să fie acum disponibile în acest repository:

```py
tokenizer.push_to_hub("dummy-model")
```

Dacă aparțineți unei organizații, specificați doar argumentul `organization` pentru a încărca în namespace-ul acestei organizații:

```py
tokenizer.push_to_hub("dummy-model", organization="huggingface")
```

Dacă doriți să utilizați un anumit token Hugging Face, sunteți liberi să specificați acest lucru metodei `push_to_hub()`:

```py
tokenizer.push_to_hub("dummy-model", organization="huggingface", use_auth_token="<TOKEN>")
```

Acum, mergeți la Model Hub pentru a găsi modelul încărcat: *https://huggingface.co/user-or-organization/dummy-model*.

Apăsați pe tabul "Files and versions", iar acum ar trebui să vedeți fișierele vizibile în următorul screenshot:

{#if fw === 'pt'}
<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/push_to_hub_dummy_model.png" alt="Dummy model containing both the tokenizer and model files." width="80%"/>
</div>
{:else}
<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/push_to_hub_dummy_model_tf.png" alt="Dummy model containing both the tokenizer and model files." width="80%"/>
</div>
{/if}

<Tip>

✏️ **Încercați!** Luați modelul și tokenizerul asociat cu checkpointul `bert-base-cased` și încărcați-l pe un repo în namespace-ul tău folosind metoda `push_to_hub()`. Verificați că repo-ul apare  în pagina dumneavoastră înainte de a-l șterge.

</Tip>

Ați văzut că metoda `push_to_hub()` acceptă mai multe argumente, ceea ce permite încărcarea într-un repository specific sau namespace al unei organizații, sau utilizarea unui token API diferit. Vă recomandăm să vă uitați la specificația metodei disponibilă direct în [documentația 🤗 Transformers](https://huggingface.co/transformers/model_sharing) pentru a înțelge ceea ce este posibil.

Metoda `push_to_hub()` este backed de packageul Python [`huggingface_hub`](https://github.com/huggingface/huggingface_hub), care oferă un API direct la Hub-ul Hugging Face. Acesta este integrat în 🤗 Transformers și în mai multe biblioteci pentru machine learning, ca de exemplu [`allenlp`](https://github.com/allenai/allennlp). Deși ne concentrăm pe integrarea în 🤗 Transformers în acest capitol, integrarea sa în propriul dumneavoastră cod sau bibliotecă este simplu.

Săriți la ultima secțiune pentru a vedea cum să încărcați fișierele în noul repository-ul creat!

## Utilizarea bibliotecii Python `huggingface_hub`[[using-the-huggingfacehub-python-library]]

Biblioteca Python `huggingface_hub` este un package care oferă un set de instrumente pentru model și datasets hubs. Oferă metode simple și clase pentru sarcini comune ca 
obținerea informațiilor despre repositorii pe hub și gestionarea lor. Oferă API-uri simple care lucrează pe lângă git pentru a gestiona conținutul acestor repositorii și integrarea Hub-ului
în proiectele și bibliotecile tale.

Similar cu utilizarea API-ului `push_to_hub`, asta va necesita un token de autentificare salvat în cache. Pentru a face acest lucru, veți avea nevoie să folosiți comanda `login` din CLI, cum am menționat în secțiunea anterioară (din nou, asigurați-vă prefixați această comandă cu simbolul `!` dacă rulați în Google Colab):

```bash
huggingface-cli login
```

Packageul `huggingface_hub` oferă mai multe metode și clase utile pentru noi. În primul rând, există câteva metode pentru crearea, ștergerea și gestionarea repositoriilor:

```python no-format
from huggingface_hub import (
    # Gestionarea utilizatorilor
    login,
    logout,
    whoami,

    # Crearea și gestionarea repositoriilor
    create_repo,
    delete_repo,
    update_repo_visibility,

    # Și câteva metode pentru a obține/schimba informațiile despre conținut
    list_models,
    list_datasets,
    list_metrics,
    list_repo_files,
    upload_file,
    delete_file,
)
```




De asemenea, el oferă clasa foarte puternică `Repository` pentru gestionarea unei repository local. Vom explora aceste metode și clasa în următoarele secțiuni pentru a înțelege cum să le utilizați.

Metoda `create_repo` poate fi utilizată pentru crearea unui nou repository în hub:

```py
from huggingface_hub import create_repo

create_repo("dummy-model")
```

Acesta va crea repositarul `dummy-model` în namespace-ul tău. Dacă doriți, puteți specifica care organizație să aparțină repositoriului folosind argumentul `organization`:

```py
from huggingface_hub import create_repo

create_repo("dummy-model", organization="huggingface")
```

Acesta va crea repositoriul `dummy-model` în spațiul organizației `huggingface`, cu condiția că dumneavoastră să apartineți acestei organizații.
Alte argumente care pot fi utile sunt:

- `private`, pentru a specifica dacă repositarul trebuie să fie vizibil celorlați sau nu.
- `token`, dacă doriți să vă înlocuiți token-ul stocat în cache cu un anumit token.
- `repo_type`, dacă doriți să creați un `dataset` sau `space` în loc de model. Valorile acceptate sunt `"dataset"` și `"space"`.

Odată ce repositoriul este creat, trebuie să adăugați fișierele în el! Săriți la secțiunea următoare pentru a vedea cele trei moduri în care se poate face acest lucru.

## Utilizarea interfeței web[[using-the-web-interface]]

Interfața web oferă instrumente pentru a gestiona repositorii direct în Hub. Cu ajutorul acestei interfețe, puteți crea ușor repositorii, adăuga fișiere (și chiar și fișiere mari!), explora modele, vizualiza diffuri, și multe altele.

Pentru a crea un nou repositoriu, vizitați [huggingface.co/new](https://huggingface.co/new):

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/new_model.png" alt="Pagină care prezintă modelul utilizat pentru crearea unui nou repository." width="80%"/>
</div>

În primul rând, specificați deținătorul repositoriului: acesta puteți fi fie dumneavoastră, fie orice organizație cu care sunteți afiliat. Dacă alegeți o organizație, modelul va fi prezent pe pagina organizației și toți membrii acesteia vor avea posibilitatea să contribuie la repositoriu.

În al doilea rând, introduceți numele modelului dumneavoastră. Acest lucru va fi și denumirea repositoriului. În final, puteți specifica dacă doriți ca modelul dumneavoastră să fie public sau privat. Modelele private nu sunt vizibile ceilorlalți.

După crearea repositoriului, ar trebui să vedeți o pagină ca aceasta:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/empty_model.png" alt="O pagină goală a modelului după crearea unui nou repository." width="80%"/>
</div>

Acesta este locul unde va fi găzduit modelul. Pentru a începe să populați acesta, puteți adăuga un fișier README direct din interfața web.

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/dummy_model.png" alt="Fișierul README care arată capacitățile Markdown." width="80%"/>
</div>

Fișierul README este scris în format Markdown - vă rugăm să fiți creativi cu el! A treia parte a acestui capitol se ocupă de crearea unui model card. Acestea sunt foarte importante pentru a aduce valoare modelului dumneavoastră, deoarece este acolo unde spuneți celorlalți ce poate face.

Dacă vă uitați la secțiunea "Fișiere și versiuni", veți vedea că nu sunt prea multe fișiere acolo încă - doar *README.md* pe care l-ați creat dumneavoastră și *.gitattributes* care urmărește fișierele mari.

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/files.png" alt="Secțiunea 'Fișiere și versiuni' arată doar fișierul .gitattributes și README.md." width="80%"/>
</div>

Vom vedea mai târziu cum să adăugați câteva fișiere noi.

## Încărcarea fișierelor modelului[[uploading-the-model-files]]

Sistemul pentru a gestiona fișierele în Hugging Face Hub se bazează pe git pentru fișierele obișnuite, și pe git-lfs (care se descrifrează ca [Git Large File Storage](https://git-lfs.github.com/)) pentru fișierele mai mari.

În secțiunea următoare, vom discuta despre trei metode diferite de încărcare a fișierelor în Hub: prin `huggingface_hub` și prin comanda git.

### Metoda `upload_file`[[the-uploadfile-approach]]

Prin `upload_file` nu este necesar să instalați git și git-lfs pe sistemul dumneavoastră. Acest lucru încarcă direct fișierele în 🤗 Hub folosind HTTP POST requests. O limitare a acestei metode este că ea nu se ocupă de fișiere care sunt mai mari de 5GB.
Dacă fișierele dumneavoastră sunt mai mari decât 5 GB, vă rugăm să urmați celelalte două metode descrise mai jos.

API-ul poate fi folosit astfel:

```py
from huggingface_hub import upload_file

upload_file(
    "<path_to_file>/config.json",
    path_in_repo="config.json",
    repo_id="<namespace>/dummy-model",
)
```

Acest lucru va încărca fișierul `config.json` disponibil la `<path_to_file>` la rootul repositoriului ca `config.json`, în `dummy-model` repository.
Alte argumente utile pot fi:

- `token`, dacă doriți să înlocuiți tokenul stocat în cache cu un alt token.
- `repo_type`, dacă doriți să încărcați într-un `dataset` sau într-un `space` în locul modelului. Valorile acceptate sunt `"dataset"` și `"space"`.

### Clasa `Repository`[[the-repository-class]]

Clasa `Repository` gestionează un repository local în-tro manieră asemănătoare gitului. Ea abstracționează majoritatea problemelor pe care le întâlniți cu git pentru a oferi toate caracteristicile necesare.

Utilizarea acestei clase necesită ca dumneavoastră să aveți instalat git și git-lfs, așadar asigurați-vă că aveți instalat git-lfs (vezi [aici](https://git-lfs.github.com/) pentru instrucțiunile de instalare) și setat înainte de a începe.

Pentru a vă juca cu repositoriul pe care l-am creat, puteți porni prin inițializarea lui ca un folder local printr-un clonarea repositoriului remote:

```py
from huggingface_hub import Repository

repo = Repository("<path_to_dummy_folder>", clone_from="<namespace>/dummy-model")
```

Acesta a creat folderele `<path_to_dummy_folder>` în folderul de lucru. Acest folder conține doar fișierul `.gitattributes` pentru că acesta este singurul fișier creat atunci când inițializați repositoriul prin `create_repo`.

Acum puteți folosi majoritatea metodelor tradiționale ale gitului:

```py
repo.git_pull()
repo.git_add()
repo.git_commit()
repo.git_push()
repo.git_tag()
```

Și altele! Vă recomandăm să vă uitați la documentația `Repository` disponibilă [aici](https://github.com/huggingface/huggingface_hub/tree/main/src/huggingface_hub#advanced-programmatic-repository-management) pentru o înțelegere generală a tuturor metodelor disponibile.

Pentru moment, avem un model și un tokenizer pe care doriți să îl trimiteți în hub. Am reușit să clonăm repositoriul local, deci putem salva fișierele în acesta.

În primul rând asigurați-vă că copia locală este la zi cu ultimele schimbări:

```py
repo.git_pull()
```

Odată ce am facut asta, salvați fișierele modelului și al tokenizerul:

```py
model.save_pretrained("<path_to_dummy_folder>")
tokenizer.save_pretrained("<path_to_dummy_folder>")
```

Folder-ul `<path_to_dummy_folder>` acum conține toate fișierele modelului dumneavoastră și ale tokenizerul. Vom urma fluxul tradițional de git adăugând fișiere în zona de pregătire, făcând commit și push fișierelor în hub:

```py
repo.git_add()
repo.git_commit("Adăugarea fișierelor modelului și ale tokenizerului")
repo.git_push()
```

Felicitări! Ați reușit să încărcați primele fișiere în hub.

### Folosirea Git[[the-git-based-approach]]

Acesta este abordarea cea mai de bază pentru încărcarea fișierelor: vom face asta cu ajutorul git și git-lfs direct. Cea mai grea parte este abstractizată prin abordări anterioare, dar există câteva excepții cu metoda următoare, așadar ne vom referi la un use-case mai complicat.

Pentru această clasă se cere să aveți instalate git și git-lfs, așadar asigurați-vă că aveți [git-lfs](https://git-lfs.github.com/) instalat (vedeți aici instrucțiunile de instalare) și configurat înainte să începeți.

În primul rând, inițializați git-lfs:

```bash
git lfs install
```

```bash
Updated git hooks.
Git LFS initialized.
```

După ce ați făcut acest lucru, primul pas este să faceți o copie a repositoriului dumneavoastră:

```bash
git clone https://huggingface.co/<namespace>/<your-model-id>
```

Numele meu de utilizator este `lysandre` și am folosit numele modelului `dummy`, așadar comanda mea se termină cu următorul rezultat:

```
git clone https://huggingface.co/lysandre/dummy
```

Acum am un folder denumit *dummy* în folderul meu de lucru. Pot intra în folder cu ajutorul `cd` și pot examina conținutul:

```bash
cd dummy && ls
```

```bash
README.md
```

Dacă ați creat repositoriul dumneavoastră folosind metodă din partea Hugging Face Hub, `create_repo` , acest folder ar trebui să conțină doar un fișier cu numele *.gitattributes*. Dacă ați urmat instrucțiunile din secțiunea anterioară pentru crearea repositoriului folosind interfața web, atunci folderul ar trebui să conțină un singur fișier *README.md* și, de asemenea, un fișier cu numele *.gitattributes*, pe care nu-l puteți vedea.

Adăugarea unui fișier de mărime obișnuită, precum un fișier de configurare, un vocabulary file sau orice alt tip de fișier care au mai puțin de câțiva megabytes se realizează exact așa cum ați face în orice sistem git. Cu toate acestea, pentru a încărca fișiere mai mari, este necesară utilizarea a git-lfs pentru a le trimite spre *huggingface.co*.

Să ne întoarcem la Python pentru a genera un model și tokenizer pe care am dori să le facem commit în repositoriul nostru dummy:

{#if fw === 'pt'}
```py
from transformers import AutoModelForMaskedLM, AutoTokenizer

checkpoint = "camembert-base"

model = AutoModelForMaskedLM.from_pretrained(checkpoint)
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

# Faceți orice cu modelul, antrenați-l, aplicați fine-tuning...

model.save_pretrained("<path_to_dummy_folder>")
tokenizer.save_pretrained("<path_to_dummy_folder>")
```
{:else}
```py
from transformers import TFAutoModelForMaskedLM, AutoTokenizer

checkpoint = "camembert-base"

model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

# Faceți orice cu modelul, antrenați-l, aplicați fine-tuning...

model.save_pretrained("<path_to_dummy_folder>")
tokenizer.save_pretrained("<path_to_dummy_folder>")
```
{/if}

Acum am salvat câteva artefacte de model și tokenizer, așadar să examinăm din nou folderul *dummy*:

```bash
ls
```

{#if fw === 'pt'}
```bash
config.json  pytorch_model.bin  README.md  sentencepiece.bpe.model  special_tokens_map.json tokenizer_config.json  tokenizer.json
```
Dacă priviți la dimensiunile fișierelor (de exemplu, folosind `ls -lh`), ar trebui să vedeți că singurul outlier este model state dict file (*pytorch_model.bin*), care are mai mult de 400 MB.

{:else}
```bash
config.json  README.md  sentencepiece.bpe.model  special_tokens_map.json  tf_model.h5  tokenizer_config.json  tokenizer.json
```
Dacă priviți la dimensiunile fișierelor (de exemplu, folosind `ls -lh`), ar trebui să vedeți că singurul outlier este model state dict file (*tf_model.h5*), care are mai mult de 400 MB.

{/if}

<Tip>
✏️ Dacă creați repositoriul folosind interfața web, fișierul *.gitattributes* va fi automat configurat pentru a considera anumite extensii de fișiere, precum *.bin* și *.h5*, ca fiind fișiere mari. În acest caz nu este necesară nici o setare suplimentară din partea ta, pentru că git-lfs le va urmări automat.
</Tip>

Acum putem continua procesul în felul nostru obișnuit cu repositoriurile Git tradiționale. Putem adăuga toate fișierele în mediul de stocare a Git folosind comanda `git add`:

```bash
git add .
```

Puteți verifica fișierele curente care sunt în stare de stocare utilizând următoarea comandă:

```bash
git status
```


{#if fw === 'pt'}
```bash
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
  modified:   .gitattributes
	new file:   config.json
	new file:   pytorch_model.bin
	new file:   sentencepiece.bpe.model
	new file:   special_tokens_map.json
	new file:   tokenizer.json
	new file:   tokenizer_config.json
```
{:else}
```bash
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
  modified:   .gitattributes
	new file:   config.json
	new file:   sentencepiece.bpe.model
	new file:   special_tokens_map.json
	new file:   tf_model.h5
	new file:   tokenizer.json
	new file:   tokenizer_config.json
```
{/if}

De asemenea, putem verifica dacă git-lfs monitorizează fisierele corecte folosind comanda sa `status`:

```bash
git lfs status
```


{#if fw === 'pt'}
```bash
On branch main
Objects to be pushed to origin/main:


Objects to be committed:

	config.json (Git: bc20ff2)
	pytorch_model.bin (LFS: 35686c2)
	sentencepiece.bpe.model (LFS: 988bc5a)
	special_tokens_map.json (Git: cb23931)
	tokenizer.json (Git: 851ff3e)
	tokenizer_config.json (Git: f0f7783)

Objects not staged for commit:


```

Vom observa că toate fișierele au `Git` ca handler, cu excepția *pytorch_model.bin* și *sentencepiece.bpe.model*, care au `LFS`. Excelent!

{:else}
```bash
On branch main
Objects to be pushed to origin/main:


Objects to be committed:

	config.json (Git: bc20ff2)
	sentencepiece.bpe.model (LFS: 988bc5a)
	special_tokens_map.json (Git: cb23931)
	tf_model.h5 (LFS: 86fce29)
	tokenizer.json (Git: 851ff3e)
	tokenizer_config.json (Git: f0f7783)

Objects not staged for commit:


```

Observăm că toate fișierele au `Git` ca handler, cu excepția *tf_model.h5*, care are `LFS`. Excelent!

{/if}

Acum vom continua cu ultimii pași: commiting și pushing la repo-urile *huggingface.co*:

```
git commit -m "Prima versiune a modelului"
```


{#if fw === 'pt'}
```bash
[main b08aab1] Prima versiune a modelului
 7 files changed, 29027 insertions(+)
  6 files changed, 36 insertions(+)
 create mode 100644 config.json
 create mode 100644 pytorch_model.bin
 create mode 100644 sentencepiece.bpe.model
 create mode 100644 special_tokens_map.json
 create mode 100644 tokenizer.json
 create mode 100644 tokenizer_config.json
```
{:else}
```bash
[main b08aab1] Prima versiune a modelului
 6 files changed, 36 insertions(+)
 create mode 100644 config.json
 create mode 100644 sentencepiece.bpe.model
 create mode 100644 special_tokens_map.json
 create mode 100644 tf_model.h5
 create mode 100644 tokenizer.json
 create mode 100644 tokenizer_config.json
```
{/if}

Pushingul poate lua câteva minute, în funcție de viteza conexiunii tale la internet și mărimea fișierelor:

```bash
git push
```

```bash
Uploading LFS objects: 100% (1/1), 433 MB | 1.3 MB/s, done.
Enumerating objects: 11, done.
Counting objects: 100% (11/11), done.
Delta compression using up to 12 threads
Compressing objects: 100% (9/9), done.
Writing objects: 100% (9/9), 288.27 KiB | 6.27 MiB/s, done.
Total 9 (delta 1), reused 0 (delta 0), pack-reused 0
To https://huggingface.co/lysandre/dummy
   891b41d..b08aab1  main -> main
```

{#if fw === 'pt'}
Dacă ne uităm la repositoriul modelului când acest lucru este finalizat, putem vedea toate fișierele recent adăugate:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/full_model.png" alt="Tabul 'Files and versions' conține acum toate fișierele recente adăugate." width="80%"/>
</div>

Interfața permite explorarea fișierelor modelului și a commiturilor și vizualizarea diferenței introduse de fiecare commit:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/diffs.gif" alt="Diferența introdusă de ultimul commit." width="80%"/>
</div>
{:else}
Dacă ne uităm la repositoriul modelului când acest lucru este finalizat, putem vedea toate fișierele recent adăugate:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/full_model_tf.png" alt="Tabul 'Files and versions' conține acum toate fișierele recente adăugate." width="80%"/>
</div>

Interfața permite explorarea fișierelor modelului și a commiturilor și vizualizarea diferenței introduse de fiecare commit:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/diffstf.gif" alt="Diferența introdusă de ultimul commit." width="80%"/>
</div>
{/if}


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter4/3.mdx" />

### Quiz la final de capitol[[end-of-chapter-quiz]]
https://huggingface.co/learn/course/ro/chapter4/6.md

# Quiz la final de capitol[[end-of-chapter-quiz]]

<CourseFloatingBanner
    chapter={4}
    classNames="absolute z-10 right-0 top-0"
/>

Să testăm ceea ce ai învățat în acest capitol!
  
### 1. La ce sunt limitate modelele din Hub?

<Question
	choices={[ 
		{
			text: "Modelele din biblioteca 🤗 Transformers.",
			explicație: "Ținând cont că modelele din biblioteca 🤗 Transformers sunt disponibile pe Hugging Face Hub, ele nu sunt singurele!"
		},
		{
			text: "Toate modelele cu o interfață similară cu 🤗 Transformers.",
			explicație: "Nu există nicio cerință de interfață când se încarcă modelele pe Hugging Face Hub."
		},
		{
			text: "Nu există limite.",
			explicație: "Adevărat! Nu există nici o limită când se încarcă modele pe Hub.",
            corect: true
		},
        {
			text: "Modele care, într-o anumită măsură, sunt legate de NLP.",
			explicație: "Nu există nicio cerință privind domeniul aplicației!"
		}
	] }
/>

### 2. Cum poți gestiona modelele pe Hub?

<Question
	choices={[ 
		{
			text: "Printr-un cont GCP.",
			explicație: "Greșit! "
		},
		{
			text: "Prin distribuția peer-to-peer.",
			explicație: "Greșit!"
		},
		{
			text: "Prin git și git-lfs.",
			explicație: "Corect! Modelele din Hub sunt simple repositorii Git, care utilizează <code>git-lfs</code> pentru fișiere mari.",
            corect: true
		}
	] }
/>

### 3. Ce poți face utilizând interfața web a Hugging Face Hub?

<Question
	choices={[ 
		{
			text: "Forkingul unui repositoriu existent.",
			explicație: "Nu se poate face forking la un repositoriu pe Hugging Face Hub."
		},
		{
			text: "Crearea unui nou model repository.",
			explicație: "Corect! Dar nu este singurul lucru pe care îl poți face...",
            corect: true
		},
		{
			text: "Gestionarea și editarea fișierelor.",
			explicație: "Corect! Dar nuu este singurul lucru pe care îl poți face...",
            correct: true
		},
        {
			text: "Încărcarea de fișiere.",
			explicație: "Corect! Dar nu este singurul lucru...",
            correct: true
		},
        {
			text: "Vizualizarea diferențelor între versiuni.",
			explicație: "Corect! Dar nu este singurul lucru pe care îl poți face...",
            corect: true
		}
	] }
/>

### 4. Ce este un model card?

<Question
	choices={[ 
		{
			text: "O descriere a modelului, deci mai puțin importantă decât fișierele model și tokenizer.",
			explicație: "Este într-adevăr o descriere a modelului, dar este un element important. Dacă descrierea nu este completă sau absentă, utilitatea modelului se reduce drastic."
		},
		{
			text: "Un mod de asigurare a reproducerii, reutilizării și egalității.",
			explicație: "Corect! Oferirea informațiilor adecvate în model card va ajuta utilizatorii să utilizeze modelul și să fie conștienți de limitele și biasurile acestuia. ",
            corect: true
		},
		{
			text: "Un fișier Python care poate fi rulat pentru a obține informații despre model.",
			explicație: "Model cardurile sunt simple fișiere Markdown."
		}
	] }
/>

Chapitru 4

### 5. Câte dintre obiectele bibliotecii 🤗 Transformers pot fi împărtășite direct pe Hub cu `push_to_hub()`?

{#if fw === 'pt'}
<Question
	choices={[
		{
			text: "Un tokenizer",
			explain: "Corect! Toți tokenizerii au metoda <code>push_to_hub</code>, și utilizând-o, vor împărtăși toate fișierele tokenizerului (vocabular, arhitectură tokenizerului, etc.) către un repo. Însă acesta nu este singura soluție corectă!",
            correct: true
		},
		{
			text: "O configurare a modelului",
			explain: "Corect! Toate configurațiile modelelor au metoda <code>push_to_hub</code>, și utilizând-o, vor împărtăși configurația către un repo. Și ce altceva poți oferi?",
            correct: true
		},
		{
			text: "Un model",
			explain: "Corect! Toate modelele au metoda <code>push_to_hub</code>, și utilizând-o, vor împărtăși ei, precum și fișierele de configurare către un repo. Și nu numai asta!",
            correct: true
		},
        {
			text: "Un Trainer",
			explain: "Corect — <code>Trainer</code> implementează metoda <code>push_to_hub</code>, și utilizând-o, vor încărca modelul, configurarea sa, tokenizerul, precum și un draft a unui model card către un repo. Încearcă și altă opțiune!",
            correct: true
		}
	]}
/>

{:else}
<Question
	choices={[
		{
			text: "Un tokenizer",
			explain: "Corect! Toți tokenizerii au metoda <code>push_to_hub</code>, și utilizând-o, vor împărtăși toate fișierele tokenizerului (vocabular, arhitectură tokenizerului, etc.) către un repo. Însă acesta nu este singura soluție corectă!",
            correct: true
		},
		{
			text: "O configurare a modelului",
			explain: "Corect! Toate configurațiile modelelor au metoda <code>push_to_hub</code>, și utilizând-o, vor împărtăși configurația către un repo. Și ce altceva poți oferi?",
            correct: true
		},
		{
			text: "Un model",
			explain: "Corect! Toate modelele au metoda <code>push_to_hub</code>, și utilizând-o, vor împărtăși ei, precum și fișierele de configurare către un repo. Și nu numai asta!",
            correct: true
		},
        {
			text: "Toate cele trei cu un callback dedicat",
			explain: "Corect — <code>PushToHubCallback</code> va trimite regular toate aceste obiecte către un repo în timpul antrenării.",
            correct: true
		}
	]}
/>
{/if}

### 6. Care este primul pas atunci când utilizați metoda `push_to_hub()` sau instrumentele CLI?

<Question
	choices={[
		{
			text: "Logarea pe website",
			explain: "Acest lucru nu vă va ajuta local."
		},
		{
			text: "Rulați 'huggingface-cli login' în terminal.",
			explain: "Corect — acest lucru va descărca și va stoca tokenul personal.",
            correct: true
		},
		{
			text: "Rulați 'notebook_login()' într-un notebook",
			explain: "Corect — acest lucru va afișa un widget pentru a vă autentifica.",
            correct: true
		},
	]
    }
/>


### 7. Aveți un model și un tokenizer, cum le puteți încărca pe ambele în Hub?

<Question
	choices={[
		{
			text: "Prin apelarea directă a metodei `push_to_hub` pe model și tokenizer",
			explain: "Corect!",
            correct: true
		},
		{
			text: "În cadrul runtime-ului Python, prin wrappingul acestora într-un <code>huggingface_hub</code>",
			explain: "Modelele și tokenizerii deja beneficiază de utilitățile <code>huggingface_hub</code>: nu vă trebuie nici un wrapping suplimentar!"
		},
		{
			text: "Prin salvarea lor pe disc și apelarea <code>transformers-cli upload-model</code>",
			explain: "Comanda <code>upload-model</code> nu există."
		}
	]}
/>

### 8. Ce operații git poți face cu clasa `Repository`?

<Question
	choices={[
		{
			text: "Un commit",
			explain: "Corect, metoda <code>git_commit()</code> este acolo pentru a face commit.",
            correct: true
		},
		{
			text: "Un pull",
			explain: "Acesta este scopul metodei <code>git_pull()</code>",
            correct: true
		},
		{
			text: "Un push",
			explain: "Metoda <code>git_push()</code> face acest lucru.",
            correct: true
		},
		{
			text: "Un merge",
			explain: "Nu, această operație nu va fi niciodată posibilă cu acest API."
		}
	]}
/>

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter4/6.mdx" />

### Crearea unui card de model[[building-a-model-card]]
https://huggingface.co/learn/course/ro/chapter4/4.md

# Crearea unui card de model[[building-a-model-card]]


<CourseFloatingBanner
    chapter={4}
    classNames="absolute z-10 right-0 top-0"
/>

Model card-ul este un fișier care este la fel de important ca și fișierele de model si tokenizer dintr-un repositoriu. Este definiția centrală a modelului, asigurând reutilizarea de către membrii comunității și reproducerea rezultatelor, oferind o platformă pe care alți membri pot construi artefactele lor.

Documentarea procesului de antrenare și evaluare ajută pe ceilalți să înțeleagă la ce să aștepte de la un model - iar oferirea informațiilor suficiente referitoare la datele folosite si preprocesarea acestora (înainte și după antrenare) vor ajuta la identificarea și înțelegerea limitelor, biasurile și contextului în care modelul este și nu este folositor.

Prin urmare, crearea unui model card care definește modelul dvs. este un pas foarte important. Aici, oferim câteva sfaturi care v-ar putea ajuta în acest sens. Crearea model cardului se face prin intermediul fișierului `README.md`, pe care l-ați văzut anterior, care este un fișier Markdown.

Conceptul de "model card" provine dintr-o direcție de cercetare a Google, publicată inițial în articolul ["Model Cards for Model Reporting"](https://arxiv.org/abs/1810.03993) scrisă de Margaret Mitchell et al. O parte semnificativă dintre informațiile conținute aici sunt bazate pe acest articol, iar noi vă recomandăm să verificați dacă doriți să înțelegeți de ce model cardurile sunt atât importante într-o lume care valorifică reproducerea, reutilizarea și echitatea.

Model cardul începe cu o prezentare scurtă și generală a ceea ce este modelul - urmată de detalii suplimentare în secțiunile următoare:

- Descrierea modelului
- Utilizări intenționate și limitările modelului
- Cum se utilizează
- Limitări și bias
- Datele de antrenare
- Procedura de antrenare
- Rezultatele evaluării

Hai să vedem ce ar trebui să conțină fiecare dintre aceste secțiuni.

### Descrierea modelului[[model-description]]

Secțiunea de descriere a modelului conține detalii generale despre model. Aceasta include arhitectura, versiunea, dacă a fost introdus într-un articol științific, dacă este disponibilă o implementare originală, autorul și informații generale despre model. Orice drepturi de autor ar trebui să fie atribuite în această secțiune. Informația generală despre procedurile de antrenare, parametrii și avertismente pot fi menționate aici.

### Utilizări intenționate și limitările modelului[[intended-uses-limitations]]

Aici descrieți utilizările pentru care modelul este destinat - inclusiv limba și domeniile unde se poate aplica. Această secțiune din model card poate documenta și zone cunoscute ca nefiind în scopul modelului sau unde este probabil să funcționeze suboptimal.

### Cum se utilizează[[how-to-use]]

Acesta secțiune ar trebui să includă exemple despre cum să utilizați modelul. Acest lucru poate prezenta utilizarea funcției `pipeline()`, utilizarea claselor de model si tokenizer, și orice cod care credeți că ar putea fi util.

### Datele de antrenare[[training-data]]

Această secțiune trebuie să indice datasetul(urile) folosit pentru instruirea modelului. O descriere scurtă a datasetului(urilor) este binevenită.

### Procedura de antrenare[[training-procedure]]

Aici descrieți toate aspectele relevante ale antrenarii care sunt utile din punct de vedere al reproducerii. Acest lucru include orice preprocessing sau postprocessing efectuat pe date, precum și detalii ca numărul de epoci pe care a fost antrenat modelul, dimensiunea batch-ului, learning rateul și alte astfel de detalii.

### Variabilele si metrice[[variable-and-metrics]]

Aici descrieți metricile utilizate la evaluarea rezultatelor, precum și diferite aspecte pe care le măsurați. Menționați metricile folosite, pe ce dataset și care dataset split, astfel încât să fie ușor de comparat performanțele modelului cu cele ale altor modele.

### Rezultatele evaluării[[evaluation-results]]

În sfârșit, indicați cum s-a comportat modelul pe datasetul de evaluare. Dacă modelul foloseste un decision threshold, atunci includeți acest decision threshold în secțiunea respectivă - sau explicați că rezultatele sunt prezentate la diferite threshold-uri pentru utilizările preconizate.

## Exemplu[[example]]

Aici puteți găsi câteva exemple ale unor Model Card:

- [`bert-base-cased`](https://huggingface.co/bert-base-cased)
- [`gpt2`](https://huggingface.co/gpt2)
- [`distilbert`](https://huggingface.co/distilbert-base-uncased)

Câteva exemple din alte organizații și companii pot fi găsite [aici](https://github.com/huggingface/model_card/blob/master/examples.md).

## Notă[[note]]

Model cardurile nu sunt o cerință obligatorie când se publică modelele și nu este necesar să includeți toate secțiunile descrise mai sus în Model Card. Cu toate acestea, documentarea explicită a modelului poate beneficia utilizatorilor viitori, deci vă recomandăm să completați cât mai multe dintre secțiuni.

## Metadatele cardului de model[[model-card-metadata]]

Dacă ați explorat puțin Hugging Face Hub atunci ar trebui să fi văzut că anumite modele aparțin anumitor categorii: puteți filtra acestea după diverse domenii. Categoriile modelului sunt identificate prin metadata adăugată în secțiunea de header a cardului.

Spre exemplu, dacă vă uitați la modelul [`camembert-base`](https://huggingface.co/camembert-base/blob/main/README.md) atunci veți observa următoarea linii din headerul cardului:

```
---
language: fr
license: mit
datasets:
- oscar
---
```

Metadatele acestei secțiuni sunt analizate de Hugging Face Hub, care apoi identifică acest model ca fiind un model francez, cu licență MIT și antrenat pe datasetul Oscar.

[Toate specificările cardului](https://github.com/huggingface/hub-docs/blame/main/modelcard.md) permite specificarea a mai multor metadate, inclusiv limba, licența, etichetele, dataseturile folosite la antrenare și metricile evaluate.

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter4/4.mdx" />

### Utilizarea modelelor preantrenate[[using-pretrained-models]]
https://huggingface.co/learn/course/ro/chapter4/2.md

# Utilizarea modelelor preantrenate[[using-pretrained-models]]

{#if fw === 'pt'}
 
<CourseFloatingBanner chapter={4}
  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/chapter4/section2_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter4/section2_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={4}
  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/chapter4/section2_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter4/section2_tf.ipynb"},
]} />

{/if}

Hubul Model oferă o modalitate simplă de a selecta modelul adecvat, astfel încât utilizarea sa în orice bibliotecă downstream poate fi efectuată în câteva linii de cod. Să vedem cum se utilizează efectiv unul dintre aceste modele și cum putem contribui înapoi la comunitate.


Să presupunem că noi căutăm un French-based model ce poate face mask filling.

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/camembert.gif" alt="Selecting the Camembert model." width="80%"/>
</div>

Alegem checkpointul „camembert-base” pentru al încerca. Identificatorul `camembert-base` este tot de ce avem nevoie pentru a începe. În capitolele precedente, am văzut cum putem inițializa modelul folosind funcția `pipeline()`:

```py
from transformers import pipeline

camembert_fill_mask = pipeline("fill-mask", model="camembert-base")
results = camembert_fill_mask("Le camembert est <mask> :)")
```

```python out
[
  {'sequence': 'Le camembert est délicieux :)', 'score': 0.49091005325317383, 'token': 7200, 'token_str': 'délicieux'}, 
  {'sequence': 'Le camembert est excellent :)', 'score': 0.1055697426199913, 'token': 2183, 'token_str': 'excellent'}, 
  {'sequence': 'Le camembert est succulent :)', 'score': 0.03453313186764717, 'token': 26202, 'token_str': 'succulent'}, 
  {'sequence': 'Le camembert est meilleur :)', 'score': 0.0330314114689827, 'token': 528, 'token_str': 'meilleur'}, 
  {'sequence': 'Le camembert est parfait :)', 'score': 0.03007650189101696, 'token': 1654, 'token_str': 'parfait'}
]
```

În mod evident, încărcarea unui model într-un pipeline este extrem de simplu. Singurul lucru la care trebuie să atrageți atenția este că checkpointul ales este adecvat pentru sarcina pe care urmează să o execute. De exemplu, în momentul în care încărăm checkpoint-ul „camembert-base” în pipelineul `fill-mask`, este perfect în regulă. Dar dacă îl încărcam în pipelineul `text-classification`, rezultatele nu vor avea nici o logică, pentru că headul „camembert-base” nu este adecvat pentru această sarcină! Recomandăm utilizarea task selectorului în interfața Hugging Face în scopul selectării checkpointurilor adecvate:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter4/tasks.png" alt="Selectorul de sarcini pe interfața web." width="80%"/>
</div>

Puteți inițializa checkpointul în mod direct folosind arhitectura modelului:

{#if fw === 'pt'}
```py
from transformers import CamembertTokenizer, CamembertForMaskedLM

tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
model = CamembertForMaskedLM.from_pretrained("camembert-base")
```

Însă, recomandăm utilizarea [claselor `Auto*`](https://huggingface.co/transformers/model_doc/auto?highlight=auto#auto-classes), deoarece acestea sunt proiectate să fie architecture-agnostic. În timp ce codul precedent limita utilizatorii la checkpoints loadable în CamemBERT architecture, utilizarea claselor `Auto*` face schimbarea checkpointurilor simplă:

```py
from transformers import AutoTokenizer, AutoModelForMaskedLM

tokenizer = AutoTokenizer.from_pretrained("camembert-base")
model = AutoModelForMaskedLM.from_pretrained("camembert-base")
```
{:else}
```py
from transformers import CamembertTokenizer, TFCamembertForMaskedLM

tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
model = TFCamembertForMaskedLM.from_pretrained("camembert-base")
```

Însă, recomandăm utilizarea [claselor `TFAuto*`](https://huggingface.co/transformers/model_doc/auto?highlight=auto#auto-classes), deoarece acestea sunt proiectate să fie architecture-agnostic. În timp ce codul precedent limita utilizatorii la checkpoints loadable în CamemBERT architecture, utilizarea claselor `TFAuto*` face schimbarea checkpointurilor simplă:

```py
from transformers import AutoTokenizer, TFAutoModelForMaskedLM

tokenizer = AutoTokenizer.from_pretrained("camembert-base")
model = TFAutoModelForMaskedLM.from_pretrained("camembert-base")
```
{/if}

<Tip>
În momentul în care folosiți un model preantrenat, asigurați-vă să verificați cum a fost antrenat și pe ce date se bazează. De asemenea, trebuie să cunoasceți limitele și prejudecățile sale. Toată această informație va fi indicată pe cartea modelului.
</Tip>



<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter4/2.mdx" />

### Exercițiu Practic: Ajustează fin un model cu GRPO
https://huggingface.co/learn/course/ro/chapter12/5.md

# Exercițiu Practic: Ajustează fin un model cu GRPO

Acum că ai văzut teoria, să o punem în practică! În acest exercițiu, vei ajusta fin un model cu GRPO.

<Tip>

Acest exercițiu a fost scris de expertul în ajustarea fină LLM [@mlabonne](https://huggingface.co/mlabonne).

</Tip>

## Instalează dependențele

În primul rând, să instalăm dependențele pentru acest exercițiu.

```bash
!pip install -qqq datasets==3.2.0 transformers==4.47.1 trl==0.14.0 peft==0.14.0 accelerate==1.2.1 bitsandbytes==0.45.2 wandb==0.19.7 --progress-bar off
!pip install -qqq flash-attn --no-build-isolation --progress-bar off
```

Acum vom importa bibliotecile necesare.

```python
import torch
from datasets import load_dataset
from peft import LoraConfig, get_peft_model
from transformers import AutoModelForCausalLM, AutoTokenizer
from trl import GRPOConfig, GRPOTrainer
```

## Importă și loghează în Weights & Biases

Weights & Biases este un instrument pentru înregistrarea și monitorizarea experimentelor tale. Îl vom folosi pentru a înregistra procesul nostru de ajustare fină.

```python
import wandb

wandb.login()
```

Poți face acest exercițiu fără să te loghezi în Weights & Biases, dar este recomandat să faci asta pentru a-ți urmări experimentele și pentru a interpreta rezultatele.

## Încarcă setul de date

Acum, să încărcăm setul de date. În acest caz, vom folosi setul de date [`mlabonne/smoltldr`](https://huggingface.co/datasets/mlabonne/smoltldr), care conține o listă de povești scurte.

```python
dataset = load_dataset("mlabonne/smoltldr")
print(dataset)
```

## Încarcă modelul

Acum, să încărcăm modelul.

Pentru acest exercițiu, vom folosi modelul [`SmolLM2-135M`](https://huggingface.co/HuggingFaceTB/SmolLM2-135M).

Acesta este un model mic de 135M parametri care rulează pe hardware limitat. Aceasta face modelul ideal pentru învățare, dar nu este cel mai puternic model de acolo. Dacă ai acces la hardware mai puternic, poți încerca să ajustezi fin un model mai mare precum [`SmolLM2-1.7B`](https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B).

```python
model_id = "HuggingFaceTB/SmolLM-135M-Instruct"
model = AutoModelForCausalLM.from_pretrained(
    model_id,
    torch_dtype="auto",
    device_map="auto",
    attn_implementation="flash_attention_2",
)
tokenizer = AutoTokenizer.from_pretrained(model_id)
```

## Încarcă LoRA

Acum, să încărcăm configurația LoRA. Vom profita de LoRA pentru a reduce numărul de parametri antrenabili, și prin urmare amprenta de memorie de care avem nevoie pentru a ajusta fin modelul.

Dacă nu ești familiar cu LoRA, poți citi mai multe despre aceasta în [Capitolul 11](https://huggingface.co/learn/course/en/chapter11/3).

```python
# Încarcă LoRA
lora_config = LoraConfig(
    task_type="CAUSAL_LM",
    r=16,
    lora_alpha=32,
    target_modules="all-linear",
)
model = get_peft_model(model, lora_config)
print(model.print_trainable_parameters())
```

```sh
Parametri antrenabili totali: 135M
```

## Definește funcția de recompensă

După cum s-a menționat în secțiunea anterioară, GRPO poate folosi orice funcție de recompensă pentru a îmbunătăți modelul. În acest caz, vom folosi o funcție de recompensă simplă care încurajează modelul să genereze text lung de 50 de token-uri.

```python
# Funcția de recompensă
ideal_length = 50


def reward_len(completions, **kwargs):
    return [-abs(ideal_length - len(completion)) for completion in completions]
```

## Definește argumentele de antrenament

Acum, să definim argumentele de antrenament. Vom folosi clasa `GRPOConfig` pentru a defini argumentele de antrenament într-un stil tipic `transformers`.

Dacă aceasta este prima dată când definești argumente de antrenament, poți verifica clasa [TrainingArguments](https://huggingface.co/docs/transformers/en/main_classes/trainer#trainingarguments) pentru mai multe informații, sau [Capitolul 2](https://huggingface.co/learn/course/en/chapter2/1) pentru o introducere detaliată.

```python
# Argumentele de antrenament
training_args = GRPOConfig(
    output_dir="GRPO",
    learning_rate=2e-5,
    per_device_train_batch_size=8,
    gradient_accumulation_steps=2,
    max_prompt_length=512,
    max_completion_length=96,
    num_generations=8,
    optim="adamw_8bit",
    num_train_epochs=1,
    bf16=True,
    report_to=["wandb"],
    remove_unused_columns=False,
    logging_steps=1,
)
```

Acum, putem inițializa antrenorul cu modelul, setul de date și argumentele de antrenament și să începem antrenamentul.

```python
# Antrenorul
trainer = GRPOTrainer(
    model=model,
    reward_funcs=[reward_len],
    args=training_args,
    train_dataset=dataset["train"],
)

# Antrenează modelul
wandb.init(project="GRPO")
trainer.train()
```

Antrenamentul durează aproximativ 1 oră pe un singur GPU A10G care este disponibil pe Google Colab sau prin Hugging Face Spaces.

## Împinge modelul pe Hub în timpul antrenamentului

Dacă setăm argumentul `push_to_hub` la `True` și argumentul `model_id` la un nume de model valid, modelul va fi împins pe Hugging Face Hub în timp ce antrenăm. Aceasta este utilă dacă vrei să începi să testezi modelul imediat!

## Interpretează rezultatele antrenamentului

`GRPOTrainer` înregistrează recompensa din funcția ta de recompensă, pierderea, și o gamă de alte metrici.

Ne vom concentra pe recompensa din funcția de recompensă și pierderea.

După cum poți vedea, recompensa din funcția de recompensă se apropie de 0 pe măsură ce modelul învață. Acesta este un semn bun că modelul învață să genereze text de lungimea corectă.

![Rezultatul oferit din funcția de recompensă](https://huggingface.co/reasoning-course/images/resolve/main/grpo/13.png)

Ai putea observa că pierderea începe de la zero și apoi crește în timpul antrenamentului, ceea ce poate părea contraontuitiv. Acest comportament este așteptat în GRPO și este direct legat de formularea matematică a algoritmului. Pierderea în GRPO este proporțională cu divergența KL (capsula relativă la politica originală). Pe măsură ce antrenamentul progresează, modelul învață să genereze text care se potrivește mai bine cu funcția de recompensă, făcându-l să devieze mai mult de la politica sa inițială. Această divergență crescândă se reflectă în valoarea pierderii în creștere, care de fapt indică că modelul se adaptează cu succes pentru a optimiza funcția de recompensă.

![Pierderea](https://huggingface.co/reasoning-course/images/resolve/main/grpo/14.png)

## Salvează și publică modelul

Să partajăm modelul cu comunitatea!

```python
merged_model = trainer.model.merge_and_unload()
merged_model.push_to_hub(
    "SmolGRPO-135M", private=False, tags=["GRPO", "Reasoning-Course"]
)
```

## Generează text

🎉 Ai ajustat fin cu succes un model cu GRPO! Acum, să generăm puțin text cu modelul.

În primul rând, vom defini un document foarte lung!

```python
prompt = """
# Un document lung despre pisică

Pisica (Felis catus), numită și pisica domestică sau pisica de casă, este un mamifer carnivor 
mic domesticit. Este singura specie domesticită din familia Felidae. Progresele în arheologie 
și genetică au arătat că domesticirea pisicii a avut loc în Orientul Apropiat în jurul anului 
7500 î.Hr. Este ținută în mod obișnuit ca animal de companie și pisică de fermă, dar se află 
și în libertate ca pisică sălbatică evitând contactul uman. Este prețuită de oameni pentru 
companie și capacitatea sa de a ucide dăunători. Ghearele sale retractabile sunt adaptate 
pentru uciderea speciilor de pradă mici precum șoarecii și șobolanii. Are un corp puternic 
și flexibil, reflexe rapide și dinți ascuțiți, iar vederea nocturnă și simțul mirosului sunt 
bine dezvoltate. Este o specie socială, dar un vânător solitar și un prădător crepuscular. 
Comunicarea pisicii include vocalizări—incluzând mieunat, tors, triluri, șuierat, mormăit 
și grunțit—precum și limbajul corpului. Poate auzi sunete prea slabe sau prea înalte în 
frecvență pentru urechile umane, cum ar fi cele făcute de mamiferele mici. Secretă și 
percep feromoni.
"""
```

messages = [
    {"role": "user", "content": prompt},
]

Acum, putem genera text cu modelul.

```python
# Generează text
from transformers import pipeline

generator = pipeline("text-generation", model="SmolGRPO-135M")

## Sau folosește modelul și tokenizer-ul pe care le-am definit mai devreme
# generator = pipeline("text-generation", model=model, tokenizer=tokenizer)

generate_kwargs = {
    "max_new_tokens": 256,
    "do_sample": True,
    "temperature": 0.5,
    "min_p": 0.1,
}

generated_text = generator(messages, generate_kwargs=generate_kwargs)

print(generated_text)
```

# Concluzie

În acest capitol, am văzut cum să ajustăm fin un model cu GRPO. Am văzut de asemenea cum să interpretăm rezultatele antrenamentului și să generăm text cu modelul.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter12/5.mdx" />

### Open R1 pentru Studenți
https://huggingface.co/learn/course/ro/chapter12/1.md

# Open R1 pentru Studenți

Bun venit într-o călătorie captivantă în lumea AI-ului open-source cu învățarea prin întărire! Acest capitol este conceput pentru a ajuta studenții să înțeleagă învățarea prin întărire și rolul acesteia în LLM-uri.

De asemenea, vom explora [Open R1](https://github.com/huggingface/open-r1), un proiect comunitar revoluționar care face AI-ul avansat accesibil pentru toată lumea. În mod specific, acest curs este pentru a ajuta studenții și cursanții să folosească și să contribuie la [Open R1](https://github.com/huggingface/open-r1).

## Ce Vei Învăța

În acest capitol, vom descompune concepte complexe în părți ușor de înțeles și îți vom arăta cum poți fi parte din acest proiect captivant de a face LLM-urile să raționeze asupra problemelor complexe.

LLM-urile au demonstrat performanțe excelente pe multe sarcini generative. Cu toate acestea, până recent, au avut dificultăți cu problemele complexe care necesită raționament. De exemplu, le este greu să se descurce cu puzzle-uri sau probleme de matematică care necesită mai mulți pași de raționament.

Open R1 este un proiect care își propune să facă LLM-urile să raționeze asupra problemelor complexe. Face acest lucru folosind învățarea prin întărire pentru a încuraja LLM-urile să 'gândească' și să raționeze.

În termeni simpli, modelul este antrenat să genereze gânduri precum și rezultate, și să structureze aceste gânduri și rezultate astfel încât să poată fi gestionate separat de către utilizator.

Să aruncăm o privire la un exemplu. Dacă ne-am dat nouă înșine sarcina de a rezolva următoarea problemă, am putea gândi astfel:

```sh
Problemă: "Am 3 mere și 2 portocale. Câte bucăți de fructe am în total?"

Gând: "Trebuie să adun numărul de mere și portocale pentru a obține numărul total de bucăți de fructe."

Răspuns: "5"
```

Putem apoi să structurăm acest gând și răspuns astfel încât să poată fi gestionate separat de către utilizator. Pentru sarcinile de raționament, LLM-urile pot fi antrenate să genereze gânduri și răspunsuri în următorul format:

```sh
<think>Trebuie să adun numărul de mere și portocale pentru a obține numărul total de bucăți de fructe.</think>
5
```

Ca utilizator, putem apoi să extragem gândul și răspunsul din rezultatul modelului și să le folosim pentru a rezolva problema.

## De Ce Este Important Pentru Studenți

Ca student, înțelegerea Open R1 și a rolului învățării prin întărire în LLM-uri este valoroasă pentru că:
- Îți arată cum se dezvoltă AI-ul de ultimă generație
- Îți oferă oportunități practice de a învăța și contribui
- Te ajută să înțelegi înspre unde se îndreaptă tehnologia AI
- Îți deschide uși către viitoare oportunități de carieră în AI

## Prezentarea Capitolului

Acest capitol este împărțit în patru secțiuni, fiecare concentrându-se pe un aspect diferit al Open R1:

### 1️⃣ Introducere în Învățarea prin Întărire și rolul acesteia în LLM-uri
Vom explora elementele de bază ale Învățării prin Întărire (RL) și rolul acesteia în antrenarea LLM-urilor.
- Ce este RL?
- Cum este folosit RL în LLM-uri?
- Ce este DeepSeek R1?
- Care sunt inovațiile cheie ale DeepSeek R1?

### 2️⃣ Înțelegerea Lucrării DeepSeek R1
Vom descompune lucrarea de cercetare care a inspirat [Open R1](https://huggingface.co/open-r1):
- Inovații și descoperiri cheie
- Procesul de antrenare și arhitectura
- Rezultate și semnificația lor

### 3️⃣ Implementarea GRPO în TRL
Vom deveni practici cu exemple de cod:
- Cum să folosești biblioteca Transformer Reinforcement Learning (TRL)
- Configurarea antrenamentului GRPO

### 4️⃣ Caz de utilizare practic pentru alinierea unui model
Vom privi un caz de utilizare practic pentru alinierea unui model folosind Open R1.
- Cum să antrenezi un model folosind GRPO în TRL
- Să-ți partajezi modelul pe [Hugging Face Hub](https://huggingface.co/models)

## Cerințe Prealabile

Pentru a obține cel mai mult din acest capitol, este util să ai:
- Înțelegere solidă a programării Python
- Familiaritate cu conceptele de machine learning
- Interes pentru AI și modelele de limbaj

Nu-ți face griji dacă îți lipsesc unele dintre acestea – vom explica conceptele cheie pe măsură ce mergem! 🚀

<Tip>

Dacă nu ai toate cerințele prealabile, consultă acest [curs](/course/chapter1/1) de la unitățile 1 la 11

</Tip>

## Cum să Folosești Acest Capitol

1. **Citește Secvențial**: Secțiunile se construiesc una pe alta, așa că este mai bine să le citești în ordine
2. **Partajează Notițe**: Scrie conceptele cheie și întrebările și discută-le cu comunitatea în [Discord](https://discord.gg/F3vZujJH)
3. **Încearcă Codul**: Când ajungem la exemplele practice, încearcă-le singur
4. **Alătură-te Comunității**: Folosește resursele pe care le oferim pentru a te conecta cu alți cursanți

Să începem explorarea Open R1 și să descoperim cum poți fi parte din a face AI-ul mai accesibil pentru toată lumea! 🚀 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter12/1.mdx" />

### Înțelegerea Lucrării DeepSeek R1
https://huggingface.co/learn/course/ro/chapter12/3.md

# Înțelegerea Lucrării DeepSeek R1

Acest capitol este un curs intensiv de lectură a unei lucrări. Vom parcurge lucrarea în termeni simpli, și apoi vom descompune conceptele cheie și concluziile.

DeepSeek R1 reprezintă un progres semnificativ în antrenarea modelelor de limbaj, în special în dezvoltarea capacităților de raționament prin învățarea prin întărire. Lucrarea introduce un nou algoritm de învățare prin întărire numit Optimizarea Relativă a Politicii de Grup (GRPO).

![DeepSeek R1 Overview](https://huggingface.co/reasoning-course/images/resolve/main/grpo/4.png)

În următorul capitol, vom construi pe această cunoaștere și vom implementa GRPO în practică.

Obiectivul inițial al lucrării a fost să exploreze dacă învățarea prin întărire pură ar putea dezvolta capacități de raționament fără ajustarea fină supervizată.

<Tip>

Până la acel moment, toate LLM-urile populare necesitau o anumită ajustare fină supervizată, pe care am explorat-o în [capitolul 11](/course/chapter11/1).

</Tip>

## Momentul Revoluționar 'Aha'

![The 'Aha Moment'](https://huggingface.co/reasoning-course/images/resolve/main/grpo/9.png)

Una dintre cele mai remarcabile descoperiri în antrenarea R1-Zero a fost apariția unui fenomen cunoscut ca "Momentul Aha." Acest fenomen este oarecum similar cu modul în care oamenii experimentează realizări bruște în timpul rezolvării problemelor. Iată cum funcționează:

1. Încercare Inițială: Modelul face o încercare inițială de a rezolva o problemă
2. Recunoaștere: Recunoaște erori potențiale sau inconsistențe
3. Auto-Corecție: Își ajustează abordarea bazat pe această recunoaștere
4. Explicație: Poate explica de ce noua abordare este mai bună

Această descoperire rezonează cu cursanții și se simte ca un moment "Eureka". Demonstrează învățarea mai degrabă decât memorarea simplă, așa că să ne luăm un moment să ne imaginăm cum se simte să ai un moment "Aha".

De exemplu, imaginează-ți că încerci să rezolvi un puzzle:
- Prima încercare: "Această piesă ar trebui să meargă aici bazat pe culoare"
- Recunoaștere: "Dar stai, forma nu se potrivește destul de bine"
- Corecție: "Ah, de fapt merge acolo"
- Explicație: "Pentru că atât culoarea cât și modelul de formă se potrivesc în această poziție"

Această abilitate a apărut natural din antrenamentul RL, fără a fi programată explicit, demonstrând învățarea mai degrabă decât memorarea simplă a unui proces din datele de antrenare.

Cel mai ușor mod de a înțelege momentul 'Aha' este să-l vezi în acțiune. Să aruncăm o privire la un exemplu. În chat-ul de mai jos, întrebăm modelul să rezolve o problemă și UI-ul arată procesul de gândire al modelului pe măsură ce rezolvă problema.

<iframe
    src="https://reasoning-course-deepseek-ai-deepseek-r1-distill-0f5fad4.hf.space/"
	frameborder="0"
	width="850"
	height="450"
></iframe>

Dacă vrei să încerci Deepseek's R1, poți de asemenea să consulți [Hugging Chat](https://huggingface.co/chat/models/deepseek-ai/DeepSeek-R1-Distill-Qwen-32B).

## Procesul de Antrenare

Antrenarea R1 a fost un proces cu mai multe faze. Să descompunem fazele și inovațiile cheie în fiecare fază.

Procesul final rezultă în două modele:
- DeepSeek-R1-Zero: Un model antrenat pur folosind învățarea prin întărire.
- DeepSeek-R1: Un model care construiește pe fundația DeepSeek-R1-Zero și adaugă ajustare fină supervizată.

| Caracteristică | DeepSeek-R1-Zero | DeepSeek-R1 |
|---------|------------------|--------------|
| Abordarea de Antrenare | RL Pur | Multi-fază (SFT + RL) |
| Ajustare Fină | Niciuna | Ajustare fină supervizată |
| Capacitate de Raționament | Emergentă | Îmbunătățită |
| Performanța AIME | 71.0% | 79.8% |
| Caracteristici Cheie | Raționament puternic dar probleme de lizibilitate | Consistență și lizibilitate mai bună a limbajului |

În timp ce DeepSeek-R1-Zero demonstrează potențialul învățării prin întărire pure pentru dezvoltarea capacităților de raționament, DeepSeek-R1 construiește pe această fundație cu o abordare mai echilibrată care prioritizează atât performanța de raționament cât și utilizabilitatea.

Procesul de antrenare implică patru faze:

1. Faza de Pornire la Rece
2. Faza RL de Raționament
3. Faza de Eșantionare prin Respingere
4. Faza RL Diversă

Să descompunem fiecare fază:

### Faza de Pornire la Rece (Fundația Calității)

![Cold Start Phase](https://huggingface.co/reasoning-course/images/resolve/main/grpo/5.png)

Această fază este concepută pentru a stabili o fundație solidă pentru lizibilitatea și calitatea răspunsurilor modelului. Folosește un set de date mic de eșantioane de înaltă calitate de la R1-Zero pentru a ajusta fin modelul V3-Base. Începând cu modelul DeepSeek-V3-Base, echipa a folosit mii de eșantioane validate, de înaltă calitate de la R1-Zero pentru ajustarea fină supervizată. Această abordare inovatoare folosește un set de date mic dar de înaltă calitate pentru a stabili o lizibilitate de bază puternică și calitatea răspunsurilor.

### Faza RL de Raționament (Construirea Capacităților)

![Reasoning RL Phase](https://huggingface.co/reasoning-course/images/resolve/main/grpo/6.png)

Faza RL de Raționament se concentrează pe dezvoltarea capacităților de raționament de bază în domenii incluzând matematica, codarea, știința și logica. Această fază folosește învățarea prin întărire bazată pe reguli, cu recompense direct legate de corectitudinea soluției.

Crucial, toate sarcinile din această fază sunt 'verificabile' deci putem verifica dacă răspunsul modelului este corect sau nu. De exemplu, în cazul matematicii, putem verifica dacă răspunsul modelului este corect folosind un rezolvitor matematic.

Ceea ce face această fază deosebit de inovatoare este abordarea sa de optimizare directă care elimină nevoia unui model de recompensă separat, simplificând procesul de antrenare.

### Faza de Eșantionare prin Respingere (Controlul Calității)

![Rejection Sampling Phase](https://huggingface.co/reasoning-course/images/resolve/main/grpo/7.png)

În timpul Fazei de Eșantionare prin Respingere, modelul generează eșantioane care sunt apoi filtrate printr-un proces de control al calității. DeepSeek-V3 servește ca judecător al calității, evaluând rezultatele într-un domeniu larg care se extinde dincolo de sarcinile pure de raționament. Datele filtrate sunt apoi folosite pentru ajustarea fină supervizată. Inovația acestei faze constă în abilitatea sa de a combina multiple semnale de calitate pentru a asigura rezultate de înalt standard.

### Faza RL Diversă (Alinierea Largă)

![Diverse RL Phase](https://huggingface.co/reasoning-course/images/resolve/main/grpo/8.png)

Faza finală RL Diversă abordează multiple tipuri de sarcini folosind o abordare hibridă sofisticată. Pentru sarcinile deterministe, folosește recompense bazate pe reguli, în timp ce sarcinile subiective sunt evaluate prin feedback LLM. Această fază își propune să atingă alinierea preferințelor umane prin abordarea sa inovatoare de recompense hibride, combinând precizia sistemelor bazate pe reguli cu flexibilitatea evaluării modelelor de limbaj.

## Algoritmul: Optimizarea Relativă a Politicii de Grup (GRPO)

Acum că avem o înțelegere bună a procesului de antrenare, să privim algoritmul care a fost folosit pentru a antrena modelul.

Autorii descriu GRPO ca o descoperire în ajustarea fină a modelelor:

![GRPO Process](https://huggingface.co/reasoning-course/images/resolve/main/grpo/10.png)

Noutatea GRPO constă în capacitatea sa de a "optimiza direct pentru rectificarea preferințelor." Aceasta semnifică o rută mai directă și eficientă către alinierea modelului cu rezultatele dorite, contrastând cu algoritmii tradiționali de Învățare prin Întărire precum PPO. Să descompunem cum funcționează GRPO prin cele trei componente principale ale sale.

### Formarea Grupurilor: Crearea de Multiple Soluții

Primul pas în GRPO este remarcabil de intuitiv - este similar cu modul în care un student ar putea rezolva o problemă dificilă încercând multiple abordări. Când primește o comandă, modelul nu generează doar un răspuns; în schimb, creează multiple încercări de a rezolva aceeași problemă (de obicei 4, 8 sau 16 încercări diferite).

Imaginează-ți că înveți un model să rezolve probleme de matematică. Pentru o întrebare despre numărarea puilor de pe o fermă, modelul ar putea genera mai multe soluții diferite:
- O soluție ar putea descompune problema pas cu pas: prima numărarea puilor totali, apoi scăderea cocoșilor, și în final contabilizarea găinilor care nu produc ouă
- Altă soluție ar putea folosi o abordare diferită dar la fel de validă
- Unele încercări ar putea conține greșeli sau soluții mai puțin eficiente

Toate aceste încercări sunt păstrate împreună ca grup, aproape ca să ai mai mulți studenti cu soluții de comparat și din care să înveți.

![Group Formation](https://huggingface.co/reasoning-course/images/resolve/main/grpo/11.jpg)

### Învățarea Preferințelor: Înțelegerea Ce Face o Soluție Bună

Aici este unde GRPO cu adevărat strălucește în simplitatea sa. Spre deosebire de alte metode pentru RLHF care întotdeauna necesită un model de recompensă separat pentru a prezice cât de bună ar putea fi o soluție, GRPO poate folosi orice funcție sau model pentru a evalua calitatea unei soluții. De exemplu, am putea folosi o funcție de lungime pentru a recompensa răspunsuri mai scurte sau un rezolvitor matematic pentru a recompensa soluții matematice precise.

Procesul de evaluare privește la diverse aspecte ale fiecărei soluții:
- Este răspunsul final corect?
- A urmat soluția formatarea corespunzătoare (cum ar fi folosirea tag-urilor XML corecte)?
- Se potrivește raționamentul cu răspunsul oferit?

Ceea ce face această abordare deosebit de inteligentă este modul în care gestionează evaluarea. În loc să dea doar scoruri absolute, GRPO normalizează recompensele în cadrul fiecărui grup. Folosește o formulă simplă dar eficientă pentru estimarea avantajului relativ al grupului:

```
Avantaj = (recompensă - mean(recompense_grup)) / std(recompense_grup)
```

![Preference Learning](https://huggingface.co/reasoning-course/images/resolve/main/grpo/12.jpg)

Această normalizare este ca să notezi pe o curbă, dar pentru AI. Ajută modelul să înțeleagă care soluții din cadrul grupului au fost mai bune sau mai rele comparativ cu colegii lor, mai degrabă decât să se uite doar la scoruri absolute.

### Optimizarea: Învățarea din Experiență

Pasul final este unde GRPO îi învață modelul să se îmbunătățească bazat pe ceea ce a învățat din evaluarea grupului de soluții. Acest proces este atât puternic cât și stabil, folosind două principii principale:

1. Încurajează modelul să producă mai multe soluții ca cele de succes în timp ce se îndepărtează de abordările mai puțin eficiente
2. Include un mecanism de siguranță (numit penalitate de divergență KL) care previne modelul să se schimbe prea drastic dintr-o dată

Această abordare se dovedește mai stabilă decât metodele tradiționale pentru că:
- Se uită la multiple soluții împreună mai degrabă decât să compare doar două pe rând
- Normalizarea bazată pe grup ajută la prevenirea problemelor cu scalarea recompenselor
- Penalitatea KL acționează ca o plasă de siguranță, asigurându-se că modelul nu uită ce știa deja în timp ce învață lucruri noi

<Tip>

Inovațiile cheie ale GRPO sunt:
- Învățarea direct din orice funcție sau model, eliminând dependența de un model de recompensă separat.
- Învățarea bazată pe grup, care este mai stabilă și eficientă decât metodele tradiționale cum ar fi comparațiile perechi.

</Tip>

Această descompunere este complexă, dar concluzia cheie este că GRPO este o modalitate mai eficientă și stabilă de a antrena un model să raționeze.

### Algoritmul GRPO în Pseudocod

Acum că înțelegem componentele cheie ale GRPO, să privim algoritmul în pseudocod. Aceasta este o versiune simplificată a algoritmului, dar surprinde ideile cheie.

```
Input: 
- initial_policy: Modelul de început care va fi antrenat
- reward_function: Funcția care evaluează rezultatele
- training_prompts: Set de exemple de antrenare
- group_size: Numărul de rezultate per comandă (de obicei 4-16)

Algoritm GRPO:
1. Pentru fiecare iterație de antrenare:
   a. Setează reference_policy = initial_policy (captura politica actuală)
   b. Pentru fiecare comandă în lot:
      i. Generează group_size rezultate diferite folosind initial_policy
      ii. Calculează recompensele pentru fiecare rezultat folosind reward_function
      iii. Normalizează recompensele în cadrul grupului:
           normalized_advantage = (recompensă - mean(recompense)) / std(recompense)
      iv. Actualizează politica maximizând rația tăiată:
          min(prob_ratio * normalized_advantage, 
              clip(prob_ratio, 1-epsilon, 1+epsilon) * normalized_advantage)
          - kl_weight * KL(initial_policy || reference_policy)
          
          unde prob_ratio este current_prob / reference_prob

Output: Model de politică optimizat
```

Acest algoritm arată cum GRPO combină estimarea avantajului bazat pe grup cu optimizarea politicii menținând în același timp stabilitatea prin tăiere și constrângeri de divergență KL.

## Rezultate și Impact

Acum că am explorat algoritmul, să privim rezultatele. DeepSeek R1 atinge performanțe de ultimă generație în multiple domenii:

| Domeniu | Rezultate Cheie |
|--------|-------------|
| Matematică | • 79.8% pe AIME 2024<br>• 97.3% pe MATH-500 |
| Codare | • Rating Codeforces: 2029<br>• LiveCodeBench: 65.9% |
| Cunoștințe Generale | • MMLU: 90.8%<br>• GPQA Diamond: 71.5% |
| Sarcini de Limbaj | • AlpacaEval 2.0: 87.6% rată de câștig<br>• FRAMES: 82.5% |

Impactul practic al modelului se extinde dincolo de benchmark-uri prin prețurile sale cost-eficiente ale API-ului ($0.14 per milion de token-uri de intrare) și distilarea cu succes a modelului în diverse dimensiuni (1.5B la 70B parametri). În mod notabil, chiar și modelul de 7B atinge 55.5% pe AIME 2024, în timp ce versiunea distilată de 70B se apropie de performanța o1-mini pe MATH-500 (94.5%), demonstrând conservarea eficientă a capacităților la diferite scale.

## Limitări și Provocări ale GRPO

În timp ce GRPO reprezintă un progres semnificativ în învățarea prin întărire pentru modelele de limbaj, este important să înțelegem limitările și provocările sale:

- **Costul Generării**: Generarea de multiple completări (4-16) pentru fiecare prompt crește cerințele computaționale comparativ cu metodele care generează doar una sau două completări.
- **Constrângerile Dimensiunii Lotului**: Nevoia de a procesa grupuri de completări împreună poate limita dimensiunile eficiente ale loturilor, adăugând complexitate procesului de antrenare și potențial încetinind antrenarea.
- **Designul Funcției de Recompensă**: Calitatea antrenamentului depinde foarte mult de funcții de recompensă bine concepute. Recompensele prost concepute pot duce la comportamente neintenționate sau optimizarea pentru obiectivele greșite.
- **Compromisurile Dimensiunii Grupului**: Alegerea dimensiunii optime a grupului implică echilibrarea diversității soluțiilor împotriva costului computațional. Prea puține eșantioane s-ar putea să nu ofere suficientă diversitate, în timp ce prea multe cresc timpul de antrenare și cerințele de resurse.
- **Reglarea Divergenței KL**: Găsirea echilibrului potrivit pentru penalitatea divergenței KL necesită reglare atentă - prea mare și modelul nu va învăța eficient, prea mică și poate devia prea departe de capacitățile sale inițiale.

## Concluzie

Lucrarea DeepSeek R1 reprezintă o piatră de hotar semnificativă în dezvoltarea modelelor de limbaj. Algoritmul Group Relative Policy Optimization (GRPO) a demonstrat că învățarea prin întărire pură poate într-adevăr dezvolta capacități puternice de raționament, provocând presupunerile anterioare despre necesitatea ajustării fine supervizate.

Poate cel mai important, DeepSeek R1 a arătat că este posibil să echilibrezi performanța înaltă cu considerații practice precum cost-eficiența și accesibilitatea. Distilarea cu succes a capacităților modelului în diferite dimensiuni, de la 1.5B la 70B parametri, demonstrează o cale înainte pentru a face capacitățile AI avansate mai larg disponibile.

---

În următoarea secțiune, vom explora implementări practice ale acestor concepte, concentrându-ne pe cum să folosești GRPO și RFTrans în propriile tale proiecte de dezvoltare a modelelor de limbaj.

## Quiz

### 1. Care este principala inovație a lucrării DeepSeek R1?

<Question
    choices={[
        {
            text: "Algoritmul GRPO care permite învățarea din preferințe cu și fără un model de recompensă",
            explain: "Corect! Inovația cheie a GRPO este capacitatea sa de a optimiza direct pentru rectificarea preferințelor, făcându-l mai eficient decât metodele RL tradiționale.",
            correct: true
        },
        {
            text: "Folosirea mai multor GPU-uri pentru antrenare decât orice model anterior",
            explain: "Inovația lucrării este în abordarea sa algoritmică (GRPO) mai degrabă decât în resursele computaționale folosite."
        },
        {
            text: "Crearea unui model de limbaj mai mare decât cele existente",
            explain: "Inovația constă în metodologia de antrenare și algoritmul GRPO, nu în dimensiunea modelului."
        }
    ]}
/>

### 2. Care sunt cele patru faze ale procesului de antrenare DeepSeek R1?

<Question
    choices={[
        {
            text: "Pornire la Rece, RL de Raționament, Eșantionare prin Respingere și RL Diversă",
            explain: "Corect! Acestea sunt exact cele patru faze descrise în lucrare, fiecare servind un scop specific în pipeline-ul de antrenare.",
            correct: true
        },
        {
            text: "Pre-antrenare, Ajustare Fină, Testare și Implementare",
            explain: "Lucrarea descrie în mod specific patru faze diferite: Pornire la Rece (fundația calității), RL de Raționament (construirea capacităților), Eșantionare prin Respingere (controlul calității) și RL Diversă (alinierea largă)."
        },
        {
            text: "Colectarea Datelor, Antrenarea Modelului, Evaluarea și Optimizarea",
            explain: "Lucrarea descrie un proces specific cu patru faze care include fazele de Pornire la Rece, RL de Raționament, Eșantionare prin Respingere și RL Diversă."
        }
    ]}
/>

### 3. Ce este fenomenul 'Momentul Aha' în antrenarea R1-Zero?

<Question
    choices={[
        {
            text: "Un proces în care modelul recunoaște erorile, se auto-corectează și explică corecturile sale",
            explain: "Corect! Lucrarea descrie aceasta ca un proces cu patru pași: încercare inițială, recunoașterea erorilor, auto-corecția și explicația îmbunătățirii.",
            correct: true
        },
        {
            text: "Punctul în care modelul atinge performanța la nivel uman",
            explain: "'Momentul Aha' se referă în mod specific la capacitatea modelului de a recunoaște și corecta propriile greșeli, similar cu realizările umane în rezolvarea problemelor."
        },
        {
            text: "Când modelul își completează procesul de antrenare",
            explain: "'Momentul Aha' se referă la capacitatea emergentă a modelului de a recunoaște erorile și de a se auto-corecta în timpul rezolvării problemelor, nu despre completarea antrenării."
        }
    ]}
/>

### 4. Cum funcționează formarea grupurilor în GRPO?

<Question
    choices={[
        {
            text: "Generează multiple soluții (4-16) pentru aceeași problemă și le evaluează împreună",
            explain: "Corect! GRPO generează multiple încercări de a rezolva aceeași problemă, de obicei 4, 8 sau 16 încercări diferite, care sunt apoi evaluate ca grup.",
            correct: true
        },
        {
            text: "Combină multiple modele într-un ansamblu",
            explain: "GRPO folosește un singur model pentru a genera multiple încercări de soluție, nu un ansamblu de modele diferite."
        },
        {
            text: "Împarte datele de antrenare în grupuri diferite",
            explain: "Formarea grupurilor în GRPO implică generarea de multiple soluții pentru aceeași problemă, nu împărțirea datelor de antrenare."
        }
    ]}
/>

### 5. Care este diferența cheie între DeepSeek-R1-Zero și DeepSeek-R1?

<Question
    choices={[
        {
            text: "R1-Zero folosește RL pur în timp ce R1 combină RL cu ajustarea fină supervizată",
            explain: "Corect! După cum se arată în tabelul de comparație, R1-Zero folosește antrenare RL pură în timp ce R1 folosește o abordare multi-fază combinând ajustarea fină supervizată cu RL, rezultând în consistență mai bună a limbajului.",
            correct: true
        },
        {
            text: "R1-Zero este mai mic decât R1",
            explain: "Diferența este în abordările lor de antrenare (RL pur vs. multi-fază), nu în dimensiunile modelelor lor."
        },
        {
            text: "R1-Zero a fost antrenat pe mai puține date",
            explain: "Distincția cheie este metodologia lor de antrenare: RL pur pentru R1-Zero versus o abordare combinată SFT și RL pentru R1."
        }
    ]}
/> 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter12/3.mdx" />

### Exercițiu Practic: GRPO cu Unsloth
https://huggingface.co/learn/course/ro/chapter12/6.md

# Exercițiu Practic: GRPO cu Unsloth

În acest exercițiu, vei ajusta fin un model cu GRPO (Optimizarea Relativă a Politicii de Grup) folosind Unsloth, pentru a îmbunătăți capacitățile de raționament ale unui model. Am acoperit GRPO în [Capitolul 3](/course/chapter3/3).

Unsloth este o bibliotecă care accelerează ajustarea fină a LLM-urilor, făcând posibilă antrenarea modelelor mai repede și cu mai puține resurse computaționale. Unsloth se conectează la TRL, deci vom construi pe ceea ce am învățat în secțiunile anterioare, și o vom adapta pentru specificațiile Unsloth.

<Tip>

Acest exercițiu poate fi rulat pe un GPU T4 Google Colab gratuit. Pentru cea mai bună experiență, urmărește notebook-ul legat mai sus și încearcă-l singur.

</Tip>

## Instalează dependențele

În primul rând, să instalăm bibliotecile necesare. Vom avea nevoie de Unsloth pentru ajustarea fină accelerată și vLLM pentru inferența rapidă.

```bash
pip install unsloth vllm
pip install --upgrade pillow
```

## Configurarea Unsloth

Unsloth oferă o clasă (`FastLanguageModel`) care integrează transformers cu optimizările Unsloth. Să o importăm:

```python
from unsloth import FastLanguageModel
```

Acum, să încărcăm modelul Google Gemma 3 1B Instruct și să-l configurăm pentru ajustarea fină:

```python
from unsloth import FastLanguageModel
import torch

max_seq_length = 1024  # Poate crește pentru urme de raționament mai lungi
lora_rank = 32  # Rang mai mare = mai inteligent, dar mai lent

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="google/gemma-3-1b-it",
    max_seq_length=max_seq_length,
    load_in_4bit=True,  # False pentru LoRA 16bit
    fast_inference=True,  # Activează inferența rapidă vLLM
    max_lora_rank=lora_rank,
    gpu_memory_utilization=0.6,  # Reduce dacă rămâi fără memorie
)

model = FastLanguageModel.get_peft_model(
    model,
    r=lora_rank,  # Alege orice număr > 0 ! Sugerat 8, 16, 32, 64, 128
    target_modules=[
        "q_proj",
        "k_proj",
        "v_proj",
        "o_proj",
        "gate_proj",
        "up_proj",
        "down_proj",
    ],  # Elimină QKVO dacă rămâi fără memorie
    lora_alpha=lora_rank,
    use_gradient_checkpointing="unsloth",  # Activează ajustarea fină pentru context lung
    random_state=3407,
)
```

Acest cod încarcă modelul în cuantizare 4-bit pentru a economisi memoria și aplică LoRA (Adaptarea de Rang Mic) pentru ajustarea fină eficientă. Parametrul `target_modules` specifică care straturi ale modelului să fie ajustate fin, și `use_gradient_checkpointing` permite antrenarea cu contexte mai lungi.

<Tip>

Nu vom acoperi detaliile LoRA în acest capitol, dar poți învăța mai multe în [Capitolul 11](/course/chapter11/3).

</Tip>

## Pregătirea Datelor

Pentru acest exercițiu, vom folosi setul de date GSM8K, care conține probleme de matematică de gimnaziu. Vom formata datele pentru a încuraja modelul să-și arate raționamentul înainte de a oferi un răspuns.

În primul rând, vom defini formatul prompt-urilor și răspunsurilor:

```python
# Definește prompt-ul de sistem care instruiește modelul să folosească un format specific
SYSTEM_PROMPT = """
Răspunde în următorul format:
<reasoning>
...
</reasoning>
<answer>
...
</answer>
"""

XML_COT_FORMAT = """\
<reasoning>
{reasoning}
</reasoning>
<answer>
{answer}
</answer>
"""
```

Acum, să pregătim setul de date:

```python
import re
from datasets import load_dataset, Dataset


# Funcții helper pentru a extrage răspunsuri din formate diferite
def extract_xml_answer(text: str) -> str:
    answer = text.split("<answer>")[-1]
    answer = answer.split("</answer>")[0]
    return answer.strip()


def extract_hash_answer(text: str) -> str | None:
    if "####" not in text:
        return None
    return text.split("####")[1].strip()


# Funcție pentru a pregăti setul de date GSM8K
def get_gsm8k_questions(split="train") -> Dataset:
    data = load_dataset("openai/gsm8k", "main")[split]
    data = data.map(
        lambda x: {
            "prompt": [
                {"role": "system", "content": SYSTEM_PROMPT},
                {"role": "user", "content": x["question"]},
            ],
            "answer": extract_hash_answer(x["answer"]),
        }
    )
    return data


dataset = get_gsm8k_questions()
```

Setul de date este pregătit prin extragerea răspunsului din setul de date și formatarea acestuia ca șir de caractere.

## Definirea Funcțiilor de Recompensă

După cum am discutat într-o [pagină anterioară](/course/chapter13/4), GRPO poate folosi funcții de recompensă pentru a ghida învățarea modelului bazată pe criterii verificabile precum lungimea și formatarea.

În acest exercițiu, vom defini mai multe funcții de recompensă care încurajează diferite aspecte ale unui raționament bun. De exemplu, vom recompensa modelul pentru oferirea unui răspuns întreg, și pentru urmarea formatului strict.

```python
# Funcția de recompensă care verifică dacă răspunsul este corect
def correctness_reward_func(prompts, completions, answer, **kwargs) -> list[float]:
    responses = [completion[0]["content"] for completion in completions]
    q = prompts[0][-1]["content"]
    extracted_responses = [extract_xml_answer(r) for r in responses]
    print(
        "-" * 20,
        f"Întrebare:\n{q}",
        f"\nRăspuns:\n{answer[0]}",
        f"\nRăspuns model:\n{responses[0]}",
        f"\nExtras:\n{extracted_responses[0]}",
    )
    return [2.0 if r == a else 0.0 for r, a in zip(extracted_responses, answer)]


# Funcția de recompensă care verifică dacă răspunsul este un întreg
def int_reward_func(completions, **kwargs) -> list[float]:
    responses = [completion[0]["content"] for completion in completions]
    extracted_responses = [extract_xml_answer(r) for r in responses]
    return [0.5 if r.isdigit() else 0.0 for r in extracted_responses]


# Funcția de recompensă care verifică dacă completarea urmează formatul strict
def strict_format_reward_func(completions, **kwargs) -> list[float]:
    pattern = r"^<reasoning>\n.*?\n</reasoning>\n<answer>\n.*?\n</answer>\n$"
    responses = [completion[0]["content"] for completion in completions]
    matches = [re.match(pattern, r) for r in responses]
    return [0.5 if match else 0.0 for match in matches]


# Funcția de recompensă care verifică dacă completarea urmează un format mai relaxat
def soft_format_reward_func(completions, **kwargs) -> list[float]:
    pattern = r"<reasoning>.*?</reasoning>\s*<answer>.*?</answer>"
    responses = [completion[0]["content"] for completion in completions]
    matches = [re.match(pattern, r) for r in responses]
    return [0.5 if match else 0.0 for match in matches]


# Funcția de recompensă care numără tag-urile XML și penalizează conținutul extra
def count_xml(text) -> float:
    count = 0.0
    if text.count("<reasoning>\n") == 1:
        count += 0.125
    if text.count("\n</reasoning>\n") == 1:
        count += 0.125
    if text.count("\n<answer>\n") == 1:
        count += 0.125
        count -= len(text.split("\n</answer>\n")[-1]) * 0.001
    if text.count("\n</answer>") == 1:
        count += 0.125
        count -= (len(text.split("\n</answer>")[-1]) - 1) * 0.001
    return count


def xmlcount_reward_func(completions, **kwargs) -> list[float]:
    contents = [completion[0]["content"] for completion in completions]
    return [count_xml(c) for c in contents]
```

Aceste funcții de recompensă servesc diferite scopuri:

| Funcția de Recompensă | Scopul |
|-----------------|---------|
| `correctness_reward_func` | Recompensează modelul când răspunsul său se potrivește cu răspunsul corect |
| `int_reward_func` | Recompensează modelul pentru oferirea unui răspuns numeric |
| `strict_format_reward_func` și `soft_format_reward_func` | Recompensează modelul pentru urmarea formatului specificat |
| `xmlcount_reward_func` | Recompensează utilizarea corectă a tag-urilor XML și penalizează conținutul extra după tag-urile de închidere |

## Antrenarea cu GRPO

Acum vom configura antrenorul GRPO cu modelul nostru, tokenizer-ul și funcțiile de recompensă. Această parte urmează aceeași abordare ca [exercițiul anterior](/course/chapter12/5).

```python
from trl import GRPOConfig, GRPOTrainer

max_prompt_length = 256

training_args = GRPOConfig(
    learning_rate=5e-6,
    adam_beta1=0.9,
    adam_beta2=0.99,
    weight_decay=0.1,
    warmup_ratio=0.1,
    lr_scheduler_type="cosine",
    optim="paged_adamw_8bit",
    logging_steps=1,
    per_device_train_batch_size=1,
    gradient_accumulation_steps=1,  # Crește la 4 pentru antrenare mai lină
    num_generations=6,  # Scade dacă rămâi fără memorie
    max_prompt_length=max_prompt_length,
    max_completion_length=max_seq_length - max_prompt_length,
    # num_train_epochs = 1, # Setează la 1 pentru o rulare completă de antrenare
    max_steps=250,
    save_steps=250,
    max_grad_norm=0.1,
    report_to="none",  # Poate folosi Weights & Biases
    output_dir="outputs",
)

trainer = GRPOTrainer(
    model=model,
    processing_class=tokenizer,
    reward_funcs=[
        xmlcount_reward_func,
        soft_format_reward_func,
        strict_format_reward_func,
        int_reward_func,
        correctness_reward_func,
    ],
    args=training_args,
    train_dataset=dataset,
)
```

`GRPOConfig` setează diferiți hiperparametri pentru antrenare:
- `use_vllm`: Activează inferența rapidă cu vLLM
- `learning_rate`: Controlează cât de repede învață modelul
- `num_generations`: Numărul de completări de generat pentru fiecare prompt
- `max_steps`: Numărul total de pași de antrenare de efectuat

Acum să începem antrenarea:

```python
trainer.train()
```

<Tip warning={true}>

Antrenarea poate dura ceva timp. S-ar putea să nu vezi recompensele crescând imediat - poate dura 150-200 de pași înainte să începi să vezi îmbunătățiri. Fii răbdător!

</Tip>

## Testarea Modelului

După antrenare, să testăm modelul nostru pentru a vedea cum performează. În primul rând, vom salva greutățile LoRA:

```python
model.save_lora("grpo_saved_lora")
```

Acum, să testăm modelul cu o întrebare nouă:

```python
from vllm import SamplingParams

text = tokenizer.apply_chat_template(
    [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": "Calculează pi."},
    ],
    tokenize=False,
    add_generation_prompt=True,
)

sampling_params = SamplingParams(
    temperature=0.8,
    top_p=0.95,
    max_tokens=1024,
)
output = (
    model.fast_generate(
        text,
        sampling_params=sampling_params,
        lora_request=model.load_lora("grpo_saved_lora"),
    )[0]
    .outputs[0]
    .text
)

print(output)
```

Ar trebui să vezi că modelul urmează acum formatul specificat, arătându-și raționamentul înainte de a oferi un răspuns.

## Salvarea Modelului

Unsloth oferă mai multe opțiuni pentru salvarea modelului tău ajustat fin, dar ne vom concentra pe cea mai comună.

```python
# Salvează cu precizie de 16 biți
model.save_pretrained_merged("model", tokenizer, save_method="merged_16bit")
```

## Încărcarea pe Hugging Face Hub

Vom încărca modelul pe Hugging Face Hub folosind metoda `push_to_hub_merged`. Această metodă ne permite să încărcăm modelul în multiple formate de cuantizare.
```python
# Încarcă pe Hugging Face Hub (necesită un token)
model.push_to_hub_merged(
    "numele-tau/numele-modelului",
    tokenizer,
    save_method="merged_16bit",
    token="token-ul-tau",
)
```

Unsloth suportă de asemenea salvarea în format GGUF pentru utilizare cu llama.cpp:

```python
model.push_to_hub_gguf(
    "numele-tau/numele-modelului",
    tokenizer,
    quantization_method=["q4_k_m", "q8_0", "q5_k_m"],
    token="token-ul-tau",
)
```

Fișierele GGUF pot fi folosite cu llama.cpp sau sisteme bazate pe UI precum Jan sau Open WebUI.

## Concluzie

În acest exercițiu, ai învățat cum să:
1. Configurezi Unsloth pentru ajustarea fină accelerată
2. Pregătești datele pentru antrenarea GRPO
3. Definești funcții de recompensă personalizate pentru a ghida învățarea modelului
4. Antrenezi un model folosind GRPO
5. Testezi modelul ajustat fin
6. Salvezi modelul în diverse formate

GRPO este o tehnică puternică pentru alinierea modelelor de limbaj cu comportamente specifice, iar Unsloth o face accesibilă chiar și pe hardware limitat. Prin combinarea mai multor funcții de recompensă, poți ghida modelul să urmeze un format specific în timp ce îi îmbunătățești și capacitățile de raționament.

Pentru mai multe informații și resurse, verifică:
- [Documentația Unsloth](https://docs.unsloth.ai/)
- [Discord Unsloth](https://discord.gg/unsloth)
- [GitHub Unsloth](https://github.com/unslothai/unsloth) 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter12/6.mdx" />

### Înțelegerea Avansată a Optimizării Relative a Politicii de Grup (GRPO) în DeepSeekMath
https://huggingface.co/learn/course/ro/chapter12/3a.md

# Înțelegerea Avansată a Optimizării Relative a Politicii de Grup (GRPO) în DeepSeekMath

<Tip>

Această secțiune se scufundă în detaliile tehnice și matematice ale GRPO. A fost scrisă de [Shirin Yamani](https://github.com/shirinyamani).

</Tip>

Să ne aprofundăm înțelegerea GRPO astfel încât să putem îmbunătăți procesul de antrenare al modelului nostru.

GRPO evaluează direct răspunsurile generate de model comparându-le în cadrul grupurilor de generare pentru a optimiza modelul de politică, în loc să antreneze un model de valoare separat (Critic). Această abordare duce la o reducere semnificativă a costului computațional!

GRPO poate fi aplicat la orice sarcină verificabilă unde corectitudinea răspunsului poate fi determinată. De exemplu, în raționamentul matematic, corectitudinea răspunsului poate fi ușor verificată prin compararea acestuia cu adevărul de bază.

Înainte de a ne scufunda în detaliile tehnice, să vizualizăm cum funcționează GRPO la un nivel înalt:

![deep](./img/2.jpg)

Acum că avem o prezentare vizuală, să descompunem cum funcționează GRPO pas cu pas.

## Algoritmul GRPO

Inovația principală a GRPO este abordarea sa de evaluare și învățare din multiple răspunsuri generate simultan. În loc să se bazeze pe un model de recompensă separat, compară rezultatele din același grup pentru a determina care ar trebui să fie întărite.

Să parcurgem fiecare pas al algoritmului în detaliu:

### Pasul 1: Eșantionarea Grupului

Primul pas este să genereze multiple răspunsuri posibile pentru fiecare întrebare. Aceasta creează un set divers de rezultate care pot fi comparate între ele.

Pentru fiecare întrebare $q$, modelul va genera $G$ rezultate (dimensiunea grupului) din politica antrenată: ${o_1, o_2, o_3, \dots, o_G}\pi_{\theta_{\text{old}}}$, $G=8$ unde fiecare $o_i$ reprezintă o completare din model.

#### Exemplu:

Pentru a face acest lucru concret, să ne uităm la o problemă aritmetică simplă:

- **Întrebarea** $q$ : $\text{Calculează}\space2 + 2 \times 6$
- **Rezultatele** $(G = 8)$: $\{o_1:14 \text{ (corect)}, o_2:16 \text{ (greșit)}, o_3:10 \text{ (greșit)}, \ldots, o_8:14 \text{ (corect)}\}$

Observă cum unele dintre răspunsurile generate sunt corecte (14) în timp ce altele sunt greșite (16 sau 10). Această diversitate este crucială pentru următorul pas.

### Pasul 2: Calculul Avantajului

Odată ce avem multiple răspunsuri, avem nevoie de o modalitate de a determina care sunt mai bune decât altele. Aici intervine calculul avantajului.

#### Distribuția Recompenselor:

În primul rând, atribuim un scor de recompensă fiecărui răspuns generat. În acest exemplu, vom folosi un model de recompensă, dar după cum am învățat în secțiunea anterioară, putem folosi orice funcție care returnează recompense.

Atribuie un scor RM fiecăruia dintre răspunsurile generate bazat pe corectitudine $r_i$ *(de exemplu, 1 pentru răspuns corect, 0 pentru răspuns greșit)* apoi pentru fiecare dintre $r_i$ calculează următoarea valoare de Avantaj

#### Formula Valorii Avantajului:

Ideea cheie a GRPO este că nu avem nevoie de măsuri absolute ale calității - putem compara rezultatele în cadrul aceluiași grup. Aceasta se face folosind standardizarea:

$$A_i = \frac{r_i - \text{mean}(\{r_1, r_2, \ldots, r_G\})}{\text{std}(\{r_1, r_2, \ldots, r_G\})}$$

#### Exemplu:

Continuând cu exemplul nostru aritmetic pentru același exemplu de mai sus, imaginează-ți că avem 8 răspunsuri, 4 dintre care sunt corecte și restul greșite, prin urmare:
- Media Grupului: $mean(r_i) = 0.5$
- Std: $std(r_i) = 0.53$
- Valoarea Avantajului:
	- Răspuns corect: $A_i = \frac{1 - 0.5}{0.53}= 0.94$
	- Răspuns greșit: $A_i = \frac{0 - 0.5}{0.53}= -0.94$

#### Interpretarea:  

Acum că am calculat valorile avantajului, să înțelegem ce înseamnă:

Această standardizare (adică ponderarea $A_i$) permite modelului să evalueze performanța relativă a fiecărui răspuns, ghidând procesul de optimizare să favorizeze răspunsurile care sunt mai bune decât media (recompensă mare) și să descurajeze pe cele care sunt mai rele.  De exemplu, dacă $A_i > 0$, atunci $o_i$ este un răspuns mai bun decât nivelul mediu din grupul său; și dacă $A_i < 0$, atunci $o_i$ atunci calitatea răspunsului este mai mică decât media (adică calitate/performanță slabă).

Pentru exemplul de mai sus, dacă $A_i = 0.94 \text{(rezultat corect)}$ atunci în timpul pașilor de optimizare probabilitatea sa de generare va fi crescută.

Cu valorile avantajului noastre calculate, suntem acum gata să actualizăm politica.

### Pasul 3: Actualizarea Politicii

Pasul final este să folosim aceste valori ale avantajului pentru a actualiza modelul nostru astfel încât să devină mai probabil să genereze răspunsuri bune în viitor.

Funcția țintă pentru actualizarea politicii este:

$$J_{GRPO}(\theta) = \left[\frac{1}{G} \sum_{i=1}^{G} \min \left( \frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)} A_i, \text{clip}\left( \frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)}, 1 - \epsilon, 1 + \epsilon \right) A_i \right)\right]- \beta D_{KL}(\pi_{\theta} || \pi_{ref})$$

Această formulă ar putea părea intimidantă la început, dar este construită din mai multe componente care fiecare servesc un scop important. Să le descompunem una câte una.

## Componentele Cheie ale Funcției Țintă

Funcția de actualizare GRPO combină mai multe tehnici pentru a asigura o învățare stabilă și eficientă. Să examinăm fiecare componentă:

### 1. Raportul de Probabilitate

Raportul de probabilitate este definit ca:

$\left(\frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)}\right)$ 

Intuitiv, formula compară cât de mult diferă probabilitatea de răspuns a modelului nou de probabilitatea de răspuns a modelului vechi în timp ce încorporează o preferință pentru răspunsuri care îmbunătățesc rezultatul așteptat.

#### Interpretarea:
- Dacă $\text{raport} > 1$, modelul nou atribuie o probabilitate mai mare răspunsului $o_i$​ decât modelul vechi.
- Dacă $\text{raport} < 1$, modelul nou atribuie o probabilitate mai mică lui $o_i$​ 

Acest raport ne permite să controlăm cât de mult se schimbă modelul la fiecare pas, ceea ce ne duce la următoarea componentă.

### 2. Funcția de Tăiere

Funcția de tăiere este definită ca:

$\text{clip}\left( \frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)}, 1 - \epsilon, 1 + \epsilon\right)$ 

Limitează raportul discutat mai sus să fie în intervalul $[1 - \epsilon, 1 + \epsilon]$ pentru a evita/controla schimbări drastice sau actualizări nebunești și să nu pășească prea departe de politica veche. Cu alte cuvinte, limitează cât de mult poate crește raportul de probabilitate pentru a ajuta la menținerea stabilității prin evitarea actualizărilor care împing modelul nou prea departe de cel vechi.

#### Exemplu $\space \text{să presupunem}(\epsilon = 0.2)$
Să ne uităm la două scenarii diferite pentru a înțelege mai bine această funcție de tăiere:

- **Cazul 1**: dacă noua politică are o probabilitate de 0.9 pentru un răspuns specific și vechea politică are o probabilitate de 0.5, înseamnă că acest răspuns este întărit de noua politică să aibă o probabilitate mai mare, dar într-o limită controlată care este tăierea pentru a-și strânge mâinile să nu devină drastică 
	- $\text{Raport}: \frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)} = \frac{0.9}{0.5} = 1.8  → \text{Clip}\space1.2$ (limita superioară 1.2) 
- **Cazul 2**: Dacă noua politică nu este în favoarea unui răspuns (probabilitate mai mică de exemplu 0.2), însemnând dacă răspunsul nu este benefic creșterea ar putea fi incorectă, și modelul ar fi penalizat.
	- $\text{Raport}: \frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)} = \frac{0.2}{0.5} = 0.4  →\text{Clip}\space0.8$ (limita inferioară 0.8)
#### Interpretarea:
- Formula încurajează modelul nou să favorizeze răspunsuri pe care modelul vechi le-a subponderat **dacă ele îmbunătățesc rezultatul**.
- Dacă modelul vechi deja favoriza un răspuns cu o probabilitate mare, modelul nou poate totuși să-l întărească **dar doar într-o limită controlată $[1 - \epsilon, 1 + \epsilon]$, $\text{(de exemplu, }\epsilon = 0.2, \space \text{deci} \space [0.8-1.2])$**.
- Dacă modelul vechi a supraestimat un răspuns care performează prost, modelul nou este **descurajat** să mențină acea probabilitate mare.
- Prin urmare, intuitiv, prin încorporarea raportului de probabilitate, funcția obiectiv asigură că actualizările politicii sunt proporționale cu avantajul $A_i$ în timp ce sunt moderate pentru a preveni schimbări drastice.

În timp ce funcția de tăiere ajută la prevenirea schimbărilor drastice, avem nevoie de încă o măsură de protecție pentru a ne asigura că modelul nostru nu deviază prea departe de comportamentul său original.

### 3. Divergența KL

Termenul de divergență KL este:

$\beta D_{KL}(\pi_{\theta} || \pi_{ref})$

În termenul de divergență KL, $\pi_{ref}$ este practic rezultatul modelului pre-actualizare, `per_token_logps` și $\pi_{\theta}$ este rezultatul modelului nou, `new_per_token_logps`. Teoretic, divergența KL este minimizată pentru a preveni modelul să devieze prea departe de comportamentul său original în timpul optimizării. Aceasta ajută să găsească un echilibru între îmbunătățirea performanței bazată pe semnalul de recompensă și menținerea coerenței. În acest context, minimizarea divergenței KL reduce riscul ca modelul să genereze text fără sens sau, în cazul raționamentului matematic, să producă răspunsuri extrem de incorecte.

#### Interpretarea
- O penalitate de divergență KL păstrează rezultatele modelului aproape de distribuția sa originală, prevenind schimbări extreme.
- În loc să derive către rezultate complet iraționale, modelul și-ar rafina înțelegerea în timp ce încă permite unele explorări

#### Definiția Matematică
Pentru cei interesați de detaliile matematice, să ne uităm la definiția formală:

Să ne amintim că distanța KL este definită după cum urmează:
$$D_{KL}(P || Q) = \sum_{x \in X} P(x) \log \frac{P(x)}{Q(x)}$$
În RLHF, cele două distribuții de interes sunt adesea distribuția versiunii noului model, P(x), și o distribuție a politicii de referință, Q(x).

#### Rolul Parametrului $\beta$

Coeficientul $\beta$ controlează cât de puternic impunem constrângerea divergenței KL:

-  **$\beta$ Mai Mare (Penalitate KL Mai Puternică)**
    - Mai multă constrângere asupra actualizărilor politicii. Modelul rămâne aproape de distribuția sa de referință.
    - Poate încetini adaptarea: Modelul ar putea avea dificultăți să exploreze răspunsuri mai bune.
- **$\beta$ Mai Mic (Penalitate KL Mai Slabă)**
    - Mai multă libertate să actualizeze politica: Modelul poate devia mai mult de la referință.
    - Adaptare mai rapidă dar risc de instabilitate: Modelul ar putea învăța comportamente de hack-uire a recompenselor.
	- Risc de supra-optimizare: Dacă modelul de recompensă este defectuos, politica ar putea genera rezultate fără sens.
- **Originala** [DeepSeekMath](https://arxiv.org/abs/2402.03300) lucrare a setat acest $\beta= 0.04$

Acum că înțelegem componentele GRPO, să vedem cum funcționează împreună într-un exemplu complet.

## Exemplu Lucrat cu GRPO

Pentru a ne solidifica înțelegerea GRPO, să parcurgem un exemplu complet de la început la sfârșit.

### Problema Exemplu

$$\text{Î: Calculează}\space2 + 2 \times 6$$

### Pasul 1: Eșantionarea Grupului

În primul rând, generăm multiple răspunsuri din modelul nostru:

Generează $(G = 8)$ răspunsuri, $4$ dintre care sunt răspunsul corect ($14, \text{recompensă=} 1$) și $4$ incorecte $\text{(recompensă= 0)}$, Prin urmare:

$${o_1:14(corect), o_2:10 (greșit), o_3:16 (greșit), ... o_G:14(corect)}$$

### Pasul 2: Calculul Avantajului

Apoi, calculăm valorile avantajului pentru a determina care răspunsuri sunt mai bune decât media:

- Media Grupului: 
$$mean(r_i) = 0.5$$
- Std: $$std(r_i) = 0.53$$
- Valoarea Avantajului:
	- Răspuns corect: $A_i = \frac{1 - 0.5}{0.53}= 0.94$
	- Răspuns greșit: $A_i = \frac{0 - 0.5}{0.53}= -0.94$

### Pasul 3: Actualizarea Politicii

În final, actualizăm modelul nostru pentru a întări răspunsurile corecte:

- Presupunând că probabilitatea vechii politici ($\pi_{\theta_{old}}$) pentru un rezultat corect $o_1$ este $0.5$ și noua politică o crește la $0.7$ atunci:
$$\text{Raport}: \frac{0.7}{0.5} = 1.4  →\text{după Clip}\space1.2 \space (\epsilon = 0.2)$$
- Apoi când funcția țintă este re-ponderată, modelul tinde să întărească generarea rezultatului corect, și $\text{Divergența KL}$ limitează deviația de la politica de referință.

Cu înțelegerea teoretică la locul ei, să vedem cum poate fi implementat GRPO în cod.

## Exemplu de Implementare

Să punem totul împreună într-un exemplu practic. Următorul cod demonstrează cum să implementezi GRPO în PyTorch.

### 1. Încărcarea Modelului și Generarea Răspunsurilor

În primul rând, trebuie să încărcăm un model și să generăm multiple răspunsuri pentru o întrebare dată:

```python
import torch
import torch.nn.functional as F
from transformers import AutoModelForCausalLM, AutoTokenizer

# Încarcă modelul și tokenizer-ul
model_name = "Qwen/Qwen2-Math-1.5B"
model = AutoModelForCausalLM.from_pretrained(model_name)
tokenizer = AutoTokenizer.from_pretrained(model_name)
model.eval()

# Mută modelul pe GPU dacă este disponibil
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)

# Prompt de intrare
prompt = "Rezolvă y = 2x + 1 pentru x = 2, y = "  # Răspuns corect: 5
inputs = tokenizer(prompt, return_tensors="pt", padding=True)
input_ids = inputs["input_ids"].to(device)  # Forma: (1, prompt_len)
attention_mask = inputs["attention_mask"].to(device)

# Pasul 1: Generează 8 răspunsuri (B = 2 grupuri, G = 4 răspunsuri per grup)
batch_size, num_generations = 2, 4
outputs = model.generate(
    input_ids=input_ids,  # Forma: (1, prompt_len)
    attention_mask=attention_mask,
    max_new_tokens=1,  # seq_len = 1 (un singur token per răspuns)
    num_return_sequences=batch_size * num_generations,  # 8 răspunsuri în total
    do_sample=True,
    top_k=10,
    temperature=0.7,
    pad_token_id=tokenizer.eos_token_id,
    return_dict_in_generate=True,
    output_scores=True,
)
```

această Generare inițială (Înainte de Orice Pași) va produce ceva de genul:

```text
Rezultatul 1: 5.0
Rezultatul 2: 6.0
Rezultatul 3: 7.0
Rezultatul 4: 5.0
Rezultatul 5: 10.0
Rezultatul 6: 2.0
Rezultatul 7: 5.0
Rezultatul 8: 5.0
```

### 2. Calcularea Recompenselor

Acum, trebuie să determinăm care răspunsuri sunt corecte și să atribuim recompense în consecință:

Cu GRPO, cu același prompt de eșantion, generăm multiple completări. Deci, de exemplu, pentru prompt-urile noastre de `"Rezolvă y = 2x + 1 pentru x = 2, y = "` și `Rezolvă y = 2x + 1 pentru x = 4, y = "` avem două grupuri de rezultate generate pentru prompt-ul dat unul este să zicem 
- `[5, 6, 7, 5]` și celălalt este 
- `[10, 2, 9, 9]` în timp ce răspunsul corect este 5 și 9. 

Observă că în practică aceste scoruri de recompensă sunt obținute printr-o funcție de recompensă bazată pe reguli care atribuie recompense bazate pe corectitudinea răspunsului sau un model mai complex bazat pe rețele neuronale care poate fi antrenat să atribuie recompense bazate pe corectitudinea răspunsului sau un amestec din ambele. Dar pentru simplitate să spunem că recompensa noastră per răspuns este 1 dacă răspunsul este corect și 0 dacă este greșit, prin urmare:  
```python
reward_1 = [1, 0, 0, 1]
reward_2 = [0, 0, 1, 1]
```
apoi obținem media și std-ul grupului de recompense;

```python
# Forma: (B * G,) = (8,) pentru că avem 2 grupuri de 4 generări pe care le aplatizăm
rewards = torch.tensor([1, 0, 0, 1, 0, 0, 1, 1], dtype=torch.float32)
num_generations = 4

# Recompense grupate: Forma (B, G) = 2, 4)
rewards_grouped = rewards.view(-1, num_generations)

# Media per grup: Forma (B,) = (2,)
mean_grouped_rewards = rewards_grouped.mean(dim=1)

# Std per grup: Forma (B,) = (2,)
std_grouped_rewards = rewards_grouped.std(dim=1)

# Difuzează pentru a se potrivi cu recompensele și normalizează: Forma (B * G,) = (8,)
# de ce avem nevoie să difuzăm? pentru că trebuie să calculăm valorile avantajului pentru fiecare răspuns din grup
mean_grouped_rewards = mean_grouped_rewards.repeat_interleave(num_generations, dim=0)
std_grouped_rewards = std_grouped_rewards.repeat_interleave(num_generations, dim=0)
```
aceasta va produce:
```text
Recompense Grupate: tensor([[1., 0., 0., 1.],
                        [0., 0., 1., 1.]])
Media per grup: tensor([0.5000, 0.5000])
Std per grup: tensor([0.5774, 0.5774])
Media Difuzată: tensor([0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000])
Std Difuzat: tensor([0.5774, 0.5774, 0.5774, 0.5774, 0.5774, 0.5774, 0.5774, 0.5774])
```
Acum putem calcula valorile avantajului pentru fiecare răspuns:
```python
# Avantaje: Forma (B * G,) = (8,)
advantages = (rewards - mean_grouped_rewards) / (std_grouped_rewards + 1e-8)
```
aceasta va produce:
```text
Avantaje: tensor([ 0.8659, -0.8660, -0.8660,  0.8659, -0.8660, -0.8660,  0.8659,  0.8659])
```
care vine din formula Avantajului de mai sus, deci:
```text
Pentru reward_1 = [1, 0, 0, 1]:
1 - 0.5 / 0.5774 ≈ 0.8659
0 - 0.5 / 0.5774 ≈ -0.8660
Pentru reward_2 = [0, 0, 1, 1]: Același model.
```
cu toate acestea, forma aici este `(B*G,) = (8,)` dar în practică, avem nevoie să avem forma de `(B, G) = (2, 4)` pentru a se potrivi cu forma logits, nu? Prin urmare, trebuie să unsqueeze tensor-ul avantajelor pentru a avea forma de `(B*G, 1) = (8, 1)` pentru a se potrivi cu forma logits.
```python
# Forma (B * G, 1) = (8, 1) pentru a se potrivi cu forma logits
advantages = advantages.unsqueeze(1)
```
care va produce:
```text
Avantaje: tensor([[ 0.8659],
                    [-0.8660],
                    [-0.8660],
                    [ 0.8659],
                    [-0.8660],
                    [-0.8660],
                    [ 0.8659],
                    [ 0.8659]])
```
acum suntem bine, să trecem la următorul pas de actualizare a modelului de politică bazat pe valorile avantajului.

### 3. Actualizarea Politicii
În final, folosim valorile avantajului pentru a actualiza modelul nostru:

```python
# Calculează raportul de probabilitate între politicile noi și vechi
ratio = torch.exp(
    new_per_token_logps - per_token_logps
)  # Forma: (B*G, seq_len) seq_len este lungimea rezultatului adică numărul de token-uri generate deci aici pentru simplitate să presupunem că este 1 # (8, 1)
```

Observă că `per_token_logps` poate fi obținut prin trecerea rezultatelor generate la model și obținerea logits-urilor și apoi aplicarea funcției softmax pentru a obține probabilitățile `F.softmax(logits, dim=-1)`.

```python
# Funcția de Tăiere
eps = self.cliprange  # de exemplu 0.2
pg_losses1 = -advantages * ratio  # Forma: (B*G, seq_len)  #(8, 1)
pg_losses2 = -advantages * torch.clamp(
    ratio, 1.0 - eps, 1.0 + eps
)  # Forma: (B*G, seq_len) #(8, 1)
pg_loss_max = torch.max(pg_losses1, pg_losses2)  # Forma: (B*G, seq_len) #(8, 1)


# Acum Combină cu penalitatea KL # Forma: (B*G, seq_len) #(8, 1)
per_token_loss = pg_loss_max + self.beta * per_token_kl
```

`per_token_kl` poate fi de asemenea calculat după cum urmează:

```python
# Forma: (B*G, seq_len) #(8, 1)
per_token_kl = F.kl_div(
    F.log_softmax(new_per_token_logps, dim=-1),
    F.softmax(per_token_logps, dim=-1),
    reduction="none",
).sum(dim=-1, keepdim=True)
```

Exemplul complet poate fi găsit [aici](./basic_example.py). GRPO este de asemenea implementat de excelenta echipă TRL, poți verifica implementarea [TRL/GRPO_trainer](https://github.com/huggingface/trl/blob/main/trl/trainer/grpo_trainer.py) pentru mai multe detalii.

## Rezumat și Pași Următori

Felicitări! Acum ai învățat despre Optimizarea Relativă a Politicii de Grup (GRPO). Pentru a recapitula ce am acoperit:

1. GRPO compară multiple rezultate în cadrul unui grup pentru a determina care sunt mai bune decât altele, fără a necesita un model de valoare separat.
2. Calculul avantajului standardizează recompensele pentru a identifica care răspunsuri sunt peste sau sub medie.
3. Actualizarea politicii folosește o funcție obiectiv tăiată cu o penalitate de divergență KL pentru a asigura învățarea stabilă.

Această abordare este deosebit de puternică pentru sarcinile de raționament matematic, unde corectitudinea poate fi verificată obiectiv. Metoda GRPO permite antrenare mai eficientă comparativ cu abordările RLHF tradiționale care necesită un model critic separat.

Pe măsură ce continui să explorezi GRPO, consideră să experimentezi cu diferite dimensiuni de grup, funcții de recompensă și coeficienți de penalitate KL pentru a vedea cum afectează performanța modelului tău.

Antrenare fericită! 🚀

## Referințe

1. [Cartea RLHF de Nathan Lambert](https://github.com/natolambert/rlhf-book)
2. [Raportul Tehnic DeepSeek-V3](https://huggingface.co/papers/2412.19437)
3. [DeepSeekMath](https://huggingface.co/papers/2402.03300) 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter12/3a.mdx" />

### Implementarea GRPO în TRL
https://huggingface.co/learn/course/ro/chapter12/4.md

# Implementarea GRPO în TRL

În această pagină, vom învăța cum să implementăm Optimizarea Relativă a Politicii de Grup (GRPO) folosind biblioteca Transformer Reinforcement Learning (TRL). Ne vom concentra pe implementarea practică cu cod minimal.

Vom explora conceptele centrale ale GRPO așa cum sunt întruchipate în GRPOTrainer din TRL, folosind fragmente din documentația oficială TRL pentru a ne ghida.

<Tip>

Acest capitol este destinat începătorilor TRL. Dacă ești deja familiar cu TRL, ai putea de asemenea să consulți [implementarea Open R1](https://github.com/huggingface/open-r1/blob/main/src/open_r1/grpo.py) a GRPO.

</Tip>

În primul rând, să ne reamintim unele dintre conceptele importante ale algoritmului GRPO:

- Formarea Grupului: Modelul generează multiple completări pentru fiecare prompt.
- Învățarea Preferințelor: Modelul învață dintr-o funcție de recompensă care compară grupuri de completări.
- Configurația Antrenamentului: Modelul folosește o configurație pentru a controla procesul de antrenare.

Ce trebuie să facem pentru a implementa GRPO?

- Să definim un set de date de prompt-uri.
- Să definim o funcție de recompensă care ia o listă de completări și returnează o listă de recompense.
- Să configurăm procesul de antrenare cu un GRPOConfig.
- Să antrenăm modelul folosind GRPOTrainer.

Iată un exemplu minimal pentru a începe antrenamentul GRPO:

```python
from trl import GRPOTrainer, GRPOConfig
from datasets import load_dataset

# 1. Încarcă setul tău de date
dataset = load_dataset("setul_tau_de_date", split="train")


# 2. Definește o funcție de recompensă simplă
def reward_func(completions, **kwargs):
    """Exemplu: Recompensează completările mai lungi"""
    return [float(len(completion)) for completion in completions]


# 3. Configurează antrenamentul
training_args = GRPOConfig(
    output_dir="output",
    num_train_epochs=3,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=2,
    logging_steps=10,
)

# 4. Inițializează și antrenează
trainer = GRPOTrainer(
    model="modelul_tau",  # de exemplu "Qwen/Qwen2-0.5B-Instruct"
    args=training_args,
    train_dataset=dataset,
    reward_funcs=reward_func,
)
trainer.train()
```

## Componentele Cheie

### 1. Formatul Setului de Date

Setul tău de date ar trebui să conțină prompt-uri la care modelul va răspunde. Antrenorul GRPO va genera multiple completări pentru fiecare prompt și va folosi funcția de recompensă pentru a le compara.

### 2. Funcția de Recompensă

Funcția de recompensă este crucială - determină cum învață modelul. Iată două exemple practice:

```python
# Exemplul 1: Recompensă bazată pe lungimea completării
def reward_length(completions, **kwargs):
    return [float(len(completion)) for completion in completions]


# Exemplul 2: Recompensă bazată pe potrivirea unui model
import re


def reward_format(completions, **kwargs):
    pattern = r"^<think>.*?</think><answer>.*?</answer>$"
    return [1.0 if re.match(pattern, c) else 0.0 for c in completions]
```

### 3. Configurația Antrenamentului

Parametrii cheie de considerat în `GRPOConfig`:

```python
training_args = GRPOConfig(
    # Parametrii esențiali
    output_dir="output",
    num_train_epochs=3,
    num_generation=4,  # Numărul de completări de generat pentru fiecare prompt
    per_device_train_batch_size=4,  # Vrem să obținem toate generările într-un lot de dispozitiv
    # Opțional dar util
    gradient_accumulation_steps=2,
    learning_rate=1e-5,
    logging_steps=10,
    # Specific GRPO (opțional)
    use_vllm=True,  # Accelerează generarea
)
```

Parametrul `num_generation` este deosebit de important pentru GRPO deoarece definește dimensiunea grupului - câte completări diferite va genera modelul pentru fiecare prompt. Acesta este un diferențiator cheie de alte metode RL:

- Prea mic (de exemplu, 2-3): S-ar putea să nu ofere suficientă diversitate pentru comparații semnificative
- Recomandat (4-16): Oferă un echilibru bun între diversitate și eficiența computațională
- Valori mai mari: Pot îmbunătăți învățarea dar cresc semnificativ costul computațional

Dimensiunea grupului ar trebui aleasă în funcție de resursele tale computaționale și complexitatea sarcinii tale. Pentru sarcini simple, grupuri mai mici (4-8) pot fi suficiente, în timp ce sarcinile de raționament mai complexe ar putea beneficia de grupuri mai mari (8-16).

## Sfaturi pentru Succes

1. **Gestionarea Memoriei**: Ajustează `per_device_train_batch_size` și `gradient_accumulation_steps` în funcție de memoria GPU-ului tău.
2. **Viteza**: Activează `use_vllm=True` pentru generare mai rapidă dacă modelul tău este suportat.
3. **Monitorizarea**: Urmărește metricile înregistrate în timpul antrenamentului:
   - `reward`: Recompensa medie pe completări
   - `reward_std`: Deviația standard în cadrul grupurilor de recompense
   - `kl`: Divergența KL de la modelul de referință

## Designul Funcției de Recompensă

Lucrarea DeepSeek R1 demonstrează mai multe abordări eficiente pentru designul funcției de recompensă pe care le poți adapta pentru propria ta implementare GRPO:

### 1. Recompense Bazate pe Lungime

Una dintre cele mai ușoare recompense de implementat este o recompensă bazată pe lungime. Poți recompensa completări mai lungi:

```python
def reward_len(completions, **kwargs):
    ideal_length = 20
    return [-abs(ideal_length - len(completion)) for completion in completions]
```

Această funcție de recompensă penalizează completările care sunt prea scurte sau prea lungi, încurajând modelul să genereze completări care sunt aproape de lungimea ideală de 20 de token-uri.

<!-- # TODO: update links when PR is merged -->

<iframe 
    src="https://marimo.app/gh/huggingface/notebooks/main/e?entrypoint=course%2Fen%2Fchapter13%2Fgrpo_length.py&embed=true&show-chrome=false"
    title="Marimo Notebook"
    width="100%"
    height="800px"
    frameBorder="0"
    allow="clipboard-write"
></iframe>

## 2. Recompense Bazate pe Reguli pentru Sarcini Verificabile

Pentru sarcini cu răspunsuri obiectiv corecte (cum ar fi matematica sau codarea), poți implementa funcții de recompensă bazate pe reguli:

```python
def problem_reward(completions, answers, **kwargs):
    """Funcție de recompensă pentru probleme de matematică cu răspunsuri verificabile
    completions: lista de completări de evaluat
    answers: lista de răspunsuri la problemele din setul de date
    """

    rewards = []
    for completion, correct_answer in zip(completions, answers):
        # Extrage răspunsul din completare
        try:
            # Acesta este un exemplu simplificat - ai avea nevoie de parsing corespunzător
            answer = extract_final_answer(completion)
            # Recompensă binară: 1 pentru corect, 0 pentru incorect
            reward = 1.0 if answer == correct_answer else 0.0
            rewards.append(reward)
        except:
            # Dacă nu putem parsa un răspuns, dăm o recompensă mică
            rewards.append(0.0)

    return rewards
```

<!-- # TODO: update links when PR is merged -->

<iframe 
    src="https://marimo.app/gh/huggingface/notebooks/main/e?entrypoint=course%2Fen%2Fchapter13%2Fgrpo_math.py&embed=true&show-chrome=false"
    title="Marimo Notebook"
    width="100%"
    height="800px"
    frameBorder="0"
    allow="clipboard-write"
></iframe>

## 3. Recompense Bazate pe Format

Poți de asemenea să recompensezi formatarea corespunzătoare, care a fost importantă în antrenamentul DeepSeek R1:

```python
def format_reward(completions, **kwargs):
    """Recompensează completările care urmează formatul dorit"""
    # Exemplu: Verifică dacă completarea urmează un format gândește-apoi-răspunde
    pattern = r"<think>(.*?)</think>\s*<answer>(.*?)</answer>"

    rewards = []
    for completion in completions:
        match = re.search(pattern, completion, re.DOTALL)
        if match:
            # Verifică dacă există conținut substanțial în ambele secțiuni
            think_content = match.group(1).strip()
            answer_content = match.group(2).strip()

            if len(think_content) > 20 and len(answer_content) > 0:
                rewards.append(1.0)
            else:
                rewards.append(
                    0.5
                )  # Recompensă parțială pentru format corect dar conținut limitat
        else:
            rewards.append(0.0)  # Nicio recompensă pentru format incorect

    return rewards
```

<!-- # TODO: update links when PR is merged -->

<iframe 
    src="https://marimo.app/gh/huggingface/notebooks/main/e?entrypoint=course%2Fen%2Fchapter13%2Fgrpo_format.py&embed=true&show-chrome=false"
    title="Marimo Notebook"
    width="100%"
    height="800px"
    frameBorder="0"
    allow="clipboard-write"
></iframe>

Aceste exemple demonstrează cum poți implementa funcții de recompensă inspirate din procesul de antrenare DeepSeek R1, concentrându-se pe corectitudine, formatare și semnale combinate.

## Asta e tot!

În următoarea secțiune, vei urma un exercițiu pentru a implementa GRPO în TRL. 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter12/4.mdx" />

### În curând...
https://huggingface.co/learn/course/ro/chapter12/7.md

# În curând...

Acest capitol se desfășoară acum ca o cohortă live! Dacă ai terminat materialul până acum, iată la ce să te aștepți:

## Programul Cursului

| Data | Unitatea |
|------|------|
| ~7 Martie, 2025~ | ~Examen Fără Cod și Certificare~ |
| ~14 Martie, 2025~ | ~Următorul Exercițiu Practic~ |
| 21 Martie, 2025 | Revizuirea interactivă a codului |
| Aprilie 2025 | Mai mult material scris despre construirea modelelor de raționament |
| Aprilie 2025 | Sesiuni live despre construirea Open R1 |
| Aprilie 2025 | Examen de Cod și Certificare |

## Să Rămâi la Curent

Dacă vrei să urmezi cursul, urmărește [The Reasoning Course](https://huggingface.co/reasoning-course) și alătură-te [comunității Discord](https://discord.gg/F3vZujJH)! 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter12/7.mdx" />

### Introducere în Învățarea prin Întărire și rolul acesteia în LLM-uri
https://huggingface.co/learn/course/ro/chapter12/2.md

# Introducere în Învățarea prin Întărire și rolul acesteia în LLM-uri

Bun venit la prima pagină!

Vom începe călătoria noastră în lumea captivantă a Învățării prin Întărire (RL) și vom descoperi cum aceasta revoluționează modul în care antrenăm Modelele de Limbaj precum cele pe care le-ai putea folosi în fiecare zi.

<Tip>

În acest capitol, ne concentrăm pe învățarea prin întărire pentru modelele de limbaj. Cu toate acestea, învățarea prin întărire este un domeniu larg cu multe aplicații dincolo de modelele de limbaj. Dacă ești interesat să înveți mai multe despre învățarea prin întărire, ar trebui să consulți [Cursul de Învățare prin Întărire Profundă](https://huggingface.co/courses/deep-rl-course/en/unit1/introduction).

</Tip>

Această pagină îți va oferi o introducere prietenoasă și clară în RL, chiar dacă nu ai întâlnit-o niciodată înainte. Vom descompune ideile principale și vom vedea de ce RL devine atât de important în domeniul Modelelor Mari de Limbaj (LLM-uri).

## Ce este Învățarea prin Întărire (RL)?

Imaginează-ți că antrenezi un câine. Vrei să-l înveți să stea. Ai putea spune "Stai!" și apoi, dacă câinele stă, îi dai o recompensă și îl lauzi. Dacă nu stă, ai putea să-l ghidezi blând sau doar să încerci din nou. De-a lungul timpului, câinele învață să asocieze statul cu recompensa pozitivă (recompensa și lauda) și este mai probabil să stea când spui "Stai!" din nou. În învățarea prin întărire, ne referim la acest feedback ca **recompensă**.

Aceasta, pe scurt, este ideea de bază din spatele Învățării prin Întărire! În loc de un câine, avem un **model de limbaj** (în învățarea prin întărire, îl numim **agent**), și în loc de tine, avem **mediul** care oferă feedback.

![RL terms Process](https://huggingface.co/reasoning-course/images/resolve/main/grpo/3.jpg)

Să descompunem elementele cheie ale RL:

### Agent

Acesta este cel care învață. În exemplul cu câinele, câinele este agentul. În contextul LLM-urilor, LLM-ul însuși devine agentul pe care vrem să-l antrenăm. Agentul este cel care ia decizii și învață din mediu și din recompensele sale.

### Mediu

Acesta este lumea în care trăiește și interacționează agentul. Pentru câine, mediul este casa ta și tu. Pentru un LLM, mediul este puțin mai abstract – ar putea fi utilizatorii cu care interacționează, sau un scenariu simulat pe care îl configurăm pentru el. Mediul oferă feedback agentului.

### Acțiune

Acestea sunt alegerile pe care agentul le poate face în mediu. Acțiunile câinelui sunt lucruri precum "stai", "ridică-te", "latră", etc. Pentru un LLM, acțiunile ar putea fi generarea de cuvinte într-o propoziție, alegerea ce răspuns să dea la o întrebare, sau deciderea cum să răspundă într-o conversație.

### Recompensă

Acesta este feedback-ul pe care mediul îl oferă agentului după ce acesta întreprinde o acțiune. Recompensele sunt de obicei numere.

**Recompensele pozitive** sunt ca recompensele și laudele – îi spun agentului "bună treabă, ai făcut ceva corect!".

**Recompensele negative** (sau penalitățile) sunt ca un "nu" blând – îi spun agentului "nu a fost tocmai corect, încearcă altceva". Pentru câine, recompensa este răsplata.

Pentru un LLM, recompensele sunt concepute să reflecte cât de bine se descurcă LLM-ul la o sarcină specifică – poate fi cât de util, adevărat sau inofensiv este răspunsul său.

### Politică

Aceasta este strategia agentului pentru alegerea acțiunilor. Este ca înțelegerea câinelui despre ce ar trebui să facă când spui "Stai!". În RL, politica este ceea ce încercăm cu adevărat să învățăm și să îmbunătățim. Este un set de reguli sau o funcție care îi spune agentului ce acțiune să întreprindă în diferite situații. Inițial, politica ar putea fi aleatorie, dar pe măsură ce agentul învață, politica devine mai bună la alegerea acțiunilor care duc la recompense mai mari.

## Procesul RL: Încercări și Erori

![RL Process](https://huggingface.co/reasoning-course/images/resolve/main/grpo/1.jpg)

Învățarea prin Întărire se întâmplă printr-un proces de încercări și erori:

| Pas | Proces | Descriere |
|------|---------|-------------|
| 1. Observație | Agentul observă mediul | Agentul preia informații despre starea sa actuală și împrejurimile |
| 2. Acțiune | Agentul întreprinde o acțiune bazată pe politica sa actuală | Folosind strategia sa învățată (politica), agentul decide ce să facă în continuare |
| 3. Feedback | Mediul îi oferă agentului o recompensă | Agentul primește feedback despre cât de bună sau rea a fost acțiunea sa |
| 4. Învățare | Agentul își actualizează politica bazată pe recompensă | Agentul își ajustează strategia - întărind acțiunile care au dus la recompense mari și evitând pe cele care au dus la recompense mici |
| 5. Iterație | Repetă procesul | Acest ciclu continuă, permițând agentului să-și îmbunătățească continuu luarea deciziilor |

Gândește-te la învățarea mersului pe bicicletă. Ai putea să te legeni și să cazi la început (recompensă negativă!). Dar când reușești să te echilibrezi și să pedalezi lin, te simți bine (recompensă pozitivă!). Îți ajustezi acțiunile bazat pe acest feedback – înclinându-te puțin, pedalând mai repede, etc. – până când înveți să mergi bine. RL este similar – se referă la învățarea prin interacțiune și feedback.

## Rolul RL în Modelele Mari de Limbaj (LLM-uri)

Acum, de ce este RL atât de important pentru Modelele Mari de Limbaj?

Ei bine, antrenarea LLM-urilor cu adevărat bune este complicată. Le putem antrena pe cantități masive de text de pe internet, și devin foarte bune la prezicerea următorului cuvânt într-o propoziție. Astfel învață să genereze text fluent și corect din punct de vedere gramatical, după cum am învățat în [capitolul 2](/course/chapter2/1).

Cu toate acestea, a fi doar fluent nu este suficient. Vrem ca LLM-urile noastre să fie mai mult decât bune la înșiruirea cuvintelor. Vrem ca ele să fie:

* **Utile:** Să ofere informații utile și relevante.  
* **Inofensive:** Să evite generarea de conținut toxic, părtinitor sau dăunător.  
* **Aliniate cu Preferințele Umane:** Să răspundă în moduri pe care oamenii le găsesc naturale, utile și captivante.

Metodele de pre-antrenare LLM, care se bazează în principal pe prezicerea următorului cuvânt din datele text, uneori nu reușesc în aceste aspecte.

În timp ce antrenamentul supervizat este excelent la producerea de rezultate structurate, poate fi mai puțin eficient la producerea de răspunsuri utile, inofensive și aliniate. Explorăm antrenamentul supervizat în [capitolul 11](/course/chapter11/1).

Modelele ajustate fin ar putea genera text fluent și structurat care este încă factual incorect, părtinitor, sau nu răspunde cu adevărat la întrebarea utilizatorului într-un mod util.

**Intră Învățarea prin Întărire!** RL ne oferă o modalitate de a ajusta fin aceste LLM-uri pre-antrenate pentru a atinge mai bine aceste calități dorite. Este ca să-i dai câinelui nostru LLM antrenament suplimentar pentru a deveni un companion bine comportat și util, nu doar un câine care știe să latre fluent!

## Învățarea prin Întărire din Feedback Uman (RLHF)

O tehnică foarte populară pentru alinierea modelelor de limbaj este **Învățarea prin Întărire din Feedback Uman (RLHF)**. În RLHF, folosim feedback-ul uman ca proxy pentru semnalul de "recompensă" în RL. Iată cum funcționează:

1. **Obține Preferințe Umane:** Am putea cere oamenilor să compare diferite răspunsuri generate de LLM pentru aceeași intrare și să ne spună ce răspuns preferă. De exemplu, am putea arăta unui om două răspunsuri diferite la întrebarea "Care este capitala Franței?" și să îl întrebăm "Care răspuns este mai bun?".

2. **Antrenează un Model de Recompensă:** Folosim aceste date de preferință umană pentru a antrena un model separat numit **model de recompensă**. Acest model de recompensă învață să prezică ce fel de răspunsuri vor prefera oamenii. Învață să evalueze răspunsurile bazat pe utilitate, inofensivitate și alinierea cu preferințele umane.

3. **Ajustează fin LLM-ul cu RL:** Acum folosim modelul de recompensă ca mediu pentru agentul nostru LLM. LLM-ul generează răspunsuri (acțiuni), și modelul de recompensă evaluează aceste răspunsuri (oferă recompense). În esență, antrenăm LLM-ul să producă text pe care modelul nostru de recompensă (care a învățat din preferințele umane) îl consideră bun.

![RL Basic Concept](https://huggingface.co/reasoning-course/images/resolve/main/grpo/2.jpg)  

Dintr-o perspectivă generală, să privim beneficiile folosirii RL în LLM-uri:

| Beneficiu | Descriere |
|---------|-------------|
| Control Îmbunătățit | RL ne permite să avem mai mult control asupra tipului de text pe care îl generează LLM-urile. Le putem ghida să producă text care este mai aliniat cu obiective specifice, cum ar fi a fi util, creativ sau concis. |
| Alinierea Îmbunătățită cu Valorile Umane | RLHF, în particular, ne ajută să aliniată LLM-urile cu preferințele umane complexe și adesea subiective. Este greu să scrii reguli pentru "ce face un răspuns bun," dar oamenii pot judeca și compara ușor răspunsurile. RLHF permite modelului să învețe din aceste judecăți umane. |
| Reducerea Comportamentelor Nedorite | RL poate fi folosit pentru a reduce comportamentele negative în LLM-uri, cum ar fi generarea de limbaj toxic, răspândirea dezinformării sau exhibarea prejudecăților. Prin conceperea recompenselor care penalizează aceste comportamente, putem îndemna modelul să le evite. |

Învățarea prin Întărire din Feedback Uman a fost folosită pentru a antrena multe dintre cele mai populare LLM-uri de astăzi, cum ar fi GPT-4 de la OpenAI, Gemini de la Google și R1 de la DeepSeek. Există o gamă largă de tehnici pentru RLHF, cu grade variate de complexitate și sofisticare. În acest capitol, ne vom concentra pe Optimizarea Relativă a Politicii de Grup (GRPO), care este o tehnică pentru RLHF care s-a dovedit eficientă la antrenarea LLM-urilor care sunt utile, inofensive și aliniate cu preferințele umane.

## De ce ar trebui să ne pese de GRPO (Optimizarea Relativă a Politicii de Grup)?

Există multe tehnici pentru RLHF, dar acest curs se concentrează pe GRPO pentru că reprezintă un progres semnificativ în învățarea prin întărire pentru modelele de limbaj.

Să considerăm pe scurt două dintre celelalte tehnici populare pentru RLHF:

- Optimizarea Politicii Proximale (PPO)
- Optimizarea Directă a Preferinței (DPO)

Optimizarea Politicii Proximale (PPO) a fost una dintre primele tehnici foarte eficiente pentru RLHF. Folosește o metodă de gradient de politică pentru a actualiza politica bazată pe recompensa de la un model de recompensă separat.

Optimizarea Directă a Preferinței (DPO) a fost dezvoltată mai târziu ca o tehnică mai simplă care elimină nevoia unui model de recompensă separat folosind datele de preferință direct. În esență, încadrează problema ca o sarcină de clasificare între răspunsurile alese și respinse.

<Tip>

DPO și PPO sunt algoritmi complecși de învățare prin întărire în sine, pe care nu îi vom acoperi în acest curs. Dacă ești interesat să înveți mai multe despre ei, poți consulta următoarele resurse:

- [Optimizarea Politicii Proximale](https://huggingface.co/docs/trl/main/en/ppo_trainer)
- [Optimizarea Directă a Preferinței](https://huggingface.co/docs/trl/main/en/dpo_trainer)

</Tip>

Spre deosebire de DPO și PPO, GRPO grupează eșantioane similare împreună și le compară ca grup. Abordarea bazată pe grup oferă gradienți mai stabili și proprietăți de convergență mai bune comparativ cu alte metode.

GRPO nu folosește date de preferință cum face DPO, ci în schimb compară grupuri de eșantioane similare folosind un semnal de recompensă de la un model sau funcție.

GRPO este flexibil în modul în care obține semnalele de recompensă - poate funcționa cu un model de recompensă (cum face PPO) dar nu necesită în mod strict unul. Aceasta pentru că GRPO poate încorpora semnale de recompensă de la orice funcție sau model care poate evalua calitatea răspunsurilor.

De exemplu, am putea folosi o funcție de lungime pentru a recompensa răspunsuri mai scurte, un rezolvitor matematic pentru a verifica corectitudinea soluției, sau o funcție de corectitudine factuală pentru a recompensa răspunsurile care sunt mai precise din punct de vedere factual. Această flexibilitate face GRPO deosebit de versatil pentru diferite tipuri de sarcini de aliniere.

---

Felicitări pentru completarea Modulului 1! Acum ai o introducere solidă în Învățarea prin Întărire și rolul său crucial în modelarea viitorului Modelelor Mari de Limbaj. Înțelegi conceptele de bază ale RL, de ce este folosit pentru LLM-uri, și ai fost introdus la GRPO, un algoritm cheie în acest domeniu.

În următorul modul, ne vom murdări mâinile și ne vom scufunda în lucrarea DeepSeek R1 pentru a vedea aceste concepte în acțiune!

## Quiz

### 1. Care sunt componentele cheie ale Învățării prin Întărire?

<Question
    choices={[
        {
            text: "Agent, Mediu, Acțiune, Recompensă și Politică",
            explain: "Corect! Acestea sunt componentele fundamentale care alcătuiesc un sistem de învățare prin întărire.",
            correct: true
        },
        {
            text: "Model, Date, Funcție de Pierdere și Optimizator",
            explain: "Acestea sunt componente mai des asociate cu învățarea supervizată."
        },
        {
            text: "Intrare, Ieșire și Straturi Ascunse",
            explain: "Acestea sunt componente ale arhitecturii rețelelor neuronale, nu specific componente RL."
        }
    ]}
/>

### 2. Care este principalul avantaj al RLHF pentru antrenarea modelelor de limbaj?

<Question
    choices={[
        {
            text: "Ajută la alinierea modelelor cu preferințele și valorile umane",
            explain: "Corect! RLHF folosește feedback-ul uman pentru a ghida modelele către un comportament mai util, inofensiv și aliniat.",
            correct: true
        },
        {
            text: "Face modelele să genereze text mai rapid",
            explain: "RLHF nu se concentrează în principal pe îmbunătățirea vitezei de generare."
        },
        {
            text: "Reduce utilizarea memoriei modelului",
            explain: "RLHF nu se concentrează pe eficiența modelului sau optimizarea memoriei."
        }
    ]}
/>

### 3. În contextul RL pentru LLM-uri, ce reprezintă o "acțiune"?

<Question
    choices={[
        {
            text: "Generarea de cuvinte sau alegerea răspunsurilor într-o conversație",
            explain: "Corect! Pentru LLM-uri, acțiunile implică de obicei decizii de generare a textului.",
            correct: true
        },
        {
            text: "Actualizarea greutăților modelului",
            explain: "Aceasta este parte din procesul de antrenare, nu o acțiune în contextul RL."
        },
        {
            text: "Procesarea token-urilor de intrare",
            explain: "Aceasta este parte din operațiunea modelului, nu o acțiune în contextul RL."
        }
    ]}
/>

### 4. Care este rolul recompenselor în antrenamentul RL al modelelor de limbaj?

<Question
    choices={[
        {
            text: "Să ofere feedback despre cât de bine se aliniază răspunsurile modelului cu comportamentul dorit",
            explain: "Corect! Recompensele ghidează modelul către generarea de răspunsuri mai utile, adevărate și apropiate.",
            correct: true
        },
        {
            text: "Să măsoare dimensiunea vocabularului modelului",
            explain: "Recompensele nu sunt folosite pentru a evalua cunoștințele vocabularului."
        },
        {
            text: "Să determine viteza de antrenare a modelului",
            explain: "Recompensele oferă feedback asupra calității răspunsului, nu asupra eficienței antrenamentului."
        }
    ]}
/>

### 5. Ce este o recompensă în contextul RL pentru LLM-uri?

<Question
    choices={[
        {
            text: "Un scor numeric care măsoară calitatea unui răspuns",
            explain: "Corect! Recompensele oferă feedback asupra calității răspunsului, ghidând modelul către comportamentul dorit.",
            correct: true
        },
        {
            text: "O funcție care generează răspunsuri",
            explain: "Recompensele sunt feedback asupra calității răspunsului, nu procesul de generare în sine."
        },
        {
            text: "Un model care evaluează calitatea răspunsurilor",
            explain: "Recompensele sunt feedback asupra calității răspunsului, nu un model de evaluare."
        }
    ]}
/> 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter12/2.mdx" />

### Fine-tuning, Check![[fine-tuning-check]]
https://huggingface.co/learn/course/ro/chapter3/5.md

# Fine-tuning, Check![[fine-tuning-check]]

<CourseFloatingBanner
    chapter={3}
    classNames="absolute z-10 right-0 top-0"
/>

A fost distractiv! În primele două capitole ați învățat despre modele și tokenizere, iar acum știți cum să le ajustați fin (fine-tune) pentru propriile date. Pentru a recapitula, în acest capitol ați:

{#if fw === 'pt'}
* Ați învățat despre dataset-urile din [Hub](https://huggingface.co/datasets)
* Ați învățat cum să încărcați și să preprocessați dataset-uri, inclusiv folosind împachetare dinamică (dynamic padding) și colatori
* Ați implementat propria ajustare fină (fine-tuning) și evaluare a unui model
* Ați implementat o buclă de antrenament la un nivel mai scăzut (lower-level)
* Ați folosit 🤗 Accelerate pentru a adapta cu ușurință bucla voastră de antrenament, astfel încât să funcționeze pentru mai multe GPU-uri sau TPU-uri

{:else}
* Ați învățat despre dataset-urile din [Hub](https://huggingface.co/datasets)
* Ați învățat cum să încărcați și să preprocessați dataset-uri
* Ați învățat cum să ajustați fin și să evaluați un model cu Keras
* Ați implementat o metrică personalizată

{/if}


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter3/5.mdx" />

### Introducere[[introducere]]
https://huggingface.co/learn/course/ro/chapter3/1.md

# Introducere[[introducere]]

<CourseFloatingBanner
    chapter={3}
    classNames="absolute z-10 right-0 top-0"
/>

În [Capitolul 2](/course/chapter2) am explorat modul de utilizare a tokenizerelor și a modelelor preantrenate pentru a efectua predicții. Dar ce se întâmplă dacă doriți să ajustați un model preantrenat pentru propriul dvs. set de date? Iată subiectul acestui capitol! Veți învăța:

{#if fw === 'pt'}
* Cum să configurați un set mare de date din Hub
* Cum să utilizați API-ul  `Trainer` pentru a ajusta un model
* Cum să utilizați o buclă de instruire personalizată
* Cum să profitați de biblioteca Accelerate 🤗 pentru a rula cu ușurință bucla de instruire personalizată pe orice configurație distribuită

{:else}
* Cum să configurați un set mare de date din Hub
* Cum să utilizați Keras pentru a ajusta un model
* Cum să utilizați Keras pentru a obține predicții
* Cum să utilizați o măsură personalizată

{/if}

Pentru a încărca checkpoint-urile antrenate în Hugging Face Hub, veți avea nevoie de un cont huggingface.co: [creați un cont](https://huggingface.co/join)


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter3/1.mdx" />

### Ajustarea unui model folosind Trainer API[[ajustarea-unui-model-folosind-trainer-api]]
https://huggingface.co/learn/course/ro/chapter3/3.md

# Ajustarea unui model folosind Trainer API[[ajustarea-unui-model-folosind-trainer-api]]

<CourseFloatingBanner chapter={3}
  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/chapter3/section3.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter3/section3.ipynb"},
]} />

<Youtube id="nvBXf7s7vTI"/>

🤗 Transformers oferă o clasă `Trainer` pentru a vă ajuta să reglați mai bine oricare dintre modelele preinstruite pe care le oferă pe setul dvs. de date. După ce ați făcut toată munca de preprocesare a datelor din ultima secțiune, vă mai rămân doar câțiva pași pentru a defini clasa `Trainer`. Cea mai dificilă parte va fi probabil pregătirea mediului pentru a rula `Trainer.train()`, deoarece acesta va rula foarte lent pe un CPU. Dacă nu aveți un GPU configurat, puteți obține acces gratuit la GPU-uri sau TPU-uri pe [Google Colab] (https://colab.research.google.com/).

Exemplele de cod de mai jos presupun că ați executat deja exemplele din secțiunea anterioară. Iată un scurt rezumat care recapitulează ceea ce aveți nevoie:

```py
from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding

raw_datasets = load_dataset("glue", "mrpc")
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)


def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)


tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
```

### Antrenarea[[antrenarea]]

Primul pas înainte de a ne defini modelul `Trainer` este să definim o clasă `TrainingArguments` care va conține toți hiperparametrii pe care `Trainer` îi va utiliza pentru formare și evaluare. Singurul argument pe care trebuie să îl furnizați este un folder în care va fi salvat modelul antrenat, precum și punctele de control de pe parcurs. Pentru tot restul, puteți lăsa valorile implicite, care ar trebui să funcționeze destul de bine pentru o ajustare de bază.
 
```py
from transformers import TrainingArguments

training_args = TrainingArguments("test-trainer")
```

<Tip>

💡 Dacă doriți să încărcați automat modelul în Hub în timpul instruirii, treceți `push_to_hub=True` în `TrainingArguments`. Vom afla mai multe despre acest lucru în [Capitolul 4](/course/chapter4/3).

</Tip>

Al doilea pas este să ne definim modelul. Ca și în [capitolul anterior](/course/chapter2), vom folosi clasa `AutoModelForSequenceClassification`, cu două etichete:

```py
from transformers import AutoModelForSequenceClassification

model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
```

Veți observa că, spre deosebire de [Capitolul 2](/course/chapter2), veți primi un avertisment după instanțierea acestui model preinstruit. Acest lucru se datorează faptului că BERT nu a fost instruit în prealabil cu privire la clasificarea perechilor de propoziții, astfel încât head-ul modelului instruit în prealabil a fost eliminat și a fost adăugat în schimb un nou head adecvat pentru clasificarea secvențelor. Avertizările indică faptul că unele ponderi nu au fost utilizate (cele corespunzătoare head-ului de preformare eliminat) și că altele au fost inițializate aleatoriu (cele pentru noul head). În încheiere, vă încurajează să antrenați modelul, ceea ce vom face acum.

Odată ce avem modelul nostru, putem defini un `Trainer` transmițându-i toate obiectele construite până acum - `modelul`, `training_args`, seturile de date de formare și validare, `data_collator` și `tokenizer`:

```py
from transformers import Trainer

trainer = Trainer(
    model,
    training_args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    tokenizer=tokenizer,
)
```

Rețineți că atunci când treceți `tokenizer` așa cum am făcut aici, `data_collator` implicit folosit de `Trainer` va fi un `DataCollatorWithPadding` așa cum a fost definit anterior, deci puteți sări peste linia `data_collator=data_collator` în acest apel. Era totuși important să vă arăt această parte a procesării în secțiunea 2!

Pentru a regla cu precizie modelul pe setul nostru de date, trebuie doar să apelăm metoda `train()` a `Trainer`:

```py
trainer.train()
```

Acest lucru va începe reglarea fină (care ar trebui să dureze câteva minute pe un GPU) și va raporta valoarea pierderii de formare la fiecare 500 de pași. Cu toate acestea, nu vă va spune cât de bine (sau rău) funcționează modelul dumneavoastră. Acest lucru se datorează faptului că:

1. Nu am precizat că `Trainer` trebuie să evalueze în timpul antrenamentului prin setarea `evaluation_strategy` la `„steps”` (evaluare la fiecare `eval_steps`) sau `„epoch”` (evaluare la sfârșitul fiecărei epoch).
2. Nu am furnizat pentru `Trainer` o funcție `compute_metrics()` pentru a calcula o valoare metrică în timpul evaluării (altfel evaluarea ar fi afișat doar pierderea, care nu este un număr foarte intuitiv).

### Evaluarea[[evaluarea]]

Să vedem cum putem construi o funcție utilă `compute_metrics()` și să o folosim data viitoare când ne antrenăm. Funcția trebuie să preia un obiect `EvalPrediction` (care este un tuple numit cu un câmp `predictions` și un câmp `label_ids`) și va returna un dicționar care mapează șiruri de caractere în valori float. Pentru a obține unele predicții de la modelul nostru, putem utiliza comanda `Trainer.predict()`:

```py
predictions = trainer.predict(tokenized_datasets["validation"])
print(predictions.predictions.shape, predictions.label_ids.shape)
```

```python out
(408, 2) (408,)
```

Rezultatul metodei `predict()` este un alt tuple numit cu trei câmpuri: `predictions`, `label_ids` și `metrics`. Câmpul `metrics` va conține doar pierderea pe setul de date transmis, precum și unele valori metrice de timp (cât timp a durat predicția, în total și în medie). După ce vom finaliza funcția `compute_metrics()` și o vom transmite către `Trainer`, acest câmp va conține și metrica returnată de `compute_metrics()`.

După cum puteți vedea, `predictions` este un array bidimensional cu forma 408 x 2 (408 fiind numărul de elemente din setul de date pe care l-am folosit). Acestea sunt logits pentru fiecare element al setului de date pe care l-am transmis la `predict()` (după cum ați văzut în [capitolul anterior](/course/chapter2), toate modelele Transformer returnează logits). Pentru a le transforma în predicții pe care le putem compara cu etichetele noastre, trebuie să luăm indicele cu valoarea maximă pe a doua axă:

```py
import numpy as np

preds = np.argmax(predictions.predictions, axis=-1)
```

Acum putem compara `preds` cu etichetele. Pentru a construi funcția noastră `compute_metric()`, ne vom baza pe  valorile metrice din biblioteca 🤗 [Evaluate](https://github.com/huggingface/evaluate/). Putem încărca  valorile metrice asociate cu setul de date MRPC la fel de ușor cum am încărcat setul de date, de data aceasta cu funcția `evaluate.load()`. Obiectul returnat are o metodă `compute()` pe care o putem utiliza pentru a efectua calculul metric:

```py
import evaluate

metric = evaluate.load("glue", "mrpc")
metric.compute(predictions=preds, references=predictions.label_ids)
```

```python out
{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
```

Rezultatele exacte pe care le obțineți pot varia, deoarece inițializarea aleatorie a head-ului modelului poate schimba parametrii obținuți. Aici, putem vedea că modelul nostru are o precizie de 85,78% pe setul de validare și un scor F1 de 89,97. Acestea sunt cele două valori metrice utilizate pentru a evalua rezultatele pe setul de date MRPC pentru criteriul de referință GLUE. Tabelul din [lucrarea BERT] (https://arxiv.org/pdf/1810.04805.pdf) a raportat un scor F1 de 88,9 pentru modelul de bază. Acesta a fost modelul „fără casare”, în timp ce noi folosim în prezent modelul „cu casare”, ceea ce explică rezultatul mai bun.

Adunând totul, obținem funcția noastră `compute_metrics()`:

```py
def compute_metrics(eval_preds):
    metric = evaluate.load("glue", "mrpc")
    logits, labels = eval_preds
    predictions = np.argmax(logits, axis=-1)
    return metric.compute(predictions=predictions, references=labels)
```

Și pentru a vedea cum se utilizează în practică pentru a raporta datele metrice la sfârșitul fiecărei perioade, iată cum definim un nou `Trainer` cu această funcție `compute_metrics()`:

```py
training_args = TrainingArguments("test-trainer", evaluation_strategy="epoch")
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)

trainer = Trainer(
    model,
    training_args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    tokenizer=tokenizer,
    compute_metrics=compute_metrics,
)
```

Rețineți că creăm un nou `TrainingArguments` cu `evaluation_strategy` setat la `„epoch”` și un nou model - în caz contrar, am continua doar instruirea modelului pe care l-am instruit deja. Pentru a lansa o nouă rulare de formare, executăm:

```py
trainer.train()
```

De data aceasta, acesta va raporta pierderea de validare și metrica la sfârșitul fiecărei perioade, pe lângă valoarea pierderii de formare. Din nou, scorul exact de acuratețe/F1 pe care îl veți obține ar putea fi puțin diferit de ceea ce am găsit noi, din cauza inițializării aleatorii a modelului, dar ar trebui să fie în aceeași zonă.

Modelul `Trainer` va funcționa din start pe mai multe GPU sau TPU și oferă o mulțime de opțiuni, cum ar fi formarea cu precizie mixtă (utilizați `fp16 = True` în argumentele de formare). Vom trece în revistă toate funcțiile pe care le suportă în capitolul 10.

Aceasta încheie introducerea la reglarea fină cu ajutorul API-ului `Trainer`. În [Capitolul 7](/course/chapter7) va fi prezentat un exemplu de efectuare a acestei operații pentru cele mai comune sarcini NLP, dar pentru moment să analizăm cum se poate face același lucru în PyTorch pur.

<Tip>

✏️ **Încercați!** Ajustați un model pe setul de date GLUE SST-2, folosind procesarea datelor efectuată în secțiunea 2.

</Tip>



<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter3/3.mdx" />

### End-of-chapter quiz[[end-of-chapter-quiz]]
https://huggingface.co/learn/course/ro/chapter3/6.md

# End-of-chapter quiz[[end-of-chapter-quiz]]

<CourseFloatingBanner
    chapter={3}
    classNames="absolute z-10 right-0 top-0"
/>

Testați-vă cunoștințele din acest capitol!

### 1. Dataset-ul `emotion` conține mesaje de pe Twitter etichetate cu emoții. Căutați-l în [Hub](https://huggingface.co/datasets) și citiți descrierea dataset-ului. Care dintre acestea nu este una dintre emoțiile sale de bază?

<Question
	choices={[
		{
			text: "Bucurie",
			explain: "Încercați din nou - această emoție este prezentă în acel set de date!"
		},
		{
			text: "Iubire",
			explain: "Încercați din nou - această emoție este prezentă în acel set de date!"
		},
		{
			text: "Confuzie",
			explain: "Corect! Confuzia nu este una dintre cele șase emoții de bază.",
            correct: true
		},
        {
			text: "Surpriză",
			explain: "Surpriză! Încercați din nou!"
		}
	]}
/>

### 2. Căutați dataset-ul `ar_sarcasm` în [Hub](https://huggingface.co/datasets). Ce tip de sarcină suportă?

<Question
	choices={[
		{
			text: "Clasificarea sentimentelor",
			explain: "Corect! Puteți verifica etichetele (tags) din descrierea dataset-ului.",
            correct: true
		},
		{
			text: "Traducere automată (Machine translation)",
			explain: "Nu este corect — aruncați din nou o privire la <a href='https://huggingface.co/datasets/ar_sarcasm'>descrierea dataset-ului</a>!"
		},
		{
			text: "Named entity recognition",
			explain: "Nu este corect — aruncați din nou o privire la <a href='https://huggingface.co/datasets/ar_sarcasm'>descrierea dataset-ului</a>!"
		},
        {
			text: "Răspuns la întrebări (Question answering)",
			explain: "Din păcate, nu ați răspuns corect. Încercați din nou!"
		}
	]}
/>

### 3. Cum se așteaptă modelul BERT ca o pereche de propoziții să fie procesată?

<Question
	choices={[
		{
			text: "Tokens_of_sentence_1 [SEP] Tokens_of_sentence_2",
			explain: "Este nevoie de un token special <code>[SEP]</code> pentru a separa cele două propoziții, dar nu este singurul lucru necesar!"
		},
		{
			text: "[CLS] Tokens_of_sentence_1 Tokens_of_sentence_2",
			explain: "Este nevoie de un token special <code>[CLS]</code> la început, dar nu este singurul lucru necesar!"
		},
		{
			text: "[CLS] Tokens_of_sentence_1 [SEP] Tokens_of_sentence_2 [SEP]",
			explain: "Corect!",
            correct: true
		},
        {
			text: "[CLS] Tokens_of_sentence_1 [SEP] Tokens_of_sentence_2",
			explain: "Este nevoie atât de un token special <code>[CLS]</code> la început, cât și de un token special <code>[SEP]</code> pentru a separa cele două propoziții, dar mai lipsește ceva!"
		}
	]}
/>

{#if fw === 'pt'}
### 4. Care sunt avantajele metodei `Dataset.map()`?

<Question
	choices={[
		{
			text: "Rezultatele funcției sunt memorate în cache, astfel încât nu va dura nimic dacă reexecutăm codul.",
			explain: "Acesta este într-adevăr unul dintre avantajele acestei metode! Totuși, nu este singurul...",
            correct: true
		},
		{
			text: "Poate aplica multiprocessing pentru a rula mai rapid decât aplicând funcția pe fiecare element din dataset.",
			explain: "Aceasta este o caracteristică interesantă, dar nu este unica!",
            correct: true
		},
		{
			text: "Nu încarcă întregul set de date în memorie, salvând rezultatele de îndată ce este procesat un element.",
			explain: "Acesta este un avantaj al acestei metode. Există însă și altele!",
            correct: true
		},
	]}
/>

### 5. Ce înseamnă umplerea dinamică (dynamic padding)?

<Question
	choices={[
		{
			text: "E atunci când împachetezi intrările pentru fiecare batch la lungimea maximă din întregul dataset.",
			explain: "Este adevărat că împachetăm când creăm batch-ul, dar nu până la lungimea maximă din întregul set de date."
		},
		{
			text: "E atunci când împachetezi intrările în momentul creării batch-ului, la lungimea maximă a propozițiilor din acel batch.",
			explain: "Corect! Partea \"dinamică\" vine din faptul că mărimea fiecărui batch este stabilită în momentul creării, iar ca urmare, toate batch-urile ar putea avea forme diferite.",
            correct: true
		},
		{
			text: "E atunci când împachetezi intrările astfel încât fiecare propoziție să aibă același număr de tokeni ca precedenta în dataset.",
			explain: "Nu este corect, și nici nu are sens să ne uităm la ordinea din dataset, din moment ce îl amestecăm în timpul antrenamentului."
		},
	]}
/>

### 6. Care este scopul unei funcții de „collate"?

<Question
	choices={[
		{
			text: "Asigură că toate secvențele din setul de date au aceeași lungime.",
			explain: "O funcție de collate se ocupă de manipularea batch-urilor individuale, nu a întregului set de date. În plus, vorbim despre funcțiile de collate în general, nu despre <code>DataCollatorWithPadding</code> în mod specific."
		},
		{
			text: "Reunește toate eșantioanele într-un batch.",
			explain: "Corect! Poți transmite funcția de collate ca argument al unui <code>DataLoader</code>. Am folosit funcția <code>DataCollatorWithPadding</code>, care împachetează toate elementele dintr-un batch astfel încât să aibă aceeași lungime.",
            correct: true
		},
		{
			text: "Preprocesează întregul set de date.",
			explain: "Aceasta ar fi o funcție de preprocessing, nu o funcție de collate."
		},
        {
			text: "Trunchiază secvențele din setul de date.",
			explain: "O funcție de collate se ocupă de manipularea batch-urilor individuale, nu a întregului set de date. Dacă sunteți interesați de trunchiere, puteți folosi argumentul <code>truncate</code> al <code>tokenizer</code>."
		}
	]}
/>

### 7. Ce se întâmplă când instanțiați una dintre clasele `AutoModelForXxx` cu un model de limbaj preantrenat (cum ar fi `bert-base-uncased`), care corespunde unei alte sarcini decât cea pentru care a fost antrenat?

<Question
	choices={[
		{
			text: "Nimic, dar primești un avertisment.",
			explain: "Primiți un avertisment, dar asta nu e tot!"
		},
		{
			text: "Head-ul modelului preantrenat este eliminat, iar în locul lui este inserat un head nou, potrivit pentru sarcină.",
			explain: "Corect. De exemplu, când am folosit <code>AutoModelForSequenceClassification</code> cu <code>bert-base-uncased</code>, am primit avertismente la instanțierea modelului. Head-ul preantrenat nu este folosit pentru sarcina de clasificare secvențială, așa că este eliminat și un nou head este instanțiat cu greutăți inițializate aleator.",
            correct: true
		},
		{
			text: "Head-ul modelului preantrenat este eliminat.",
			explain: "Mai trebuie să se întâmple și altceva. Încercați din nou!"
		},
        {
			text: "Nimic, pentru că modelul poate fi ajustat fin (fine-tuned) chiar și pentru o altă sarcină.",
			explain: "Head-ul preantrenat al modelului nu a fost antrenat pentru această sarcină, deci trebuie eliminat!"
		}
	]}
/>

### 8. Care este scopul folosirii `TrainingArguments`?

<Question
	choices={[
		{
			text: "Conține toți hiperparametrii folosiți pentru antrenare și evaluare cu <code>Trainer</code>.",
			explain: "Corect!",
            correct: true
		},
		{
			text: "Specifică dimensiunea modelului.",
			explain: "Dimensiunea modelului este definită de configurația modelului, nu de clasa <code>TrainingArguments</code>."
		},
		{
			text: "Conține doar hiperparametrii folosiți pentru evaluare.",
			explain: "În exemplu, am specificat și unde va fi salvat modelul și checkpoint-urile. Încercați din nou!"
		},
        {
			text: "Conține doar hiperparametrii folosiți pentru antrenare.",
			explain: "În exemplu, am folosit și un <code>evaluation_strategy</code>, așadar acest lucru afectează evaluarea. Încercați din nou!"
		}
	]}
/>

### 9. De ce ar trebui să folosiți biblioteca 🤗 Accelerate?

<Question
	choices={[
		{
			text: "Oferă acces la modele mai rapide.",
			explain: "Nu, biblioteca 🤗 Accelerate nu oferă niciun model."
		},
		{
			text: "Oferă o API la nivel înalt, astfel încât să nu fie nevoie să implementați propria buclă de antrenament.",
			explain: "Asta am făcut cu <code>Trainer</code>, nu cu biblioteca 🤗 Accelerate. Încercați din nou!"
		},
		{
			text: "Face ca buclele noastre de antrenament să funcționeze pe strategii distribuite.",
			explain: "Corect! Cu 🤗 Accelerate, buclele voastre de antrenament vor funcționa pentru mai multe GPU-uri și TPU-uri.",
            correct: true
		},
        {
			text: "Oferă mai multe funcții de optimizare.",
			explain: "Nu, biblioteca 🤗 Accelerate nu oferă nicio funcție de optimizare."
		}
	]}
/>

{:else}
### 4. Ce se întâmplă când instanțiați una dintre clasele `TFAutoModelForXxx` cu un model de limbaj preantrenat (cum ar fi `bert-base-uncased`), care corespunde unei alte sarcini decât cea pentru care a fost antrenat?

<Question
	choices={[
		{
			text: "Nimic, dar primești un avertisment.",
			explain: "Primiți un avertisment, dar asta nu e tot!"
		},
		{
			text: "Head-ul modelului preantrenat este eliminat, iar în locul lui este inserat un head nou, potrivit pentru sarcină.",
			explain: "Corect. De exemplu, când am folosit <code>TFAutoModelForSequenceClassification</code> cu <code>bert-base-uncased</code>, am primit avertismente la instanțierea modelului. Head-ul preantrenat nu este folosit pentru sarcina de clasificare secvențială, așa că este eliminat și un nou head este instanțiat cu greutăți inițializate aleator.",
            correct: true
		},
		{
			text: "Head-ul modelului preantrenat este eliminat.",
			explain: "Mai trebuie să se întâmple și altceva. Încercați din nou!"
		},
        {
			text: "Nimic, pentru că modelul poate fi ajustat fin (fine-tuned) chiar și pentru o altă sarcină.",
			explain: "Head-ul preantrenat al modelului nu a fost antrenat pentru această sarcină, deci trebuie eliminat!"
		}
	]}
/>

### 5. Modelele TensorFlow din `transformers` sunt deja modele Keras. Ce avantaj oferă acest lucru?

<Question
	choices={[
		{
			text: "Modelele funcționează pe un TPU din start.",
			explain: "Ești pe aproape! Totuși, sunt necesare câteva modificări suplimentare. De exemplu, trebuie să rulați totul într-un context <code>TPUStrategy</code>, inclusiv inițializarea modelului."
		},
		{
			text: "Puteți valorifica metodele existente precum <code>compile()</code>, <code>fit()</code> și <code>predict()</code>.",
			explain: "Corect! Odată ce aveți datele, antrenarea necesită foarte puțin efort.",
            correct: true
		},
		{
			text: "Învățați atât Keras, cât și transformers.",
			explain: "Corect, dar căutăm altceva :)",
			correct: true
		},
        {
			text: "Puteți calcula cu ușurință metrici legate de dataset.",
			explain: "Keras ne ajută la antrenarea și evaluarea modelului, nu la calcularea metricilor legate de dataset."
		}
	]}
/>

### 6. Cum vă puteți defini propria metrică personalizată?

<Question
	choices={[
		{
			text: "Prin moștenirea <code>tf.keras.metrics.Metric</code>.",
			explain: "Perfect!",
			correct: true
		},
		{
			text: "Folosind API-ul funcțional Keras.",
			explain: "Mai încearcă!"
		},
		{
			text: "Folosind o funcție apelabilă cu semnătura <code>metric_fn(y_true, y_pred)</code>.",
			explain: "Corect!",
			correct: true
		},
        {
			text: "Căutând pe Google.",
			explain: "Nu este răspunsul pe care îl căutăm, dar probabil v-ar putea ajuta să-l găsiți.",
			correct: true
		}
	]}
/>

{/if}

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter3/6.mdx" />

### Ajustarea unui model cu Keras[[ajustarea-unui-model-cu-keras]]
https://huggingface.co/learn/course/ro/chapter3/3_tf.md

# Ajustarea unui model cu Keras[[ajustarea-unui-model-cu-keras]]

<CourseFloatingBanner chapter={3}
  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/chapter3/section3_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter3/section3_tf.ipynb"},
]} />

După ce ați efectuat toate lucrările de preprocesare a datelor din ultima secțiune, mai aveți doar câțiva pași de făcut pentru a antrena modelul. Rețineți, totuși, că comanda `model.fit()` va rula foarte lent pe un CPU. Dacă nu aveți un GPU configurat, puteți obține acces gratuit la GPU sau TPU pe [Google Colab](https://colab.research.google.com/).

Exemplele de cod de mai jos presupun că ați executat deja exemplele din secțiunea anterioară. Iată un scurt rezumat care recapitulează ceea ce aveți nevoie:

```py
from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding
import numpy as np

raw_datasets = load_dataset("glue", "mrpc")
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)


def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)


tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)

data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf")

tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
    columns=["attention_mask", "input_ids", "token_type_ids"],
    label_cols=["labels"],
    shuffle=True,
    collate_fn=data_collator,
    batch_size=8,
)

tf_validation_dataset = tokenized_datasets["validation"].to_tf_dataset(
    columns=["attention_mask", "input_ids", "token_type_ids"],
    label_cols=["labels"],
    shuffle=False,
    collate_fn=data_collator,
    batch_size=8,
)
```

### Antrenarea[[antrenarea]]

Modelele TensorFlow importate din 🤗 Transformers sunt deja modele Keras. În cele ce urmează găsiți o scurtă introducere în Keras.

<Youtube id="rnTGBy2ax1c"/>

Aceasta înseamnă că, odată ce avem datele noastre, este nevoie de foarte puțină muncă pentru a începe antrenarea pe baza acestora.

<Youtube id="AUozVp78dhk"/>

Ca și în [capitolul anterior](/course/chapter2), vom folosi clasa `TFAutoModelForSequenceClassification`, cu două etichete: 

```py
from transformers import TFAutoModelForSequenceClassification

model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
```

Veți observa că, spre deosebire de [Capitolul 2](/course/chapter2), veți primi un avertisment după instanțierea acestui model preinstruit. Acest lucru se datorează faptului că BERT nu a fost preinstruit pentru clasificarea perechilor de propoziții, astfel încât head-ul modelului preinstruit a fost eliminat și a fost introdus în schimb un nou head adecvat pentru clasificarea secvențelor. Avertizările indică faptul că unele ponderi nu au fost utilizate (cele corespunzătoare head-ului de preformare eliminat) și că altele au fost inițializate aleatoriu (cele pentru noul head). În încheiere, vă încurajează să antrenați modelul, ceea ce vom face acum.

Pentru a realiza fine-tuning-ul modelului pe setul nostru de date, trebuie doar să `compilăm()` modelul nostru și apoi să transmitem datele noastre metodei `fit()`. Aceasta va începe procesul de `fine-tuning` (care ar trebui să dureze câteva minute pe un GPU) și va raporta pierderea de formare pe parcurs, plus pierderea de validare la sfârșitul fiecărei epoci.

<Tip>

Rețineți că modelele 🤗 Transformers au o abilitate specială pe care majoritatea modelelor Keras nu o au - ele pot utiliza automat o valoare adecvată a pierderii pe care o calculează intern. Ele vor utiliza această valoare în mod implicit dacă nu setați un argument de pierdere în `compile()`. Rețineți că pentru a utiliza  valoarea internă a pierderii va trebui să transmiteți etichetele ca parte a datelor de intrare, nu ca etichetă separată, care este modul normal de utilizare a etichetelor cu modelele Keras. Veți vedea exemple în acest sens în partea 2 a cursului, unde definirea funcției de pierdere corecte poate fi complicată. Cu toate acestea, pentru clasificarea secvențelor, o funcție de pierdere Keras standard funcționează bine, așa că aceasta este cea pe care o vom utiliza aici.

</Tip>

```py
from tensorflow.keras.losses import SparseCategoricalCrossentropy

model.compile(
    optimizer="adam",
    loss=SparseCategoricalCrossentropy(from_logits=True),
    metrics=["accuracy"],
)
model.fit(
    tf_train_dataset,
    validation_data=tf_validation_dataset,
)
```

<Tip warning={true}>

Remarcați o problemă foarte des întâlnită aici - *puteți* doar să transmiteți numele pierderii ca șir de caractere către Keras, dar în mod implicit Keras va presupune că ați aplicat deja un softmax rezultatelor dvs. Cu toate acestea, multe modele produc valorile chiar înainte de aplicarea softmax, care sunt cunoscute și sub numele de *logits*. Trebuie să spunem funcției de pierdere că asta face modelul nostru, iar singura modalitate de a face acest lucru este să o apelăm direct, mai degrabă decât prin nume cu un șir.

</Tip>


### Îmbunătățirea performanțelor de instruire[[Îmbunătățirea-performanțelor-de-instruire]]

<Youtube id="cpzq6ESSM5c"/>

Dacă încercați codul de mai sus, acesta rulează cu siguranță, dar veți constata că valoarea pierderii scade doar lent sau sporadic. Cauza principală
este *rata de învățare*. Ca și în cazul pierderii, atunci când îi transmitem lui Keras numele unui optimizator ca șir de caractere, Keras inițializează
optimizatorul respectiv cu valori implicite pentru toți parametrii, inclusiv rata de învățare. Totuși, din experiența îndelungată, știm
că modelele transformatoare beneficiază de o rată de învățare mult mai mică decât cea implicită pentru Adam, care este 1e-3, scrisă și
ca 10 la puterea -3, sau 0,001. 5e-5 (0,00005), care este de aproximativ douăzeci de ori mai mică, este un punct de plecare mult mai bun.

În plus față de scăderea ratei de învățare, avem un al doilea as în mânecă: putem reduce încet rata de învățare
pe parcursul instruirii. În literatura de specialitate, veți vedea uneori că acest lucru este denumit *decădere* sau *analizare*
a ratei de învățare. În Keras, cel mai bun mod de a face acest lucru este de a utiliza un *programator al ratei de învățare*. Un program bun de utilizat este
`PolynomialDecay` - în ciuda numelui, cu setările implicite, acesta pur și simplu scade liniar rata de învățare de la valoarea inițială
până la valoarea finală pe parcursul instruirii, ceea ce este exact ceea ce ne dorim. Pentru a utiliza corect un programator,
totuși, trebuie să îi spunem cât va dura antrenamentul. Calculăm acest lucru ca `num_train_steps` mai jos.

```py
from tensorflow.keras.optimizers.schedules import PolynomialDecay

batch_size = 8
num_epochs = 3
# Numărul etapelor de formare este numărul de eșantioane din setul de date, împărțit la dimensiunea batch-ului, apoi înmulțit
# cu numărul total de epoci. Rețineți că setul de date tf_train_dataset de aici este un set de date tf.data.Dataset în batch-uri,
num_train_steps = len(tf_train_dataset) * num_epochs
lr_scheduler = PolynomialDecay(
    initial_learning_rate=5e-5, end_learning_rate=0.0, decay_steps=num_train_steps
)
from tensorflow.keras.optimizers import Adam

opt = Adam(learning_rate=lr_scheduler)
```

<Tip>

Biblioteca Transformers 🤗 are, de asemenea, o funcție `create_optimizer()` care va crea un optimizator `AdamW` cu rata de învățare în scădere. Aceasta este o scurtătură convenabilă pe care o veți vedea în detaliu în secțiunile viitoare ale cursului.

</Tip>

Acum avem optimizatorul nostru complet nou și putem încerca să ne antrenăm cu el. În primul rând, să reîncărcăm modelul, pentru a reseta modificările aduse ponderilor în urma instruirii pe care tocmai am efectuat-o, iar apoi îl putem compila cu noul optimizator:

```py
import tensorflow as tf

model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
model.compile(optimizer=opt, loss=loss, metrics=["accuracy"])
```

Acum, ne potrivim din nou:

```py
model.fit(tf_train_dataset, validation_data=tf_validation_dataset, epochs=3)
```

<Tip>

💡 Dacă doriți să încărcați automat modelul dvs. în Hub în timpul instruirii, puteți trece un `PushToHubCallback` în metoda `model.fit()`. Vom afla mai multe despre acest lucru în [Capitolul 4](/course/chapter4/3)

</Tip>

### Predicțiile modelului[[predicțiile-modelului]]

<Youtube id="nx10eh4CoOs"/>


Este foarte interesant să ne antrenăm și să vedem cum scade valoarea pierderii, dar ce se întâmplă dacă dorim să obținem rezultate de la modelul antrenat, fie pentru a calcula anumiți parametri, fie pentru a utiliza modelul în producție? Pentru a face acest lucru, putem folosi metoda `predict()`. Aceasta va returna *logit-urile* din rezultatele modelului, unul pentru fiecare clasă.

```py
preds = model.predict(tf_validation_dataset)["logits"]
```

Putem converti aceste logiți în predicții de clasă ale modelului folosind `argmax` pentru a găsi cel mai mare logit, care corespunde celei mai probabile clase:

```py
class_preds = np.argmax(preds, axis=1)
print(preds.shape, class_preds.shape)
```

```python out
(408, 2) (408,)
```
Acum, să folosim `preds` pentru a calcula niște indicatori! Putem încărca indicatorii de măsurare asociați cu setul de date MRPC la fel de ușor cum am încărcat setul de date, de data aceasta cu funcția `evaluate.load()`. Obiectul returnat are o metodă `compute()` pe care o putem folosi pentru a face calculul metric:

```py
import evaluate

metric = evaluate.load("glue", "mrpc")
metric.compute(predictions=class_preds, references=raw_datasets["validation"]["label"])
```

```python out
{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
```

Rezultatele exacte pe care le obțineți pot varia, deoarece inițializarea aleatorie a head-ului modelului poate schimba parametrii obținuți. Aici, putem vedea că modelul nostru are o precizie de 85,78% pe setul de validare și un scor F1 de 89,97. Acestea sunt cele două metrici utilizate pentru a evalua rezultatele pe setul de date MRPC pentru criteriul de referință GLUE. Tabelul din [lucrarea BERT] (https://arxiv.org/pdf/1810.04805.pdf) a raportat un scor F1 de 88,9 pentru modelul de bază. Acesta a fost modelul `uncased`, în timp ce noi folosim în prezent modelul `cased`, ceea ce explică rezultatul mai bun.

Aceasta încheie introducerea la ajustarea fină utilizând API-ul Keras. În [Capitolul 7](/course/chapter7) va fi prezentat un exemplu de efectuare a acestui lucru pentru cele mai comune sarcini NLP. Dacă doriți să vă perfecționați abilitățile cu API-ul Keras, încercați să reglați un model pe setul de date GLUE SST-2, utilizând prelucrarea datelor pe care ați făcut-o în secțiunea 2.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter3/3_tf.mdx" />

### O instruire completă[[o-instruire-completă]]
https://huggingface.co/learn/course/ro/chapter3/4.md

# O instruire completă[[o-instruire-completă]]

<CourseFloatingBanner chapter={3}
  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/chapter3/section4.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter3/section4.ipynb"},
]} />

<Youtube id="Dh9CL8fyG80"/>

Acum vom vedea cum să obținem aceleași rezultate ca în secțiunea anterioară, dar fără să folosim clasa `Trainer`. Din nou, presupunem că ați parcurs deja procesarea datelor din secțiunea 2. Iată un scurt rezumat al tot ceea ce veți avea nevoie:

```py
from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding

raw_datasets = load_dataset("glue", "mrpc")
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)


def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)


tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
```

### Pregătirea pentru antrenament[[pregătirea-pentru-antrenament]]

Înainte de a scrie efectiv bucla de antrenament, va trebui să definim câteva obiecte. Primele sunt încărcătoarele de date (dataloaders) pe care le vom folosi pentru a itera pe batch-uri. Dar, înainte de a putea defini acele dataloaders, trebuie să aplicăm un pic de postprocesare dataset-urilor noastre `tokenized_datasets`, pentru a ne ocupa de câteva lucruri pe care `Trainer` le făcea automat pentru noi. Mai exact, trebuie să:

- Eliminăm coloanele care corespund valorilor pe care modelul nu le așteaptă (cum ar fi coloanele `sentence1` și `sentence2`).
- Redenumim coloana `label` în `labels` (pentru că modelul se așteaptă ca argumentul să se numească `labels`).
- Setăm formatul dataset-urilor astfel încât să returneze tensori PyTorch în loc de liste.

`tokenized_datasets` are câte o metodă pentru fiecare dintre acești pași:

```py
tokenized_datasets = tokenized_datasets.remove_columns(["sentence1", "sentence2", "idx"])
tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
tokenized_datasets.set_format("torch")
tokenized_datasets["train"].column_names
```

Putem apoi să verificăm că rezultatul are doar coloanele pe care modelul le va accepta:

```python
["attention_mask", "input_ids", "labels", "token_type_ids"]
```

Acum, după ce am terminat acest pas, putem defini foarte ușor dataloader-urile noastre:

```py
from torch.utils.data import DataLoader

train_dataloader = DataLoader(
    tokenized_datasets["train"], shuffle=True, batch_size=8, collate_fn=data_collator
)
eval_dataloader = DataLoader(
    tokenized_datasets["validation"], batch_size=8, collate_fn=data_collator
)
```

Pentru a verifica rapid că nu există nicio eroare în procesarea datelor, putem inspecta un batch astfel:

```py
for batch in train_dataloader:
    break
{k: v.shape for k, v in batch.items()}
```

```python out
{'attention_mask': torch.Size([8, 65]),
 'input_ids': torch.Size([8, 65]),
 'labels': torch.Size([8]),
 'token_type_ids': torch.Size([8, 65])}
```

Observați că formele reale ar putea fi ușor diferite pentru voi, pentru că am setat `shuffle=True` în dataloader-ul nostru de antrenament și pentru că împachetăm (padding) la lungimea maximă în interiorul batch-ului.

Acum că am terminat complet procesarea datelor (un obiectiv satisfăcător, dar uneori greu de atins pentru orice practician ML), să trecem la model. Îl instanțiem exact ca în secțiunea anterioară:

```py
from transformers import AutoModelForSequenceClassification

model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
```

Pentru a ne asigura că totul va decurge fără probleme în timpul antrenamentului, trecem batch-ul nostru prin model:

```py
outputs = model(**batch)
print(outputs.loss, outputs.logits.shape)
```

```python out
tensor(0.5441, grad_fn=<NllLossBackward>) torch.Size([8, 2])
```

Toate modelele 🤗 Transformers vor returna pierderea (loss) când `labels` sunt furnizate, și, de asemenea, obținem logits (două pentru fiecare intrare în batch, deci un tensor de mărimea 8 x 2).

Suntem aproape gata să scriem bucla de antrenament! Ne mai lipsesc două lucruri: un optimizer și un scheduler pentru rata de învățare. Pentru că încercăm să reproducem ceea ce făcea `Trainer`, vom folosi aceleași valori implicite. Optimizer-ul folosit de `Trainer` este `AdamW`, care este același cu Adam, dar cu o abordare particulară pentru regularizarea weight decay (vedeți lucrarea ["Decoupled Weight Decay Regularization"](https://arxiv.org/abs/1711.05101) de Ilya Loshchilov și Frank Hutter):

```py
from transformers import AdamW

optimizer = AdamW(model.parameters(), lr=5e-5)
```

În final, scheduler-ul pentru rata de învățare folosit implicit este doar o descreștere liniară de la valoarea maximă (5e-5) la 0. Pentru a-l defini corect, trebuie să știm numărul de pași de antrenament pe care îi vom face, care este numărul de epoci dorit înmulțit cu numărul de batch-uri de antrenament (care este lungimea dataloader-ului nostru de antrenament). `Trainer` folosește trei epoci implicit, așa că vom urma acest exemplu:

```py
from transformers import get_scheduler

num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)
print(num_training_steps)
```

```python out
1377
```

### Bucla de antrenament[[bucla-de-antrenament]]

Încă un lucru: vom dori să folosim GPU-ul dacă avem acces la unul (pe un CPU, antrenamentul poate dura câteva ore în loc de câteva minute). Pentru asta, definim un `device` pe care vom pune modelul și batch-urile noastre:

```py
import torch

device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
model.to(device)
device
```

```python out
device(type='cuda')
```

Acum suntem gata de antrenament! Pentru a ne face o idee despre momentul în care se va termina antrenamentul, adăugăm o bară de progres peste numărul de pași de antrenament, folosind biblioteca `tqdm`:

```py
from tqdm.auto import tqdm

progress_bar = tqdm(range(num_training_steps))

model.train()
for epoch in range(num_epochs):
    for batch in train_dataloader:
        batch = {k: v.to(device) for k, v in batch.items()}
        outputs = model(**batch)
        loss = outputs.loss
        loss.backward()

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)
```

Observați că partea principală a buclei de antrenament arată foarte asemănător cu cea din introducere. Nu am cerut niciun raport, așa că această buclă de antrenament nu ne va spune nimic despre performanța modelului. Pentru a avea feedback, trebuie să adăugăm o buclă de evaluare.


### Bucla de evaluare [[bucla-de-evaluare]]

Ca și înainte, vom folosi o metrică oferită de biblioteca 🤗 Evaluate. Am văzut deja metoda `metric.compute()`, dar metricle pot de fapt să acumuleze batch-uri pentru noi în timp ce parcurgem bucla de predicție, cu metoda `add_batch()`. Odată ce am acumulat toate batch-urile, putem obține rezultatul final cu `metric.compute()`. Iată cum să implementăm toate acestea într-o buclă de evaluare:

```py
import evaluate

metric = evaluate.load("glue", "mrpc")
model.eval()
for batch in eval_dataloader:
    batch = {k: v.to(device) for k, v in batch.items()}
    with torch.no_grad():
        outputs = model(**batch)

    logits = outputs.logits
    predictions = torch.argmax(logits, dim=-1)
    metric.add_batch(predictions=predictions, references=batch["labels"])

metric.compute()
```

```python out
{'accuracy': 0.8431372549019608, 'f1': 0.8907849829351535}
```

Din nou, rezultatele voastre vor fi ușor diferite din cauza aleatorietății în inițializarea layer-ului final (model head) și a amestecării datelor, dar ar trebui să fie în aceeași zonă valorică.

<Tip>

✏️ **Încercați!** Modificați bucla de antrenament anterioară pentru a vă rafina modelul pe dataset-ul SST-2.

</Tip>

### Îmbunătățiți circuitul de antrenament cu 🤗 Accelerate[[îmbunătățiți-circuitul-de-antrenament-cu-accelerate]]

<Youtube id="s7dy8QRgjJ0" />

Bucla de antrenament pe care am definit-o anterior funcționează bine pe un singur CPU sau GPU. Dar, folosind biblioteca [🤗 Accelerate](https://github.com/huggingface/accelerate), cu doar câteva ajustări putem activa antrenarea distribuită pe mai multe GPU-uri sau TPU-uri. Pornind de la crearea dataloader-urilor de antrenament și validare, iată cum arată bucla noastră manuală de antrenament:

```py
from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler

model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
optimizer = AdamW(model.parameters(), lr=3e-5)

device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
model.to(device)

num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)

progress_bar = tqdm(range(num_training_steps))

model.train()
for epoch in range(num_epochs):
    for batch in train_dataloader:
        batch = {k: v.to(device) for k, v in batch.items()}
        outputs = model(**batch)
        loss = outputs.loss
        loss.backward()

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)
```

Iar aici sunt modificările:

```diff
+ from accelerate import Accelerator
  from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler

+ accelerator = Accelerator()

  model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
  optimizer = AdamW(model.parameters(), lr=3e-5)

- device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
- model.to(device)

+ train_dataloader, eval_dataloader, model, optimizer = accelerator.prepare(
+     train_dataloader, eval_dataloader, model, optimizer
+ )

  num_epochs = 3
  num_training_steps = num_epochs * len(train_dataloader)
  lr_scheduler = get_scheduler(
      "linear",
      optimizer=optimizer,
      num_warmup_steps=0,
      num_training_steps=num_training_steps
  )

  progress_bar = tqdm(range(num_training_steps))

  model.train()
  for epoch in range(num_epochs):
      for batch in train_dataloader:
-         batch = {k: v.to(device) for k, v in batch.items()}
          outputs = model(**batch)
          loss = outputs.loss
-         loss.backward()
+         accelerator.backward(loss)

          optimizer.step()
          lr_scheduler.step()
          optimizer.zero_grad()
          progress_bar.update(1)
```

Prima linie de adăugat este linia de import. A doua linie instanțiază un obiect `Accelerator` care va examina mediul și va inițializa setarea distribuită corespunzătoare. 🤗 Accelerate se ocupă de plasarea pe device pentru voi, așa că puteți elimina liniile care pun modelul pe device (sau, dacă preferați, le puteți schimba să folosească `accelerator.device` în loc de `device`).

Apoi, partea principală a muncii este făcută în linia care trimite dataloaders, modelul și optimizer-ul la `accelerator.prepare()`. Aceasta va împacheta acele obiecte în containerul potrivit pentru a vă asigura că antrenarea distribuită funcționează corespunzător. Restul modificărilor constau în eliminarea liniei care mută batch-ul pe `device` (din nou, dacă doriți să o păstrați puteți doar să o schimbați să folosească `accelerator.device`) și înlocuirea `loss.backward()` cu `accelerator.backward(loss)`.

<Tip>
⚠️ Pentru a beneficia de creșterea vitezei oferită de Cloud TPU-uri, vă recomandăm să împachetați mostrele la o lungime fixă folosind argumentele `padding="max_length"` și `max_length` ale tokenizer-ului.
</Tip>

Dacă vreți să copiați și să lipiți pentru a vă juca, iată cum arată bucla completă de antrenament cu 🤗 Accelerate:

```py
from accelerate import Accelerator
from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler

accelerator = Accelerator()

model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
optimizer = AdamW(model.parameters(), lr=3e-5)

train_dl, eval_dl, model, optimizer = accelerator.prepare(
    train_dataloader, eval_dataloader, model, optimizer
)

num_epochs = 3
num_training_steps = num_epochs * len(train_dl)
lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)

progress_bar = tqdm(range(num_training_steps))

model.train()
for epoch in range(num_epochs):
    for batch in train_dl:
        outputs = model(**batch)
        loss = outputs.loss
        accelerator.backward(loss)

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)
```

Plasând acest cod într-un fișier `train.py` îl face rulabil pe orice tip de configurare distribuită. Pentru a-l încerca în configurarea voastră distribuită, rulați comanda:

```bash
accelerate config
```

care vă va cere să răspundeți la câteva întrebări și vă va crea un fișier de configurare folosit de comanda:

```
accelerate launch train.py
```

care va porni antrenarea distribuită.

Dacă vreți să încercați asta într-un Notebook (de exemplu, pentru a-l testa cu TPU-uri pe Colab), doar lipiți codul într-o funcție `training_function()` și rulați într-o celulă finală:

```python
from accelerate import notebook_launcher

notebook_launcher(training_function)
```

Puteți găsi mai multe exemple în [repo-ul 🤗 Accelerate](https://github.com/huggingface/accelerate/tree/main/examples).

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter3/4.mdx" />

### Prelucrarea datelor[[prelucrarea-datelor]]
https://huggingface.co/learn/course/ro/chapter3/2.md

# Prelucrarea datelor[[prelucrarea-datelor]]

{#if fw === 'pt'}

<CourseFloatingBanner chapter={3}
  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/chapter3/section2_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter3/section2_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={3}
  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/chapter3/section2_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter3/section2_tf.ipynb"},
]} />

{/if}

{#if fw === 'pt'}
Continuând cu exemplul din [capitolul anterior](/course/chapter2), iată cum am antrena un clasificator de secvențe pe un batch în PyTorch:

```python
import torch
from transformers import AdamW, AutoTokenizer, AutoModelForSequenceClassification

# La fel ca înainte
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = [
    "I've been waiting for a HuggingFace course my whole life.",
    "This course is amazing!",
]
batch = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")

# Ceva nou
batch["labels"] = torch.tensor([1, 1])

optimizer = AdamW(model.parameters())
loss = model(**batch).loss
loss.backward()
optimizer.step()
```
{:else}
Continuând cu exemplul din [capitolul anterior](/course/chapter2), iată cum am antrena un clasificator de secvențe pe un batch în TensorFlow:

```python
import tensorflow as tf
import numpy as np
from transformers import AutoTokenizer, TFAutoModelForSequenceClassification

# La fel ca înainte
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = [
    "I've been waiting for a HuggingFace course my whole life.",
    "This course is amazing!",
]
batch = dict(tokenizer(sequences, padding=True, truncation=True, return_tensors="tf"))

# Ceva nou
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy")
labels = tf.convert_to_tensor([1, 1])
model.train_on_batch(batch, labels)
```
{/if}

Desigur, doar antrenarea modelului pe două propoziții nu va da rezultate foarte bune. Pentru a obține rezultate mai bune, va trebui să pregătiți un set de date mai mare.

În această secțiune vom folosi ca exemplu setul de date MRPC (Microsoft Research Paraphrase Corpus), introdus într-o [lucrare](https://www.aclweb.org/anthology/I05-5002.pdf) de William B. Dolan și Chris Brockett. Setul de date este format din 5 801 perechi de propoziții, cu o etichetă care indică dacă acestea sunt parafrazări sau nu (adică, dacă ambele propoziții înseamnă același lucru). L-am selectat pentru acest capitol deoarece este un set de date mic, astfel încât este ușor de experimentat cu formarea pe acesta.

### Încărcarea unui set de date din Hub[[încărcarea-unui-set-de-date-din-Hub]]

{#if fw === 'pt'}
<Youtube id="_BZearw7f0w"/>
{:else}
<Youtube id="W_gMJF0xomE"/>
{/if}

Hub-ul nu conține doar modele, ci și multe seturi de date în limbi diferite. Puteți naviga printre seturile de date [aici](https://huggingface.co/datasets) și vă recomandăm să încercați să încărcați și să procesați un nou set de date după ce ați parcurs această secțiune (consultați documentația generală [aici](https://huggingface.co/docs/datasets/loading)). Dar, pentru moment, să ne concentrăm asupra setului de date MRPC! Acesta este unul dintre cele 10 seturi de date care compun [GLUE benchmark](https://gluebenchmark.com/), care este un benchmark academic utilizat pentru a măsura performanța modelelor ML în 10 sarcini diferite de clasificare a textului.

Biblioteca 🤗 Datasets oferă o comandă foarte simplă pentru a descărca și stoca în cache un set de date pe Hub. Putem descărca setul de date MRPC astfel:

<Tip>
⚠️ **Atenție** Asigurați-vă că `datasets` este instalat prin rularea `pip install datasets`. Apoi, încărcați setul de date MRPC și tipăriți-l pentru a vedea ce conține.
</Tip> 

```py
from datasets import load_dataset

raw_datasets = load_dataset("glue", "mrpc")
raw_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['sentence1', 'sentence2', 'label', 'idx'],
        num_rows: 3668
    })
    validation: Dataset({
        features: ['sentence1', 'sentence2', 'label', 'idx'],
        num_rows: 408
    })
    test: Dataset({
        features: ['sentence1', 'sentence2', 'label', 'idx'],
        num_rows: 1725
    })
})
```

După cum puteți vedea, obținem un obiect `DatasetDict` care conține setul de instruire, setul de validare și setul de testare. Fiecare dintre acestea conține mai multe coloane (`sentence1`, `sentence2`, `label` și `idx`) și un număr de rânduri variabil, care reprezintă numărul de elemente din fiecare set (astfel, există 3.668 de perechi de propoziții în setul de instruire, 408 în setul de validare și 1.725 în setul de testare).

Această comandă descarcă și pune în cache setul de date, implicit în *~/.cache/huggingface/datasets*. Reamintim din capitolul 2 că puteți personaliza folderul cache prin setarea variabilei de mediu `HF_HOME`.

Putem accesa fiecare pereche de propoziții din obiectul nostru `raw_datasets` prin indexare, ca într-un dicționar:

```py
raw_train_dataset = raw_datasets["train"]
raw_train_dataset[0]
```

```python out
{'idx': 0,
 'label': 1,
 'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
 'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .'}
```

Putem vedea că etichetele sunt deja numere întregi, deci nu va trebui să efectuăm nicio prelucrare prealabilă. Pentru a ști ce număr întreg corespunde fiecărei etichete, putem inspecta `features` din `raw_train_dataset`. Acest lucru ne va indica tipul fiecărei coloane:

```py
raw_train_dataset.features
```

```python out
{'sentence1': Value(dtype='string', id=None),
 'sentence2': Value(dtype='string', id=None),
 'label': ClassLabel(num_classes=2, names=['not_equivalent', 'equivalent'], names_file=None, id=None),
 'idx': Value(dtype='int32', id=None)}
```

În culise, `label` este de tipul `ClassLabel`, iar maparea numerelor întregi și numele etichetei este stocată în folderul *names*. `0` corespunde la `not_equivalent`, iar `1` corespunde la `equivalent`.

<Tip>

✏️ **Încercați!** Uitați-vă la elementul 15 din setul de antrenament și la elementul 87 din setul de validare. Care sunt etichetele lor?

</Tip>

### Preprocesarea unui set de date[[preprocesarea-unui-set-de-date]]

{#if fw === 'pt'}
<Youtube id="0u3ioSwev3s"/>
{:else}
<Youtube id="P-rZWqcB6CE"/>
{/if}

Pentru a preprocesa setul de date, trebuie să convertim textul în numere pe care modelul le înțelege. După cum ați văzut în [capitolul anterior](/course/chapter2), acest lucru se face cu ajutorul unui tokenizer. Putem furniza tokenizatorului o propoziție sau o listă de propoziții, astfel încât putem tokeniza direct primele propoziții și toate propozițiile secundare din fiecare pereche, astfel:

```py
from transformers import AutoTokenizer

checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
tokenized_sentences_1 = tokenizer(raw_datasets["train"]["sentence1"])
tokenized_sentences_2 = tokenizer(raw_datasets["train"]["sentence2"])
```

Cu toate acestea, nu putem pur și simplu să transmitem două secvențe modelului și să obținem o predicție care să indice dacă cele două propoziții sunt parafraze sau nu. Trebuie să tratăm cele două secvențe ca pe o pereche și să aplicăm preprocesarea corespunzătoare. Din fericire, tokenizatorul poate, de asemenea, să ia o pereche de secvențe și să le pregătească în modul în care se așteaptă modelul nostru BERT: 

```py
inputs = tokenizer("This is the first sentence.", "This is the second one.")
inputs
```

```python out
{ 
  'input_ids': [101, 2023, 2003, 1996, 2034, 6251, 1012, 102, 2023, 2003, 1996, 2117, 2028, 1012, 102],
  'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
  'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
}
```

Am discutat despre cheile `input_ids` și `attention_mask` în [Capitolul 2](/course/chapter2), dar am amânat discuția despre `token_type_ids`. În acest exemplu, aceasta este ceea ce îi spune modelului care parte a intrării este prima propoziție și care este a doua propoziție.

<Tip>

✏️ **Încercați!** Luați elementul 15 din setul de antrenament și tokenizați cele două propoziții separat apoi ca pe o pereche. Care este diferența dintre cele două rezultate?

</Tip>

Dacă decodificăm ID-urile din `input_ids` înapoi în cuvinte:

```py
tokenizer.convert_ids_to_tokens(inputs["input_ids"])
```

Vom obține:

```python out
['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
```

Astfel, modelul se așteaptă ca intrările să fie de forma `[CLS] sentence1 [SEP] sentence2 [SEP]` atunci când există două propoziții. Alinierea acestui lucru cu `token_type_ids` ne dă:

```python out
['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
[      0,      0,    0,     0,       0,          0,   0,       0,      1,    1,     1,        1,     1,   1,       1]
```

După cum puteți vedea, părțile de intrare corespunzătoare `[CLS] sentence1 [SEP]` au toate un ID de tip token de `0`, în timp ce celelalte părți, corespunzătoare `sentence2 [SEP]`, au toate un ID de tip token de `1`.

Rețineți că, dacă selectați un checkpoint diferit, nu veți avea neapărat `token_type_ids` în intrările dvs. tokenizate (de exemplu, acestea nu sunt returnate dacă utilizați un model DistilBERT). Acestea sunt returnate numai atunci când modelul va ști ce să facă cu ele, deoarece le-a văzut în timpul preinstruirii sale. 

Aici, BERT este preinstruit cu ID-uri de tip token și, pe lângă obiectivul de modelare a limbajului mascat despre care am vorbit în [[Chapter 1]](/course/chapter1), are un obiectiv suplimentar numit _next sentence prediction_. Obiectivul acestei sarcini este de a modela relația dintre perechile de propoziții.

În cazul predicției propoziției următoare, modelul primește perechi de propoziții (cu token-uri mascate aleatoriu) și i se cere să prezică dacă a doua propoziție o urmează pe prima. Pentru ca sarcina să nu fie complicată, jumătate din timp propozițiile se succed reciproc în documentul original din care au fost extrase, iar cealaltă jumătate din timp cele două propoziții provin din două documente diferite. 

În general, nu trebuie să vă faceți griji dacă există sau nu `token_type_ids` în intrările dvs. tokenizate: atâta timp cât utilizați același checkpoint pentru tokenizator și model, totul va fi bine, deoarece tokenizatorul știe ce să furnizeze modelului său.

Acum că am văzut cum tokenizatorul nostru poate trata o pereche de propoziții, îl putem folosi pentru a tokeniza întregul nostru set de date: la fel ca în [previous chapter](/course/chapter2), putem furniza tokenizatorului o listă de perechi de propoziții oferindu-i lista primelor propoziții, apoi lista celor de-a doua propoziții. Acest lucru este, de asemenea, compatibil cu opțiunile de padding și trunchiere pe care le-am văzut în [Chapter 2](/course/chapter2). Așadar, o modalitate de preprocesare a setului de date de instruire este:

```py
tokenized_dataset = tokenizer(
    raw_datasets["train"]["sentence1"],
    raw_datasets["train"]["sentence2"],
    padding=True,
    truncation=True,
)
```

Această metodă funcționează corespunzător, dar are dezavantajul de a returna un dicționar (cu cheile noastre, `input_ids`, `attention_mask` și `token_type_ids`, și valori care sunt liste ale listelor). De asemenea, va funcționa numai dacă aveți suficientă memorie RAM pentru a stoca întregul set de date în timpul tokenizării (în timp ce seturile de date din biblioteca 🤗 Datasets sunt fișiere [Apache Arrow](https://arrow.apache.org/) stocate pe disc, deci păstrați încărcate în memorie numai eșantioanele pe care le solicitați).

Pentru a păstra informațiile sub forma unui set de date, vom utiliza metoda [`Dataset.map()`](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.map). Acest lucru ne permite, de asemenea, o flexibilitate sporită, în cazul în care avem nevoie de mai multe preprocesări decât simpla tokenizare. Metoda `map()` funcționează prin aplicarea unei funcții pe fiecare element al setului de date, deci să definim o funcție care să tokenizeze intrările noastre:

```py
def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
```

Această funcție acceptă un dicționar (precum elementele din setul nostru de date) și returnează un nou dicționar cu cheile `input_ids`, `attention_mask` și `token_type_ids`. Rețineți că funcționează și în cazul în care dicționarul `example` conține mai multe eșantioane (fiecare cheie fiind o listă de propoziții), deoarece `tokenizer` funcționează pe liste de perechi de propoziții, așa cum am văzut anterior. Acest lucru ne va permite să folosim opțiunea `batched=True` în apelul nostru la `map`, ceea ce va accelera foarte mult tokenizarea. `tokenizer` este susținut de un tokenizer scris în Rust din biblioteca [🤗 Tokenizers](https://github.com/huggingface/tokenizers). Acest tokenizator poate fi foarte rapid, dar numai dacă îi oferim o mulțime de intrări deodată.

Rețineți că am omis deocamdată argumentul `padding` în funcția noastră de tokenizare. Acest lucru se datorează faptului că umplerea tuturor eșantioanelor la lungimea maximă nu este eficientă: este mai bine să umplem eșantioanele atunci când construim un batch, deoarece atunci trebuie să umplem doar la lungimea maximă din acel batch, și nu la lungimea maximă din întregul set de date. Acest lucru poate economisi mult timp și putere de procesare atunci când intrările au lungimi variate! 

Iată cum aplicăm funcția de tokenizare la toate seturile noastre de date simultan. Utilizăm `batched=True` în apelul către `map`, astfel încât funcția să fie aplicată la mai multe elemente ale setului nostru de date simultan, și nu la fiecare element în parte. Acest lucru permite o preprocesare mai rapidă.

```py
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
tokenized_datasets
```

Modul în care biblioteca 🤗 Datasets aplică această procesare este prin adăugarea de noi câmpuri la seturile de date, câte unul pentru fiecare cheie din dicționarul returnat de funcția de preprocesare:

```python out
DatasetDict({
    train: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 3668
    })
    validation: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 408
    })
    test: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 1725
    })
})
```

Puteți utiliza chiar și multiprocesarea atunci când aplicați funcția de preprocesare cu `map()` prin transmiterea unui argument `num_proc`. Nu am făcut acest lucru aici deoarece biblioteca 🤗 Tokenizers utilizează deja mai multe fire pentru a tokeniza mai rapid eșantioanele noastre, dar dacă nu utilizați un tokenizator rapid susținut de această bibliotecă, acest lucru v-ar putea accelera preprocesarea.

Funcția noastră `tokenize_function` returnează un dicționar cu cheile `input_ids`, `attention_mask` și `token_type_ids`, astfel încât aceste trei câmpuri sunt adăugate la toate diviziunile setului nostru de date. Rețineți că am fi putut, de asemenea, să modificăm câmpurile existente dacă funcția noastră de preprocesare a returnat o nouă valoare pentru o cheie existentă în setul de date căruia i-am aplicat funcția `map()`.

Ultimul lucru pe care va trebui să îl facem este să umplem toate exemplele la lungimea celui mai lung element atunci când grupăm elementele împreună - o tehnică la care ne referim ca *umplere dinamică*.

### Umplere dinamică[[umplere-dinamică]]

<Youtube id="7q5NyFT8REg"/>

{#if fw === 'pt'}
Funcția care este responsabilă de combinarea eșantioanelor în cadrul unui batch se numește *funcție de colaționare*. Este un argument pe care îl puteți trece atunci când construiți un `DataLoader`, implicit fiind o funcție care va converti eșantioanele în tensori PyTorch și le va concatena (recursiv dacă elementele dvs. sunt liste, tupluri sau dicționare). În cazul nostru, va fi de asemenea să aplicați umplutură pentru a avea toate intrările de aceeași lungime. Clasa `DataCollatorWithPadding` face exact acest lucru (și un pic mai mult, după cum am văzut anterior). Ia un tokenizer atunci când este instanțiat (pentru a știi ce token de completare să folosească și dacă modelul se așteaptă la padding în stânga sau în dreapta) și va face tot ce aveți nevoie. Umplerea dinamică poate fi aplicată în funcție de necesități la fiecare batch și pentru a evita să avem intrări prea lungi cu o mulțime de umpluturi. Acest lucru va accelera antrenamentul destul de mult, dar rețineți că, dacă vă antrenați pe o TPU, acest lucru poate cauza probleme - TPU-urile preferă forme fixe, chiar și atunci când ar putea necesita padding suplimentar.

{:else}

Funcția care este responsabilă de reunirea eșantioanelor în cadrul unui batch se numește *funcție de colaționare*. Funcția de colaționare implicită este o funcție care va converti eșantioanele în tf.Tensor și le va concatena (recursiv dacă elementele dvs. sunt liste, tuples sau dicționare). În cazul nostru, va fi de asemenea să aplicați umplutură pentru a avea toate intrările de aceeași lungime. Clasa `DataCollatorWithPadding` face exact acest lucru (și un pic mai mult, după cum am văzut anterior). Ia un tokenizer atunci când este instanțiat (pentru a știi ce token de completare să folosească și dacă modelul se așteaptă la padding în stânga sau în dreapta) și va face tot ce aveți nevoie. Umplerea dinamică poate fi aplicată în funcție de necesități la fiecare batch și pentru a evita să avem intrări prea lungi cu o mulțime de umpluturi. Acest lucru va accelera antrenamentul destul de mult, dar rețineți că, dacă vă antrenați pe o TPU, acest lucru poate cauza probleme - TPU-urile preferă forme fixe, chiar și atunci când ar putea necesita padding suplimentar.

{/if}

Pentru a face acest lucru în practică, trebuie să definim o funcție de colaționare care va aplica cantitatea corectă de umplutură elementelor din setul de date pe care dorim să le grupăm. Din fericire, biblioteca 🤗 Transformers ne oferă o astfel de funcție prin `DataCollatorWithPadding`. Aceasta preia un tokenizer atunci când o instanțiați (pentru a știi ce token de umplutură să utilizați și dacă modelul se așteaptă ca umplutura să fie la stânga sau la dreapta intrărilor) și va face tot ceea ce aveți nevoie:

{#if fw === 'pt'}
```py
from transformers import DataCollatorWithPadding

data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
```
{:else}
```py
from transformers import DataCollatorWithPadding

data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf")
```
{/if}

Pentru a testa această nouă opțiune, să luăm câteva eșantioane din setul nostru de formare pe care dorim să le grupăm. Aici, eliminăm coloanele `idx`, `sentence1` și `sentence2` deoarece nu vor fi necesare și conțin șiruri de caractere (și nu putem crea tensori cu șiruri de caractere) și aruncăm o privire la lungimile fiecărei intrări din batch:

```py
samples = tokenized_datasets["train"][:8]
samples = {k: v for k, v in samples.items() if k not in ["idx", "sentence1", "sentence2"]}
[len(x) for x in samples["input_ids"]]
```

```python out
[50, 59, 47, 67, 59, 50, 62, 32]
```

Nici o surpriză, obținem eșantioane de diferite lungimi, de la 32 la 67. Umplerea dinamică înseamnă că toate eșantioanele din acest batch ar trebui să fie umplute la o lungime de 67, lungimea maximă din cadrul batch-ului. Fără umplutură dinamică, toate eșantioanele ar trebui să fie umplute la lungimea maximă din întregul set de date sau la lungimea maximă pe care modelul o poate accepta. Să verificăm de două ori dacă `data_collator` completează dinamic batch-ul în mod corespunzător:

```py
batch = data_collator(samples)
{k: v.shape for k, v in batch.items()}
```

{#if fw === 'tf'}

```python out
{'attention_mask': TensorShape([8, 67]),
 'input_ids': TensorShape([8, 67]),
 'token_type_ids': TensorShape([8, 67]),
 'labels': TensorShape([8])}
```

{:else}

```python out
{'attention_mask': torch.Size([8, 67]),
 'input_ids': torch.Size([8, 67]),
 'token_type_ids': torch.Size([8, 67]),
 'labels': torch.Size([8])}
```

Perfect! Acum că am trecut de la text brut la batch-uri cu care modelul nostru se poate descurca, suntem gata să îl ajustăm!

{/if}

<Tip>

✏️ **Încearcați!** Replicați preprocesarea pe setul de date GLUE SST-2. Acesta este puțin diferit, deoarece este compus din propoziții simple în loc de perechi, dar restul lucrurilor pe care le-am făcut ar trebui să fie la fel. Pentru o provocare mai dificilă, încercați să scrieți o funcție de preprocesare care să funcționeze pe oricare dintre sarcinile GLUE.

</Tip>

{#if fw === 'tf'}

Acum că avem setul nostru de date și un compilator de date, trebuie să le punem împreună. Am putea încărca manual batch-uri și să le asamblăm, dar este mult de lucru și probabil nici nu este foarte eficient. În schimb, există o metodă simplă care oferă o soluție performantă la această problemă: `to_tf_dataset()`. Aceasta va împacheta un `tf.data.Dataset` cu funcția de colaționare încorporată. Dacă utilizați `DataCollatorWithPadding`, acest lucru nu va fi o problemă deoarece ea funcționează cu dicționare, liste și tensori NumPy, precum și cu tensori TensorFlow!

```py
tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
    columns=["attention_mask", "input_ids", "token_type_ids"],
    label_cols=["labels"],
    shuffle=True,
    collate_fn=data_collator,
    batch_size=8,
)

tf_validation_dataset = tokenized_datasets["validation"].to_tf_dataset(
    columns=["attention_mask", "input_ids", "token_type_ids"],
    label_cols=["labels"],
    shuffle=False,
    collate_fn=data_collator,
    batch_size=8,
)
```

Și asta este tot! Putem folosi aceste seturi de date în cursul următor, unde instruirea va fi extrem de simplă după toată munca grea de preprocesare a datelor.

{/if}


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter3/2.mdx" />

### Folosiți setul de date adnotat[[folosiți-setul-de-date-adnotat]]
https://huggingface.co/learn/course/ro/chapter10/5.md

# Folosiți setul de date adnotat[[folosiți-setul-de-date-adnotat]]

<CourseFloatingBanner chapter={10}
  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/chapter10/section5.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter10/section5.ipynb"},
]} />

Vom învăța acum cum să exportăm și să folosim datele adnotate pe care le avem în Argilla.

## Încărcați setul de date

Mai întâi, va trebui să ne asigurăm că suntem conectați la instanța noastră Argilla ca în pașii anteriori:

```python
import argilla as rg

HF_TOKEN = "..."  # doar pentru space-uri private

client = rg.Argilla(
    api_url="...",
    api_key="...",
    headers={"Authorization": f"Bearer {HF_TOKEN}"},  # doar pentru space-uri private
)
```

Și acum, vom încărca setul de date cu care vom lucra:

```python
dataset = client.datasets(name="ag_news")
```

Încărcarea setului de date și apelarea înregistrărilor sale cu `dataset.records` este suficient pentru a începe să folosiți setul de date și înregistrările pentru propriile scopuri și pipeline-uri. Cu toate acestea, vom învăța de asemenea cum să facem câteva operații opționale, precum filtrarea înregistrărilor și exportarea setului de date către Hugging Face Hub.

## Filtrați setul de date

Uneori doriți să folosiți doar înregistrările care au fost completate, așa că vom filtra mai întâi înregistrările din setul nostru de date pe baza statutului lor:

```python
status_filter = rg.Query(filter=rg.Filter([("status", "==", "completed")]))

filtered_records = dataset.records(status_filter)
```

>[!TIP]
>⚠️ Rețineți că înregistrările cu statutul `completed` (adică înregistrările care îndeplinesc răspunsurile minime trimise configurate în setările de distribuire a sarcinii) ar putea avea mai mult de un răspuns și că fiecare răspuns poate avea orice statut dintre `submitted`, `draft` sau `discarded`.

Aflați mai multe despre interogarea și filtrarea înregistrărilor în [documentația Argilla](https://docs.argilla.io/latest/how_to_guides/query/).

## Exportați către Hub

Acum putem exporta adnotările noastre către Hugging Face Hub, astfel încât să le putem împărtăși cu alții. Pentru a face asta, va trebui să convertim înregistrările într-un 🤗 Dataset și apoi să îl împingem către Hub:

```python
filtered_records.to_datasets().push_to_hub("argilla/ag_news_annotated")
```

Alternativ, putem exporta direct setul de date Argilla complet (inclusiv înregistrările în așteptare) astfel:

```python
dataset.to_hub(repo_id="argilla/ag_news_annotated")
```

Aceasta este o alegere interesantă în cazul în care alții vor să deschidă setul de date în instanțele lor Argilla, deoarece setările sunt salvate automat și ei pot pur și simplu să importe setul de date complet folosind o singură linie de cod:

```python
dataset = rg.Dataset.from_hub(repo_id="argilla/ag_news_annotated")
``` 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter10/5.mdx" />

### Introducere în Argilla[[introducere-în-argilla]]
https://huggingface.co/learn/course/ro/chapter10/1.md

# Introducere în Argilla[[introducere-în-argilla]]

<CourseFloatingBanner
    chapter={10}
    classNames="absolute z-10 right-0 top-0"
/>

În Capitolul 5 ați învățat cum să construiți un set de date folosind biblioteca 🤗 Datasets și în Capitolul 6 ați explorat cum să ajustați modele pentru unele sarcini NLP comune. În acest capitol, veți învăța cum să folosiți [Argilla](https://argilla.io) pentru a **anota și cura seturi de date** pe care le puteți folosi pentru a antrena și evalua modelele dvs. .

Cheia pentru antrenarea modelelor care funcționează bine este să aveți date de înaltă calitate. Deși există unele seturi de date bune în Hub pe care le-ați putea folosi pentru a antrena și evalua modelele dvs. , acestea s-ar putea să nu fie relevante pentru aplicația sau cazul de utilizare specific. În acest scenariu, s-ar putea să doriți să construiți și să curați un set de date propriu. Argilla vă va ajuta să faceți acest lucru eficient.

<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter10/signin-hf-page.png" alt="Pagina de autentificare Argilla."/>

Cu Argilla puteți:

- transforma datele nestructurate în **date structurate** care să fie folosite în sarcini NLP.
- cura un set de date pentru a trece de la un set de date de calitate scăzută la un **set de date de înaltă calitate**.
- aduna **feedback uman** pentru LLM-uri și modele multi-modale.
- invita experți să colaboreze cu dvs. în Argilla, sau să aduceți adnotări de la mulțime!

Iată câteva dintre lucrurile pe care le veți învăța în acest capitol:

- Cum să vă configurați propria instanță Argilla.
- Cum să încărcați un set de date și să îl configurați pe baza unor sarcini NLP populare.
- Cum să folosiți interfața Argilla pentru a vă anota setul de date.
- Cum să folosiți setul de date curat și să îl exportați către Hub. 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter10/1.mdx" />

### Încărcați setul de date în Argilla[[încărcați-setul-de-date-în-argilla]]
https://huggingface.co/learn/course/ro/chapter10/3.md

# Încărcați setul de date în Argilla[[încărcați-setul-de-date-în-argilla]]

<CourseFloatingBanner chapter={10}
  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/chapter10/section3.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter10/section3.ipynb"},
]} />

În funcție de sarcina NLP cu care lucrați și cazul de utilizare sau aplicația specifică, datele dvs. și sarcina de adnotare vor arăta diferit. Pentru această secțiune a cursului, vom folosi [un set de date care colectează știri](https://huggingface.co/datasets/SetFit/ag_news) pentru a completa două sarcini: o clasificare de text asupra subiectului fiecărui text și o clasificare de token-uri pentru a identifica entitățile numite menționate.

<iframe
  src="https://huggingface.co/datasets/SetFit/ag_news/embed/viewer/default/train"
  frameborder="0"
  width="100%"
  height="560px"
></iframe>

Este posibil să importați seturi de date din Hub folosind direct interfața Argilla, dar vom folosi SDK-ul pentru a învăța cum putem face editări suplimentare la date dacă este necesar.

## Configurați setul de date

Primul pas este să ne conectăm la instanța Argilla așa cum am făcut în secțiunea anterioară:

```python
import argilla as rg

HF_TOKEN = "..."  # doar pentru space-uri private

client = rg.Argilla(
    api_url="...",
    api_key="...",
    headers={"Authorization": f"Bearer {HF_TOKEN}"},  # doar pentru space-uri private
)
```

Acum putem gândi la setările setului nostru de date în Argilla. Acestea reprezintă sarcina de adnotare pe care o vom face asupra datelor noastre. Mai întâi, putem încărca setul de date din Hub și să inspectăm caracteristicile acestuia, astfel încât să ne asigurăm că configurăm setul de date corect.

```python
from datasets import load_dataset

data = load_dataset("SetFit/ag_news", split="train")
data.features
```

Acestea sunt caracteristicile setului nostru de date:

```python out
{'text': Value(dtype='string', id=None),
 'label': Value(dtype='int64', id=None),
 'label_text': Value(dtype='string', id=None)}
```

Conține un `text` și de asemenea câteva etichete inițiale pentru clasificarea textului. Le vom adăuga la setările setului nostru de date împreună cu o întrebare `spans` pentru entitățile numite:

```python
settings = rg.Settings(
    fields=[rg.TextField(name="text")],
    questions=[
        rg.LabelQuestion(
            name="label", title="Clasificați textul:", labels=data.unique("label_text")
        ),
        rg.SpanQuestion(
            name="entities",
            title="Evidențiați toate entitățile din text:",
            labels=["PERSON", "ORG", "LOC", "EVENT"],
            field="text",
        ),
    ],
)
```

Să aprofundăm puțin ce înseamnă aceste setări. Mai întâi, am definit **fields** (câmpuri), acestea includ informațiile pe care le vom anota. În acest caz, avem doar un câmp și vine sub forma unui text, așa că am ales un `TextField`.

Apoi, definim **questions** (întrebări) care reprezintă sarcinile pe care vrem să le efectuăm asupra datelor noastre:

- Pentru sarcina de clasificare de text am ales o `LabelQuestion` și am folosit valorile unice ale coloanei `label_text` ca etichetele noastre, pentru a ne asigura că întrebarea este compatibilă cu etichetele care există deja în setul de date.
- Pentru sarcina de clasificare de token-uri, vom avea nevoie de o `SpanQuestion`. Am definit un set de etichete pe care le vom folosi pentru acea sarcină, plus câmpul pe care vom desena intervalele.

Pentru a afla mai multe despre toate tipurile disponibile de câmpuri și întrebări și alte setări avansate, precum metadatele și vectorii, mergeți la [documentația Argilla](https://docs.argilla.io/latest/how_to_guides/dataset/#define-dataset-settings).

## Încărcați setul de date

Acum că am definit câteva setări, putem crea setul de date:

```python
dataset = rg.Dataset(name="ag_news", settings=settings)

dataset.create()
```

Setul de date apare acum în instanța noastră Argilla, dar veți vedea că este gol:

<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter10/empty_dataset.png" alt="Captură de ecran cu setul de date gol."/>

Acum trebuie să adăugăm înregistrările pe care le vom anota, adică rândurile din setul nostru de date. Pentru a face asta, va trebui pur și simplu să înregistrăm datele ca înregistrări și să oferim o mapare pentru acele elemente care nu au același nume în seturile de date din hub și Argilla:

```python
dataset.records.log(data, mapping={"label_text": "label"})
```

În maparea noastră, am specificat că coloana `label_text` din setul de date ar trebui mapată la întrebarea cu numele `label`. În acest fel, vom folosi etichetele existente din setul de date ca pre-adnotări astfel încât să putem anota mai rapid.

În timp ce înregistrările continuă să se înregistreze, puteți începe deja să lucrați cu setul de date în interfața Argilla. În acest moment, ar trebui să arate așa:

<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter10/argilla_initial_dataset.png" alt="Captură de ecran cu setul de date în Argilla."/>

Acum setul nostru de date este gata să începem adnotarea! 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter10/3.mdx" />

### Argilla, terminat![[argilla-terminat]]
https://huggingface.co/learn/course/ro/chapter10/6.md

# Argilla, terminat![[argilla-terminat]]

<CourseFloatingBanner
    chapter={10}
    classNames="absolute z-10 right-0 top-0"
/>

Asta e tot! Felicitări! 👏

În acest capitol, ați învățat pașii de bază pentru a:
- configura Argilla.
- adnota pentru a îmbunătăți calitatea setului de date.
- adapta un set de date existent și să îl reutilizați pentru o sarcină NLP diferită.
- partaja setul de date adnotat cu comunitatea în Hugging Face Hub.

## Ce urmează?
- Verificați mai multe tutoriale pas cu pas pentru alte sarcini populare în [pagina de tutoriale](https://docs.argilla.io/latest/tutorials/).
- Puteți de asemenea să explorați alte exemple de seturi de date în această [demonstrație](https://demo.argilla.io/sign-in?auth=ZGVtbzoxMjM0NTY3OA==).
- Dacă doriți să continuați să învățați despre Argilla și caracteristici mai avansate, consultați [documentația Argilla](https://docs.argilla.io/latest/). 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter10/6.mdx" />

### Adnotați setul de date[[adnotați-setul-de-date]]
https://huggingface.co/learn/course/ro/chapter10/4.md

# Adnotați setul de date[[adnotați-setul-de-date]]

<CourseFloatingBanner
    chapter={10}
    classNames="absolute z-10 right-0 top-0"
/>

Acum este timpul să începem să lucrăm din interfața Argilla pentru a adnota setul nostru de date.

## Aliniați echipa cu îndrumări de adnotare

Înainte să începeți să adnotați setul de date, este întotdeauna o practică bună să scrieți câteva îndrumări, mai ales dacă lucrați ca parte dintr-o echipă. Aceasta vă va ajuta să vă aliniați asupra sarcinii și utilizării diferitelor etichete, și să rezolvați întrebările sau conflictele când apar.

În Argilla, puteți merge la pagina de setări a setului de date din interfață și să modificați îndrumările și descrierile întrebărilor pentru a ajuta la aliniere.

<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter10/argilla_dataset_settings.png" alt="Captură de ecran cu pagina Dataset Settings din Argilla."/>

Dacă doriți să aprofundați subiectul despre cum să scrieți îndrumări bune, vă recomandăm să citiți [această postare de blog](https://argilla.io/blog/annotation-guidelines-practices) și referințele bibliografice menționate acolo.

## Distribuiți sarcina

În pagina de setări a setului de date, puteți de asemenea să schimbați setările de distribuire a setului de date. Aceasta vă va ajuta să adnotați mai eficient când lucrați ca parte dintr-o echipă. Valoarea implicită pentru răspunsurile minime trimise este 1, ceea ce înseamnă că de îndată ce o înregistrare are 1 răspuns trimis va fi considerată completă și va conta către progresul din setul de date.

Uneori, doriți să aveți mai mult de un răspuns trimis per înregistrare, de exemplu, dacă doriți să analizați acordul între adnotatori în sarcina dvs. . În acel caz, asigurați-vă că schimbați această setare la un număr mai mare, dar întotdeauna mai mic sau egal cu numărul total de adnotatori. Dacă lucrați singur la sarcină, doriți ca această setare să fie 1.

## Adnotați înregistrări

>[!TIP]
>💡 Dacă implementați Argilla într-un Hugging Face Space, orice membri ai echipei se vor putea autentifica folosind Hugging Face OAuth. Altfel, s-ar putea să trebuiască să creați utilizatori pentru ei urmând [acest ghid](https://docs.argilla.io/latest/how_to_guides/user/).

Când deschideți setul de date, veți realiza că prima întrebare este deja completată cu câteva etichete sugerate. Aceasta pentru că în secțiunea anterioară am mapat întrebarea noastră numită `label` la coloana `label_text` din setul de date, astfel încât trebuie pur și simplu să revizuim și să corectăm etichetele deja existente:

<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter10/argilla_initial_dataset.png" alt="Captură de ecran cu setul de date în Argilla."/>

Pentru clasificarea de token-uri, va trebui să adăugăm toate etichetele manual, deoarece nu am inclus nicio sugestie. Așa ar putea arăta după adnotările de interval:

<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter10/argilla_dataset_with_spans.png" alt="Captură de ecran cu setul de date din Argilla cu intervale adnotate."/>

Pe măsură ce vă deplasați prin diferitele înregistrări, există diferite acțiuni pe care le puteți întreprinde:
- trimiteți răspunsurile, odată ce ați terminat cu înregistrarea.
- salvați-le ca ciornă, în cazul în care doriți să reveniți la ele mai târziu.
- renunțați la ele, dacă înregistrarea nu ar trebui să facă parte din setul de date sau nu veți da răspunsuri pentru ea.

În secțiunea următoare, veți învăța cum puteți exporta și folosi acele adnotări. 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter10/4.mdx" />

### Quiz de final de capitol[[quiz-de-final-de-capitol]]
https://huggingface.co/learn/course/ro/chapter10/7.md

# Quiz de final de capitol[[quiz-de-final-de-capitol]]

<CourseFloatingBanner
    chapter={10}
    classNames="absolute z-10 right-0 top-0"
/>

Să testăm ce ați învățat în acest capitol!

### 1. Pentru ce puteți folosi Argilla?

<Question
	choices={[
		{
			text: "Pentru a transforma datele nestructurate în date structurate pentru sarcini NLP",
			explain: "Puteți folosi Argilla pentru a adăuga adnotări la un set de date și să îl folosiți pentru sarcini NLP.",
            correct: true
		},
		{
			text: "Pentru a extrage date de pe un site web public pentru a construi un set de date",
			explain: "Aceasta nu este o caracteristică din Argilla, dar puteți extrage date de pe un site web public și să îl transformați într-un set de date Argilla pentru adnotări folosind SDK-ul Python."
		},
		{
			text: "Pentru a îmbunătăți calitatea unui set de date existent",
			explain: "Puteți folosi adnotările anterioare ca sugestii și să le corectați pentru a îmbunătăți calitatea unui set de date.",
			correct: true
		},
		{
			text: "Pentru a adapta un set de date existent la propriul caz de utilizare",
			explain: "Puteți folosi diferite tipuri de întrebări în Argilla pentru a adapta un set de date existent la propriul caz de utilizare.",
			correct: true
		},
		{
			text: "Pentru a antrena modelul",
			explain: "Nu puteți antrena un model direct în Argilla, dar puteți folosi datele pe care le curați în Argilla pentru a antrena propriul model",
		},
		{
			text: "Pentru a genera seturi de date sintetice",
			explain: "Pentru a genera seturi de date sintetice, puteți folosi pachetul distilabel și apoi să folosiți Argilla pentru a revizui și cura datele generate.",
		}
	]}
/>

### 2. Argilla funcționează DOAR în Hugging Face Spaces și cu Hugging Face Datasets.

<Question
	choices={[
        {
			text: "Adevărat",
			explain: "Puteți de asemenea să implementați Argilla local folosind Docker și puteți folosi SDK-ul Python pentru a încărca orice tip de date, inclusiv Hugging Face Datasets."
        },
        {
			text: "Fals",
			explain: "Puteți de asemenea să implementați Argilla local folosind Docker și puteți folosi SDK-ul Python pentru a încărca orice tip de date, inclusiv Hugging Face Datasets.",
			correct: true
        }
	]}
/>

### 3. Aveți nevoie de un token Hugging Face pentru a conecta SDK-ul Python la serverul Argilla.

<Question
	choices={[
        {
			text: "Adevărat",
			explain: "Aceasta este necesară doar dacă Space-ul Argilla este privat!",
        },
        {
			text: "Fals",
			explain: "Nu aveți nevoie de un token dacă folosiți un Space Argilla public sau o implementare locală cu Docker.",
			correct: true
        }
	]}
/>

### 4. Ce sunt **câmpurile** în Argilla? Câte câmpuri puteți folosi?

<Question
	choices={[
        {
			text: "Câmpurile arată datele pe care le adnotăm. Toate aceste informații trebuie să fie colectate într-un singur câmp.",
			explain: "Puteți să împărțiți informațiile pe mai multe câmpuri, dacă aveți nevoie."
        },
		{
			text: "Câmpurile arată datele pe care le adnotăm. Toate aceste informații pot fi împărțite pe mai multe câmpuri.",
			explain: "Da, puteți avea mai multe câmpuri și de asemenea câmpuri de diferite tipuri (text, chat, imagine...) în funcție de tipul de date pe care le adnotați.",
			correct: true
        },
        {
			text: "Câmpurile conțin metadatele înregistrărilor. Puteți folosi câte aveți nevoie.",
			explain: "Puteți avea atât câmpuri cât și metadate în setul de date, dar ele servesc scopuri separate. Metadatele sunt folosite pentru filtrare și sortare ca informații extra, în timp ce câmpurile arată datele pe care le adnotăm."
        }
	]}
/>

### 5. Care este cel mai bun tip de întrebare pentru o sarcină de clasificare de token-uri?

<Question
	choices={[
        {
			text: "O SpanQuestion",
			explain: "SpanQuestions vă permit să evidențiați bucăți de text și să aplicați o etichetă la ele. Acesta este cel mai bun tip pentru o sarcină de clasificare de token-uri.",
            correct: true
        },
        {
			text: "O LabelQuestion",
			explain: "Acest tip de întrebare vă permite să selectați o etichetă care se aplică la întreaga înregistrare. Acest tip este cel mai bun pentru o sarcină de clasificare de text."
        },
        {
			text: "O TextQuestion",
			explain: "Acest tip de întrebare vă permite să scrieți text. Aceasta nu este potrivită pentru o sarcină de clasificare de token-uri."
        },
        {
			text: "Niciuna din cele de mai sus",
			explain: "SpanQuestions vă permit să evidențiați bucăți de text și să aplicați o etichetă la ele. Acesta este cel mai bun tip pentru o sarcină de clasificare de token-uri."
        }
	]}
/>

### 6. Care este scopul butonului "Save as draft"?

<Question
	choices={[
        {
			text: "Să trimiteți răspunsurile",
			explain: "Acest buton salvează răspunsurile, dar nu le trimite",
        },
        {
			text: "Să salvați răspunsurile fără să le trimiteți",
			explain: "Aceasta este o metodă validă de încărcare a unui model Hugging Face din Hub",
			correct: true
        },
        {
			text: "Să renunțați la o înregistrare",
			explain: "Încercați din nou -- nu puteți încărca un model folosind prefixul 'demos'."
        }
	]}
/>

### 7. Argilla nu oferă etichete sugerate automat, trebuie să furnizați acele date singur.

<Question
	choices={[
        {
			text: "Adevărat",
			explain: "Puteți adăuga sugestii la înregistrările dvs. (sau să le actualizați) în orice moment al proiectului.",
            correct: true
        },
        {
			text: "Fals",
			explain: "Dacă doriți să vedeți etichete sugerate, trebuie să le înregistrați singur când creați înregistrările sau la un moment ulterior."
        }
	]}
/>

### 8. Selectați toți pașii necesari pentru a exporta complet un set de date Argilla către Hub:

<Question
	choices={[
        {
			text: "Trebuie să fiți conectat la serverul Argilla: <code>client= rg.Argilla(api_url='...', api_key='...')</code>",
			explain: "Da, pentru a interacționa cu serverul va trebui să îl instanțiați mai întâi.",
            correct: true
        },
		{
			text: "Importați setul de date din hub: <code>dataset = rg.Dataset.from_hub(repo_id='argilla/ag_news_annotated')</code>",
			explain: "Nu. Aceasta este pentru a importa un set de date din Hub în instanța Argilla.",
        },
        {
			text: "Încărcați setul de date: <code>dataset = client.datasets(name='my_dataset')</code>",
			explain: "Da, veți avea nevoie de aceasta pentru operații ulterioare",
			correct: true
        },
        {
			text: "Convertiți setul de date Argilla într-un set de date Datasets: <code>dataset = dataset.to_datasets()</code>",
			explain: "Aceasta nu este necesară dacă exportați setul de date complet. Argilla se va ocupa de aceasta pentru dvs. . Cu toate acestea, s-ar putea să aveți nevoie de ea dacă lucrați cu un subset de înregistrări."
        },
        {
			text: "Folosiți metoda </code>to_hub</code> pentru a exporta setul de date: <code>dataset.to_hub(repo_id='my_username/dataset_name')</code>",
			explain: "Aceasta va împinge setul de date la repo id-ul indicat, și va crea un repo nou dacă nu există.",
            correct: true
        },
	]}
/> 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter10/7.mdx" />

### Configurați-vă instanța Argilla[[configurați-vă-instanța-argilla]]
https://huggingface.co/learn/course/ro/chapter10/2.md

# Configurați-vă instanța Argilla[[configurați-vă-instanța-argilla]]

<CourseFloatingBanner chapter={10}
  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/chapter10/section2.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter10/section2.ipynb"},
]} />

Pentru a începe să folosiți Argilla, va trebui să configurați mai întâi propria instanță Argilla. Apoi va trebui să instalați SDK-ul Python pentru a putea gestiona Argilla folosind cod Python.

## Implementați interfața Argilla

Cea mai ușoară modalitate de a configura instanța Argilla este prin Hugging Face Spaces. Pentru a crea Space-ul Argilla, urmați pur și simplu [acest formular](https://huggingface.co/new-space?template=argilla%2Fargilla-template-space). Dacă aveți nevoie de îndrumări suplimentare, consultați [ghidul rapid Argilla](https://docs.argilla.io/latest/getting_started/quickstart/).
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter10/space_config.png" alt="Formularul de configurare Space."/>

>[!WARNING]
> ⚠️ S-ar putea să doriți să activați **Persistent storage** astfel încât datele să nu se piardă dacă Space-ul este pus în pauză sau repornit.
> Puteți face asta din Settings-urile Space-ului dvs. .

Odată ce Argilla este operațională, vă puteți autentifica cu acreditările dvs. .

## Instalați și conectați SDK-ul Python

Acum puteți merge în mediul Python sau notebook-ul dvs. și instala biblioteca argilla:

`!pip install argilla`

Să ne conectăm cu instanța noastră Argilla. Pentru asta veți avea nevoie de următoarele informații:

- **URL-ul API-ului dvs. **: Acesta este URL-ul unde rulează Argilla. Dacă folosiți un Space, puteți deschide Space-ul, face clic pe cele trei puncte din colțul din dreapta sus, apoi "Embed this Space" și copiați **Direct URL**. Ar trebui să arate ca `https://<numele-utilizatorului>.<numele-space-ului>.hf.space`.
- **Cheia API**: Pentru a obține cheia, autentificați-vă în instanța Argilla și mergeți la "My Settings", apoi copiați cheia API.
- **Token-ul HF**: Dacă Space-ul dvs. este privat, veți avea nevoie de un Access Token în contul Hugging Face Hub cu permisiuni de scriere.

```python
import argilla as rg

HF_TOKEN = "..."  # doar pentru space-uri private

client = rg.Argilla(
    api_url="...",
    api_key="...",
    headers={"Authorization": f"Bearer {HF_TOKEN}"},  # doar pentru space-uri private
)
```

Pentru a verifica că totul funcționează corect, vom apela `me`. Aceasta ar trebui să returneze utilizatorul nostru:

```python
client.me
```

Dacă a funcționat, instanța Argilla este operațională și sunteți conectat la ea! Felicitări!

Acum putem începe să încărcăm primul nostru set de date în Argilla. 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter10/2.mdx" />

### Cum să scrieți un issue bun[[cum-sa-scrieti-un-issue-bun]]
https://huggingface.co/learn/course/ro/chapter8/5.md

# Cum să scrieți un issue bun[[cum-sa-scrieti-un-issue-bun]]

<CourseFloatingBanner chapter={8}
  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/chapter8/section5.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter8/section5.ipynb"},
]} />

Când întâlniți ceva care nu pare corect cu una dintre bibliotecile Hugging Face, ar trebui cu siguranță să ne spuneți astfel încât să o putem repara (același lucru se aplică pentru orice bibliotecă open source, de altfel). Dacă nu sunteți complet siguri dacă bug-ul se află în propriul vostru cod sau într-una dintre bibliotecile noastre, primul loc de verificat sunt [forumurile](https://discuss.huggingface.co/). Comunitatea vă va ajuta să clarificați acest lucru, și echipa Hugging Face urmărește de asemenea îndeaproape discuțiile de acolo.

<Youtube id="_PAli-V4wj0"/>

Când sunteți siguri că aveți un bug în mână, primul pas este să construiți un exemplu minimal reproductibil.

## Crearea unui exemplu minimal reproductibil[[crearea-unui-exemplu-minimal-reproductibil]]

Este foarte important să izolați bucata de cod care produce bug-ul, deoarece nimeni din echipa Hugging Face nu este magician (încă), și nu pot repara ceea ce nu pot vedea. Un exemplu minimal reproductibil ar trebui, așa cum indică numele, să fie reproductibil. Aceasta înseamnă că nu ar trebui să se bazeze pe niciun fișier extern sau date pe care le-ați putea avea. Încercați să înlocuiți datele pe care le folosiți cu niște valori fictive care arată ca cele reale și încă produc aceeași eroare.

<Tip>

🚨 Multe probleme din repository-ul 🤗 Transformers sunt nerezolvate deoarece datele folosite pentru a le reproduce nu sunt accesibile.

</Tip>

Odată ce aveți ceva care este autonom, puteți încerca să îl reduceți la și mai puține linii de cod, construind ceea ce numim un _exemplu minimal reproductibil_. Deși aceasta necesită puțină muncă suplimentară din partea voastră, veți fi aproape garantat să primiți ajutor și o reparație dacă furnizați un reproductor de bug frumos și scurt.

Dacă vă simțiți suficient de confortabil, mergeți să inspectați codul sursă unde se întâmplă bug-ul vostru. Ați putea găsi o soluție la problema voastră (caz în care puteți chiar sugera un pull request pentru a o repara), dar mai general, aceasta poate ajuta întreținătorii să înțeleagă mai bine sursa când citesc raportul vostru.

## Completarea șablonului de issue[[completarea-sablonului-de-issue]]

Când depuneți un issue, veți observa că există un șablon de completat. Vom urma pe cel pentru [problemele 🤗 Transformers](https://github.com/huggingface/transformers/issues/new/choose) aici, dar același tip de informații va fi necesar dacă raportați o problemă într-un alt repository. Nu lăsați șablonul gol: luarea timpului pentru a-l completa va maximiza șansele voastre de a primi un răspuns și de a vă rezolva problema.

În general, când depuneți un issue, rămâneți întotdeauna politicoși. Acesta este un proiect open source, deci folosiți software gratuit, și nimeni nu are nicio obligație să vă ajute. Puteți include ceea ce simțiți că este critică justificată în problema voastră, dar atunci întreținătorii s-ar putea să o ia prost și să nu se grăbească să vă ajute. Asigurați-vă că citiți [codul de conduită](https://github.com/huggingface/transformers/blob/master/CODE_OF_CONDUCT.md) al proiectului.

### Includerea informațiilor despre mediul vostru[[includerea-informatiilor-despre-mediul-vostru]]

🤗 Transformers oferă un utilitar pentru a obține toate informațiile de care avem nevoie despre mediul vostru. Doar tastați următoarele în terminalul vostru:

```
transformers-cli env
```

și ar trebui să obțineți ceva ca aceasta:

```out
Copy-and-paste the text below in your GitHub issue and FILL OUT the two last points.

- `transformers` version: 4.12.0.dev0
- Platform: Linux-5.10.61-1-MANJARO-x86_64-with-arch-Manjaro-Linux
- Python version: 3.7.9
- PyTorch version (GPU?): 1.8.1+cu111 (True)
- Tensorflow version (GPU?): 2.5.0 (True)
- Flax version (CPU?/GPU?/TPU?): 0.3.4 (cpu)
- Jax version: 0.2.13
- JaxLib version: 0.1.65
- Using GPU in script?: <fill in>
- Using distributed or parallel set-up in script?: <fill in>
```

Puteți de asemenea adăuga un `!` la începutul comenzii `transformers-cli env` pentru a o executa dintr-o celulă de notebook, și apoi să copiați și lipiți rezultatul la începutul problemei voastre.

### Etichetarea persoanelor[[etichetarea-persoanelor]]

Etichetarea persoanelor prin tastarea unui `@` urmat de handle-ul lor GitHub le va trimite o notificare astfel încât să vă vadă problema și ar putea răspunde mai repede. Folosiți aceasta cu moderație, deoarece persoanele pe care le etichetați s-ar putea să nu aprecieze să fie notificate dacă este ceva cu care nu au nicio legătură directă. Dacă v-ați uitat la fișierele sursă legate de bug-ul vostru, ar trebui să etichetați ultima persoană care a făcut modificări la linia pe care credeți că este responsabilă pentru problema voastră (puteți găsi această informație uitându-vă la acea linie pe GitHub, selectând-o, apoi făcând clic pe "View git blame").

Altfel, șablonul oferă sugestii de persoane de etichetat. În general, nu etichetați niciodată mai mult de trei persoane!

### Includerea unui exemplu reproductibil[[includerea-unui-exemplu-reproductibil]]

Dacă ați reușit să creați un exemplu autonom care produce bug-ul, acum este momentul să îl includeți! Tastați o linie cu trei backtick-uri urmată de `python`, astfel:

```
```python
```

Apoi lipiți exemplul vostru minimal reproductibil și tastați o linie nouă cu trei backtick-uri. Aceasta va asigura că codul vostru este formatat corespunzător.

Dacă nu ați reușit să creați un exemplu reproductibil, explicați în pași clari cum ați ajuns la problema voastră. Includeți un link către un notebook Google Colab unde ați primit eroarea dacă puteți. Cu cât mai multe informații partajați, cu atât mai bine vor putea întreținătorii să vă răspundă.

În toate cazurile, ar trebui să copiați și să lipiți întregul mesaj de eroare pe care îl primiți. Dacă lucrați în Colab, amintiți-vă că unele dintre frame-uri pot fi comprimate automat în stack trace, așa că asigurați-vă că le extindeți înainte de copiere. Ca și cu exemplul de cod, puneți acel mesaj de eroare între două linii cu trei backtick-uri, astfel încât să fie formatat corespunzător.

### Descrierea comportamentului așteptat[[descrierea-comportamentului-asteptat]]

Explicați în câteva linii ce ați așteptat să obțineți, astfel încât întreținătorii să înțeleagă complet problema. Această parte este în general destul de evidentă, așa că ar trebui să încapă într-o propoziție, dar în unele cazuri ați putea avea multe de spus.

## Și apoi ce?[[si-apoi-ce]]

Odată ce problema voastră este depusă, asigurați-vă să verificați rapid că totul arată în regulă. Puteți edita problema dacă ați făcut o greșeală, sau chiar să îi schimbați titlul dacă realizați că problema este diferită de ceea ce ați gândit inițial.

Nu are sens să faceți ping la oameni dacă nu primiți un răspuns. Dacă nimeni nu vă ajută în câteva zile, este probabil că nimeni nu a putut înțelege problema voastră. Nu ezitați să vă întoarceți la exemplul reproductibil. Îl puteți face mai scurt și mai la obiect? Dacă nu primiți un răspuns într-o săptămână, puteți lăsa un mesaj cerând cu blândețe ajutor, mai ales dacă v-ați editat problema pentru a include mai multe informații despre problemă. 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter8/5.mdx" />

### Introducere[[introducere]]
https://huggingface.co/learn/course/ro/chapter8/1.md

# Introducere[[introducere]]

<CourseFloatingBanner
    chapter={8}
    classNames="absolute z-10 right-0 top-0"
/>

Acum că știți cum să abordați cele mai frecvente sarcini NLP cu 🤗 Transformers, ar trebui să puteți începe propriile proiecte! În acest capitol vom explora ce să faceți când întâmpinați o problemă. Veți învăța cum să depanați cu succes codul sau antrenamentul și cum să cereți ajutor de la comunitate dacă nu reușiți să rezolvați problema singuri. Și dacă credeți că ați găsit o eroare într-una dintre bibliotecile Hugging Face, vă vom arăta cea mai bună modalitate de a o raporta astfel încât problema să fie rezolvată cât mai rapid posibil.

Mai precis, în acest capitol veți învăța:

- Primul lucru de făcut când primiți o eroare
- Cum să cereți ajutor pe [forumuri](https://discuss.huggingface.co/)
- Cum să faceți debugging pipeline-ului de antrenament
- Cum să scrieți o problemă bună

Nimic din acestea nu este specific 🤗 Transformers sau ecosistemului Hugging Face, desigur; lecțiile din acest capitol sunt aplicabile majorității proiectelor open source! 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter8/1.mdx" />

### Cum să ceri ajutor pe forumuri[[cum-sa-ceri-ajutor-pe-forumuri]]
https://huggingface.co/learn/course/ro/chapter8/3.md

# Cum să ceri ajutor pe forumuri[[cum-sa-ceri-ajutor-pe-forumuri]]

<CourseFloatingBanner chapter={8}
  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/chapter8/section3.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter8/section3.ipynb"},
]} />

<Youtube id="S2EEG3JIt2A"/>

[Forumurile Hugging Face](https://discuss.huggingface.co) sunt un loc excelent pentru a obține ajutor de la echipa open source și comunitatea mai largă Hugging Face. Iată cum arată pagina principală într-o zi obișnuită:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forums.png" alt="Forumurile Hugging Face." width="100%"/>
</div>

În partea stângă puteți vedea toate categoriile în care sunt grupate diversele subiecte, în timp ce partea dreaptă arată subiectele cele mai recente. Un subiect este o postare care conține un titlu, o categorie și o descriere; este destul de similar cu formatul GitHub issues pe care l-am văzut când am creat propriul nostru set de date în [Capitolul 5](/course/chapter5). Așa cum sugerează numele, categoria [Beginners](https://discuss.huggingface.co/c/beginners/5) este destinată în principal persoanelor care abia încep cu bibliotecile și ecosistemul Hugging Face. Orice întrebare despre oricare dintre biblioteci este binevenită acolo, fie pentru a depana cod sau pentru a cere ajutor despre cum să faci ceva. (Totuși, dacă întrebarea ta se referă la o bibliotecă în particular, probabil ar trebui să mergi la categoria corespunzătoare a bibliotecii pe forum.)

În mod similar, categoriile [Intermediate](https://discuss.huggingface.co/c/intermediate/6) și [Research](https://discuss.huggingface.co/c/research/7) sunt pentru întrebări mai avansate, de exemplu despre biblioteci sau despre cercetări NLP noi și interesante pe care ați dori să le discutați.

Și, în mod natural, ar trebui să menționăm și categoria [Course](https://discuss.huggingface.co/c/course/20), unde puteți pune orice întrebări aveți care sunt legate de cursul Hugging Face!

Odată ce ați selectat o categorie, veți fi gata să scrieți primul vostru subiect. Puteți găsi câteva [ghiduri](https://discuss.huggingface.co/t/how-to-request-support/3128) pe forum despre cum să faceți acest lucru, și în această secțiune vom arunca o privire asupra unor caracteristici care alcătuiesc un subiect bun.

## Scrierea unei postări bune pe forum[[scrierea-unei-postari-bune-pe-forum]]

Ca exemplu în curs de desfășurare, să presupunem că încercăm să generăm embeddings din articole Wikipedia pentru a crea un motor de căutare personalizat. Ca de obicei, încărcăm tokenizer-ul și modelul după cum urmează:

```python
from transformers import AutoTokenizer, AutoModel

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
model = AutoModel.from_pretrained(model_checkpoint)
```

Acum să presupunem că încercăm să încorporăm o întreagă secțiune din [articolul Wikipedia](https://en.wikipedia.org/wiki/Transformers) despre Transformers (franciza, nu biblioteca!):

```python
text = """
Generation One is a retroactive term for the Transformers characters that
appeared between 1984 and 1993. The Transformers began with the 1980s Japanese
toy lines Micro Change and Diaclone. They presented robots able to transform
into everyday vehicles, electronic items or weapons. Hasbro bought the Micro
Change and Diaclone toys, and partnered with Takara. Marvel Comics was hired by
Hasbro to create the backstory; editor-in-chief Jim Shooter wrote an overall
story, and gave the task of creating the characthers to writer Dennis O'Neil.
Unhappy with O'Neil's work (although O'Neil created the name "Optimus Prime"),
Shooter chose Bob Budiansky to create the characters.

The Transformers mecha were largely designed by Shōji Kawamori, the creator of
the Japanese mecha anime franchise Macross (which was adapted into the Robotech
franchise in North America). Kawamori came up with the idea of transforming
mechs while working on the Diaclone and Macross franchises in the early 1980s
(such as the VF-1 Valkyrie in Macross and Robotech), with his Diaclone mechs
later providing the basis for Transformers.

The primary concept of Generation One is that the heroic Optimus Prime, the
villainous Megatron, and their finest soldiers crash land on pre-historic Earth
in the Ark and the Nemesis before awakening in 1985, Cybertron hurtling through
the Neutral zone as an effect of the war. The Marvel comic was originally part
of the main Marvel Universe, with appearances from Spider-Man and Nick Fury,
plus some cameos, as well as a visit to the Savage Land.

The Transformers TV series began around the same time. Produced by Sunbow
Productions and Marvel Productions, later Hasbro Productions, from the start it
contradicted Budiansky's backstories. The TV series shows the Autobots looking
for new energy sources, and crash landing as the Decepticons attack. Marvel
interpreted the Autobots as destroying a rogue asteroid approaching Cybertron.
Shockwave is loyal to Megatron in the TV series, keeping Cybertron in a
stalemate during his absence, but in the comic book he attempts to take command
of the Decepticons. The TV series would also differ wildly from the origins
Budiansky had created for the Dinobots, the Decepticon turned Autobot Jetfire
(known as Skyfire on TV), the Constructicons (who combine to form
Devastator),[19][20] and Omega Supreme. The Marvel comic establishes early on
that Prime wields the Creation Matrix, which gives life to machines. In the
second season, the two-part episode The Key to Vector Sigma introduced the
ancient Vector Sigma computer, which served the same original purpose as the
Creation Matrix (giving life to Transformers), and its guardian Alpha Trion.
"""

inputs = tokenizer(text, return_tensors="pt")
logits = model(**inputs).logits
```

```python output
IndexError: index out of range in self
```

Uh-oh, am întâmpinat o problemă -- și mesajul de eroare este mult mai criptic decât cele pe care le-am văzut în [secțiunea 2](/course/chapter8/section2)! Nu putem înțelege nimic din traceback-ul complet, așa că decidem să ne îndreptăm către forumurile Hugging Face pentru ajutor. Cum am putea să formulăm subiectul?

Pentru a începe, trebuie să facem clic pe butonul "New Topic" din colțul din dreapta sus (rețineți că pentru a crea un subiect, va trebui să fim conectați):

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forums-new-topic.png" alt="Crearea unui nou subiect pe forum." width="100%"/>
</div>

Aceasta deschide o interfață de scriere unde putem introduce titlul subiectului nostru, să selectăm o categorie și să redactăm conținutul:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forum-topic01.png" alt="Interfața pentru crearea unui subiect pe forum." width="100%"/>
</div>

Deoarece eroarea pare să fie exclusiv despre 🤗 Transformers, vom selecta aceasta pentru categorie. Prima noastră încercare de a explica problema ar putea arăta cam așa:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forum-topic02.png" alt="Redactarea conținutului pentru un nou subiect pe forum." width="100%"/>
</div>

Deși acest subiect conține mesajul de eroare cu care avem nevoie de ajutor, există câteva probleme cu modul în care este scris:

1. Titlul nu este foarte descriptiv, așa că oricine navighează pe forum nu va putea să spună despre ce este subiectul fără să citească și conținutul.
2. Corpul nu oferă suficiente informații despre _de unde_ vine eroarea și _cum_ să o reproduci.
3. Subiectul etichetează câteva persoane direct cu un ton oarecum exigent.

Subiecte ca acesta probabil nu vor primi un răspuns rapid (dacă primesc unul deloc), așa că să vedem cum îl putem îmbunătăți. Vom începe cu prima problemă de a alege un titlu bun.

### Alegerea unui titlu descriptiv[[alegerea-unui-titlu-descriptiv]]

Dacă încercați să obțineți ajutor cu o eroare în codul vostru, o regulă bună este să includeți suficiente informații în titlu astfel încât alții să poată determina rapid dacă cred că pot răspunde la întrebarea voastră sau nu. În exemplul nostru în curs de desfășurare, știm numele excepției care este ridicată și avem câteva indicii că este declanșată în forward pass-ul modelului, unde apelăm `model(**inputs)`. Pentru a comunica aceasta, un titlu posibil ar putea fi:

> Sursa IndexError în forward pass-ul AutoModel?

Acest titlu îi spune cititorului _de unde_ credeți că vine eroarea, și dacă au întâlnit un `IndexError` înainte, există o șansă bună că vor ști cum să îl depaneze. Desigur, titlul poate fi orice doriți, și alte variații precum:

> De ce modelul meu produce un IndexError?

ar putea fi de asemenea bune. Acum că avem un titlu descriptiv, să ne uităm la îmbunătățirea corpului.

### Formatarea fragmentelor de cod[[formatarea-fragmentelor-de-cod]]

Citirea codului sursă este destul de grea într-un IDE, dar este și mai grea când codul este copiat și lipit ca text simplu! Din fericire, forumurile Hugging Face suportă utilizarea Markdown, așa că ar trebui să înconjurați întotdeauna blocurile de cod cu trei backtick-uri (```) astfel încât să fie mai ușor de citit. Să facem aceasta pentru a înfrumuseța mesajul de eroare -- și în timp ce suntem la asta, să facem corpul puțin mai politicos decât versiunea noastră originală:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forum-topic03.png" alt="Subiectul nostru revizuit pe forum, cu formatarea corectă a codului." width="100%"/>
</div>

Așa cum puteți vedea în captură, înconjurarea blocurilor de cod în backtick-uri transformă textul brut în cod formatat, complet cu stilizare color! De asemenea, rețineți că backtick-urile simple pot fi folosite pentru a formata variabile inline, așa cum am făcut pentru `distilbert-base-uncased`. Acest subiect arată mult mai bine, și cu puțin noroc am putea găsi pe cineva în comunitate care poate ghici despre ce este eroarea. Cu toate acestea, în loc să ne bazăm pe noroc, să facem viața mai ușoară prin includerea traceback-ului în toată splendoarea sa!

### Includerea traceback-ului complet[[includerea-traceback-ului-complet]]

Deoarece ultima linie a traceback-ului este adesea suficientă pentru a vă depana propriul cod, poate fi tentant să furnizați doar aceea în subiectul vostru pentru a "economisi spațiu". Deși este bine intenționat, aceasta de fapt face _mai greu_ pentru alții să depaneze problema deoarece informațiile care sunt mai sus în traceback pot fi de asemenea foarte utile. Deci, o practică bună este să copiați și să lipiți _întregul_ traceback, asigurându-vă că este frumos formatat. Deoarece aceste traceback-uri pot deveni destul de lungi, unii oameni preferă să le arate după ce au explicat codul sursă. Să facem aceasta. Acum, subiectul nostru pe forum arată astfel:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forum-topic04.png" alt="Exemplul nostru de subiect pe forum, cu traceback-ul complet." width="100%"/>
</div>

Aceasta este mult mai informativă, și un cititor atent ar putea fi capabil să indice că problema pare să se datoreze trecerii unei intrări lungi din cauza acestei linii în traceback:

> Token indices sequence length is longer than the specified maximum sequence length for this model (583 > 512).

Cu toate acestea, putem face lucrurile și mai ușoare pentru ei prin furnizarea codului real care a declanșat eroarea. Să facem asta acum.

### Furnizarea unui exemplu reproductibil[[furnizarea-unui-exemplu-reproductibil]]

Dacă ați încercat vreodată să depanați codul altcuiva, probabil că ați încercat mai întâi să recreați problema pe care au raportat-o astfel încât să puteți începe să vă faceți drumul prin traceback pentru a identifica eroarea. Nu este diferit când vine vorba de a obține (sau a da) asistență pe forumuri, așa că ajută cu adevărat dacă puteți furniza un exemplu mic care reproduce eroarea. Jumătate din timp, simpla parcurgere a acestui exercițiu vă va ajuta să vă dați seama ce merge prost. În orice caz, piesa lipsă din exemplul nostru este să arătăm _intrările_ pe care le-am furnizat modelului. Făcând aceasta ne dă ceva ca următorul exemplu completat:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/forum-topic05.png" alt="Versiunea finală a subiectului nostru pe forum." width="100%"/>
</div>

Acest subiect conține acum destul de multe informații și este scris într-un mod care este mult mai probabil să atragă atenția comunității și să primească un răspuns util. Cu aceste ghiduri de bază, puteți acum crea subiecte grozave pentru a găsi răspunsurile la întrebările voastre 🤗 Transformers! 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter8/3.mdx" />

### Partea 2 completă![[partea-2-completa]]
https://huggingface.co/learn/course/ro/chapter8/6.md

# Partea 2 completă![[partea-2-completa]]

<CourseFloatingBanner
    chapter={8}
    classNames="absolute z-10 right-0 top-0"
/>

Felicitări, ați ajuns la sfârșitul celei de-a doua părți a cursului! Lucrăm activ la cea de-a treia, așa că abonați-vă la [newsletter-ul](https://huggingface.curated.co/) nostru pentru a vă asigura că nu îi ratați lansarea.

Ar trebui să puteți acum să abordați o gamă largă de sarcini NLP și să ajustați fin sau să preantrenați un model pe ele. Nu uitați să vă partajați rezultatele cu comunitatea pe [Model Hub](https://huggingface.co/models).

Abia așteptăm să vedem ce veți construi cu cunoștințele pe care le-ați câștigat! 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter8/6.mdx" />

### Debugging-ul pipeline-ului de antrenament[[debugging-pipeline-ului-de-antrenament]]
https://huggingface.co/learn/course/ro/chapter8/4.md

# Debugging-ul pipeline-ului de antrenament[[debugging-pipeline-ului-de-antrenament]]

<CourseFloatingBanner chapter={8}
  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/chapter8/section4.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter8/section4.ipynb"},
]} />

Ați scris un script frumos pentru a antrena sau ajusta fin un model pe o sarcină dată, urmând cu atenție sfaturile din [Capitolul 7](/course/chapter7). Dar când lansați comanda `trainer.train()`, se întâmplă ceva oribil: primiți o eroare 😱! Sau mai rău, totul pare să fie în regulă și antrenamentul rulează fără eroare, dar modelul rezultat este prost. În această secțiune, vă vom arăta ce puteți face pentru a depana acest tip de probleme.

## Debugging-ul pipeline-ului de antrenament[[debugging-pipeline-ului-de-antrenament]]

<Youtube id="L-WSwUWde1U"/>

Problema când întâlniți o eroare în `trainer.train()` este că ar putea veni din mai multe surse, deoarece `Trainer` de obicei pune împreună multe lucruri. Convertește seturile de date în dataloader-e, așa că problema ar putea fi ceva greșit în setul vostru de date, sau o problemă când încearcă să grupeze elementele seturilor de date împreună. Apoi ia un batch de date și îl alimentează la model, așa că problema ar putea fi în codul modelului. După aceea, calculează gradienții și efectuează pasul de optimizare, așa că problema ar putea fi și în optimizatorul vostru. Și chiar dacă totul merge bine pentru antrenament, ceva ar putea merge prost în timpul evaluării dacă există o problemă cu metrica voastră.

Cea mai bună modalitate de a face debugging la o eroare care apare în `trainer.train()` este să parcurgeți manual întregul pipeline pentru a vedea unde au mers lucrurile prost. Eroarea este apoi adesea foarte ușor de rezolvat.

Pentru a demonstra aceasta, vom folosi următorul script care (încearcă să) ajusteze fin un model DistilBERT pe [setul de date MNLI](https://huggingface.co/datasets/glue):

```py
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    AutoModelForSequenceClassification,
    TrainingArguments,
    Trainer,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)
model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint)

args = TrainingArguments(
    f"distilbert-finetuned-mnli",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
)

metric = evaluate.load("glue", "mnli")


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    return metric.compute(predictions=predictions, references=labels)


trainer = Trainer(
    model,
    args,
    train_dataset=raw_datasets["train"],
    eval_dataset=raw_datasets["validation_matched"],
    compute_metrics=compute_metrics,
)
trainer.train()
```

Dacă încercați să îl executați, veți fi întâmpinați cu o eroare destul de criptică:

```python out
'ValueError: You have to specify either input_ids or inputs_embeds'
```

### Verificați datele voastre[[verificati-datele-voastre]]

Aceasta este de la sine înțeles, dar dacă datele voastre sunt corupte, `Trainer` nu va putea forma batch-uri, cu atât mai puțin să vă antreneze modelul. Deci primul lucru, trebuie să aruncați o privire asupra a ceea ce este în setul vostru de antrenament.

Pentru a evita nenumărate ore petrecute încercând să reparați ceva care nu este sursa bug-ului, vă recomandăm să folosiți `trainer.train_dataset` pentru verificările voastre și nimic altceva. Să facem asta aici:

```py
trainer.train_dataset[0]
```

```python out
{'hypothesis': 'Product and geography are what make cream skimming work. ',
 'idx': 0,
 'label': 1,
 'premise': 'Conceptually cream skimming has two basic dimensions - product and geography.'}
```

Observați ceva greșit? Aceasta, în conjuncție cu mesajul de eroare despre `input_ids` lipsă, ar trebui să vă facă să realizați că acelea sunt texte, nu numere pe care modelul le poate înțelege. Aici, eroarea originală este foarte înșelătoare deoarece `Trainer` elimină automat coloanele care nu se potrivesc cu semnătura modelului (adică argumentele așteptate de model). Aceasta înseamnă că aici, totul în afară de etichete a fost eliminat. Nu a existat astfel nicio problemă cu crearea batch-urilor și apoi trimiterea lor la model, care la rândul său s-a plâns că nu a primit intrarea potrivită.

De ce nu au fost procesate datele? Am folosit metoda `Dataset.map()` pe seturi de date pentru a aplica tokenizer-ul pe fiecare eșantion. Dar dacă vă uitați cu atenție la cod, veți vedea că am făcut o greșeală când am trecut seturile de antrenament și evaluare la `Trainer`. În loc să folosim `tokenized_datasets` aici, am folosit `raw_datasets` 🤦. Să reparăm asta!

```py
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    AutoModelForSequenceClassification,
    TrainingArguments,
    Trainer,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)
model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint)

args = TrainingArguments(
    f"distilbert-finetuned-mnli",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
)

metric = evaluate.load("glue", "mnli")


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    return metric.compute(predictions=predictions, references=labels)


trainer = Trainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation_matched"],
    compute_metrics=compute_metrics,
)
trainer.train()
```

Acest cod nou va da acum o eroare diferită (progres!):

```python out
'ValueError: expected sequence of length 43 at dim 1 (got 37)'
```

Uitându-ne la traceback, putem vedea că eroarea se întâmplă în pasul de colectare a datelor:

```python out
~/git/transformers/src/transformers/data/data_collator.py in torch_default_data_collator(features)
    105                 batch[k] = torch.stack([f[k] for f in features])
    106             else:
--> 107                 batch[k] = torch.tensor([f[k] for f in features])
    108 
    109     return batch
```

Deci, ar trebui să ne îndreptăm către asta. Înainte să facem asta, totuși, să terminăm de inspectat datele noastre, doar pentru a fi 100% siguri că sunt corecte.

Un lucru pe care ar trebui să îl faceți întotdeauna când depanați o sesiune de antrenament este să aruncați o privire asupra intrărilor decodate ale modelului vostru. Nu putem înțelege numerele pe care le alimentăm direct, așa că ar trebui să ne uităm la ceea ce reprezintă acele numere. În computer vision, de exemplu, aceasta înseamnă să ne uităm la imaginile decodate ale pixelilor pe care îi treceți, în vorbire înseamnă să ascultați eșantioanele audio decodate, și pentru exemplul nostru NLP aici înseamnă să folosim tokenizer-ul nostru pentru a decoda intrările:

```py
tokenizer.decode(trainer.train_dataset[0]["input_ids"])
```

```python out
'[CLS] conceptually cream skimming has two basic dimensions - product and geography. [SEP] product and geography are what make cream skimming work. [SEP]'
```

Deci pare corect. Ar trebui să faceți aceasta pentru toate cheile din intrări:

```py
trainer.train_dataset[0].keys()
```

```python out
dict_keys(['attention_mask', 'hypothesis', 'idx', 'input_ids', 'label', 'premise'])
```

Rețineți că cheile care nu corespund intrărilor acceptate de model vor fi eliminate automat, așa că aici vom păstra doar `input_ids`, `attention_mask`, și `label` (care va fi redenumit `labels`). Pentru a verifica din nou semnătura modelului, puteți afișa clasa modelului vostru, apoi să verificați documentația sa:

```py
type(trainer.model)
```

```python out
transformers.models.distilbert.modeling_distilbert.DistilBertForSequenceClassification
```

Deci în cazul nostru, putem verifica parametrii acceptați pe [această pagină](https://huggingface.co/transformers/model_doc/distilbert.html#distilbertforsequenceclassification). `Trainer` va înregistra de asemenea coloanele pe care le elimină.

Am verificat că ID-urile de intrare sunt corecte prin decodarea lor. Următorul este `attention_mask`:

```py
trainer.train_dataset[0]["attention_mask"]
```

```python out
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
```

Deoarece nu am aplicat padding în preprocesarea noastră, aceasta pare perfect naturală. Pentru a fi siguri că nu există nicio problemă cu acea mască de atenție, să verificăm că are aceeași lungime ca ID-urile noastre de intrare:

```py
len(trainer.train_dataset[0]["attention_mask"]) == len(
    trainer.train_dataset[0]["input_ids"]
)
```

```python out
True
```

Asta e bun! În sfârșit, să verificăm eticheta noastră:

```py
trainer.train_dataset[0]["label"]
```

```python out
1
```

Ca ID-urile de intrare, acesta este un număr care nu are cu adevărat sens de unul singur. Așa cum am văzut înainte, maparea între întregi și numele etichetelor este stocată în atributul `names` al *feature*-ului corespunzător al setului de date:

```py
trainer.train_dataset.features["label"].names
```

```python out
['entailment', 'neutral', 'contradiction']
```

Deci `1` înseamnă `neutral`, ceea ce înseamnă că cele două propoziții pe care le-am văzut mai sus nu sunt în contradicție, și prima nu implică a doua. Pare corect!

Nu avem ID-uri de tip token aici, deoarece DistilBERT nu le așteaptă; dacă aveți unele în modelul vostru, ar trebui să vă asigurați de asemenea că se potrivesc corespunzător unde sunt prima și a doua propoziție în intrare.

<Tip>

✏️ **Rândul vostru!** Verificați că totul pare corect cu al doilea element al setului de date de antrenament.

</Tip>

Facem verificarea doar pe setul de antrenament aici, dar ar trebui desigur să verificați din nou seturile de validare și test în același mod.

Acum că știm că seturile noastre de date arată bine, este timpul să verificăm următorul pas al pipeline-ului de antrenament.

### De la seturi de date la dataloader-e[[de-la-seturi-de-date-la-dataloader-e]]

Următorul lucru care poate merge prost în pipeline-ul de antrenament este când `Trainer` încearcă să formeze batch-uri din setul de antrenament sau validare. Odată ce sunteți siguri că seturile de date ale `Trainer` sunt corecte, puteți încerca să formați manual un batch executând următoarele (înlocuiți `train` cu `eval` pentru dataloader-ul de validare):

```py
for batch in trainer.get_train_dataloader():
    break
```

Acest cod creează dataloader-ul de antrenament, apoi iterează prin el, oprindu-se la prima iterație. Dacă codul se execută fără eroare, aveți primul batch de antrenament pe care îl puteți inspecta, și dacă codul dă eroare, știți sigur că problema este în dataloader, așa cum este cazul aici:

```python out
~/git/transformers/src/transformers/data/data_collator.py in torch_default_data_collator(features)
    105                 batch[k] = torch.stack([f[k] for f in features])
    106             else:
--> 107                 batch[k] = torch.tensor([f[k] for f in features])
    108 
    109     return batch

ValueError: expected sequence of length 45 at dim 1 (got 76)
```

Inspectarea ultimului frame al traceback-ului ar trebui să fie suficientă pentru a vă da un indiciu, dar să facem puțină săpătură. Majoritatea problemelor în timpul creării batch-ului apar din cauza colectării exemplelor într-un singur batch, așa că primul lucru de verificat când aveți îndoieli este ce `collate_fn` folosește `DataLoader`-ul vostru:

```py
data_collator = trainer.get_train_dataloader().collate_fn
data_collator
```

```python out
<function transformers.data.data_collator.default_data_collator(features: List[InputDataClass], return_tensors='pt') -> Dict[str, Any]>
```

Deci acesta este `default_data_collator`, dar nu este ceea ce vrem în acest caz. Vrem să facem padding la exemplele noastre la cea mai lungă propoziție din batch, ceea ce se face de colectorul `DataCollatorWithPadding`. Și acest colector de date ar trebui să fie folosit în mod implicit de `Trainer`, deci de ce nu este folosit aici?

Răspunsul este pentru că nu am trecut `tokenizer` la `Trainer`, așa că nu a putut crea `DataCollatorWithPadding` pe care îl vrem. În practică, nu ar trebui să ezitați niciodată să treceți explicit colectorul de date pe care doriți să îl folosiți, pentru a vă asigura că evitați acest tip de erori. Să adaptăm codul nostru pentru a face exact asta:

```py
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    AutoModelForSequenceClassification,
    DataCollatorWithPadding,
    TrainingArguments,
    Trainer,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)
model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint)

args = TrainingArguments(
    f"distilbert-finetuned-mnli",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
)

metric = evaluate.load("glue", "mnli")


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    return metric.compute(predictions=predictions, references=labels)


data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

trainer = Trainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation_matched"],
    compute_metrics=compute_metrics,
    data_collator=data_collator,
    tokenizer=tokenizer,
)
trainer.train()
```

Vestea bună? Nu primim aceeași eroare ca înainte, ceea ce este cu siguranță progres. Vestea proastă? Primim în schimb o eroare CUDA infamă:

```python out
RuntimeError: CUDA error: CUBLAS_STATUS_ALLOC_FAILED when calling `cublasCreate(handle)`
```

Aceasta este rea deoarece erorile CUDA sunt extrem de greu de depanat în general. Vom vedea într-un minut cum să rezolvăm aceasta, dar mai întâi să terminăm analiza noastră a creării batch-ului.

Dacă sunteți siguri că colectorul vostru de date este cel potrivit, ar trebui să încercați să îl aplicați pe câteva eșantioane din setul vostru de date:

```py
data_collator = trainer.get_train_dataloader().collate_fn
batch = data_collator([trainer.train_dataset[i] for i in range(4)])
```

Acest cod va eșua deoarece `train_dataset` conține coloane string, pe care `Trainer` le elimină de obicei. Le puteți elimina manual, sau dacă doriți să replicați exact ceea ce face `Trainer` în culise, puteți apela metoda privată `Trainer._remove_unused_columns()` care face asta:

```py
data_collator = trainer.get_train_dataloader().collate_fn
actual_train_set = trainer._remove_unused_columns(trainer.train_dataset)
batch = data_collator([actual_train_set[i] for i in range(4)])
```

Ar trebui apoi să puteți depana manual ce se întâmplă în interiorul colectorului de date dacă eroarea persistă.

Acum că am depanat procesul de creare a batch-ului, este timpul să trecem unul prin model! 

### Trecerea prin model[[trecerea-prin-model]]

Ar trebui să puteți obține un batch executând următoarea comandă:

```py
for batch in trainer.get_train_dataloader():
    break
```

Dacă rulați acest cod într-un notebook, s-ar putea să primiți o eroare CUDA similară cu cea pe care am văzut-o mai devreme, caz în care trebuie să reporniți notebook-ul și să reexecutați ultimul fragment fără linia `trainer.train()`. Acesta este al doilea lucru cel mai enervant despre erorile CUDA: ele strică iremediabil kernel-ul vostru. Cel mai enervant lucru despre ele este faptul că sunt greu de depanat.

De ce este așa? Are de-a face cu modul în care funcționează GPU-urile. Ele sunt extrem de eficiente la executarea multor operații în paralel, dar dezavantajul este că atunci când una dintre acele instrucțiuni rezultă într-o eroare, nu știți instantaneu. Doar când programul apelează o sincronizare a multiplelor procese de pe GPU va realiza că ceva a mers prost, așa că eroarea este de fapt ridicată într-un loc care nu are nimic de-a face cu ceea ce a creat-o. De exemplu, dacă ne uităm la traceback-ul nostru anterior, eroarea a fost ridicată în timpul backward pass-ului, dar vom vedea într-un minut că de fapt provine din ceva din forward pass.

Deci cum depanăm aceste erori? Răspunsul este simplu: nu o facem. Dacă eroarea voastră CUDA nu este o eroare out-of-memory (ceea ce înseamnă că nu există suficientă memorie în GPU-ul vostru), ar trebui să vă întoarceți întotdeauna la CPU pentru a o depana.

Pentru a face aceasta în cazul nostru, trebuie doar să punem modelul înapoi pe CPU și să îl apelăm pe batch-ul nostru -- batch-ul returnat de `DataLoader` nu a fost încă mutat pe GPU:

```python
outputs = trainer.model.cpu()(**batch)
```

```python out
~/.pyenv/versions/3.7.9/envs/base/lib/python3.7/site-packages/torch/nn/functional.py in nll_loss(input, target, weight, size_average, ignore_index, reduce, reduction)
   2386         )
   2387     if dim == 2:
-> 2388         ret = torch._C._nn.nll_loss(input, target, weight, _Reduction.get_enum(reduction), ignore_index)
   2389     elif dim == 4:
   2390         ret = torch._C._nn.nll_loss2d(input, target, weight, _Reduction.get_enum(reduction), ignore_index)

IndexError: Target 2 is out of bounds.
```

Deci, imaginea devine mai clară. În loc să avem o eroare CUDA, acum avem un `IndexError` în calculul loss-ului (deci nimic de-a face cu backward pass-ul, așa cum am spus mai devreme). Mai precis, putem vedea că este target 2 care creează eroarea, așa că acesta este un moment foarte bun pentru a verifica numărul de etichete al modelului nostru:

```python
trainer.model.config.num_labels
```

```python out
2
```

Cu două etichete, doar 0 și 1 sunt permise ca target-uri, dar conform mesajului de eroare am primit un 2. Primirea unui 2 este de fapt normală: dacă ne amintim numele etichetelor pe care le-am extras mai devreme, erau trei, deci avem indicii 0, 1 și 2 în setul nostru de date. Problema este că nu am spus asta modelului nostru, care ar fi trebuit să fie creat cu trei etichete. Să reparăm asta!

```py
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    AutoModelForSequenceClassification,
    DataCollatorWithPadding,
    TrainingArguments,
    Trainer,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)
model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=3)

args = TrainingArguments(
    f"distilbert-finetuned-mnli",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
)

metric = evaluate.load("glue", "mnli")


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    return metric.compute(predictions=predictions, references=labels)


data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

trainer = Trainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation_matched"],
    compute_metrics=compute_metrics,
    data_collator=data_collator,
    tokenizer=tokenizer,
)
trainer.train()
```

Nu includem încă linia `trainer.train()`, pentru a ne lua timpul să verificăm că totul arată bine. Dacă cerem un batch și îl trecem la modelul nostru, acum funcționează fără eroare!

```py
for batch in trainer.get_train_dataloader():
    break

outputs = trainer.model.cpu()(**batch)
```

Următorul pas este apoi să ne întoarcem la GPU și să verificăm că totul încă funcționează:

```py
import torch

device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
batch = {k: v.to(device) for k, v in batch.items()}

outputs = trainer.model.to(device)(**batch)
```

Dacă încă primiți o eroare, asigurați-vă că reporniți notebook-ul și executați doar ultima versiune a scriptului.

### Efectuarea unui pas de optimizare[[efectuarea-unui-pas-de-optimizare]]

Acum că știm că putem construi batch-uri care trec efectiv prin model, suntem gata pentru următorul pas al pipeline-ului de antrenament: calcularea gradienților și efectuarea unui pas de optimizare.

Prima parte este doar o chestiune de a apela metoda `backward()` pe loss:

```py
loss = outputs.loss
loss.backward()
```

Este destul de rar să primiți o eroare în această etapă, dar dacă o faceți, asigurați-vă să vă întoarceți la CPU pentru a obține un mesaj de eroare util.

Pentru a efectua pasul de optimizare, trebuie doar să creăm `optimizer` și să apelăm metoda sa `step()`:

```py
trainer.create_optimizer()
trainer.optimizer.step()
```

Din nou, dacă folosiți optimizatorul implicit în `Trainer`, nu ar trebui să primiți o eroare în această etapă, dar dacă aveți un optimizator personalizat, ar putea fi câteva probleme de depanat aici. Nu uitați să vă întoarceți la CPU dacă primiți o eroare CUDA ciudată în această etapă. Vorbind despre erorile CUDA, mai devreme am menționat un caz special. Să aruncăm o privire asupra acestuia acum.

### Gestionarea erorilor CUDA out-of-memory[[gestionarea-erorilor-cuda-out-of-memory]]

Ori de câte ori primiți un mesaj de eroare care începe cu `RuntimeError: CUDA out of memory`, aceasta indică faptul că nu aveți memorie GPU. Aceasta nu este legată direct de codul vostru și se poate întâmpla cu un script care rulează perfect. Această eroare înseamnă că ați încercat să puneți prea multe lucruri în memoria internă a GPU-ului vostru, și aceasta a rezultat într-o eroare. Ca și cu alte erori CUDA, va trebui să reporniți kernel-ul pentru a fi într-un punct unde puteți rula din nou antrenamentul.

Pentru a rezolva această problemă, trebuie doar să folosiți mai puțin spațiu GPU -- ceva care este adesea mai ușor de spus decât de făcut. În primul rând, asigurați-vă că nu aveți două modele pe GPU în același timp (dacă nu este necesar pentru problema voastră, desigur). Apoi, probabil ar trebui să reduceți dimensiunea batch-ului, deoarece aceasta afectează direct dimensiunile tuturor ieșirilor intermediare ale modelului și gradienții lor. Dacă problema persistă, considerați folosirea unei versiuni mai mici a modelului vostru.

<Tip>

În următoarea parte a cursului, vom examina tehnici mai avansate care vă pot ajuta să reduceți amprenta de memorie și să vă permită să ajustați fin cele mai mari modele.

</Tip>

### Evaluarea modelului[[evaluarea-modelului]]

Acum că am rezolvat toate problemele cu codul nostru, totul este perfect și antrenamentul ar trebui să ruleze fără probleme, nu? Nu atât de repede! Dacă rulați comanda `trainer.train()`, totul va arăta bine la început, dar după un timp veți primi următoarele:

```py
# Aceasta va dura mult timp și va da eroare, așa că nu ar trebui să rulați această celulă
trainer.train()
```

```python out
TypeError: only size-1 arrays can be converted to Python scalars
```

Veți realiza că această eroare apare în timpul fazei de evaluare, deci acesta este ultimul lucru pe care va trebui să îl depanăm.

Puteți rula bucla de evaluare a `Trainer` independent de antrenament astfel:

```py
trainer.evaluate()
```

```python out
TypeError: only size-1 arrays can be converted to Python scalars
```

<Tip>

💡 Ar trebui să vă asigurați întotdeauna că puteți rula `trainer.evaluate()` înainte de a lansa `trainer.train()`, pentru a evita risipa multor resurse de calcul înainte de a întâlni o eroare.

</Tip>

Înainte de a încerca să depanați o problemă în bucla de evaluare, ar trebui să vă asigurați mai întâi că v-ați uitat la date, puteți forma un batch corespunzător și puteți rula modelul pe el. Am completat toți acești pași, așa că următorul cod poate fi executat fără eroare:

```py
for batch in trainer.get_eval_dataloader():
    break

batch = {k: v.to(device) for k, v in batch.items()}

with torch.no_grad():
    outputs = trainer.model(**batch)
```

Eroarea vine mai târziu, la sfârșitul fazei de evaluare, și dacă ne uităm la traceback vedem aceasta:

```python trace
~/git/datasets/src/datasets/metric.py in add_batch(self, predictions, references)
    431         """
    432         batch = {"predictions": predictions, "references": references}
--> 433         batch = self.info.features.encode_batch(batch)
    434         if self.writer is None:
    435             self._init_writer()
```

Aceasta ne spune că eroarea provine din modulul `datasets/metric.py` -- deci aceasta este o problemă cu funcția noastră `compute_metrics()`. Aceasta ia un tuplu cu logit-urile și etichetele ca array-uri NumPy, așa că să încercăm să îi alimentăm asta:

```py
predictions = outputs.logits.cpu().numpy()
labels = batch["labels"].cpu().numpy()

compute_metrics((predictions, labels))
```

```python out
TypeError: only size-1 arrays can be converted to Python scalars
```

Primim aceeași eroare, deci problema se află cu siguranță în acea funcție. Dacă ne uităm înapoi la codul ei, vedem că doar transmite `predictions` și `labels` la `metric.compute()`. Deci există o problemă cu acea metodă? Nu chiar. Să aruncăm o privire rapidă asupra formelor:

```py
predictions.shape, labels.shape
```

```python out
((8, 3), (8,))
```

Predicțiile noastre sunt încă logit-uri, nu predicțiile reale, de aceea metrica returnează această eroare (oarecum obscură). Reparația este destul de ușoară; trebuie doar să adăugăm un argmax în funcția `compute_metrics()`:

```py
import numpy as np


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    predictions = np.argmax(predictions, axis=1)
    return metric.compute(predictions=predictions, references=labels)


compute_metrics((predictions, labels))
```

```python out
{'accuracy': 0.625}
```

Acum eroarea noastră este reparată! Aceasta a fost ultima, așa că scriptul nostru va antrena acum un model corespunzător.

Pentru referință, iată scriptul complet reparat:

```py
import numpy as np
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    AutoModelForSequenceClassification,
    DataCollatorWithPadding,
    TrainingArguments,
    Trainer,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)
model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=3)

args = TrainingArguments(
    f"distilbert-finetuned-mnli",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    learning_rate=2e-5,
    num_train_epochs=3,
    weight_decay=0.01,
)

metric = evaluate.load("glue", "mnli")


def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    predictions = np.argmax(predictions, axis=1)
    return metric.compute(predictions=predictions, references=labels)


data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

trainer = Trainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation_matched"],
    compute_metrics=compute_metrics,
    data_collator=data_collator,
    tokenizer=tokenizer,
)
trainer.train()
```

În acest caz, nu mai sunt probleme, și scriptul nostru va ajusta fin un model care ar trebui să dea rezultate rezonabile. Dar ce putem face când antrenamentul continuă fără nicio eroare, și modelul antrenat nu performează deloc bine? Aceasta este partea cea mai dificilă a machine learning-ului, și vă vom arăta câteva tehnici care pot ajuta.

<Tip>

💡 Dacă folosiți o buclă de antrenament manuală, aceiași pași se aplică pentru a vă depana pipeline-ul de antrenament, dar este mai ușor să îi separați. Asigurați-vă că nu ați uitat `model.eval()` sau `model.train()` la locurile potrivite, sau `zero_grad()` la fiecare pas, totuși!

</Tip>

## Depanarea erorilor silențioase în timpul antrenamentului[[depanarea-erorilor-silentioase-in-timpul-antrenamentului]]

Ce putem face pentru a depana un antrenament care se completează fără eroare dar nu obține rezultate bune? Vă vom da câteva indicii aici, dar fiți conștienți că acest tip de depanare este partea cea mai grea a machine learning-ului, și nu există un răspuns magic.

### Verificați datele voastre (din nou!)[[verificati-datele-voastre-din-nou]]

Modelul vostru va învăța ceva doar dacă este de fapt posibil să învețe ceva din datele voastre. Dacă există o eroare care corupe datele sau etichetele sunt atribuite aleatoriu, este foarte probabil că nu veți obține niciun antrenament de model pe setul vostru de date. Deci începeți întotdeauna prin a verifica din nou intrările și etichetele voastre decodate, și întrebați-vă următoarele întrebări:

- Sunt datele decodate înțelegibile?
- Sunteți de acord cu etichetele?
- Există o etichetă care este mai comună decât altele?
- Care ar trebui să fie loss-ul/metrica dacă modelul ar prezice un răspuns aleatoriu/întotdeauna același răspuns?

<Tip warning={true}>

⚠️ Dacă faceți antrenament distribuit, afișați eșantioane din setul vostru de date în fiecare proces și verificați de trei ori că obțineți același lucru. O eroare comună este să aveți o sursă de aleatoriu în crearea datelor care face ca fiecare proces să aibă o versiune diferită a setului de date.

</Tip>

După ce vă uitați la datele voastre, treceți prin câteva dintre predicțiile modelului și decodați-le și pe ele. Dacă modelul prezice întotdeauna același lucru, ar putea fi pentru că setul vostru de date este părtinitor către o categorie (pentru problemele de clasificare); tehnici precum supraesantionarea claselor rare ar putea ajuta.

Dacă loss-ul/metrica pe care o obțineți pe modelul vostru inițial este foarte diferită de loss-ul/metrica pe care ați aștepta-o pentru predicții aleatorii, verificați din nou modul în care loss-ul sau metrica voastră este calculată, deoarece probabil există o eroare acolo. Dacă folosiți mai multe loss-uri pe care le adăugați la sfârșit, asigurați-vă că sunt de aceeași scară.

Când sunteți siguri că datele voastre sunt perfecte, puteți vedea dacă modelul este capabil să se antreneze pe ele cu un test simplu.

### Supraajustați modelul vostru pe un batch[[supraajustati-modelul-vostru-pe-un-batch]]

Supraajustarea este de obicei ceva pe care încercăm să îl evităm când antrenăm, deoarece înseamnă că modelul nu învață să recunoască caracteristicile generale pe care vrem să le recunoască, ci în schimb doar memorează eșantioanele de antrenament. Cu toate acestea, încercarea de a vă antrena modelul pe un batch din nou și din nou este un test bun pentru a verifica dacă problema așa cum ați formulat-o poate fi rezolvată de modelul pe care încercați să îl antrenați. De asemenea, vă va ajuta să vedeți dacă rata voastră de învățare inițială este prea mare.

Făcând aceasta odată ce ați definit `Trainer` este foarte ușor; doar luați un batch de date de antrenament, apoi rulați o buclă mică de antrenament manual folosind doar acel batch pentru ceva ca 20 de pași:

```py
for batch in trainer.get_train_dataloader():
    break

batch = {k: v.to(device) for k, v in batch.items()}
trainer.create_optimizer()

for _ in range(20):
    outputs = trainer.model(**batch)
    loss = outputs.loss
    loss.backward()
    trainer.optimizer.step()
    trainer.optimizer.zero_grad()
```

<Tip>

💡 Dacă datele voastre de antrenament sunt dezechilibrate, asigurați-vă să construiți un batch de date de antrenament care conține toate etichetele.

</Tip>

Modelul rezultat ar trebui să aibă rezultate aproape perfecte pe același `batch`. Să calculăm metrica pe predicțiile rezultate:

```py
with torch.no_grad():
    outputs = trainer.model(**batch)
preds = outputs.logits
labels = batch["labels"]

compute_metrics((preds.cpu().numpy(), labels.cpu().numpy()))
```

```python out
{'accuracy': 1.0}
```

100% acuratețe, acum acesta este un exemplu frumos de supraajustare (ceea ce înseamnă că dacă încercați modelul vostru pe orice altă propoziție, foarte probabil vă va da un răspuns greșit)!

Dacă nu reușiți să faceți modelul vostru să obțină rezultate perfecte ca aceasta, înseamnă că există ceva greșit cu modul în care ați formulat problema sau datele voastre, așa că ar trebui să reparați asta. Doar când reușiți să treceți testul de supraajustare puteți fi siguri că modelul vostru poate învăța de fapt ceva.

<Tip warning={true}>

⚠️ Va trebui să vă recreați modelul și `Trainer` după acest test, deoarece modelul obținut probabil nu va putea să se recupereze și să învețe ceva util pe setul vostru complet de date.

</Tip>

### Nu ajustați nimic până nu aveți o primă linie de bază[[nu-ajustati-nimic-pana-nu-aveti-o-prima-linie-de-baza]]

Ajustarea hiperparametrilor este întotdeauna subliniată ca fiind partea cea mai grea a machine learning-ului, dar este doar ultimul pas pentru a vă ajuta să câștigați puțin la metrică. Majoritatea timpului, hiperparametrii impliciți ai `Trainer` vor funcționa bine pentru a vă da rezultate bune, așa că nu vă lansați într-o căutare de hiperparametri consumatoare de timp și costisitoare până nu aveți ceva care bate linia de bază pe care o aveți pe setul vostru de date.

Odată ce aveți un model suficient de bun, puteți începe să ajustați puțin. Nu încercați să lansați o mie de rulări cu hiperparametri diferiți, ci comparați câteva rulări cu valori diferite pentru un hiperparametru pentru a avea o idee despre care are cel mai mare impact.

Dacă ajustați modelul în sine, păstrați-l simplu și nu încercați nimic pe care nu îl puteți justifica în mod rezonabil. Asigurați-vă întotdeauna că vă întoarceți la testul de supraajustare pentru a verifica că schimbarea voastră nu a avut consecințe neintenționate.

### Cereți ajutor[[cereti-ajutor]]

Sperăm că veți fi găsit câteva sfaturi în această secțiune care v-au ajutat să vă rezolvați problema, dar dacă nu este cazul, amintiți-vă că puteți întotdeauna să cereți comunității pe [forumuri](https://discuss.huggingface.co/).

Iată câteva resurse suplimentare care se pot dovedi utile:

- ["Reproducibility as a vehicle for engineering best practices"](https://docs.google.com/presentation/d/1yHLPvPhUs2KGI5ZWo0sU-PKU3GimAk3iTsI38Z-B5Gw/edit#slide=id.p) de Joel Grus
- ["Checklist for debugging neural networks"](https://towardsdatascience.com/checklist-for-debugging-neural-networks-d8b2a9434f21) de Cecelia Shao
- ["How to unit test machine learning code"](https://medium.com/@keeper6928/how-to-unit-test-machine-learning-code-57cf6fd81765) de Chase Roberts
- ["A Recipe for Training Neural Networks"](http://karpathy.github.io/2019/04/25/recipe/) de Andrej Karpathy

Desigur, nu fiecare problemă pe care o întâlniți când antrenați rețele neuronale este vina voastră! Dacă întâlniți ceva în biblioteca 🤗 Transformers sau 🤗 Datasets care nu pare corect, s-ar putea să fi întâlnit o eroare. Ar trebui cu siguranță să ne spuneți totul despre aceasta, și în secțiunea următoare vă vom explica exact cum să faceți asta. 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter8/4.mdx" />

### Quiz de sfârșit de capitol[[quiz-de-sfarsit-de-capitol]]
https://huggingface.co/learn/course/ro/chapter8/7.md

# Quiz de sfârșit de capitol[[quiz-de-sfarsit-de-capitol]]

<CourseFloatingBanner
    chapter={8}
    classNames="absolute z-10 right-0 top-0"
/>

Să testăm ce ați învățat în acest capitol!

### 1. În ce ordine ar trebui să citiți un traceback Python?

<Question
	choices={[
		{
			text: "De sus în jos",
			explain: "Încercați din nou -- deși majoritatea celorlalte limbaje de programare afișează excepția în partea de sus, Python este special în această privință."
		},
		{
			text: "De jos în sus",
			explain: "Corect! Un avantaj al traceback-urilor Python care arată excepția în partea de jos este că este mai ușor de depanat când lucrați în terminal și aceasta este ultima linie pe care o vedeți.",
			correct: true
		}
	]}
/>

### 2. Ce este un exemplu minimal reproductibil?

<Question
	choices={[
		{
			text: "O implementare simplă a unei arhitecturi Transformer dintr-un articol de cercetare",
			explain: "Deși este foarte educativ să implementați propriile modele Transformer de la zero, nu despre asta vorbim aici."
		},
		{
			text: "Un bloc de cod compact și autonom care poate fi rulat fără nicio dependență externă de fișiere sau date private",
			explain: "Corect! Exemplele minimale reproductibile îi ajută pe întreținătorii bibliotecii să reproducă problema pe care o aveți, astfel încât să poată găsi soluții mai rapid.",
			correct: true
		},
		{
			text: "O captură de ecran a traceback-ului Python",
			explain: "Încercați din nou -- deși este tentant să includeți o captură de ecran a erorii cu care vă confruntați când depuneți o problemă, aceasta face foarte dificil pentru alții să reproducă eroarea."
		},
		{
			text: "Un notebook care conține întreaga analiză, inclusiv părți care nu sunt legate de eroare",
			explain: "Nu chiar -- deși poate fi util să partajați un notebook Google Colab care arată eroarea, asigurați-vă că este scurt și conține doar codul relevant."
		}
	]}
/>

### 3. Să presupunem că încercați să rulați următorul cod, care aruncă o eroare:

```py
from transformers import GPT3ForSequenceClassification

# ImportError: cannot import name 'GPT3ForSequenceClassification' from 'transformers' (/Users/lewtun/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/__init__.py)
# ---------------------------------------------------------------------------
# ImportError                               Traceback (most recent call last)
# /var/folders/28/k4cy5q7s2hs92xq7_h89_vgm0000gn/T/ipykernel_30848/333858878.py in <module>
# ----> 1 from transformers import GPT3ForSequenceClassification

# ImportError: cannot import name 'GPT3ForSequenceClassification' from 'transformers' (/Users/lewtun/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/__init__.py)
```

Care dintre următoarele ar putea fi o alegere bună pentru titlul unui subiect pe forum pentru a cere ajutor?

<Question
	choices={[
		{
			text: "<code>ImportError: cannot import name 'GPT3ForSequenceClassification' from 'transformers' (/Users/lewtun/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/__init__.py)</code>",
			explain: "Includerea ultimei linii a traceback-ului poate fi descriptivă, dar aceasta este mai bine rezervată pentru corpul principal al subiectului. Încercați din nou!"
		},
		{
			text: "Problemă cu <code>from transformers import GPT3ForSequenceClassification</code>",
			explain: "Încercați din nou -- deși aceasta oferă informații utile, probabil este cel mai bine rezervată pentru corpul principal al textului.",
		},
		{
			text: "De ce nu pot importa <code>GPT3ForSequenceClassification</code>?",
			explain: "Alegere bună! Acest titlu este concis și îi dă cititorului un indiciu despre ce ar putea fi greșit (adică că GPT-3 nu este suportat în 🤗 Transformers).",
			correct: true
		},
		{
			text: "Este GPT-3 suportat în 🤗 Transformers?",
			explain: "Bună alegere! Folosirea întrebărilor ca titluri de subiecte este o modalitate excelentă de a comunica problema comunității.",
			correct: true
		}
	]}
/>

### 4. Să presupunem că ați încercat să rulați `trainer.train()` și vă confruntați cu o eroare criptică care nu vă spune exact de unde vine eroarea. Care dintre următoarele este primul loc unde ar trebui să căutați erori în pipeline-ul vostru de antrenament?

<Question
	choices={[
		{
			text: "Pasul de optimizare unde calculăm gradienții și efectuăm backpropagation",
			explain: "Deși pot exista bug-uri în optimizatorul vostru, acesta este de obicei la mai mulți pași în pipeline-ul de antrenament, așa că există alte lucruri de verificat mai întâi. Încercați din nou!"
		},
		{
			text: "Pasul de evaluare unde calculăm metricile",
			explain: "Evaluarea este de obicei ceea ce faceți după antrenament pentru o epocă completă, așa că ar trebui să verificați mai întâi undeva mai devreme în pipeline-ul de antrenament."
		},
		{
			text: "Seturile de date",
			explain: "Corect! Privirea la datele voastre este aproape întotdeauna primul lucru pe care ar trebui să îl faceți, pentru a vă asigura că textul este codificat corespunzător, are caracteristicile așteptate, și așa mai departe.",
			correct: true
		},
		{
			text: "Dataloader-ele",
			explain: "Încercați din nou -- aceasta este foarte aproape de primul lucru pe care ar trebui să îl verificați. Vă amintiți ce obiect transmitem la dataloader-e?"
		}
	]}
/>

### 5. Care este cea mai bună modalitate de a depana o eroare CUDA?

<Question
	choices={[
		{
			text: "Postați mesajul de eroare pe forumuri sau GitHub.",
			explain: "Asta nu va ajuta pe nimeni deoarece mesajele de eroare CUDA sunt de obicei foarte neinformative."
		},
		{
			text: "Executați același cod pe CPU.",
			explain: "Exact, asta ar trebui să vă dea un mesaj de eroare mai bun!",
			correct: true
		},
		{
			text: "Citiți traceback-ul pentru a afla ce a cauzat eroarea.",
			explain: "Asta este ceea ce ați face pentru orice altă eroare, dar erorile CUDA nu sunt de obicei ridicate unde s-au întâmplat deoarece majoritatea operațiilor CUDA sunt asincrone."
		},
		{
			text: "Reduceți dimensiunea batch-ului.",
			explain: "Reducerea dimensiunii batch-ului este de obicei o strategie bună pentru gestionarea erorilor CUDA out-of-memory, dar nu pentru această problemă particulară. Încercați din nou!"
		},
		{
			text: "Reporniți kernel-ul Jupyter.",
			explain: "Încercați din nou -- repornirea kernel-ului nu va face eroarea să dispară prin magie!",
		}
	]}
/>

### 6. Care este cea mai bună modalitate de a face ca o problemă pe GitHub să fie reparată?

<Question
	choices={[
		{
			text: "Postați un exemplu complet reproductibil al bug-ului.",
			explain: "Da, aceasta este cea mai bună modalitate de a îi ajuta pe întreținători să găsească bug-ul vostru. Ce altceva ar trebui să faceți?",
			correct: true
		},
		{
			text: "Cereți în fiecare zi o actualizare.",
			explain: "Este puțin probabil să vă aducă ajutor; oamenii probabil vă vor ignora mai mult.",
		},
		{
			text: "Inspectați codul sursă din jurul bug-ului și încercați să găsiți motivul pentru care se întâmplă. Postați rezultatele în problemă.",
			explain: "Asta îi va ajuta cu siguranță pe întreținători! Și dacă găsiți sursa bug-ului și o reparație, puteți chiar deschide un pull request. Ce altceva ar trebui să faceți?",
			correct: true
		}
	]}
/>

### 7. De ce este supraajustarea pe un batch de obicei o tehnică bună de depanare?

<Question
	choices={[
		{
			text: "Nu este; supraajustarea este întotdeauna rea și ar trebui evitată.",
			explain: "Când antrenați pe întregul set de date, supraajustarea poate într-adevăr fi un semn că modelul vostru nu se va generaliza bine la exemple noi. Pentru depanare, totuși, nu antrenăm de obicei pe întregul set de date. Încercați din nou!"
		},
		{
			text: "Ne permite să verificăm că modelul este capabil să reducă loss-ul la zero.",
			explain: "Corect! Cu un batch mic cu doar două exemple, putem verifica rapid dacă modelul este capabil să învețe.",
			correct: true
		},
		{
			text: "Ne permite să verificăm că formele tensorilor intrărilor și etichetelor noastre sunt corecte.",
			explain: "Încercați din nou -- dacă formele tensorilor voștri sunt dezalinate, atunci cu siguranță nu veți putea antrena, nici măcar pe un singur batch.",
		}
	]}
/>

### 8. De ce este o idee bună să includeți detalii despre mediul vostru de calcul cu `transformers-cli env` când creați o problemă nouă în repository-ul 🤗 Transformers?

<Question
	choices={[
		{
			text: "Le permite întreținătorilor să înțeleagă ce versiune a bibliotecii folosiți.",
			explain: "Corect! Deoarece fiecare versiune majoră a bibliotecii poate avea modificări în API, știind ce versiune specifică folosiți poate ajuta la restrângerea problemei. Care sunt celelalte beneficii?",
			correct: true
		},
		{
			text: "Le permite întreținătorilor să știe dacă rulați cod pe Windows, macOS sau Linux.",
			explain: "Corect! Erorile pot fi uneori cauzate de sistemul de operare specific pe care îl folosiți, și știind acest lucru îi ajută pe întreținători să le reproducă local. Dar aceasta nu este singura cauză.",
			correct: true
		},
		{
			text: "Le permite întreținătorilor să știe dacă rulați cod pe un GPU sau CPU.",
			explain: "Corect! Așa cum am văzut în acest capitol, codul rulat pe GPU-uri sau CPU-uri poate produce rezultate sau erori diferite, și știind ce hardware folosiți poate ajuta la concentrarea atenției întreținătorilor. Dar aceasta nu este singurul beneficiu...",
			correct: true
		}
	]}
/> 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter8/7.mdx" />

### Debugging-ul pipeline-ului de antrenament[[debugging-pipeline-ului-de-antrenament]]
https://huggingface.co/learn/course/ro/chapter8/4_tf.md

# Debugging-ul pipeline-ului de antrenament[[debugging-pipeline-ului-de-antrenament]]

<CourseFloatingBanner chapter={8}
  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/chapter8/section4_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter8/section4_tf.ipynb"},
]} />

Ați scris un script frumos pentru a antrena sau ajusta fin un model pe o sarcină dată, urmând cu atenție sfaturile din [Capitolul 7](/course/chapter7). Dar când lansați comanda `model.fit()`, se întâmplă ceva oribil: primiți o eroare 😱! Sau mai rău, totul pare să fie în regulă și antrenamentul rulează fără eroare, dar modelul rezultat este prost. În această secțiune, vă vom arăta ce puteți face pentru a depana acest tip de probleme.

## Debugging-ul pipeline-ului de antrenament[[debugging-pipeline-ului-de-antrenament]]

<Youtube id="N9kO52itd0Q"/>

Problema când întâlniți o eroare în `model.fit()` este că ar putea veni din mai multe surse, deoarece antrenamentul aduce de obicei împreună multe lucruri la care ați lucrat până în acel punct. Problema ar putea fi ceva greșit în setul vostru de date, sau o problemă când încearcă să grupeze elementele seturilor de date împreună. Sau ar putea fi ceva greșit în codul modelului, sau funcția voastră de loss sau optimizatorul. Și chiar dacă totul merge bine pentru antrenament, ceva ar putea merge prost în timpul evaluării dacă există o problemă cu metrica voastră.

Cea mai bună modalitate de a face debugging la o eroare care apare în `model.fit()` este să parcurgeți manual întregul pipeline pentru a vedea unde au mers lucrurile prost. Eroarea este apoi adesea foarte ușor de rezolvat.

Pentru a demonstra aceasta, vom folosi următorul script care (încearcă să) ajusteze fin un model DistilBERT pe [setul de date MNLI](https://huggingface.co/datasets/glue):

```py
from datasets import load_dataset
import evaluate
from transformers import (
    AutoTokenizer,
    TFAutoModelForSequenceClassification,
)

raw_datasets = load_dataset("glue", "mnli")

model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)


def preprocess_function(examples):
    return tokenizer(examples["premise"], examples["hypothesis"], truncation=True)


tokenized_datasets = raw_datasets.map(preprocess_function, batched=True)

train_dataset = tokenized_datasets["train"].to_tf_dataset(
    columns=["input_ids", "labels"], batch_size=16, shuffle=True
)

validation_dataset = tokenized_datasets["validation_matched"].to_tf_dataset(
    columns=["input_ids", "labels"], batch_size=16, shuffle=True
)

model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint)

model.compile(loss="sparse_categorical_crossentropy", optimizer="adam")

model.fit(train_dataset)
```

Dacă încercați să îl executați, ați putea primi niște `VisibleDeprecationWarning`-uri când faceți conversia setului de date -- aceasta este o problemă UX cunoscută pe care o avem, deci vă rugăm să o ignorați. Dacă citiți cursul după, să zicem, noiembrie 2021 și încă se întâmplă, atunci trimiteți tweet-uri furioase la @carrigmat până când o repară.

Ce este o problemă mai serioasă, totuși, este că primim o eroare directă. Și este într-adevăr, înfricoșător de lungă:

```python out
ValueError: No gradients provided for any variable: ['tf_distil_bert_for_sequence_classification/distilbert/embeddings/word_embeddings/weight:0', '...']
```

Ce înseamnă asta? Am încercat să antrenăm pe datele noastre, dar nu am primit niciun gradient? Aceasta este destul de nedumeritoare; cum începem să depanăm ceva ca aceasta? Când eroarea pe care o primiți nu sugerează imediat unde este problema, cea mai bună soluție este adesea să parcurgeți lucrurile în secvență, asigurându-vă la fiecare etapă că totul arată corect. Și desigur, locul de început este întotdeauna să...

### Verificați datele voastre[[verificati-datele-voastre]]

Aceasta este de la sine înțeles, dar dacă datele voastre sunt corupte, Keras nu va putea să le repare pentru voi. Deci primul lucru, trebuie să aruncați o privire asupra a ceea ce este în setul vostru de antrenament.

Deși este tentant să vă uitați în interiorul `raw_datasets` și `tokenized_datasets`, vă recomandăm puternic să mergeți la date chiar la punctul unde vor intra în model. Aceasta înseamnă să citiți o ieșire din `tf.data.Dataset` pe care l-ați creat cu funcția `to_tf_dataset()`! Deci cum facem asta? Obiectele `tf.data.Dataset` ne dau batch-uri întregi odată și nu suportă indexarea, așa că nu putem să cerem pur și simplu `train_dataset[0]`. Putem, totuși, să îi cerem politicos un batch:

```py
for batch in train_dataset:
    break
```

`break` termină bucla după o iterație, așa că aceasta prinde primul batch care iese din `train_dataset` și îl salvează ca `batch`. Acum, să aruncăm o privire asupra a ceea ce este în interior:

```python out
{'attention_mask': <tf.Tensor: shape=(16, 76), dtype=int64, numpy=
 array([[1, 1, 1, ..., 0, 0, 0],
        [1, 1, 1, ..., 0, 0, 0],
        [1, 1, 1, ..., 0, 0, 0],
        ...,
        [1, 1, 1, ..., 1, 1, 1],
        [1, 1, 1, ..., 0, 0, 0],
        [1, 1, 1, ..., 0, 0, 0]])>,
 'label': <tf.Tensor: shape=(16,), dtype=int64, numpy=array([0, 2, 1, 2, 1, 1, 2, 0, 0, 0, 1, 0, 1, 2, 2, 1])>,
 'input_ids': <tf.Tensor: shape=(16, 76), dtype=int64, numpy=
 array([[ 101, 2174, 1010, ...,    0,    0,    0],
        [ 101, 3174, 2420, ...,    0,    0,    0],
        [ 101, 2044, 2048, ...,    0,    0,    0],
        ...,
        [ 101, 3398, 3398, ..., 2051, 2894,  102],
        [ 101, 1996, 4124, ...,    0,    0,    0],
        [ 101, 1999, 2070, ...,    0,    0,    0]])>}
```

Aceasta pare corectă, nu? Transmitem `labels`, `attention_mask`, și `input_ids` la model, care ar trebui să fie tot ce are nevoie pentru a calcula ieșirile și a calcula loss-ul. Deci de ce nu avem un gradient? Priviți mai atent: transmitem un singur dicționar ca intrare, dar un batch de antrenament este de obicei un tensor de intrare sau dicționar, plus un tensor de etichete. Etichetele noastre sunt doar o cheie în dicționarul nostru de intrare.

Este aceasta o problemă? Nu întotdeauna, de fapt! Dar este una dintre cele mai comune probleme pe care le veți întâlni când antrenați modele Transformer cu TensorFlow. Modelele noastre pot calcula toate loss-ul intern, dar pentru a face asta etichetele trebuie să fie transmise în dicționarul de intrare. Acesta este loss-ul care este folosit când nu specificăm o valoare de loss la `compile()`. Keras, pe de altă parte, se așteaptă de obicei ca etichetele să fie transmise separat de dicționarul de intrare, și calculele de loss vor eșua de obicei dacă nu faceți asta.

Problema a devenit acum mai clară: am transmis un argument `loss`, ceea ce înseamnă că îi cerem lui Keras să calculeze loss-urile pentru noi, dar am transmis etichetele noastre ca intrări la model, nu ca etichete în locul pe care Keras le așteaptă. Trebuie să alegem una sau alta: fie folosim loss-ul intern al modelului și păstrăm etichetele unde sunt, fie continuăm să folosim loss-urile Keras, dar mutăm etichetele în locul pe care Keras le așteaptă. Pentru simplitate, să adoptăm prima abordare. Schimbați apelul la `compile()` să citească:

```py
model.compile(optimizer="adam")
```

Acum vom folosi loss-ul intern al modelului, și această problemă ar trebui să fie rezolvată! 

<Tip>

✏️ **Rândul vostru!** Ca o provocare opțională după ce am rezolvat celelalte probleme, puteți încerca să vă întoarceți la acest pas și să faceți modelul să funcționeze cu loss-ul original calculat de Keras în loc de loss-ul intern. Va trebui să adăugați `"labels"` la argumentul `label_cols` al `to_tf_dataset()` pentru a vă asigura că etichetele sunt scoase corect, ceea ce vă va da gradienți -- dar mai există o problemă cu loss-ul pe care l-am specificat. Antrenamentul va rula încă cu această problemă, dar învățarea va fi foarte lentă și va ajunge la un platou la un loss de antrenament ridicat. Puteți să vă dați seama ce este?

Un indiciu codificat ROT13, dacă sunteți blocați: Vs lbh ybbx ng gur bhgchgf bs FrdhraprPynffvsvpngvba zbqryf va Genafsbezref, gurve svefg bhgchg vf `ybtvgf`. Jung ner ybtvgf?

Și un al doilea indiciu: Jura lbh fcrpvsl bcgvzvmref, npgvingvbaf be ybffrf jvgu fgevatf, Xrenf frgf nyy gur nethzrag inyhrf gb gurve qrsnhygf. Jung nethzragf qbrf FcnefrPngrtbevpnyPebffragebcl unir, naq jung ner gurve qrsnhygf?

</Tip>

Acum, să încercăm antrenamentul. Ar trebui să primim gradienți acum, așa că cu speranță (muzică sinistră se aude aici) putem pur și simplu să apelăm `model.fit()` și totul va funcționa bine!

```python out
  246/24543 [..............................] - ETA: 15:52 - loss: nan
```

Oh nu.

`nan` nu este o valoare foarte încurajatoare pentru loss. Cu toate acestea, am verificat datele noastre, și pare destul de bună. Dacă aceea nu este problema, unde putem merge următorul? Următorul pas evident este să...

### Verificați modelul vostru[[verificati-modelul-vostru]]

`model.fit()` este o funcție de conveniență foarte grozavă în Keras, dar face multe lucruri pentru voi, și aceasta poate face mai complicat să găsiți exact unde a apărut o problemă. Dacă depanați modelul vostru, o strategie care poate ajuta cu adevărat este să trimitieți doar un singur batch la model, și să vă uitați la ieșirile pentru acel batch în detaliu. Un alt sfat foarte util dacă modelul aruncă erori este să `compile()` modelul cu `run_eagerly=True`. Aceasta îl va face mult mai lent, dar va face mesajele de eroare mult mai comprehensibile, deoarece vor indica exact unde în codul modelului vostru a apărut problema.

Pentru moment, totuși, nu avem nevoie de `run_eagerly` încă. Să rulăm `batch`-ul pe care l-am primit înainte prin model și să vedem cum arată ieșirile:

```py
model(batch)
```

```python out
TFSequenceClassifierOutput(loss=<tf.Tensor: shape=(16,), dtype=float32, numpy=
array([nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
       nan, nan, nan], dtype=float32)>, logits=<tf.Tensor: shape=(16, 2), dtype=float32, numpy=
array([[nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan],
       [nan, nan]], dtype=float32)>, hidden_states=None, attentions=None)
```

Ei bine, aceasta este complicată. Totul este `nan`! Dar aceasta este ciudată, nu? Cum ar deveni toate logit-urile noastre `nan`? `nan` înseamnă "not a number" (nu un număr). Valorile `nan` apar adesea când efectuați o operație interzisă, cum ar fi împărțirea la zero. Dar un lucru care este foarte important de știut despre `nan` în machine learning este că această valoare tinde să se *propage*. Dacă înmulțiți un număr cu `nan`, ieșirea este de asemenea `nan`. Și dacă primiți un `nan` oriunde în ieșirea voastră, loss-ul voastru sau gradientul voastru, atunci se va răspândi rapid prin întreaga voastră rețea -- deoarece când acea valoare `nan` este propagată înapoi prin rețeaua voastră, veți primi gradienți `nan`, și când actualizările de greutăți sunt calculate cu acei gradienți, veți primi greutăți `nan`, și acele greutăți vor calcula și mai multe ieșiri `nan`! În curând întreaga rețea va fi doar un bloc mare de `nan`-uri. Odată ce se întâmplă asta, este destul de greu să vedeți unde a început problema. Cum putem izola unde `nan` a intrat pentru prima dată?

Răspunsul este să încercăm să *reinițializăm* modelul nostru. Odată ce am început antrenamentul, am primit un `nan` undeva și s-a propagat rapid prin întregul model. Deci, să încărcăm modelul dintr-un checkpoint și să nu facem nicio actualizare de greutăți, și să vedem unde primim o valoare `nan`:

```py
model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint)
model(batch)
```

Când rulăm asta, primim:

```py out
TFSequenceClassifierOutput(loss=<tf.Tensor: shape=(16,), dtype=float32, numpy=
array([0.6844486 ,        nan,        nan, 0.67127866, 0.7068601 ,
              nan, 0.69309855,        nan, 0.65531296,        nan,
              nan,        nan, 0.675402  ,        nan,        nan,
       0.69831556], dtype=float32)>, logits=<tf.Tensor: shape=(16, 2), dtype=float32, numpy=
array([[-0.04761693, -0.06509043],
       [-0.0481936 , -0.04556257],
       [-0.0040929 , -0.05848458],
       [-0.02417453, -0.0684005 ],
       [-0.02517801, -0.05241832],
       [-0.04514256, -0.0757378 ],
       [-0.02656011, -0.02646275],
       [ 0.00766164, -0.04350497],
       [ 0.02060014, -0.05655622],
       [-0.02615328, -0.0447021 ],
       [-0.05119278, -0.06928903],
       [-0.02859691, -0.04879177],
       [-0.02210129, -0.05791225],
       [-0.02363213, -0.05962167],
       [-0.05352269, -0.0481673 ],
       [-0.08141848, -0.07110836]], dtype=float32)>, hidden_states=None, attentions=None)
```

*Acum* ajungem undeva! Nu există valori `nan` în logit-urile noastre, ceea ce este liniștitor. Dar vedem câteva valori `nan` în loss-ul nostru! Există ceva special despre acele eșantioane în particular care cauzează această problemă? Să vedem care sunt (rețineți că dacă rulați acest cod voi înșivă, ați putea primi indici diferiți deoarece setul de date a fost amestecat):

```python
import numpy as np

loss = model(batch).loss.numpy()
indices = np.flatnonzero(np.isnan(loss))
indices
```

```python out
array([ 1,  2,  5,  7,  9, 10, 11, 13, 14])
```

Să ne uităm la eșantioanele din care au venit acești indici:

```python
input_ids = batch["input_ids"].numpy()
input_ids[indices]
```

```python out
array([[  101,  2007,  2032, ...,     0,     0,     0],
       [  101,  1998,  6814, ...,     0,     0,     0],
       [  101,  1998,  2007, ...,     0,     0,     0],
       ...,
       [  101, 13543,  1999, ...,     0,     0,     0]])
```

Ei bine, există multe aici, dar nimic nu iese în evidență ca fiind neobișnuit. Să ne uităm la etichete:

```python out
labels = batch['labels'].numpy()
labels[indices]
```

```python out
array([2, 2, 2, 2, 2, 2, 2, 2, 2])
```

Ah! Eșantioanele `nan` au toate aceeași etichetă, și este eticheta 2. Aceasta este un indiciu foarte puternic. Faptul că primim doar un loss de `nan` când eticheta noastră este 2 sugerează că acesta este un moment foarte bun pentru a verifica numărul de etichete în modelul nostru:

```python
model.config.num_labels
```

```python out
2
```

Acum vedem problema: modelul crede că sunt doar două clase, dar etichetele merg până la 2, ceea ce înseamnă că există de fapt trei clase (deoarece 0 este de asemenea o clasă). Așa am primit un `nan` -- prin încercarea de a calcula loss-ul pentru o clasă inexistentă! Să încercăm să schimbăm asta și să reîncadrăm modelul:

```
model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=3)
model.compile(optimizer='adam')
model.fit(train_dataset)
```

```python out
  869/24543 [>.............................] - ETA: 15:29 - loss: 1.1032
```

Antrenăm! Nu mai sunt `nan`-uri, și loss-ul nostru scade... cumva. Dacă îl urmăriți pentru o vreme, ați putea începe să deveniți puțin nerăbdători, deoarece valoarea loss-ului rămâne încăpățânat de mare. Să oprim antrenamentul aici și să încercăm să ne gândim la ce ar putea cauza această problemă. În acest punct, suntem destul de siguri că atât datele cât și modelul sunt în regulă, dar modelul nostru nu învață bine. Ce altceva rămâne? Este timpul să...

### Verificați hiperparametrii voștri[[verificati-hiperparametrii-vostri]]

Dacă vă uitați înapoi la codul de mai sus, ați putea să nu puteți vedea niciun hiperparametru deloc, în afară de poate `batch_size`, și acela nu pare un vinovat probabil. Nu vă lăsați înșelați, totuși; întotdeauna există hiperparametri, și dacă nu îi puteți vedea, înseamnă doar că nu știți la ce sunt setați. În particular, amintiți-vă un lucru critic despre Keras: dacă setați un loss, optimizator, sau funcție de activare cu un string, _toate argumentele sale vor fi setate la valorile lor implicite_. Aceasta înseamnă că deși folosirea string-urilor pentru aceasta este foarte convenabilă, ar trebui să fiți foarte atenți când o faceți, deoarece poate ascunde ușor lucruri critice de la voi. (Oricine încearcă provocarea opțională de mai sus ar trebui să ia notă cu atenție de acest fapt.)

În acest caz, unde am setat un argument cu un string? Setăm optimizatorul cu un string. Ar putea ascunde asta ceva de la noi? Să aruncăm o privire la [argumentele sale](https://www.tensorflow.org/api_docs/python/tf/keras/optimizers/Adam).

Iese ceva în evidență aici? Exact -- rata de învățare! Când folosim doar string-ul `'adam'`, vom primi rata de învățare implicită, care este 0.001, sau 1e-3. Aceasta este mult prea mare pentru un model Transformer! În general, recomandăm să încercați rate de învățare între 1e-5 și 1e-4 pentru modelele voastre; aceasta este undeva între de 10X și 100X mai mică decât valoarea pe care o folosim de fapt aici. Sună a o problemă majoră, așa că să încercăm să o reducem. Pentru a face asta, trebuie să importăm obiectul `optimizer` real. În timp ce suntem la asta, să reinițializăm modelul din checkpoint, în cazul în care antrenamentul cu rata de învățare mare i-a deteriorat greutățile:

```python
from tensorflow.keras.optimizers import Adam

model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint)
model.compile(optimizer=Adam(5e-5))
```

<Tip>

💡 Puteți de asemenea importa funcția `create_optimizer()` din 🤗 Transformers, care vă va da un optimizator AdamW cu weight decay corect precum și warmup și decay pentru rata de învățare. Acest optimizator va produce adesea rezultate puțin mai bune decât cele pe care le obțineți cu optimizatorul Adam implicit.

</Tip>

Acum, putem încerca să încadrăm modelul cu noua rată de învățare îmbunătățită:

```python
model.fit(train_dataset)
```

```python out
319/24543 [..............................] - ETA: 16:07 - loss: 0.9718
```

Acum loss-ul nostru chiar merge undeva! Antrenamentul pare în sfârșit că funcționează. Există o lecție aici: când modelul vostru rulează dar loss-ul nu scade, și sunteți siguri că datele voastre sunt în regulă, este o idee bună să verificați hiperparametri ca rata de învățare și weight decay. Setarea oricăruia dintre acestea prea mare este foarte probabil să cauzeze antrenamentul să "stagneze" la o valoare mare de loss.

## Alte probleme potențiale[[alte-probleme-potentiale]]

Am acoperit problemele din scriptul de mai sus, dar există mai multe erori comune cu care v-ați putea confrunta. Să aruncăm o privire la o listă (foarte incompletă).

### Gestionarea erorilor out-of-memory[[gestionarea-erorilor-out-of-memory]]

Semnul revelator al rămânerii fără memorie este o eroare ca "OOM when allocating tensor" -- OOM este prescurtare pentru "out of memory". Aceasta este o problemă foarte comună când aveți de-a face cu modele de limbă mari. Dacă întâlniți aceasta, o strategie bună este să vă înjumătățiți dimensiunea batch-ului și să încercați din nou. Rețineți, totuși, că unele modele sunt *foarte* mari. De exemplu, GPT-2 complet are 1.5B parametri, ceea ce înseamnă că veți avea nevoie de 6 GB de memorie doar pentru a stoca modelul, și încă 6 GB pentru gradienții săi! Antrenamentul modelului GPT-2 complet va necesita de obicei peste 20 GB de VRAM indiferent de dimensiunea batch-ului pe care îl folosiți, pe care doar câteva GPU-uri îl au. Modele mai ușoare cum ar fi `distilbert-base-cased` sunt mult mai ușor de rulat, și se antrenează mult mai rapid de asemenea.

<Tip>

În următoarea parte a cursului, vom examina tehnici mai avansate care vă pot ajuta să reduceți amprenta de memorie și să vă permită să ajustați fin cele mai mari modele.

</Tip>

### TensorFlow flămând flămând 🦛[[tensorflow-flamand-flamand]]

O particularitate specifică a TensorFlow de care ar trebui să fiți conștienți este că alocă *toată* memoria GPU pentru sine de îndată ce încărcați un model sau faceți orice antrenament, și apoi împarte acea memorie după cum este necesar. Aceasta este diferită de comportamentul altor framework-uri, cum ar fi PyTorch, care alocă memorie după cum este necesar cu CUDA în loc să o facă intern. Un avantaj al abordării TensorFlow este că poate da adesea erori utile când rămâneți fără memorie, și se poate recupera din acea stare fără să strice întregul kernel CUDA. Dar există de asemenea un dezavantaj important: dacă rulați două procese TensorFlow simultan, atunci **o să aveți probleme**.

Dacă rulați pe Colab nu trebuie să vă faceți griji pentru aceasta, dar dacă rulați local aceasta este cu siguranță ceva de care ar trebui să fiți atenți. În particular, fiți conștienți că închiderea unei file de notebook nu închide neapărat acel notebook! Ați putea fi nevoiți să selectați notebook-urile care rulează (cele cu o iconiță verde) și să le închideți manual în listarea de directoare. Orice notebook care rula și folosea TensorFlow ar putea să țină încă o grămadă din memoria GPU, și aceasta înseamnă că orice notebook nou pe care îl porniți ar putea întâlni probleme foarte ciudate.

Dacă începeți să primiți erori despre CUDA, BLAS, sau cuBLAS în cod care funcționa înainte, aceasta este adesea de vină. Puteți folosi o comandă cum ar fi `nvidia-smi` pentru a verifica -- când închideți sau reporniți notebook-ul curent, este cea mai multă memorie liberă, sau este încă în uz? Dacă este încă în uz, altceva o ține!

### Verificați din nou datele voastre![[verificati-din-nou-datele-voastre]]

Modelul vostru va învăța ceva doar dacă este de fapt posibil să învețe ceva din datele voastre. Dacă există o eroare care corupe datele sau etichetele sunt atribuite aleatoriu, este foarte probabil că nu veți obține niciun antrenament de model pe setul vostru de date. Un instrument util aici este `tokenizer.decode()`. Aceasta va transforma `input_ids` înapoi în string-uri, așa că puteți vizualiza datele și vedea dacă datele voastre de antrenament învață ceea ce vreți să învețe. De exemplu, după ce obțineți un `batch` din `tf.data.Dataset`-ul vostru cum am făcut mai sus, puteți decoda primul element astfel:

```py
input_ids = batch["input_ids"].numpy()
tokenizer.decode(input_ids[0])
```

Apoi puteți să îl comparați cu prima etichetă, astfel:

```py
labels = batch["labels"].numpy()
label = labels[0]
```

Odată ce puteți vizualiza datele voastre în acest mod, vă puteți întreba următoarele întrebări:

- Sunt datele decodate înțelegibile?
- Sunteți de acord cu etichetele?
- Există o etichetă care este mai comună decât altele?
- Care ar trebui să fie loss-ul/metrica dacă modelul ar prezice un răspuns aleatoriu/întotdeauna același răspuns?

După ce vă uitați la datele voastre, treceți prin câteva dintre predicțiile modelului -- dacă modelul vostru scoate tokenuri, încercați să le decodați și pe acelea! Dacă modelul prezice întotdeauna același lucru, ar putea fi pentru că setul vostru de date este părtinitor către o categorie (pentru problemele de clasificare), așa că tehnici precum supraesantionarea claselor rare ar putea ajuta. Alternativ, aceasta poate fi de asemenea cauzată de probleme de antrenament cum ar fi setări proaste de hiperparametri.

Dacă loss-ul/metrica pe care o obțineți pe modelul vostru inițial înainte de orice antrenament este foarte diferită de loss-ul/metrica pe care ați aștepta-o pentru predicții aleatorii, verificați din nou modul în care loss-ul sau metrica voastră este calculată, deoarece probabil există o eroare acolo. Dacă folosiți mai multe loss-uri pe care le adăugați la sfârșit, asigurați-vă că sunt de aceeași scară.

Când sunteți siguri că datele voastre sunt perfecte, puteți vedea dacă modelul este capabil să se antreneze pe ele cu un test simplu.

### Supraajustați modelul vostru pe un batch[[supraajustati-modelul-vostru-pe-un-batch]]

Supraajustarea este de obicei ceva pe care încercăm să îl evităm când antrenăm, deoarece înseamnă că modelul nu învață să recunoască caracteristicile generale pe care vrem să le recunoască, ci în schimb doar memorează eșantioanele de antrenament. Cu toate acestea, încercarea de a vă antrena modelul pe un batch din nou și din nou este un test bun pentru a verifica dacă problema așa cum ați formulat-o poate fi rezolvată de modelul pe care încercați să îl antrenați. De asemenea, vă va ajuta să vedeți dacă rata voastră de învățare inițială este prea mare.

Făcând aceasta odată ce ați definit `model`-ul vostru este foarte ușor; doar luați un batch de date de antrenament, apoi tratați acel `batch` ca întreg setul vostru de date, antrenând pe el pentru un număr mare de epoci:

```py
for batch in train_dataset:
    break

# Asigurați-vă că ați rulat model.compile() și ați setat optimizatorul,
# și loss-ul/metricile voastre dacă le folosiți

model.fit(batch, epochs=20)
```

<Tip>

💡 Dacă datele voastre de antrenament sunt dezechilibrate, asigurați-vă să construiți un batch de date de antrenament care conține toate etichetele.

</Tip>

Modelul rezultat ar trebui să aibă rezultate aproape perfecte pe `batch`, cu un loss care scade rapid către 0 (sau valoarea minimă pentru loss-ul pe care îl folosiți).

Dacă nu reușiți să faceți modelul vostru să obțină rezultate perfecte ca aceasta, înseamnă că există ceva greșit cu modul în care ați formulat problema sau datele voastre, așa că ar trebui să reparați asta. Doar când reușiți să treceți testul de supraajustare puteți fi siguri că modelul vostru poate învăța de fapt ceva.

<Tip warning={true}>

⚠️ Va trebui să vă recreați modelul și să recompilați după acest test de supraajustare, deoarece modelul obținut probabil nu va putea să se recupereze și să învețe ceva util pe setul vostru complet de date.

</Tip>

### Nu ajustați nimic până nu aveți o primă linie de bază[[nu-ajustati-nimic-pana-nu-aveti-o-prima-linie-de-baza]]

Ajustarea intensă a hiperparametrilor este întotdeauna subliniată ca fiind partea cea mai grea a machine learning-ului, dar este doar ultimul pas pentru a vă ajuta să câștigați puțin la metrică. Valori *foarte* proaste pentru hiperparametrii voștri, cum ar fi folosirea ratei de învățare implicite Adam de 1e-3 cu un model Transformer, vor face învățarea să procedeze foarte lent sau să stagneze complet, desigur, dar majoritatea timpului hiperparametri "rezonabili", cum ar fi o rată de învățare de la 1e-5 la 5e-5, vor funcționa bine pentru a vă da rezultate bune. Deci, nu vă lansați într-o căutare de hiperparametri consumatoare de timp și costisitoare până nu aveți ceva care bate linia de bază pe care o aveți pe setul vostru de date.

Odată ce aveți un model suficient de bun, puteți începe să ajustați puțin. Nu încercați să lansați o mie de rulări cu hiperparametri diferiți, ci comparați câteva rulări cu valori diferite pentru un hiperparametru pentru a avea o idee despre care are cel mai mare impact.

Dacă ajustați modelul în sine, păstrați-l simplu și nu încercați nimic pe care nu îl puteți justifica în mod rezonabil. Asigurați-vă întotdeauna că vă întoarceți la testul de supraajustare pentru a verifica că schimbarea voastră nu a avut consecințe neintenționate.

### Cereți ajutor[[cereti-ajutor]]

Sperăm că veți fi găsit câteva sfaturi în această secțiune care v-au ajutat să vă rezolvați problema, dar dacă nu este cazul, amintiți-vă că puteți întotdeauna să cereți comunității pe [forumuri](https://discuss.huggingface.co/).

Iată câteva resurse suplimentare care se pot dovedi utile:

- ["Reproducibility as a vehicle for engineering best practices"](https://docs.google.com/presentation/d/1yHLPvPhUs2KGI5ZWo0sU-PKU3GimAk3iTsI38Z-B5Gw/edit#slide=id.p) de Joel Grus
- ["Checklist for debugging neural networks"](https://towardsdatascience.com/checklist-for-debugging-neural-networks-d8b2a9434f21) de Cecelia Shao
- ["How to unit test machine learning code"](https://medium.com/@keeper6928/how-to-unit-test-machine-learning-code-57cf6fd81765) de Chase Roberts
- ["A Recipe for Training Neural Networks"](http://karpathy.github.io/2019/04/25/recipe/) de Andrej Karpathy

Desigur, nu fiecare problemă pe care o întâlniți când antrenați rețele neuronale este vina voastră! Dacă întâlniți ceva în biblioteca 🤗 Transformers sau 🤗 Datasets care nu pare corect, s-ar putea să fi întâlnit o eroare. Ar trebui cu siguranță să ne spuneți totul despre aceasta, și în secțiunea următoare vă vom explica exact cum să faceți asta. 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter8/4_tf.mdx" />

### Ce să faci când primești o eroare[[ce-sa-fac-cand-primesti-o-eroare]]
https://huggingface.co/learn/course/ro/chapter8/2.md

# Ce să faci când primești o eroare[[ce-sa-fac-cand-primesti-o-eroare]]

<CourseFloatingBanner chapter={8}
  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/chapter8/section2.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter8/section2.ipynb"},
]} />

În această secțiune vom examina câteva erori comune care pot apărea când încercați să generați predicții din modelul Transformer pe care l-ați ajustat recent. Aceasta vă va pregăti pentru [secțiunea 4](/course/chapter8/section4), unde vom explora cum să depanați faza de antrenament în sine.

<Youtube id="DQ-CpJn6Rc4"/>

Am pregătit un [repository model șablon](https://huggingface.co/lewtun/distilbert-base-uncased-finetuned-squad-d5716d28) pentru această secțiune, și dacă doriți să rulați codul din acest capitol trebuie mai întâi să copiați modelul în contul dumneavoastră pe [Hugging Face Hub](https://huggingface.co). Pentru a face acest lucru, mai întâi conectați-vă rulând fie următoarele într-un notebook Jupyter:

```python
from huggingface_hub import notebook_login

notebook_login()
```

sau următoarele în terminalul dumneavoastră preferat:

```bash
huggingface-cli login
```

Aceasta vă va solicita să introduceți numele de utilizator și parola și va salva un token sub *~/.cache/huggingface/*. Odată ce v-ați conectat, puteți copia repository-ul șablon cu următoarea funcție:

```python
from distutils.dir_util import copy_tree
from huggingface_hub import Repository, snapshot_download, create_repo, get_full_repo_name


def copy_repository_template():
    # Clone the repo and extract the local path
    template_repo_id = "lewtun/distilbert-base-uncased-finetuned-squad-d5716d28"
    commit_hash = "be3eaffc28669d7932492681cd5f3e8905e358b4"
    template_repo_dir = snapshot_download(template_repo_id, revision=commit_hash)
    # Create an empty repo on the Hub
    model_name = template_repo_id.split("/")[1]
    create_repo(model_name, exist_ok=True)
    # Clone the empty repo
    new_repo_id = get_full_repo_name(model_name)
    new_repo_dir = model_name
    repo = Repository(local_dir=new_repo_dir, clone_from=new_repo_id)
    # Copy files
    copy_tree(template_repo_dir, new_repo_dir)
    # Push to Hub
    repo.push_to_hub()
```

Acum când apelați `copy_repository_template()`, aceasta va crea o copie a repository-ului șablon sub contul dumneavoastră.

## Depanarea pipeline-ului din 🤗 Transformers[[depanarea-pipeline-ului-din-transformers]]

Pentru a începe călătoria noastră în lumea minunată a depanării modelelor Transformer, considerați următorul scenariu: lucrați cu un coleg la un proiect de răspuns la întrebări pentru a ajuta clienții unui site web de e-commerce să găsească răspunsuri despre produsele de consum. Colegul dumneavoastră vă trimite un mesaj precum:

> Salut! Tocmai am rulat un experiment folosind tehnicile din [Capitolul 7](/course/chapter7/7) al cursului Hugging Face și am obținut rezultate grozave pe SQuAD! Cred că putem folosi acest model ca punct de plecare pentru proiectul nostru. ID-ul modelului pe Hub este "lewtun/distillbert-base-uncased-finetuned-squad-d5716d28". Simte-te liber să îl testezi :)

Iar primul lucru la care vă gândiți este să încărcați modelul folosind `pipeline` din 🤗 Transformers:

```python
from transformers import pipeline

model_checkpoint = get_full_repo_name("distillbert-base-uncased-finetuned-squad-d5716d28")
reader = pipeline("question-answering", model=model_checkpoint)
```

```python out
"""
OSError: Can't load config for 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28'. Make sure that:

- 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co/models'

- or 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file
"""
```

Oh nu, se pare că ceva a mers prost! Dacă sunteți nou în programare, acest tip de erori pot părea puțin criptice la început (ce este un `OSError`?!). Eroarea afișată aici este doar ultima parte dintr-un raport de eroare mult mai mare numit _Python traceback_ (cunoscut și ca stack trace). De exemplu, dacă rulați acest cod pe Google Colab, ar trebui să vedeți ceva similar cu următoarea captură de ecran:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/traceback.png" alt="Un traceback Python." width="100%"/>
</div>

Există multe informații conținute în aceste rapoarte, așa că să parcurgem împreună părțile cheie. Primul lucru de reținut este că traceback-urile ar trebui citite _de jos în sus_. Aceasta poate părea ciudat dacă sunteți obișnuiți să citiți textul în engleză de sus în jos, dar reflectă faptul că traceback-ul arată secvența de apeluri de funcții pe care `pipeline` le face când descarcă modelul și tokenizer-ul. (Consultați [Capitolul 2](/course/chapter2) pentru mai multe detalii despre cum funcționează `pipeline` în culise.)

<Tip>

🚨 Vedeți acea casetă albastră din jurul "6 frames" în traceback-ul din Google Colab? Aceasta este o caracteristică specială a Colab, care comprimă traceback-ul în "frame-uri". Dacă nu reușiți să găsiți sursa unei erori, asigurați-vă că extindeți traceback-ul complet făcând clic pe acele două săgeți mici.

</Tip>

Aceasta înseamnă că ultima linie a traceback-ului indică ultimul mesaj de eroare și dă numele excepției care a fost ridicată. În acest caz, tipul excepției este `OSError`, care indică o eroare legată de sistem. Dacă citim mesajul de eroare însoțitor, putem vedea că pare să existe o problemă cu fișierul *config.json* al modelului și ni se dau două sugestii pentru a o rezolva:

```python out
"""
Make sure that:

- 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co/models'

- or 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file
"""
```

<Tip>

💡 Dacă întâlniți un mesaj de eroare care este dificil de înțeles, doar copiați și lipiți mesajul în bara de căutare Google sau [Stack Overflow](https://stackoverflow.com/) (da, chiar!). Există o șansă bună că nu sunteți prima persoană care întâlnește eroarea, și aceasta este o modalitate bună de a găsi soluții pe care alții din comunitate le-au postat. De exemplu, căutarea pentru `OSError: Can't load config for` pe Stack Overflow dă mai multe [rezultate](https://stackoverflow.com/search?q=OSError%3A+Can%27t+load+config+for+) care ar putea fi folosite ca punct de plecare pentru rezolvarea problemei.

</Tip>

Prima sugestie ne cere să verificăm dacă ID-ul modelului este într-adevăr corect, așa că primul lucru de făcut este să copiem identificatorul și să îl lipim în bara de căutare a Hub-ului:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/wrong-model-id.png" alt="Numele greșit al modelului." width="100%"/>
</div>

Hmm, într-adevăr pare că modelul colegului nostru nu este pe Hub... aha, dar există o greșeală de tipar în numele modelului! DistilBERT are doar un "l" în numele său, așa că să corectăm asta și să căutăm "lewtun/distilbert-base-uncased-finetuned-squad-d5716d28" în schimb:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/true-model-id.png" alt="Numele corect al modelului." width="100%"/>
</div>

Bun, aceasta a dat un rezultat. Acum să încercăm să descărcăm din nou modelul cu ID-ul corect:

```python
model_checkpoint = get_full_repo_name("distilbert-base-uncased-finetuned-squad-d5716d28")
reader = pipeline("question-answering", model=model_checkpoint)
```

```python out
"""
OSError: Can't load config for 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28'. Make sure that:

- 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co/models'

- or 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file
"""
```

Argh, eșuat din nou -- bun venit în viața de zi cu zi a unui inginer de machine learning! Deoarece am corectat ID-ul modelului, problema trebuie să fie în repository în sine. O modalitate rapidă de a accesa conținutul unui repository pe 🤗 Hub este prin funcția `list_repo_files()` din biblioteca `huggingface_hub`:

```python
from huggingface_hub import list_repo_files

list_repo_files(repo_id=model_checkpoint)
```

```python out
['.gitattributes', 'README.md', 'pytorch_model.bin', 'special_tokens_map.json', 'tokenizer_config.json', 'training_args.bin', 'vocab.txt']
```

Interesant -- nu pare să existe un fișier *config.json* în repository! Nu e de mirare că `pipeline`-ul nostru nu a putut încărca modelul; colegul nostru trebuie să fi uitat să împingă acest fișier pe Hub după ce l-a ajustat fin. În acest caz, problema pare destul de simplă de rezolvat: am putea să îi cerem să adauge fișierul, sau, deoarece putem vedea din ID-ul modelului că modelul preantrenat folosit a fost [`distilbert-base-uncased`](https://huggingface.co/distilbert-base-uncased), putem descărca configurația pentru acest model și să o împingem în repository-ul nostru pentru a vedea dacă aceasta rezolvă problema. Să încercăm asta. Folosind tehnicile pe care le-am învățat în [Capitolul 2](/course/chapter2), putem descărca configurația modelului cu clasa `AutoConfig`:

```python
from transformers import AutoConfig

pretrained_checkpoint = "distilbert-base-uncased"
config = AutoConfig.from_pretrained(pretrained_checkpoint)
```

<Tip warning={true}>

🚨 Abordarea pe care o adoptăm aici nu este infailibilă, deoarece colegul nostru poate să fi modificat configurația `distilbert-base-uncased` înainte de a ajusta fin modelul. În viața reală, am vrea să verificăm cu ei mai întâi, dar în scopurile acestei secțiuni vom presupune că au folosit configurația implicită.

</Tip>

Apoi putem împinge aceasta în repository-ul nostru de model cu funcția `push_to_hub()` a configurației:

```python
config.push_to_hub(model_checkpoint, commit_message="Add config.json")
```

Acum putem testa dacă aceasta a funcționat încărcând modelul din cel mai recent commit pe ramura `main`:

```python
reader = pipeline("question-answering", model=model_checkpoint, revision="main")

context = r"""
Extractive Question Answering is the task of extracting an answer from a text
given a question. An example of a question answering dataset is the SQuAD
dataset, which is entirely based on that task. If you would like to fine-tune a
model on a SQuAD task, you may leverage the
examples/pytorch/question-answering/run_squad.py script.

🤗 Transformers is interoperable with the PyTorch, TensorFlow, and JAX
frameworks, so you can use your favourite tools for a wide variety of tasks!
"""

question = "What is extractive question answering?"
reader(question=question, context=context)
```

```python out
{'score': 0.38669535517692566,
 'start': 34,
 'end': 95,
 'answer': 'the task of extracting an answer from a text given a question'}
```

Woohoo, a funcționat! Să recapitulăm ce ați învățat tocmai:

- Mesajele de eroare în Python sunt cunoscute ca _traceback-uri_ și sunt citite de jos în sus. Ultima linie a mesajului de eroare conține de obicei informațiile de care aveți nevoie pentru a localiza sursa problemei.
- Dacă ultima linie nu conține informații suficiente, parcurgeți traceback-ul în sus și vedeți dacă puteți identifica unde în codul sursă apare eroarea.
- Dacă niciunul dintre mesajele de eroare nu vă poate ajuta să depanați problema, încercați să căutați online o soluție la o problemă similară.
- Biblioteca `huggingface_hub` oferă o suită de instrumente pe care le puteți folosi pentru a interacționa cu și a depana repository-urile de pe Hub.

Acum că știți cum să depanați un pipeline, să aruncăm o privire la un exemplu mai complicat în forward pass-ul modelului în sine.

## Depanarea forward pass-ului modelului dumneavoastră[[depanarea-forward-pass-ului-modelului-dumneavoastra]]

Deși `pipeline` este grozav pentru majoritatea aplicațiilor unde aveți nevoie să generați rapid predicții, uneori va trebui să accesați logit-urile modelului (să zicem, dacă aveți o post-procesare personalizată pe care ați dori să o aplicați). Pentru a vedea ce poate merge prost în acest caz, să extragem mai întâi modelul și tokenizer-ul din `pipeline`-ul nostru:

```python
tokenizer = reader.tokenizer
model = reader.model
```

Apoi avem nevoie de o întrebare, așa că să vedem dacă framework-urile noastre preferate sunt suportate:

```python
question = "Which frameworks can I use?"
```

Așa cum am văzut în [Capitolul 7](/course/chapter7), pașii obișnuiți pe care trebuie să îi facem sunt tokenizarea intrărilor, extragerea logit-urilor token-urilor de început și sfârșit, și apoi decodarea span-ului de răspuns:

```python
import torch

inputs = tokenizer(question, context, add_special_tokens=True)
input_ids = inputs["input_ids"][0]
outputs = model(**inputs)
answer_start_scores = outputs.start_logits
answer_end_scores = outputs.end_logits
# Get the most likely beginning of answer with the argmax of the score
answer_start = torch.argmax(answer_start_scores)
# Get the most likely end of answer with the argmax of the score
answer_end = torch.argmax(answer_end_scores) + 1
answer = tokenizer.convert_tokens_to_string(
    tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end])
)
print(f"Question: {question}")
print(f"Answer: {answer}")
```

```python out
"""
OSError: Can't load config for 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28'. Make sure that:

- 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co/models'

- or 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file
"""
```

Oh nu, se pare că avem o eroare în codul nostru! Dar nu ne temem de puțină depanare. Puteți folosi debugger-ul Python într-un notebook:

<Youtube id="rSPyvPw0p9k"/>

sau într-un terminal:

<Youtube id="5PkZ4rbHL6c"/>

Aici, citirea mesajului de eroare ne spune că `'list' object has no attribute 'size'`, și putem vedea o săgeată `-->` care indică linia unde a fost ridicată problema în `model(**inputs)`. Puteți depana aceasta interactiv folosind debugger-ul Python, dar pentru moment vom afișa pur și simplu o porțiune din `inputs` pentru a vedea ce avem:

```python
inputs["input_ids"][:5]
```

```python out
[101, 2029, 7705, 2015, 2064]
```

Aceasta cu siguranță arată ca o `list` Python obișnuită, dar să verificăm din nou tipul:

```python
type(inputs["input_ids"])
```

```python out
list
```

Da, aceasta este cu siguranță o `list` Python. Deci ce a mers prost? Amintiți-vă din [Capitolul 2](/course/chapter2) că clasele `AutoModelForXxx` din 🤗 Transformers operează pe _tensori_ (fie în PyTorch sau TensorFlow), și o operație comună este să extragi dimensiunile unui tensor folosind `Tensor.size()` în, să zicem, PyTorch. Să aruncăm din nou o privire la traceback, pentru a vedea care linie a declanșat excepția:

```
~/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py in forward(self, input_ids, attention_mask, head_mask, inputs_embeds, output_attentions, output_hidden_states, return_dict)
    471             raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
    472         elif input_ids is not None:
--> 473             input_shape = input_ids.size()
    474         elif inputs_embeds is not None:
    475             input_shape = inputs_embeds.size()[:-1]

AttributeError: 'list' object has no attribute 'size'
```

Se pare că codul nostru a încercat să apeleze `input_ids.size()`, dar aceasta în mod clar nu va funcționa pentru o `list` Python, care este doar un container. Cum putem rezolva această problemă? Căutarea mesajului de eroare pe Stack Overflow oferă destul de multe [rezultate](https://stackoverflow.com/search?q=AttributeError%3A+%27list%27+object+has+no+attribute+%27size%27&s=c15ec54c-63cb-481d-a749-408920073e8f) relevante. Făcând clic pe primul afișează o întrebare similară cu a noastră, cu răspunsul arătat în captura de ecran de mai jos:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter8/stack-overflow.png" alt="Un răspuns de pe Stack Overflow." width="100%"/>
</div>

Răspunsul recomandă să adăugăm `return_tensors='pt'` la tokenizer, așa că să vedem dacă aceasta funcționează pentru noi:

```python out
inputs = tokenizer(question, context, add_special_tokens=True, return_tensors="pt")
input_ids = inputs["input_ids"][0]
outputs = model(**inputs)
answer_start_scores = outputs.start_logits
answer_end_scores = outputs.end_logits
# Get the most likely beginning of answer with the argmax of the score
answer_start = torch.argmax(answer_start_scores)
# Get the most likely end of answer with the argmax of the score
answer_end = torch.argmax(answer_end_scores) + 1
answer = tokenizer.convert_tokens_to_string(
    tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end])
)
print(f"Question: {question}")
print(f"Answer: {answer}")
```

```python out
"""
Question: Which frameworks can I use?
Answer: pytorch, tensorflow, and jax
"""
```

Minunat, a funcționat! Acesta este un exemplu grozav de cât de util poate fi Stack Overflow: prin identificarea unei probleme similare, am putut beneficia de experiența altora din comunitate. Cu toate acestea, o căutare ca aceasta nu va da întotdeauna un răspuns relevant, așa că ce puteți face în astfel de cazuri? Din fericire, există o comunitate primitoare de dezvoltatori pe [forumurile Hugging Face](https://discuss.huggingface.co/) care vă pot ajuta! În secțiunea următoare, vom arunca o privire asupra modului în care puteți formula întrebări bune pe forum care au șanse să primească răspuns. 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter8/2.mdx" />

### Quiz de final de capitol[[end-of-chapter-quiz]]
https://huggingface.co/learn/course/ro/chapter9/9.md

# Quiz de final de capitol[[end-of-chapter-quiz]]

<CourseFloatingBanner
    chapter={9}
    classNames="absolute z-10 right-0 top-0"
/>

Să testăm ce ați învățat în acest capitol!

### 1. Pentru ce puteți folosi Gradio?

<Question
	choices={[
        {
			text: "Să creați un demo pentru modelul vostru de machine learning",
			explain: "Cu câteva linii de cod Python puteți genera un demo pentru modelul vostru ML folosind biblioteca noastră de componente pre-construite.",
			correct: true
		},
		{
			text: "Să partajați modelul vostru de machine learning cu alții",
			explain: "Folosind parametrul <code>share=True</code> în metoda launch, puteți genera un link de partajare pe care să îl trimiteți oricui.",
            correct: true
		},
		{
			text: "Să depanați modelul vostru",
			explain: "Un avantaj al unui demo gradio este să puteți testa modelul cu date reale pe care le puteți schimba și să observați predicțiile modelului schimbându-se în timp real, ajutându-vă să depanați modelul.",
			correct: true
		},
		{
			text: "Să antrenați modelul vostru",
			explain: "Gradio este conceput să fie folosit pentru inferența modelului, DUPĂ ce modelul este antrenat.",
		}
	]}
/>

### 2. Gradio funcționează DOAR cu modele PyTorch

<Question
	choices={[
        {
			text: "Adevărat",
			explain: "Gradio funcționează cu modele PyTorch, dar funcționează și pentru orice tip de model de machine learning!"
        },
        {
			text: "Fals",
			explain: "Gradio este agnostic față de model, ceea ce înseamnă că puteți crea un demo pentru orice tip de model de machine learning.",
			correct: true
        }
	]}
/>

### 3. De unde puteți lansa un demo Gradio?

<Question
	choices={[
        {
			text: "IDE-uri Python standard",
			explain: "Gradio funcționează excelent cu IDE-ul vostru preferat.",
            correct: true
        },
        {
			text: "Notebook-uri Google Colab",
			explain: "Puteți crea și lansa un demo în notebook-ul vostru Google Colab.",
			correct: true
        },
        {
			text: "Notebook-uri Jupyter",
			explain: "Alegere bună - Puteți crea și lansa un demo în notebook-ul vostru Jupyter.",
			correct: true
        }
	]}
/>

### 4. Gradio este construit în primul rând pentru modele NLP

<Question
	choices={[
        {
			text: "Adevărat",
			explain: "Gradio funcționează cu aproape orice tip de date, nu doar NLP."
        },
        {
			text: "Fals",
			explain: "Gradio oferă dezvoltatorilor o bibliotecă de componente pre-construite pentru aproape toate tipurile de date.",
            correct: true
        }
	]}
/>

### 5. Care dintre următoarele caracteristici sunt suportate de Gradio?

<Question
	choices={[
        {
			text: "Intrări și ieșiri multiple",
			explain: "Intrări și ieșiri multiple sunt posibile cu gradio. Tot ce trebuie să faceți este să transmiteți o listă de intrări și ieșiri la parametrii corespunzători",
            correct: true
        },
        {
			text: "Stare pentru persistența datelor",
			explain: "Gradio este capabil să adauge stare la interfața voastră.",
			correct: true
        },
        {
			text: "Autentificare cu nume de utilizator și parole",
			explain: "Transmiteți o listă de tuple nume de utilizator/parolă la metoda `launch` pentru a adăuga autentificare.",
			correct: true
        },
        {
			text: "Analize automate pentru cine folosește demo-ul vostru gradio",
			explain: "Încercați din nou - Gradio nu oferă dezvoltatorilor analize despre cine folosește demo-urile lor."
        },
        {
			text: "Încărcarea unui model din hub-ul de modele Hugging Face sau Hugging Face Spaces",
			explain: "Absolut - încărcați orice model Hugging Face folosind metoda <code>gr.Interface.load()</code>",
			correct: true
        }
	]}
/>

### 6. Care dintre următoarele sunt moduri valide de a încărca un model Hugging Face din Hub sau Spaces?

<Question
	choices={[
        {
			text: "gr.Interface.load('huggingface/{user}/{model_name}')",
			explain: "Aceasta este o metodă validă de a încărca un model Hugging Face din Hub",
            correct: true
        },
        {
			text: "gr.Interface.load('model/{user}/{model_name}')",
			explain: "Aceasta este o metodă validă de a încărca un model Hugging Face din Hub",
			correct: true
        },
        {
			text: "gr.Interface.load('demos/{user}/{model_name}')",
			explain: "Încercați din nou -- nu puteți încărca un model folosind prefixul 'demos'."
        },
        {
			text: "gr.Interface.load('spaces/{user}/{model_name}')",
			explain: "Aceasta este o metodă validă de a încărca un model Hugging Face din Spaces",
			correct: true
        }
	]}
/>

### 7. Selectați toți pașii necesari pentru adăugarea stării la interfața voastră Gradio

<Question
	choices={[
        {
			text: "Transmiteți un parametru suplimentar în funcția voastră de predicție, care reprezintă starea interfeței.",
			explain: "Un parametru suplimentar care stochează istoricul sau starea interfeței voastre este necesar.",
            correct: true
        },
        {
			text: "La sfârșitul funcției de predicție, returnați valoarea actualizată a stării ca o valoare de returnare suplimentară.",
			explain: "Această valoare de istoric sau stare trebuie să fie returnată de funcția voastră.",
            correct: true
        },
        {
			text: "Adăugați componentele de intrare și ieșire de stare când creați Interface-ul vostru",
			explain: "Gradio oferă o componentă de intrare și ieșire de stare pentru a persista datele.",
            correct: true
        }
	]}
/>

### 8. Care dintre următoarele sunt componente incluse în biblioteca Gradio?

<Question
	choices={[
        {
			text: "Textbox.",
			explain: "Da, puteți crea cutii de text cu componenta Textbox.",
            correct: true
        },
        {
			text: "Graph.",
			explain: "În prezent nu există o componentă Graph.",
        },
        {
			text: "Image.",
			explain: "Da, puteți crea un widget de încărcare imagini cu componenta Image.",
            correct: true
        },
        {
			text: "Audio.",
			explain: "Da, puteți crea un widget de încărcare audio cu componenta Audio.",
            correct: true
        },
	]}
/>

### 9. Ce vă permite să faceți Gradio `Blocks`?

<Question
	choices={[
        {
			text: "Să combinați mai multe demo-uri într-o aplicație web",
			explain: "Puteți folosi `with gradio.Tabs():` pentru a adăuga file pentru mai multe demo-uri",
			correct: true
        },
        {
			text: "Să atribuiți declanșatori de evenimente precum clicked/changed/etc la componentele `Blocks`",
			explain: "Când atribuiți un eveniment, transmiteți trei parametri: fn: funcția care ar trebui apelată, inputs: (lista) componentei(lor) de intrare, și outputs: (lista) componentelor de ieșire care ar trebui apelate.",
			correct: true
        },
        {
			text: "Să determinați automat care componentă `Blocks` ar trebui să fie interactivă vs. statică",
			explain: "Pe baza declanșatorilor de evenimente pe care îi definiți, `Blocks` determină automat dacă o componentă ar trebui să accepte intrări de la utilizator sau nu.",
			correct: true
        },
		 {
			text: "Să creați demo-uri cu mai mulți pași; adică să vă permită să reutilizați ieșirea unei componente ca intrare la următoarea",
			explain: "Puteți folosi o componentă pentru intrarea unui declanșator de eveniment, dar ieșirea altuia.",
            correct: true
        },
	]}
/>

### 10. Puteți partaja un link public la un demo `Blocks` și puteți găzdui un demo `Blocks` pe Hugging Face spaces.

<Question
	choices={[
        {
			text: "Adevărat",
			explain: "La fel ca `Interface`, toate capacitățile de partajare și găzduire sunt aceleași pentru demo-urile `Blocks`!",
			correct: true
        },
        {
			text: "Fals",
			explain: "La fel ca `Interface`, toate capacitățile de partajare și găzduire sunt aceleași pentru demo-urile `Blocks`!",
			correct: false
        }
	]}
/> 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter9/9.mdx" />

### Integrări cu Hugging Face Hub[[integrations-with-the-hugging-face-hub]]
https://huggingface.co/learn/course/ro/chapter9/5.md

# Integrări cu Hugging Face Hub[[integrations-with-the-hugging-face-hub]]

<CourseFloatingBanner chapter={9}
  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/chapter9/section5.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter9/section5.ipynb"},
]} />

Pentru a vă face viața și mai ușoară, Gradio se integrează direct cu Hugging Face Hub și Hugging Face Spaces.
Puteți încărca demo-uri din Hub și Spaces cu o singură *linie de cod*.

### Încărcarea modelelor din Hugging Face Hub[[loading-models-from-the-hugging-face-hub]]
Pentru a începe, alegeți unul dintre miile de modele pe care Hugging Face le oferă prin Hub, așa cum este descris în [Capitolul 4](/course/chapter4/2).

Folosind metoda specială `Interface.load()`, transmiteți `"model/"` (sau, echivalent, `"huggingface/"`) 
urmată de numele modelului. 
De exemplu, iată codul pentru a construi un demo pentru [GPT-J](https://huggingface.co/EleutherAI/gpt-j-6B), un model de limbaj mare, cu adăugarea a câtorva exemple de intrări:

```py
import gradio as gr

title = "GPT-J-6B"
description = "Gradio Demo for GPT-J 6B, a transformer model trained using Ben Wang's Mesh Transformer JAX. 'GPT-J' refers to the class of model, while '6B' represents the number of trainable parameters. To use it, simply add your text, or click one of the examples to load them. Read more at the links below."
article = "<p style='text-align: center'><a href='https://github.com/kingoflolz/mesh-transformer-jax' target='_blank'>GPT-J-6B: A 6 Billion Parameter Autoregressive Language Model</a></p>"

gr.Interface.load(
    "huggingface/EleutherAI/gpt-j-6B",
    inputs=gr.Textbox(lines=5, label="Input Text"),
    title=title,
    description=description,
    article=article,
).launch()
```
    
Codul de mai sus va produce interfața de mai jos:

<iframe src="https://course-demos-gpt-j-6B.hf.space" frameBorder="0" height="750" title="Gradio app" class="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>

Încărcarea unui model în acest mod folosește [API-ul de Inferență](https://huggingface.co/inference-api) de la Hugging Face,
în loc să încarce modelul în memorie. Acest lucru este ideal pentru modele uriașe precum GPT-J sau T0pp care
 necesită multă RAM.

### Încărcarea din Hugging Face Spaces[[loading-from-hugging-face-spaces]]
Pentru a încărca orice Space din Hugging Face Hub și a-l recrea local, puteți transmite `spaces/` la `Interface`, urmată de numele Space-ului.

Vă amintiți demo-ul din secțiunea 1 care elimina fundalul unei imagini? Să-l încărcăm din Hugging Face Spaces:

```py
gr.Interface.load("spaces/abidlabs/remove-bg").launch()
```

<iframe src="https://course-demos-remove-bg-original.hf.space" frameBorder="0" height="650" title="Gradio app" class="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>

Unul dintre lucrurile cool la încărcarea demo-urilor din Hub sau Spaces este că le puteți personaliza 
prin suprascrierea oricăruia dintre 
parametri. Aici, adăugăm un titlu și facem să funcționeze cu o webcam în schimb:

```py
gr.Interface.load(
    "spaces/abidlabs/remove-bg", inputs="webcam", title="Remove your webcam background!"
).launch()
```

<iframe src="https://course-demos-Remove-bg.hf.space" frameBorder="0" height="550" title="Gradio app" class="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>

Acum că am explorat câteva moduri de a integra Gradio cu Hugging Face Hub, să aruncăm o privire la câteva caracteristici avansate ale clasei `Interface`. Aceasta este tema următoarei secțiuni! 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter9/5.mdx" />

### Introducere în Gradio[[introduction-to-gradio]]
https://huggingface.co/learn/course/ro/chapter9/1.md

# Introducere în Gradio[[introduction-to-gradio]]

<CourseFloatingBanner
    chapter={9}
    classNames="absolute z-10 right-0 top-0"
/>

În acest capitol vom învăța cum să construim **demo-uri interactive** pentru modelele noastre de machine learning.

De ce să construim un demo sau o interfață grafică pentru modelul nostru de machine learning în primul rând? Demo-urile permit:

- **Dezvoltatorilor de machine learning** să își prezinte cu ușurință lucrarea unei audiențe largi, inclusiv echipelor non-tehnice sau clienților
- **Cercetătorilor** să reproducă mai ușor modelele și comportamentul machine learning
- **Testerilor de calitate** sau **utilizatorilor finali** să identifice și să depaneze mai ușor punctele de eșec ale modelelor
- **Utilizatorilor diverși** să descopere prejudecățile algoritmice din modele

Vom folosi biblioteca Gradio pentru a construi demo-uri pentru modelele noastre. Gradio vă permite să construiți, să personalizați și să partajați demo-uri web pentru orice model de machine learning, în întregime în Python.

Iată câteva exemple de demo-uri de machine learning construite cu Gradio:

* Un model de **recunoaștere a schițelor** care primește o schiță și afișează etichete cu ceea ce crede că este desenat:

<iframe src="https://course-demos-draw2.hf.space" frameBorder="0" height="450" title="Gradio app" class="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>

* Un model extractiv de **răspunsuri la întrebări** care primește un paragraf de context și o întrebare și afișează un răspuns și un scor de probabilitate (am discutat despre acest tip de model [în Capitolul 7](/course/chapter7/7)):

<iframe src="https://course-demos-question-answering-simple.hf.space" frameBorder="0" height="640" title="Gradio app" class="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>

* Un model de **eliminare a fundalului** care primește o imagine și afișează imaginea cu fundalul eliminat:

<iframe src="https://course-demos-remove-bg-original.hf.space" frameBorder="0" height="640" title="Gradio app" class="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>

Acest capitol este împărțit în secțiuni care includ atât _concepte_ cât și _aplicații_. După ce învățați conceptul din fiecare secțiune, îl veți aplica pentru a construi un anumit tip de demo, de la clasificarea imaginilor la recunoașterea vocii. Până când terminați acest capitol, veți putea construi aceste demo-uri (și multe altele!) în doar câteva linii de cod Python.

<Tip>
👀 Consultați <a href="https://huggingface.co/spaces" target="_blank">Hugging Face Spaces</a> pentru a vedea multe exemple recente de demo-uri de machine learning construite de comunitatea machine learning!
</Tip> 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter9/1.mdx" />

### Înțelegerea clasei Interface[[understanding-the-interface-class]]
https://huggingface.co/learn/course/ro/chapter9/3.md

# Înțelegerea clasei Interface[[understanding-the-interface-class]]

<CourseFloatingBanner chapter={9}
  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/chapter9/section3.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter9/section3.ipynb"},
]} />

În această secțiune, vom examina mai îndeaproape clasa `Interface` și vom înțelege
parametrii principali folosiți pentru a crea una.

## Cum să creați o Interface[[how-to-create-an-interface]]

Veți observa că clasa `Interface` are 3 parametri necesari:

`Interface(fn, inputs, outputs, ...)`

Acești parametri sunt:

  - `fn`: funcția de predicție care este încapsulată de interfața Gradio. Această funcție poate lua unul sau mai mulți parametri și poate returna una sau mai multe valori
  - `inputs`: tipul(tipurile) componentei de intrare. Gradio oferă multe componente pre-construite precum `"image"` sau `"mic"`.
  - `outputs`: tipul(tipurile) componentei de ieșire. Din nou, Gradio oferă multe componente pre-construite de ex. `"image"` sau `"label"`.

Pentru o listă completă a componentelor, [consultați documentația Gradio](https://gradio.app/docs). Fiecare componentă pre-construită poate fi personalizată prin instanțierea clasei corespunzătoare componentei.

De exemplu, așa cum am văzut în [secțiunea anterioară](/course/chapter9/2),
în loc să transmiteți `"textbox"` la parametrul `inputs`, puteți transmite o componentă `Textbox(lines=7, label="Prompt")` pentru a crea o cutie de text cu 7 linii și o etichetă.

Să aruncăm o privire la un alt exemplu, de data aceasta cu o componentă `Audio`.

## Un exemplu simplu cu audio[[a-simple-example-with-audio]]

Așa cum am menționat mai devreme, Gradio oferă multe intrări și ieșiri diferite.
Deci să construim o `Interface` care funcționează cu audio.

În acest exemplu, vom construi o funcție audio-la-audio care primește un
fișier audio și pur și simplu îl inversează.

Vom folosi pentru intrare componenta `Audio`. Când folosiți componenta `Audio`,
puteți specifica dacă doriți ca `source`-ul audio să fie un fișier pe care utilizatorul
îl încarcă sau un microfon cu care utilizatorul își înregistrează vocea. În acest caz, să
o setăm la `"microphone"`. Doar pentru distracție, vom adăuga o etichetă la `Audio`-ul nostru care spune
"Vorbiți aici...".

În plus, am dori să primim audio-ul ca un array numpy pentru a putea cu ușurință
să îl "inversăm". Deci vom seta `"type"`-ul să fie `"numpy"`, care transmite datele de intrare
ca un tuplu de (`sample_rate`, `data`) în funcția noastră.

Vom folosi și componenta de ieșire `Audio` care poate reda automat
un tuplu cu o rată de eșantionare și un array numpy de date ca fișier audio redabil.
În acest caz, nu avem nevoie să facem nicio personalizare, deci vom folosi scurtătura string
`"audio"`.


```py
import numpy as np
import gradio as gr


def reverse_audio(audio):
    sr, data = audio
    reversed_audio = (sr, np.flipud(data))
    return reversed_audio


mic = gr.Audio(source="microphone", type="numpy", label="Vorbiți aici...")
gr.Interface(reverse_audio, mic, "audio").launch()
```

Codul de mai sus va produce o interfață ca cea de mai jos (dacă browserul dvs. nu
vă cere permisiuni pentru microfon, <a href="https://huggingface.co/spaces/course-demos/audio-reverse" target="_blank">deschideți demo-ul într-o filă separată</a>.)

<iframe src="https://course-demos-audio-reverse.hf.space" frameBorder="0" height="250" title="Gradio app" class="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>

Acum ar trebui să puteți să vă înregistrați vocea și să vă auziți vorbind în sens invers - înfricoșător 👻!

## Gestionarea intrărilor și ieșirilor multiple[[handling-multiple-inputs-and-outputs]]

Să spunem că avem o funcție mai complicată, cu intrări și ieșiri multiple.
În exemplul de mai jos, avem o funcție care primește un index dropdown, o valoare slider și un număr,
și returnează un eșantion audio al unui ton muzical.

Priviți cum transmitem o listă de componente de intrare și ieșire,
și vedeți dacă puteți urmări ce se întâmplă.

Cheia aici este că atunci când transmiteți:
* o listă de componente de intrare, fiecare componentă corespunde unui parametru în ordine.
* o listă de componente de ieșire, fiecare componentă corespunde unei valori returnate.

Fragmentul de cod de mai jos arată cum trei componente de intrare se aliniază cu cei trei argumente ai funcției `generate_tone()`:

```py
import numpy as np
import gradio as gr

notes = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]


def generate_tone(note, octave, duration):
    sr = 48000
    a4_freq, tones_from_a4 = 440, 12 * (octave - 4) + (note - 9)
    frequency = a4_freq * 2 ** (tones_from_a4 / 12)
    duration = int(duration)
    audio = np.linspace(0, duration, duration * sr)
    audio = (20000 * np.sin(audio * (2 * np.pi * frequency))).astype(np.int16)
    return (sr, audio)


gr.Interface(
    generate_tone,
    [
        gr.Dropdown(notes, type="index"),
        gr.Slider(minimum=4, maximum=6, step=1),
        gr.Number(value=1, label="Durata în secunde"),
    ],
    "audio",
).launch()
```

<iframe src="https://course-demos-generate-tone.hf.space" frameBorder="0" height="450" title="Gradio app" class="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>


### Metoda `launch()`[[the-launch-method]]

Până acum, am folosit metoda `launch()` pentru a lansa interfața, dar nu
am discutat cu adevărat ce face.

În mod implicit, metoda `launch()` va lansa demo-ul într-un server web care
rulează local. Dacă rulați codul într-un notebook Jupyter sau Colab, atunci
Gradio va incorpora interfața GUI demo în notebook pentru a o putea folosi cu ușurință.

Puteți personaliza comportamentul `launch()` prin parametri diferiți:

  - `inline` - dacă să afișeze interfața inline în notebook-urile Python.
  - `inbrowser` - dacă să lanseze automat interfața într-o filă nouă în browserul implicit.
  - `share` - dacă să creeze un link partajabil public de pe calculatorul dvs. pentru interfață. Cam ca un link Google Drive!

Vom acoperi parametrul `share` în mult mai multe detalii în următoarea secțiune!

## ✏️ Să aplicăm![[lets-apply-it]]

Să construim o interfață care vă permite să demonstrați un model de **recunoaștere vocală**.
Pentru a o face interesantă, vom accepta *fie* o intrare de microfon, fie un fișier încărcat.

Ca de obicei, vom încărca modelul nostru de recunoaștere vocală folosind funcția `pipeline()` din 🤗 Transformers.
Dacă aveți nevoie de o reîmprospătare rapidă, puteți să vă întoarceți la [acea secțiune din Capitolul 1](/course/chapter1/3). Următorul, vom implementa o funcție `transcribe_audio()` care procesează audio-ul și returnează transcrierea. În final, vom încapsula această funcție într-o `Interface` cu componentele `Audio` pentru intrări și doar text pentru ieșire. În ansamblu, codul pentru această aplicație este următorul:

```py
from transformers import pipeline
import gradio as gr

model = pipeline("automatic-speech-recognition")


def transcribe_audio(audio):
    transcription = model(audio)["text"]
    return transcription


gr.Interface(
    fn=transcribe_audio,
    inputs=gr.Audio(type="filepath"),
    outputs="text",
).launch()
```

Dacă browserul dvs. nu vă cere permisiuni pentru microfon, <a href="https://huggingface.co/spaces/course-demos/audio-reverse" target="_blank">deschideți demo-ul într-o filă separată</a>.

<iframe src="https://course-demos-asr.hf.space" frameBorder="0" height="550" title="Gradio app" class="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>


Asta e! Acum puteți folosi această interfață pentru a transcrie audio. Observați aici că
prin transmiterea parametrului `optional` ca `True`, permitem utilizatorului să ofere fie
un microfon, fie un fișier audio (sau niciunul, dar asta va returna un mesaj de eroare).

Continuați să vedeți cum să vă partajați interfața cu alții! 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter9/3.mdx" />

### Caracteristici avansate ale Interface[[advanced-interface-features]]
https://huggingface.co/learn/course/ro/chapter9/6.md

# Caracteristici avansate ale Interface[[advanced-interface-features]]

<CourseFloatingBanner chapter={9}
  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/chapter9/section6.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter9/section6.ipynb"},
]} />

Acum că putem construi și partaja o interfață de bază, să explorăm câteva caracteristici mai avansate precum starea și interpretarea.

### Folosirea stării pentru a persista datele[[using-state-to-persist-data]]

Gradio are suport pentru *starea sesiunii*, unde datele persistă prin multiple trimiteri într-o
încărcare de pagină. Starea sesiunii este utilă pentru construirea demo-urilor, de exemplu, chatbot-uri unde doriți să
persistați datele pe măsură ce utilizatorul interacționează cu modelul. Rețineți că starea sesiunii nu partajează datele între diferiți utilizatori ai modelului dvs.

Pentru a stoca date într-o stare de sesiune, trebuie să faceți trei lucruri:

1. Transmiteți un *parametru suplimentar* în funcția dvs., care reprezintă starea interfeței.
1. La sfârșitul funcției, returnați valoarea actualizată a stării ca o *valoare de returnare suplimentară*.
1. Adăugați componentele de intrare 'state' și ieșire 'state' când creați `Interface`-ul dvs.

Vedeți exemplul de chatbot de mai jos:

```py
import random

import gradio as gr


def chat(message, history):
    history = history or []
    if message.startswith("How many"):
        response = random.randint(1, 10)
    elif message.startswith("How"):
        response = random.choice(["Great", "Good", "Okay", "Bad"])
    elif message.startswith("Where"):
        response = random.choice(["Here", "There", "Somewhere"])
    else:
        response = "I don't know"
    history.append((message, response))
    return history, history


iface = gr.Interface(
    chat,
    ["text", "state"],
    ["chatbot", "state"],
    allow_screenshot=False,
    allow_flagging="never",
)
iface.launch()
```

<iframe src="https://course-demos-Chatbot-Demo.hf.space" frameBorder="0" height="350" title="Gradio app" class="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>

Observați cum starea componentei de ieșire persistă prin trimiteri.
Notă: puteți transmite o valoare implicită la parametrul state,
care este folosită ca valoarea inițială a stării.

### Folosirea interpretării pentru a înțelege predicțiile[[using-interpretation-to-understand-predictions]]

Majoritatea modelelor de machine learning sunt cutii negre și logica internă a funcției este ascunsă de utilizatorul final. Pentru a încuraja transparența, am făcut foarte ușor să adăugați interpretare la modelul dvs. prin simpla setare a cuvântului cheie interpretation în clasa Interface la default. Aceasta permite utilizatorilor dvs. să înțeleagă ce părți ale intrării sunt responsabile pentru ieșire. Aruncați o privire la interfața simplă de mai jos care arată un clasificator de imagini care include și interpretare:

```py
import requests
import tensorflow as tf

import gradio as gr

inception_net = tf.keras.applications.MobileNetV2()  # load the model

# Download human-readable labels for ImageNet.
response = requests.get("https://git.io/JJkYN")
labels = response.text.split("\n")


def classify_image(inp):
    inp = inp.reshape((-1, 224, 224, 3))
    inp = tf.keras.applications.mobilenet_v2.preprocess_input(inp)
    prediction = inception_net.predict(inp).flatten()
    return {labels[i]: float(prediction[i]) for i in range(1000)}


image = gr.Image(shape=(224, 224))
label = gr.Label(num_top_classes=3)

title = "Gradio Image Classifiction + Interpretation Example"
gr.Interface(
    fn=classify_image, inputs=image, outputs=label, interpretation="default", title=title
).launch()
```

Testați funcția de interpretare prin trimiterea unei intrări apoi făcând clic pe Interpret sub componenta de ieșire.

<iframe src="https://course-demos-gradio-image-interpretation.hf.space" frameBorder="0" height="570" title="Gradio app" class="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>

Pe lângă metoda de interpretare implicită pe care o oferă Gradio, puteți specifica și `shap` pentru parametrul `interpretation` și să setați parametrul `num_shap`. Aceasta folosește interpretarea bazată pe Shapley, despre care puteți citi mai multe [aici](https://christophm.github.io/interpretable-ml-book/shap.html).
În final, puteți transmite și propria funcție de interpretare în parametrul `interpretation`. Vedeți un exemplu în pagina de început a Gradio [aici](https://gradio.app/getting_started/).

Aceasta încheie explorarea noastră profundă a clasei `Interface` din Gradio. Așa cum am văzut, această clasă face simplu să creați demo-uri de machine learning în câteva linii de cod Python. Cu toate acestea, uneori veți dori să personalizați demo-ul prin schimbarea layout-ului sau înlănțuirea mai multor funcții de predicție împreună. Nu ar fi frumos dacă am putea cumva să împărțim `Interface`-ul în "blocuri" personalizabile? Din fericire, se poate! Aceasta este tema secțiunii finale. 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter9/6.mdx" />

### Gradio, verificat![[gradio-check]]
https://huggingface.co/learn/course/ro/chapter9/8.md

# Gradio, verificat![[gradio-check]]

<CourseFloatingBanner
    chapter={9}
    classNames="absolute z-10 right-0 top-0"
/>

Aceasta încheie capitolul despre construirea demo-urilor cool ML cu Gradio - sperăm că v-a plăcut! Pentru a recapitula, în acest capitol am învățat:

- Cum să creați demo-uri Gradio cu API-ul de nivel înalt `Interface`, și cum să configurați diferite modalități de intrare și ieșire.
- Diferite moduri de a partaja demo-urile Gradio, prin link-uri temporare și găzduire pe [Hugging Face Spaces](https://huggingface.co/spaces).
- Cum să integrați demo-urile Gradio cu modelele și Spaces de pe Hugging Face Hub.
- Caracteristici avansate precum păstrarea stării într-un demo sau furnizarea autentificării.
- Cum să aveți control complet asupra fluxului de date și layout-ului demo-ului dvs. cu Gradio Blocks.

Dacă doriți să vă testați înțelegerea conceptelor acoperite în acest capitol, consultați quiz-ul din următoarea secțiune!

## Unde să mergeți mai departe?[[where-to-next]]

Dacă doriți să aflați mai multe despre Gradio, puteți

- Să aruncați o privire la [Demo-uri](https://github.com/gradio-app/gradio/tree/main/demo) în repo, sunt destul de multe exemple acolo.
- Să vedeți pagina [Ghiduri](https://gradio.app/guides/), unde puteți găsi ghiduri despre caracteristici cool și avansate.
- Să consultați pagina [Documentație](https://gradio.app/docs/) pentru a învăța detaliile. 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter9/8.mdx" />

### Partajarea demo-urilor cu alții[[sharing-demos-with-others]]
https://huggingface.co/learn/course/ro/chapter9/4.md

# Partajarea demo-urilor cu alții[[sharing-demos-with-others]]

<CourseFloatingBanner chapter={9}
  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/chapter9/section4.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter9/section4.ipynb"},
]} />

Acum că ați construit un demo, probabil doriți să îl partajați cu alții. Demo-urile Gradio
pot fi partajate în două moduri: folosind un ***link de partajare temporar*** sau ***găzduire permanentă pe Spaces***.

Vom acoperi ambele abordări în curând. Dar înainte să vă partajați demo-ul, este posibil să doriți să îl înfrumusețați 💅.

### Înfrumusețarea demo-ului Gradio:[[polishing-your-gradio-demo]]

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter9/gradio-demo-overview.png" alt="Overview of a gradio interface">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter9/gradio-demo-overview-dark.png" alt="Overview of a gradio interface">
</div>

Pentru a adăuga conținut suplimentar la demo-ul dvs., clasa `Interface` suportă câțiva parametri opționali:
    - `title`: puteți da un titlu demo-ului dvs., care apare _deasupra_ componentelor de intrare și ieșire.
    - `description`: puteți da o descriere (în text, Markdown sau HTML) pentru interfață, care apare deasupra componentelor de intrare și ieșire și sub titlu.
    - `article`: puteți scrie și un articol extins (în text, Markdown sau HTML) explicând interfața. Dacă este furnizat, apare _sub_ componentele de intrare și ieșire.
    - `theme`: nu vă plac culorile implicite? Setați tema să folosească una dintre `default`, `huggingface`, `grass`, `peach`. Puteți adăuga și prefixul `dark-`, de ex. `dark-peach` pentru tema întunecată (sau doar `dark` pentru tema întunecată implicită).
    - `examples`: pentru a face demo-ul *mult mai ușor de folosit*, puteți furniza exemple de intrări pentru funcție. Acestea apar sub componentele UI și pot fi folosite pentru a popula interfața. Acestea ar trebui furnizate ca o listă imbricată, în care lista exterioară constă din exemple și fiecare listă interioară constă dintr-o intrare corespunzătoare fiecărei componente de intrare.
    - `live`: dacă doriți să faceți demo-ul "live", adică modelul să ruleze din nou de fiecare dată când intrarea se schimbă, puteți seta `live=True`. Acest lucru are sens să se folosească cu modele rapide (vom vedea un exemplu la sfârșitul acestei secțiuni)
Folosind opțiunile de mai sus, ajungem la o interfață mai completă. Rulați codul de mai jos pentru a putea conversa cu Rick și Morty:

```py
title = "Ask Rick a Question"
description = """
The bot was trained to answer questions based on Rick and Morty dialogues. Ask Rick anything!
<img src="https://huggingface.co/spaces/course-demos/Rick_and_Morty_QA/resolve/main/rick.png" width=200px>
"""

article = "Check out [the original Rick and Morty Bot](https://huggingface.co/spaces/kingabzpro/Rick_and_Morty_Bot) that this demo is based off of."

gr.Interface(
    fn=predict,
    inputs="textbox",
    outputs="text",
    title=title,
    description=description,
    article=article,
    examples=[["What are you doing?"], ["Where should we time travel to?"]],
).launch()
```

Folosind opțiunile de mai sus, ajungem la o interfață mai completă. Încercați interfața de mai jos:

<iframe src="https://course-demos-Rick-and-Morty-QA.hf.space" frameBorder="0" height="800" title="Gradio app" class="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>

### Partajarea demo-ului cu link-uri temporare[[sharing-your-demo-with-temporary-links]]
Acum că avem un demo funcțional al modelului nostru de machine learning, să învățăm cum să partajăm cu ușurință un link către interfața noastră.
Interfețele pot fi partajate public cu ușurință prin setarea `share=True` în metoda `launch()`:

```python
gr.Interface(classify_image, "image", "label").launch(share=True)
```

Aceasta generează un link public, partajabil pe care îl puteți trimite oricui! Când trimiteți acest link, utilizatorul din cealaltă parte poate încerca modelul în browserul lor timp de până la 72 de ore. Deoarece procesarea se întâmplă pe dispozitivul dvs. (atâta timp cât dispozitivul dvs. rămâne pornit!), nu trebuie să vă faceți griji cu privire la împachetarea dependențelor. Dacă lucrați dintr-un notebook Google Colab, un link de partajare este întotdeauna creat automat. De obicei arată cam așa: **XXXXX.gradio.app**. Deși link-ul este servit printr-un link Gradio, suntem doar un proxy pentru serverul dvs. local și nu stocăm datele trimise prin interfețe.

Rețineți totuși că aceste link-uri sunt accesibile public, ceea ce înseamnă că oricine poate folosi modelul dvs. pentru predicție! Prin urmare, asigurați-vă să nu expuneți informații sensibile prin funcțiile pe care le scrieți sau să permiteți modificări critice pe dispozitivul dvs. Dacă setați `share=False` (implicit), doar un link local este creat.

### Găzduirea demo-ului pe Hugging Face Spaces[[hosting-your-demo-on-hugging-face-spaces]]

Un link de partajare pe care îl puteți transmite colegilor este cool, dar cum puteți găzdui permanent demo-ul și să îl faceți să existe în propriul său "spațiu" pe internet?

Hugging Face Spaces oferă infrastructura pentru a găzdui permanent modelul dvs. Gradio pe internet, **gratuit**! Spaces vă permite să creați și să împingeți într-un repo (public sau privat),
unde codul interfața dvs. Gradio
va exista într-un fișier `app.py`. [Citiți un tutorial pas cu pas](https://huggingface.co/blog/gradio-spaces) pentru a începe, sau urmăriți un videoclip exemplu mai jos.

<Youtube id="LS9Y2wDVI0k" />

## ✏️ Să aplicăm![[lets-apply-it]]

Folosind ceea ce tocmai am învățat în secțiunile de până acum, să creăm demo-ul de recunoaștere a schițelor pe care l-am văzut în [secțiunea unu din acest capitol](/course/chapter9/1). Să adăugăm câteva personalizări la interfața noastră și să setăm `share=True` pentru a crea un link public pe care îl putem transmite.

Putem încărca etichetele din [class_names.txt](https://huggingface.co/spaces/dawood/Sketch-Recognition/blob/main/class_names.txt) și să încărcăm modelul pytorch pre-antrenat din [pytorch_model.bin](https://huggingface.co/spaces/dawood/Sketch-Recognition/blob/main/pytorch_model.bin). Descărcați aceste fișiere urmărind link-ul și făcând clic pe download în colțul din stânga sus al previzualizării fișierului. Să aruncăm o privire la codul de mai jos pentru a vedea cum folosim aceste fișiere pentru a încărca modelul nostru și a crea o funcție `predict()`:
```py
from pathlib import Path
import torch
import gradio as gr
from torch import nn

LABELS = Path("class_names.txt").read_text().splitlines()

model = nn.Sequential(
    nn.Conv2d(1, 32, 3, padding="same"),
    nn.ReLU(),
    nn.MaxPool2d(2),
    nn.Conv2d(32, 64, 3, padding="same"),
    nn.ReLU(),
    nn.MaxPool2d(2),
    nn.Conv2d(64, 128, 3, padding="same"),
    nn.ReLU(),
    nn.MaxPool2d(2),
    nn.Flatten(),
    nn.Linear(1152, 256),
    nn.ReLU(),
    nn.Linear(256, len(LABELS)),
)
state_dict = torch.load("pytorch_model.bin", map_location="cpu")
model.load_state_dict(state_dict, strict=False)
model.eval()


def predict(im):
    x = torch.tensor(im, dtype=torch.float32).unsqueeze(0).unsqueeze(0) / 255.0
    with torch.no_grad():
        out = model(x)
    probabilities = torch.nn.functional.softmax(out[0], dim=0)
    values, indices = torch.topk(probabilities, 5)
    return {LABELS[i]: v.item() for i, v in zip(indices, values)}
```

Acum că avem o funcție `predict()`. Următorul pas este să definim și să lansăm interfața noastră gradio:

```py
interface = gr.Interface(
    predict,
    inputs="sketchpad",
    outputs="label",
    theme="huggingface",
    title="Sketch Recognition",
    description="Who wants to play Pictionary? Draw a common object like a shovel or a laptop, and the algorithm will guess in real time!",
    article="<p style='text-align: center'>Sketch Recognition | Demo Model</p>",
    live=True,
)
interface.launch(share=True)
```

<iframe src="https://course-demos-Sketch-Recognition.hf.space" frameBorder="0" height="650" title="Gradio app" class="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>


Observați parametrul `live=True` în `Interface`, care înseamnă că demo-ul cu schițe face
o predicție de fiecare dată când cineva desenează pe sketchpad (fără buton de submit!).

În plus, am setat și argumentul `share=True` în metoda `launch()`.
Aceasta va crea un link public pe care îl puteți
trimite oricui! Când trimiteți acest link, utilizatorul din cealaltă parte poate încerca
modelul de recunoaștere a schițelor. Pentru a reitera, ați putea găzdui și modelul pe Hugging Face Spaces,
care este modul în care reușim să încorporăm demo-ul de mai sus.

Următorul, vom acoperi alte moduri în care Gradio poate fi folosit cu ecosistemul Hugging Face! 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter9/4.mdx" />

### Introducere în Gradio Blocks[[introduction-to-gradio-blocks]]
https://huggingface.co/learn/course/ro/chapter9/7.md

# Introducere în Gradio Blocks[[introduction-to-gradio-blocks]]

<CourseFloatingBanner chapter={9}
  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/chapter9/section7.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter9/section7.ipynb"},
]} />

În secțiunile anterioare am explorat și am creat demo-uri folosind clasa `Interface`. În această secțiune vom introduce API-ul nostru de nivel scăzut **recent dezvoltat** numit `gradio.Blocks`.

Acum, care este diferența între `Interface` și `Blocks`?

- ⚡ `Interface`: un API de nivel înalt care vă permite să creați un demo complet de machine learning simplu prin furnizarea unei liste de intrări și ieșiri.

- 🧱 `Blocks`: un API de nivel scăzut care vă permite să aveți control complet asupra fluxurilor de date și layout-ului aplicației dvs. Puteți construi aplicații foarte complexe, cu mai mulți pași folosind `Blocks` (ca în "piese de construcție").


### De ce Blocks 🧱?[[why-blocks-]]

Așa cum am văzut în secțiunile anterioare, clasa `Interface` vă permite să creați cu ușurință demo-uri complete de machine learning cu doar câteva linii de cod. API-ul `Interface` este extrem de ușor de folosit, dar îi lipsește flexibilitatea pe care o oferă API-ul `Blocks`. De exemplu, ați putea dori să:

- Grupați demo-uri înrudite ca file multiple într-o aplicație web
- Schimbați layout-ul demo-ului dvs., de ex. să specificați unde sunt localizate intrările și ieșirile
- Aveți interfețe cu mai mulți pași, în care ieșirea unui model devine intrarea următorului model, sau să aveți fluxuri de date mai flexibile în general
- Schimbați proprietățile unei componente (de exemplu, opțiunile dintr-un dropdown) sau vizibilitatea sa pe baza intrării utilizatorului

Vom explora toate aceste concepte mai jos.

### Crearea unui demo simplu folosind Blocks[[creating-a-simple-demo-using-blocks]]

După ce ați instalat Gradio, rulați codul de mai jos ca script Python, notebook Jupyter sau notebook Colab.

```py
import gradio as gr


def flip_text(x):
    return x[::-1]


demo = gr.Blocks()

with demo:
    gr.Markdown(
        """
    # Flip Text!
    Start typing below to see the output.
    """
    )
    input = gr.Textbox(placeholder="Flip this text")
    output = gr.Textbox()

    input.change(fn=flip_text, inputs=input, outputs=output)

demo.launch()
```

<iframe src="https://course-demos-flip-text.hf.space" frameBorder="0" height="400" title="Gradio app" class="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>

Acest exemplu simplu de mai sus introduce 4 concepte care stau la baza Blocks:

1. Blocks vă permite să construiți aplicații web care combină markdown, HTML, butoane și componente interactive simplu prin instanțierea obiectelor în Python într-un context `with gradio.Blocks`.
<Tip>
🙋Dacă nu sunteți familiarizați cu declarația `with` în Python, vă recomandăm să consultați excelentul [tutorial](https://realpython.com/python-with-statement/) de la Real Python. Întoarceți-vă aici după citirea acestuia 🤗
</Tip>
Ordinea în care instanțiați componentele contează deoarece fiecare element este redat în aplicația web în ordinea în care a fost creat. (Layout-uri mai complexe sunt discutate mai jos)

2. Puteți defini funcții Python obișnuite oriunde în codul dvs. și să le rulați cu intrări de la utilizator folosind `Blocks`. În exemplul nostru, avem o funcție simplă care "inversează" textul de intrare, dar puteți scrie orice funcție Python, de la un calcul simplu la procesarea predicțiilor dintr-un model de machine learning.

3. Puteți atribui evenimente oricărei componente `Blocks`. Aceasta va rula funcția dvs. când componenta este apăsată, schimbată, etc. Când atribuiți un eveniment, transmiteți trei parametri: `fn`: funcția care ar trebui apelată, `inputs`: (lista) componentei(lor) de intrare, și `outputs`: (lista) componentelor de ieșire care ar trebui apelate.

   În exemplul de mai sus, rulăm funcția `flip_text()` când valoarea din `Textbox`-ul numit intrare `input` se schimbă. Evenimentul citește valoarea din `input`, o transmite ca parametru de nume la `flip_text()`, care apoi returnează o valoare care este atribuită celui de-al doilea `Textbox` numit `output`.

   Pentru a vedea o listă a evenimentelor pe care le suportă fiecare componentă, consultați [documentația](https://www.gradio.app/docs/) Gradio.

4. Blocks determină automat dacă o componentă ar trebui să fie interactivă (să accepte intrări de la utilizator) sau nu, pe baza declanșatorilor de evenimente pe care îi definiți. În exemplul nostru, prima cutie de text este interactivă, deoarece valoarea sa este folosită de funcția `flip_text()`. A doua cutie de text nu este interactivă, deoarece valoarea sa nu este niciodată folosită ca intrare. În unele cazuri, ați putea dori să suprascriați acest lucru, ceea ce puteți face prin transmiterea unui boolean la parametrul `interactive` al componentei (de ex. `gr.Textbox(placeholder="Flip this text", interactive=True)`).

### Personalizarea layout-ului demo-ului dvs.[[customizing-the-layout-of-your-demo]]

Cum putem folosi `Blocks` pentru a personaliza layout-ul demo-ului nostru? În mod implicit, `Blocks` redă componentele pe care le creați vertical într-o coloană. Puteți schimba asta prin crearea de coloane suplimentare `with gradio.Column():` sau rânduri `with gradio.Row():` și crearea componentelor în acele contexte.

Iată ce ar trebui să țineți minte: orice componente create sub o `Column` (aceasta este și setarea implicită) vor fi aranjate vertical. Orice componentă creată sub un `Row` va fi aranjată orizontal, similar cu [modelul flexbox în dezvoltarea web](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox).

În final, puteți crea și file pentru demo-ul dvs. folosind managerul de context `with gradio.Tabs()`. În acest context, puteți crea file multiple prin specificarea `with gradio.TabItem(name_of_tab):` copii. Orice componentă creată în interiorul unui context `with gradio.TabItem(name_of_tab):` apare în acea filă.

Acum să adăugăm o funcție `flip_image()` la demo-ul nostru și să adăugăm o filă nouă care inversează imaginile. Mai jos este un exemplu cu 2 file și folosește și un Row:

```py
import numpy as np
import gradio as gr

demo = gr.Blocks()


def flip_text(x):
    return x[::-1]


def flip_image(x):
    return np.fliplr(x)


with demo:
    gr.Markdown("Flip text or image files using this demo.")
    with gr.Tabs():
        with gr.TabItem("Flip Text"):
            with gr.Row():
                text_input = gr.Textbox()
                text_output = gr.Textbox()
            text_button = gr.Button("Flip")
        with gr.TabItem("Flip Image"):
            with gr.Row():
                image_input = gr.Image()
                image_output = gr.Image()
            image_button = gr.Button("Flip")

    text_button.click(flip_text, inputs=text_input, outputs=text_output)
    image_button.click(flip_image, inputs=image_input, outputs=image_output)

demo.launch()
```

<iframe src="https://course-demos-flip-text-image.hf.space" frameBorder="0" height="450" title="Gradio app" class="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>


Veți observa că în acest exemplu, am creat și o componentă `Button` în fiecare filă, și am atribuit un eveniment de clic fiecărui buton, care este ceea ce rulează de fapt funcția.

### Explorarea evenimentelor și stării[[exploring-events-and-state]]

La fel cum puteți controla layout-ul, `Blocks` vă oferă control fin asupra evenimentelor care declanșează apelurile de funcții. Fiecare componentă și multe layout-uri au evenimente specifice pe care le suportă.

De exemplu, componenta `Textbox` are 2 evenimente: `change()` (când valoarea din interiorul cutiei de text se schimbă), și `submit()` (când un utilizator apasă tasta enter în timp ce este focalizat pe cutia de text). Componentele mai complexe pot avea și mai multe evenimente: de exemplu, componenta `Audio` are și evenimente separate pentru când fișierul audio este redat, șters, întrerupt, etc. Consultați documentația pentru evenimentele pe care le suportă fiecare componentă.

Puteți atașa declanșatorul de eveniment la niciunul, unul sau mai multe dintre aceste evenimente. Creați un declanșator de eveniment prin apelarea numelui evenimentului pe instanța componentei ca funcție -- de ex. `textbox.change(...)` sau `btn.click(...)`. Funcția primește trei parametri, așa cum s-a discutat mai sus:

- `fn`: funcția care să ruleze
- `inputs`: o (listă de) componentă(e) ale căror valori ar trebui furnizate ca parametri de intrare la funcție. Valoarea fiecărei componente este mapată la parametrul funcției corespunzător, în ordine. Acest parametru poate fi None dacă funcția nu primește parametri.
- `outputs`: o (listă de) componentă(e) ale căror valori ar trebui actualizate pe baza valorilor returnate de funcție. Fiecare valoare returnată setează valoarea componentei corespunzătoare, în ordine. Acest parametru poate fi None dacă funcția nu returnează nimic.

Puteți face chiar ca componenta de intrare și ieșire să fie aceeași componentă, așa cum facem în acest exemplu care folosește un model GPT pentru completarea textului:

```py
import gradio as gr

api = gr.Interface.load("huggingface/EleutherAI/gpt-j-6B")


def complete_with_gpt(text):
    # Use the last 50 characters of the text as context
    return text[:-50] + api(text[-50:])


with gr.Blocks() as demo:
    textbox = gr.Textbox(placeholder="Type here and press enter...", lines=4)
    btn = gr.Button("Generate")

    btn.click(complete_with_gpt, textbox, textbox)

demo.launch()
```

<iframe src="https://course-demos-blocks-gpt.hf.space" frameBorder="0" height="300" title="Gradio app" class="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>

### Crearea demo-urilor cu mai mulți pași[[creating-multi-step-demos]]

În unele cazuri, ați putea dori un _demo cu mai mulți pași_, în care reutilizați ieșirea unei funcții ca intrare la următoarea. Acest lucru este foarte ușor de făcut cu `Blocks`, deoarece puteți folosi o componentă pentru intrarea unei declanșări de eveniment, dar ieșirea alteia. Priviți componenta text din exemplul de mai jos, valoarea sa este rezultatul unui model speech-to-text, dar este transmisă și într-un model de analiză de sentiment:

```py
from transformers import pipeline

import gradio as gr

asr = pipeline("automatic-speech-recognition", "facebook/wav2vec2-base-960h")
classifier = pipeline("text-classification")


def speech_to_text(speech):
    text = asr(speech)["text"]
    return text


def text_to_sentiment(text):
    return classifier(text)[0]["label"]


demo = gr.Blocks()

with demo:
    audio_file = gr.Audio(type="filepath")
    text = gr.Textbox()
    label = gr.Label()

    b1 = gr.Button("Recognize Speech")
    b2 = gr.Button("Classify Sentiment")

    b1.click(speech_to_text, inputs=audio_file, outputs=text)
    b2.click(text_to_sentiment, inputs=text, outputs=label)

demo.launch()
```

<iframe src="https://course-demos-blocks-multi-step.hf.space" frameBorder="0" height="600" title="Gradio app" class="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>

### Actualizarea Proprietăților Componentei[[updating-component-properties]]

Până acum, am văzut cum să creăm evenimente pentru a actualiza valoarea unei alte componente. Dar ce se întâmplă dacă doriți să schimbați alte proprietăți ale unei componente, cum ar fi vizibilitatea unei cutii de text sau opțiunile dintr-un grup de butoane radio? Puteți face acest lucru prin returnarea metodei `update()` a unei clase de componentă în loc de o valoare de returnare obișnuită din funcția dvs.

Acest lucru este cel mai ușor ilustrat cu un exemplu:

```py
import gradio as gr


def change_textbox(choice):
    if choice == "short":
        return gr.Textbox.update(lines=2, visible=True)
    elif choice == "long":
        return gr.Textbox.update(lines=8, visible=True)
    else:
        return gr.Textbox.update(visible=False)


with gr.Blocks() as block:
    radio = gr.Radio(
        ["short", "long", "none"], label="What kind of essay would you like to write?"
    )
    text = gr.Textbox(lines=2, interactive=True)

    radio.change(fn=change_textbox, inputs=radio, outputs=text)
    block.launch()
```

<iframe src="https://course-demos-blocks-update-component-properties.hf.space" frameBorder="0" height="300" title="Gradio app" class="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>

Tocmai am explorat toate conceptele de bază ale `Blocks`! La fel ca în cazul `Interface`-urilor, puteți crea demo-uri cool care pot fi partajate folosind `share=True` în metoda `launch()` sau implementate pe [Hugging Face Spaces](https://huggingface.co/spaces). 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter9/7.mdx" />

### Construirea primului tău demo[[building-your-first-demo]]
https://huggingface.co/learn/course/ro/chapter9/2.md

# Construirea primului tău demo[[building-your-first-demo]]

<CourseFloatingBanner chapter={9}
  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/chapter9/section2.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter9/section2.ipynb"},
]} />

Să începem prin instalarea Gradio! Întrucât este un pachet Python, pur și simplu rulează:

`$ pip install gradio `

Puteți rula Gradio oriunde, fie din IDE-ul tău Python preferat, în notebook-urile Jupyter sau chiar în Google Colab 🤯!
Deci instalați Gradio oriunde rulați Python!

Să începem cu un exemplu simplu "Hello World" pentru a ne familiariza cu sintaxa Gradio:

```py
import gradio as gr


def greet(name):
    return "Hello " + name


demo = gr.Interface(fn=greet, inputs="text", outputs="text")

demo.launch()
```

Să parcurgem codul de mai sus:

- Mai întâi, definim o funcție numită `greet()`. În acest caz, este o funcție simplă care adaugă "Hello" înaintea numelui tău, dar poate fi *orice* funcție Python în general. De exemplu, în aplicațiile de machine learning, această funcție ar *apela un model pentru a face o predicție* pe o intrare și ar returna ieșirea.
- Apoi, creăm o `Interface` Gradio cu trei argumente, `fn`, `inputs`, și `outputs`. Aceste argumente definesc funcția de predicție, precum și _tipul_ componentelor de intrare și ieșire pe care le-am dori. În cazul nostru, ambele componente sunt cutii de text simple.
- Apoi apelăm metoda `launch()` pe `Interface`-ul pe care l-am creat.

Dacă rulați acest cod, interfața de mai jos va apărea automat într-un notebook Jupyter/Colab, sau va apărea într-un browser la **[http://localhost:7860](http://localhost:7860/)** dacă rulează dintr-un script.

<iframe src="https://course-demos-hello-world.hf.space" frameBorder="0" height="250" title="Gradio app" class="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>

Încercați să folosiți această interfață grafică chiar acum cu numele vostru sau cu alte intrări!

Veți observa că în această interfață grafică, Gradio a dedus automat numele parametrului de intrare (`name`)
și l-a aplicat ca etichetă deasupra cutiei de text. Ce se întâmplă dacă doriți să schimbați asta?
Sau dacă doriți să personalizați cutia de text în alt fel? În acest caz, puteți
instanția un obiect de clasă care reprezintă componenta de intrare.

Priviți exemplul de mai jos:

```py
import gradio as gr


def greet(name):
    return "Hello " + name


# Instanțiem clasa Textbox
textbox = gr.Textbox(label="Tastați numele aici:", placeholder="John Doe", lines=2)

gr.Interface(fn=greet, inputs=textbox, outputs="text").launch()
```

<iframe src="https://course-demos-hello-world-custom.hf.space" frameBorder="0" height="300" title="Gradio app" class="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>

Aici, am creat o cutie de text de intrare cu o etichetă, un placeholder și un număr setat de linii.
Ați putea face același lucru pentru cutia de text de ieșire, dar o vom lăsa pentru mai târziu.

Am văzut că cu doar câteva linii de cod, Gradio vă permite să creați o interfață simplă în jurul oricărei funcții
cu orice tip de intrări sau ieșiri. În această secțiune, am început cu o
cutie de text simplă, dar în următoarele secțiuni, vom acoperi alte tipuri de intrări și ieșiri. Să aruncăm acum o privire la includerea unor NLP într-o aplicație Gradio.


## 🤖 Includerea predicțiilor modelului[[including-model-predictions]]

Să construim acum o interfață simplă care vă permite să demonstrați un model de **generare de text** ca GPT-2.

Vom încărca modelul nostru folosind funcția `pipeline()` din 🤗 Transformers.
Dacă aveți nevoie de o reîmprospătare rapidă, puteți să vă întoarceți la [acea secțiune din Capitolul 1](/course/chapter1/3#text-generation).

Primul, definim o funcție de predicție care primește un prompt de text și returnează completarea textului:

```py
from transformers import pipeline

model = pipeline("text-generation")


def predict(prompt):
    completion = model(prompt)[0]["generated_text"]
    return completion
```

Această funcție completează prompt-urile pe care le furnizați, și puteți să o rulați cu propriile prompt-uri de intrare pentru a vedea cum funcționează. Iată un exemplu (este posibil să obțineți o completare diferită):

```
predict("My favorite programming language is")
```

```
>> My favorite programming language is Haskell. I really enjoyed the Haskell language, but it doesn't have all the features that can be applied to any other language. For example, all it does is compile to a byte array.
```

Acum că avem o funcție pentru generarea predicțiilor, putem crea și lansa o `Interface` în același mod în care am făcut mai devreme:

```py
import gradio as gr

gr.Interface(fn=predict, inputs="text", outputs="text").launch()
```


Asta e! Acum puteți folosi această interfață pentru a genera text folosind modelul GPT-2 așa cum se arată mai jos 🤯.

<iframe src="https://course-demos-gpt-2.hf.space" frameBorder="0" height="300" title="Gradio app" class="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>

Continuați să citiți pentru a vedea cum să construiți alte tipuri de demo-uri cu Gradio! 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter9/2.mdx" />

### Sesiuni live și workshop-uri[[sesiuni-live-si-workshop-uri]]
https://huggingface.co/learn/course/ro/events/1.md

# Sesiuni live și workshop-uri[[sesiuni-live-si-workshop-uri]]

Pentru lansarea părților 1 și 2 ale cursului, am organizat mai multe sesiuni de codare live și workshop-uri. Puteți găsi linkuri către înregistrările acestor sesiuni și workshop-uri mai jos.

## Sesiuni de codare live[[sesiuni-de-codare-live]]

Pentru prima sesiune, Sylvain trece prin Capitolul 1 al cursului împreună cu voi, explicându-l pas cu pas:

<div class="flex justify-center">
<Youtube id="aV4wfnIakSQ"/>
</div>

În a doua sesiune, este rândul lui Lewis să prezinte Capitolul 2:

<div class="flex justify-center">
<Youtube id="qEl7eORxpFA"/>
</div>

Pentru că Capitolul 2 este atât de interesant, Sylvain a oferit și el o prezentare pas cu pas!

<div class="flex justify-center">
<Youtube id="u4e8OGWYpPk"/>
</div>

Pentru Capitolul 3, Lewis revine să vă ghideze prin cod:

<div class="flex justify-center">
<Youtube id="Be4s0dsbavM"/>
</div>

În sfârșit, Omar încheie sesiunile live legate de prima parte a cursului abordând capitolul 4:

<div class="flex justify-center">
<Youtube id="1ATVsyBxu1I"/>
</div>

## Workshop-uri[[workshop-uri]]

În primul workshop, Merve îl primește pe Lewis pentru a discuta secțiunea 7 din capitolul 7 despre [question answering]( https://huggingface.co/course/chapter7/7?fw=pt).

<div class="flex justify-center">
<Youtube id="Ihgk8kGLpIE"/>
</div>

Pentru al doilea workshop, Merve îl găzduiește pe Leandro pentru a vorbi despre capitolul 7, secțiunea 6 privind [antrenarea unui model de limbaj cauzal de la zero]( https://huggingface.co/course/chapter7/6?fw=pt) cu o aplicație cu [CodeParrot](https://huggingface.co/codeparrot).

<div class="flex justify-center">
<Youtube id="ExUR7w6xe94"/>
</div> 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/events/1.mdx" />

### Petrecerea Gradio Blocks[[petrecerea-gradio-blocks]]
https://huggingface.co/learn/course/ro/events/3.md

# Petrecerea Gradio Blocks[[petrecerea-gradio-blocks]]

Împreună cu lansarea capitolului Gradio al cursului, Hugging Face a găzduit un eveniment comunitar pentru construirea de demo-uri interesante de machine learning folosind noua funcționalitate Gradio Blocks.

Puteți găsi toate demo-urile pe care le-a creat comunitatea sub organizația [`Gradio-Blocks`](https://huggingface.co/Gradio-Blocks) pe Hub. Iată câteva exemple de la câștigători:

**Limbaj natural către SQL**

<iframe src="https://curranj-words-to-sql.hf.space" frameBorder="0" height="640" title="Aplicația Gradio" class="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> 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/events/3.mdx" />

### Evenimentul de lansare a părții 2[[evenimentul-de-lansare-a-partii-2]]
https://huggingface.co/learn/course/ro/events/2.md

# Evenimentul de lansare a părții 2[[evenimentul-de-lansare-a-partii-2]]

Pentru lansarea părții 2 a cursului, am organizat un eveniment live cu două zile de prezentări înainte de un sprint de fine-tuning. Dacă l-ați ratat, puteți urmări prezentările care sunt toate listate mai jos!

## Ziua 1: O viziune de ansamblu asupra Transformers și cum să îi antrenați[[ziua-1-o-viziune-de-ansamblu-asupra-transformers-si-cum-sa-ii-antrenati]]

**Thomas Wolf:** *Transfer Learning și nașterea bibliotecii Transformers*

<div class="flex justify-center">
<Youtube id="wCYVeahJES0"/>
</div>

<p align="center">
<img src="https://i.imgur.com/9eq8oUi.png" alt="Un rezumat vizual al prezentării lui Thom" width="80%"/>
</p>

Thomas Wolf este co-fondator și Chief Science Officer la Hugging Face. Instrumentele create de Thomas Wolf și echipa Hugging Face sunt folosite în peste 5.000 de organizații de cercetare, inclusiv Facebook Artificial Intelligence Research, Google Research, DeepMind, Amazon Research, Apple, Allen Institute for Artificial Intelligence, precum și majoritatea departamentelor universitare. Thomas Wolf este inițiatorul și președintele principal al celei mai mari colaborări de cercetare care a existat vreodată în Inteligența Artificială: ["BigScience"](https://bigscience.huggingface.co), precum și al unui set de [biblioteci și instrumente](https://github.com/huggingface/) utilizate pe scară largă. Thomas Wolf este, de asemenea, un educator prolific, un lider de opinie în domeniul Inteligenței Artificiale și Procesării Limbajului Natural, și un speaker invitat regulat la conferințe din întreaga lume [https://thomwolf.io](https://thomwolf.io).

**Jay Alammar:** *O introducere vizuală ușoară la modelele Transformers*

<div class="flex justify-center">
<Youtube id="VzvG23gmcYU"/>
</div>

<p align="center">
<img src="https://i.imgur.com/rOZAuE9.png" alt="Un rezumat vizual al prezentării lui Jay" width="80%"/>
</p>

Prin blog-ul său popular de ML, Jay a ajutat milioane de cercetători și ingineri să înțeleagă vizual instrumentele și conceptele de machine learning de la nivel de bază (ajungând în documentația NumPy, Pandas) până la cele de ultimă generație (Transformers, BERT, GPT-3).

**Margaret Mitchell:** *Despre valori în dezvoltarea ML*

<div class="flex justify-center">
<Youtube id="8j9HRMjh_s8"/>
</div>

<p align="center">
<img src="https://i.imgur.com/NuIsnY3.png" alt="Un rezumat vizual al prezentării lui Margaret" width="80%"/>
</p>

Margaret Mitchell este o cercetătoare care lucrează pe Ethical AI, concentrându-se în prezent pe aspectele dezvoltării AI informate de etică în tehnologie. A publicat peste 50 de lucrări pe generarea limbajului natural, tehnologii asistate, computer vision și etica AI, și deține multiple brevete în domeniile generării de conversații și clasificării sentimentelor. Anterior a lucrat la Google AI ca Staff Research Scientist, unde a fondat și co-condus grupul Ethical AI de la Google, concentrat pe cercetarea fundamentală în etica AI și operaționalizarea eticii AI în cadrul Google. Înainte de a se alătura Google, a fost cercetătoare la Microsoft Research, concentrându-se pe generarea de la computer vision la limbaj; și a fost postdoc la Johns Hopkins, concentrându-se pe modelarea Bayesiană și extragerea informațiilor. Deține un doctorat în Computer Science de la Universitatea din Aberdeen și un master în lingvistică computațională de la Universitatea din Washington. În timp ce și-a luat diplomele, a lucrat din 2005-2012 pe machine learning, tulburări neurologice și tehnologii asistate la Oregon Health and Science University. A condus o serie de workshop-uri și inițiative la intersecția diversității, incluziunii, informaticii și eticii. Munca ei a primit premii de la Secretarul de Apărare Ash Carter și American Foundation for the Blind, și a fost implementată de multiple companii de tehnologie. Îi place grădinăritul, câinii și pisicile.

**Matthew Watson și Chen Qian:** *Workflow-uri NLP cu Keras*

<div class="flex justify-center">
<Youtube id="gZIP-_2XYMM"/>
</div>

<p align="center">
<img src="https://i.imgur.com/1vD2az8.png" alt="Un rezumat vizual al prezentării lui Matt și Chen" width="80%"/>
</p>

Matthew Watson este inginer de machine learning în echipa Keras, cu focus pe API-uri de modelare de nivel înalt. A studiat Computer Graphics în timpul licenței și unui master la Stanford University. Aproape o majoră în engleză care s-a îndreptat către informatică, este pasionat de lucrul interdisciplinar și de a face NLP accesibil unei audiențe mai largi.

Chen Qian este inginer software din echipa Keras, cu focus pe API-uri de modelare de nivel înalt. Chen a obținut o diplomă de master în Electrical Engineering de la Stanford University, și este interesat în special de simplificarea implementărilor de cod ale sarcinilor ML și ML-ul la scară largă.

**Mark Saroufim:** *Cum să antrenezi un model cu Pytorch*

<div class="flex justify-center">
<Youtube id="KmvPlW2cbIo"/>
</div>

<p align="center">
<img src="https://i.imgur.com/TPmlkm8.png" alt="Un rezumat vizual al prezentării lui Mark" width="80%"/>
</p>

Mark Saroufim este Partner Engineer la Pytorch lucrând pe instrumente OSS de producție inclusiv TorchServe și Pytorch Enterprise. În viețile sale trecute, Mark a fost Applied Scientist și Product Manager la Graphcore, [yuri.ai](http://yuri.ai/), Microsoft și NASA's JPL. Pasiunea sa principală este să facă programarea mai distractivă.

**Jakob Uszkoreit:** *Nu e stricat, așa că <del>nu-l repara</del> să-l stricăm*

<div class="flex justify-center">
<Youtube id="C6jweXYFHSA"/>
</div>

<p align="center">
<img src="https://i.imgur.com/5dWQeNB.png" alt="Un rezumat vizual al prezentării lui Jakob" width="80%"/>
</p>

Jakob Uszkoreit este co-fondatorul Inceptive. Inceptive proiectează molecule de ARN pentru vaccinuri și terapeutice folosind deep learning la scară largă într-un ciclu strâns cu experimente de randament ridicat cu scopul de a face medicamentele bazate pe RNA mai accesibile, mai eficiente și mai aplicabile pe scară largă. Anterior, Jakob a lucrat la Google pentru mai mult de un deceniu, conducând echipe de cercetare și dezvoltare în Google Brain, Research și Search lucrând pe fundamentele deep learning, computer vision, înțelegerea limbajului și traducerea automată.

## Ziua 2: Instrumentele de folosit[[ziua-2-instrumentele-de-folosit]]

**Lewis Tunstall:** *Antrenament simplu cu 🤗 Transformers Trainer*

<div class="flex justify-center">
<Youtube id="u--UVvH-LIQ"/>
</div>

Lewis este inginer de machine learning la Hugging Face, concentrându-se pe dezvoltarea de instrumente open-source și pe a le face accesibile comunității mai largi. Este, de asemenea, co-autor al cărții O'Reilly [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/). Îl puteți urmări pe Twitter (@_lewtun) pentru sfaturi și trucuri NLP!

**Matthew Carrigan:** *Funcționalități noi TensorFlow pentru 🤗 Transformers și 🤗 Datasets*

<div class="flex justify-center">
<Youtube id="gQUlXp1691w"/>
</div>

Matt este responsabil pentru întreținerea TensorFlow la Transformers, și în cele din urmă va conduce o lovitură de stat împotriva facțiunii PyTorch incumbente, care probabil va fi coordonată prin contul său de Twitter @carrigmat.

**Lysandre Debut:** *Hugging Face Hub ca mijloc de colaborare și partajare a proiectelor de Machine Learning*

<div class="flex justify-center">
<Youtube id="RBw1TmdEZp0"/>
</div>

<p align="center">
<img src="https://i.imgur.com/TarIPCz.png" alt="Un rezumat vizual al prezentării lui Lysandre" width="80%"/>
</p>

Lysandre este Machine Learning Engineer la Hugging Face unde este implicat în multe proiecte open source. Scopul său este să facă Machine Learning accesibil pentru toată lumea prin dezvoltarea de instrumente puternice cu un API foarte simplu.

**Lucile Saulnier:** *Obțineți propriul tokenizer cu 🤗 Transformers & 🤗 Tokenizers*

<div class="flex justify-center">
<Youtube id="UkNmyTFKriI"/>
</div>

Lucile este inginer de machine learning la Hugging Face, dezvoltând și susținând utilizarea instrumentelor open source. Este, de asemenea, implicată activ în multe proiecte de cercetare în domeniul Procesării Limbajului Natural, cum ar fi antrenamentul colaborativ și BigScience.

**Sylvain Gugger:** *Supraîncărcați bucla de antrenament PyTorch cu 🤗 Accelerate*

<div class="flex justify-center">
<Youtube id="t8Krzu-nSeY"/>
</div>

Sylvain este Research Engineer la Hugging Face și unul dintre principalii întreținători ai 🤗 Transformers și dezvoltatorul din spatele 🤗 Accelerate. Îi place să facă antrenamentul modelelor mai accesibil.

**Merve Noyan:** *Prezentați demo-urile modelelor cu 🤗 Spaces*

<div class="flex justify-center">
<Youtube id="vbaKOa4UXoM"/>
</div>

Merve este developer advocate la Hugging Face, lucrând la dezvoltarea de instrumente și construirea de conținut în jurul lor pentru a democratiza machine learning pentru toată lumea.

**Abubakar Abid:** *Construirea aplicațiilor de Machine Learning rapid*

<div class="flex justify-center">
<Youtube id="c7mle2yYpwQ"/>
</div>

<p align="center">
<img src="https://i.imgur.com/qWIFeiF.png" alt="Un rezumat vizual al prezentării lui Abubakar" width="80%"/>
</p>

Abubakar Abid este CEO-ul [Gradio](www.gradio.app). A primit licența în Bachelor of Science în Electrical Engineering și Computer Science de la MIT în 2015, și doctoratul în Applied Machine Learning de la Stanford în 2021. În rolul său ca CEO al Gradio, Abubakar lucrează pentru a face modelele de machine learning mai ușor de demonstrat, depanat și implementat.

**Mathieu Desvé:** *AWS ML Vision: Făcând Machine Learning accesibil pentru toți clienții*

<div class="flex justify-center">
<Youtube id="O2e3pXO4aRE"/>
</div>

<p align="center">
<img src="https://i.imgur.com/oLdZTKy.png" alt="Un rezumat vizual al prezentării lui Mathieu" width="80%"/>
</p>

Entuziast al tehnologiei, maker în timpul liber. Îmi plac provocările și rezolvarea problemelor clienților și utilizatorilor, și lucrul cu oameni talentați pentru a învăța în fiecare zi. Din 2004, lucrez în multiple poziții comutând între frontend, backend, infrastructură, operațiuni și management. Încerc să rezolv probleme tehnice și manageriale comune într-o manieră agilă.

**Philipp Schmid:** *Antrenament gestionat cu Amazon SageMaker și 🤗 Transformers*

<div class="flex justify-center">
<Youtube id="yG6J2Zfo8iw"/>
</div>

Philipp Schmid este Machine Learning Engineer și Tech Lead la Hugging Face, unde conduce colaborarea cu echipa Amazon SageMaker. Este pasionat de democratizarea și punerea în producție a modelelor NLP de ultimă generație și îmbunătățirea ușurinței de utilizare pentru Deep Learning. 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/events/2.mdx" />

### Tokenizeri, verificare![[tokenizers-check]]
https://huggingface.co/learn/course/ro/chapter6/9.md

# Tokenizeri, verificare![[tokenizers-check]]

<CourseFloatingBanner
    chapter={6}
    classNames="absolute z-10 right-0 top-0"
/>

Bună treabă la finalizarea acestui capitol!

După această scufundare adâncă în tokenizers, ar trebui să:

- Să fii capabil să antrenezi un nou tokenizer folosind unul vechi ca model
- Să înțelegi modului de utilizare a offseturilor pentru a face map tokenilor în intervalul lor original de text
- Cunoști diferențele dintre BPE, WordPiece și Unigram
- Fii capabil să combini blocurile furnizate de biblioteca 🤗 Tokenizers pentru a vă construi propriul tokenizer
- Să poți folosi acest tokenizer în cadrul bibliotecii 🤗 Transformers


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter6/9.mdx" />

### Tokenizarea Byte-Pair Encoding[[byte-pair-encoding-tokenization]]
https://huggingface.co/learn/course/ro/chapter6/5.md

# Tokenizarea Byte-Pair Encoding[[byte-pair-encoding-tokenization]]

<CourseFloatingBanner chapter={6}
  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/chapter6/section5.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter6/section5.ipynb"},
]} />

Byte-Pair Encoding (BPE) a fost inițial dezvoltat ca un algoritm de comprimare a textelor și apoi utilizat de OpenAI pentru tokenizare la preantrenarea modelului GPT. Acesta este utilizat de o mulțime de modele Transformers, inclusiv GPT, GPT-2, RoBERTa, BART și DeBERTa.

<Youtube id="HEikzVL-lZU"/>

<Tip>

💡 Această secțiune acoperă BPE în profunzime, mergând până la prezentarea unei implementări complete. Puteți sări la sfârșit dacă doriți doar o prezentare generală a algoritmului de tokenizare.

</Tip>

## Algoritmul de antrenare[[training-algorithm]]

Antrenarea BPE începe prin calcularea setului unic de cuvinte utilizate în corpus (după finalizarea etapelor de normalizare și pre-tokenizare), apoi construirea vocabularului prin preluarea tuturor simbolurilor utilizate pentru scrierea acestor cuvinte. Ca un exemplu foarte simplu, să spunem că corpusul nostru utilizează aceste cinci cuvinte:

```
"hug", "pug", "pun", "bun", "hugs"
```

Vocabularul de bază va fi atunci `["b", "g", "h", "n", "p", "s", "u"]`. Pentru cazurile din lumea reală, vocabularul de bază va conține cel puțin toate caracterele ASCII și, probabil, și unele caractere Unicode. Dacă un exemplu pe care îl tokenizați utilizează un caracter care nu se află în corpusul de antrenare, acel caracter va fi convertit într-un token necunoscut. Acesta este unul dintre motivele pentru care o mulțime de modele NLP sunt foarte proaste la analizarea conținutului cu emoji, de exemplu.

<Tip>

Tokenizerele GPT-2 și RoBERTa (care sunt destul de asemănătoare) au o modalitate inteligentă de a rezolva acest lucru: ele nu privesc cuvintele ca fiind scrise cu caractere Unicode, ci cu bytes. În acest fel, vocabularul de bază are o dimensiune mică (256), dar fiecare caracter la care vă puteți gândi va fi inclus și nu va ajunge să fie convertit într-un token necunoscut. Acest truc se numește *byte-level BPE*.

</Tip>

După obținerea acestui vocabular de bază, adăugăm noi tokeni până când se atinge dimensiunea dorită a vocabularului prin învățarea prin *merges*, care sunt reguli de merge a două elemente ale vocabularului existent într-unul nou. Astfel, la început, aceste fuziuni vor crea tokenuri cu două caractere, iar apoi, pe măsură ce antrenamentul progresează, subwords mai lungi.

În orice etapă din timpul antrenării tokenizerului, algoritmul BPE va căuta cea mai frecventă pereche de tokenuri existente (prin "pereche" înțelegem aici doi tokeni consecutivi într-un cuvânt). Acea pereche cea mai frecventă este cea care va fi mergea, iar noi ștergem și repetăm pentru pasul următor.

Revenind la exemplul nostru anterior, să presupunem că cuvintele au următoarele frecvențe:

```
("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
```

înțelegând că `"hug"` a fost prezent de 10 ori în corpus, `"pug"` de 5 ori, `"pun"` de 12 ori, `"bun"` de 4 ori, iar `"hugs"` de 5 ori. Începem antrenamentul împărțind fiecare cuvânt în caractere (cele care formează vocabularul nostru inițial), astfel încât să putem vedea fiecare cuvânt ca pe o listă de tokeni:

```
("h" "u" "g", 10), ("p" "u" "g", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "u" "g" "s", 5)
```

Apoi ne uităm la perechi. Perechea `("h", "u")` este prezentă în cuvintele `"hug"` și `"hugs"`, deci de 15 ori în total în corpus. Totuși, nu este cea mai frecventă pereche: această onoare revine perechii `("u", "g")`, care este prezentă în cuvintele `"hug"`, `"pug"` și `"hugs"`, pentru un total de 20 de ori în vocabular.

Astfel, prima regulă de merge învățată de tokenizer este `("u", "g") -> "ug"`, ceea ce înseamnă că `"ug"` va fi adăugat la vocabular, iar perechea ar trebui să fie merged în toate cuvintele din corpus. La sfârșitul acestei etape, vocabularul și corpus-ul arată astfel:

```
Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug"]
Corpus: ("h" "ug", 10), ("p" "ug", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "ug" "s", 5)
```

Acum avem câteva perechi care rezultă într-un token mai lung de două caractere: perechea `("h", "ug")`, de exemplu (prezentă de 15 ori în corpus). Cea mai frecventă pereche în această etapă este `("u", "n")`, prezentă însă de 16 ori în corpus, astfel încât a doua regulă de îmbinare învățată este `("u", "n") -> "un"`. Adăugarea acestei reguli la vocabular și mergingul tuturor aparițiilor existente ne conduce la:

```
Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug", "un"]
Corpus: ("h" "ug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("h" "ug" "s", 5)
```

Acum, cea mai frecventă pereche este `("h", "ug")`, așa că învățăm regula de erge `("h", "ug") -> "hug"`, care ne oferă primul nostru simbol din trei litere. După fuzionare, corpusul arată astfel:

```
Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug", "un", "hug"]
Corpus: ("hug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("hug" "s", 5)
```

Și continuăm astfel până când ajungem la dimensiunea dorită a vocabularului.

<Tip>

✏️ **Acum e rândul tău!** Care crezi că va fi următoarea regulă de fuziune?

</Tip>

## Algoritmul de tokenizare[[tokenization-algorithm]]

Tokenizarea urmează îndeaproape procesul de antrenare, în sensul că noile inputuri sunt tokenizate prin aplicarea următoarelor etape:

1. Normalizare
2. Pre-tokenizare
3. Divizarea cuvintelor în caractere individuale
4. Aplicarea regulilor de merge învățate în ordine asupra acestor împărțiri

Să luăm exemplul pe care l-am folosit în timpul antrenamentului, cu cele trei reguli de merge învățate:

```
("u", "g") -> "ug"
("u", "n") -> "un"
("h", "ug") -> "hug"
```

Cuvântul `"bug"` va fi tokenizat ca `["b", "ug"]`. Cu toate acestea, cuvântul `"mug"` va fi tokenizat ca `["[UNK]", "ug"]` deoarece litera `"m"` nu a fost în vocabularul de bază. De asemenea, cuvântul `"thug"` va fi tokenizat ca `["[UNK]", "hug"]`: litera `"t"` nu se află în vocabularul de bază, iar aplicarea regulilor de merge duce mai întâi la fuzionarea lui `"u"` și `"g"` și apoi la fuzionarea lui `"h"` și `"ug"`.

<Tip>

✏️ **Acum e rândul tău!** Cum crezi că va fi tokenizat cuvântul `"unhug"`?

</Tip>

## Implementarea BPE[[implementing-bpe]]

Acum să aruncăm o privire la implementarea algoritmului BPE. Aceasta nu va fi o versiune optimizată pe care o puteți utiliza pe un corpus mare; dorim doar să vă arătăm codul pentru a putea înțelege algoritmul puțin mai bine.

În primul rând avem nevoie de un corpus, așa că haideți să creăm unul simplu cu câteva propoziții:

```python
corpus = [
    "This is the Hugging Face Course.",
    "This chapter is about tokenization.",
    "This section shows several tokenizer algorithms.",
    "Hopefully, you will be able to understand how they are trained and generate tokens.",
]
```

Apoi, trebuie să pre-tokenizăm acest corpus în cuvinte. Deoarece replicăm un tokenizator BPE (precum GPT-2), vom utiliza tokenizatorul `gpt2` pentru pre-tokenizare:

```python
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("gpt2")
```

Apoi, calculăm frecvențele fiecărui cuvânt din corpus la fel ca în cazul pre-tokenizării:

```python
from collections import defaultdict

word_freqs = defaultdict(int)

for text in corpus:
    words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
    new_words = [word for word, offset in words_with_offsets]
    for word in new_words:
        word_freqs[word] += 1

print(word_freqs)
```

```python out
defaultdict(int, {'This': 3, 'Ġis': 2, 'Ġthe': 1, 'ĠHugging': 1, 'ĠFace': 1, 'ĠCourse': 1, '.': 4, 'Ġchapter': 1,
    'Ġabout': 1, 'Ġtokenization': 1, 'Ġsection': 1, 'Ġshows': 1, 'Ġseveral': 1, 'Ġtokenizer': 1, 'Ġalgorithms': 1,
    'Hopefully': 1, ',': 1, 'Ġyou': 1, 'Ġwill': 1, 'Ġbe': 1, 'Ġable': 1, 'Ġto': 1, 'Ġunderstand': 1, 'Ġhow': 1,
    'Ġthey': 1, 'Ġare': 1, 'Ġtrained': 1, 'Ġand': 1, 'Ġgenerate': 1, 'Ġtokens': 1})
```

Următorul pas este calcularea vocabularului de bază, format din toate caracterele utilizate în corpus:

```python
alphabet = []

for word in word_freqs.keys():
    for letter in word:
        if letter not in alphabet:
            alphabet.append(letter)
alphabet.sort()

print(alphabet)
```

```python out
[ ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's',
  't', 'u', 'v', 'w', 'y', 'z', 'Ġ']
```

De asemenea, adăugăm tokenurile speciale utilizate de model la începutul vocabularului respectiv. În cazul GPT-2, singurul simbol special este `"<|endoftext|>"`:

```python
vocab = ["<|endoftext|>"] + alphabet.copy()
```

Acum trebuie să împărțim fiecare cuvânt în caractere individuale, pentru a putea începe antrenarea:

```python
splits = {word: [c for c in word] for word in word_freqs.keys()}
```

Acum că suntem pregătiți pentru antrenare, să scriem o funcție care calculează frecvența fiecărei perechi. Va trebui să folosim această funcție la fiecare etapă a antrenare:

```python
def compute_pair_freqs(splits):
    pair_freqs = defaultdict(int)
    for word, freq in word_freqs.items():
        split = splits[word]
        if len(split) == 1:
            continue
        for i in range(len(split) - 1):
            pair = (split[i], split[i + 1])
            pair_freqs[pair] += freq
    return pair_freqs
```

Să aruncăm o privire la o parte din acest dicționar după separările inițiale:

```python
pair_freqs = compute_pair_freqs(splits)

for i, key in enumerate(pair_freqs.keys()):
    print(f"{key}: {pair_freqs[key]}")
    if i >= 5:
        break
```

```python out
('T', 'h'): 3
('h', 'i'): 3
('i', 's'): 5
('Ġ', 'i'): 2
('Ġ', 't'): 7
('t', 'h'): 3
```

Acum, pentru a găsi cea mai frecventă pereche este nevoie doar de o loop rapid:

```python
best_pair = ""
max_freq = None

for pair, freq in pair_freqs.items():
    if max_freq is None or max_freq < freq:
        best_pair = pair
        max_freq = freq

print(best_pair, max_freq)
```

```python out
('Ġ', 't') 7
```

Așadar, prima îmbinare care trebuie învățată este `('Ġ', 't') -> 'Ġt'`, și adăugăm `'Ġt'` la vocabular:

```python
merges = {("Ġ", "t"): "Ġt"}
vocab.append("Ġt")
```

Pentru a continua, trebuie să aplicăm aceast merge în dicționarul nostru `splits`. Să scriem o altă funcție pentru acest lucru:

```python
def merge_pair(a, b, splits):
    for word in word_freqs:
        split = splits[word]
        if len(split) == 1:
            continue

        i = 0
        while i < len(split) - 1:
            if split[i] == a and split[i + 1] == b:
                split = split[:i] + [a + b] + split[i + 2 :]
            else:
                i += 1
        splits[word] = split
    return splits
```

Și putem arunca o privire la rezultatul primului merge:

```py
splits = merge_pair("Ġ", "t", splits)
print(splits["Ġtrained"])
```

```python out
['Ġt', 'r', 'a', 'i', 'n', 'e', 'd']
```

Acum avem tot ce ne trebuie pentru a face bucle până când vom învăța toate mergeu-rile dorite. Ne focusăm pe o mărime a vocabularui de 50:

```python
vocab_size = 50

while len(vocab) < vocab_size:
    pair_freqs = compute_pair_freqs(splits)
    best_pair = ""
    max_freq = None
    for pair, freq in pair_freqs.items():
        if max_freq is None or max_freq < freq:
            best_pair = pair
            max_freq = freq
    splits = merge_pair(*best_pair, splits)
    merges[best_pair] = best_pair[0] + best_pair[1]
    vocab.append(best_pair[0] + best_pair[1])
```

Ca rezultat, am învățat 19 reguli de merge(vocabularul inițial avea o dimensiune de 31 -- 30 de caractere din alfabet, plus simbolul special):

```py
print(merges)
```

```python out
{('Ġ', 't'): 'Ġt', ('i', 's'): 'is', ('e', 'r'): 'er', ('Ġ', 'a'): 'Ġa', ('Ġt', 'o'): 'Ġto', ('e', 'n'): 'en',
 ('T', 'h'): 'Th', ('Th', 'is'): 'This', ('o', 'u'): 'ou', ('s', 'e'): 'se', ('Ġto', 'k'): 'Ġtok',
 ('Ġtok', 'en'): 'Ġtoken', ('n', 'd'): 'nd', ('Ġ', 'is'): 'Ġis', ('Ġt', 'h'): 'Ġth', ('Ġth', 'e'): 'Ġthe',
 ('i', 'n'): 'in', ('Ġa', 'b'): 'Ġab', ('Ġtoken', 'i'): 'Ġtokeni'}
```

Iar vocabularul este compus din simbolul special, alfabetul inițial și toate rezultatele merge-urilor:

```py
print(vocab)
```

```python out
['<|endoftext|>', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o',
 'p', 'r', 's', 't', 'u', 'v', 'w', 'y', 'z', 'Ġ', 'Ġt', 'is', 'er', 'Ġa', 'Ġto', 'en', 'Th', 'This', 'ou', 'se',
 'Ġtok', 'Ġtoken', 'nd', 'Ġis', 'Ġth', 'Ġthe', 'in', 'Ġab', 'Ġtokeni']
```

<Tip>

💡 Folosind `train_new_from_iterator()` pe același corpus nu va rezulta exact același vocabular. Acest lucru se datorează faptului că atunci când există o alegere a celei mai frecvente perechi, am selectat-o pe prima întâlnită, în timp ce biblioteca 🤗 Tokenizers o selectează pe prima pe baza ID-urilor sale interne.

</Tip>

Pentru a tokeniza un text nou, îl pre-tokenizăm, îl împărțim, apoi aplicăm toate regulile de merge învățate:

```python
def tokenize(text):
    pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)
    pre_tokenized_text = [word for word, offset in pre_tokenize_result]
    splits = [[l for l in word] for word in pre_tokenized_text]
    for pair, merge in merges.items():
        for idx, split in enumerate(splits):
            i = 0
            while i < len(split) - 1:
                if split[i] == pair[0] and split[i + 1] == pair[1]:
                    split = split[:i] + [merge] + split[i + 2 :]
                else:
                    i += 1
            splits[idx] = split

    return sum(splits, [])
```

Putem încerca acest lucru pe orice text compus din caractere din alfabet:

```py
tokenize("This is not a token.")
```

```python out
['This', 'Ġis', 'Ġ', 'n', 'o', 't', 'Ġa', 'Ġtoken', '.']
```

<Tip warning={true}>

⚠️ Implementarea noastră va arunca o eroare dacă există un caracter necunoscut, deoarece nu am făcut nimic pentru a le gestiona. GPT-2 nu are de fapt un token necunoscut (este imposibil să obțineți un caracter necunoscut atunci când utilizați BPE la nivel de bytes), dar acest lucru s-ar putea întâmpla aici deoarece nu am inclus toate byte-urile posibile în vocabularul inițial. Acest aspect al BPE depășește domeniul de aplicare al acestei secțiuni, așa că am omis detaliile.

</Tip>

Asta e tot pentru algoritmul BPE! În continuare, ne vom uita la WordPiece.

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter6/5.mdx" />

### Introducere[[introducere]]
https://huggingface.co/learn/course/ro/chapter6/1.md

# Introducere[[introducere]]

<CourseFloatingBanner
    chapter={6}
    classNames="absolute z-10 right-0 top-0"
/>

În [Capitolul 3](/course/chapter3), am examinat cum să facem fine-tune unui model pentru o anumită sarcină. Când facem acest lucru, utilizăm același tokenizer cu care modelul a fost antrenat - dar ce facem când dorim să antrenăm un model de la zero? În așa cazuri, utilizarea unui tokenizer care a fost antrenat pe un corpus dintr-un alt domeniu sau limbă este, de obicei, suboptimal. De exemplu, un tokenizer antrenat pe un corpus în limba engleză va funcționa rău pe un corpus de texte în limba japoneză, deoarece utilizarea spațiilor și a punctuației este foarte diferită în cele două limbi.

În acest capitol, veți învăța cum să antrenați un tokenizer complet nou pe un corpus de texte, astfel încât să poată fi utilizat pentru a antrena un model de limbaj. Acest lucru va fi realizat cu ajutorul bibliotecii [🤗 Tokenizers](https://github.com/huggingface/tokenizers), care oferă tokenizerii "rapizi" din biblioteca [🤗 Transformers](https://github.com/huggingface/transformers). Vom examina îndeaproape caracteristicile pe care această bibliotecă le oferă și vom explora cum tokenizatorii rapizi diferă de versiunile "lente".

Subiectele pe care le vom acoperi includ:

* Cum să antrenați un tokenizer nou, similar celui utilizat de un anumit checkpoint pe un corpus nou de texte
* Caracteristicile speciale ale tokenizerilor rapizi
* Diferențele dintre cei trei algoritmi principali de subword tokenization utilizate în NLP în prezent
* Cum să construiți un tokenizer de la zero cu biblioteca 🤗 Tokenizers și să îl antrenați pe anumite date

Tehnicile prezentate în acest capitol vă vor pregăti pentru secțiunea din [Capitolul 7](/course/chapter7/6), unde vom examina crearea unui model de limbaj pentru codul sursă Python. Să începem prin a explora ce înseamnă să "antrenați" un tokenizer în primul rând.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter6/1.mdx" />

### Superputerile tokenizerilor rapizi[[fast-tokenizers-special-powers]]
https://huggingface.co/learn/course/ro/chapter6/3.md

# Superputerile tokenizerilor rapizi[[fast-tokenizers-special-powers]]

{#if fw === 'pt'}

<CourseFloatingBanner chapter={6}
  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/chapter6/section3_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter6/section3_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={6}
  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/chapter6/section3_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter6/section3_tf.ipynb"},
]} />

{/if}

În această secțiune, vom analiza mai atent capacitățile tokenizerilor din 🤗 Transformers. Până acum, i-am folosit doar pentru tokenizarea inputurilor sau decodificarea ID-urilor înapoi în text, dar tokenizerii - în special cei susținuți de biblioteca 🤗 Tokenizers - pot face mult mai multe. Pentru a ilustra aceste funcții suplimentare, vom explora modul de reproducere a rezultatelor pipeline-urilor `token-classification` (pe care le-am numit `ner`) și `question-answering` pe care le-am întâlnit pentru prima dată în [Capitolul 1](/course/chapter1).

<Youtube id="g8quOxoqhHQ"/>

În discuția următoare, vom face adesea distincția între tokenizatori "lenți" și "rapizi". Tokenizerii lenți sunt cei scriși în Python în interiorul bibliotecii 🤗 Transformers, în timp ce versiunile rapide sunt cele furnizate de 🤗 Tokenizers, care sunt scrise în Rust. Dacă vă amintiți de tabelul din [Capitolul 5](/course/chapter5/3) care a raportat cât timp a durat un tokenizer rapid și unul lent pentru a tokeniza datasetul Drug Review, ar trebui să aveți o idee despre de ce îi numim lenți și rapizi:

|               | Fast tokenizer | Slow tokenizer
:--------------:|:--------------:|:-------------:
`batched=True`  | 10,8s          | 4min41s
`batched=False` | 59,2s          | 5min3s

<Tip warning={true}>

⚠️ Atunci când tokenizați o singură propoziție, nu veți vedea întotdeauna o diferență de viteză între versiunea lentă și rapidă ale aceluiași tokenizer. De fapt, versiunea rapidă poate fi chiar mai lentă! Abia atunci când tokenizați multe texte în paralel, în același timp, veți putea observa clar diferența.

</Tip>

## Batch encoding[[batch-encoding]]

<Youtube id="3umI3tm27Vw"/>

Rezultatul unui tokenizer nu este un simplu dicționar Python; ceea ce obținem este de fapt un obiect special `BatchEncoding`. Este o subclasă a unui dicționar (de aceea am putut indexa acel rezultat fără nici o problemă mai devreme), dar cu metode suplimentare care sunt utilizate în principal de către tokenizerii rapizi.

Pe lângă capacitățile lor de paralelizare, funcționalitatea principală a tokenizerilor rapizi este aceea că aceștia țin întotdeauna evidența intervalului original de texte din care provin tokenii finali - o funcționalitate pe care o numim *offset mapping*. Acest lucru deblochează funcții precum mappingul fiecărui cuvânt la tokens pe care îi generează sau mappingul fiecărui caracter al textului original la tokenul în care se află și invers.

Acum hai să aruncăm o privire la un exemplu:

```py
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
encoding = tokenizer(example)
print(type(encoding))
```

Așa cum s-a menționat anterior, în outputul tokenizerului obținem un obiect `BatchEncoding`:

```python out
<class 'transformers.tokenization_utils_base.BatchEncoding'>
```

Deoarece clasa `AutoTokenizer` selectează un tokenizer rapid în mod implicit, putem utiliza metodele suplimentare pe care acest obiect `BatchEncoding` le oferă. Avem două metode de verificare dacă tokenizerul nostru este unul lent sau rapid. Putem verifica fie atributul `is_fast` al tokenizer-ului:

```python
tokenizer.is_fast
```

```python out
True
```

sau același atribut al `encoding`:

```python
encoding.is_fast
```

```python out
True
```

Hai să vedem ce ne permite un tokenizer rapid să facem. În primul rând, putem accesa tokenul fără a fi nevoie să facem convert ID-urile înapoi în tokens:

```py
encoding.tokens()
```

```python out
['[CLS]', 'My', 'name', 'is', 'S', '##yl', '##va', '##in', 'and', 'I', 'work', 'at', 'Hu', '##gging', 'Face', 'in',
 'Brooklyn', '.', '[SEP]']
```

În acest caz tokenul la indexul 5 este `##yl`, ceea ce este o parte a cuvântului "Sylvain" în propoziția originală. În același timp putem folosi metoda `word_ids()` pentru a obține indexul cuvântului din care provine fiecare token:

```py
encoding.word_ids()
```

```python out
[None, 0, 1, 2, 3, 3, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, None]

Putem vedea că tokenizerul are tokeni speciali `[CLS]` și `[SEP]` care sunt mapped la `None`, iar apoi fiecare token este mapped la cuvântul din care provine. Acest lucru este deosebit de util pentru a determina dacă un token este la începutul unui cuvânt sau dacă două tokenuri sunt în același cuvânt. Ne-am putea baza pe prefixul `##` pentru aceasta, dar funcționează doar pentru tokenizeri de tip BERT; această metodă funcționează pentru orice tip de tokenizator, atâta timp cât este unul rapid. În capitolul următor, vom vedea cum putem utiliza această capabilitate pentru a aplica labeluri pe care le avem pentru fiecare cuvânt în mod corespunzător tokenurilor în sarcini precum named entity recognition (NER) și part-of-speech (POS). De asemenea, îl putem utiliza pentru a face mask tuturor tokenurilor care provin din același cuvânt în masked language modeling(o tehnică numită _whole word masking_).

<Tip>

Noțiunea de ceea ce este un cuvânt este complicată. De exemplu, "I'll" (o prescurtare a "I will") contează ca unul sau două cuvinte? Acest lucru depinde de tokenizer și de operațiunea de pre-tokenizare pe care o aplică. Unii tokenizeri se divid doar pe spații, așa că vor considera acest lucru ca un singur cuvânt. Alții folosesc punctuația pe lângă spații, deci vor considera două cuvinte.

✏️ **Încercați!** Creați un tokenizer din checkpointurile `bert-base-cased` și `roberta-base` și tokenizați "81s" cu ele. Ce observați? Care sunt ID-urile cuvintelor?

</Tip>

În mod similar, există o metodă `sentence_ids()` pe care o putem utiliza pentru a face map unui token la propoziția din care provine (deși, în acest caz, `token_type_ids` returnate de tokenizer ne pot oferi aceeași informație).

În cele din urmă, putem face map oricărui cuvânt sau token la caracterele din textul original și invers, prin intermediul metodelor `word_to_chars()` sau `token_to_chars()` și `char_to_word()` sau `char_to_token()`. De exemplu, metoda `word_ids()` ne-a spus că `##yl` face parte din cuvântul cu indicele 3, dar ce cuvânt este în propoziție? Putem afla astfel:

```py
start, end = encoding.word_to_chars(3)
example[start:end]
```

```python out
Sylvain
```

Așa cum am menționat anterior, toate acestea sunt posibile datorită faptului că tokenizerul rapid ține evidența spanului de text de la care provine fiecare token într-o listă de *offseturi*. Pentru a ilustra modul în care se utilizează acestea, în continuare vă vom arăta cum să replicați rezultatele pipelineului `token-classification` manual.

<Tip>

✏️ **Încercați!** Creați propriul exemplu de text și încercați să înțelegeți care tokenuri sunt asociate cu ID-ul cuvântului și, de asemenea, cum să extrageți spanurile pentru singur cuvânt. Pentru puncte bonus, încercați să utilizați două propoziții ca inputuri și să vedeți dacă ID-urile propozițiilor au sens pentru voi.

</Tip>

## În interiorul pipelineului `token-classification`[[inside-the-token-classification-pipeline]]

În [Capitolul 1](/course/chapter1) am avut primul nostru contact aplicând NER - unde sarcina constă în identificarea părților textului care corespund entităților precum persoane, locații sau organizații - cu funcția `pipeline()` din 🤗 Transformers. Apoi, în [Capitolul 2](/course/chapter2), am văzut cum un pipeline grupează cele trei etape necesare pentru a obține predicțiile de la un text "raw": tokenizare, trecerea inputurilor prin model și post-procesare. Primele două etape din pipelineul `token-classification` sunt la fel ca în orice alt pipeline, dar post-procesarea este puțin mai complexă - hai să vedem cum!

{#if fw === 'pt'}

<Youtube id="0E7ltQB7fM8"/>

{:else}

<Youtube id="PrX4CjrVnNc"/>

{/if}

### Obținerea rezultatelor de bază cu pipelineul[[getting-the-base-results-with-the-pipeline]]

În primul rând, trebuie să luăm un token classification pipeline pentru a obține câteva rezultate ca să le putem compara manual. Modelul utilizat în mod implicit este [`dbmdz/bert-large-cased-finetuned-conll03-english`](https://huggingface.co/dbmdz/bert-large-cased-finetuned-conll03-english); acesta aplică NER pe propoziții:

```py
from transformers import pipeline

token_classifier = pipeline("token-classification")
token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
```

```python out
[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S', 'start': 11, 'end': 12},
 {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl', 'start': 12, 'end': 14},
 {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va', 'start': 14, 'end': 16},
 {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in', 'start': 16, 'end': 18},
 {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu', 'start': 33, 'end': 35},
 {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging', 'start': 35, 'end': 40},
 {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face', 'start': 41, 'end': 45},
 {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
```

Modelul a identificat fiecare token generdat de "Sylvain" ca o persoană, fiecare token generat de "Hugging Face" ca o organizație, și fiecare token "Brooklin" ca o locație". În același timp putem întreba pipelineul să grupeze împreună tokenurile care corespund cu aceeași entitate.

```py
from transformers import pipeline

token_classifier = pipeline("token-classification", aggregation_strategy="simple")
token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
```

```python out
[{'entity_group': 'PER', 'score': 0.9981694, 'word': 'Sylvain', 'start': 11, 'end': 18},
 {'entity_group': 'ORG', 'score': 0.97960204, 'word': 'Hugging Face', 'start': 33, 'end': 45},
 {'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
```

`Aggregation_strategy` aleasă va modifica scorurile calculate pentru fiecare entitate grupată. Cu `"simple"`, scorul este doar media scorurilor pentru fiecare token din entitatea dată: de exemplu, scorul pentru "Sylvain" este media scorurilor pe care le-am văzut în exemplele anterioare pentru token-urile `S`, `##yl`, `##va` și `##in`. Alte strategii disponibile sunt:

- `"first"`, unde scorul fiecărei entități este scorul primului token al acelei entități (astfel, pentru "Sylvain" ar fi 0.993828, scorul token-ului `S`)
- `"max"`, unde scorul fiecărei entități este scorul maxim al tokenilor din acea entitate (astfel, pentru "Hugging Face" ar fi 0.98879766, scorul "Face")
- `"average"`, unde scorul fiecărei entități este media scorurilor cuvintelor care compun acea entitate (astfel, pentru "Sylvain" nu ar exista nicio diferență față de strategia `"simple"`, dar "Hugging Face" ar avea un scor de 0.9819, media scorurilor pentru "Hugging", 0.975 și "Face", 0.98879)

Acum să vedem cum putem obține aceste rezultate fără a folosi funcția `pipeline()`!

### De la inputuri la predicții[[from-inputs-to-predictions]]

{#if fw === 'pt'}

În primul rând trebuie să tokenizăm inputurile și sp le trecem prin model. Acest lucru este făcut excat ca în [Capitolul 2](/course/chapter2); noi am inițializat tokenizerul și modelul folosind clasa `AutoXxx` și apoi am folosit-o pe exemplul nostru:

```py
from transformers import AutoTokenizer, AutoModelForTokenClassification

model_checkpoint = "dbmdz/bert-large-cased-finetuned-conll03-english"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
model = AutoModelForTokenClassification.from_pretrained(model_checkpoint)

example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
inputs = tokenizer(example, return_tensors="pt")
outputs = model(**inputs)
```

Deoarece aici folosim `AutoModelForTokenClassification` , noi primim un set de logits pentru fiecare token în input sequence:

```py
print(inputs["input_ids"].shape)
print(outputs.logits.shape)
```

```python out
torch.Size([1, 19])
torch.Size([1, 19, 9])
```

{:else}

În primul rând trebuie să tokenizăm inputurile și să le trecem prin model. Acest lucru este făcut excat ca în [Capitolul 2](/course/chapter2); noi am inițializat tokenizerul și modelul folosind clasa `TFAutoXxx` și apoi am folosit-o pe exemplul nostru:

```py
from transformers import AutoTokenizer, TFAutoModelForTokenClassification

model_checkpoint = "dbmdz/bert-large-cased-finetuned-conll03-english"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
model = TFAutoModelForTokenClassification.from_pretrained(model_checkpoint)

example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
inputs = tokenizer(example, return_tensors="tf")
outputs = model(**inputs)
```

Deoarece aici folosim `TFAutoModelForTokenClassification`, noi primim un set de logits pentru fiecare token în input sequence:

```py
print(inputs["input_ids"].shape)
print(outputs.logits.shape)
```

```python out
(1, 19)
(1, 19, 9)
```

{/if}

Noi avem un batch cu 1 secvență din 19 tokenuri și modelul are 9 labeluri diferite, deci outputul modelului are un shape de 1 x 19 x 9. Ca și pentru text classification pipeline, noi folosim o funcție softmax pentru a face convert logiturilor în probabilități și luăm argmax pentru a obține predicții(atrage atenția asupra fatpului că putem lua argmax pe logituri deoarece softmax nu schimbă ordinea):

{#if fw === 'pt'}

```py
import torch

probabilities = torch.nn.functional.softmax(outputs.logits, dim=-1)[0].tolist()
predictions = outputs.logits.argmax(dim=-1)[0].tolist()
print(predictions)
```

{:else}

```py
import tensorflow as tf

probabilities = tf.math.softmax(outputs.logits, axis=-1)[0]
probabilities = probabilities.numpy().tolist()
predictions = tf.math.argmax(outputs.logits, axis=-1)[0]
predictions = predictions.numpy().tolist()
print(predictions)
```

{/if}

```python out
[0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 6, 6, 6, 0, 8, 0, 0]
```

Atributul `model.config.id2label` con;ine mappingul indexilor la labeluri pe care le putem folosi pentru a înțelege predicțiile:

```py
model.config.id2label
```

```python out
{0: 'O',
 1: 'B-MISC',
 2: 'I-MISC',
 3: 'B-PER',
 4: 'I-PER',
 5: 'B-ORG',
 6: 'I-ORG',
 7: 'B-LOC',
 8: 'I-LOC'}
```

Cum am văzut mai devreme, există 9 labeluri: `O` este labelul pentru tokenurile care nu se află în nicio entitate numită(aceasta reprezintă "exteriorul"), și avem apoi două labeluri pentru fiecare tip de entitate (divers, persoană, organizație și locație). Eticheta `B-XXX` indică faptul că tokenul se află la începutul entității `XXX` și eticheta `I-XXX` indică faptul că tokenul se află în interiorul entității `XXX`. De exemplu, în exemplul curent ne-am aștepta ca modelul nostru să clasifice tokenul `S` ca `B-PER` (începutul unei entități de-tip persoană) și tokenurile `##yl`, `##va` și `##in` ca `I-PER` (în interiorul unei entități de tip persoană).

S-ar putea să credeți că modelul a greșit în acest caz, deoarece a atribuit eticheta `I-PER` tuturor acestor patru tokeni, dar acesta nu este în întregime adevărat. Există, de fapt, două formate pentru labelurile `B-` și `I-`: *IOB1* și *IOB2*. Formatul IOB2 (în roz mai jos), este cel pe care l-am introdus, în timp ce formatul IOB1 (în albastru), utilizează labelurile care încep cu `B-` doar pentru a separa două entități adiacente de același tip. Modelul pe care îl utilizăm a fost fine-tuned pe un dataset care utilizează acel format, ceea ce explică de ce atribuie labelul `I-PER` tokenului `S`.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/IOB_versions.svg" alt="IOB1 vs IOB2 format"/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/IOB_versions-dark.svg" alt="IOB1 vs IOB2 format"/>
</div>

Cu maparea aceasta, suntem gat a să reproducem(aproape în total) rezultat primului pipeline -- noi putem lua scorul și labelul fiecărui token care nu a fost clasificat ca `O`:

```py
results = []
tokens = inputs.tokens()

for idx, pred in enumerate(predictions):
    label = model.config.id2label[pred]
    if label != "O":
        results.append(
            {"entity": label, "score": probabilities[idx][pred], "word": tokens[idx]}
        )

print(results)
```

```python out
[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S'},
 {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl'},
 {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va'},
 {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in'},
 {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu'},
 {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging'},
 {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face'},
 {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn'}]
```

Acest lucru este foarte similar cu ce am avut mai devreme, cu o excepție: pipelineul de asemenea ne-a oferit informație despre `start` și `end` al fiecărei entități în propoziția originală. Acum e momentul când offset mappingul nostru ne va ajuta. Pentru a obține offseturile, noi trebuie să setăm `return_offsets_mapping=True` când aplicăm tokenizerul pe inputurile noastre:

```py
inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
inputs_with_offsets["offset_mapping"]
```

```python out
[(0, 0), (0, 2), (3, 7), (8, 10), (11, 12), (12, 14), (14, 16), (16, 18), (19, 22), (23, 24), (25, 29), (30, 32),
 (33, 35), (35, 40), (41, 45), (46, 48), (49, 57), (57, 58), (0, 0)]
```

Fiecare tuple este spanul de text care corespunde fiecărui token, unde `(0, 0)` este rezervat pentru tokenii speciali. Noi am văzut înainte că tokenul la indexul 5 este `##yl`, care are aici `(12, 14)` ca offsets. Dacă luăm sliceul corespunzător în exemplul nostru:

```py
example[12:14]
```

noi obținem spanul propriu de text fără `##`:

```python out
yl
```

Folosind aceasta, putem acum completa rezultatele anterioare:


```py
results = []
inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
tokens = inputs_with_offsets.tokens()
offsets = inputs_with_offsets["offset_mapping"]

for idx, pred in enumerate(predictions):
    label = model.config.id2label[pred]
    if label != "O":
        start, end = offsets[idx]
        results.append(
            {
                "entity": label,
                "score": probabilities[idx][pred],
                "word": tokens[idx],
                "start": start,
                "end": end,
            }
        )

print(results)
```

```python out
[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S', 'start': 11, 'end': 12},
 {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl', 'start': 12, 'end': 14},
 {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va', 'start': 14, 'end': 16},
 {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in', 'start': 16, 'end': 18},
 {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu', 'start': 33, 'end': 35},
 {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging', 'start': 35, 'end': 40},
 {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face', 'start': 41, 'end': 45},
 {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
```

Acest răspuns e același răspuns pe care l-am primit de la primul pipeline:

### Gruparea entităților[[grouping-entities]]

Utilizarea offseturilor pentru a determina cheile de start și de sfârșit pentru fiecare entitate este util, dar această informație nu este strict necesară. Când dorim să grupăm entitățile împreună, totuși, offseturile ne vor salva o mulțime de messy code. De exemplu, dacă am dori să grupăm împreună tokenii `Hu`, `##gging` și `Face`, am putea crea reguli speciale care să spună că primele două ar trebui să fie atașate și să înlăturăm `##`, iar `Face` ar trebui adăugat cu un spațiu, deoarece nu începe cu `##` -- dar acest lucru ar funcționa doar pentru acest tip particular de tokenizer. Ar trebui să scriem un alt set de reguli pentru un tokenizer SentencePiece sau unul Byte-Pair-Encoding (discutat mai târziu în acest capitol).

Cu offseturile, tot acel cod custom dispare: pur și simplu putem lua spanul din textul original care începe cu primul token și se termină cu ultimul token. Deci, în cazul tokenurilor `Hu`, `##gging` și `Face`, ar trebui să începem la caracterul 33 (începutul lui `Hu`) și să ne oprim înainte de caracterul 45 (sfârșitul lui `Face`):

```py
example[33:45]
```

```python out
Hugging Face
```

Pentru a scrie codul care post-procesează predicțiile în timp ce grupăm entitățile, vom grupa entitățile care sunt consecutive și labeled cu `I-XXX`, cu excepția primeia, care poate fi labeled ca `B-XXX` sau `I-XXX` (decidem să oprim gruparea unei entități atunci când întâlnim un `O`, un nou tip de entitate, sau un `B-XXX` care ne spune că o entitate de același tip începe):

```py
import numpy as np

results = []
inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
tokens = inputs_with_offsets.tokens()
offsets = inputs_with_offsets["offset_mapping"]

idx = 0
while idx < len(predictions):
    pred = predictions[idx]
    label = model.config.id2label[pred]
    if label != "O":
        # Remove the B- or I-
        label = label[2:]
        start, _ = offsets[idx]

        # Grab all the tokens labeled with I-label
        all_scores = []
        while (
            idx < len(predictions)
            and model.config.id2label[predictions[idx]] == f"I-{label}"
        ):
            all_scores.append(probabilities[idx][pred])
            _, end = offsets[idx]
            idx += 1

        # The score is the mean of all the scores of the tokens in that grouped entity
        score = np.mean(all_scores).item()
        word = example[start:end]
        results.append(
            {
                "entity_group": label,
                "score": score,
                "word": word,
                "start": start,
                "end": end,
            }
        )
    idx += 1

print(results)
```

Și obținem aceleași răspuns ca de la pipelineul secundar!

```python out
[{'entity_group': 'PER', 'score': 0.9981694, 'word': 'Sylvain', 'start': 11, 'end': 18},
 {'entity_group': 'ORG', 'score': 0.97960204, 'word': 'Hugging Face', 'start': 33, 'end': 45},
 {'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
```

Alt exemplu de sarcină unde offseturile sunt extrem de useful pentru răspunderea la întrebări. Scufundându-ne în pipelineuri, un lucru pe care îl vom face în următoarea secțiune, ne vom premite să ne uităm peste o caracteristică a tokenizerului în librăria 🤗 Transformers: vom avea de-a face cu overflowing tokens când truncăm un input de o anumită lungime.



<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter6/3.mdx" />

### Tokenizarea WordPiece[[wordpiece-tokenization]]
https://huggingface.co/learn/course/ro/chapter6/6.md

# Tokenizarea WordPiece[[wordpiece-tokenization]]

<CourseFloatingBanner chapter={6}
  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/chapter6/section6.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter6/section6.ipynb"},
]} />

WordPiece este algoritmul de tokenizare dezvoltat de Google pentru preantrenarea BERT. De atunci, acesta a fost reutilizat în numeroase modele Transformers bazate pe BERT, cum ar fi DistilBERT, MobileBERT, Funnel Transformers și MPNET. Este foarte similar cu BPE în ceea ce privește antrenarea, dar tokenizarea efectivă se face diferit.

<Youtube id="qpv6ms_t_1A"/>

<Tip>

💡 Această secțiune acoperă WordPiece în profunzime, mergând până la prezentarea unei implementări complete. Puteți sări la sfârșit dacă doriți doar o prezentare generală a algoritmului de tokenizare.

</Tip>

## Algoritmul de antrenare[[training-algorithm]]

<Tip warning={true}>

⚠️ Google nu a publicat niciodată implementarea algoritmului de formare a WordPiece, astfel încât ceea ce urmează este cea mai bună presupunere a noastră bazată pe literatura publicată. Este posibil să nu fie 100% exactă.

</Tip>

La fel ca BPE, WordPiece pornește de la un vocabular restrâns care include simbolurile speciale utilizate de model și alfabetul inițial. Deoarece identifică subcuvinte prin adăugarea unui prefix (cum ar fi `##` pentru BERT), fiecare cuvânt este inițial împărțit prin adăugarea prefixului respectiv la toate caracterele din cuvânt. Astfel, de exemplu, `"word"` este împărțit astfel:

```
w ##o ##r ##d
```

Astfel, alfabetul inițial conține toate caracterele prezente la începutul unui cuvânt și caracterele prezente în interiorul unui cuvânt cu prefixul WordPiece.

Apoi, la fel ca BPE, WordPiece învață reguli de merge. Principala diferență este modul în care este selectată perechea care urmează să fie merged. În loc să selecteze cea mai frecventă pereche, WordPiece calculează un scor pentru fiecare pereche, utilizând următoarea formulă:

$$\mathrm{score} = (\mathrm{freq\_of\_pair}) / (\mathrm{freq\_of\_first\_element} \times \mathrm{freq\_of\_second\_element})$$

Împărțind frecvența perechii la produsul frecvențelor fiecărei părți a acesteia, algoritmul prioritizează fuzionarea perechilor în care părțile individuale sunt mai puțin frecvente în vocabular. De exemplu, nu va fuziona neapărat `("un", "##able")` chiar dacă această pereche apare foarte frecvent în vocabular, deoarece cele două perechi `"un"` și `"##able"` vor apărea probabil fiecare într-o mulțime de alte cuvinte și vor avea o frecvență ridicată. În schimb, o pereche precum `("hu", "##gging")` va fi probabil fuzionată mai repede (presupunând că cuvântul "hugging" apare frecvent în vocabular), deoarece `"hu"` și `"##gging"` sunt probabil mai puțin frecvente individual.

Să ne uităm la același vocabular pe care l-am folosit în exemplul de antrenare BPE:

```
("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
```

Spliturile aici vor fi:

```
("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##g" "##s", 5)
```

deci vocabularul inițial va fi `["b", "h", "p", "##g", "##n", "##s", "##u"]` (dacă uităm deocamdată de tokenurile speciale). Cea mai frecventă pereche este `("##u", "##g")` (prezentă de 20 de ori), dar frecvența individuală a lui `"##u"` este foarte mare, astfel încât scorul său nu este cel mai mare (este 1 / 36). Toate perechile cu un `"##u"` au de fapt același scor (1 / 36), astfel încât cel mai bun scor revine perechii `("##g", "##s")` - singura fără un `"##u"` - cu 1 / 20, iar prima îmbinare învățată este `("##g", "##s") -> ("##gs")`.

Rețineți că atunci când facem merge, eliminăm `##` dintre cele două tokenuri, deci adăugăm `"##gs"` la vocabular și aplicăm merge în cuvintele din corpus:

```
Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs"]
Corpus: ("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##gs", 5)
```

În acest moment, `"##u"` se află în toate perechile posibile, deci toate au același scor. Să spunem că în acest caz, prima pereche este merged, deci `("h", "##u") -> "hu"`. Acest lucru ne duce la:

```
Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu"]
Corpus: ("hu" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("hu" "##gs", 5)
```

Apoi, următorul scor cu cel mai bun rezultat este împărțit de `("hu", "##g")` și `("hu", "##gs")` (cu 1/15, comparativ cu 1/21 pentru toate celelalte perechi), astfel încât prima pereche cu cel mai mare scor este merged

```
Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu", "hug"]
Corpus: ("hug", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("hu" "##gs", 5)
```

și continuăm astfel până când ajungem la dimensiunea dorită a vocabularului.

<Tip>

✏️ **Acum e rândul tău!** Care va fi următoarea regulă de merge?

</Tip>

## Algoritm de tokenizare[[tokenization-algorithm]]

Tokenizarea diferă în WordPiece și BPE prin faptul că WordPiece salvează doar vocabularul final, nu și regulile de merge învățate. Pornind de la cuvântul de tokenizat, WordPiece găsește cel mai lung subcuvânt care se află în vocabular, apoi îl împarte. De exemplu, dacă folosim vocabularul învățat în exemplul de mai sus, pentru cuvântul `"hugs"` cel mai lung subcuvânt de la început care se află în vocabular este `"hug"`, așa că împărțim acolo și obținem `["hug", "##s"]`. Apoi continuăm cu `"##s"`, care se află în vocabular, deci tokenizarea lui `"hugs"` este `["hug", "##s"]`.

Cu BPE, am fi aplicat mergeurile învățate în ordine și am fi tokenizat acest lucru ca `["hu", "##gs"]`, deci encodingul este diferit.

Ca un alt exemplu, să vedem cum ar fi tokenizat cuvântul `"bugs"`. `"b"` este cel mai lung subcuvânt care începe de la începutul cuvântului care se află în vocabular, așa că îl împărțim acolo și obținem `["b", "##ugs"]`. Apoi, `"##u"` este cel mai lung subcuvânt care începe de la începutul cuvântului `"##ugs"` care se află în vocabular, deci îl separăm și obținem `["b", "##u, "##gs"]`. În cele din urmă, `"##gs"` se află în vocabular, deci această ultimă listă este tokenizarea lui `"bugs"`.

Atunci când tokenizarea ajunge într-un stadiu în care nu este posibilă găsirea unui subcuvânt în vocabular, întregul cuvânt este tokenizat ca necunoscut - astfel, de exemplu, `"mug"` ar fi tokenizat ca `["[UNK]"]`, la fel ca `"bum"` (chiar dacă putem începe cu `"b"` și `"##u"`, `"##m"` nu face parte din vocabular, iar tokenizarea rezultată va fi `["[UNK]"]`, nu `["b", "##u", "[UNK]"]`). Aceasta este o altă diferență față de BPE, care ar clasifica doar caracterele individuale care nu se află în vocabular ca necunoscute.

<Tip>

✏️ **Acum e rândul tău!** Cum va fi tokenizat cuvântul `"pugs"`?

</Tip>

## Implementând WordPiece[[implementing-wordpiece]]

Acum să aruncăm o privire la o implementare a algoritmului WordPiece. La fel ca în cazul BPE, acest lucru este doar pedagogic și nu veți putea să îl utilizați pe un corpus mare.

Vom utiliza același corpus ca în exemplul BPE:

```python
corpus = [
    "This is the Hugging Face Course.",
    "This chapter is about tokenization.",
    "This section shows several tokenizer algorithms.",
    "Hopefully, you will be able to understand how they are trained and generate tokens.",
]
```

În primul rând, trebuie să pre-tokenizăm corpusul în cuvinte. Deoarece replicăm un tokenizator WordPiece (precum BERT), vom utiliza tokenizatorul `bert-base-cased` pentru pre-tokenizare:

```python
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
```

Apoi, calculăm frecvențele fiecărui cuvânt din corpus la fel ca în cazul pre-tokenizării:

```python
from collections import defaultdict

word_freqs = defaultdict(int)
for text in corpus:
    words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
    new_words = [word for word, offset in words_with_offsets]
    for word in new_words:
        word_freqs[word] += 1

word_freqs
```

```python out
defaultdict(
    int, {'This': 3, 'is': 2, 'the': 1, 'Hugging': 1, 'Face': 1, 'Course': 1, '.': 4, 'chapter': 1, 'about': 1,
    'tokenization': 1, 'section': 1, 'shows': 1, 'several': 1, 'tokenizer': 1, 'algorithms': 1, 'Hopefully': 1,
    ',': 1, 'you': 1, 'will': 1, 'be': 1, 'able': 1, 'to': 1, 'understand': 1, 'how': 1, 'they': 1, 'are': 1,
    'trained': 1, 'and': 1, 'generate': 1, 'tokens': 1})
```

După cum am văzut mai devreme, alfabetul este setul unic compus din toate primele litere ale cuvintelor și toate celelalte litere care apar în cuvintele cu prefixul `##`:

```python
alphabet = []
for word in word_freqs.keys():
    if word[0] not in alphabet:
        alphabet.append(word[0])
    for letter in word[1:]:
        if f"##{letter}" not in alphabet:
            alphabet.append(f"##{letter}")

alphabet.sort()
alphabet

print(alphabet)
```

```python out
['##a', '##b', '##c', '##d', '##e', '##f', '##g', '##h', '##i', '##k', '##l', '##m', '##n', '##o', '##p', '##r', '##s',
 '##t', '##u', '##v', '##w', '##y', '##z', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u',
 'w', 'y']
```

De asemenea, adăugăm simbolurile speciale utilizate de model la începutul vocabularului respectiv. În cazul BERT, este vorba de lista `["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"]``:

```python
vocab = ["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"] + alphabet.copy()
```

Apoi trebuie să împărțim fiecare cuvânt, cu toate literele care nu sunt primele cu prefixul `##`:

```python
splits = {
    word: [c if i == 0 else f"##{c}" for i, c in enumerate(word)]
    for word in word_freqs.keys()
}
```

Acum că suntem pregătiți pentru antrenare, să scriem o funcție care calculează scorul fiecărei perechi. Va trebui să folosim această funcție la fiecare etapă a antrenării:

```python
def compute_pair_scores(splits):
    letter_freqs = defaultdict(int)
    pair_freqs = defaultdict(int)
    for word, freq in word_freqs.items():
        split = splits[word]
        if len(split) == 1:
            letter_freqs[split[0]] += freq
            continue
        for i in range(len(split) - 1):
            pair = (split[i], split[i + 1])
            letter_freqs[split[i]] += freq
            pair_freqs[pair] += freq
        letter_freqs[split[-1]] += freq

    scores = {
        pair: freq / (letter_freqs[pair[0]] * letter_freqs[pair[1]])
        for pair, freq in pair_freqs.items()
    }
    return scores
```

Să aruncăm o privire la o parte din acest dicționar după separările inițiale:

```python
pair_scores = compute_pair_scores(splits)
for i, key in enumerate(pair_scores.keys()):
    print(f"{key}: {pair_scores[key]}")
    if i >= 5:
        break
```

```python out
('T', '##h'): 0.125
('##h', '##i'): 0.03409090909090909
('##i', '##s'): 0.02727272727272727
('i', '##s'): 0.1
('t', '##h'): 0.03571428571428571
('##h', '##e'): 0.011904761904761904
```

Acum, pentru a găsi perechea cu cel mai bun scor este nevoie doar de un loop rapid:

```python
best_pair = ""
max_score = None
for pair, score in pair_scores.items():
    if max_score is None or max_score < score:
        best_pair = pair
        max_score = score

print(best_pair, max_score)
```

```python out
('a', '##b') 0.2
```

Așadar, primul merge de învățat este `('a', '##b') -> 'ab'`, iar noi adăugăm `'ab'` la vocabular:

```python
vocab.append("ab")
```

Pentru a continua, trebuie să aplicăm acest merge în dicționarul nostru `splits`. Să scriem o altă funcție pentru acest lucru:

```python
def merge_pair(a, b, splits):
    for word in word_freqs:
        split = splits[word]
        if len(split) == 1:
            continue
        i = 0
        while i < len(split) - 1:
            if split[i] == a and split[i + 1] == b:
                merge = a + b[2:] if b.startswith("##") else a + b
                split = split[:i] + [merge] + split[i + 2 :]
            else:
                i += 1
        splits[word] = split
    return splits
```

Și putem arunca o privire la rezultatul primului merge:

```py
splits = merge_pair("a", "##b", splits)
splits["about"]
```

```python out
['ab', '##o', '##u', '##t']
```

Acum avem tot ce ne trebuie pentru a face bucle până când vom învăța toate merge-urile dorite. Să ne propunem un vocabular de mărimea 70:

```python
vocab_size = 70
while len(vocab) < vocab_size:
    scores = compute_pair_scores(splits)
    best_pair, max_score = "", None
    for pair, score in scores.items():
        if max_score is None or max_score < score:
            best_pair = pair
            max_score = score
    splits = merge_pair(*best_pair, splits)
    new_token = (
        best_pair[0] + best_pair[1][2:]
        if best_pair[1].startswith("##")
        else best_pair[0] + best_pair[1]
    )
    vocab.append(new_token)
```

Ne putem uita apoi la vocabularul generat:

```py
print(vocab)
```

```python out
['[PAD]', '[UNK]', '[CLS]', '[SEP]', '[MASK]', '##a', '##b', '##c', '##d', '##e', '##f', '##g', '##h', '##i', '##k',
 '##l', '##m', '##n', '##o', '##p', '##r', '##s', '##t', '##u', '##v', '##w', '##y', '##z', ',', '.', 'C', 'F', 'H',
 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u', 'w', 'y', 'ab', '##fu', 'Fa', 'Fac', '##ct', '##ful', '##full', '##fully',
 'Th', 'ch', '##hm', 'cha', 'chap', 'chapt', '##thm', 'Hu', 'Hug', 'Hugg', 'sh', 'th', 'is', '##thms', '##za', '##zat',
 '##ut']
```

După cum putem vedea, în comparație cu BPE, acest tokenizator învață părțile din cuvinte ca tokenuri puțin mai repede.

<Tip>

💡 Folosind `train_new_from_iterator()` pe același corpus nu va rezulta exact același vocabular. Acest lucru se datorează faptului că biblioteca 🤗 Tokenizers nu implementează WordPiece pentru antrenare (deoarece nu suntem complet siguri cum funcționează intern), ci utilizează BPE în schimb.

</Tip>

Pentru a tokeniza un text nou, îl pre-tokenizăm, îl împărțim, apoi aplicăm algoritmul de tokenizare pe fiecare cuvânt. Adică, căutăm cel mai mare subcuvânt începând de la începutul primului cuvânt și îl împărțim, apoi repetăm procesul pentru a doua parte și așa mai departe pentru restul acelui cuvânt și pentru următoarele cuvinte din text:

```python
def encode_word(word):
    tokens = []
    while len(word) > 0:
        i = len(word)
        while i > 0 and word[:i] not in vocab:
            i -= 1
        if i == 0:
            return ["[UNK]"]
        tokens.append(word[:i])
        word = word[i:]
        if len(word) > 0:
            word = f"##{word}"
    return tokens
```

Haideți să-l testăm pe un cuvânt care se află în vocabular și pe altul care nu se află:


```python
print(encode_word("Hugging"))
print(encode_word("HOgging"))
```

```python out
['Hugg', '##i', '##n', '##g']
['[UNK]']
```

Acum, scriem o funcție care tokenizează un text:

```python
def tokenize(text):
    pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)
    pre_tokenized_text = [word for word, offset in pre_tokenize_result]
    encoded_words = [encode_word(word) for word in pre_tokenized_text]
    return sum(encoded_words, [])
```

Îl putem încerca pe orice text:

```python
tokenize("This is the Hugging Face course!")
```

```python out
['Th', '##i', '##s', 'is', 'th', '##e', 'Hugg', '##i', '##n', '##g', 'Fac', '##e', 'c', '##o', '##u', '##r', '##s',
 '##e', '[UNK]']
```

Asta e tot pentru algoritmul WordPiece! Acum să aruncăm o privire la Unigram.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter6/6.mdx" />

### Quiz de sfârșit de capitol[[end-of-chapter-quiz]]
https://huggingface.co/learn/course/ro/chapter6/10.md

# Quiz de sfârșit de capitol[[end-of-chapter-quiz]]

<CourseFloatingBanner
    chapter={6}
    classNames="absolute z-10 right-0 top-0"
/>

Hai să testăm ceea ce ai învățat în acest capitol!

### 1. Când ar trebui să antrenezi un nou tokenizer?

<Question
	choices={[
		{
			text: "Când datasetul tău este similar cu cel utilizat de un model preantrenat și doriți să preantrenați un model nou",
			explain: "În acest caz, pentru a economisi timp și resurse, o alegere mai bună ar fi să folosești același tokenizer ca și modelul preantrenat și să faceți fine-tune acelui model."
		},
		{
			text: "Atunci când datasetul tău este similar cu cel utilizat de un model preantrenat existent și doriți să faceți fine-tune unui nou model utilizând acest model preantrenat",
			explain: "Pentru a face fine-tune unui model dintr-un model preantrenat, trebuie să utilizați întotdeauna același tokenizer."
		},
		{
			text: "Atunci când datasetul tău este diferit de cel utilizat de un model preantrenat existent și doriți să preantrenați un nou model",
			explain: "Corect! În acest caz nu există niciun avantaj în a utiliza același tokenizer.",
            correct: true
		},
        {
			text: "Atunci datasetul tău este diferit de cel utilizat de un model preantrenat existent, dar doriți să faceți fine-tune unui model nou utilizând acest model preantrenat",
			explain: "Pentru a face fine-tune unui model dintr-un model preantrenat, trebuie să utilizați întotdeauna același tokenizer."
		}
	]}
/>

### 2. Care este avantajul utilizării unui generator de liste de texte în comparație cu o listă de liste de texte atunci când utilizați `train_new_from_iterator()`?

<Question
	choices={[
		{
			text: "Acesta este singurul tip pe care metoda <code>train_new_from_iterator()</code> îl acceptă.",
			explain: "O listă de liste de texte este un tip special de generator de liste de texte, astfel încât metoda o va accepta și pe aceasta. Încercați din nou!"
		},
		{
			text: "Veți evita încărcarea întregului dataset în memorie.",
			explain: "Corect! Fiecare batch de texte va fi eliberat din memorie atunci când iterați, iar câștigul va fi vizibil mai ales dacă utilizați 🤗 Datasets pentru a stoca textele.",
			correct: true
		},
		{
			text: "Acest lucru va permite bibliotecii 🤗 Tokenizers să utilizeze multiprocessing.",
			explain: "Nu, oricum va folosi multiprocessing."
		},
        {
			text: "Tokenizerul pe care îl vei antrena va genera texte mai bune.",
			explain: "Tokenizerul nu generează text - îl confundați cu un model lingvistic?"
		}
	]}
/>

### 3. Care sunt avantajele utilizării unui tokenizer "rapid"?

<Question
	choices={[
		{
			text: "Acesta poate procesa inputuri mai rapid decât un tokenizator lent atunci când faci batch mai multor inputuri împreună.",
			explain: "Corect! Datorită paralelismului implementat în Rust, acesta va fi mai rapid pe batch-uri de inputuri. La ce alt beneficiu te poți gândi?",
			correct: true
		},
		{
			text: "Tokenizerele rapide tokenizează întotdeauna mai rapid decât omologii lor lenți.",
			explain: "Un tokenizer rapid poate fi de fapt mai lent atunci când îi dai doar unul sau foarte puține texte, deoarece nu poate utiliza paralelismul."
		},
		{
			text: "Poate aplica padding și truncation.",
			explain: "Adevărat, dar și tokenizerele lente fac asta."
		},
        {
			text: "Acesta are unele caracteristici suplimentare care vă permit să asociați tokenii cu intervalul de text care le-a creat.",
			explain: "Într-adevăr - acestea se numesc offset mappings. Totuși, acesta nu este singurul avantaj.",
			correct: true
		}
	]}
/>

### 4. Cum tratează pipelineul `token-classification` entitățile care se întind pe mai mulți tokeni?

<Question
	choices={[
		{
			text: "Entitățile cu același label sunt merged într-o singură entitate.",
			explain: "Explicația aceasta e prea simplă. Încearcă din nou!"
		},
		{
			text: "Există un label pentru începutul unei entități și un label pentru continuarea unei entități.",
			explain: "Corect!",
			correct: true
		},
		{
			text: "Într-un cuvânt dat, atâta timp cât primul token are labelul entității, întregul cuvânt este considerat etichetat cu entitatea respectivă.",
			explain: "Aceasta este o strategie de gestionare a entităților. Ce alte răspunsuri se aplică aici?",
			correct: true
		},
        {
			text: "Atunci când un token are labelul unei entități date, orice alt token următor cu aceeași label este considerat parte a aceleiași entități, cu excepția cazului în care este etichetat ca fiind începutul unei noi entități.",
			explain: "Acesta este cel mai comun mod de a grupa entitățile împreună - deși nu este singurul răspuns corect.",
			correct: true
		}
	]}
/>

### 5. Cum gestionează pipelineul `question-answering` contextele lungi?

<Question
	choices={[
		{
			text: "De fapt, nu o face, deoarece trunchiază contextul lung la lungimea maximă acceptată de model.",
			explain: "Există un truc pe care îl poți folosi pentru a gestiona contextele lungi. Îți amintești care este acesta?"
		},
		{
			text: "Acesta împarte contextul în mai multe părți și calculează media rezultatelor obținute.",
			explain: "Nu, nu ar avea sens să se facă o medie a rezultatelor, deoarece unele părți ale contextului nu vor include răspunsul."
		},
		{
			text: "Acesta împarte contextul în mai multe părți (cu suprapuneri) și găsește scorul maxim pentru un răspuns în fiecare parte.",
			explain: "Acesta este răspunsul corect!",
			correct: true
		},
        {
			text: "Acesta împarte contextul în mai multe părți (fără suprapunere, pentru eficiență) și găsește scorul maxim pentru un răspuns în fiecare parte.",
			explain: "Nu, acesta include o anumită suprapunere între părți pentru a evita situația în care răspunsul ar fi împărțit în două părți."
		}
	]}
/>

### 6. Ce este normalizarea?

<Question
	choices={[
		{
			text: "Este vorba de orice curățare pe care tokenizerul o efectuează asupra textelor în etapele inițiale.",
			explain: "Corect - de exemplu, ar putea implica eliminarea accentelor sau a spațiilor, sau scrierea cu mijuscule a inputurilor.",
			correct: true
		},
		{
			text: "Este o tehnică de augmentare a datelor care constă în a face textul mai normal prin eliminarea cuvintelor rare.",
			explain: "Este incorect! Încearcă din nou."
		},
		{
			text: "Acesta este ultimul pas de post-procesare în care tokenizerul adaugă tokenii speciali.",
			explain: "Această etapă se numește pur și simplu post-procesare."
		},
        {
			text: "Este atunci când embeddingurile sunt realizate cu media 0 și standard deviation 1, prin scăderea mediei și împărțirea la std.",
			explain: "Acest proces se numește de obicei normalizare atunci când este aplicat valorilor pixelilor în computer vision, dar nu este ceea ce înseamnă normalizare în NLP."
		}
	]}
/>

### 7. Ce este pre-tokenizarea pentru un subword tokenizer?

<Question
	choices={[
		{
			text: "Acesta este pasul dinaintea tokenizării, în care se aplică augmentarea datelor (cum ar fi mascarea aleatorie).",
			explain: "Nu, acest pas face parte din preprocesare."
		},
		{
			text: "Este pasul de dinaintea tokenizării, în care operațiile de curățare dorite sunt aplicate textului.",
			explain: "Nu, acesta este pasul de normalizare."
		},
		{
			text: "Acesta este pasul dinaintea aplicării modelul de tokenizer, pentru a împărți inputul în cuvinte.",
			explain: "Acesta este răspunsul corect!",
			correct: true
		},
        {
			text: "Acesta este pasul dinaintea aplicării modelul de  tokenizer, pentru a împărți inputul în tokenuri.",
			explain: "Nu, împărțirea în tokenuri este sarcina modelul de tokenizer."
		}
	]}
/>

### 8. Selectați propozițiile care se aplică modelului de tokenizare BPE.

<Question
	choices={[
		{
			text: "BPE este un algoritm de tokenizare a subcuvintelor care începe cu un vocabular mic și învață reguli de merge.",
			explain: "Acesta este într-adevăr cazul!",
			correct: true
		},
		{
			text: "BPE este un algoritm de tokenizare a subcuvintelor care începe cu un vocabular mare și elimină progresiv tokenii din acesta.",
			explain: "Nu, aceasta este abordarea adoptată de un alt algoritm de tokenizare."
		},
		{
			text: "Tokenizerele BPE învață regulile de merge prin mergeul perechii de tokeni care este cea mai frecventă.",
			explain: "Acest lucru este corect!",
			correct: true
		},
		{
			text: "Un tokenizer BPE învață o regulă de merge prin mergeul perechii de tokeni care maximizează un scor care privilegiază perechile frecvente cu părți individuale mai puțin frecvente.",
			explain: "Nu, aceasta este strategia aplicată de un alt algoritm de tokenizare."
		},
		{
			text: "BPE tokenizează cuvintele în subcuvinte prin divizarea lor în caractere și apoi prin aplicarea regulilor de merge.",
			explain: "Acest lucru este corect!",
			correct: true
		},
		{
			text: "BPE tokenizează cuvintele în subcuvinte prin găsirea celui mai lung subcuvânt de la început care se află în vocabular, apoi repetă procesul pentru restul textului.",
			explain: "Nu, acesta este un alt mod de a face lucrurile al algoritmului de tokenizare."
		},
	]}
/>

### 9. Selectați propozițiile care se aplică modelului de tokenizare WordPiece.

<Question
	choices={[
		{
			text: "WordPiece este un algoritm de tokenizare a subcuvintelor care începe cu un vocabular mic și învață reguli de merge.",
			explain: "Acesta este într-adevăr cazul!",
			correct: true
		},
		{
			text: "WordPiece este un algoritm de tokenizare a subcuvintelor care începe cu un vocabular mare și elimină progresiv tokenii din acesta.",
			explain: "Nu, aceasta este abordarea adoptată de un alt algoritm de tokenizare."
		},
		{
			text: "Tokenizerele WordPiece învață regulile de merge prin mergeul perechii de tokeni care este cea mai frecventă.",
			explain: "Nu, aceasta este abordarea adoptată de un alt algoritm de tokenizare!",
		},
		{
			text: "Un tokenizer WordPiece învață o regulă de merge prin mergeul perechii de tokeni care maximizează un scor care privilegiază perechile frecvente cu părți individuale mai puțin frecvente.",
			explain: "Acest lucru este corect!",
			correct: true
		},
		{
			text: "WordPiece tokenizează cuvintele în subcuvinte prin găsirea celei mai probabile segmentări în tokeni, în conformitate cu modelul.",
			explain: "Nu, acesta este modul în care funcționează un alt algoritm de tokenizare."
		},
		{
			text: "WordPiece tokenizează cuvintele în subcuvinte prin găsirea celui mai lung subcuvânt de la început care se află în vocabular, apoi repetă procesul pentru restul textului.",
			explain: "Da, acesta este modul în care WordPiece procedează pentru encoding.",
			correct: true
		},
	]}
/>

### 10. Selectați propozițiile care se aplică modelului de tokenizare Unigram.

<Question
	choices={[
		{
			text: "Unigram este un algoritm de tokenizare a subcuvintelor care începe cu un vocabular mic și învață reguli de merge.",
			explain: "Nu, aceasta este abordarea adoptată de un alt algoritm de tokenizare."
		},
		{
			text: "Unigram este un algoritm de tokenizare a subcuvintelor care începe cu un vocabular mare și elimină progresiv tokenurile din el.",
			explain: "Acest lucru este corect!",
			correct: true
		},
		{
			text: "Unigram își adaptează vocabularul prin minimizarea unei pierderi calculate pe întregul corpus.",
			explain: "Acest lucru este corect!",
			correct: true
		},
		{
			text: "Unigram își adaptează vocabularul prin păstrarea celor mai frecvente subcuvinte.",
			explain: "Nu, acest lucru este incorect."
		},
		{
			text: "Unigram tokenizează cuvintele în subcuvinte prin găsirea celei mai probabile segmentări în tokenuri, conform modelului.",
			explain: "Acest lucru este corect!",
			correct: true
		},
		{
			text: "Unigram tokenizează cuvintele în subcuvinte prin divizarea lor în caractere, apoi aplică regulile de merge.",
			explain: "Nu, acesta este modul în care funcționează un alt algoritm de tokenizare."
		},
	]}
/>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter6/10.mdx" />

### Construirea unui tokenizer, bloc cu bloc[[building-a-tokenizer-block-by-block]]
https://huggingface.co/learn/course/ro/chapter6/8.md

# Construirea unui tokenizer, bloc cu bloc[[building-a-tokenizer-block-by-block]]

<CourseFloatingBanner chapter={6}
  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/chapter6/section8.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter6/section8.ipynb"},
]} />

După cum am văzut în secțiunile anterioare, tokenizarea cuprinde mai multe etape:

- Normalizare (orice curățare a textului care este considerată necesară, cum ar fi eliminarea spațiilor sau a accentelor, normalizarea Unicode etc.)
- Pre-tokenizarea (împărțirea inputului în cuvinte)
- Rularea inputului prin model (utilizarea cuvintelor pre-tokenizate pentru a produce o secvență de tokeni)
- Post-procesare (adăugarea tokenilor speciali ai tokenizerului, generarea attention maskului și a ID-urilor de tip token)

Ca un reminder, iată o altă perspectivă asupra procesului general:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/tokenization_pipeline.svg" alt="The tokenization pipeline.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/tokenization_pipeline-dark.svg" alt="The tokenization pipeline.">
</div>

Biblioteca 🤗 Tokenizers a fost construită pentru a oferi mai multe opțiuni pentru fiecare dintre acești pași, pe care le puteți amesteca și combina împreună. În această secțiune vom vedea cum putem construi un tokenizer de la zero, spre deosebire de antrenarea unui tokenizer nou dintr-unul vechi, așa cum am făcut în [secțiunea 2](/course/chapter6/2). Veți putea apoi să construiți orice fel de tokenizer la care vă puteți gândi!

<Youtube id="MR8tZm5ViWU"/>

Mai exact, biblioteca este construită în jurul unei clase centrale `Tokenizer` cu building grupate în submodule:

- `normalizers` conține toate tipurile posibile de `Normalizer` pe care le puteți folosi (lista completă [aici](https://huggingface.co/docs/tokenizers/api/normalizers)).
- `pre_tokenizers` conține toate tipurile de `PreTokenizer` pe care le poți folosi(lista completă [aici](https://huggingface.co/docs/tokenizers/api/pre-tokenizers)).
- `models` conține diferitele tipuri de `Model` pe care le puteți folosi, precum `BPE`, `WordPiece` și `Unigram` (lista completă [aici](https://huggingface.co/docs/tokenizers/api/models)).
- `trainers` conține toate tipurile diferite de `Trainer` pe care le puteți folosi pentru a vă antrena modelul pe un corpus (unul pentru fiecare tip de model; lista completă [aici](https://huggingface.co/docs/tokenizers/api/trainers)).
- `post_processors` conține diferitele tipuri de `PostProcessor` pe care le puteți utiliza (lista completă [aici](https://huggingface.co/docs/tokenizers/api/post-processors)).
- `decoders` conține diferitele tipuri de `Decoder` pe care le puteți utiliza pentru a decoda rezultatele tokenizării (lista completă [aici](https://huggingface.co/docs/tokenizers/components#decoders)).

Puteți găsi întreaga listă de blocuri [aici](https://huggingface.co/docs/tokenizers/components).

## Obținerea unui corpus[[acquiring-a-corpus]]

Pentru a antrena noul nostru tokenizer, vom utiliza un corpus mic de text (astfel încât exemplele să ruleze rapid). Pașii pentru obținerea corpusului sunt similari cu cei pe care i-am urmat la [începutul acestui capitol](/course/chapter6/2), dar de data aceasta vom utiliza datasetul [WikiText-2](https://huggingface.co/datasets/wikitext):

```python
from datasets import load_dataset

dataset = load_dataset("wikitext", name="wikitext-2-raw-v1", split="train")


def get_training_corpus():
    for i in range(0, len(dataset), 1000):
        yield dataset[i : i + 1000]["text"]
```

Funcția `get_training_corpus()` este un generator care va produce batch-uri de 1 000 de texte, pe care le vom utiliza pentru a antrena tokenizerul.

🤗 Tokenizers pot fi, de asemenea, antrenate direct pe fișiere text. Iată cum putem genera un fișier text care să conțină toate textele/inputurile din WikiText-2 pe care le putem utiliza local:

```python
with open("wikitext-2.txt", "w", encoding="utf-8") as f:
    for i in range(len(dataset)):
        f.write(dataset[i]["text"] + "\n")
```

În continuare vă vom arăta cum să vă construiți propriile tokenizere BERT, GPT-2 și XLNet, bloc cu bloc. Acest lucru ne va oferi un exemplu pentru fiecare dintre cei trei algoritmi principali de tokenizare: WordPiece, BPE și Unigram. Să începem cu BERT!

## Construirea unui tokenizator WordPiece de la zero[[building-a-wordpiece-tokenizer-from-scratch]]

Pentru a construi un tokenizer cu biblioteca 🤗 Tokenizers, începem prin a inițializa un obiect `Tokenizer` cu un `model`, apoi îi setăm atributele `normalizer`, `pre_tokenizer`, `post_processor` și `decoder` la valorile dorite.

Pentru acest exemplu, vom crea un `Tokenizer` cu un model WordPiece:

```python
from tokenizers import (
    decoders,
    models,
    normalizers,
    pre_tokenizers,
    processors,
    trainers,
    Tokenizer,
)

tokenizer = Tokenizer(models.WordPiece(unk_token="[UNK]"))
```

Trebuie să specificăm `unk_token` astfel încât modelul să știe ce să returneze atunci când întâlnește caractere necunoscute. Alte argumente pe care le putem seta aici includ `vocab` al modelului nostru (vom antrena modelul, deci nu este nevoie să setăm acest lucru) și `max_input_chars_per_word`, care specifică o lungime maximă pentru fiecare cuvânt (cuvintele mai lungi decât valoarea trecută vor fi divizate).

Primul pas al tokenizării este normalizarea, așa că să începem cu aceasta. Deoarece BERT este utilizat pe scară largă, există un `BertNormalizer` cu opțiunile clasice pe care le putem seta pentru BERT: `lowercase` și `strip_accents`, care se explică de la sine; `clean_text` pentru a elimina toate caracterele de control și a înlocui spațiile repetate cu unul singur; și `handle_chinese_chars`, care plasează spații în jurul caracterelor chinezești. Pentru a replica tokenizerul `bert-base-uncased`, putem seta doar acest normalizator:

```python
tokenizer.normalizer = normalizers.BertNormalizer(lowercase=True)
```

În general, atunci când construiți un nou tokenizer, nu veți avea acces la un normalizator atât de util, deja implementat în biblioteca 🤗 Tokenizers - așa că să vedem cum să creăm manual normalizatorul BERT. Biblioteca oferă un normalizator `Lowercase` și un normalizator `StripAccents` și puteți compune mai multe normalizatoare folosind un `Sequence`:

```python
tokenizer.normalizer = normalizers.Sequence(
    [normalizers.NFD(), normalizers.Lowercase(), normalizers.StripAccents()]
)
```

De asemenea, folosim un normalizator Unicode `NFD`, deoarece în caz contrar normalizatorul `StripAccents` nu va recunoaște corect caracterele accentuate și astfel nu le va elimina.

După cum am mai văzut, putem folosi metoda `normalize_str()` a `normalizer` pentru a verifica efectele pe care le are asupra unui text dat:

```python
print(tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))
```

```python out
hello how are u?
```

<Tip>

**Pentru a merge mai departe** Dacă testați cele două versiuni ale normalizatorilor anteriori pe un șir care conține caracterul Unicode `u"\u0085"` veți observa cu siguranță că acești doi normalizatori nu sunt exact echivalenți.
Pentru a nu complica prea mult versiunea cu `normalizers.Sequence` , nu am inclus înlocuirile Regex pe care `BertNormalizer` le cere atunci când argumentul `clean_text` este setat la `True` - care este comportamentul implicit. Dar nu vă faceți griji: este posibil să obțineți exact aceeași normalizare fără a utiliza utilul `BertNormalizer` prin adăugarea a două `normalizers.Replace` la secvența normalizers.


</Tip>

Urmează etapa de pre-tokenizare. Din nou, există un `BertPreTokenizer` pre-construit pe care îl putem utiliza:

```python
tokenizer.pre_tokenizer = pre_tokenizers.BertPreTokenizer()
```

Sau îl putem construi de la zero:

```python
tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()
```

Rețineți că pre-tokenizatorul `Whitespace` separă spațiul și toate caracterele care nu sunt litere, cifre sau caracterul underscore, deci tehnic separă spațiul și punctuația:

```python
tokenizer.pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
```

```python out
[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
 ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]
```

Dacă doriți să separați doar spațiile, ar trebui să utilizați în schimb pre-tokenizerul `WhitespaceSplit`:

```python
pre_tokenizer = pre_tokenizers.WhitespaceSplit()
pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
```

```python out
[("Let's", (0, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre-tokenizer.', (14, 28))]
```

Ca și în cazul normalizatorilor, puteți utiliza un `Sequence` pentru a compune mai mulți pre-tokenizeri:

```python
pre_tokenizer = pre_tokenizers.Sequence(
    [pre_tokenizers.WhitespaceSplit(), pre_tokenizers.Punctuation()]
)
pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
```

```python out
[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
 ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]
```

Următorul pas în pipelineul de tokenizare este rularea inputurilor prin model. Am specificat deja modelul nostru în inițializare, dar mai trebuie să îl antrenăm, ceea ce va necesita un `WordPieceTrainer`. Principalul lucru de reținut atunci când inițializați un trainer în 🤗 Tokenizers este că trebuie să îi transmiteți toți tokenii speciali pe care intenționați să îi utilizați - în caz contrar, acesta nu le va adăuga la vocabular, deoarece acestea nu se află în corpusul de antrenare:

```python
special_tokens = ["[UNK]", "[PAD]", "[CLS]", "[SEP]", "[MASK]"]
trainer = trainers.WordPieceTrainer(vocab_size=25000, special_tokens=special_tokens)
```

Pe lângă specificarea `vocab_size` și `special_tokens`, putem seta `min_frequency` (numărul de ori în care un simbol trebuie să apară pentru a fi inclus în vocabular) sau putem schimba `continuing_subword_prefix` (dacă dorim să folosim ceva diferit de `##`).

Pentru a antrena modelul nostru folosind iteratorul pe care l-am definit anterior, trebuie doar să executăm această comandă:

```python
tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
```

De asemenea, putem utiliza fișiere text pentru a ne antrena tokenizerul, care ar arăta astfel (în prealabil, reinițializăm modelul cu un `WordPiece` gol):

```python
tokenizer.model = models.WordPiece(unk_token="[UNK]")
tokenizer.train(["wikitext-2.txt"], trainer=trainer)
```

În ambele cazuri, putem apoi testa tokenizerul pe un text prin apelarea metodei `encode()`:

```python
encoding = tokenizer.encode("Let's test this tokenizer.")
print(encoding.tokens)
```

```python out
['let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.']
```

`encoding`-ul obținut este un `Encoding`, care conține toate rezultatele necesare ale tokenizerului în diferitele sale atribute: `ids`, `type_ids`, `tokens`, `offsets`, `attention_mask`, `special_tokens_mask`, și `overflowing`.

Ultimul pas în pipelineul de tokenizare este postprocesarea. Trebuie să adăugăm tokenul `[CLS]` la început și tokenul `[SEP]` la sfârșit (sau după fiecare propoziție, dacă avem o pereche de propoziții). Vom folosi un `TemplateProcessor` pentru aceasta, dar mai întâi trebuie să cunoaștem ID-urile tokenilor `[CLS]` și `[SEP]` din vocabular:

```python
cls_token_id = tokenizer.token_to_id("[CLS]")
sep_token_id = tokenizer.token_to_id("[SEP]")
print(cls_token_id, sep_token_id)
```

```python out
(2, 3)
```

Pentru a scrie templateul pentru `TemplateProcessor`, trebuie să specificăm cum să tratăm o singură propoziție și o pereche de propoziții. Pentru ambele, scriem tokeni speciali pe care dorim să îi folosim; prima (sau singura) propoziție este reprezentată de `$A`, în timp ce a doua propoziție (dacă facem encoding unei perechi) este reprezentată de `$B`. Pentru fiecare dintre acestea (tokeni speciali și propoziții), specificăm și ID-ul tipului de token corespunzător după două puncte.

Modelul clasic BERT este astfel definit după cum urmează:

```python
tokenizer.post_processor = processors.TemplateProcessing(
    single=f"[CLS]:0 $A:0 [SEP]:0",
    pair=f"[CLS]:0 $A:0 [SEP]:0 $B:1 [SEP]:1",
    special_tokens=[("[CLS]", cls_token_id), ("[SEP]", sep_token_id)],
)
```

Rețineți că trebuie să transmitem ID-urile tokenilor speciali, astfel încât tokenizerul să le poată converti corect în ID-urile lor.

Odată ce acest lucru este adăugat, revenind la exemplul nostru anterior vom obține:

```python
encoding = tokenizer.encode("Let's test this tokenizer.")
print(encoding.tokens)
```

```python out
['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.', '[SEP]']
```

Și pe o pereche de propoziții, obținem rezultatul corect:

```python
encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences.")
print(encoding.tokens)
print(encoding.type_ids)
```

```python out
['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '...', '[SEP]', 'on', 'a', 'pair', 'of', 'sentences', '.', '[SEP]']
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
```

Aproape am terminat de construit acest tokenizer de la zero - ultimul pas este să includem un decodor:

```python
tokenizer.decoder = decoders.WordPiece(prefix="##")
```

Haideți să-l testăm pe `encoding`-ul nostru anterior:

```python
tokenizer.decode(encoding.ids)
```

```python out
"let's test this tokenizer... on a pair of sentences."
```

Grozav! Putem salva tokenizatorul nostru într-un singur fișier JSON, astfel:

```python
tokenizer.save("tokenizer.json")
```

Apoi putem reîncărca acel fișier într-un obiect `Tokenizer` cu metoda `from_file()`:

```python
new_tokenizer = Tokenizer.from_file("tokenizer.json")
```

Pentru a utiliza acest tokenizer în 🤗 Transformers, trebuie să îl încorporăm în `PreTrainedTokenizerFast`. Putem fie să folosim clasa generică, fie, dacă tokenizerul nostru corespunde unui model existent, să folosim clasa respectivă (aici, `BertTokenizerFast`). Dacă aplicați această lecție pentru a construi un tokenizer nou, va trebui să utilizați prima opțiune.

Pentru a include tokenizatorul într-un `PreTrainedTokenizerFast`, putem fie să transmitem tokenizerul construit ca `tokenizer_object`, fie să transmitem fișierul tokenizerului salvat ca `tokenizer_file`. Cel mai important lucru de reținut este că trebuie să setăm manual toți tokenii speciali, deoarece această clasă nu poate deduce din obiectul `tokenizer` care token este tokenul mască, tokenul `[CLS]`, etc.:

```python
from transformers import PreTrainedTokenizerFast

wrapped_tokenizer = PreTrainedTokenizerFast(
    tokenizer_object=tokenizer,
    # tokenizer_file="tokenizer.json", # You can load from the tokenizer file, alternatively
    unk_token="[UNK]",
    pad_token="[PAD]",
    cls_token="[CLS]",
    sep_token="[SEP]",
    mask_token="[MASK]",
)
```

Dacă utilizați o clasă specifică de tokenizer (cum ar fi `BertTokenizerFast`), va trebui să specificați doar tokenii speciali care sunt diferiți de cei impliciți (aici, niciunul):

```python
from transformers import BertTokenizerFast

wrapped_tokenizer = BertTokenizerFast(tokenizer_object=tokenizer)
```

Apoi puteți utiliza acest tokenizer ca orice alt tokenizer 🤗 Transformers. Îl puteți salva cu metoda `save_pretrained()` sau îl puteți încărca în Hub cu metoda `push_to_hub()`.

Acum că am văzut cum să construim un tokenizer WordPiece, hai să facem același lucru pentru un tokenizer BPE. Vom merge un pic mai repede, deoarece cunoașteți toți pașii, și vom evidenția doar diferențele.

## Construirea unui tokenizer BPE de la zero[[building-a-bpe-tokenizer-from-scratch]]

Să construim acum un tokenizer GPT-2. Ca și pentru tokenizer BERT, începem prin inițializarea unui `Tokenizer` cu un model BPE:

```python
tokenizer = Tokenizer(models.BPE())
```

De asemenea, la fel ca în cazul BERT, am putea inițializa acest model cu un vocabular, dacă am avea unul (în acest caz, ar trebui să oferim `vocab` și `merges`), dar din moment ce vom antrena de la zero, nu avem nevoie să facem acest lucru. De asemenea, nu trebuie să specificăm un `unk_token` deoarece GPT-2 utilizează BPE la nivel de bytes, care nu necesită acest lucru.

GPT-2 nu utilizează un normalizator, deci sărim peste acest pas și trecem direct la pre-tokenizare:

```python
tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)
```

Opțiunea pe care am adăugat-o aici la `ByteLevel` nu este pentru a adăuga un spațiu unei propoziții (care este implicit în caz contrar). Putem arunca o privire la pre-tokenizarea unui text exemplu ca înainte:

```python
tokenizer.pre_tokenizer.pre_tokenize_str("Let's test pre-tokenization!")
```

```python out
[('Let', (0, 3)), ("'s", (3, 5)), ('Ġtest', (5, 10)), ('Ġpre', (10, 14)), ('-', (14, 15)),
 ('tokenization', (15, 27)), ('!', (27, 28))]
```

Urmează modelul, care are nevoie de antrenare. Pentru GPT-2, singurul token special este tokenul de sfârșit de text:

```python
trainer = trainers.BpeTrainer(vocab_size=25000, special_tokens=["<|endoftext|>"])
tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
```

Ca și în cazul `WordPieceTrainer`, precum și `vocab_size` și `special_tokens`, putem specifica `min_frequency` dacă dorim, sau dacă avem un sufix de sfârșit de cuvânt (cum ar fi `</w>`), îl putem seta cu `end_of_word_suffix`.

Acest tokenizer poate fi antrenat și pe fișiere text:

```python
tokenizer.model = models.BPE()
tokenizer.train(["wikitext-2.txt"], trainer=trainer)
```

Să aruncăm o privire la tokenizarea unui exemplu de text:

```python
encoding = tokenizer.encode("Let's test this tokenizer.")
print(encoding.tokens)
```

```python out
['L', 'et', "'", 's', 'Ġtest', 'Ġthis', 'Ġto', 'ken', 'izer', '.']
```

Aplicăm postprocesarea la nivel de bytes pentru tokenizerul GPT-2 după cum urmează:

```python
tokenizer.post_processor = processors.ByteLevel(trim_offsets=False)
```

Opțiunea `trim_offsets = False` indică post-procesorului că ar trebui să lăsăm offseturile tokenilor care încep cu "Ġ" așa cum sunt: în acest fel, începutul offseturilor va indica spațiul dinaintea cuvântului, nu primul caracter al cuvântului (deoarece spațiul face parte din punct de vedere tehnic din token). Să aruncăm o privire asupra rezultatului cu textul pe căruia tocmai i-am făcut encoding, unde `'Ġtest'` este tokenul de la indexul 4:

```python
sentence = "Let's test this tokenizer."
encoding = tokenizer.encode(sentence)
start, end = encoding.offsets[4]
sentence[start:end]
```

```python out
' test'
```

În cele din urmă, adăugăm un decoder la nivel de bytes:

```python
tokenizer.decoder = decoders.ByteLevel()
```

și putem verifica de două ori dacă funcționează corect:

```python
tokenizer.decode(encoding.ids)
```

```python out
"Let's test this tokenizer."
```

Grozav! Acum că am terminat, putem salva tokenizatorul ca înainte și îl putem încorpora într-un `PreTrainedTokenizerFast` sau `GPT2TokenizerFast` dacă dorim să îl folosim în 🤗 Transformers:

```python
from transformers import PreTrainedTokenizerFast

wrapped_tokenizer = PreTrainedTokenizerFast(
    tokenizer_object=tokenizer,
    bos_token="<|endoftext|>",
    eos_token="<|endoftext|>",
)
```

or:

```python
from transformers import GPT2TokenizerFast

wrapped_tokenizer = GPT2TokenizerFast(tokenizer_object=tokenizer)
```

Ca un ultim exemplu, vă vom arăta cum să construiți un tokenizer Unigram de la zero.

## Construirea unui tokenizer Unigram de la zero[[building-a-unigram-tokenizer-from-scratch]]

Să construim acum un tokenizer XLNet. Ca și în cazul tokenizerelor anterioare, începem prin inițializarea unui `Tokenizer` cu un model Unigram:

```python
tokenizer = Tokenizer(models.Unigram())
```

Din nou, am putea inițializa acest model cu un vocabular, dacă am avea unul.

Pentru normalizare, XLNet utilizează câteva înlocuiri (care provin din SentencePiece):

```python
from tokenizers import Regex

tokenizer.normalizer = normalizers.Sequence(
    [
        normalizers.Replace("``", '"'),
        normalizers.Replace("''", '"'),
        normalizers.NFKD(),
        normalizers.StripAccents(),
        normalizers.Replace(Regex(" {2,}"), " "),
    ]
)
```

Acest lucru înlocuitește <code>``</code> și <code>''</code> cu <code>"</code> și orice secvență de două sau mai multe spații cu un singur spațiu, precum și ștergerea accentelor în textele ce trebuie tokenizate.

Pre-tokenizerul care trebuie utilizat pentru orice tokenizer SentencePiece este `Metaspace`:

```python
tokenizer.pre_tokenizer = pre_tokenizers.Metaspace()
```

Putem arunca o privire la pre-tokenizarea unui exemplu de text ca mai înainte:

```python
tokenizer.pre_tokenizer.pre_tokenize_str("Let's test the pre-tokenizer!")
```

```python out
[("▁Let's", (0, 5)), ('▁test', (5, 10)), ('▁the', (10, 14)), ('▁pre-tokenizer!', (14, 29))]
```

Urmează modelul, care are nevoie de antrenare. XLNet are destul de mulți tokeni speciali:

```python
special_tokens = ["<cls>", "<sep>", "<unk>", "<pad>", "<mask>", "<s>", "</s>"]
trainer = trainers.UnigramTrainer(
    vocab_size=25000, special_tokens=special_tokens, unk_token="<unk>"
)
tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
```

Un argument foarte important care nu trebuie uitat pentru `UnigramTrainer` este `unk_token`. Putem trece și alte argumente specifice algoritmului Unigram, cum ar fi `shrinking_factor` pentru fiecare pas în care eliminăm tokeni (valoarea implicită este 0,75) sau `max_piece_length` pentru a specifica lungimea maximă a unui token dat (valoarea implicită este 16).

Acest tokenizer poate fi antrenat și pe fișiere text:

```python
tokenizer.model = models.Unigram()
tokenizer.train(["wikitext-2.txt"], trainer=trainer)
```

Să aruncăm o privire la tokenizarea unui exemplu de text:

```python
encoding = tokenizer.encode("Let's test this tokenizer.")
print(encoding.tokens)
```

```python out
['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.']
```

O particularitate a XLNet este că pune tokenul `<cls>` la sfârșitul propoziției, cu un ID de tip 2 (pentru a-l distinge de ceilalți tokeni). Ca urmare, este făcut padding la stânga. Putem trata toți tokenii speciali și ID-urile de tip ale tokenilor cu un template, ca în cazul BERT, dar mai întâi trebuie să obținem ID-urile tokenilor `<cls>` și `<sep>`:

```python
cls_token_id = tokenizer.token_to_id("<cls>")
sep_token_id = tokenizer.token_to_id("<sep>")
print(cls_token_id, sep_token_id)
```

```python out
0 1
```

Templateul arată astfel:

```python
tokenizer.post_processor = processors.TemplateProcessing(
    single="$A:0 <sep>:0 <cls>:2",
    pair="$A:0 <sep>:0 $B:1 <sep>:1 <cls>:2",
    special_tokens=[("<sep>", sep_token_id), ("<cls>", cls_token_id)],
)
```

Și putem testa că funcționează prin codificarea unei perechi de propoziții:

```python
encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences!")
print(encoding.tokens)
print(encoding.type_ids)
```

```python out
['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.', '.', '.', '<sep>', '▁', 'on', '▁', 'a', '▁pair', 
  '▁of', '▁sentence', 's', '!', '<sep>', '<cls>']
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2]
```

În cele din urmă, adăugăm un decoder `Metaspace`:

```python
tokenizer.decoder = decoders.Metaspace()
```

și am terminat cu acest tokenizer! Putem salva tokenizerul ca înainte și îl putem încorpora într-un `PreTrainedTokenizerFast` sau `XLNetTokenizerFast` dacă dorim să îl folosim în 🤗 Transformers. Un lucru de reținut atunci când se utilizează `PreTrainedTokenizerFast` este că, pe lângă tokenii speciali, trebuie să spunem bibliotecii 🤗 Transformers să facă padding la stânga:

```python
from transformers import PreTrainedTokenizerFast

wrapped_tokenizer = PreTrainedTokenizerFast(
    tokenizer_object=tokenizer,
    bos_token="<s>",
    eos_token="</s>",
    unk_token="<unk>",
    pad_token="<pad>",
    cls_token="<cls>",
    sep_token="<sep>",
    mask_token="<mask>",
    padding_side="left",
)
```

Sau alternativ:

```python
from transformers import XLNetTokenizerFast

wrapped_tokenizer = XLNetTokenizerFast(tokenizer_object=tokenizer)
```

Acum că ați văzut cum sunt utilizate diferitele blocuri de construcție pentru a construi tokenizeri existenți, ar trebui să puteți scrie orice tokenizer doriți cu biblioteca 🤗 Tokenizers și să îl puteți utiliza în 🤗 Transformers.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter6/8.mdx" />

### Normalizare și pre-tokenizare[[normalization-and-pre-tokenization]]
https://huggingface.co/learn/course/ro/chapter6/4.md

# Normalizare și pre-tokenizare[[normalization-and-pre-tokenization]]

<CourseFloatingBanner chapter={6}
  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/chapter6/section4.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter6/section4.ipynb"},
]} />

Înainte de a analiza în profunzime cei mai comuni trei algoritmi de subword tokenization utilizați cu modelele Transformer (Byte-Pair Encoding [BPE], WordPiece și Unigram), vom arunca mai întâi o privire la preprocesarea pe care fiecare tokenizer o aplică textului. Iată o prezentare generală a etapelor din pipelineul de tokenizare:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/tokenization_pipeline.svg" alt="The tokenization pipeline.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/tokenization_pipeline-dark.svg" alt="The tokenization pipeline.">
</div>

Înainte de a împărți un text în subtokens (în conformitate cu modelul său), tokenizerul efectuează doi pași: _normalization__ și _pre-tokenization_.

## Normalization[[normalization]]

<Youtube id="4IIC2jI9CaU"/>

Etapa de normalizare implică o curățare generală, cum ar fi eliminarea spațiilor inutile, a le face minuscule, și/sau ștergerea accentelor. Dacă sunteți familiarizat cu [Unicode normalization](http://www.unicode.org/reports/tr15/) (cum ar fi NFC sau NFKC), acest lucru poate fi aplicat și de tokenizer.

`Tokenizer`-ul 🤗 Transformers are un atribut numit `backend_tokenizer` care oferă acces la tokenizatorul de bază din biblioteca 🤗 Tokenizers:

```py
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
print(type(tokenizer.backend_tokenizer))
```

```python out
<class 'tokenizers.Tokenizer'>
```

Atributul `normalizer` al obiectului `tokenizer` are o metodă `normalize_str()` pe care o putem folosi pentru a vedea cum se realizează normalizarea:

```py
print(tokenizer.backend_tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))
```

```python out
'hello how are u?'
```

În acest exemplu, din moment ce am ales checkpointul `bert-base-uncased`, normalizarea a aplicat scrierea cu minusculă și a eliminat accentele.

<Tip>

✏️ **Încercați!** Încărcați un tokenizer din checkpointul `bert-base-cased` și treceți-i același exemplu. Care sunt principalele diferențe pe care le puteți observa între versiunile cased și uncased ale tokenizerului?

</Tip>

## Pre-tokenization[[pre-tokenization]]

<Youtube id="grlLV8AIXug"/>

După cum vom vedea în secțiunile următoare, un tokenizer nu poate fi antrenat doar pe text raw. În schimb, trebuie mai întâi să împărțim textele în entități mici, cum ar fi cuvintele. Aici intervine etapa de pre-tokenizare. După cum am văzut în [Capitolul 2](/course/chapter2), un tokenizer bazat pe cuvinte poate împărți pur și simplu un text raw în cuvinte pe baza spațiului și a punctuației. Aceste cuvinte vor fi limitele subtokenurilor pe care tokenizerul le poate învăța în timpul instruirii sale.

Pentru a vedea cum un tokenizator rapid efectuează pre-tokenizarea, putem utiliza metoda `pre_tokenize_str()` a atributului `pre_tokenizer` al obiectului `tokenizer`:

```py
tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are  you?")
```

```python out
[('Hello', (0, 5)), (',', (5, 6)), ('how', (7, 10)), ('are', (11, 14)), ('you', (16, 19)), ('?', (19, 20))]
```

Observați cum tokenizatorul ține deja evidența offseturilor, acesta fiind modul în care ne poate oferi mappingul offseturilor pe care l-am folosit în secțiunea anterioară. Aici, tokenizatorul ignoră cele două spații și le înlocuiește cu unul singur, dar offsetul sare între `are` și `you` pentru a ține cont de acest lucru.

Deoarece utilizăm un tokenizer BERT, pre-tokenizarea implică separarea spațiilor și a punctuației. Alți tokenizatori pot avea reguli diferite pentru acest pas. De exemplu, dacă folosim tokenizatorul GPT-2:

```py
tokenizer = AutoTokenizer.from_pretrained("gpt2")
tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are  you?")
```

va despărți în spații și punctuație, dar va păstra spațiile și le va înlocui cu un simbol `Ġ`, permițându-i să recupereze spațiile originale dacă facem decode tokenilor:

```python out
[('Hello', (0, 5)), (',', (5, 6)), ('Ġhow', (6, 10)), ('Ġare', (10, 14)), ('Ġ', (14, 15)), ('Ġyou', (15, 19)),
 ('?', (19, 20))]
```

De asemenea, rețineți că, spre deosebire de tokenizatorul BERT, acest tokenizator nu ignoră spațiul dublu.

Pentru un ultim exemplu, să aruncăm o privire la tokenizerul T5, care se bazează pe algoritmul SentencePiece:

```py
tokenizer = AutoTokenizer.from_pretrained("t5-small")
tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are  you?")
```

```python out
[('▁Hello,', (0, 6)), ('▁how', (7, 10)), ('▁are', (11, 14)), ('▁you?', (16, 20))]
```

Ca și tokenizatorul GPT-2, acesta păstrează spațiile și le înlocuiește cu un token specific (`_`), dar tokenizatorul T5 separă doar spațiile, nu și punctuația. De asemenea, observați că a adăugat un spațiu implicit la începutul propoziției (înainte de `Hello`) și a ignorat spațiul dublu dintre `are` și `you`.

Acum că am văzut puțin din modul în care diferite tokenizere procesează textul, putem începe să explorăm algoritmii care stau la baza acestora. Vom începe cu o privire rapidă asupra SentencePiece, care se aplică pe scară largă; apoi, în următoarele trei secțiuni, vom examina modul în care funcționează cei trei algoritmi principali utilizați pentru tokenizarea subcuvintelor.

## SentencePiece[[sentencepiece]]

[SentencePiece](https://github.com/google/sentencepiece) este un algoritm de tokenizare pentru preprocesarea textului pe care îl puteți utiliza cu oricare dintre modelele pe care le vom vedea în următoarele trei secțiuni. Acesta consideră textul ca o secvență de caractere Unicode și înlocuiește spațiile cu un caracter special, `▁`. Folosit împreună cu algoritmul Unigram (a se vedea [secțiunea 7](/course/chapter7/7)), nu necesită nici măcar o etapă de pre-tokenizare, ceea ce este foarte util pentru limbile în care nu se folosește caracterul spațiu (cum ar fi chineza sau japoneza).

Cealaltă caracteristică principală a SentencePiece este *reversible tokenization*: deoarece nu există un tratament special al spațiilor, decodarea tokenurilor se face pur și simplu prin concatenarea lor și înlocuirea `_` cu spații - acest lucru rezultă în textul normalizat. După cum am văzut mai devreme, tokenizatorul BERT elimină spațiile care se repetă, deci tokenizarea sa nu este reversibilă.

## Prezentare generală a algoritmului[[algorithm-overview]]

În următoarele secțiuni, vom analiza cei trei algoritmi principali de tokenizare a subcuvintelor: BPE (utilizat de GPT-2 și alții), WordPiece (utilizat de exemplu de BERT) și Unigram (utilizat de T5 și alții). Înainte de a începe, iată o scurtă prezentare generală a modului în care funcționează fiecare dintre acestea. Nu ezitați să reveniți la acest tabel după ce citiți fiecare dintre secțiunile următoare, dacă încă nu are sens pentru dumneavoastră.

Model | BPE | WordPiece | Unigram
:----:|:---:|:---------:|:------:
Training | Pornește de la un vocabular restrâns și învață reguli de îmbinare a tokenilor |  Pornește de la un vocabular restrâns și învață reguli de îmbinare a tokenilor | Pornește de la un vocabular mare și învață regulile de eliminare a tokenilor
Training step | Combină tokenii corespunzători celei mai comune perechi | Combină tokenii corespunzători perechii cu cel mai bun scor pe baza frecvenței perechii, privilegiind perechile în care fiecare token individual este mai puțin frecvent| Elimină toți tokenii din vocabular care vor minimiza pierderea calculată pe întregul corpus
Learns | Reguli de combinare și un vocabular| Doar un vocabular| Un vocabular cu un anumit scor pentru fiecare token
Encoding | Împarte un cuvânt în caractere și aplică îmbinările învățate în timpul antrenării | Găsește cel mai lung subcuvânt începând de la început care se află în vocabular, apoi face același lucru pentru restul cuvântului | Găsește cea mai probabilă împărțire în tokens, folosind scorurile învățate în timpul antrenării

Acum hai să trecem la BPE!

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter6/4.mdx" />

### Tokenizarea Unigram[[unigram-tokenization]]
https://huggingface.co/learn/course/ro/chapter6/7.md

# Tokenizarea Unigram[[unigram-tokenization]]

<CourseFloatingBanner chapter={6}
  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/chapter6/section7.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter6/section7.ipynb"},
]} />

Algoritmul Unigram este adesea utilizat în SentencePiece, care este algoritmul de tokenizare utilizat de modele precum AlBERT, T5, mBART, Big Bird și XLNet.

<Youtube id="TGZfZVuF9Yc"/>

<Tip>

💡 Această secțiune acoperă Unigram în profunzime, mergând până la prezentarea unei implementări complete. Puteți sări la sfârșit dacă doriți doar o prezentare generală a algoritmului de tokenizare.

</Tip>

## Algoritm de antrenare[[training-algorithm]]

În comparație cu BPE și WordPiece, Unigram lucrează în cealaltă direcție: pornește de la un vocabular mare și elimină tokeni din acesta până când ajunge la dimensiunea dorită. Există mai multe opțiuni pentru a construi acel vocabular de bază: putem lua, de exemplu, cele mai comune substrings din cuvintele pre-tokenizate sau putem aplica BPE pe corpusul inițial cu o dimensiune mare a vocabularului.

La fiecare etapă a antrenării, algoritmul Unigram calculează o pierdere pe corpus oferit, având în vedere vocabularul curent. Apoi, pentru fiecare simbol din vocabular, algoritmul calculează cu cât ar crește pierderea globală dacă simbolul ar fi eliminat și caută simbolurile care ar crește cel mai puțin pierderea. Aceste simboluri au cel mai redus efect asupra pierderii globale din corpus, deci, într-un fel, sunt "mai puțin necesare" și sunt cei mai buni candidați pentru eliminare.

Aceasta este o operațiune foarte costisitoare, așa că nu eliminăm doar simbolul asociat cu cea mai mică creștere a pierderii, ci procentul \\(p\\) (\\(p\\) fiind un hyperparametru pe care îl poți controla, de obicei 10 sau 20) din simbolurile asociate cu cea mai mică creștere a pierderilor. Acest proces este se repetă până când vocabularul atinge dimensiunea dorită.

Rețineți că nu eliminăm niciodată caracterele de bază, pentru a ne asigura că orice cuvânt poate fi tokenizat.

Acum, acest lucru este încă puțin vag: partea principală a algoritmului este de a calcula o pierdere asupra corpusului și de a vedea cum se schimbă atunci când eliminăm unele tokenuri din vocabular, dar nu am explicat încă cum să facem acest lucru. Acest pas se bazează pe algoritmul de tokenizare al unui model Unigram, așa că îl vom analiza în continuare.

Vom reutiliza corpusul din exemplele anterioare:

```
("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
```

iar pentru acest exemplu, vom lua toate substringurile stricte pentru vocabularul inițial:

```
["h", "u", "g", "hu", "ug", "p", "pu", "n", "un", "b", "bu", "s", "hug", "gs", "ugs"]
```

## Algoritm de tokenizare[[tokenization-algorithm]]

Un model Unigram este un tip de model lingvistic care consideră că fiecare token este independent de tokenii anteriori. Este cel mai simplu model lingvistic, în sensul că probabilitatea simbolului X având în vedere contextul anterior este doar probabilitatea simbolului X. Astfel, dacă am utiliza un model lingvistic Unigram pentru a genera text, am prezice întotdeauna simbolul cel mai frecvent.

Probabilitatea unui token dat este frecvența sa (numărul de ori în care îl găsim) în corpusul original, împărțită la suma tuturor aparițiilor tuturor tokenilor din vocabular (pentru a ne asigura că probabilitățile sunt egale cu 1). De exemplu, `"ug"` este prezent în `"hug"`, `"pug"`, și `"hugs"`, deci are o frecvență de 20 în corpusul nostru.

Iată frecvențele tuturor subcuvintelor posibile din vocabular:

```
("h", 15) ("u", 36) ("g", 20) ("hu", 15) ("ug", 20) ("p", 17) ("pu", 17) ("n", 16)
("un", 16) ("b", 4) ("bu", 4) ("s", 5) ("hug", 15) ("gs", 5) ("ugs", 5)
```

Astfel, suma tuturor frecvențelor este 210, iar probabilitatea subcuvântului `"ug"` este 20/210.

<Tip>

✏️ **Acum este rândul tău!** Scrie codul pentru a calcula frecvențele de mai sus și verifică de două ori dacă rezultatele afișate sunt corecte, precum și suma totală.

</Tip>

Acum, pentru a tokeniza un cuvânt dat, ne uităm la toate segmentările posibile în tokeni și calculăm probabilitatea fiecăruia în conformitate cu modelul Unigram. Deoarece toate token-urile sunt considerate independente, această probabilitate este doar produsul probabilității fiecărui token. De exemplu, tokenizarea `["p", "u", "g"]` a lui `"pug"` are probabilitatea:

$$P([``p", ``u", ``g"]) = P(``p") \times P(``u") \times P(``g") = \frac{5}{210} \times \frac{36}{210} \times \frac{20}{210} = 0.000389$$

Comparativ, tokenizarea `["pu", "g"]` are probabilitatea:

$$P([``pu", ``g"]) = P(``pu") \times P(``g") = \frac{5}{210} \times \frac{20}{210} = 0.0022676$$

astfel încât una este mult mai probabilă decât alta. În general, tokenizările cu cei mai puțini tokeni posibili vor avea cea mai mare probabilitate (din cauza acelei împărțiri la 210 repetată pentru fiecare token), ceea ce corespunde cu ceea ce dorim intuitiv: să împărțim un cuvânt în cel mai mic număr de tokenuri posibil.

Tokenizarea unui cuvânt cu modelul Unigram este atunci tokenizarea cu cea mai mare probabilitate. În exemplul `"pug"`, iată probabilitățile pe care le-am obține pentru fiecare segmentare posibilă:

```
["p", "u", "g"] : 0.000389
["p", "ug"] : 0.0022676
["pu", "g"] : 0.0022676
```

Astfel, `"pug"` ar fi tokenizat ca `["p", "ug"]` sau `["pu", "g"]`, în funcție de care dintre aceste segmentări este întâlnită prima (rețineți că într-un corpus mai mare, cazurile de egalitate ca acesta vor fi rare).

În acest caz, a fost ușor să găsim toate segmentările posibile și să le calculăm probabilitățile, dar în general va fi puțin mai greu. Există un algoritm clasic utilizat pentru acest lucru, numit *algoritmul Viterbi*. În esență, putem construi un grafic pentru a detecta segmentările posibile ale unui cuvânt dat, spunând că există o ramură de la caracterul _a_ la caracterul _b_ dacă subcuvântul de la _a_ la _b_ se află în vocabular, și atribuind ramurii respective probabilitatea subcuvântului.

Pentru a găsi calea din acest grafic care va avea cel mai bun scor, algoritmul Viterbi determină, pentru fiecare poziție din cuvânt, segmentarea cu cel mai bun scor care se termină la poziția respectivă. Deoarece mergem de la început la sfârșit, cel mai bun scor poate fi găsit prin parcurgerea în buclă a tuturor subcuvintelor care se termină la poziția curentă și apoi folosind cel mai bun scor de tokenizare de la poziția la care începe acest subcuvânt. Apoi, trebuie doar să derulăm calea parcursă pentru a ajunge la sfârșit.

Să aruncăm o privire la un exemplu folosind vocabularul nostru și cuvântul `"unhug"`. Pentru fiecare poziție, subcuvintele cu cele mai bune scoruri care se termină acolo sunt următoarele:

```
Character 0 (u): "u" (score 0.171429)
Character 1 (n): "un" (score 0.076191)
Character 2 (h): "un" "h" (score 0.005442)
Character 3 (u): "un" "hu" (score 0.005442)
Character 4 (g): "un" "hug" (score 0.005442)
```

Astfel, `"unhug"` ar fi tokenizat ca `["un", "hug"]`.

<Tip>

✏️ **Acum e rândul tău!** Determinați tokenizarea cuvântului `"huggun"` și scorul acestuia.

</Tip>

## Înapoi la antrenare[[back-to-training]]

Acum că am văzut cum funcționează tokenizarea, putem analiza mai în profunzime pierderea utilizată în timpul antrenării. În orice etapă dată, această pierdere este calculată prin tokenizarea fiecărui cuvânt din corpus, utilizând vocabularul curent și modelul Unigram determinat de frecvențele fiecărui token din corpus (după cum am văzut mai devreme).

Fiecare cuvânt din corpus are un scor, iar pierderea este negative log likelihood a acestor scoruri - adică suma pentru toate cuvintele din corpus a tuturor `-log(P(word))`.

Să ne întoarcem la exemplul nostru cu următorul corpus:

```
("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
```

Tokenizarea fiecărui cuvânt cu scorurile lor respective este:

```
"hug": ["hug"] (score 0.071428)
"pug": ["pu", "g"] (score 0.007710)
"pun": ["pu", "n"] (score 0.006168)
"bun": ["bu", "n"] (score 0.001451)
"hugs": ["hug", "s"] (score 0.001701)
```

Deci, pierderea este:

```
10 * (-log(0.071428)) + 5 * (-log(0.007710)) + 12 * (-log(0.006168)) + 4 * (-log(0.001451)) + 5 * (-log(0.001701)) = 169.8
```

Acum trebuie să calculăm modul în care eliminarea fiecărui token afectează pierderea. Acest lucru este destul de plictisitor, așa că îl vom face doar pentru doi tokeni aici și vom păstra întregul proces pentru atunci când vom avea cod care să ne ajute. În acest caz (foarte) special, aveam două tokenizări echivalente ale tuturor cuvintelor: după cum am văzut mai devreme, de exemplu, `"pug"` ar putea fi tokenizat `["p", "ug"]` cu același scor. Astfel, eliminarea simbolului `"pu"` din vocabular va produce exact aceeași pierdere.

Pe de altă parte, eliminarea lui `"hug"` va agrava pierderea, deoarece tokenizarea lui `"hug"` și `"hugs"` va deveni:

```
"hug": ["hu", "g"] (score 0.006802)
"hugs": ["hu", "gs"] (score 0.001701)
```

Aceste modificări vor determina creșterea pierderii cu:

```
- 10 * (-log(0.071428)) + 10 * (-log(0.006802)) = 23.5
```

Prin urmare, tokenul `"pu"` va fi probabil eliminat din vocabular, dar nu și `"hug"`.

## Implementarea Unigram[[implementarea-unigram]]

Acum să implementăm în cod tot ceea ce am văzut până acum. Ca și în cazul BPE și WordPiece, aceasta nu este o implementare eficientă a algoritmului Unigram (dimpotrivă), dar ar trebui să vă ajute să-l înțelegeți puțin mai bine.

Vom folosi ca exemplu același corpus ca și până acum:

```python
corpus = [
    "This is the Hugging Face Course.",
    "This chapter is about tokenization.",
    "This section shows several tokenizer algorithms.",
    "Hopefully, you will be able to understand how they are trained and generate tokens.",
]
```

De data aceasta, vom folosi `xlnet-base-cased` ca modelul nostru:

```python
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("xlnet-base-cased")
```

Ca și pentru BPE și WordPiece, începem prin a număra numărul de apariții ale fiecărui cuvânt în corpus:

```python
from collections import defaultdict

word_freqs = defaultdict(int)
for text in corpus:
    words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
    new_words = [word for word, offset in words_with_offsets]
    for word in new_words:
        word_freqs[word] += 1

word_freqs
```

Apoi, trebuie să inițializăm vocabularul nostru la ceva mai mare decât dimensiunea vocabularului pe care o vom dori la final. Trebuie să includem toate caracterele de bază (altfel nu vom putea tokeniza fiecare cuvânt), dar pentru substringurile mai mari le vom păstra doar pe cele mai comune, așa că le vom sorta după frecvență:

```python
char_freqs = defaultdict(int)
subwords_freqs = defaultdict(int)
for word, freq in word_freqs.items():
    for i in range(len(word)):
        char_freqs[word[i]] += freq
        # Loop through the subwords of length at least 2
        for j in range(i + 2, len(word) + 1):
            subwords_freqs[word[i:j]] += freq

# Sortarea subcuvintelor după frecvență
sorted_subwords = sorted(subwords_freqs.items(), key=lambda x: x[1], reverse=True)
sorted_subwords[:10]
```

```python out
[('▁t', 7), ('is', 5), ('er', 5), ('▁a', 5), ('▁to', 4), ('to', 4), ('en', 4), ('▁T', 3), ('▁Th', 3), ('▁Thi', 3)]
```

Grupăm caracterele cu cele mai bune subcuvinte pentru a ajunge la un vocabular inițial de dimensiunea 300:

```python
token_freqs = list(char_freqs.items()) + sorted_subwords[: 300 - len(char_freqs)]
token_freqs = {token: freq for token, freq in token_freqs}
```

<Tip>

💡 SentencePiece utilizează un algoritm mai eficient numit Enhanced Suffix Array (ESA) pentru a crea vocabularul inițial.

</Tip>

În continuare, calculăm suma tuturor frecvențelor, pentru a converti frecvențele în probabilități. Pentru modelul nostru, vom stoca logaritmii probabilităților, deoarece este mai stabil din punct de vedere numeric să adăugăm logaritmi decât să multiplicăm numere mici, iar acest lucru va simplifica calcularea pierderii modelului:

```python
from math import log

total_sum = sum([freq for token, freq in token_freqs.items()])
model = {token: -log(freq / total_sum) for token, freq in token_freqs.items()}
```

Acum funcția principală este cea care tokenizează cuvintele folosind algoritmul Viterbi. După cum am văzut mai devreme, acest algoritm calculează cea mai bună segmentare a fiecărui substring din cuvânt, pe care o vom stoca într-o variabilă numită `best_segmentations`. Vom stoca un dicționar pentru fiecare poziție din cuvânt (de la 0 la lungimea totală a acestuia), cu două chei: indicele de început al ultimului token din cea mai bună segmentare și scorul celei mai bune segmentări. Cu ajutorul indicelui de început al ultimului token, vom putea extrage segmentarea completă odată ce lista este complet populată.

Popularea listei se face cu doar două bucle: bucla principală trece peste fiecare poziție de început, iar a doua buclă încearcă toate subcuvintele care încep la acea poziție de început. Dacă substringul se află în vocabular, avem o nouă segmentare a cuvântului până la acea poziție finală, pe care o comparăm cu cea din `best_segmentations`.

Odată ce bucla principală este terminată, pornim de la sfârșit și sărim de la o poziție de început la alta, înregistrând tokenii pe parcurs, până când ajungem la începutul cuvântului:

```python
def encode_word(word, model):
    best_segmentations = [{"start": 0, "score": 1}] + [
        {"start": None, "score": None} for _ in range(len(word))
    ]
    for start_idx in range(len(word)):
        # This should be properly filled by the previous steps of the loop
        best_score_at_start = best_segmentations[start_idx]["score"]
        for end_idx in range(start_idx + 1, len(word) + 1):
            token = word[start_idx:end_idx]
            if token in model and best_score_at_start is not None:
                score = model[token] + best_score_at_start
                # If we have found a better segmentation ending at end_idx, we update
                if (
                    best_segmentations[end_idx]["score"] is None
                    or best_segmentations[end_idx]["score"] > score
                ):
                    best_segmentations[end_idx] = {"start": start_idx, "score": score}

    segmentation = best_segmentations[-1]
    if segmentation["score"] is None:
        # We did not find a tokenization of the word -> unknown
        return ["<unk>"], None

    score = segmentation["score"]
    start = segmentation["start"]
    end = len(word)
    tokens = []
    while start != 0:
        tokens.insert(0, word[start:end])
        next_start = best_segmentations[start]["start"]
        end = start
        start = next_start
    tokens.insert(0, word[start:end])
    return tokens, score
```

Putem încerca deja modelul nostru inițial pe câteva cuvinte:

```python
print(encode_word("Hopefully", model))
print(encode_word("This", model))
```

```python out
(['H', 'o', 'p', 'e', 'f', 'u', 'll', 'y'], 41.5157494601402)
(['This'], 6.288267030694535)
```

Acum este ușor de calculat pierderea modelului pe corpus!

```python
def compute_loss(model):
    loss = 0
    for word, freq in word_freqs.items():
        _, word_loss = encode_word(word, model)
        loss += freq * word_loss
    return loss
```

Putem verifica dacă funcționează pe modelul pe care îl avem:

```python
compute_loss(model)
```

```python out
413.10377642940875
```

Nici calcularea scorurilor pentru fiecare token nu este foarte dificilă; trebuie doar să calculăm pierderea pentru modelele obținute prin ștergerea fiecărui tokeb:

```python
import copy


def compute_scores(model):
    scores = {}
    model_loss = compute_loss(model)
    for token, score in model.items():
        # We always keep tokens of length 1
        if len(token) == 1:
            continue
        model_without_token = copy.deepcopy(model)
        _ = model_without_token.pop(token)
        scores[token] = compute_loss(model_without_token) - model_loss
    return scores
```

Îl putem încerca pe un token dat:

```python
scores = compute_scores(model)
print(scores["ll"])
print(scores["his"])
```

Deoarece `"ll"` este folosit în tokenizarea lui `"Hopefully"`, iar eliminarea lui ne va face, probabil, să folosim tokenul `"l"` de două ori în schimb, ne așteptăm să aibă o pierdere pozitivă. `"his"` este folosit doar în interiorul cuvântului `"This"`, care este tokenizat ca el însuși, deci ne așteptăm să aibă o pierdere zero. Iată rezultatele:

```python out
6.376412403623874
0.0
```

<Tip>

💡 Această abordare este foarte ineficientă, astfel încât SentencePiece utilizează o aproximare a pierderii modelului fără simbolul X: în loc să înceapă de la zero, înlocuiește simbolul X cu segmentarea sa în vocabularul rămas. În acest fel, toate scorurile pot fi calculate odată, în același timp cu pierderea modelului.

</Tip>

Cu toate acestea la locul lor, ultimul lucru pe care trebuie să îl facem este să adăugăm la vocabular tokeni speciali utilizate de model, apoi să facem o buclă până când am eliminat suficienți tokeni din vocabular pentru a ajunge la dimensiunea dorită:

```python
percent_to_remove = 0.1
while len(model) > 100:
    scores = compute_scores(model)
    sorted_scores = sorted(scores.items(), key=lambda x: x[1])
    # Remove percent_to_remove tokens with the lowest scores.
    for i in range(int(len(model) * percent_to_remove)):
        _ = token_freqs.pop(sorted_scores[i][0])

    total_sum = sum([freq for token, freq in token_freqs.items()])
    model = {token: -log(freq / total_sum) for token, freq in token_freqs.items()}
```

Apoi, pentru a tokeniza un text, trebuie doar să aplicăm pre-tokenizarea și apoi să folosim funcția `encode_word()`:

```python
def tokenize(text, model):
    words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
    pre_tokenized_text = [word for word, offset in words_with_offsets]
    encoded_words = [encode_word(word, model)[0] for word in pre_tokenized_text]
    return sum(encoded_words, [])


tokenize("This is the Hugging Face course.", model)
```

```python out
['▁This', '▁is', '▁the', '▁Hugging', '▁Face', '▁', 'c', 'ou', 'r', 's', 'e', '.']
```

Asta e tot pentru Unigram! Sperăm că până acum vă simțiți ca un expert în toate lucrurile legate de tokenizer. În secțiunea următoare, vom aprofunda elementele de bază ale bibliotecii 🤗 Tokenizers și vă vom arăta cum le puteți utiliza pentru a vă construi propriul tokenizer.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter6/7.mdx" />

### Tokenizerii rapizi în pipeline-ul de QA[[fast-tokenizers-in-the-qa-pipeline]]
https://huggingface.co/learn/course/ro/chapter6/3b.md

# Tokenizerii rapizi în pipeline-ul de QA[[fast-tokenizers-in-the-qa-pipeline]]

{#if fw === 'pt'}

<CourseFloatingBanner chapter={6}
  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/chapter6/section3b_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter6/section3b_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={6}
  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/chapter6/section3b_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter6/section3b_tf.ipynb"},
]} />

{/if}

Acum ne vom aprofunda în pipelineul `question-answering` și să vedem cum putem valorifica offesturile pentru a primi răspunzuri la întrebări la îndemână din context, asemănător cum am făcut cu entitățile grupate în secțiunea precedentă. Pe urmă vom vedea cum vom face față contextelor foarte lungi care ajung truncate. Puteți trece peste această secțiune dacă nu sunteți interesat în întrebarea care răspunde la sarcina aceasta.

{#if fw === 'pt'}

<Youtube id="_wxyB3j3mk4"/>

{:else}

<Youtube id="b3u8RzBCX9Y"/>

{/if}

## Folosind `question-answering` pipeline[[using-the-question-answering-pipeline]]

Cum am văzut în [Capitolul 1](/course/chapter1), noi putem folosi pipelineul `question-answering` ca acesta pentru a răspunde la o întrebare:

```py
from transformers import pipeline

question_answerer = pipeline("question-answering")
context = """
🤗 Transformers is backed by the three most popular deep learning libraries — Jax, PyTorch, and TensorFlow — with a seamless integration
between them. It's straightforward to train your models with one before loading them for inference with the other.
"""
question = "Which deep learning libraries back 🤗 Transformers?"
question_answerer(question=question, context=context)
```

```python out
{'score': 0.97773,
 'start': 78,
 'end': 105,
 'answer': 'Jax, PyTorch and TensorFlow'}
```

Spre deosebire de alte pipelineuri, care nu put trunca și face split la text care este mai lung decât lungimea maxim acceptată de model(și, prin urmare, pot pierde informații la sfârșitul unui document), accest pipeline poate face față contextelor foarte lungi și va returna răspunsul la întrebare chiar dacă aceasta se află la sfârșit:

```py
long_context = """
🤗 Transformers: State of the Art NLP

🤗 Transformers provides thousands of pretrained models to perform tasks on texts such as classification, information extraction,
question answering, summarization, translation, text generation and more in over 100 languages.
Its aim is to make cutting-edge NLP easier to use for everyone.

🤗 Transformers provides APIs to quickly download and use those pretrained models on a given text, fine-tune them on your own datasets and
then share them with the community on our model hub. At the same time, each python module defining an architecture is fully standalone and
can be modified to enable quick research experiments.

Why should I use transformers?

1. Easy-to-use state-of-the-art models:
  - High performance on NLU and NLG tasks.
  - Low barrier to entry for educators and practitioners.
  - Few user-facing abstractions with just three classes to learn.
  - A unified API for using all our pretrained models.
  - Lower compute costs, smaller carbon footprint:

2. Researchers can share trained models instead of always retraining.
  - Practitioners can reduce compute time and production costs.
  - Dozens of architectures with over 10,000 pretrained models, some in more than 100 languages.

3. Choose the right framework for every part of a model's lifetime:
  - Train state-of-the-art models in 3 lines of code.
  - Move a single model between TF2.0/PyTorch frameworks at will.
  - Seamlessly pick the right framework for training, evaluation and production.

4. Easily customize a model or an example to your needs:
  - We provide examples for each architecture to reproduce the results published by its original authors.
  - Model internals are exposed as consistently as possible.
  - Model files can be used independently of the library for quick experiments.

🤗 Transformers is backed by the three most popular deep learning libraries — Jax, PyTorch and TensorFlow — with a seamless integration
between them. It's straightforward to train your models with one before loading them for inference with the other.
"""
question_answerer(question=question, context=long_context)
```

```python out
{'score': 0.97149,
 'start': 1892,
 'end': 1919,
 'answer': 'Jax, PyTorch and TensorFlow'}
```

Hai să vedem cum el face toate astea!

## Folosind un model pentru răspunderea la întrebări[[using-a-model-for-question-answering]]

Ca în cazul oricărui altui pipeline, începem prin tokenizarea datelor de intrare și apoi le trimitem prin model. Checkpointul utilizat în mod implicit pentru pipelineul `question-answering` este [`distilbert-base-cased-distilled-squad`](https://huggingface.co/distilbert-base-cased-distilled-squad) ("squad" din nume provine de la datasetul pe care modelul a fost ajustat; vom vorbi mai multe despre datasetul SQuAD în [Capitolul 7](/course/chapter7/7)):

{#if fw === 'pt'}

```py
from transformers import AutoTokenizer, AutoModelForQuestionAnswering

model_checkpoint = "distilbert-base-cased-distilled-squad"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint)

inputs = tokenizer(question, context, return_tensors="pt")
outputs = model(**inputs)
```

{:else}

```py
from transformers import AutoTokenizer, TFAutoModelForQuestionAnswering

model_checkpoint = "distilbert-base-cased-distilled-squad"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
model = TFAutoModelForQuestionAnswering.from_pretrained(model_checkpoint)

inputs = tokenizer(question, context, return_tensors="tf")
outputs = model(**inputs)
```

{/if}

Observați că noi tokenizăm întrebrea și contextul ca o perecehe, cu întrebarea prima.


<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/question_tokens.svg" alt="An example of tokenization of question and context"/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter6/question_tokens-dark.svg" alt="An example of tokenization of question and context"/>
</div>

Modelele create pentru răspunderea la întrebări funcționează puțin diferit de modelele pe care le-am văzut până acum. Folosind imaginea de mai sus ca exemplu, modelul a fost antrenat pentru a prezice indicele tokenului cu care începe răspunsului (aici 21) și indicele simbolului la care se termină răspunsul (aici 24). Acesta este motivul pentru care modelele respective nu returnează un singur tensor de logits, ci două: unul pentru logits-ul corespunzători tokenului cu care începe răspunsului și unul pentru logits-ul corespunzător tokenului de sfârșit al răspunsului. Deoarece în acest caz avem un singur input care conține 66 de token-uri, obținem:

```py
start_logits = outputs.start_logits
end_logits = outputs.end_logits
print(start_logits.shape, end_logits.shape)
```

{#if fw === 'pt'}

```python out
torch.Size([1, 66]) torch.Size([1, 66])
```

{:else}

```python out
(1, 66) (1, 66)
```

{/if}

Pentru a converti acești logits în probabilități, vom aplica o funcție softmax - dar înainte de aceasta, trebuie să ne asigurăm că mascăm indicii care nu fac parte din context. Inputul nostru este `[CLS] întrebare [SEP] context [SEP]`, deci trebuie să mascăm token-urile întrebării, precum și tokenul `[SEP]`. Cu toate acestea, vom păstra simbolul `[CLS]`, deoarece unele modele îl folosesc pentru a indica faptul că răspunsul nu se află în context.

Deoarece vom aplica ulterior un softmax, trebuie doar să înlocuim logiturile pe care dorim să le mascăm cu un număr negativ mare. Aici, folosim `-10000`:

{#if fw === 'pt'}

```py
import torch

sequence_ids = inputs.sequence_ids()
# Mask everything apart from the tokens of the context
mask = [i != 1 for i in sequence_ids]
# Unmask the [CLS] token
mask[0] = False
mask = torch.tensor(mask)[None]

start_logits[mask] = -10000
end_logits[mask] = -10000
```

{:else}

```py
import tensorflow as tf

sequence_ids = inputs.sequence_ids()
# Mask everything apart from the tokens of the context
mask = [i != 1 for i in sequence_ids]
# Unmask the [CLS] token
mask[0] = False
mask = tf.constant(mask)[None]

start_logits = tf.where(mask, -10000, start_logits)
end_logits = tf.where(mask, -10000, end_logits)
```

{/if}

Acum că am mascat în mod corespunzător logiturile corespunzătoare pozițiilor pe care nu dorim să le prezicem, putem aplica softmax:

{#if fw === 'pt'}

```py
start_probabilities = torch.nn.functional.softmax(start_logits, dim=-1)[0]
end_probabilities = torch.nn.functional.softmax(end_logits, dim=-1)[0]
```

{:else}

```py
start_probabilities = tf.math.softmax(start_logits, axis=-1)[0].numpy()
end_probabilities = tf.math.softmax(end_logits, axis=-1)[0].numpy()
```

{/if}

La acest stadiu, am putea lua argmax al probabilităților de început și de sfârșit - dar am putea ajunge la un indice de început care este mai mare decât indicele de sfârșit, deci trebuie să luăm câteva precauții suplimentare. Vom calcula probabilitățile fiecărui  `start_index` și `end_index` posibil în cazul în care `start_index <= end_index`, apoi vom lua un tuple `(start_index, end_index)` cu cea mai mare probabilitate.

Presupunând că evenimentele "The answer starts at `start_index`" și "The answer ends at `end_index`" sunt independente, probabilitatea ca răspunsul să înceapă la `start_index` și să se termine la `end_index` este:

$$\mathrm{start\_probabilities}[\mathrm{start\_index}] \times \mathrm{end\_probabilities}[\mathrm{end\_index}]$$ 

Deci, pentru a calcula toate scorurile, trebuie doar să calculăm toate produsele \\(\mathrm{start\_probabilities}[\mathrm{start\_index}] \times \mathrm{end\_probabilities}[\mathrm{end\_index}]\\) unde `start_index <= end_index`.

Mai întâi hai să calculăm toate produsele posibile:

```py
scores = start_probabilities[:, None] * end_probabilities[None, :]
```

{#if fw === 'pt'}

Apoi vom masca valorile în care `start_index > end_index` prin stabilirea lor la `0` (celelalte probabilități sunt toate numere pozitive). Funcția `torch.triu()` returnează partea triunghiulară superioară a tensorului 2D trecut ca argument, deci va face această mascare pentru noi:

```py
scores = torch.triu(score)
```

{:else}

Apoi vom masca valorile în care `start_index > end_index` prin stabilirea lor la `0` (celelalte probabilități sunt toate numere pozitive). Funcția `np.triu()` returnează partea triunghiulară superioară a tensorului 2D trecut ca argument, deci va face această mascare pentru noi:

```py
import numpy as np

scores = np.triu(scores)
```

{/if}

Acum trebuie doar să obținem indicele maximului. Deoarece PyTorch va returna indicele în tensorul aplatizat, trebuie să folosim operațiile floor division `//` și modulusul `%` pentru a obține `start_index` și `end_index`:

```py
max_index = scores.argmax().item()
start_index = max_index // scores.shape[1]
end_index = max_index % scores.shape[1]
print(scores[start_index, end_index])
```

Nu am terminat încă, dar cel puțin avem deja scorul corect pentru răspuns (puteți verifica acest lucru comparându-l cu primul rezultat din secțiunea anterioară):

```python out
0.97773
```

<Tip>

✏️ **Încercați!** Calculați indicii de început și de sfârșit pentru cele mai probabile cinci răspunsuri.

</Tip>

Avem `start_index` și `end_index` ale răspunsului în termeni de tokens, deci acum trebuie doar să convertim în character indices în context. Acesta este momentul în care offseturile vor fi foarte utile. Putem să le luăm și să le folosim așa cum am făcut în sarcina de clasificare a tokenurilor:

```py
inputs_with_offsets = tokenizer(question, context, return_offsets_mapping=True)
offsets = inputs_with_offsets["offset_mapping"]

start_char, _ = offsets[start_index]
_, end_char = offsets[end_index]
answer = context[start_char:end_char]
```

Acum trebuie doar să formatăm totul pentru a obține rezultatul nostru:

```py
result = {
    "answer": answer,
    "start": start_char,
    "end": end_char,
    "score": scores[start_index, end_index],
}
print(result)
```

```python out
{'answer': 'Jax, PyTorch and TensorFlow',
 'start': 78,
 'end': 105,
 'score': 0.97773}
```

Grozav! Este la fel ca în primul nostru exemplu!

<Tip>

✏️ **Încercați!** Utilizați cele mai bune scoruri pe care le-ați calculat anterior pentru a afișa cele mai probabile cinci răspunsuri. Pentru a vă verifica rezultatele, întoarceți-vă la primul pipeline și introduceți `top_k=5` atunci când îl apelați.

</Tip>

## Gestionarea contextelor lungi[[handling-long-contexts]]

Dacă încercăm să tokenizăm întrebarea și contextul lung pe care le-am folosit ca un exemplu anterior, vom obține un număr de tokenuri mai mare decât lungimea maximă utilizată în pipelineul `question-answering` (care este 384):

```py
inputs = tokenizer(question, long_context)
print(len(inputs["input_ids"]))
```

```python out
461
```

Prin urmare, va trebui să trunchiem inputurile la lungimea maximă. Există mai multe modalități prin care putem face acest lucru, dar nu dorim să trunchiem întrebarea, ci doar contextul. Deoarece contextul este a doua propoziție, vom utiliza strategia de trunchiere `"only_second"`. Problema care apare atunci este că răspunsul la întrebare poate să nu fie în contextul trunchiat. Aici, de exemplu, am ales o întrebare la care răspunsul se află spre sfârșitul contextului, iar atunci când îl trunchiem, răspunsul nu este prezent:

```py
inputs = tokenizer(question, long_context, max_length=384, truncation="only_second")
print(tokenizer.decode(inputs["input_ids"]))
```

```python out
"""
[CLS] Which deep learning libraries back [UNK] Transformers? [SEP] [UNK] Transformers : State of the Art NLP

[UNK] Transformers provides thousands of pretrained models to perform tasks on texts such as classification, information extraction,
question answering, summarization, translation, text generation and more in over 100 languages.
Its aim is to make cutting-edge NLP easier to use for everyone.

[UNK] Transformers provides APIs to quickly download and use those pretrained models on a given text, fine-tune them on your own datasets and
then share them with the community on our model hub. At the same time, each python module defining an architecture is fully standalone and
can be modified to enable quick research experiments.

Why should I use transformers?

1. Easy-to-use state-of-the-art models:
  - High performance on NLU and NLG tasks.
  - Low barrier to entry for educators and practitioners.
  - Few user-facing abstractions with just three classes to learn.
  - A unified API for using all our pretrained models.
  - Lower compute costs, smaller carbon footprint:

2. Researchers can share trained models instead of always retraining.
  - Practitioners can reduce compute time and production costs.
  - Dozens of architectures with over 10,000 pretrained models, some in more than 100 languages.

3. Choose the right framework for every part of a model's lifetime:
  - Train state-of-the-art models in 3 lines of code.
  - Move a single model between TF2.0/PyTorch frameworks at will.
  - Seamlessly pick the right framework for training, evaluation and production.

4. Easily customize a model or an example to your needs:
  - We provide examples for each architecture to reproduce the results published by its original authors.
  - Model internal [SEP]
"""
```

Aceasta înseamnă că modelul va avea dificultăți în a alege răspunsul corect. Pentru a rezolva acest lucru, pipelineul `question-answering` ne permite să împărțim contextul în bucăți mai mici, specificând lungimea maximă. Pentru a ne asigura că nu împărțim contextul exact în locul nepotrivit pentru a face posibilă găsirea răspunsului, aceasta include și o anumită suprapunere între bucăți.

Putem cere tokenizerului (rapid sau lent) să facă acest lucru pentru noi adăugând `return_overflowing_tokens=True`, și putem specifica suprapunerea dorită cu argumentul `stride`. Iată un exemplu, folosind o propoziție mai mică:

```py
sentence = "This sentence is not too long but we are going to split it anyway."
inputs = tokenizer(
    sentence, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2
)

for ids in inputs["input_ids"]:
    print(tokenizer.decode(ids))
```

```python out
'[CLS] This sentence is not [SEP]'
'[CLS] is not too long [SEP]'
'[CLS] too long but we [SEP]'
'[CLS] but we are going [SEP]'
'[CLS] are going to split [SEP]'
'[CLS] to split it anyway [SEP]'
'[CLS] it anyway. [SEP]'
```

După cum putem vedea, propoziția a fost împărțită în bucăți astfel încât fiecare intrare din `inputs["input_ids"]` să aibă cel mult 6 token-uri (aici ar trebui să adăugăm padding pentru ca ultima intrare să aibă aceeași dimensiune ca celelalte) și există o suprapunere de 2 tokenuri între fiecare intrare.

Să aruncăm o privire mai atentă la rezultatul tokenizării:

```py
print(inputs.keys())
```

```python out
dict_keys(['input_ids', 'attention_mask', 'overflow_to_sample_mapping'])
```

Așa cum era de așteptat, obținem ID-uri de intrare și un attention mask. Ultima cheie, `overflow_to_sample_mapping`, este o hartă care ne spune cărei propoziții îi corespunde fiecare dintre rezultate - aici avem 7 rezultate care provin toate din (singura) propoziție pe care am transmis-o tokenizerului:

```py
print(inputs["overflow_to_sample_mapping"])
```

```python out
[0, 0, 0, 0, 0, 0, 0]
```

Acest lucru este mai util atunci când tokenizăm mai multe propoziții împreună. De exemplu, aceasta:

```py
sentences = [
    "This sentence is not too long but we are going to split it anyway.",
    "This sentence is shorter but will still get split.",
]
inputs = tokenizer(
    sentences, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2
)

print(inputs["overflow_to_sample_mapping"])
```

gets us:

```python out
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]
```

ceea ce înseamnă că prima propoziție este împărțită în 7 fragmente ca înainte, iar următoarele 4 fragmente provin din a doua propoziție.

Acum să ne întoarcem la contextul nostru lung. În mod implicit, pipelineul `question-answering` utilizează o lungime maximă de 384, așa cum am menționat mai devreme, și un stride de 128, care corespund modului în care modelul a fost fine-tuned (puteți ajusta acești parametri prin trecerea argumentelor `max_seq_len` și `stride` atunci când apelați pipelineul). Astfel, vom utiliza acești parametri la tokenizare. Vom adăuga, de asemenea, padding (pentru a avea sampleuri de aceeași lungime, astfel încât să putem construi tensori), precum și pentru a solicita offsets:

```py
inputs = tokenizer(
    question,
    long_context,
    stride=128,
    max_length=384,
    padding="longest",
    truncation="only_second",
    return_overflowing_tokens=True,
    return_offsets_mapping=True,
)
```

Aceste "inputuri" vor conține ID-urile de input și attention maskurile așteptate de model, precum și offseturile și "overflow_to_sample_mapping" despre care tocmai am vorbit. Deoarece cei doi nu sunt parametri utilizați de model, îi vom scoate din `inputs` (și nu vom stoca harta, deoarece nu este utilă aici) înainte de a-l converti într-un tensor:

{#if fw === 'pt'}

```py
_ = inputs.pop("overflow_to_sample_mapping")
offsets = inputs.pop("offset_mapping")

inputs = inputs.convert_to_tensors("pt")
print(inputs["input_ids"].shape)
```

```python out
torch.Size([2, 384])
```

{:else}

```py
_ = inputs.pop("overflow_to_sample_mapping")
offsets = inputs.pop("offset_mapping")

inputs = inputs.convert_to_tensors("tf")
print(inputs["input_ids"].shape)
```

```python out
(2, 384)
```

{/if}

Contextul nostru lung a fost împărțit în două, ceea ce înseamnă că, după ce trece prin modelul nostru, vom avea două seturi de logits de început și de sfârșit:

```py
outputs = model(**inputs)

start_logits = outputs.start_logits
end_logits = outputs.end_logits
print(start_logits.shape, end_logits.shape)
```

{#if fw === 'pt'}

```python out
torch.Size([2, 384]) torch.Size([2, 384])
```

{:else}

```python out
(2, 384) (2, 384)
```

{/if}

Ca și înainte, mai întâi mascăm tokenii care nu fac parte din context înainte de a lua softmax. De asemenea, mascăm toți padding tokens(marcate de attention mask):

{#if fw === 'pt'}

```py
sequence_ids = inputs.sequence_ids()
# Mask everything apart from the tokens of the context
mask = [i != 1 for i in sequence_ids]
# Unmask the [CLS] token
mask[0] = False
# Mask all the [PAD] tokens
mask = torch.logical_or(torch.tensor(mask)[None], (inputs["attention_mask"] == 0))

start_logits[mask] = -10000
end_logits[mask] = -10000
```

{:else}

```py
sequence_ids = inputs.sequence_ids()
# Mask everything apart from the tokens of the context
mask = [i != 1 for i in sequence_ids]
# Unmask the [CLS] token
mask[0] = False
# Mask all the [PAD] tokens
mask = tf.math.logical_or(tf.constant(mask)[None], inputs["attention_mask"] == 0)

start_logits = tf.where(mask, -10000, start_logits)
end_logits = tf.where(mask, -10000, end_logits)
```

{/if}

Then we can use the softmax to convert our logits to probabilities:

{#if fw === 'pt'}

```py
start_probabilities = torch.nn.functional.softmax(start_logits, dim=-1)
end_probabilities = torch.nn.functional.softmax(end_logits, dim=-1)
```

{:else}

```py
start_probabilities = tf.math.softmax(start_logits, axis=-1).numpy()
end_probabilities = tf.math.softmax(end_logits, axis=-1).numpy()
```

{/if}

Următorul pas este similar cu ceea ce am făcut pentru contextul mic, dar îl repetăm pentru fiecare dintre cele două chunkuri. Atribuim un scor tuturor intervalelor posibile de răspuns, apoi luăm intervalul cu cel mai bun scor:

{#if fw === 'pt'}

```py
candidates = []
for start_probs, end_probs in zip(start_probabilities, end_probabilities):
    scores = start_probs[:, None] * end_probs[None, :]
    idx = torch.triu(scores).argmax().item()

    start_idx = idx // scores.shape[1]
    end_idx = idx % scores.shape[1]
    score = scores[start_idx, end_idx].item()
    candidates.append((start_idx, end_idx, score))

print(candidates)
```

{:else}

```py
candidates = []
for start_probs, end_probs in zip(start_probabilities, end_probabilities):
    scores = start_probs[:, None] * end_probs[None, :]
    idx = np.triu(scores).argmax().item()

    start_idx = idx // scores.shape[1]
    end_idx = idx % scores.shape[1]
    score = scores[start_idx, end_idx].item()
    candidates.append((start_idx, end_idx, score))

print(candidates)
```

{/if}

```python out
[(0, 18, 0.33867), (173, 184, 0.97149)]
```

Cei doi candidați corespund celor mai bune răspunsuri pe care modelul le-a putut găsi în fiecare parte. Modelul este mult mai încrezător că răspunsul corect se află în a doua parte (ceea ce este un semn bun!). Acum trebuie doar să facem map celor două intervale de tokenuri cu intervalele de caractere din context (trebuie să o punem în corespondență doar pe a doua pentru a avea răspunsul nostru, dar este interesant să vedem ce a ales modelul în prima parte).

<Tip>

✏️ **Încercați!** Adaptați codul de mai sus pentru a returna scorurile și spanurile intervalele pentru cele mai probabile cinci răspunsuri (în total, nu pe chunk).

</Tip>

`offsets`-urile pe care le-am luat mai devreme este de fapt o listă de offsets, cu o listă pentru fiecare chunk de text:

```py
for candidate, offset in zip(candidates, offsets):
    start_token, end_token, score = candidate
    start_char, _ = offset[start_token]
    _, end_char = offset[end_token]
    answer = long_context[start_char:end_char]
    result = {"answer": answer, "start": start_char, "end": end_char, "score": score}
    print(result)
```

```python out
{'answer': '\n🤗 Transformers: State of the Art NLP', 'start': 0, 'end': 37, 'score': 0.33867}
{'answer': 'Jax, PyTorch and TensorFlow', 'start': 1892, 'end': 1919, 'score': 0.97149}
```

Dacă ignorăm primul rezultat, obținem același rezultat ca și pipelineul noastru pentru acest context lung - yay!

<Tip>

✏️ **Încercați!** Utilizați cele mai bune scoruri pe care le-ați calculat înainte pentru a afișa cele mai probabile cinci răspunsuri (pentru întregul context, nu pentru fiecare chunk). Pentru a vă verifica rezultatele, întoarceți-vă la primul pipeline și introduceți `top_k=5` atunci când îl apelați.

</Tip>

Aici se încheie scufundarea noastră în capacitățile tokenizerului. Vom pune toate acestea din nou în practică în capitolul următor, când vă vom arăta cum să ajustați un model pentru o serie de sarcini NLP comune.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter6/3b.mdx" />

### Antrenarea unui nou tokenizer dintr-unul vechi[[training-a-new-tokenizer-from-an-old-one]]
https://huggingface.co/learn/course/ro/chapter6/2.md

# Antrenarea unui nou tokenizer dintr-unul vechi[[training-a-new-tokenizer-from-an-old-one]]

<CourseFloatingBanner chapter={6}
  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/chapter6/section2.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter6/section2.ipynb"},
]} />

Dacă un model de limbaj nu este disponibil în limba dorită sau dacă corpusul tău este foarte diferit de cel pe care modelul de limbaj a fost antrenat, este probabil că veți dori să antrenați modelul de la zero, folosind un tokenizer adaptat datelor tale. Acest lucru va necesita antrenarea unui nou tokenizer pe datasetul tău. Dar ce înseamnă exact asta? Când am examinat pentru prima dată tokenizatorii în [Capitolul 2](/course/chapter2), am văzut că majoritatea modelelor Transformer folosesc un _algoritm de subword tokenization_. Pentru a identifica care subcuvinte sunt de interes și apar cel mai frecvent în corpusul respectiv, tokenizerul trebuie să examineze cu atenție toate textele din corpus - un proces pe care îl numim *antrenare*. Regulile exacte care conduc această antrenare depind de tipul de tokenizer utilizat și vom prezenta cei trei algoritmi principali mai târziu în acest capitol.

<Youtube id="DJimQynXZsQ"/>

<Tip warning={true}>

⚠️ Antrenarea unui tokenizer nu este același lucru ca antrenarea unui model! Antrenarea modelului folosește stochastic gradient descent pentru a face pierderea puțin mai mică pentru fiecare batch. Este randomizată prin natură (ceea ce înseamnă că trebuie să setați niște seeduri pentru a obține aceleași rezultate atunci când faceți aceeași antrenare de două ori). Antrenarea unui tokenizer este un proces statistic care încearcă să identifice care subcuvinte sunt cele mai bune pentru a fi selectate pentru un anumit corpus, și regulile exacte utilizate pentru a le selecta depind de algoritmul de tokenizare. Este determinist, ceea ce înseamnă că întotdeauna obțineți aceleași rezultate atunci când antrenați cu același algoritm pe același corpus.

</Tip>

## Asamblarea unui corpus[[assembling-a-corpus]]

Există o interfață API foarte simplă în 🤗 Transformers pe care o puteți utiliza pentru a antrena un nou tokenizer cu aceleași caracteristici ca unul existent: `AutoTokenizer.train_new_from_iterator()`. Pentru a vedea acest lucru în acțiune, să zicem că vrem să antrenăm GPT-2 de la zero, dar într-o altă limbă decât engleza. Prima noastră sarcină va fi să adunăm multe date în acea limbă într-un corpus de antrenare. Pentru a oferi exemple pe care toată lumea le poate înțelege, nu vom folosi o limbă ca rusă sau chineza aici, ci mai degrabă o limbă engleză specializată: codul Python.

Biblioteca [🤗 Datasets](https://github.com/huggingface/datasets) ne poate ajuta să asamblăm un corpus de cod sursă Python. Vom folosi funcția obișnuită `load_dataset()` pentru a descărca și a păstra în cache datasetul [CodeSearchNet](https://huggingface.co/datasets/code_search_net). Acest dataset a fost creat pentru [Provocarea CodeSearchNet](https://wandb.ai/github/CodeSearchNet/benchmark) și conține milioane de funcții din biblioteci open-source de pe GitHub în mai multe limbaje de programare. Aici, vom încărca partea Python a acestui dataset:

```py
from datasets import load_dataset

# Acest lucru poate dura câteva minute pentru a încărca, așa că luați o pauză și beți o ceașcă de cafea sau ceai în timp ce așteptați!
raw_datasets = load_dataset("code_search_net", "python")
```

Putem să ne uităm la splitul de antrenare pentru a vedea la care coloane avem acces:

```py
raw_datasets["train"]
```

```python out
Dataset({
    features: ['repository_name', 'func_path_in_repository', 'func_name', 'whole_func_string', 'language', 
      'func_code_string', 'func_code_tokens', 'func_documentation_string', 'func_documentation_tokens', 'split_name', 
      'func_code_url'
    ],
    num_rows: 412178
})
```

Putem vedea că dataset-ul separă docstringurile de cod și sugerează o tokenizare a ambelor. Aici, vom folosi doar coloana `whole_func_string` pentru a antrena tokenizerul nostru. Putem să ne uităm la un exemplu al unei astfel de funcții prin indexarea în splitul de antrenare:

```py
print(raw_datasets["train"][123456]["whole_func_string"])
```

care ar trebui să printeze următorul lucru:

```out
def handle_simple_responses(
      self, timeout_ms=None, info_cb=DEFAULT_MESSAGE_CALLBACK):
    """Accepts normal responses from the device.

    Args:
      timeout_ms: Timeout in milliseconds to wait for each response.
      info_cb: Optional callback for text sent from the bootloader.

    Returns:
      OKAY packet's message.
    """
    return self._accept_responses('OKAY', info_cb, timeout_ms=timeout_ms)
```

Primul lucru pe care trebuie să-l facem este să transformăm setul de date într-un _iterator_ de liste de texte - de exemplu, o listă de liste de texte. Utilizarea listelor de texte va permite tokenizerului nostru să funcționeze mai rapid (antrenându-se pe batch-uri de texte în loc de a procesa texte individuale unul câte unul), iar acesta ar trebui să fie un iterator dacă dorim să evităm să avem tot în memoria RAM deodată. Dacă corpusul tău este uriaș, veți dori să profitați de faptul că 🤗 Datasets nu încarcă totul în memoria RAM, ci stochează elementele datasetului pe disc.

Următoarea operație ar crea o listă de liste de 1.000 de texte fiecare, dar ar încărca totul în memorie:

```py
# Nu faceți uncomment următoarei linii dacă datasetul vostru este mare, ci doar dacă este mic!
# training_corpus = [raw_datasets["train"][i: i + 1000]["whole_func_string"] for i in range(0, len(raw_datasets["train"]), 1000)]
```

Utilizând un generator Python, putem evita ca Python să încarce orice în memorie până când este realmente necesar. Pentru a crea un astfel de generator, trebuie doar să înlocuiți parantezele pătrate cu paranteze rotunde:

```py
training_corpus = (
    raw_datasets["train"][i : i + 1000]["whole_func_string"]
    for i in range(0, len(raw_datasets["train"]), 1000)
)
```

Această linie de cod nu extrage niciun element al setului de date; doar creează un obiect pe care îl puteți utiliza într-un `for` loop din Python. Textele vor fi încărcate doar atunci când veți avea nevoie de ele(adică atunci când sunteți la pasul loopului `for` care le solicită), iar doar 1.000 de texte vor fi încărcate la un moment dat. Acest mod vă permite să nu epuizați memoria RAM chiar dacă prelucrați un set de date uriaș.

Problema cu un obiect generator este că poate fi utilizat doar o dată, așadar, în loc să ne ofere lista primelor 10 cifre de două ori:

```py
gen = (i for i in range(10))
print(list(gen))
print(list(gen))
```

noi le primim o dată și apoi o listă goală:

```python out
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[]
```

Din acest motive noi definim o funcție ce returnează în schimb un generator: 

```py
def get_training_corpus():
    return (
        raw_datasets["train"][i : i + 1000]["whole_func_string"]
        for i in range(0, len(raw_datasets["train"]), 1000)
    )


training_corpus = get_training_corpus()
```

În același timp poți defini un generator înăuntrul unui `for` loop folosing statementul `yield`:

```py
def get_training_corpus():
    dataset = raw_datasets["train"]
    for start_idx in range(0, len(dataset), 1000):
        samples = dataset[start_idx : start_idx + 1000]
        yield samples["whole_func_string"]
```

ceea ce va produce același generator ca înainte, dar îți va permite să folosești o logică mai complexă decât cea pe care ai putea să o folosești într-un list comprehension.

## Antrenarea unui nou tokenizer[[training-a-new-tokenizer]]

Acum că avem corpusul nostru sub forma unui iterator de batch-uri de texte, suntem gata să antrenăm un nou tokenizer. Pentru a face acest lucru, trebuie mai întâi să încărcăm tokenizerul pe care dorim să-l asociem cu modelul nostru (aici, GPT-2):

```py
from transformers import AutoTokenizer

old_tokenizer = AutoTokenizer.from_pretrained("gpt2")
```

Chiar dacă urmează să antrenăm un nou tokenizer, este o idee bună să facem acest lucru pentru a evita să începem să facem tot de la zero. Astfel, nu vom fi nevoiți să specificăm nimic despre algoritmul de tokenizare sau despre special tokens pe care îi vom utiliza; noul nostru tokenizer va fi exact la fel ca GPT-2, iar singur lucrul care se va schimba este vocabularul, care va fi determinat de antrenarea pe corpusul nostru.

Mai întâi, hai să vedem cum ar interpreta acest tokenizer un exemplu de funcție:

```py
example = '''def add_numbers(a, b):
    """Add the two numbers `a` and `b`."""
    return a + b'''

tokens = old_tokenizer.tokenize(example)
tokens
```

```python out
['def', 'Ġadd', '_', 'n', 'umbers', '(', 'a', ',', 'Ġb', '):', 'Ċ', 'Ġ', 'Ġ', 'Ġ', 'Ġ"""', 'Add', 'Ġthe', 'Ġtwo',
 'Ġnumbers', 'Ġ`', 'a', '`', 'Ġand', 'Ġ`', 'b', '`', '."', '""', 'Ċ', 'Ġ', 'Ġ', 'Ġ', 'Ġreturn', 'Ġa', 'Ġ+', 'Ġb']
```

Acest tokenizer are câteva simboluri speciale, cum ar fi `Ġ` și `Ċ`, care denotă spații și noi linii, respectiv. Așa cum se poate vedea, acest lucru nu este prea eficient: tokenizerul returnează tokenuri individuale pentru fiecare spațiu, când ar putea grupa împreună nivelurile de indentare (deoarece având seturi de patru sau opt spații va fi foarte comun în cod). De asemenea, a divizat numele funcției într-un mod ciudat, nefiind obișnuit să vadă cuvinte care conțin caracterele `_`.

Acum hai să antrenăm un nou tokenizer și să vedem dacă rezolvă aceste probleme. Pentru aceasta, vom utiliza metoda `train_new_from_iterator()`:

```py
tokenizer = old_tokenizer.train_new_from_iterator(training_corpus, 52000)
```

Această comandă ar putea dura puțin timp dacă corpusul este foarte mare, dar pentru acest dataset de 1,6 GB de texte este extrem de rapid (1 minut și 16 secunde pe un procesor AMD Ryzen 9 3900X cu 12 nuclee).

Reține că `AutoTokenizer.train_new_from_iterator()` funcționează doar dacă tokenizerul pe care îl utilizați este un tokenizer "rapid". Așa cum veți vedea în următoarea secțiune, biblioteca 🤗 Transformers conține două tipuri de tokenizeri: unii sunt scriși în pur Python, iar alții (cei rapizi) sunt susținuți de biblioteca 🤗 Tokenizers, care este scrisă în limbajul de programare Rust. Python este limbajul cel mai frecvent utilizat pentru aplicații de data science și deep learning, dar atunci când orice trebuie să fie paralelizat pentru a fi rapid, trebuie să fie scris într-un alt limbaj de programare. De exemplu, multiplicările matricelor care sunt la baza calculelor modelului sunt scrise în CUDA, o bibliotecă C optimizată pentru GPU-uri.

Antrenarea unui tokenizer nou în pur  Python ar fi extrem de lent, de aceea am dezvoltat biblioteca 🤗 Tokenizers. Reține că, la fel cum nu a trebuit să învățați limbajul CUDA pentru a putea executa modelul pe un batch de inputuri pe un GPU, nu veți avea nevoie să învățați Rust pentru a utiliza un tokenizer rapid. Biblioteca 🤗 Tokenizers oferă legături Python pentru multe metode care apelează intern unele bucăți de cod în Rust; de exemplu, pentru a paraleliza antrenarea noului tokenizer sau, așa cum am văzut în [Capitolul 3](/course/chapter3), tokenizarea unui batch de inputuri.

Majoritatea modelelor Transformer au un tokenizer rapid disponibil (există unele excepții pe care le puteți verifica [aici](https://huggingface.co/transformers/#supported-frameworks)), iar API-ul `AutoTokenizer` selectează întotdeauna tokenizerul rapid pentru tine dacă este disponibil. În următoarea secțiune, vom examina unele dintre celelalte caracteristici speciale ale tokenizerilor rapizi, care vor fi foarte utile pentru sarcini precum clasificarea tokenilor și răspunderea la întrebări. Înainte de a face acest lucru, totuși, să încercăm noul nostru tokenizer pe exemplul anterior:

```py
tokens = tokenizer.tokenize(example)
tokens
```

```python out
['def', 'Ġadd', '_', 'numbers', '(', 'a', ',', 'Ġb', '):', 'ĊĠĠĠ', 'Ġ"""', 'Add', 'Ġthe', 'Ġtwo', 'Ġnumbers', 'Ġ`',
 'a', '`', 'Ġand', 'Ġ`', 'b', '`."""', 'ĊĠĠĠ', 'Ġreturn', 'Ġa', 'Ġ+', 'Ġb']
```

Aici din nou vedem simboluri speciale ca `Ġ` sau `Ċ` care denotă spații sau linii noi, dar în același timp putem vedea că tokenizerul nostru a învățat câțiva tokens care sunt foarte specifici la corpusul de funcții Python: de exemplu, tokenul `ĊĠĠĠ` care reprezintă indentarea, sau tokenul `Ġ"""` care reprezintă cele trei ghilimele cu care se începe un docstring. Tokenizerul, de asemenea face split corect numelui funției pe `_`. Aceasta chiar este o reprezentare compactă: comparativ, utilizând limba tokenizerului englez pe același exemplu ne va da o propoziție mai lungă:

```py
print(len(tokens))
print(len(old_tokenizer.tokenize(example)))
```

```python out
27
36
```

Hai să ne uităm la un alt exemplu:

```python
example = """class LinearLayer():
    def __init__(self, input_size, output_size):
        self.weight = torch.randn(input_size, output_size)
        self.bias = torch.zeros(output_size)

    def __call__(self, x):
        return x @ self.weights + self.bias
    """
tokenizer.tokenize(example)
```

```python out
['class', 'ĠLinear', 'Layer', '():', 'ĊĠĠĠ', 'Ġdef', 'Ġ__', 'init', '__(', 'self', ',', 'Ġinput', '_', 'size', ',',
 'Ġoutput', '_', 'size', '):', 'ĊĠĠĠĠĠĠĠ', 'Ġself', '.', 'weight', 'Ġ=', 'Ġtorch', '.', 'randn', '(', 'input', '_',
 'size', ',', 'Ġoutput', '_', 'size', ')', 'ĊĠĠĠĠĠĠĠ', 'Ġself', '.', 'bias', 'Ġ=', 'Ġtorch', '.', 'zeros', '(',
 'output', '_', 'size', ')', 'ĊĊĠĠĠ', 'Ġdef', 'Ġ__', 'call', '__(', 'self', ',', 'Ġx', '):', 'ĊĠĠĠĠĠĠĠ',
 'Ġreturn', 'Ġx', 'Ġ@', 'Ġself', '.', 'weights', 'Ġ+', 'Ġself', '.', 'bias', 'ĊĠĠĠĠ']
```

În plus față de tokenul corespunzător unei indentări, aici putem vedea și un token pentru o indentare dublă: `ĊĠĠĠĠĠĠĠ`. Cuvintele speciale din Python, cum ar fi `class`, `init`, `call`, `self` și `return`, sunt tokenizate fiecare ca un singur token, și putem vedea că, pe lângă divizarea la `_` și `.`, tokenizerul divizează corect chiar și numele scrise în stil camel-case: `LinearLayer` este tokenizeat ca `["ĠLinear", "Layer"]`.

## Salvarea tokenizerului[[saving-the-tokenizer]]

Pentru a ne asigura că îl putem utiliza mai târziu, trebuie să salvăm noul nostru tokenizer. Asemănător salvării unui model, acest lucru se realizează cu metoda `save_pretrained()`:

```py
tokenizer.save_pretrained("code-search-net-tokenizer")
```

Acest lucru va crea un nou folder numit *code-search-net-tokenizer*, care va conține toate fișierele necesare pentru a reîncărca tokenizerul. Dacă doriți să partajați acest tokenizer cu colegii și prietenii tăi, îl puteți încărca pe Hub prin conectarea la contul tău. Dacă lucrați într-un notebook, există un convenience function pentru a vă ajuta cu acest lucru:

```python
from huggingface_hub import notebook_login

notebook_login()
```

Acest lucru va afișa un widget în care puteți introduce credențialele de conectare Hugging Face. Dacă nu lucrați într-un notebook, introduceți simplu următoarea linie în terminal:

```bash
huggingface-cli login
```

După ce v-ați conectat, puteți face push tokenizerului prin executarea următoarei comenzi:

```py
tokenizer.push_to_hub("code-search-net-tokenizer")
```

Acest lucru va crea un nou repositoriu în namespace-ul tău cu numele `code-search-net-tokenizer`, care va conține fișierul tokenizerului. După aceea, puteți încărca tokenizerul de oriunde cu metoda `from_pretrained()`:

```py
# Înlocuiți "huggingface-course" mai jos cu namespace-ul tău pentru a utiliza propriul tokenizer
tokenizer = AutoTokenizer.from_pretrained("huggingface-course/code-search-net-tokenizer")
```

Acum sunteți gata să antrenați un model de limbaj de la zero și să îl ajustați pentru sarcina voastră! Vom face acest lucru în [Capitolul 7](/course/chapter7), dar mai întâi, în continuarea acestui capitol, vom arunca o privire mai atentă asupra tokenizerilor rapizi și vom explora în detaliu ce se întâmplă atunci când apelați metoda `train_new_from_iterator()`.

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter6/2.mdx" />

### Evaluarea
https://huggingface.co/learn/course/ro/chapter11/5.md

# Evaluarea

Cu un model ajustat fin prin SFT sau LoRA SFT, ar trebui să îl evaluăm pe criterii de referință standard. Ca ingineri de machine learning, ar trebui să mențineți o suită de evaluări relevante pentru domeniul dumneavoastră țintă de interes. În această pagină, vom privi la unele dintre criteriile de referință cele mai comune și cum să le folosiți pentru a evalua modelul dumneavoastră. De asemenea, vom vedea cum să creați criterii de referință personalizate pentru cazul specific de utilizare.

## Criterii de referință automate

Criteriile de referință automate servesc ca instrumente standardizate pentru evaluarea modelelor de limbaj pe diferite sarcini și capacități. În timp ce acestea oferă un punct de plecare util pentru înțelegerea performanței modelului, este important să recunoaștem că reprezintă doar o piesă dintr-o strategie de evaluare cuprinzătoare.

## Înțelegerea criteriilor de referință automate

Criteriile de referință automate constau de obicei din seturi de date curate cu sarcini și metrici de evaluare predefinite. Aceste criterii de referință au ca scop să evalueze diverse aspecte ale capacității modelului, de la înțelegerea de bază a limbajului la raționament complex. Avantajul cheie al folosirii criteriilor de referință automate este standardizarea lor - permit compararea consistentă între modele diferite și oferă rezultate reproductibile.

Cu toate acestea, este crucial să înțelegem că performanța la criterii de referință nu se traduce întotdeauna direct în eficacitate din lumea reală. Un model care excelează la criterii de referință academice poate totuși să se confrunte cu aplicații de domeniu specifice sau cazuri de utilizare practice.

## Criterii de referință pentru cunoștințe generale

[MMLU](https://huggingface.co/datasets/cais/mmlu) (Massive Multitask Language Understanding) testează cunoștințele pe 57 de subiecte, de la știință la științe umane. Deși cuprinzător, poate să nu reflecte profunzimea expertizei necesare pentru domenii specifice. TruthfulQA evaluează tendința unui model de a reproduce concepții greșite comune, deși nu poate captura toate formele de dezinformare.

## Criterii de referință pentru raționament

[BBH](https://huggingface.co/datasets/lukaemon/bbh) (Big Bench Hard) și [GSM8K](https://huggingface.co/datasets/openai/gsm8k) se concentrează pe sarcini de raționament complex. BBH testează gândirea logică și planificarea, în timp ce GSM8K vizează specific rezolvarea problemelor matematice. Aceste criterii de referință ajută la evaluarea capacităților analitice dar pot să nu captureze raționamentul nuanțat necesar în scenarii din lumea reală.

## Înțelegerea limbajului

[HELM](https://github.com/stanford-crfm/helm) oferă un cadru de evaluare holistic. Criterii de referință precum HELM oferă perspective asupra capacităților de procesare a limbajului pe aspecte precum bunul simț, cunoștințele despre lume și raționamentul. Dar pot să nu reprezinte complet complexitatea conversației naturale sau terminologia specifică domeniului.

## Criterii de referință specifice domeniului

Să ne uităm la câteva criterii de referință care se concentrează pe domenii specifice precum matematica, codarea și chatul.

[Criteriul de referință MATH](https://huggingface.co/papers/2103.03874) este un alt instrument important de evaluare pentru raționamentul matematic. Constă din 12.500 de probleme din competiții de matematică, acoperind algebra, geometria, teoria numerelor, numărarea, probabilitatea și altele. Ceea ce face MATH deosebit de provocator este că necesită raționament în mai mulți pași, înțelegerea notației matematice formale și capacitatea de a genera soluții pas cu pas. Spre deosebire de sarcinile aritmetice mai simple, problemele MATH necesită adesea strategii sofisticate de rezolvare a problemelor și aplicații de concepte matematice.

[Criteriul de referință HumanEval](https://github.com/openai/human-eval) este un set de date de evaluare axat pe codare format din 164 de probleme de programare. Criteriul de referință testează capacitatea unui model de a genera cod Python funcțional corect care rezolvă sarcinile de programare date. Ceea ce face HumanEval deosebit de valoros este că evaluează atât capacitățile de generare de cod, cât și corectitudinea funcțională prin executarea efectivă a cazurilor de test, mai degrabă decât doar similaritatea superficială cu soluțiile de referință. Problemele variază de la manipularea de bază a șirurilor la algoritmi și structuri de date mai complexe.

[Alpaca Eval](https://tatsu-lab.github.io/alpaca_eval/) este un cadru de evaluare automatizat conceput pentru a evalua calitatea modelelor de limbaj care urmează instrucțiuni. Folosește GPT-4 ca judecător pentru a evalua ieșirile modelului pe diverse dimensiuni, inclusiv utilitatea, onestitatea și inofensivitatea. Cadrul include un set de date cu 805 de prompturi atent curate și poate evalua răspunsurile față de modele multiple de referință precum Claude, GPT-4 și altele. Ceea ce face Alpaca Eval deosebit de util este capacitatea sa de a oferi evaluări consistente, scalabile fără a necesita adnotatori umani, în timp ce încă capturează aspecte nuanțate ale performanței modelului pe care metricile tradiționale le-ar putea rata.

## Abordări alternative de evaluare

Multe organizații au dezvoltat metode alternative de evaluare pentru a aborda limitările criteriilor de referință standard:

### LLM-ca-judecător

Folosirea unui model de limbaj pentru a evalua ieșirile altuia a devenit din ce în ce mai populară. Această abordare poate oferi feedback mai nuanțat decât metricile tradiționale, deși vine cu propriile sale prejudecăți și limitări.

### Arene de evaluare

Arenele de evaluare precum [Chatbot Arena](https://lmarena.ai/) oferă o abordare unică pentru evaluarea LLM prin feedback crowdsourced. În aceste platforme, utilizatorii se angajează în "bătălii" anonime între două LLM-uri, punând întrebări și votând care model oferă răspunsuri mai bune. Această abordare capturează modelele de utilizare și preferințele din lumea reală prin întrebări diverse, provocatoare, cu studii care arată acord puternic între voturile crowdsourced și evaluările experților. Deși puternice, aceste platforme au limitări, inclusiv prejudecăți potențiale ale bazei de utilizatori, distribuții distorsionate ale prompturilor și o concentrare principală pe utilitate mai degrabă decât pe considerațiile de siguranță.

### Suite de criterii de referință personalizate

Organizațiile dezvoltă adesea suite interne de criterii de referință adaptate nevoilor și cazurilor lor specifice de utilizare. Acestea ar putea include teste de cunoștințe specifice domeniului sau scenarii de evaluare care reflectă condițiile reale de implementare.

## Evaluare personalizată

În timp ce criteriile de referință standard oferă o linie de bază utilă, nu ar trebui să fie singura metodă de evaluare. Iată cum să dezvoltați o abordare mai cuprinzătoare:

1. Începeți cu criterii de referință standard relevante pentru a stabili o linie de bază și a permite comparația cu alte modele.

2. Identificați cerințele și provocările specifice ale cazului dumneavoastră de utilizare. Ce sarcini va îndeplini efectiv modelul? Ce tipuri de erori ar fi cele mai problematice?

3. Dezvoltați seturi de date de evaluare personalizate care reflectă cazul real de utilizare. Aceasta ar putea include:
   - Întrebări reale ale utilizatorilor din domeniul dumneavoastră
   - Cazuri comune de margine pe care le-ați întâlnit
   - Exemple de scenarii deosebit de provocatoare

4. Considerați implementarea unei strategii de evaluare pe mai multe niveluri:
   - Metrici automate pentru feedback rapid
   - Evaluare umană pentru înțelegere nuanțată
   - Revizuire de experți de domeniu pentru aplicații specializate
   - Testare A/B în medii controlate

## Implementarea evaluărilor personalizate

În această secțiune, vom implementa evaluarea pentru modelul nostru ajustat fin. Putem folosi [`lighteval`](https://github.com/huggingface/lighteval) pentru a evalua modelul nostru ajustat fin pe criterii de referință standard, care conține o gamă largă de sarcini încorporate în bibliotecă. Trebuie doar să definim sarcinile pe care dorim să le evaluăm și parametrii pentru evaluare.

Sarcinile LightEval sunt definite folosind un format specific:

```
{suite}|{task}|{num_few_shot}|{auto_reduce}
```

| Parametru | Descriere |
|-----------|-----------|
| `suite` | Suita de criterii de referință (de ex., 'mmlu', 'truthfulqa') |
| `task` | Sarcina specifică din suită (de ex., 'abstract_algebra') |
| `num_few_shot` | Numărul de exemple de inclus în prompt (0 pentru zero-shot) |
| `auto_reduce` | Dacă să reducă automat exemplele few-shot dacă promptul este prea lung (0 sau 1) |

Exemplu: `"mmlu|abstract_algebra|0|0"` evaluează pe sarcina de algebră abstractă MMLU cu inferență zero-shot.

## Exemplu de pipeline de evaluare

Să configurăm un pipeline de evaluare pentru modelul nostru ajustat fin. Vom evalua modelul pe un set de sub-sarcini care se raportează la domeniul medicinei.

Iată un exemplu complet de evaluare pe criterii de referință automate relevante pentru un domeniu specific folosind Lighteval cu backend-ul VLLM:

```bash
lighteval accelerate \
    "pretrained=your-model-name" \
    "mmlu|anatomy|0|0" \
    "mmlu|high_school_biology|0|0" \
    "mmlu|high_school_chemistry|0|0" \
    "mmlu|professional_medicine|0|0" \
    --max_samples 40 \
    --batch_size 1 \
    --output_path "./results" \
    --save_generations true
```

Rezultatele sunt afișate în format tabular arătând:

```
|                  Task                  |Version|Metric|Value |   |Stderr|
|----------------------------------------|------:|------|-----:|---|-----:|
|all                                     |       |acc   |0.3333|±  |0.1169|
|leaderboard:mmlu:_average:5             |       |acc   |0.3400|±  |0.1121|
|leaderboard:mmlu:anatomy:5              |      0|acc   |0.4500|±  |0.1141|
|leaderboard:mmlu:high_school_biology:5  |      0|acc   |0.1500|±  |0.0819|
```

Lighteval include, de asemenea, un API Python pentru sarcini de evaluare mai detaliate, care este util pentru manipularea rezultatelor într-un mod mai flexibil. Consultați [documentația Lighteval](https://huggingface.co/docs/lighteval/using-the-python-api) pentru mai multe informații.

<Tip>

✏️ **Încercați!** Evaluați modelul dumneavoastră ajustat fin pe o sarcină specifică în lighteval.

</Tip>

# Chestionar de sfârșit de capitol[[end-of-chapter-quiz]]

<CourseFloatingBanner
    chapter={11}
    classNames="absolute z-10 right-0 top-0"
/>

### 1. Care sunt principalele avantaje ale folosirii criteriilor de referință automate pentru evaluarea modelului?

<Question
	choices={[
		{
			text: "Ele oferă metrici perfecte de performanță din lumea reală",
			explain: "Incorect! Deși criteriile de referință automate sunt utile, nu se traduc întotdeauna direct în performanța din lumea reală."
		},
		{
			text: "Ele permit compararea standardizată între modele și oferă rezultate reproductibile",
			explain: "Corect! Aceasta este unul dintre beneficiile cheie ale criteriilor de referință automate.",
			correct: true
		},
		{
			text: "Ele elimină nevoia pentru orice altă formă de evaluare",
			explain: "Incorect! Criteriile de referință automate ar trebui să facă parte dintr-o strategie de evaluare cuprinzătoare, nu singura metodă."
		}
	]}
/>

### 2. Care criteriu de referință testează specific cunoștințele pe 57 de subiecte diferite?

<Question
	choices={[
		{
			text: "BBH (Big Bench Hard)",
			explain: "Incorect! BBH se concentrează pe sarcini de raționament complex, nu pe cunoștințe ample de subiecte."
		},
		{
			text: "GSM8K",
			explain: "Incorect! GSM8K vizează specific rezolvarea problemelor matematice."
		},
		{
			text: "MMLU",
			explain: "Corect! MMLU (Massive Multitask Language Understanding) testează cunoștințele pe 57 de subiecte, de la știință la științe umane.",
			correct: true
		}
	]}
/>

### 3. Ce este LLM-ca-judecător?

<Question
	choices={[
		{
			text: "Folosirea unui model de limbaj pentru a evalua ieșirile altuia",
			explain: "Corect! Aceasta este o abordare alternativă de evaluare care poate oferi feedback mai nuanțat.",
			correct: true
		},
		{
			text: "Un criteriu de referință care testează raționamentul judiciar",
			explain: "Incorect! LLM-ca-judecător se referă la folosirea unui model pentru a evalua pe altul, nu la testarea raționamentului judiciar."
		},
		{
			text: "O metodă pentru antrenarea modelelor pe seturi de date juridice",
			explain: "Incorect! Aceasta nu este legată de antrenarea pe date juridice, ci de folosirea unui model pentru a evalua ieșirile altuia."
		}
	]}
/>

### 4. Ce ar trebui inclus într-o strategie de evaluare cuprinzătoare?

<Question
	choices={[
		{
			text: "Doar criterii de referință automate",
			explain: "Incorect! O strategie cuprinzătoare ar trebui să includă mai multe tipuri de evaluare."
		},
		{
			text: "Metrici automate, evaluare umană, revizuire de experți și testare A/B",
			explain: "Corect! O abordare cuprinzătoare include mai multe tipuri de evaluare pentru o înțelegere completă a performanței.",
			correct: true
		},
		{
			text: "Doar evaluare umană",
			explain: "Incorect! Deși evaluarea umană este importantă, o strategie cuprinzătoare necesită mai multe abordări."
		}
	]}
/>

### 5. Care este o limitare a criteriilor de referință automate?

<Question
	choices={[
		{
			text: "Sunt prea scumpe de rulat",
			explain: "Incorect! Costul nu este de obicei principala limitare a criteriilor de referință automate."
		},
		{
			text: "Performanța la criterii de referință nu se traduce întotdeauna direct în eficacitate din lumea reală",
			explain: "Corect! Aceasta este o limitare cheie de ținut minte când folosiți criterii de referință automate.",
			correct: true
		},
		{
			text: "Pot evalua doar modele mici",
			explain: "Incorect! Criteriile de referință automate pot fi folosite pentru a evalua modele de diverse dimensiuni."
		}
	]}
/>

### 6. Care este scopul creării seturilor de date de evaluare personalizate?

<Question
	choices={[
		{
			text: "Pentru a reflecta cazul dumneavoastră specific de utilizare și a include întrebări reale ale utilizatorilor din domeniul dumneavoastră",
			explain: "Corect! Seturile de date personalizate ajută să asigure că evaluarea este relevantă pentru nevoile dumneavoastră specifice.",
			correct: true
		},
		{
			text: "Pentru a înlocui complet criteriile de referință standard",
			explain: "Incorect! Seturile de date personalizate ar trebui să completeze, nu să înlocuiască, criteriile de referință standard."
		},
		{
			text: "Pentru a face evaluarea mai ușoară",
			explain: "Incorect! Crearea seturilor de date personalizate necesită efort suplimentar dar oferă evaluare mai relevantă."
		}
	]}
/> 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter11/5.mdx" />

### Fine-tuning supervizat
https://huggingface.co/learn/course/ro/chapter11/1.md

# Fine-tuning supervizat

În [Capitolul 2 Secțiunea 2](/course/chapter2/2), am văzut că modelele de limbaj generativ pot fi ajustate pentru sarcini specifice, cum ar fi rezumarea și răspunsul la întrebări. Cu toate acestea, în zilele noastre este mult mai frecvent să ajustăm modelele de limbaj pe o gamă largă de sarcini simultan; o metodă cunoscută sub numele de fine-tuning supervizat (SFT). Acest proces ajută modelele să devină mai versatile și capabile să gestioneze diverse cazuri de utilizare. Majoritatea LLM-urilor cu care oamenii interacționează pe platforme precum ChatGPT au trecut prin SFT pentru a fi mai utile și aliniate cu preferințele umane. Vom împărți acest capitol în patru secțiuni:

## 1️⃣ Template-uri de chat

Template-urile de chat structurează interacțiunile dintre utilizatori și modelele AI, asigurând răspunsuri consecvente și adecvate contextual. Acestea includ componente precum prompturi de sistem și mesaje bazate pe roluri.

## 2️⃣ Fine-tuning supervizat

Fine-tuningul supervizat (SFT) este un proces critic pentru adaptarea modelelor de limbaj pre-antrenate la sarcini specifice. Aceasta implică antrenarea modelului pe un set de date specific sarcinii cu exemple etichetate. Pentru un ghid detaliat despre SFT, inclusiv pașii cheie și cele mai bune practici, consultați [secțiunea de fine-tuning supervizat din documentația TRL](https://huggingface.co/docs/trl/en/sft_trainer).

## 3️⃣ Adaptarea de rang scăzut (LoRA)

Adaptarea de rang scăzut (LoRA) este o tehnică pentru fine-tuningul modelelor de limbaj prin adăugarea de matrice de rang scăzut la straturile modelului. Aceasta permite un fine-tuning eficient păstrând în același timp cunoștințele pre-antrenate ale modelului. Unul dintre beneficiile cheie ale LoRA este economia semnificativă de memorie pe care o oferă, făcând posibilă ajustarea modelelor mari pe hardware cu resurse limitate.

## 4️⃣ Evaluarea

Evaluarea este un pas crucial în procesul de fine-tuning. Ne permite să măsurăm performanța modelului pe un set de date specific sarcinii.

<Tip>
⚠️ Pentru a beneficia de toate funcționalitățile disponibile cu Model Hub și 🤗 Transformers, recomandăm <a href="https://huggingface.co/join">crearea unui cont</a>.
</Tip>

## Referințe

- [Documentația Transformers despre template-urile de chat](https://huggingface.co/docs/transformers/main/en/chat_templating)
- [Script pentru fine-tuning supervizat în TRL](https://github.com/huggingface/trl/blob/main/trl/scripts/sft.py)
- [`SFTTrainer` în TRL](https://huggingface.co/docs/trl/main/en/sft_trainer)
- [Lucrarea despre optimizarea directă a preferințelor](https://arxiv.org/abs/2305.18290)
- [Fine-tuning supervizat cu TRL](https://huggingface.co/docs/trl/sft_trainer)
- [Cum să faceți fine-tuning la Google Gemma cu ChatML și Hugging Face TRL](https://github.com/huggingface/alignment-handbook)  
- [Fine-tuning LLM pentru a genera cataloage de produse persane în format JSON](https://huggingface.co/learn/cookbook/en/fine_tuning_llm_to_generate_persian_product_catalogs_in_json_format) 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter11/1.mdx" />

### Fine-tuning supervizat
https://huggingface.co/learn/course/ro/chapter11/3.md

# Fine-tuning supervizat

Fine-tuningul supervizat (SFT) este un proces folosit în principal pentru a adapta modelele de limbaj pre-antrenate să urmeze instrucțiuni, să se angajeze în dialog și să folosească formate specifice de ieșire. În timp ce modelele pre-antrenate au capacități generale impresionante, SFT ajută la transformarea lor în modele asemănătoare asistentului care pot înțelege și răspunde mai bine la prompturile utilizatorilor. Acest lucru se realizează de obicei prin antrenare pe seturi de date cu conversații și instrucțiuni scrise de oameni.

Această pagină oferă un ghid pas cu pas pentru fine-tuningul modelului [`deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B`](https://huggingface.co/deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B) folosind [`SFTTrainer`](https://huggingface.co/docs/trl/en/sft_trainer). Urmând acești pași, puteți adapta modelul să îndeplinească anumite sarcini mai eficient.

## Când să utilizați SFT

Înainte de a se adânci în implementare, este important să înțelegem când SFT este alegerea potrivită pentru proiectul dumneavoastră. Ca primul pas, ar trebui să considerați dacă folosirea unui model existent ajustat pentru instrucțiuni cu prompturi bine elaborate ar fi suficientă pentru cazul dumneavoastră de utilizare. SFT implică resurse computaționale semnificative și efort de inginerie, așa că ar trebui urmărit doar când promptarea modelelor existente se dovedește insuficientă.

<Tip>
Considerați SFT doar dacă:
- Aveți nevoie de performanțe suplimentare dincolo de ceea ce poate realiza promptarea
- Aveți un caz de utilizare specific în care costul folosirii unui model mare de uz general depășește costul fine-tuningului unui model mai mic
- Aveți nevoie de formate de ieșire specializate sau cunoștințe specifice domeniului cu care modelele existente se confruntă
</Tip>

Dacă determinați că SFT este necesar, decizia de a continua depinde de doi factori principali:

### Controlul template-ului
SFT permite control precis asupra structurii de ieșire a modelului. Acest lucru este deosebit de valoros când aveți nevoie ca modelul să:
1. Genereze răspunsuri într-un format specific de template de chat
2. Urmeze scheme stricte de ieșire
3. Mențină stiluri consecvente în toate răspunsurile

### Adaptarea la domeniu
Când lucrați în domenii specializate, SFT ajută la alinierea modelului cu cerințele specifice domeniului prin:
1. Predarea terminologiei și conceptelor domeniului
2. Impunerea standardelor profesionale
3. Gestionarea adecvată a întrebărilor tehnice
4. Urmarea ghidurilor specifice industriei

<Tip>
Înainte de a începe SFT, evaluați dacă cazul dumneavoastră de utilizare necesită:
- Formatare precisă de ieșire
- Cunoștințe specifice domeniului
- Modele consecvente de răspuns
- Aderarea la ghiduri specifice

Această evaluare va ajuta să determinați dacă SFT este abordarea potrivită pentru nevoile dumneavoastră.
</Tip>

## Pregătirea setului de date

Procesul de fine-tuning supervizat necesită un set de date specific sarcinii structurat cu perechi intrare-ieșire. Fiecare pereche ar trebui să conțină:
1. Un prompt de intrare
2. Răspunsul așteptat al modelului
3. Orice context sau metadate suplimentare

Calitatea datelor de antrenare este crucială pentru succesul fine-tuningului. Să vedem cum să pregătim și să validăm setul de date:

<iframe
  src="https://huggingface.co/datasets/HuggingFaceTB/smoltalk/embed/viewer/all/train?row=0"
  frameborder="0"
  width="100%"
  height="360px"
></iframe>

## Configurația antrenamentului

Succesul fine-tuningului depinde în mare măsură de alegerea parametrilor de antrenare corecți. Să explorăm fiecare parametru important și cum să îi configurați eficient:

Configurația SFTTrainer necesită considerarea mai multor parametri care controlează procesul de antrenare. Să explorăm fiecare parametru și scopul lor:

1. **Parametrii duratei antrenamentului**:
   - `num_train_epochs`: Controlează durata totală a antrenamentului
   - `max_steps`: Alternativă la epoci, stabilește numărul maxim de pași de antrenare
   - Mai multe epoci permit învățare mai bună dar riscă supraadaptarea

2. **Parametrii dimensiunii batch-ului**:
   - `per_device_train_batch_size`: Determină utilizarea memoriei și stabilitatea antrenamentului
   - `gradient_accumulation_steps`: Permite dimensiuni efective mai mari ale batch-ului
   - Batch-uri mai mari oferă gradienți mai stabili dar necesită mai multă memorie

3. **Parametrii ratei de învățare**:
   - `learning_rate`: Controlează dimensiunea actualizărilor greutăților
   - `warmup_ratio`: Porțiunea de antrenament folosită pentru încălzirea ratei de învățare
   - Prea mare poate cauza instabilitate, prea mică rezultă în învățare lentă

4. **Parametrii de monitorizare**:
   - `logging_steps`: Frecvența înregistrării metricilor
   - `eval_steps`: Cât de des să evalueze pe datele de validare
   - `save_steps`: Frecvența salvării punctelor de verificare ale modelului

<Tip>
Începeți cu valori conservatoare și ajustați bazat pe monitorizare:
- Începeți cu 1-3 epoci
- Folosiți dimensiuni mai mici ale batch-ului inițial
- Monitorizați metricile de validare atent
- Ajustați rata de învățare dacă antrenamentul este instabil
</Tip>

## Implementare cu TRL

Acum că înțelegem componentele cheie, să implementăm antrenamentul cu validare și monitorizare adecvate. Vom folosi clasa `SFTTrainer` din biblioteca Transformers Reinforcement Learning (TRL), care este construită pe biblioteca `transformers`. Iată un exemplu complet folosind biblioteca TRL:

```python
from datasets import load_dataset
from trl import SFTConfig, SFTTrainer
import torch

# Setați dispozitivul
device = "cuda" if torch.cuda.is_available() else "cpu"

# Încărcați setul de date
dataset = load_dataset("HuggingFaceTB/smoltalk", "all")

# Configurați modelul și tokenizer-ul
model_name = "HuggingFaceTB/SmolLM2-135M"
model = AutoModelForCausalLM.from_pretrained(pretrained_model_name_or_path=model_name).to(
    device
)
tokenizer = AutoTokenizer.from_pretrained(pretrained_model_name_or_path=model_name)
# Configurați template-ul de chat
model, tokenizer = setup_chat_format(model=model, tokenizer=tokenizer)

# Configurați trainer-ul
training_args = SFTConfig(
    output_dir="./sft_output",
    max_steps=1000,
    per_device_train_batch_size=4,
    learning_rate=5e-5,
    logging_steps=10,
    save_steps=100,
    eval_strategy="steps",
    eval_steps=50,
)

# Inițializați trainer-ul
trainer = SFTTrainer(
    model=model,
    args=training_args,
    train_dataset=dataset["train"],
    eval_dataset=dataset["test"],
    processing_class=tokenizer,
)

# Începeți antrenamentul
trainer.train()
```

<Tip>
Când folosiți un set de date cu un câmp "messages" (ca exemplul de mai sus), SFTTrainer aplică automat template-ul de chat al modelului, pe care îl recuperează de pe hub. Aceasta înseamnă că nu aveți nevoie de nicio configurație suplimentară pentru a gestiona conversațiile în stil chat - trainer-ul va formata mesajele conform formatului de template așteptat al modelului.
</Tip>

## Împachetarea setului de date

SFTTrainer suportă împachetarea exemplelor pentru a optimiza eficiența antrenamentului. Această funcționalitate permite ca mai multe exemple scurte să fie împachetate în aceeași secvență de intrare, maximizând utilizarea GPU în timpul antrenamentului. Pentru a activa împachetarea, pur și simplu setați `packing=True` în constructorul SFTConfig. Când folosiți seturi de date împachetate cu `max_steps`, fiți conștienți că s-ar putea să antrenați pentru mai multe epoci decât ați așteptat, în funcție de configurația de împachetare. Puteți personaliza modul în care exemplele sunt combinate folosind o funcție de formatare - deosebit de utilă când lucrați cu seturi de date care au mai multe câmpuri precum perechi întrebare-răspuns. Pentru seturile de date de evaluare, puteți dezactiva împachetarea setând `eval_packing=False` în SFTConfig. Iată un exemplu de bază de personalizare a configurației de împachetare:

```python
# Configurați împachetarea
training_args = SFTConfig(packing=True)

trainer = SFTTrainer(model=model, train_dataset=dataset, args=training_args)

trainer.train()
```

Când împachetați setul de date cu mai multe câmpuri, puteți defini o funcție de formatare personalizată pentru a combina câmpurile într-o singură secvență de intrare. Această funcție ar trebui să ia o listă de exemple și să returneze un dicționar cu secvența de intrare împachetată. Iată un exemplu de funcție de formatare personalizată:

```python
def formatting_func(example):
    text = f"### Question: {example['question']}\n ### Answer: {example['answer']}"
    return text


training_args = SFTConfig(packing=True)
trainer = SFTTrainer(
    "facebook/opt-350m",
    train_dataset=dataset,
    args=training_args,
    formatting_func=formatting_func,
)
```

## Monitorizarea progresului antrenamentului

Monitorizarea eficientă este crucială pentru succesul fine-tuningului. Să explorăm ce să urmărim în timpul antrenamentului:

### Înțelegerea modelelor de pierdere

Pierderea antrenamentului urmează de obicei trei faze distincte:
1. Scădere abruptă inițială: Adaptare rapidă la noua distribuție de date
2. Stabilizare graduală: Rata de învățare încetinește pe măsură ce modelul se ajustează fin
3. Convergența: Valorile pierderilor se stabilizează, indicând finalizarea antrenamentului

<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/nlp_course_sft_loss_graphic.png" alt="Antrenamentul SFTTrainer" />

### Metrici de monitorizat

Monitorizarea eficientă implică urmărirea metricilor cantitative și evaluarea metricilor calitative. Metricile disponibile sunt:

- Pierderea antrenamentului
- Pierderea validării
- Progresia ratei de învățare
- Normele gradientului

<Tip warning={true}>
Urmăriți aceste semne de avertizare în timpul antrenamentului:
1. Pierderea validării crește în timp ce pierderea antrenamentului scade (supraadaptare)
2. Nicio îmbunătățire semnificativă în valorile pierderilor (subadaptare)
3. Valori extrem de mici ale pierderilor (posibilă memorizare)
4. Formatare inconsistentă a ieșirii (probleme de învățare a template-ului)
</Tip>

### Calea către convergență

Pe măsură ce antrenamentul progresează, curba pierderii ar trebui să se stabilizeze treptat. Indicatorul cheie al antrenamentului sănătos este un decalaj mic între pierderea antrenamentului și cea de validare, sugerând că modelul învață modele generalizabile mai degrabă decât să memoreze exemple specifice. Valorile absolute ale pierderilor vor varia în funcție de sarcina și setul de date.

### Monitorizarea progresului antrenamentului

Graficul de mai sus arată o progresie tipică a antrenamentului. Observați cum atât pierderea antrenamentului, cât și cea de validare scad brusc la început, apoi se nivelează treptat. Acest model indică faptul că modelul învață eficient menținând în același timp capacitatea de generalizare.

### Semne de avertizare de urmărit

Mai multe modele în curbele pierderilor pot indica probleme potențiale. Mai jos ilustrăm semne comune de avertizare și soluții pe care le putem considera.

<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/sft_loss_1.png" alt="Antrenamentul SFTTrainer" />

Dacă pierderea validării scade cu o rată semnificativ mai lentă decât pierderea antrenamentului, modelul probabil se supraadaptează la datele de antrenare. Considerați:
- Reducerea pașilor de antrenare
- Creșterea dimensiunii setului de date
- Validarea calității și diversității setului de date

<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/sft_loss_2.png" alt="Antrenamentul SFTTrainer" />

Dacă pierderea nu arată îmbunătățiri semnificative, modelul ar putea să:
- Învețe prea încet (încercați să creșteți rata de învățare)
- Se confrunte cu sarcina (verificați calitatea datelor și complexitatea sarcinii)
- Atingă limitările arhitecturii (considerați un model diferit)

<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/sft_loss_3.png" alt="Antrenamentul SFTTrainer" />

Valori extrem de mici ale pierderilor ar putea sugera memorizare mai degrabă decât învățare. Acest lucru este deosebit de îngrijorător dacă:
- Modelul performează slab pe exemple noi, similare
- Ieșirile lipsesc de diversitate
- Răspunsurile sunt prea similare cu exemplele de antrenare

<Tip warning={true}>
Monitorizați atât valorile pierderilor, cât și ieșirile efective ale modelului în timpul antrenamentului. Uneori pierderea poate arăta bine în timp ce modelul dezvoltă comportamente nedorite. Evaluarea calitativă regulată a răspunsurilor modelului ajută la detectarea problemelor pe care metricile singure le-ar putea rata.
</Tip>

Ar trebui să observăm că interpretarea valorilor pierderilor pe care o descriem aici este destinată cazului cel mai comun, și de fapt, valorile pierderilor se pot comporta în moduri diferite în funcție de model, setul de date, parametrii de antrenare, etc. Dacă sunteți interesați să explorați mai multe despre modelele descrise, ar trebui să consultați acest articol de blog de la oamenii de la [Fast AI](https://www.fast.ai/posts/2023-09-04-learning-jumps/).

## Evaluarea după SFT

În secțiunea [11.4](/en/chapter11/4) vom învăța cum să evaluăm modelul folosind seturi de date de referință. Pentru moment, ne vom concentra pe evaluarea calitativă a modelului.

După finalizarea SFT, considerați aceste acțiuni de urmărire:

1. Evaluați modelul temeinic pe datele de test păstrate deoparte
2. Validați aderarea la template pe diverse intrări
3. Testați reținerea cunoștințelor specifice domeniului
4. Monitorizați metricile de performanță din lumea reală

<Tip>
Documentați procesul de antrenare, inclusiv:
- Caracteristicile setului de date
- Parametrii antrenamentului
- Metricile de performanță
- Limitările cunoscute
Această documentație va fi valoroasă pentru iterațiile viitoare ale modelului.
</Tip>

## Chestionar

### 1. Ce parametri controlează durata antrenamentului în SFT?

<Question
	choices={[
		{
			text: "num_train_epochs și max_steps",
			explain: "Corect! Acești parametri determină cât timp va antrena modelul, fie prin numărul de epoci, fie prin pașii totali.",
			correct: true
		},
		{
			text: "batch_size și learning_rate",
			explain: "Deși aceștia afectează antrenamentul, nu controlează direct durata."
		},
		{
			text: "gradient_checkpointing și warmup_ratio",
			explain: "Acești parametri afectează eficiența și stabilitatea antrenamentului, nu durata."
		}
	]}
/>

### 2. Ce model în curbele pierderilor indică supraadaptarea potențială?

<Question
    choices={[
        {
            text: "Pierderea validării crește în timp ce pierderea antrenamentului continuă să scadă",
            explain: "Corect! Această divergență între pierderea antrenamentului și cea de validare este un semn clasic al supraadaptării.",
            correct: true
        },
        {
            text: "Atât pierderea antrenamentului, cât și cea de validare scad constant",
            explain: "Acest model indică de fapt un antrenament sănătos."
        },
        {
            text: "Pierderea antrenamentului rămâne constantă în timp ce pierderea validării scade",
            explain: "Acesta ar fi un model neobișnuit și nu indică supraadaptarea."
        }
    ]}
/>

### 3. Pentru ce este folosit gradient_accumulation_steps?

<Question
    choices={[
        {
            text: "Pentru a crește dimensiunea efectivă a batch-ului fără a folosi mai multă memorie",
            explain: "Corect! Acumulează gradienții pe mai multe treceri înainte înainte de a actualiza greutățile.",
            correct: true
        },
        {
            text: "Pentru a salva puncte de verificare în timpul antrenamentului",
            explain: "Aceasta este gestionată de parametrii save_steps și save_strategy."
        },
        {
            text: "Pentru a controla programul ratei de învățare",
            explain: "Programarea ratei de învățare este controlată de learning_rate și warmup_ratio."
        }
    ]}
/>

### 4. Ce ar trebui să monitorizați în timpul antrenamentului SFT?

<Question
    choices={[
        {
            text: "Atât metricile cantitative, cât și ieșirile calitative",
            explain: "Corect! Monitorizarea ambelor tipuri de metrici ajută la detectarea tuturor problemelor potențiale.",
            correct: true
        },
        {
            text: "Doar pierderea antrenamentului",
            explain: "Pierderea antrenamentului singură nu este suficientă pentru a asigura un comportament bun al modelului."
        },
        {
            text: "Doar calitatea ieșirii modelului",
            explain: "Deși importantă, evaluarea calitativă singură ratează dinamici importante ale antrenamentului."
        }
    ]}
/>

### 5. Ce indică convergența sănătoasă în timpul antrenamentului?

<Question
    choices={[
        {
            text: "Un decalaj mic între pierderea antrenamentului și cea de validare",
            explain: "Corect! Aceasta indică faptul că modelul învață modele generalizabile.",
            correct: true
        },
        {
            text: "Pierderea antrenamentului ajungând la zero",
            explain: "Valori extrem de mici ale pierderilor ar putea indica memorizare mai degrabă decât învățare."
        },
        {
            text: "Pierderea validării fiind mai mică decât pierderea antrenamentului",
            explain: "Aceasta ar fi neobișnuită și ar putea indica probleme cu setul de validare."
        }
    ]}
/>

## 💐 Bună treabă!

Ați învățat cum să faceți fine-tuning la modele folosind SFT! Pentru a continua învățarea:
1. Încercați notebook-ul cu parametri diferiți
2. Experimentați cu alte seturi de date
3. Contribuiți cu îmbunătățiri la materialul cursului

## Resurse adiționale

- [Documentația TRL](https://huggingface.co/docs/trl)
- [Depozitul de exemple SFT](https://github.com/huggingface/trl/blob/main/trl/scripts/sft.py)
- [Cele mai bune practici pentru fine-tuning](https://huggingface.co/docs/transformers/training) 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter11/3.mdx" />

### Concluzie
https://huggingface.co/learn/course/ro/chapter11/6.md

# Concluzie

În acest capitol, am explorat componentele esențiale ale fine-tuningului modelelor de limbaj:

1. **Template-urile de chat** oferă structură interacțiunilor cu modelul, asigurând răspunsuri consecvente și adecvate prin formatare standardizată.

2. **Fine-tuningul supervizat (SFT)** permite adaptarea modelelor pre-antrenate la sarcini specifice menținând în același timp cunoștințele lor fundamentale.

3. **LoRA** oferă o abordare eficientă pentru fine-tuning prin reducerea parametrilor antrenabili păstrând în același timp performanța modelului.

4. **Evaluarea** ajută la măsurarea și validarea eficacității fine-tuningului prin diverse metrici și criterii de referință.

Aceste tehnici, când sunt combinate, permit crearea de modele specializate de limbaj care pot excela în sarcini specifice rămânând în același timp eficiente din punct de vedere computațional. Indiferent dacă construiți un bot de servicii pentru clienți sau un asistent specific domeniului, înțelegerea acestor concepte este crucială pentru adaptarea cu succes a modelului. 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter11/6.mdx" />

### LoRA (Adaptarea de rang scăzut)
https://huggingface.co/learn/course/ro/chapter11/4.md

# LoRA (Adaptarea de rang scăzut)

Fine-tuningul modelelor mari de limbaj este un proces intensiv în resurse. LoRA este o tehnică care ne permite să facem fine-tuning la modele mari de limbaj cu un număr mic de parametri. Funcționează prin adăugarea și optimizarea unor matrice mai mici la greutățile atenției, reducând de obicei parametrii antrenabili cu aproximativ 90%.

## Înțelegerea LoRA

LoRA (Adaptarea de rang scăzut) este o tehnică de fine-tuning eficientă din punct de vedere al parametrilor care îngheață greutățile modelului pre-antrenat și injectează matrice de descompunere de rang antrenabile în straturile modelului. În loc să antreneze toți parametrii modelului în timpul fine-tuningului, LoRA descompune actualizările greutăților în matrice mai mici prin descompunere de rang scăzut, reducând semnificativ numărul de parametri antrenabili menținând în același timp performanța modelului. De exemplu, când este aplicat la GPT-3 175B, LoRA a redus parametrii antrenabili de 10.000 de ori și cerințele de memorie GPU de 3 ori comparativ cu fine-tuningul complet. Puteți citi mai multe despre LoRA în [lucrarea LoRA](https://arxiv.org/pdf/2106.09685).

LoRA funcționează prin adăugarea de perechi de matrice de descompunere de rang la straturile transformer, concentrându-se de obicei pe greutățile atenției. În timpul inferenței, aceste greutăți ale adaptorului pot fi îmbinate cu modelul de bază, rezultând în nicio latență suplimentară. LoRA este deosebit de util pentru adaptarea modelelor mari de limbaj la sarcini sau domenii specifice menținând în același timp cerințele de resurse gestionabile.

## Avantajele cheie ale LoRA

1. **Eficiența memoriei**:
   - Doar parametrii adaptorului sunt stocați în memoria GPU
   - Greutățile modelului de bază rămân înghețate și pot fi încărcate la precizie mai mică
   - Permite fine-tuningul modelelor mari pe GPU-uri de consum

2. **Caracteristici de antrenare**:
   - Integrare nativă PEFT/LoRA cu configurare minimă
   - Suport pentru QLoRA (LoRA cuantificat) pentru eficiență de memorie și mai bună

3. **Gestionarea adaptorului**:
   - Salvarea greutăților adaptorului în timpul punctelor de verificare
   - Funcționalități pentru îmbinarea adaptorilor înapoi în modelul de bază

## Încărcarea adaptorilor LoRA cu PEFT

[PEFT](https://github.com/huggingface/peft) este o bibliotecă care oferă o interfață unificată pentru încărcarea și gestionarea metodelor PEFT, inclusiv LoRA. Vă permite să încărcați și să comutați cu ușurință între diferite metode PEFT, făcând mai ușor să experimentați cu diferite tehnici de fine-tuning.

Adaptorii pot fi încărcați pe un model pre-antrenat cu `load_adapter()`, care este util pentru a încerca adaptori diferiți ale căror greutăți nu sunt îmbinate. Setați greutățile adaptorului activ cu funcția `set_adapter()`. Pentru a reveni la modelul de bază, ați putea folosi unload() pentru a descărca toate modulele LoRA. Acest lucru face ușor să comutați între greutăți specifice sarcinilor diferite.

```python
from peft import PeftModel, PeftConfig

config = PeftConfig.from_pretrained("ybelkada/opt-350m-lora")
model = AutoModelForCausalLM.from_pretrained(config.base_model_name_or_path)
lora_model = PeftModel.from_pretrained(model, "ybelkada/opt-350m-lora")
```

![lora_load_adapter](https://github.com/huggingface/smol-course/raw/main/3_parameter_efficient_finetuning/images/lora_adapter.png)

## Fine-tuning LLM folosind `trl` și `SFTTrainer` cu LoRA

[SFTTrainer](https://huggingface.co/docs/trl/sft_trainer) din `trl` oferă integrare cu adaptorii LoRA prin biblioteca [PEFT](https://huggingface.co/docs/peft/en/index). Aceasta înseamnă că putem face fine-tuning la un model în același mod ca și cu SFT, dar folosim LoRA pentru a reduce numărul de parametri pe care trebuie să îi antrenăm.

Vom folosi clasa `LoRAConfig` din PEFT în exemplul nostru. Configurarea necesită doar câțiva pași de configurare:

1. Definiți configurația LoRA (rang, alfa, dropout)
2. Creați SFTTrainer cu configurația PEFT
3. Antrenați și salvați greutățile adaptorului

## Configurația LoRA

Să parcurgem configurația LoRA și parametrii cheie.

| Parametru | Descriere |
|-----------|-----------|
| `r` (rang) | Dimensiunea matricelor de rang scăzut folosite pentru actualizările greutăților. De obicei între 4-32. Valori mai mici oferă mai multă compresie dar potențial mai puțină expresivitate. |
| `lora_alpha` | Factor de scalare pentru straturile LoRA, de obicei setat la 2x valoarea rangului. Valori mai mari rezultă în efecte de adaptare mai puternice. |
| `lora_dropout` | Probabilitatea dropout pentru straturile LoRA, de obicei 0.05-0.1. Valori mai mari ajută la prevenirea supraadaptării în timpul antrenamentului. |
| `bias` | Controlează antrenarea termenilor de bias. Opțiunile sunt "none", "all" sau "lora_only". "none" este cel mai comun pentru eficiența memoriei. |
| `target_modules` | Specifică la care module ale modelului să aplice LoRA. Poate fi "all-linear" sau module specifice precum "q_proj,v_proj". Mai multe module permit o adaptabilitate mai mare dar cresc utilizarea memoriei. |

<Tip>
Când implementați metode PEFT, începeți cu valori mici ale rangului (4-8) pentru LoRA și monitorizați pierderea antrenamentului. Folosiți seturi de validare pentru a preveni supraadaptarea și comparați rezultatele cu liniile de bază de fine-tuning complet când este posibil. Eficacitatea diferitelor metode poate varia în funcție de sarcină, așa că experimentarea este cheia.
</Tip>

## Folosirea TRL cu PEFT

Metodele PEFT pot fi combinate cu TRL pentru fine-tuning pentru a reduce cerințele de memorie. Putem trece `LoraConfig` la model când îl încărcăm.

```python
from peft import LoraConfig

# TODO: Configurați parametrii LoRA
# r: dimensiunea rangului pentru matricele de actualizare LoRA (mai mică = mai multă compresie)
rank_dimension = 6
# lora_alpha: factor de scalare pentru straturile LoRA (mai mare = adaptare mai puternică)
lora_alpha = 8
# lora_dropout: probabilitatea dropout pentru straturile LoRA (ajută la prevenirea supraadaptării)
lora_dropout = 0.05

peft_config = LoraConfig(
    r=rank_dimension,  # Dimensiunea rangului - de obicei între 4-32
    lora_alpha=lora_alpha,  # Factor de scalare LoRA - de obicei 2x rangul
    lora_dropout=lora_dropout,  # Probabilitatea dropout pentru straturile LoRA
    bias="none",  # Tipul bias pentru LoRA. bias-urile corespunzătoare vor fi actualizate în timpul antrenamentului.
    target_modules="all-linear",  # La care module să aplice LoRA
    task_type="CAUSAL_LM",  # Tipul sarcinii pentru arhitectura modelului
)
```

Mai sus, am folosit `device_map="auto"` pentru a atribui automat modelul la dispozitivul corect. De asemenea, puteți atribui manual modelul la un dispozitiv specific folosind `device_map={"": device_index}`.

Vom avea nevoie, de asemenea, să definim `SFTTrainer` cu configurația LoRA.

```python
# Creați SFTTrainer cu configurația LoRA
trainer = SFTTrainer(
    model=model,
    args=args,
    train_dataset=dataset["train"],
    peft_config=peft_config,  # Configurația LoRA
    max_seq_length=max_seq_length,  # Lungimea maximă a secvenței
    processing_class=tokenizer,
)
```

<Tip>

✏️ **Încercați!** Construiți pe modelul dumneavoastră ajustat fin din secțiunea anterioară, dar faceți fine-tuning cu LoRA. Folosiți setul de date `HuggingFaceTB/smoltalk` pentru a face fine-tuning la un model `deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B`, folosind configurația LoRA pe care am definit-o mai sus.

</Tip>

## Îmbinarea adaptorilor LoRA

După antrenarea cu LoRA, s-ar putea să doriți să îmbinați greutățile adaptorului înapoi în modelul de bază pentru implementare mai ușoară. Aceasta creează un singur model cu greutățile combinate, eliminând necesitatea de a încărca adaptorii separat în timpul inferenței.

Procesul de îmbinare necesită atenție la gestionarea memoriei și precizie. Deoarece va trebui să încărcați atât modelul de bază, cât și greutățile adaptorului simultan, asigurați-vă că există memorie GPU/CPU suficientă disponibilă. Folosirea `device_map="auto"` în `transformers` va găsi dispozitivul corect pentru model bazat pe hardware-ul dumneavoastră.

Mențineți precizia consistentă (de ex. float16) pe tot parcursul procesului, potrivind precizia folosită în timpul antrenamentului și salvând modelul îmbinat în același format pentru implementare.

## Implementarea îmbinării

După antrenarea unui adaptor LoRA, puteți îmbina greutățile adaptorului înapoi în modelul de bază. Iată cum să faceți acest lucru:

```python
import torch
from transformers import AutoModelForCausalLM
from peft import PeftModel

# 1. Încărcați modelul de bază
base_model = AutoModelForCausalLM.from_pretrained(
    "base_model_name", torch_dtype=torch.float16, device_map="auto"
)

# 2. Încărcați modelul PEFT cu adaptorul
peft_model = PeftModel.from_pretrained(
    base_model, "path/to/adapter", torch_dtype=torch.float16
)

# 3. Îmbinați greutățile adaptorului cu modelul de bază
merged_model = peft_model.merge_and_unload()
```

Dacă întâmpinați discrepanțe de dimensiune în modelul salvat, asigurați-vă că salvați și tokenizer-ul:

```python
# Salvați atât modelul, cât și tokenizer-ul
tokenizer = AutoTokenizer.from_pretrained("base_model_name")
merged_model.save_pretrained("path/to/save/merged_model")
tokenizer.save_pretrained("path/to/save/merged_model")
```

<Tip>

✏️ **Încercați!** Îmbinați greutățile adaptorului înapoi în modelul de bază. Folosiți setul de date `HuggingFaceTB/smoltalk` pentru a face fine-tuning la un model `deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B`, folosind configurația LoRA pe care am definit-o mai sus.

</Tip>

# Resurse

- [LoRA: Adaptarea de rang scăzut a modelelor mari de limbaj](https://arxiv.org/pdf/2106.09685)
- [Documentația PEFT](https://huggingface.co/docs/peft)
- [Articolul de blog Hugging Face despre PEFT](https://huggingface.co/blog/peft) 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter11/4.mdx" />

### E timpul examenului!
https://huggingface.co/learn/course/ro/chapter11/7.md

# E timpul examenului!

E timpul să vă puneți cunoștințele la încercare! Am pregătit un scurt chestionar pentru a vă testa înțelegerea conceptelor abordate în acest capitol.

Pentru a face chestionarul, va trebui să urmați acești pași:

1. Conectați-vă la contul dumneavoastră Hugging Face.
2. Răspundeți la întrebările din chestionar.
3. Trimiteți răspunsurile dumneavoastră.

## Chestionar cu alegere multiplă

În acest chestionar, vi se va cere să selectați răspunsul corect dintr-o listă de opțiuni. Vă vom testa pe elementele fundamentale ale fine-tuningului supervizat.

<iframe
	src="https://nlp-course-supervised-finetuning-quiz.hf.space"
	frameborder="0"
	width="850"
	height="450"
></iframe>

## Chestionar de cod

În acest chestionar, vi se va cere să scrieți cod pentru a completa o sarcină. Vă vom testa pe codul pe care l-ați studiat în curs din biblioteci precum `datasets`, `transformers`, `peft` și `TRL`.

<iframe
	src="https://nlp-course-sft-code-quiz.hf.space"
	frameborder="0"
	width="850"
	height="450"
></iframe> 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter11/7.mdx" />

### Template-uri de chat
https://huggingface.co/learn/course/ro/chapter11/2.md

# Template-uri de chat

## Introducere

Template-urile de chat sunt esențiale pentru structurarea interacțiunilor dintre modelele de limbaj și utilizatori. Indiferent dacă construiți un chatbot simplu sau un agent AI complex, înțelegerea modului de a formata corect conversațiile este crucială pentru a obține cele mai bune rezultate de la modelul dumneavoastră. În acest ghid, vom explora ce sunt template-urile de chat, de ce sunt importante și cum să le folosiți eficient.

<Tip>
Template-urile de chat sunt cruciale pentru:
- Menținerea unei structuri consecvente de conversație
- Asigurarea identificării corecte a rolurilor
- Gestionarea contextului pe mai multe tururi
- Suportarea funcționalităților avansate precum utilizarea instrumentelor
</Tip>

## Tipuri de modele și template-uri

### Modele de bază versus modele instruct
Un model de bază este antrenat pe date text brute pentru a prezice următorul token, în timp ce un model instruct este ajustat specific pentru a urma instrucțiuni și a se angaja în conversații. De exemplu, [`SmolLM2-135M`](https://huggingface.co/HuggingFaceTB/SmolLM2-135M) este un model de bază, în timp ce [`SmolLM2-135M-Instruct`](https://huggingface.co/HuggingFaceTB/SmolLM2-135M-Instruct) este varianta sa ajustată pentru instrucțiuni.

Modelele ajustate pentru instrucțiuni sunt antrenate să urmeze o structură conversațională specifică, făcându-le mai potrivite pentru aplicații de chatbot. Mai mult, modelele instruct pot gestiona interacțiuni complexe, inclusiv utilizarea instrumentelor, intrări multimodale și apeluri de funcții.

Pentru a face ca un model de bază să se comporte ca un model instruct, trebuie să formatăm prompturile într-un mod consecvent pe care modelul să îl înțeleagă. Aici intervin template-urile de chat. ChatML este unul dintre aceste formate de template care structurează conversațiile cu indicatori clari de rol (sistem, utilizator, asistent). Iată un ghid despre [ChatML](https://huggingface.co/HuggingFaceTB/SmolLM2-135M-Instruct/blob/e2c3f7557efbdec707ae3a336371d169783f1da1/tokenizer_config.json#L146).

<Tip warning={true}>
Când folosiți un model instruct, verificați întotdeauna că folosiți formatul corect de template de chat. Folosirea unui template greșit poate duce la performanțe slabe ale modelului sau comportament neașteptat. Cea mai ușoară modalitate de a vă asigura de acest lucru este să verificați configurația tokenizer-ului modelului pe Hub. De exemplu, modelul `SmolLM2-135M-Instruct` folosește <a href="https://huggingface.co/HuggingFaceTB/SmolLM2-135M-Instruct/blob/e2c3f7557efbdec707ae3a336371d169783f1da1/tokenizer_config.json#L146">această configurație</a>.
</Tip>

### Formate comune de template

Înainte de a aprofunda implementări specifice, este important să înțelegem cum diferite modele se așteaptă ca conversațiile lor să fie formatate. Să explorăm câteva formate comune de template folosind un exemplu simplu de conversație:

Vom folosi următoarea structură de conversație pentru toate exemplele:

```python
messages = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "Hello!"},
    {"role": "assistant", "content": "Hi! How can I help you today?"},
    {"role": "user", "content": "What's the weather?"},
]
```

Acesta este template-ul ChatML folosit în modele precum SmolLM2 și Qwen 2:

```sh
<|im_start|>system
You are a helpful assistant.<|im_end|>
<|im_start|>user
Hello!<|im_end|>
<|im_start|>assistant
Hi! How can I help you today?<|im_end|>
<|im_start|>user
What's the weather?<|im_start|>assistant
```

Acesta folosește formatul de template `mistral`:

```sh
<s>[INST] You are a helpful assistant. [/INST]
Hi! How can I help you today?</s>
[INST] Hello! [/INST]
```

Diferențele cheie între aceste formate includ:
1. **Gestionarea mesajelor de sistem**:
   - Llama 2 înfășoară mesajele de sistem în etichete `<<SYS>>`
   - Llama 3 folosește etichete `<|system|>` cu încheierea `</s>`
   - Mistral include mesajul de sistem în prima instrucțiune
   - Qwen folosește rolul explicit `system` cu etichete `<|im_start|>`
   - ChatGPT folosește prefixul `SYSTEM:`

2. **Granițele mesajelor**:
   - Llama 2 folosește etichete `[INST]` și `[/INST]`
   - Llama 3 folosește etichete specifice rolurilor (`<|system|>`, `<|user|>`, `<|assistant|>`) cu încheierea `</s>`
   - Mistral folosește `[INST]` și `[/INST]` cu `<s>` și `</s>`
   - Qwen folosește token-uri de start/sfârșit specifice rolurilor

3. **Token-uri speciale**:
   - Llama 2 folosește `<s>` și `</s>` pentru granițele conversației
   - Llama 3 folosește `</s>` pentru a încheia fiecare mesaj
   - Mistral folosește `<s>` și `</s>` pentru granițele turului
   - Qwen folosește token-uri de start/sfârșit specifice rolurilor

Înțelegerea acestor diferențe este cheia pentru a lucra cu modele variate. Să vedem cum biblioteca transformers ne ajută să gestionăm aceste variații automat:

```python
from transformers import AutoTokenizer

# Acestea vor folosi template-uri diferite automat
mistral_tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-Instruct-v0.1")
qwen_tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen-7B-Chat")
smol_tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM2-135M-Instruct")

messages = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "Hello!"},
]

# Fiecare va formata conform template-ului modelului său
mistral_chat = mistral_tokenizer.apply_chat_template(messages, tokenize=False)
qwen_chat = qwen_tokenizer.apply_chat_template(messages, tokenize=False)
smol_chat = smol_tokenizer.apply_chat_template(messages, tokenize=False)
```

<details>
<summary>Faceți clic pentru a vedea exemplele de template</summary>

Template ChatML pentru Qwen 2 și SmolLM2:

```sh
<|im_start|>system
You are a helpful assistant.<|im_end|>
<|im_start|>user
Hello!<|im_end|>
<|im_start|>assistant
Hi! How can I help you today?<|im_end|>
<|im_start|>user
What's the weather?<|im_start|>assistant
```

Template Mistral:

```sh
<s>[INST] You are a helpful assistant. [/INST]
Hi! How can I help you today?</s>
[INST] Hello! [/INST]
```

</details>

### Funcționalități avansate
Template-urile de chat pot gestiona scenarii mai complexe dincolo de simpla interacțiune conversațională, inclusiv:

1. **Utilizarea instrumentelor**: Când modelele trebuie să interacționeze cu instrumente externe sau API-uri
2. **Intrări multimodale**: Pentru gestionarea imaginilor, audio sau alte tipuri de media
3. **Apeluri de funcții**: Pentru executarea structurată de funcții
4. **Context multi-turn**: Pentru menținerea istoricului conversației

<Tip>
Când implementați funcționalități avansate:
- Testați temeinic cu modelul dumneavoastră specific. Template-urile de viziune și utilizare a instrumentelor sunt deosebit de diverse.
- Monitorizați cu atenție utilizarea token-urilor între fiecare funcționalitate și model.
- Documentați formatul așteptat pentru fiecare funcționalitate
</Tip>

Pentru conversații multimodale, template-urile de chat pot include referințe la imagini sau imagini codificate în base64:

```python
messages = [
    {
        "role": "system",
        "content": "You are a helpful vision assistant that can analyze images.",
    },
    {
        "role": "user",
        "content": [
            {"type": "text", "text": "What's in this image?"},
            {"type": "image", "image_url": "https://example.com/image.jpg"},
        ],
    },
]
```

Iată un exemplu de template de chat cu utilizarea instrumentelor:

```python
messages = [
    {
        "role": "system",
        "content": "You are an AI assistant that can use tools. Available tools: calculator, weather_api",
    },
    {"role": "user", "content": "What's 123 * 456 and is it raining in Paris?"},
    {
        "role": "assistant",
        "content": "Let me help you with that.",
        "tool_calls": [
            {
                "tool": "calculator",
                "parameters": {"operation": "multiply", "x": 123, "y": 456},
            },
            {"tool": "weather_api", "parameters": {"city": "Paris", "country": "France"}},
        ],
    },
    {"role": "tool", "tool_name": "calculator", "content": "56088"},
    {
        "role": "tool",
        "tool_name": "weather_api",
        "content": "{'condition': 'rain', 'temperature': 15}",
    },
]
```

## Cele mai bune practici

### Ghiduri generale
Când lucrați cu template-uri de chat, urmați aceste practici cheie:

1. **Formatare consecventă**: Folosiți întotdeauna același format de template în toată aplicația
2. **Definirea clară a rolurilor**: Specificați clar rolurile (sistem, utilizator, asistent, instrument) pentru fiecare mesaj
3. **Gestionarea contextului**: Fiți atenți la limitele token-urilor când mențineți istoricul conversației
4. **Gestionarea erorilor**: Includeți gestionarea adecvată a erorilor pentru apelurile de instrumente și intrările multimodale
5. **Validare**: Validați structura mesajelor înainte de a le trimite la model

<Tip warning={true}>
Capcane comune de evitat:
- Amestecarea diferitelor formate de template în aceeași aplicație
- Depășirea limitelor de token-uri cu istoricuri lungi de conversație
- Neescaparea corespunzătoare a caracterelor speciale în mesaje
- Uitarea validării structurii mesajelor de intrare
- Ignorarea cerințelor specifice de template ale modelului
</Tip>

## Exercițiu practic

Să exersăm implementarea template-urilor de chat cu un exemplu din lumea reală.

<Tip>
Urmați acești pași pentru a converti setul de date `HuggingFaceTB/smoltalk` în formatul chatml:

1. Încărcați setul de date:
```python
from datasets import load_dataset

dataset = load_dataset("HuggingFaceTB/smoltalk")
```

2. Creați o funcție de procesare:
```python
def convert_to_chatml(example):
    return {
        "messages": [
            {"role": "user", "content": example["input"]},
            {"role": "assistant", "content": example["output"]},
        ]
    }
```

3. Aplicați template-ul de chat folosind tokenizer-ul modelului ales

Amintiți-vă să validați că formatul de ieșire se potrivește cu cerințele modelului țintă!
</Tip>

## Resurse adiționale

- [Ghidul Hugging Face pentru template-uri de chat](https://huggingface.co/docs/transformers/main/en/chat_templating)
- [Documentația Transformers](https://huggingface.co/docs/transformers)
- [Depozitul de exemple pentru template-uri de chat](https://github.com/chujiezheng/chat_templates) 

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter11/2.mdx" />

### Crearea propriului tău dataset[[creating-your-own-dataset]]
https://huggingface.co/learn/course/ro/chapter5/5.md

# Crearea propriului tău dataset[[creating-your-own-dataset]]

<CourseFloatingBanner chapter={5}
  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/chapter5/section5.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter5/section5.ipynb"},
]} />

Uneori, datasetul necesar pentru a construi o aplicație NLP nu există, astfel încât veți trebui să-l creați singuri. În această secțiune vom arăta cum să creați un corpus de [GitHub issues](https://github.com/features/issues/), care sunt utilizate în mod obișnuit pentru a urmări erorile sau feature-urile din repositoriile GitHub. Acest corpus poate fi folosit pentru diverse scopuri, inclusiv:

* Explorarea timpului necesar pentru închiderea unor issues deschise sau pull requesturi
* Antrenarea unui _multilabel classifier_ care poate eticheta issue-urile cu metadate pe baza descrierii issue-urilor (de exemplu, "bug", "enhancement" sau "question")
* Crearea unui motor de căutare semantică pentru a găsi care issues se potrivesc query-ului utilizatorului

În această secțiune ne vom focusa pe crearea corpusului, și în următoarea vom aborda aplicația motorului de căutare semantic. Pentru a păstra lucrurile meta, vom folosi issue-urile GitHub asociate cu un proiect open source popular: 🤗 Datasets! Să vedem cum să obținem datele și să explorăm informațiile conținute în aceste issue-uri.

## Obținerea datelor[[getting-the-data]]

Puteți găsi toate issue-urile din 🤗 Datasets navigând către tabul [Issues](https://github.com/huggingface/datasets/issues) al repositorului. Așa cum arată următorul screenshot, la momentul scrierii acestui text existau 331 de issues deschise și 668 închise.

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter5/datasets-issues.png" alt="Issue-urile GitHub asociate cu 🤗 Datasets." width="80%"/>
</div>

Dacă ați da clic pe una dintre aceste issue-uri veți găsi că aceasta conține un titlu, o descriere și un set de labeluri care caracterizează issue-ul. Un exemplu este prezentat în screenshotul următor.

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter5/datasets-issues-single.png" alt="Un issue tipic în GitHub din repositoriul 🤗 Datasets." width="80%"/>
</div>

Pentru a descărca toate issue-urile din repositoriu, vom folosi [GitHub REST API](https://docs.github.com/en/rest) pentru a enumera [`Issues` endpoint](https://docs.github.com/en/rest/reference/issues#list-repository-issues). Aceast endpoint returnează o listă de obiecte JSON, cu fiecare obiect conținând un număr mare de câmpuri care includ titlul și descrierea precum și metadata despre starea issue-ului și așa mai departe.

Un mod convenabil de descărcare a issue-urilor este prin utilizarea librăriei `requests`, care este modalitatea standard pentru a face cereri HTTP în Python. Puteți instala libraria rulând comanda:

```python
!pip install requests
```

Odată cu instalarea librariei, puteți face cereri GET la `Issues` endpoint prin invocarea funcției `requests.get()`. De exemplu, puteți rula următorul cod pentru a obține primul issue din prima pagină:

```py
import requests

url = "https://api.github.com/repos/huggingface/datasets/issues?page=1&per_page=1"
response = requests.get(url)
```

Obiectul `response` conține o cantitate mare de informații utile despre requestul efectuat, inclusiv HTTP status code:

```py
response.status_code
```

```python out
200
```

unde statusul `200` înseamnă că cererea a fost reușită (puteți găsi o listă completă de status coduri [aici](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes)). De ceea ce suntem însă interesați este _payload_, care poate fi accesat în diverse formaturi precum bytes, string sau JSON. Deoarece știm că issue-urile noastre sunt în format JSON, să inspectăm payload-ul astfel:

```py
response.json()
```

```python out
[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/2792',
  'repository_url': 'https://api.github.com/repos/huggingface/datasets',
  'labels_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/labels{/name}',
  'comments_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/comments',
  'events_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/events',
  'html_url': 'https://github.com/huggingface/datasets/pull/2792',
  'id': 968650274,
  'node_id': 'MDExOlB1bGxSZXF1ZXN0NzEwNzUyMjc0',
  'number': 2792,
  'title': 'Update GooAQ',
  'user': {'login': 'bhavitvyamalik',
   'id': 19718818,
   'node_id': 'MDQ6VXNlcjE5NzE4ODE4',
   'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4',
   'gravatar_id': '',
   'url': 'https://api.github.com/users/bhavitvyamalik',
   'html_url': 'https://github.com/bhavitvyamalik',
   'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers',
   'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}',
   'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}',
   'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}',
   'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions',
   'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs',
   'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos',
   'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}',
   'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events',
   'type': 'User',
   'site_admin': False},
  'labels': [],
  'state': 'open',
  'locked': False,
  'assignee': None,
  'assignees': [],
  'milestone': None,
  'comments': 1,
  'created_at': '2021-08-12T11:40:18Z',
  'updated_at': '2021-08-12T12:31:17Z',
  'closed_at': None,
  'author_association': 'CONTRIBUTOR',
  'active_lock_reason': None,
  'pull_request': {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/2792',
   'html_url': 'https://github.com/huggingface/datasets/pull/2792',
   'diff_url': 'https://github.com/huggingface/datasets/pull/2792.diff',
   'patch_url': 'https://github.com/huggingface/datasets/pull/2792.patch'},
  'body': '[GooAQ](https://github.com/allenai/gooaq) dataset was recently updated after splits were added for the same. This PR contains new updated GooAQ with train/val/test splits and updated README as well.',
  'performed_via_github_app': None}]
```

Uau, aceasta e o cantitate mare de informație! Putem vedea câmpuri utile cum ar fi `title`, `body` și `number` care descriu problema, precum și informații despre utilizatorul GitHub care a deschis issue-ul.

<Tip>

✏️ **Încercați!** Faceți clic pe câteva dintre URL-urile din payload-ul JSON de mai sus pentru a vă familiariza cu tipul de informații către care se face referire pentru fiecare GitHub issue.

</Tip>

După cum este descris în [documentația](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting) GitHub, solicitările neautentificate sunt limitate la 60 de solicitări pe oră. Deși puteți crește `per_page` query parameter pentru a reduce numărul de solicitări pe care le faceți, oricum veți atinge limita pentru orice repository care are mai mult de câteva mii de issues. Prin urmare, ar trebui să urmați [instrucțiunile](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) GitHub pentru crearea unui _personal access token_ astfel încât să puteți crește limita la 5.000 de solicitări pe oră. Odată ce aveți tokenul, îl puteți include ca parte a request header:

```py
GITHUB_TOKEN = xxx  # Copy your GitHub token here
headers = {"Authorization": f"token {GITHUB_TOKEN}"}
```

<Tip warning={true}>

⚠️ Nu oferiți nimănui un notebook cu `GITHUB_TOKEN` în el . Vă recomandăm să ștergeți ultima celulă odată ce ați executat-o pentru a evita scurgerea accidentală a acestor informații. Chiar mai bine, stocați tokenul într-un fișier *.env* și utilizați biblioteca `python-dotenv` pentru a îl încărca automat ca variabilă de mediu.

</Tip>

Acum că avem tokenul de acces, hai să creăm o funcție care să poată descărca toate issue-urile dintr-un repositoriu GitHub:

```py
import time
import math
from pathlib import Path
import pandas as pd
from tqdm.notebook import tqdm


def fetch_issues(
    owner="huggingface",
    repo="datasets",
    num_issues=10_000,
    rate_limit=5_000,
    issues_path=Path("."),
):
    if not issues_path.is_dir():
        issues_path.mkdir(exist_ok=True)

    batch = []
    all_issues = []
    per_page = 100  # Number of issues to return per page
    num_pages = math.ceil(num_issues / per_page)
    base_url = "https://api.github.com/repos"

    for page in tqdm(range(num_pages)):
        # Query with state=all to get both open and closed issues
        query = f"issues?page={page}&per_page={per_page}&state=all"
        issues = requests.get(f"{base_url}/{owner}/{repo}/{query}", headers=headers)
        batch.extend(issues.json())

        if len(batch) > rate_limit and len(all_issues) < num_issues:
            all_issues.extend(batch)
            batch = []  # Flush batch for next time period
            print(f"Reached GitHub rate limit. Sleeping for one hour ...")
            time.sleep(60 * 60 + 1)

    all_issues.extend(batch)
    df = pd.DataFrame.from_records(all_issues)
    df.to_json(f"{issues_path}/{repo}-issues.jsonl", orient="records", lines=True)
    print(
        f"Downloaded all the issues for {repo}! Dataset stored at {issues_path}/{repo}-issues.jsonl"
    )
```

Acum când apelăm `fetch_issues()` va descărca toate problemele în batch-uri pentru a evita depășirea limitei GitHub pe numărul de solicitări pe oră; rezultatul va fi stocat într-un fișier `_repository_name-issues.jsonl`, unde fiecare linie este un obiect JSON care reprezintă un issue. Mai jos folosim această funcție pentru a obține toate issue-urile de la 🤗 Datasets:

```py
# În dependență de conexiunea ta la internet, acest lucru poate dura câteva minute...
fetch_issues()
```

Odată ce issue-urile sunt descărcate, le putem încărca local utilizând abilitățile noastre dobândite în [secțiunea 2](/course/chapter5/2):

```py
issues_dataset = load_dataset("json", data_files="datasets-issues.jsonl", split="train")
issues_dataset
```

```python out
Dataset({
    features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'timeline_url', 'performed_via_github_app'],
    num_rows: 3019
})
```

Great, am creat primul nostru dataset de la zero! Dar de ce sunt mai mult de câteva mii de issue-uri atunci când tabul de issue-uri al repositoriului 🤗 Datasets afișează doar aproximativ 1.000 de issue-uri în total 🤔? Conform descris în [documentația](https://docs.github.com/en/rest/reference/issues#list-issues-assigned-to-the-authenticated-user) GitHub, acest lucru s-a întâmplat pentru că am descărcat și toate pull requesturile:

> GitHub's REST API v3 considers every pull request an issue, but not every issue is a pull request. For this reason, "Issues" endpoints may return both issues and pull requests in the response. You can identify pull requests by the `pull_request` key. Be aware that the `id` of a pull request returned from "Issues" endpoints will be an issue id.

Deoarece conținutul issue-urilor și pull requesturilor este destul de diferit, hai să preprocesăm puțin datele pentru a ne permite să le diferențiem între ele.

## Curățarea datelor[[cleaning-up-the-data]]

Fragmentul de mai sus din documentația GitHub ne spune că coloana `pull_request` poate fi utilizată pentru a diferenția între issues și pull requests. Să analizăm un sampple aleatoriu pentru a vedea care este diferența. Așa cum am făcut în [secțiunea 3](/course/chapter5/3), vom înlănțui `Dataset.shuffle()` și `Dataset.select()` pentru a crea un sample aleatoriu și apoi vom împerechea coloanele `html_url` și `pull_request` pentru a putea compara diversele URL-uri:

```py
sample = issues_dataset.shuffle(seed=666).select(range(3))

# Print out the URL and pull request entries
for url, pr in zip(sample["html_url"], sample["pull_request"]):
    print(f">> URL: {url}")
    print(f">> Pull request: {pr}\n")
```

```python out
>> URL: https://github.com/huggingface/datasets/pull/850
>> Pull request: {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/850', 'html_url': 'https://github.com/huggingface/datasets/pull/850', 'diff_url': 'https://github.com/huggingface/datasets/pull/850.diff', 'patch_url': 'https://github.com/huggingface/datasets/pull/850.patch'}

>> URL: https://github.com/huggingface/datasets/issues/2773
>> Pull request: None

>> URL: https://github.com/huggingface/datasets/pull/783
>> Pull request: {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/783', 'html_url': 'https://github.com/huggingface/datasets/pull/783', 'diff_url': 'https://github.com/huggingface/datasets/pull/783.diff', 'patch_url': 'https://github.com/huggingface/datasets/pull/783.patch'}
```

Aici putem vedea că fiecare pull request este asociat cu diverse URL-uri, în timp ce issue-urile obișnuite au o intrare `None`. Putem utiliza această distincție pentru a crea o nouă coloană `is_pull_request` care verifică dacă câmpul `pull_request` este `None` sau nu:

```py
issues_dataset = issues_dataset.map(
    lambda x: {"is_pull_request": False if x["pull_request"] is None else True}
)
```

<Tip>

✏️ **Încercați!** Calculați timpul mediu necesar pentru închiderea issue-urilor în Datasets. Vă poate fi utilă funcția `Dataset.filter()` pentru a filtra pull requesturile și issue-urile deschise, și puteți utiliza funcția `Dataset.set_format()` pentru a converti datasetul într-un `DataFrame` astfel încât să puteți manipula cu ușurință timestampurile `created_at` și `closed_at`. Pentru puncte bonus, calculați timpul mediu necesar pentru închiderea pull requesturilor.

</Tip>

Deși am putea continua să curățăm datasetul prin eliminarea sau redenumirea unor coloane, este, în general, o practică bună să păstrăm datasetul cât mai "raw" posibil la acest stadiu, astfel încât să poată fi utilizat ușor în multiple aplicații.

Înainte de a încărca datasetul în Hugging Face Hub, trebuie să rezolvăm chestie care lipsește din el: comentariile asociate fiecărui issue și pull request. Le vom adăuga în continuare cu-- ați ghicit -- GitHub REST API!

## Îmbunătățirea datasetului[[augmenting-the-dataset]]

După cum se vede în următorul screenshot, comentariile asociate unui issue sau pull request oferă o sursă bogată de informații, în special dacă suntem interesați să construim un motor de căutare pentru a răspunde la întrebările utilizatorilor despre bibliotecă.

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter5/datasets-issues-comment.png" alt="Comentariile asociate unei probleme despre 🤗 Datasets." width="80%"/>
</div>

GitHub REST API oferă un endpoint [`Comments`](https://docs.github.com/en/rest/reference/issues#list-issue-comments) care returnează toate comentariile asociate numărului problemei. Să testăm endpointul pentru a vedea ce returnează:

```py
issue_number = 2792
url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments"
response = requests.get(url, headers=headers)
response.json()
```

```python out
[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/comments/897594128',
  'html_url': 'https://github.com/huggingface/datasets/pull/2792#issuecomment-897594128',
  'issue_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792',
  'id': 897594128,
  'node_id': 'IC_kwDODunzps41gDMQ',
  'user': {'login': 'bhavitvyamalik',
   'id': 19718818,
   'node_id': 'MDQ6VXNlcjE5NzE4ODE4',
   'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4',
   'gravatar_id': '',
   'url': 'https://api.github.com/users/bhavitvyamalik',
   'html_url': 'https://github.com/bhavitvyamalik',
   'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers',
   'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}',
   'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}',
   'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}',
   'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions',
   'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs',
   'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos',
   'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}',
   'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events',
   'type': 'User',
   'site_admin': False},
  'created_at': '2021-08-12T12:21:52Z',
  'updated_at': '2021-08-12T12:31:17Z',
  'author_association': 'CONTRIBUTOR',
  'body': "@albertvillanova my tests are failing here:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n    def test_load_dataset(self, dataset_name):\r\n        configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n>       self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n    self.parent.assertTrue(len(dataset[split]) > 0)\r\nE   AssertionError: False is not true\r\n```\r\nWhen I try loading dataset on local machine it works fine. Any suggestions on how can I avoid this error?",
  'performed_via_github_app': None}]
```

Putem vedea că comentariul este stocat în câmpul `body`, așa că putem scrie o funcție simplă care returnează toate comentariile asociate unei probleme prin extragerea conținutului `body` pentru fiecare element în `response.json()`:

```py
def get_comments(issue_number):
    url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments"
    response = requests.get(url, headers=headers)
    return [r["body"] for r in response.json()]


# Testăm dacă funcția lucrează cum ne dorim
get_comments(2792)
```

```python out
["@albertvillanova my tests are failing here:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n    def test_load_dataset(self, dataset_name):\r\n        configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n>       self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n    self.parent.assertTrue(len(dataset[split]) > 0)\r\nE   AssertionError: False is not true\r\n```\r\nWhen I try loading dataset on local machine it works fine. Any suggestions on how can I avoid this error?"]
```

Arată bine. Acum hai să folosim `Dataset.map()` pentru a adăuga noi coloane `comments` fiecărui issue în datasetul nostru:

```py
# Depending on your internet connection, this can take a few minutes...
issues_with_comments_dataset = issues_dataset.map(
    lambda x: {"comments": get_comments(x["number"])}
)
```

Ultimul pas este să facem push datasetului nostru pe Hub. Să vedem cum putem face asta.

## Încărcarea datasetului pe Hugging Face Hub[[uploading-the-dataset-to-the-hugging-face-hub]]

<Youtube id="HaN6qCr_Afc"/>

Acum că avem datasetul nostru augmentat, este timpul să îi facem push pe Hub pentru a-l oferi comunității! Încărcarea unui dataset este foarte simplu: la fel ca modelele și tokenizerrii din 🤗 Transformers, putem utiliza o metodă `push_to_hub()` pentru a face push unui dataset. Pentru a face asta, avem nevoie de un token de autentificare, care poate fi obținut prin autentificarea pe Hugging Face Hub cu funcția `notebook_login()`:

```py
from huggingface_hub import notebook_login

notebook_login()
```

Acest lucru va crea un widget unde poți să scrii usernameul și parola ta, iar un API token va fi salvat în *~/.huggingface/token*. Dacă rulezi codeul într-un terminal, te poți loga cu ajutor CLI: 
This will create a widget where you can enter your username and password, and an API token will be saved in *~/.huggingface/token*. If you're running the code in a terminal, you can log in via the CLI instead:

```bash
huggingface-cli login
```
O dată ce ai făcut asta, putem încărca datasetul rulând:

```py
issues_with_comments_dataset.push_to_hub("github-issues")
```

De acum, orice poate să descarce datasetul, utilizând `load_dataset()` cu ID-ul repositoriului ca `path` argument:

```py
remote_dataset = load_dataset("lewtun/github-issues", split="train")
remote_dataset
```

```python out
Dataset({
    features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
    num_rows: 2855
})
```

Cool, am încărcat datasetul nostru pe Hub și acum este disponibil pentru alții să îl utilizeze! Mai este doar un lucru important de făcut: adăugarea unui _dataset card_ care explică cum a fost creat corpusul și oferă alte informații utile pentru comunitate.

<Tip>

💡 De asemenea, puteți încărca un dataset pe Hugging Face Hub direct din terminal utilizând `huggingface-cli` și puțină magie Git. Consultați [ghidul 🤗 Datasets](https://huggingface.co/docs/datasets/share#share-a-dataset-using-the-cli) pentru detalii despre cum puteți face asta.

</Tip>

## Crearea unei dataset card[[creating-a-dataset-card]]

Datasetiroșe bine documentate sunt mai probabil să fie utile altora (inclusiv ție din viitor!), deoarece furnizează contextul pentru a permite utilizatorilor să decidă dacă datasetul este relevant pentru taskul lor și să evalueze eventualele biasuri sau riscurile asociate cu utilizarea datasetului.

Pe Hugging Face Hub, această informație este stocată în fișierul *README.md* al fiecărui dataset repository. Sunt doi pași principali pe care trebuie să îi efectuați înainte de a crea acest fișier:

1. Utilizați aplicația [`datasets-tagging`](https://huggingface.co/datasets/tagging/) pentru a crea etichete de metadate în format YAML. Aceste taguri sunt utilizate pentru o varietate de funcționalități de căutare pe Hugging Face Hub și asigură că datasetul poate fi găsit ușor de membrii comunității. Deoarece am creat un dataset custom aici, veți fi nevoiți să clonați repositoriul `datasets-tagging` și să rulați aplicația local. Iată cum arată interfața:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter5/datasets-tagger.png" alt="Interfața 'datasets-tagging'." width="80%"/>
</div>

2. Citiți [ghidul 🤗 Datasets](https://github.com/huggingface/datasets/blob/master/templates/README_guide.md) despre crearea de dataset cards informative și utilizați-l ca șablon.

Puteți crea fișierul *README.md* direct pe Hub și puteți găsi un template pentru dataset card în repositoriul `lewtun/github-issues`. Un screenshot a dataset card completată este afișată mai jos.

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter5/dataset-card.png" alt="Dataset card." width="80%"/>
</div>

<Tip>

✏️ **Încercați!** Utilizați aplicația `dataset-tagging` și [ghidul 🤗 Datasets](https://github.com/huggingface/datasets/blob/master/templates/README_guide.md) pentru a completa fișierul *README.md* pentru datasetul de probleme GitHub.

</Tip>

Astfel, am văzut în această secțiune că crearea unui dataset bun poate fi destul de complicată, dar, spre norocul nsotru, încărcarea și oferirea acestuia comunității nu sunt. În secțiunea următoare, vom utiliza datasetul nou pentru a crea un motor de căutare semantic cu 🤗 Datasets care poate să asocieze întrebări cu cele mai relevante issues și comentarii.

<Tip>

✏️ **Încercați!** Treceți prin pașii pe care i-am făcut în această secțiune pentru a crea un dataset de issues GitHub pentru o biblioteca open source care îți place(alegeți altceva înafară de 🤗 Datasets, desigur!). Pentru puncte bonus, faceți fine-tune unui multilabel classifier pentru a prezice tagurile prezente în câmpul `labels`.

</Tip>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter5/5.mdx" />

### Introducere[[introduction]]
https://huggingface.co/learn/course/ro/chapter5/1.md

# Introducere[[introduction]]

<CourseFloatingBanner
    chapter={5}
    classNames="absolute z-10 right-0 top-0"
/>

În [Capitolul 3](/course/chapter3) ați încercat biblioteca 🤗Datasets și ați văzut că existau trei pași principali atunci când vine vorba de fine-tuningul unui model:

1. Încărcați un dataset din Hugging Face Hub.
2. Preprocesați datele cu `Dataset.map()`.
3. Încărcați și calculați metricele.

Dar aceasta este doar o mică parte a ceea ce poate face 🤗 Datasets! În acest capitol, ne vom aprofunda în această bibliotecă. Pe parcurs, vom găsi răspunsuri la următoarele întrebări:

* Ce faceți atunci când datasetul tău nu este pe Hub?
* Cum puteți tăia și împărți un dataset? (Și ce dacă tu _really_ trebuie să folosești Pandas?)
* Ce faceți atunci când datasetul este uriaș și va topi RAM-ul laptopului dumneavoastră?
* Ce este "memory mapping" și Apache Arrow?
* Cum puteți crea propriul dataset și să-l trimiteți pe Hub?

Tehnicile pe care le veți învăța aici vă vor pregăti pentru sarcinile avansate de tokenizare și fine-tuning din [Capitolul 6](/course/chapter6) și [Capitolul 7](/course/chapter7) -- deci luați o cafea sau două și să începem!

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter5/1.mdx" />

### E timpul să tăiem și să analizăm datele[[time-to-slice-and-dice]]
https://huggingface.co/learn/course/ro/chapter5/3.md

# E timpul să tăiem și să analizăm datele[[time-to-slice-and-dice]]

<CourseFloatingBanner chapter={5}
  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/chapter5/section3.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter5/section3.ipynb"},
]} />

În cea mai mare parte, datele cu care lucrezi nu vor fi perfect pregătite pentru antrenarea modelelor. În această secțiune vom explora features variate pe care 🤗 Datasets le oferă pentru curățirea dataseturilor.

<Youtube id="tqfSFcPMgOI"/>

## Slicing și dicing asupra datelor[[slicing-and-dicing-our-data]]

Asemenea Pandas, 🤗 Datasets oferă mai multe funcții pentru a manipula conținutul obiectelor `Dataset` și `DatasetDict`. Am întâlnit deja metoda `Dataset.map()` în [Capitolul 3](/course/chapter3), iar în această secțiune vom explora alte funcții de care dispunem.

În acest exemplu, vom folosi [Drug Review Dataset](https://archive.ics.uci.edu/ml/datasets/Drug+Review+Dataset+%28Drugs.com%29) găzduit pe [UC Irvine Machine Learning Repository](https://archive.ics.uci.edu/ml/index.php), care conține reviewurile pacienților privind diverse medicamente, alături de bolile care sunt tratate și o evaluare de 10 stele a satisfacției pacientului.

În primul rând trebuie să descărcăm și să extragem datele, ceea ce se poate de făcut cu comenzile `wget` și `unzip`:

```py
!wget "https://archive.ics.uci.edu/ml/machine-learning-databases/00462/drugsCom_raw.zip"
!unzip drugsCom_raw.zip
```

Deoarece TSV este o variantă a CSV care folosește taburi în loc de virgulă ca separator, putem încărca aceste fișiere prin folosirea scriptului de încărcare `csv` și specificarea argumentului `delimiter` în funcția `load_dataset()` astfel:

```py
from datasets import load_dataset

data_files = {"train": "drugsComTrain_raw.tsv", "test": "drugsComTest_raw.tsv"}
# \t este caracterul tab de Python
drug_dataset = load_dataset("csv", data_files=data_files, delimiter="\t")
```

O practică bună atunci când faceți orice fel de analiză a datelor este să vă luați un mic random sample pentru a înțelege cu ce tip de date lucrați. În 🤗 Datasets, putem crea o colecție aleatorie prin legarea funcțiilor `Dataset.shuffle()` și `Dataset.select()`:

```py
drug_sample = drug_dataset["train"].shuffle(seed=42).select(range(1000))
# Vizualizați primele câteva exemple
drug_sample[:3]
```

```python out
{'Unnamed: 0': [87571, 178045, 80482],
 'drugName': ['Naproxen', 'Duloxetine', 'Mobic'],
 'condition': ['Gout, Acute', 'ibromyalgia', 'Inflammatory Conditions'],
 'review': ['"like the previous person mention, I&#039;m a strong believer of aleve, it works faster for my gout than the prescription meds I take. No more going to the doctor for refills.....Aleve works!"',
  '"I have taken Cymbalta for about a year and a half for fibromyalgia pain. It is great\r\nas a pain reducer and an anti-depressant, however, the side effects outweighed \r\nany benefit I got from it. I had trouble with restlessness, being tired constantly,\r\ndizziness, dry mouth, numbness and tingling in my feet, and horrible sweating. I am\r\nbeing weaned off of it now. Went from 60 mg to 30mg and now to 15 mg. I will be\r\noff completely in about a week. The fibro pain is coming back, but I would rather deal with it than the side effects."',
  '"I have been taking Mobic for over a year with no side effects other than an elevated blood pressure.  I had severe knee and ankle pain which completely went away after taking Mobic.  I attempted to stop the medication however pain returned after a few days."'],
 'rating': [9.0, 3.0, 10.0],
 'date': ['September 2, 2015', 'November 7, 2011', 'June 5, 2013'],
 'usefulCount': [36, 13, 128]}
```

Atrageți atenția că am fixat seedul în `Dataset.shuffle()` pentru posibilitatea de reproducere. `Dataset.select()` se așteaptă la un iterabil cu indices, deci noi am scris `range(1000)` pentru a primi primele 1000 de exemple din datasetul amestecat. Din acest sample putem vedea câteva ciudățenii în datasetul nostru:

* Coloana `Unnamed: 0` are un aspect neobișnuit, care sugerează că este un anonymized ID a fiecărui pacient.
* Coloana `condition` conține labeluri majuscule și minuscule.
* Recenziile au lungimi variate și conțin caractere Python precum `\r\n` şi caractere HTML ca `&\#039;`.

Hai să vedem cum putem folosi 🤗 Datasets pentru a face față fiecărei dintre aceste probleme. Pentru a testa ipoteza identificării pacientului pentru coloana `Unnamed: 0`, putem folosi funcția `Dataset.unique()` pentru a verifica dacă numărul de ID-uri corespunde cu numărul de rânduri în fiecare split:

```py
for split in drug_dataset.keys():
    assert len(drug_dataset[split]) == len(drug_dataset[split].unique("Unnamed: 0"))
```

Aceasta pare să confirme ipoteza, deci putem curăța datasetul puțin, redenumind coloana `Unnamed: 0` pentru a-i da un nume mai interpretabil. Putem folosi funcția `DatasetDict.rename_column()` pentru a renumea coloana în același timp în ambele splituri:

```py
drug_dataset = drug_dataset.rename_column(
    original_column_name="Unnamed: 0", new_column_name="patient_id"
)
drug_dataset
```

```python out
DatasetDict({
    train: Dataset({
        features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'],
        num_rows: 161297
    })
    test: Dataset({
        features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'],
        num_rows: 53766
    })
})
```

<Tip>

✏️ **Încearcă!** Folosiți funcția `Dataset.unique()` pentru a găsi numărul de medicamente și condiții unice în seturile de antrenare și testare.

</Tip>

În continuare, vom normaliza toate `condition` labels folosind `Dataset.map()`. La fel cum am făcut cu tokenizarea în [Capitolul 3](/course/chapter3), putem defini o funcție simplă care poate fi aplicată pe toate rândurile fiecărui split din `drug_dataset`:

```py
def lowercase_condition(example):
    return {"condition": example["condition"].lower()}


drug_dataset.map(lowercase_condition)
```

```python out
AttributeError: 'NoneType' object has no attribute 'lower'
```

Oh no, am întâmpinat o problemă cu funcția map! Din eroarea noastră se poate deduce că unele intrări din coloana `condition` sunt `None`, care nu pot fi convertite la caracterul mic pentru că nu sunt string-uri. Vom elimina aceste rânduri folosind `Dataset.filter()`, care funcționează în mod similar cu `Dataset.map()` și se așteaptă o funcție care primește un exemplu al datasetului.

În loc de a scrie o funcție explicită ca:

```py
def filter_nones(x):
    return x["condition"] is not None
```

și apoi să rulăm `drug_dataset.filter(filter_nones)`, putem face acest lucru într-o linie folosind o _funcție lambda_. În Python, funcțiile lambda sunt funcții mici care pot fi definite fără a le numi. Ele au forma generală:

```
lambda <argumente> : <expresie>
```

unde `lambda` este unul dintre [cuvintele cheie](https://docs.python.org/3/reference/lexical_analysis.html#keywords) Python, `<argumente>` reprezintă o listă/set de valori separate prin virgulă care definesc inputurile funcției și `<expresie>` reprezintă operațiile pe care dorim să le executăm. De exemplu, putem defini o funcție lambda care ridică un număr la pătrat:

```py
lambda x: x * x
```

Pentru a aplica această funcție la un input, trebuie să îi facem wrap și pe să punem inputul în paranteze:

```py
(lambda x: x * x)(3)
```

```python out
9
```

La fel, putem defini funcții lambda cu mai multe argumente prin separarea acestora prin virgulă. De exemplu, putem calcula suprafața unui triunghi ca:

```py
(lambda base, height: 0.5 * base * height)(4, 8)
```

```python out
16.0
```

Funcțiile lambda sunt utile atunci când dorim să definim funcții mici, pentru o singură folosire (pentru mai multe informații despre ele, recomandăm citirea excelentului [Real Python tutorial](https://realpython.com/python-lambda/) scris de Andre Burgaud). În contextul 🤗 Datasets, putem utiliza funcțiile lambda pentru a defini operații simple de map și filter, astfel încât să eliminăm intrările `None` din datasetul nostru:

```py
drug_dataset = drug_dataset.filter(lambda x: x["condition"] is not None)
```

Cu intrările `None` eliminate, putem normaliza coloana `condition`:

```py
drug_dataset = drug_dataset.map(lowercase_condition)
# Verificăm dacă lowercasing a funcționat
drug_dataset["train"]["condition"][:3]
```

```python out
['left ventricular dysfunction', 'adhd', 'birth control']
```

Funcționează! Acum că am curățat labelurile, să vedem cum putem curăți și recenziile.

## Crearea de noi coloane[[creating-new-columns]]

Atunci când lucrați cu recenziile clienților, o practică bună este să verificați numărul de cuvinte în fiecare recenzie. O recenzie poate fi doar un singur cuvânt, cum ar fi "Excelent!" sau un eseu complet care are sute de cuvinte și depinde de cazul pe care îl aveți la vedere, aici trebuie să vă asigurați că faceți față acestor extreme diferit. Pentru a calcula numărul de cuvinte în fiecare recenzie, vom folosi un heuristic aproximativ bazat pe splittingul textului prin spații.

Vom defini o funcție simplă care numără numărul de cuvinte din fiecare recenzie:

```py
def compute_review_length(example):
    return {"review_length": len(example["review"].split())}
```

Spre deosebire de funcția `lowercase_condition()`, `compute_review_length()` returnează un dicționar ale cărui key nu corespund uneia dintre numele coloanelor din dataset. În acest caz, atunci când `compute_review_length()` este transmis în `Dataset.map()`, el va fi aplicat pe toate rândurile din dataset pentru a crea o nouă coloană `review_length`:

```py
drug_dataset = drug_dataset.map(compute_review_length)
# Inspectăm primul exemplu de training
drug_dataset["train"][0]
```

```python out
{'patient_id': 206461,
 'drugName': 'Valsartan',
 'condition': 'left ventricular dysfunction',
 'review': '"It has no side effect, I take it in combination of Bystolic 5 Mg and Fish Oil"',
 'rating': 9.0,
 'date': 'May 20, 2012',
 'usefulCount': 27,
 'review_length': 17}
```

Așa cum era de așteptat, putem vedea o nouă coloană `review_length` adăugată la setul de antrenare. Putem sorta această nouă coloană cu `Dataset.sort()` pentru a vedea cum valorile extreme arată:

```py
drug_dataset["train"].sort("review_length")[:3]
```

```python out
{'patient_id': [103488, 23627, 20558],
 'drugName': ['Loestrin 21 1 / 20', 'Chlorzoxazone', 'Nucynta'],
 'condition': ['birth control', 'muscle spasm', 'pain'],
 'review': ['"Excellent."', '"useless"', '"ok"'],
 'rating': [10.0, 1.0, 6.0],
 'date': ['November 4, 2008', 'March 24, 2017', 'August 20, 2016'],
 'usefulCount': [5, 2, 10],
 'review_length': [1, 1, 1]}
```

Precum am presupus, unele recenii conțin doar un singur cuvânt, ceea ce, deși ar putea fi OK pentru analiza sentimentului, nu ar fi informativ dacă vrem să prezicem condiției.

<Tip>

🙋 O alternativă la adăugarea unei noi coloane într-un dataset este funcția `Dataset.add_column()`. Aceasta permite să oferiți coloana ca o listă Python sau array NumPy și poate fi utilă în situații în care `Dataset.map()` nu este bine adaptat pentru analiza dumneavoastră.

</Tip>

Hai să folosim funcția `Dataset.filter()` pentru a elimina recenziile care conțin mai puțin de 30 de cuvinte. Similar cum am făcut în cazul coloanei `condition`, putem elimina recenziile foarte scurte cerând ca recenziile să aibă o lungime mai mare decât acest prag:

```py
drug_dataset = drug_dataset.filter(lambda x: x["review_length"] > 30)
print(drug_dataset.num_rows)
```

```python out
{'train': 138514, 'test': 46108}
```

După cum vedeți, aceasta a eliminat aproximativ 15% din recenziile noastrem, din seturile originale de antrenare și testare.

<Tip>

✏️ **Încercați!** Folosiți funcția `Dataset.sort()` pentru a inspecta recenziile cu cele mai mari numere de cuvinte. Vezi [documentația](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.sort) pentru a vedea ce argument trebuie să folosești pentru a sorta recenziile în ordine descrescătoare.

</Tip>

Ultima chestie de care trebuie să ne ocupăm este prezența caracterelor HTML în recenziile noastre. Putem folosi modulul `html` din Python pentru a face unescape acestor caractere:

```py
import html

text = "I&#039;m a transformer called BERT"
html.unescape(text)
```

```python out
"I'm a transformer called BERT"
```

Vom folosi `Dataset.map()` pentru a face unescape toate caracterele HTML din corpus:

```py
drug_dataset = drug_dataset.map(lambda x: {"review": html.unescape(x["review"])})
```

În mod evident, metoda `Dataset.map()` este foarte utilă pentru procesarea datelor – și nu am abordat decât o mică parte din ceea ce poate face!

## Superputerile metodei `map()`[[the-map-methods-superpowers]]

Metoda `Dataset.map()` acceptă un argument `batched` care, dacă este setat pe `True`, cauzează ca ea să trimită un batch de exemple la funcția map în același timp (dimensiunea batchului poate fi configurată dar defaultul este 1.000). De exemplu, anterior am folosit o funcție map care a făcut unescaped toate caracterele HTML din recenziile noastre și i-a luat câteva secunde să execute (puteți citi timpul pe progress bars). Putem accelera acest lucru prin procesarea mai multor elemente în același timp folosind list comprehension.

Când specificați `batched=True` funcția primește un dicționar cu câmpurile datasetului, dar fiecare valoare este acum _list of values_ și nu doar o singură valoare. Valoarea de return a `Dataset.map()` ar trebui să fie la fel: un dicționar cu câmpurile pe care dorim să le actualizăm sau adăugăm în datasetul nostru, și o listă de valori. De exemplu, mai jos este alt mod de a face unescape tuturor caracterelor HTML din recenziile noastre, folosind `batched=True`:

```py
new_drug_dataset = drug_dataset.map(
    lambda x: {"review": [html.unescape(o) for o in x["review"]]}, batched=True
)
```

Dacă executați acest cod într-un notebook, veți vedea că această comandă se execută mult mai rapid decât cea anterioră. Și nu pentru că recenziile noastre au fost deja HTML-unescaped – dacă reexecutați instrucția precedentă (fără `batched=True`), ea va lua același timp ca înainte. Acest lucru se datorează faptului că list comprehension sunt  mai rapide decât executarea aceluiași cod într-un `for` loop, și am câștigat, de asemenea, puțină performanțp accesând multe elemente în același timp, în loc unul câte unul.

Folosirea `Dataset.map()` cu `batched=True` este esențială pentru a obține viteza "rapidă" a tokenizerilor pe care îi vom întâlni în [Capitolul 6](/course/chapter6), care pot repede să tokenizeze listelor mari de texte. De exemplu, pentru tokenizarea tuturor recenziilor medicamentelor cu un tokenizer rapid, putem folosi o funcție ca aceasta:

```py
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")


def tokenize_function(examples):
    return tokenizer(examples["review"], truncation=True)
```

Așa cum am văzut în [Capitolul 3](/course/chapter3), putem transmite un singur sau câteva exemple către tokenizer, așadar putem folosi această funcție cu sau fără `batched=True`. Hai să ne folosim de această oportunitate și să comparăm performanța diferitelor opțiuni. Într-un notebook puteți măsura timpul unei instrucțiie de o line prin adăugarea `%time` înaintea acelei linii de cod pe care doriți să o măsurați:

```python no-format
%time tokenized_dataset = drug_dataset.map(tokenize_function, batched=True)
```

Puteți și să măsurați un întreg cell prin scrierea `%%time` la începutul celulei. În hardware-ul pe care l-am executat, acest lucru a arătat 10.8s pentru această instrucție (este numărul scris după "Wall time").

<Tip>

✏️ **Încercați!** Executați aceeași instrucție cu și fără `batched=True`, apoi încercați-o cu un tokenizer lent (adaugați `use_fast=False` în metoda `AutoTokenizer.from_pretrained()`), astfel să puteți vedea ce numere obțineți pe hardwareul vostru.

</Tip>

Aici sunt rezultatele pe care le-am obținut cu și fără batching, folosind un tokenizer rapid și lent:

Options         | Fast tokenizer | Slow tokenizer
:--------------:|:--------------:|:-------------:
`batched=True`  | 10.8s          | 4min41s
`batched=False` | 59.2s          | 5min3s

Aceasta înseamnă că utilizarea unui tokenizer rapid cu opțiunea `batched=True` este de 30 de ori mai rapidă decât varianta lentă fără batching - acest lucru este pur și simplu uimitor! Acesta este motivul principal pentru care tokenizerii rapizi sunt setați implicit când se utilizează `AutoTokenizer` (și de ce sunt numiți "rapizi"). Ei pot atinge o asemenea accelerație datorită faptului că codul de tokenizare este executat în Rust, care este un limbaj care facilitează paralelizarea execuției.

Parallelization este și motivul pentru care tokenizerul rapid realizează o accelerare de aproape 6 ori cu batching: nu puteți paraleliza o singură operație de tokenizare, dar atunci când doriți să tokenizați multe texte în același timp, puteți să faceți split execuției pe mai multe procese, fiecare răspunzând pentru propriile texte.

`Dataset.map()` are și o capacitate de parallelization proprie. Deoarece nu sunt susținute de Rust, nu pot să le ofere aceeași accelerație tokenizerilori înceți ca tokenizerilor rapizi, dar pot încă fi utili (în special dacă utilizați un tokenizer care nu are o variantă rapidă). Pentru a activa multiprocessingul, folosiți argumentul `num_proc` și specificați numărul de procese să fie utilizate în apelul `Dataset.map()`:

```py
slow_tokenizer = AutoTokenizer.from_pretrained("bert-base-cased", use_fast=False)


def slow_tokenize_function(examples):
    return slow_tokenizer(examples["review"], truncation=True)


tokenized_dataset = drug_dataset.map(slow_tokenize_function, batched=True, num_proc=8)
```

Puteți experimenta puțin cu timpii pentru a determina numărul de procese optime; în cazul nostru 8 s-a dovedit a produce cea mai mare accelerație. Aici sunt rezultatele pe care le-am obținut cu și fără multiprocessing:

Options         | Fast tokenizer | Slow tokenizer
:--------------:|:--------------:|:-------------:
`batched=True`  | 10.8s          | 4min41s
`batched=False` | 59.2s          | 5min3s
`batched=True`, `num_proc=8`  | 6.52s          | 41.3s
`batched=False`, `num_proc=8` | 9.49s          | 45.2s

Aceste rezultate sunt mult mai bune pentru tokenizerul lent, dar și performanța tokenizerului rapid a fost semnificativ îmbunătățită. Cu toate acestea, trebuie să reamintim că aceasta nu va fi întotdeauna cazul - testele noastre au arătat că este mai rapid să utilizați `batched=True` fără acest argument în cazurile în care valoarea lui `num_proc` diferă de 8. În general, nu vă recomandăm utilizarea multiplicării proceselor pentru tokenizorii rapizi cu `batched=True`.

<tip>

Utilizarea `num_proc` pentru a accelera procesarea este de obicei o idee excelentă, atâta timp cât funcția pe care o utilizați nu utilizează deja multiprocessing.

</tip>

Toate aceste funcționalități condensate într-o singură metodă este foarte impresionant, dar asta nu e totul! Cu `Dataset.map()` și `batched=True` puteți modifica numărul de elemente din datasetul dumneavoastră. Acesta este extrem de util în numeroase situații în care doriți să creați mai multe caracteristici de antrenare dintr-un singur exemplu, și vom avea nevoie de acest lucru ca parte a preprocesării pentru câteva dintre sarcinile NLP pe care le vom discuta în [Capitolul 7](/course/chapter7).

<tip>

💡 În machine learning, un _exemplu_ este de obicei definit ca fiind un set de _features_ care se oferă modelului. În unele contexte, acestea vor fi seturile de coloane dintr-un `Dataset`, dar în altele (ca și aici și pentru răspunderea la întrebări) mai multe caracteristici pot fi extrase dintr-un singur exemplu și să aparțină unei singure coloane.

</tip>

Hai să vedem cum funcționează! Aici vom tokeniza exemplele și le vom face truncatela lungimea maximă de 128, dar vom cere tokenizerului să returneze *toate* chunkurile de text în loc de prima. Acest lucru poate fi făcut cu `return_overflowing_tokens=True`:

```py
def tokenize_and_split(examples):
    return tokenizer(
        examples["review"],
        truncation=True,
        max_length=128,
        return_overflowing_tokens=True,
    )
```

Hai să testăm acest lucru pe un exemplu înainte de a folosi `Dataset.map()` pentru întreg datasetul:

```py
result = tokenize_and_split(drug_dataset["train"][0])
[len(inp) for inp in result["input_ids"]]
```

```python out
[128, 49]
```

Așadar, primul nostru exemplu din setul de antrenare a devenit două features pentru că a fost tokenizat mai mult decât lungimea maximă de tokenuri pe care am specificat-o: prima cu lungimea 128 și a doua cu lungimea 49. Acum trebuie să facem acest lucru pentru toate elementele din dataset!

```py
tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True)
```

```python out
ArrowInvalid: Column 1 named condition expected length 1463 but got length 1000
```

Oh no! Acesta nu a funcționat! De ce? Citind eroarea vom afla motivul: existența unei incompatibilități în lungimea uneia dintre coloane - una cu o lungime de 1,463 și alta de 1,000. Dacă ați privit [documentația](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.map) Dataset.map(), puteți să vă reamintiți că este numărul de sampleuri care sunt oferite funcției pe care noi le facem mapping; aici aceste 1,000 exemplare creează 1,463 features noi, ceea ce duce la un shape error.

Problema este că încercăm să amestecăm două dataseturi cu mărimi diferite: coloanele `drug_dataset` vor avea un număr determinat de exemple (cele 1,000 din eroare), dar `tokenized_dataset` pe care îl construim va fi mai mare (cel cu 1,463 din eroare; el este mai mare decât 1,000 pentru că tokenizăm reviewrile lungi în mai multe exemple folosind `return_overflowing_tokens=True`). Acest lucru nu funcționează pentru un `Dataset`, așadar trebuie să eliminăm sau să modificămm coloanele din datasetul vechi pentru a se potrivi dimensiunea cu cea din noul dataset. Putem face ultima din cele două opțiuni folosind argumentul `remove_columns`:

```py
tokenized_dataset = drug_dataset.map(
    tokenize_and_split, batched=True, remove_columns=drug_dataset["train"].column_names
)
```

Acum acest lucru funcționează fără erori. Putem verifica că noul dataset are mai multe elemente decât datasetul original prin compararea lungimilor:

```py
len(tokenized_dataset["train"]), len(drug_dataset["train"])
```

```python out
(206772, 138514)
```

Am menționat că putem rezolva problema lungimilor diferite are coloanelor prin schimbarea vechilor coloane la aceeași dimensiune cu cele noi. Pentru aceasta, vom avea nevoie de câmpul `overflow_to_sample_mapping` returnat de tokenizer atunci când setăm `return_overflowing_tokens=True`. El ne oferă un mapping de la un nou feature index la indicele sampleului din care a provenit. Prin intermediul acesta, putem asocia fiecărei key prezentă în datasetul original cu o listă de valori de dimensiune corectă prin repetarea valorilor fiecărui exemplu atâta timp cât produce caracteristici noi:

```py
def tokenize_and_split(examples):
    result = tokenizer(
        examples["review"],
        truncation=True,
        max_length=128,
        return_overflowing_tokens=True,
    )
    # Extragem maparea între noul și vechiul indice
    sample_map = result.pop("overflow_to_sample_mapping")
    for key, values in examples.items():
        result[key] = [values[i] for i in sample_map]
    return result
```

Putem observa că funcționează cu `Dataset.map()` fără ca noi să avem nevoie să eliminăm coloanele vechi:

```py
tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True)
tokenized_dataset
```

```python out
DatasetDict({
    train: Dataset({
        features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'],
        num_rows: 206772
    })
    test: Dataset({
        features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'],
        num_rows: 68876
    })
})
```

Obținem același număr de features training ca și înainte, dar acum am păstrat toate câmpurile vechi. Dacă ai nevoie de ele pentru post-procesare după aplicarea modelului, ar fi util să folosiți această abordare.

Acum ați învățat cum pot fi utilizate 🤗 Datasets pentru preprocesarea datelor prin metode diferite. Deși funcțiile de preprocesare ale 🤗 Datasets vor acoperi majoritatea nevoilor de antrenare a modelului,
există momente în care veți avea nevoie să treceți la Pandas pentru accesul la caracteristici mai puternice, cum ar fi `DataFrame.groupby()` sau high-level APIs pentru vizualizare. Din fericire, 🤗 Dataset a fost proiectat astfel încât să fie interoperabil cu biblioteci precum Pandas, NumPy, PyTorch, TensorFlow și JAX. Hai să vedem cum se face acest lucru.

## De la `Dataset`s la `DataFrame`s și înapoi[[from-datasets-to-dataframes-and-back]]

<Youtube id="tfcY1067A5Q"/>

Pentru a permite conversia între diferite biblioteci, 🤗 Datasets oferă o funcția `Dataset.set_format()`. Această funcție schimbă doar _output format_ al datasetului, deci puteți ușor să treceți la un alt format fără a afecta _data format_ de bază, care este Apache Arrow. Formatarea se face direct. Pentru a demonstra acest lucru, hai să convertim datasetul nostru în Pandas:

```py
drug_dataset.set_format("pandas")
```

Acum, atunci când accesăm elementele din dataset, obținem `pandas.DataFrame` în loc de un dicționar:

```py
drug_dataset["train"][:3]
```

<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>patient_id</th>
      <th>drugName</th>
      <th>condition</th>
      <th>review</th>
      <th>rating</th>
      <th>date</th>
      <th>usefulCount</th>
      <th>review_length</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>95260</td>
      <td>Guanfacine</td>
      <td>adhd</td>
      <td>"My son is halfway through his fourth week of Intuniv..."</td>
      <td>8.0</td>
      <td>April 27, 2010</td>
      <td>192</td>
      <td>141</td>
    </tr>
    <tr>
      <th>1</th>
      <td>92703</td>
      <td>Lybrel</td>
      <td>birth control</td>
      <td>"I used to take another oral contraceptive, which had 21 pill cycle, and was very happy- very light periods, max 5 days, no other side effects..."</td>
      <td>5.0</td>
      <td>December 14, 2009</td>
      <td>17</td>
      <td>134</td>
    </tr>
    <tr>
      <th>2</th>
      <td>138000</td>
      <td>Ortho Evra</td>
      <td>birth control</td>
      <td>"This is my first time using any form of birth control..."</td>
      <td>8.0</td>
      <td>November 3, 2015</td>
      <td>10</td>
      <td>89</td>
    </tr>
  </tbody>
</table>

În continuare, vom crea un `pandas.DataFrame` pentru întregul set de antrenare prin selectarea tuturor elementelor din `drug_dataset["train"]`:

```py
train_df = drug_dataset["train"][:]
```

<Tip>

🚨 În spatele scenei, `Dataset.set_format()` schimbă formatul returnat pentru  `__getitem__()` dunder method a datasetului. Asta înseamnă că atunci când dorim să creăm un nou obiect ca `train_df` dintr-un `Dataset` în formatul `"pandas"`, trebuie să tăiem întreg datasetul pentru a obține un `pandas.DataFrame`. Puteți verifica voi înşivă că tipul lui `drug_dataset["train"]` este `Dataset`, indiferent de output format.

</Tip>

Acum putem utiliza toate funcționalitățile Pandas pe care le dorim. De exemplu, putem face fancy chaining pentru a calcula distribuția clasei printre intrările `condition`:

```py
frequencies = (
    train_df["condition"]
    .value_counts()
    .to_frame()
    .reset_index()
    .rename(columns={"index": "condition", "condition": "frequency"})
)
frequencies.head()
```

<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>condition</th>
      <th>frequency</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>birth control</td>
      <td>27655</td>
    </tr>
    <tr>
      <th>1</th>
      <td>depression</td>
      <td>8023</td>
    </tr>
    <tr>
      <th>2</th>
      <td>acne</td>
      <td>5209</td>
    </tr>
    <tr>
      <th>3</th>
      <td>anxiety</td>
      <td>4991</td>
    </tr>
    <tr>
      <th>4</th>
      <td>pain</td>
      <td>4744</td>
    </tr>
  </tbody>
</table>


Și odată ce suntem gata cu analiza noastră Pandas, putem crea întotdeauna un nou obiect `Dataset` prin utilizarea funcției `Dataset.from_pandas()`:


```py
from datasets import Dataset

freq_dataset = Dataset.from_pandas(frequencies)
freq_dataset
```


<Tip>

✏️ **Încercați!** Calculați media ratingului per medicament și salvați rezultatul într-un nou `Dataset`.

</Tip>


Acest lucru completează turul nostru de tehnici de preprocesare disponibile în 🤗 Datasets. Pentru a finisa secțiunea, vom crea un set de validare pentru a pregăti datasetul pentru antrenarea unui clasificator. Înainte de a face asta, noi vom reseta output formatul `drug_dataset` de la `"pandas"` la `"arrow" :


```python
drug_dataset.reset_format()
```

## Crearea unui set de validare [[creating-a-validation-set]]

Deși avem deja un set de testare pe care îl putem folosi pentru evaluare, este o bună practică să lăsăm setul de test neschimbat și să creăm un set de validare în timpul developmentului. Odată ce sunteți fericiți cu performanța modelului pe setul de validare, puteți face o verificare finală a setului de test. Acest proces ajută la reducerea riscului ca să vă adaptați prea mult setul de test și să depuneți un model care poate eșua analizând date reale.

🤗 Datasets oferă o funcție `Dataset.train_test_split()` bazată pe funcționalitatea celebră din `scikit-learn`. O vom folosi pentru a împărți setul nostru de antrenare în split-uri de `train` și `validation` (setăm argumentul `seed` pentru reproductabilitate):

```python
drug_dataset_clean = drug_dataset["train"].train_test_split(train_size=0.8, seed=42)
# Renumește implicit "test" split-ul la "valide"
drug_dataset_clean["validation"] = drug_dataset_clean.pop("test")
# Adaugă setul de test în `DatasetDict`
drug_dataset_clean["test"] = drug_dataset["test"]
drug_dataset_clean
```

```python out
DatasetDict({
    train: Dataset({
        features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
        num_rows: 110811
    })
    validation: Dataset({
        features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
        num_rows: 27703
    })
    test: Dataset({
        features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
        num_rows: 46108
    })
})
```

Foarte bine, am pregătit acum un dataset care este gata pentru antrenarea unor modele! În [Secțiunea 5](/course/chapter5/5) vom arăta cum puteți încărca seturile de date în Hugging Face Hub, dar în acest moment hai să punem capăt analizei noastre prin examinarea a câteva modalități de salvare a seturilor de date pe dispozitivele locale.

## Salvarea unui dataset[[saving-a-dataset]]

<Youtube id="blF9uxYcKHo"/>

Deși 🤗 Datasets va face cache fiecărui dataset și a operațiilor efectuate asupra acestuia, există momente în care veți dori să salvați un dataset pe disc (de exemplu, în cazul în care cache-ul se șterge). După cum vedeți în tabelul de mai jos, 🤗 Datasets oferă trei funcții principale pentru salvarea datelor în formate diferite:

| Format de date | Funcție |
| :---------: | :--------------------: |
|    Arrow    | `Dataset.save_to_disk()` |
|     CSV     |    `Dataset.to_csv()`    |
|    JSON     |   `Dataset.to_json()`    |

Spre exemplu, hai să salvăm datasetul nostru curățat în formatul Arrow:

```python
drug_dataset_clean.save_to_disk("drug-reviews")
```

Acest lucru va crea un folder cu următoarea structură:

```
drug-reviews/
├── dataset_dict.json
├── test
│   ├── dataset.arrow
│   ├── dataset_info.json
│   └── state.json
├── train
│   ├── dataset.arrow
│   ├── dataset_info.json
│   ├── indices.arrow
│   └── state.json
└── validation
    ├── dataset.arrow
    ├── dataset_info.json
    ├── indices.arrow
    └── state.json
```

unde se poate vedea că fiecare split este asociat cu propriul său tabel `dataset.arrow`, iar unele metadate în `dataset_info.json` și `state.json`. Poți să te gândești la formatul Arrow ca fiind un tabel fancy de coloane și rânduri, optimizat pentru construirea aplicațiilor high-performance care procesează și transportă dataseturi mari.

Odată ce setul de date este salvat, putem încărca-o folosind funcția `load_from_disk()` următoarea:

```py
from datasets import load_from_disk

drug_dataset_reloaded = load_from_disk("drug-reviews")
drug_dataset_reloaded
```

```python out
DatasetDict({
    train: Dataset({
        features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
        num_rows: 110811
    })
    validation: Dataset({
        features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
        num_rows: 27703
    })
    test: Dataset({
        features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
        num_rows: 46108
    })
})
```

Pentru formatele CSV și JSON, trebuie să stocați fiecare split într-un fișier separat. Un mod de a face acest lucru estw iterarea asupra cheilor și valorilor obiectului `DatasetDict`:

```py
for split, dataset in drug_dataset_clean.items():
    dataset.to_json(f"drug-reviews-{split}.jsonl")
```

Acesta salvează fiecare split în [JSON Lines format](https://jsonlines.org), unde fiecare rând din setul de date este stocat ca o singură linie JSON. Aici puteți vedea cum arată primul exemplu:

```bash
!head -n 1 drug-reviews-train.jsonl
```

```python out
{"patient_id":141780,"drugName":"Escitalopram","condition":"depression","review":"\"I seemed to experience the regular side effects of LEXAPRO, insomnia, low sex drive, sleepiness during the day. I am taking it at night because my doctor said if it made me tired to take it at night. I assumed it would and started out taking it at night. Strange dreams, some pleasant. I was diagnosed with fibromyalgia. Seems to be helping with the pain. Have had anxiety and depression in my family, and have tried quite a few other medications that haven't worked. Only have been on it for two weeks but feel more positive in my mind, want to accomplish more in my life. Hopefully the side effects will dwindle away, worth it to stick with it from hearing others responses. Great medication.\"","rating":9.0,"date":"May 29, 2011","usefulCount":10,"review_length":125}
```

Putem apoi folosi tehnicile de la [Secțiunea 2](/course/chapter5/2) pentru încărcarea fișierelor JSON:

```py
data_files = {
    "train": "drug-reviews-train.jsonl",
    "validation": "drug-reviews-validation.jsonl",
    "test": "drug-reviews-test.jsonl",
}
drug_dataset_reloaded = load_dataset("json", data_files=data_files)
```

Și ăsta este finalul excursiei noastre în lumea manipulării datelor cu 🤗 Datasets! Acum că avem un dataset curat, pregătit pentru antrenarea unui model, aici sunt câteva idei pe care le puteți încerca:

1. Folosiți tehnicile din [Capitolul 3](/course/chapter3) pentru a antrena un classifier care poate prezice starea pacientului pe baza recenziei medicamentului.
2. Folosiți pipelineul `summarization` din [Capitolul 1](/course/chapter1) pentru a genera rezumate ale recenziilor.

În următoarea secțiune, vom vedea cum 🤗 Datasets vă permite să lucrați cu dataseturi mari fără ca laptopul tău să explodeze :)!

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter5/3.mdx" />

### Căutare semantică cu FAISS[[semantic-search-with-faiss]]
https://huggingface.co/learn/course/ro/chapter5/6.md

# Căutare semantică cu FAISS[[semantic-search-with-faiss]]

{#if fw === 'pt'}

<CourseFloatingBanner chapter={5}
  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/chapter5/section6_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter5/section6_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={5}
  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/chapter5/section6_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter5/section6_tf.ipynb"},
]} />

{/if}

În [secțiunea 5](/course/chapter5/5), am creat un dataset cu issues și comentarii din repositoriul 🤗 Datasets. În această secțiune, vom utiliza aceste informații pentru a construi un motor de căutare care ne poate ajuta să găsim răspunsurile la  cele mai importante cele mai importante întrebări despre bibliotecă!

<Youtube id="OATCgQtNX2o"/>

## Utilizarea embeddings pentru căutare semantică[[using-embeddings-for-semantic-search]]

După cum am văzut în [Capitolul 1](/course/chapter1), Transformer-based language models reprezintă fiecare token într-un fragment de text ca un _embedding vector_. S-a dovedit că se poate face "pool" embeddingurilor individuale pentru a crea o reprezentare vectorială pentru fraze întregi, paragrafe sau (în anumite cazuri) documente. Aceste embeddings pot fi apoi utilizate pentru a găsi documente similare în corpus prin calcularea dot-product similarity (sau a unei alte metrice de similaritate) între fiecare embedding și returnarea documentelor cu cea mai mare suprapunere.

În această secțiune, vom utiliza embeddings pentru a dezvolta un motor de căutare semantică. Aceste motoare de căutare oferă mai multe avantaje față de abordările convenționale bazate pe căutarea de cuvinte cheie într-un query cu documente.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter5/semantic-search.svg" alt="Căutare semantică."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter5/semantic-search-dark.svg" alt="Căutare semantică."/>
</div>

## Încărcarea și pregătirea datasetului[[loading-and-preparing-the-dataset]]

Prima lucru pe care trebuie să îl facem este să descărcăm datasetul nostru cu GitHub issues, așa că folosim funcția `load_dataset()` ca de obicei:

```py
from datasets import load_dataset

issues_dataset = load_dataset("lewtun/github-issues", split="train")
issues_dataset
```

```python out
Dataset({
    features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
    num_rows: 2855
})
```

Aici am specificat splitul default `train` în `load_dataset()`, astfel încât returnează un `Dataset` în loc de `DatasetDict`. Primul lucru care treubuie făcut este să filtrăm pull requesturile, deoarece acestea rareori tind să fie utilizate pentru a răspunde la întrebările utilizatorilor și vor introduce noise în motorul nostru de căutare. Așa cum ar trebuie deja să știți, putem utiliza funcția `Dataset.filter()` pentru a exclude aceste rânduri din datasetul nostru. În timp ce suntem aici, putem să filtrăm și rândurile fără comentari, deoarece acestea nu oferă niciun răspuns la întrebările utilizatorilor:

```py
issues_dataset = issues_dataset.filter(
    lambda x: (x["is_pull_request"] == False and len(x["comments"]) > 0)
)
issues_dataset
```

```python out
Dataset({
    caracteristici: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
    num_rows: 771
})
```

Putem vedea că există multe coloane în datasetul nostru, majoritatea dintre care nu sunt necesare pentru a construi motorul nostru de căutare. Din perspectiva căutării, cele mai informative coloane sunt `title`, `body` și `comments`, în timp ce `html_url` ne oferă un link înapoi la problema sursă. Hai să utilizăm funcția `Dataset.remove_columns()` pentru a elimina restul:

```py
columns = issues_dataset.column_names
columns_to_keep = ["title", "body", "html_url", "comments"]
columns_to_remove = set(columns_to_keep).symmetric_difference(columns)
issues_dataset = issues_dataset.remove_columns(columns_to_remove)
issues_dataset
```

```python out
Dataset({
    features: ['html_url', 'title', 'comments', 'body'],
    num_rows: 771
})
```

Pentru a crea embeddedurile noastre, vom completa fiecare comentariu cu titlul și body-ul problemei, deoarece aceste câmpuri adesea includ informații contextuale utile. Deoarece coloana noastră `comments` este în prezent o listă de comentarii pentru fiecare issue, trebuie să "explodăm" coloana, astfel încât fiecare rând să fie format dintr-un tuple `(html_url, title, body, comment)`. În Pandas, putem face acest lucru cu funcția [`DataFrame.explode()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.explode.html), care creează un rând nou pentru fiecare element dintr-o coloană asemănătoare cu o listă, în timp ce copiază toate celelalte valori ale coloanelor. Pentru a vedea acest lucru în acțiune, să trecem la formatul pandas `DataFrame` mai întâi:

```py
issues_dataset.set_format("pandas")
df = issues_dataset[:]
```

Dacă inspectăm primul rând din acest `DataFrame`, putem vedea că există patru comentarii asociate acestei probleme:

```py
df["comments"][0].tolist()
```

```python out
['the bug code locate in ：\r\n    if data_args.task_name is not None:\r\n        # Downloading and loading a dataset from the hub.\r\n        datasets = load_dataset("glue", data_args.task_name, cache_dir=model_args.cache_dir)',
 'Hi @jinec,\r\n\r\nFrom time to time we get this kind of `ConnectionError` coming from the github.com website: https://raw.githubusercontent.com\r\n\r\nNormally, it should work if you wait a little and then retry.\r\n\r\nCould you please confirm if the problem persists?',
 'cannot connect，even by Web browser，please check that  there is some  problems。',
 'I can access https://raw.githubusercontent.com/huggingface/datasets/1.7.0/datasets/glue/glue.py without problem...']
```

Când facem explode `df`, ne așteptăm să obținem un rând pentru fiecare dintre aceste comentarii. Haideți să verificăm dacă ăsta e cazul:

```py
comments_df = df.explode("comments", ignore_index=True)
comments_df.head(4)
```

<table border="1" class="dataframe" style="table-layout: fixed; word-wrap:break-word; width: 100%;">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>html_url</th>
      <th>title</th>
      <th>comments</th>
      <th>body</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>https://github.com/huggingface/datasets/issues/2787</td>
      <td>ConnectionError: Couldn't reach https://raw.githubusercontent.com</td>
      <td>the bug code locate in ：\r\n    if data_args.task_name is not None...</td>
      <td>Hello,\r\nI am trying to run run_glue.py and it gives me this error...</td>
    </tr>
    <tr>
      <th>1</th>
      <td>https://github.com/huggingface/datasets/issues/2787</td>
      <td>ConnectionError: Couldn't reach https://raw.githubusercontent.com</td>
      <td>Hi @jinec,\r\n\r\nFrom time to time we get this kind of `ConnectionError` coming from the github.com website: https://raw.githubusercontent.com...</td>
      <td>Hello,\r\nI am trying to run run_glue.py and it gives me this error...</td>
    </tr>
    <tr>
      <th>2</th>
      <td>https://github.com/huggingface/datasets/issues/2787</td>
      <td>ConnectionError: Couldn't reach https://raw.githubusercontent.com</td>
      <td>cannot connect，even by Web browser，please check that  there is some  problems。</td>
      <td>Hello,\r\nI am trying to run run_glue.py and it gives me this error...</td>
    </tr>
    <tr>
      <th>3</th>
      <td>https://github.com/huggingface/datasets/issues/2787</td>
      <td>ConnectionError: Couldn't reach https://raw.githubusercontent.com</td>
      <td>I can access https://raw.githubusercontent.com/huggingface/datasets/1.7.0/datasets/glue/glue.py without problem...</td>
      <td>Hello,\r\nI am trying to run run_glue.py and it gives me this error...</td>
    </tr>
  </tbody>
</table>

Great, putem vedea că rândurile au fost reproduse, cu coloanele `comments` incluzând și comentariile individuale. Acum că am terminat cu Pandas, noi putem să schimăm rapid înapoi la un `Dataset` înărcând `DataFrame` în memorie:

```py
from datasets import Dataset

comments_dataset = Dataset.from_pandas(comments_df)
comments_dataset
```

```python out
Dataset({
    features: ['html_url', 'title', 'comments', 'body'],
    num_rows: 2842
})
```

Okay, acest lucru ne-a oferit câteva mii de comentarii cu care să lucrăm!


<Tip>

✏️ **Încercați!** Vezi dacă poți utiliza `Dataset.map()` pentru a exploda coloana `comments` din `issues_dataset` _fără_ a recurge la utilizarea Pandas. Acest lucru este puțin dificil; s-ar putea să găsiți utilă secțiunea ["Mapping batch"](https://huggingface.co/docs/datasets/about_map_batch#batch-mapping) din documentația 🤗 Datasets pentru această sarcină.

</Tip>

Acum că avem un singur comentariu pe rând, să creăm o nouă coloană `comments_length` care conține numărul de cuvinte din fiecare comentariu:

```py
comments_dataset = comments_dataset.map(
    lambda x: {"comment_length": len(x["comments"].split())}
)
```

Putem utiliza această nouă coloană pentru a filtra comentariile scurte, care de obicei includ lucruri precum "cc @lewtun" sau "Mulțumesc!" care nu sunt relevante pentru motorul nostru de căutare. Nu există un număr precis care trebuie selectat pentru filtru, dar aproximativ 15 cuvinte pare a fi un bun punct de plecare:

```py
comments_dataset = comments_dataset.filter(lambda x: x["comment_length"] > 15)
comments_dataset
```

```python out
Dataset({
    features: ['html_url', 'title', 'comments', 'body', 'comment_length'],
    num_rows: 2098
})
```

După ce am curățat puțin setul nostru de date, putem să concatenăm titlul, descrirea și comentariile problemei împreună într-o nouă coloană `text`. Ca de obicei, vom scrie o funcție simplă pe care o putem transmite în `Dataset.map()`:

```py
def concatenate_text(examples):
    return {
        "text": examples["title"]
        + " \n "
        + examples["body"]
        + " \n "
        + examples["comments"]
    }


comments_dataset = comments_dataset.map(concatenate_text)
```

Suntem în final pregătiți să creăm niște embeddings! Haideți să vedem cum facem acest lucru.

## Crearea embeddings-urilor de text [[creating-text-embeddings]]

Am văzut în [Capitolul 2](/course/chapter2) că putem obține token embeddings prin utilizarea clasei `AutoModel`. Tot ce trebuie să facem este să alegem un checkpoint potrivit pentru a încărca modelul. Din fericire, există o bibliotecă numită `sentence-transformers` care se ocupă de crearea embeddingurilor. Așa cum se descrie în [documentația](https://www.sbert.net/examples/applications/semantic-search/README.html#symmetric-vs-asymmetric-semantic-search) bibliotecii, cazul nostru este un exemplu de _căutare semantică asimetrică_ deoarece avem o întrebare scurtă al cărei răspuns ne-ar plăcea să îl găsim într-un document mai lung, precum un comentariu la un issue. [Tabelul] (https://www.sbert.net/docs/pretrained_models.html#model-overview) util de prezentare a modelului din documentație indică faptul că checkpointul `multi-qa-mpnet-base-dot-v1` are cea mai bună performanță pentru căutarea semantică, așa că îl vom folosi acesta pentru aplicația noastră. De asemenea, vom încărca tokenizer-ul folosind același checkpoint:

{#if fw === 'pt'}

```py
from transformers import AutoTokenizer, AutoModel

model_ckpt = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
model = AutoModel.from_pretrained(model_ckpt)
```

Pentru a accelera procesul de embedding, este util să punem modelul și inputurile pe un GPU, deci hai să facem asta acum:

```py
import torch

device = torch.device("cuda")
model.to(device)
```

{:else}

```py
from transformers import AutoTokenizer, TFAutoModel

model_ckpt = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
model = TFAutoModel.from_pretrained(model_ckpt, from_pt=True)
```

Notăm că am setat `from_pt=True` ca argument al metodei `from_pretrained()`. Acest lucru se datorează faptului că checkpoint-ul `multi-qa-mpnet-base-dot-v1` are doar PyTorch weights, astfel încât setarea `from_pt=True` le va converti automat în format TensorFlow pentru noi. După cum puteți vedea, este foarte simplu să treci de la un framework la altul în 🤗 Transformers!

{/if}

După cum am menționat anterior, dorim să reprezentăm fiecare intrare din corpusul nostru de GitHub issues sub forma unui vector, astfel încât avem nevoie să "agregăm" sau să facem media la tokem embeddings într-un anumit mod. O abordare populară este de a efectua *CLS pooling* pe outputurile modelului nostru, unde pur și simplu colectăm ultimul stadiu ascuns pentru tokenul special `[CLS]`. Următoarea funcție face acest lucru pentru noi:

```py
def cls_pooling(model_output):
    return model_output.last_hidden_state[:, 0]
```

În continuare, vom crea o funcție ajutătoare care va tokeniza o listă de documente, va plasa tensorii pe GPU, îi va alimenta în model și, în final, va aplica CLS pooling la outputuri:

{#if fw === 'pt'}

```py
def get_embeddings(text_list):
    encoded_input = tokenizer(
        text_list, padding=True, truncation=True, return_tensors="pt"
    )
    encoded_input = {k: v.to(device) for k, v in encoded_input.items()}
    model_output = model(**encoded_input)
    return cls_pooling(model_output)
```

Putem testa funcția oferindui prima intrare de text în corpusul nostru și analizând output shapeul:

```py
embedding = get_embeddings(comments_dataset["text"][0])
embedding.shape
```

```python out
torch.Size([1, 768])
```

Minunat, am transformat prima intrare din corpusul nostru într-un vector de 768 de dimensiuni! Putem utiliza `Dataset.map()` pentru a aplica funcția noastră `get_embeddings()` la fiecare rând din corpusul nostru, astfel încât să creăm o nouă coloană `embeddings` în acest mod:

```py
embeddings_dataset = comments_dataset.map(
    lambda x: {"embeddings": get_embeddings(x["text"]).detach().cpu().numpy()[0]}
)
```

{:else}

```py
def get_embeddings(text_list):
    encoded_input = tokenizer(
        text_list, padding=True, truncation=True, return_tensors="tf"
    )
    encoded_input = {k: v for k, v in encoded_input.items()}
    model_output = model(**encoded_input)
    return cls_pooling(model_output)
```

Putem testa funcția oferindui prima intrare de text în corpusul nostru și analizând output shapeul:

```py
embedding = get_embeddings(comments_dataset["text"][0])
embedding.shape
```

```python out
TensorShape([1, 768])
```

Minunat, am transformat prima intrare din corpusul nostru într-un vector de 768 de dimensiuni! Putem utiliza `Dataset.map()` pentru a aplica funcția noastră `get_embeddings()` la fiecare rând din corpusul nostru, astfel încât să creăm o nouă coloană `embeddings` în acest mod:

```py
embeddings_dataset = comments_dataset.map(
    lambda x: {"embeddings": get_embeddings(x["text"]).numpy()[0]}
)
```

{/if}

Observăm că am transformat embeddingurile în matrice NumPy -- acest lucru este necesar deoarece biblioteca 🤗 Datasets cere acest format atunci când încercăm să indexăm cu FAISS, ceea ce vom face în continuare.

## Utilizarea FAISS pentru căutare de similaritate eficientă[[using-faiss-for-efficient-similarity-search]]

Acum că avem un dataset de embeddings, avem nevoie de o modalitate de a căuta printre ele. Pentru a face acest lucru, vom utiliza o structură de date specială din 🤗 Datasets, numită _FAISS index_. [FAISS](https://faiss.ai/) (prescurtat de la Facebook AI Similarity Search) este o bibliotecă care oferă algoritmi eficienți pentru căutarea rapidă și clusteringul al embedding vectors.

Ideea de bază din spatele FAISS este crearea unei structuri de date speciale numite _index_, care permite găsirea embeddingurilor similare cu un input embedding. Crearea unui index FAISS în 🤗 Datasets este simplu -- utilizăm funcția `Dataset.add_faiss_index()` și specificăm care coloană a datasetului nostru dorim să indexăm:

```py
embeddings_dataset.add_faiss_index(column="embeddings")
```

Acum putem efectua queries pe acest index realizând o căutare a celor mai apropiați vecini cu funcția `Dataset.get_nearest_examples()`. Haideți să testăm acest lucru prin încorporarea unei întrebări astfel:

{#if fw === 'pt'}

```py
question = "How can I load a dataset offline?"
question_embedding = get_embeddings([question]).cpu().detach().numpy()
question_embedding.shape
```

```python out
torch.Size([1, 768])
```

{:else}

```py
question = "How can I load a dataset offline?"
question_embedding = get_embeddings([question]).numpy()
question_embedding.shape
```

```python out
(1, 768)
```

{/if}

La fel ca și în cazul documentelor, acum avem un vector de 768 de dimensiuni care reprezintă query-ul, pe care îl putem compara cu întregul corpus pentru a găsi embeddingurile cele mai similare:

```py
scores, samples = embeddings_dataset.get_nearest_examples(
    "embeddings", question_embedding, k=5
)
```

Funcția `Dataset.get_nearest_examples()` returnează un tuple cu scorurile care clasifică suprapunerea dintre query și document, și un set corespunzător de sampleuri (în acest caz, cele 5 match-uri). Haideți să colectăm acestea într-un `pandas.DataFrame` pentru a le putea sorta cu ușurință:

```py
import pandas as pd

samples_df = pd.DataFrame.from_dict(samples)
samples_df["scores"] = scores
samples_df.sort_values("scores", ascending=False, inplace=True)
```

Acum putem itera peste primele rânduri pentru a veadea cât de bine un query se potrivește cu comentariile disponibile:

```py
for _, row in samples_df.iterrows():
    print(f"COMMENT: {row.comments}")
    print(f"SCORE: {row.scores}")
    print(f"TITLE: {row.title}")
    print(f"URL: {row.html_url}")
    print("=" * 50)
    print()
```

```python out
"""
COMMENT: Requiring online connection is a deal breaker in some cases unfortunately so it'd be great if offline mode is added similar to how `transformers` loads models offline fine.

@mandubian's second bullet point suggests that there's a workaround allowing you to use your offline (custom?) dataset with `datasets`. Could you please elaborate on how that should look like?
SCORE: 25.505046844482422
TITLE: Discussion using datasets in offline mode
URL: https://github.com/huggingface/datasets/issues/824
==================================================

COMMENT: The local dataset builders (csv, text , json and pandas) are now part of the `datasets` package since #1726 :)
You can now use them offline
\`\`\`python
datasets = load_dataset("text", data_files=data_files)
\`\`\`

We'll do a new release soon
SCORE: 24.555509567260742
TITLE: Discussion using datasets in offline mode
URL: https://github.com/huggingface/datasets/issues/824
==================================================

COMMENT: I opened a PR that allows to reload modules that have already been loaded once even if there's no internet.

Let me know if you know other ways that can make the offline mode experience better. I'd be happy to add them :)

I already note the "freeze" modules option, to prevent local modules updates. It would be a cool feature.

----------

> @mandubian's second bullet point suggests that there's a workaround allowing you to use your offline (custom?) dataset with `datasets`. Could you please elaborate on how that should look like?

Indeed `load_dataset` allows to load remote dataset script (squad, glue, etc.) but also you own local ones.
For example if you have a dataset script at `./my_dataset/my_dataset.py` then you can do
\`\`\`python
load_dataset("./my_dataset")
\`\`\`
and the dataset script will generate your dataset once and for all.

----------

About I'm looking into having `csv`, `json`, `text`, `pandas` dataset builders already included in the `datasets` package, so that they are available offline by default, as opposed to the other datasets that require the script to be downloaded.
cf #1724
SCORE: 24.14896583557129
TITLE: Discussion using datasets in offline mode
URL: https://github.com/huggingface/datasets/issues/824
==================================================

COMMENT: > here is my way to load a dataset offline, but it **requires** an online machine
>
> 1. (online machine)
>
> ```
>
> import datasets
>
> data = datasets.load_dataset(...)
>
> data.save_to_disk(/YOUR/DATASET/DIR)
>
> ```
>
> 2. copy the dir from online to the offline machine
>
> 3. (offline machine)
>
> ```
>
> import datasets
>
> data = datasets.load_from_disk(/SAVED/DATA/DIR)
>
> ```
>
>
>
> HTH.


SCORE: 22.893993377685547
TITLE: Discussion using datasets in offline mode
URL: https://github.com/huggingface/datasets/issues/824
==================================================

COMMENT: here is my way to load a dataset offline, but it **requires** an online machine
1. (online machine)
\`\`\`
import datasets
data = datasets.load_dataset(...)
data.save_to_disk(/YOUR/DATASET/DIR)
\`\`\`
2. copy the dir from online to the offline machine
3. (offline machine)
\`\`\`
import datasets
data = datasets.load_from_disk(/SAVED/DATA/DIR)
\`\`\`

HTH.
SCORE: 22.406635284423828
TITLE: Discussion using datasets in offline mode
URL: https://github.com/huggingface/datasets/issues/824
==================================================
"""
```

Nu-i rău! A doua încercare se pare că se potrivește cu query-ul!

<Tip>

✏️ **Încearcă!** Creează propriul tău query și vezi dacp poți găsi un răspuns și să extragi documentele. S-ar putea să trebuiești să crești parametrul `k` în `Dataset.get_nearest_examples()` pentru a mări căutarea.

</Tip>

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter5/6.mdx" />

### Quiz de final de capitol[[end-of-chapter-quiz]]
https://huggingface.co/learn/course/ro/chapter5/8.md

# Quiz de final de capitol[[end-of-chapter-quiz]]

<CourseFloatingBanner
    chapter={5}
    classNames="absolute z-10 right-0 top-0"
/>

Acest capitol a acoperit o mulțime de subiecte! Nu vă faceți griji dacă nu ați înțeles toate detaliile; capitolele următoare vă vor ajuta să înțelegeți cum funcționează lucrurile mai aprofundat.

Înainte de a trece mai departe, totuși trebuie să testăm ce ați învățat în acest capitol.

### 1. Funcția `load_dataset()` din 🤗 Datasets vă permite să încărcați un dataset din care dintre următoarele locații?

<Question
    choices={[
        {
            text: "Local, de exemplu pe laptop",
            explain: "Corect! Puteți trece path-urile fișierelor locale ca argument al `data_files` al `load_dataset()` pentru a încărca dataseturi locale.",
            correct: true
        },
        {
            text: "Hugging Face Hub",
            explain: "Corect! Puteți încărca dataseturi de pe Hub furnizând ID-ul datasetului, de exemplu <code>load_dataset('emotion')</code>.",
            correct: true
        },
        {
            text: "Un server remote",
            explain: "Corect! Puteți trece URL-uri ca argument al `data_files` al `load_dataset()` pentru a încărca fișiere remote.",
            correct: true
        },
    ]}
/>

### 2. Presupunem că încărcați una dintre sarcinile GLUE astfel:

```py
from datasets import load_dataset

dataset = load_dataset("glue", "mrpc", split="train")
```

Care dintre următoarele comenzi va produce un exemplu aleatoriu de 50 de elemente din `dataset`?

<Question
    choices={[
        {
            text: "<code>dataset.sample(50)</code>",
            explain: "Acest lucru este incorect -- nu există o metodă `Dataset.sample()`."
        },
        {
            text: "<code>dataset.shuffle().select(range(50))</code>",
            explain: "Corect! Așa cum ați văzut în acest capitol, mai întâi faceți shuffle datasetului și apoi selectați exemplele din el.",
            correct: true
        },
        {
            text: "<code>dataset.select(range(50)).shuffle()</code>",
            explain: "Acest lucru este incorect -- deși codul va rula, va amesteca doar primele 50 de elemente din setul de date."
        }
    ]}
/>

### 3. Presupunem că aveți un set de date despre animale de companie numit `pets_dataset`, care are o coloană `name` care denotă numele fiecărui animal de companie. Care dintre următoarele abordări v-ar permite să filtrați setul de date pentru toate animalele de companie ale căror nume încep cu litera "L"?

<Question
    choices={[
        {
            text: "<code>pets_dataset.filter(lambda x : x['name'].startswith('L'))</code>",
            explain: "Corect! Utilizarea unei funcții lambda python pentru aceste filtre rapide este o idee grozavă. Vă puteți gândi și la o altă soluție?",
            correct: true
        },
        {
            text: "<code>pets_dataset.filter(lambda x['name'].startswith('L'))</code>",
            explain: "Acest lucru este incorect -- o funcție lambda are forma generală <code>lambda *arguments* : *expression*</code>, deci trebuie să furnizați argumente în acest caz."
        },
        {
            text: "Creați o funcție ca <code>def filter_names(x): return x['name'].startswith('L')</code> și rulați <code>pets_dataset.filter(filter_names)</code>.",
            explain: "Corect! La fel ca și cu <code>Dataset.map()</code>, puteți trece funcții explicite la <code>Dataset.filter()</code>. Acest lucru este util atunci când aveți o logică complexă care nu este potrivită pentru o funcție lambda. Care dintre celelalte soluții ar mai funcționa?",
            correct: true
        }
    ]}
/>

### 4. Ce este memory mapping?

<Question
    choices={[
        {
            text: "Mappingul între CPU și GPU RAM",
            explain: "Acest lucru nu este corect -- încearcă din nou!",
        },
        {
            text: "Mapping între RAM și stocarea sistemului de fișiere",
            explain: "Corect! 🤗 Datasets tratează fiecare dataset ca pe un fișier memory-mapped. Acest lucru permite bibliotecii să acceseze și să opereze pe elemente ale datasetului fără să fie nevoie să îl încarce complet în memorie.",
            correct: true
        },
        {
            text: "Mapping între două fișiere din cache-ul 🤗 Datasets",
            explain: "Acest lucru nu este corect -- încearcă din nou!"
        }
    ]}
/>

### 5. Care sunt principalele beneficii ale memory-mapping?

<Question
    choices={[
        {
            text: "Accesarea fișierelor memory-mapped este mai rapid decât citirea de pe disc sau scrierea pe disc.",
            explain: "Corect! Acest lucru permite 🤗 Datasets să fie extrem de rapid. Acesta însă nu este singurul beneficiu.",
            correct: true
        },
        {
            text: "Aplicațiile pot accesa segmente de date dintr-un fișier extrem de mare fără a fi necesar să citească întregul fișier în RAM mai întâi.",
            explain: "Corect! Acest lucru permite 🤗 Datasets să încarce dataseturi de câțiva gigabytes pe laptopul tău fără a fi nevoie să supraîncărcați CPU-ul. Ce alt avantaj oferă memory-mapping?",
            correct: true
        },
        {
            text: "Acesta consumă mai puțină energie, astfel încât bateria ta ține mai mult.",
            explain: "Acest lucru nu este corect -- încearcă din nou!"
        }
    ]}
/>

### 6. De ce codul următor eșuează?

```py
from datasets import load_dataset

dataset = load_dataset("allocine", streaming=True, split="train")
dataset[0]
```

<Question
    choices={[
        {
            text: "Încearcă să facă streaming unui dataset prea mare pentru a încăpea în RAM.",
            explain: "Acest lucru nu este corect -- streaming datasets sunt decomprimate pe loc, și puteți prelucra dataseturi de terabytes cu foarte puțin RAM!",
        },
        {
            text: "Încearcă să acceseze un <code>IterableDataset</code>.",
            explain: "Corect! Un <code>IterableDataset</code> este un generator, nu un container, deci ar trebui să accesați elementele sale utilizând <code>next(iter(dataset))</code>.",
            correct: true
        },
        {
            text: "Datasetul <code>allocine</code> nu are o un split <code>train</code>.",
            explain: "Acest lucru este incorect -- consultați cardul datasetului <code>allocine</code> de pe Hub pentru a vedea ce splituri conține."
        }
    ]}
/>

### 7. Care sunt principalele beneficii ale creării unui dataset card?

<Question
    choices={[
        {
            text: "Oferă informații despre utilizarea intenționată și sarcinile susținute ale datasetului, astfel încât ceilalți din comunitate să poată lua o decizie cu privire la utilizarea acestuia.",
            explain: "Corect! Dataseturile nedocumentate pot fi utilizate pentru a antrena modele care pot să nu reflecte intențiile creatorilor sau pot produce modele a căror statut juridic este incert dacă sunt antrenate pe date care încalcă confidențialitatea sau restricțiile de licențiere. Acesta nu este singurul beneficiu, totuși!",
            correct: true
        },
        {
            text: "Ajută la sublinierea biasurilor prezente într-un corpus.",
            explain: "Corect! Aproape toate dataseturile au o formă de bias, ceea ce poate produce consecințe negative. A fi conștient de ele ajută creatorii de modele să înțeleagă cum să abordeze biasurile indispensabile. Cu ce altceva vă pot ajuta dataset cardurile?",
            correct: true
        },
        {
            text: "Îmbunătățește șansele ca ceilalți din comunitate să utilizeze datasetul meu.",
            explain: "Corect! Un dataset card bine scris va avea tendința de a duce la o utilizare mai mare a prețiosului tău dataset. Ce beneficiu mai oferă?",
            correct: true
        },
    ]}
/>

### 8. Ce este căutarea semantică?

<Question
    choices={[
        {
            text: "Un mod de a căuta pentru a găsi potriviri exacte între cuvintele dintr-un query și documentele dintr-un corpus",
            explain: "Acest lucru este incorect -- acest tip de căutare se numește *căutare lexicală*, și este ceea ce vedeți de obicei cu motoarele de căutare tradiționale."
        },
        {
            text: "Un mod de a căuta documente care se potrivesc prin înțelegerea sensului contextual al unui query",
            explain: "Corect! Căutarea semantică utilizează embedding vectors pentru a reprezenta queries și documente și utilizează o metrică de similaritate pentru a măsura cantitatea de suprapunere între ele. Cum altfel ați putea descrie-o?",
            correct: true
        },
        {
            text: "Un mod de a îmbunătăți acuratețea căutării",
            explain: "Corect! Motoarele de căutare semantice pot captura intenția unui query mult mai bine decât potrivirea de cuvinte cheie și de obicei preia documente cu o precizie mai mare. Dar acesta nu este singurul răspuns corect -- ce alt beneficiu oferă căutarea semantică?",
            correct: true
        }
    ]}
/>

### 9. Pentru căutarea semantică asimetrică, de obicei aveți:

<Question
    choices={[
        {
            text: "Un query scurt și un paragraf mai lung care răspunde la query",
            explain: "Corect!",
            correct: true
        },
        {
            text: "Queries și paragrafe care sunt de aproximativ aceeași lungime",
            explain: "Acest lucru este de fapt un exemplu de căutare semantică simetrică -- încercați din nou!"
        },
        {
            text: "Un query lung și un paragraf mai scurt care răspunde la query",
            explain: "Acest lucru este incorect -- încercați din nou!"
        }
    ]}
/>

### 10. Pot utiliza 🤗 Datasets pentru a încărca date pentru utilizare în alte domenii, cum ar fi speech processing?

<Question
    choices={[
        {
            text: "Nu",
            explain: "Acest lucru este incorect -- 🤗 Datasets acceptă în prezent date tabulare, audio și imagini. Consultați datasetul <a href='https://huggingface.co/datasets/mnist'>MNIST</a> de pe Hub pentru a vedea un exemplu de computer vision."
        },
        {
            text: "Da",
            explain: "Corect! Consultați dezvoltările interesante cu privire la speech și vision în biblioteca 🤗 Transformers pentru a vedea cum 🤗 Datasets este utilizat în aceste domenii.",
            correct: true
        },
    ]}
/>

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter5/8.mdx" />

### Big data? 🤗 Datasets vine în ajutor![[big-data-datasets-to-the-rescue]]
https://huggingface.co/learn/course/ro/chapter5/4.md

# Big data? 🤗 Datasets vine în ajutor![[big-data-datasets-to-the-rescue]]

<CourseFloatingBanner chapter={5}
  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/chapter5/section4.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter5/section4.ipynb"},
]} />

În prezent, nu este de neașteptat să te confrunți cu dataseturi de câțiva gigabytes, mai ales dacă planifici să preantreenezi un transformer ca BERT sau GPT-2 de la zero. În aceste cazuri, chiar _loading_ a datelor poate fi o provocare. De exemplu, corpusul WebText folosit pentru a preantreena GPT-2 conține peste 8 milioane de documente și 40 GB de text – încărcarea acestuia în memoria RAM a laptopului tău este probabil să-i facă un atac cardiac!

Norocul este că Datasets 🤗 a fost proiectat pentru a depăși aceste limitări. El te eliberează de problemele de gestionare a memoriei, tratarea dataseturilor ca fișiere _memory-mapped_, și limitele hard driveului prin _streamingul_ intrărilor dintr-un corpus.

<Youtube id="JwISwTCPPWo"/>

În această secțiune vom explora aceste caracteristici ale 🤗Datasets cu un corpus de 825 GB numit [Pile](https://pile.eleuther.ai). Să începem!

## Ce este Pile?[[what-is-the-pile]]

Pile este un corpus de text englezesc creat de [EleutherAI](https://www.eleuther.ai) pentru antrenarea large-scale language models. Acesta include o varietate diversă de dataseturi, cuprinzând articole științifice, repositoriuri GitHub și texte web filtrate. Corpusul de antrenare este disponibil în chunkuri de [14 GB](https://the-eye.eu/public/AI/pile/), dar în același timp puteți descărca și câteva dintre [componenetele 
individuale](https://the-eye.eu/public/AI/pile_preliminary_components/). Să începem prin examinarea datasetului PubMed Abstracts, care este un corpus de rezumate din 15 milioane de publicații științifice biomedicale de pe [PubMed](https://pubmed.ncbi.nlm.nih.gov/). Datasetul este în format JSON și este comprimat cu librăria `zstandard`, așadar prima dată ne trebuie să o instalăm pe aceasta:

```py
!pip install zstandard
```

În continuare, putem încărca datasetul utilizând metoda pentru fișierele remote pe care am învățat-o în [secțiunea 2](/course/chapter5/2):

```py
from datasets import load_dataset

# Acest lucru durează câteva minute, așadar poți să te duci să îți iei un ceai sau o cafea între timp :))
data_files = "https://the-eye.eu/public/AI/pile_preliminary_components/PUBMED_title_abstracts_2019_baseline.jsonl.zst"
pubmed_dataset = load_dataset("json", data_files=data_files, split="train")
pubmed_dataset
```

```python out
Dataset({
    features: ['meta', 'text'],
    num_rows: 15518009
})
```

Putem observa că există 15.518.009 de linii și două coloane în datasetul nostru – e foarte mult!

<Tip>

✎ De abia acum, 🤗 Datasets va descompresa fișierele necesare pentru încărcarea datasetului. Dacă doriți să salvați spațiu pe hard drive-ul dvs. , puteți transmite `DownloadConfig(delete_extracted=True)` la argumentul `download_config` al `load_dataset()`. Vedeți mai multe detalii în [documentație](https://huggingface.co/docs/datasets/package_reference/builder_classes#datasets.DownloadConfig).

</Tip>

Acum hai să analizăm conținutul primei linii:

```py
pubmed_dataset[0]
```

```python out
{'meta': {'pmid': 11409574, 'language': 'eng'},
 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection.\nTo determine the prevalence of hypoxaemia in children aged under 5 years suffering acute lower respiratory infections (ALRI), the risk factors for hypoxaemia in children under 5 years of age with ALRI, and the association of hypoxaemia with an increased risk of dying in children of the same age ...'}
```

Okay, acesta pare a fi un rezumat dintr-un articol medical. Să vedem cât de mult spațiu RAM am folosit pentru încărcarea datasetului!

## Magia memory mappingului[[the-magic-of-memory-mapping]]

Una dintre modalitățile simple de măsurare a utilizării memoriei în Python este cu biblioteca `psutil`, care poate fi instalată cu `pip`:

```python
!pip install psutil
```

Biblioteca oferă o clasă `Process` ce ne permite să verificăm utilizarea memoriei procesului curent astfel:

```py
import psutil

# Process.memory_info este exprimat în bytes, așadar convertim la megabyte
print(f"Utilizare RAM: {psutil.Process().memory_info().rss / (1024 * 1024):.2f} MB")
```

```python out
Utilizarea RAM: 5678.33 MB
```

Aici `rss` se referă la _resident set size_ , care este partea memoriei ocupată de proces în RAM. Această măsurare include și memoria folosită de Python interpreter și librariile pe care le-am încărcat, așadar cantitatea reală de memorie utilizată pentru încărcarea datelor este puțin mai mică. Pentru comparație, putem să vedem cât de mare este datasetul pe disc folosind atributul `dataset_size`. Deoarece rezultatul este exprimat în bytes, putem să îl convertim manual la gigabyte:

```py
print(f"Number of files in dataset : {pubmed_dataset.dataset_size}")
size_gb = pubmed_dataset.dataset_size / (1024**3)
print(f"Dataset size (cache file) : {size_gb:.2f} GB")
```

```python out
Number of files in dataset : 20979437051
Dataset size (cache file) : 19.54 GB
```


Nice – deși este aproximativ 20 GB, putem încărca și accesa datasetul cu mult mai puțin RAM!

<Tip>

✏️ **Încercați!** Alegeți una dintre [subseturile](https://the-eye.eu/public/AI/pile_preliminary_components/) din Pile care este mai mare decât memoria RAM a laptopului sau dispozitivului tău, încărcați-o cu 🤗 Datasets și măsurați cantitatea de memorie folosită. Pentru o măsurare precisă, veți dori să faceți acest lucru într-un proces nou. Puteți găsi dimensiunile decomprimate ale fiecărui subset în Tabelul 1 din [Pile paper](https://arxiv.org/abs/2101.00027). 

</Tip>

Dacă sunteți familiarizați cu Pandas, rezultatul acesta poate veni ca o surpriză din cauza celebrei [rule of thumbă](https://wesmckinney.com/blog/apache-arrow-pandas-internals/) al lui Wes Kinney, care spune că în mod normal aveți nevoie de 5 până la 10 ori mai mult spațiu pe RAM decât mărimea datasetului. Deci 🤗 Datasets această problemă de memory management? 🤗 Datasets tratează fiecare dataset ca un [memory-mapped file](https://en.wikipedia.org/wiki/Memory-mapped_file), care oferă un mapping între spațiul RAM și stocarea pe sistem, ceea ce permite bibliotecii să acceseze și să opereze asupra elementelor datasetului fără a trebui să-l încarce în totalitate în memorie.

Memory-mapped files pot fi și distribuite între mai multe procese, ceea ce permite metodelor cum ar fi `Dataset.map()` să fie parallelized fără necesitatea mutării sau copierii datasetului. În spatele acestor facilități se află [formatul de memorie Apache Arrow](https://arrow.apache.org) și biblioteca [`pyarrow`](https://arrow.apache.org/docs/python/index.html), care realizează încărcarea datelor și procesarea la viteze fulgerătoare. (Pentru mai multe detalii despre Apache Arrow și compararea sa cu Pandas, vă rugăm să citiți [blogul lui Dejan Simic](https://towardsdatascience.com/apache-arrow-read-dataframe-with-zero-memory-69634092b1a).) Pentru a vedea acest lucru în acțiune, hai să încercăm un speed test prin iterarea asupra tuturor elementelor din datasetul PubMed Abstracts:

```py
import timeit

code_snippet = """batch_size = 1000

for idx in range(0, len(pubmed_dataset), batch_size):
    _ = pubmed_dataset[idx:idx + batch_size]
"""

time = timeit.timeit(stmt=code_snippet, number=1, globals=globals())
print(
    f"Iterated over {len(pubmed_dataset)} examples (about {size_gb:.1f} GB) in "
    f"{time:.1f}s, i.e. {size_gb/time:.3f} GB/s"
)
```

```python out
'Iterated over 15518009 examples (about 19.5 GB) in 64.2s, i.e. 0.304 GB/s'
```

Aici am folosit modulul `timeit` al Python pentru a măsura timpul de execuție necesar pentru a rula `code_snippet`. În mod normal veți putea trece peste un dataset la viteze de câteva sute de MB/s până la câțiva GB/s. Acest lucru funcționează bine pentru majoritatea aplicațiilor, dar uneori veți avea nevoie să lucrați cu un dataset care este prea mare ca să încapă pe hard driveul laptopului tău. De exemplu, dacă am încerca să descarcăm Pile în întregime, am avea nevoie de 825 GB de spațiu liber! Pentru a vă ajuta cu astfel de cazuri, 🤗 Datasets oferă o feature de streaming care permite accesarea și descărcarea elementelor, fără a trebui să descărcați întregul dataset. Hai să vedem cum funcționează!

<Tip>

💡În Jupyter notebooks poți să măsori timpul unei celule utilizând [funcția magică `%%timeit`](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-timeit).

</Tip>

## Streamingul dataseturilor[[streaming-datasets]]

Pentru a activa dataset streaming este suficient să dați argumentul `streaming=True` funcției `load_dataset()`. De exemplu, să încercăm să încărcăm datasetul PubMed Abstracts în streaming mode:

```py
pubmed_dataset_streamed = load_dataset(
    "json", data_files=data_files, split="train", streaming=True
)
```

În locul `Dataset`-ului obișnuit pe care l-am întâlnit până acum în acest capitol, obiectul returnat cu `streaming=True` este un `IterableDataset`. După nume putem deduce că pentru a accesa elementele dintr-un `IterableDataset`, trebuie să iterăm prin el. Prin urmare, putem accesa primul element al streamed datased astfel:

```py
next(iter(pubmed_dataset_streamed))
```

```python out
{'meta': {'pmid': 11409574, 'language': 'eng'},
 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection.\nTo determine the prevalence of hypoxaemia in children aged under 5 years suffering acute lower respiratory infections (ALRI), the risk factors for hypoxaemia in children under 5 years of age with ALRI, and the association of hypoxaemia with an increased risk of dying in children of the same age ...'}
```

Elementele unui streamed dataset pot fi procesate din mers folosind `IterableDataset.map()`, ceea ce este util în timpul antrenării dacă aveți nevoie să tokenizeți inputurile. Procesarea se face exact la fel ca și în [Capitolul 3](/course/chapter3), cu singura deosebire fiind că rezultatele sunt returnate una câte una:

```py
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")
tokenized_dataset = pubmed_dataset_streamed.map(lambda x: tokenizer(x["text"]))
next(iter(tokenized_dataset))
```

```python out
{'input_ids': [101, 4958, 5178, 4328, 6779, ...], 'attention_mask': [1, 1, 1, 1, 1, ...]}
```

<Tip>

💡 Pentru a accelera tokenizarea cu streaming puteți seta `batched=True`, ca și în secțiunea precedentă. Acest lucru va procesa exemplele, batch cu batch; dimensiunea implicită a batchului este de 1,000 și poate fi specificată cu argumentul `batch_size`.

</Tip>

De asemenea, puteți amesteca un streamed dataset utilizând `IterableDataset.shuffle()`, dar față de `Dataset.shuffle()` acest lucru va amesteca doar elementele dintr-un `buffer_size` predefinit:

```py
shuffled_dataset = pubmed_dataset_streamed.shuffle(buffer_size=10_000, seed=42)
next(iter(shuffled_dataset))
```

```python out
{'meta': {'pmid': 11410799, 'language': 'eng'},
 'text': 'Randomized study of dose or schedule modification of granulocyte colony-stimulating factor in platinum-based chemotherapy for elderly patients with lung cancer ...'}
```

În acest exemplu, am selectat un exemplu aleatoriu din primele 10,000 exemple din buffer. Odată ce un exemplu este accesat, locul lui în buffer este completat cu următorul exemplu din corpus (adica exemplarul 10,001 în cazul de mai sus). Puteți selecta elemente dintr-un streamed dataset utilizând funcțiile `IterableDataset.take()` și `IterableDataset.skip()`, care acționează în mod similar cu `Dataset.select()`. De exemplu, pentru a selecta primele 5 exemple din PubMed Abstracts dataset putem face următorul lucru:

```py
dataset_head = pubmed_dataset_streamed.take(5)
list(dataset_head)
```

```python out
[{'meta': {'pmid': 11409574, 'language': 'eng'},
  'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection ...'},
 {'meta': {'pmid': 11409575, 'language': 'eng'},
  'text': 'Clinical signs of hypoxaemia in children with acute lower respiratory infection: indicators of oxygen therapy ...'},
 {'meta': {'pmid': 11409576, 'language': 'eng'},
  'text': "Hypoxaemia in children with severe pneumonia in Papua New Guinea ..."},
 {'meta': {'pmid': 11409577, 'language': 'eng'},
  'text': 'Oxygen concentrators and cylinders ...'},
 {'meta': {'pmid': 11409578, 'language': 'eng'},
  'text': 'Oxygen supply in rural africa: a personal experience ...'}]
```

În mod similar puteți utiliza funcția `IterableDataset.skip()` pentru a crea splituri de antrenare și validare dintr-un set de date amestecat astfel:

```py
# Săriți primele 1,000 exemple și includeți restul în setul de antrenare
train_dataset = shuffled_dataset.skip(1000)
# Luați primele 1,000 de exemple pentru setul de validare
validation_dataset = shuffled_dataset.take(1000)
```

Hai să terminăm explorarea streamingului asupra datasetului cu o aplicație comună: combinarea multiplelor dataseturi împreună pentru a crea un singur corpus. 🤗 Datasets oferă o funcție `interleave_datasets()` care convertește o listă de obiecte `IterableDataset` într-un singur `IterableDataset`, unde elementele noului datasetsunt obținute prin alternarea între exemplele sursă. Această funcție este utilă, în special atunci când încercați să combinați dataseturi mari, ca exemplu vom face stream al subsetul FreeLaw din Pile, care reprezintă un dataset de 51 GB de opinii juridice din instanțe din SUA:

```py
law_dataset_streamed = load_dataset(
    "json",
    data_files="https://the-eye.eu/public/AI/pile_preliminary_components/FreeLaw_Opinions.jsonl.zst",
    split="train",
    streaming=True,
)
next(iter(law_dataset_streamed))
```

```python out
{'meta': {'case_ID': '110921.json',
  'case_jurisdiction': 'scotus.tar.gz',
  'date_created': '2010-04-28T17:12:49Z'},
 'text': '\n461 U.S. 238 (1983)\nOLIM ET AL.\nv.\nWAKINEKONA\nNo. 81-1581.\nSupreme Court of United States.\nArgued January 19, 1983.\nDecided April 26, 1983.\nCERTIORARI TO THE UNITED STATES COURT OF APPEALS FOR THE NINTH CIRCUIT\n*239 Michael A. Lilly, First Deputy Attorney General of Hawaii, argued the cause for petitioners. With him on the brief was James H. Dannenberg, Deputy Attorney General...'}
```

Această dataset este suficient de mare încât să pună presiune asupra RAM-ului a majorității laptopurilor, dar am reușit să îl încarcăm și să îl accesăm fără să ne facem griji. Acum hai să combinăm exemplele din dataseturile FreeLaw și PubMed Abstracts cu funcția `interleave_datasets()`:

```py
from itertools import islice
from datasets import interleave_datasets

combined_dataset = interleave_datasets([pubmed_dataset_streamed, law_dataset_streamed])
list(islice(combined_dataset, 2))
```

```python out
[{'meta': {'pmid': 11409574, 'language': 'eng'},
  'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection ...'},
 {'meta': {'case_ID': '110921.json',
   'case_jurisdiction': 'scotus.tar.gz',
   'date_created': '2010-04-28T17:12:49Z'},
  'text': '\n461 U.S. 238 (1983)\nOLIM ET AL.\nv.\nWAKINEKONA\nNo. 81-1581.\nSupreme Court of United States.\nArgued January 19, 1983.\nDecided April 26, 1983.\nCERTIORARI TO THE UNITED STATES COURT OF APPEALS FOR THE NINTH CIRCUIT\n*239 Michael A. Lilly, First Deputy Attorney General of Hawaii, argued the cause for petitioners. With him on the brief was James H. Dannenberg, Deputy Attorney General...'}]
```

Aici am folosit funcția `islice()` din modulul Python `itertools` pentru a selecta primele două exemple din datasetul combinat, și putem vedea că acestea corespund primelor exemple din fiecare dintre cele două dataseturi originale.

În final, dacă doriți să faceți streaming la Pile în întregime (825 GB), puteți rula toate fișierele pregătite după în acest mod:

```py
base_url = "https://the-eye.eu/public/AI/pile/"
data_files = {
    "train": [base_url + "train/" + f"{idx:02d}.jsonl.zst" for idx in range(30)],
    "validation": base_url + "val.jsonl.zst",
    "test": base_url + "test.jsonl.zst",
}
pile_dataset = load_dataset("json", data_files=data_files, streaming=True)
next(iter(pile_dataset["train"]))
```

```python out
{'meta': {'pile_set_name': 'Pile-CC'},
 'text': 'It is done, and submitted. You can play “Survival of the Tastiest” on Android, and on the web...'}
```

<Tip>

✏️ **Încercați!** Utilizați unul dintre cele mari corpusuri Common Crawl ca [`mc4`](https://huggingface.co/datasets/mc4) sau [`oscar`](https://huggingface.co/datasets/oscar) pentru a crea un streaming dataset multilingv care reprezintă proporția limbii vorbite într-o țară aleasă de tine. De exemplu, cele patru limbi naționale din Elveția sunt germana, franceza, italiana și romansha, așadar puteți încerca să creați un corpus elvețian prin samplingul subseturilor Oscar în funcție de proporția lor vorbită.

</Tip>

Acum aveți toate instrumentele necesare pentru a încărca și procesa dataseturi de orice formă și dimensiune – dar, din păcate, va veni un moment în care veți trebui să creați voi înșivă un dataset pentru a rezolva problema pe care o aveți. Acesta este subiectul următoarei secțiuni!


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter5/4.mdx" />

### 🤗 Datasets, verificare![[datasets-check]]
https://huggingface.co/learn/course/ro/chapter5/7.md

# 🤗 Datasets, verificare![[datasets-check]]

<CourseFloatingBanner
    chapter={5}
    classNames="absolute z-10 right-0 top-0"
/>

Ei bine, a fost un tur palpitant prin biblioteca 🤗 Datasets -- felicitări pentru că ai ajuns până aici! Cu cunoștințele pe care le-ai dobândit din acest capitol, ar trebui să fii capabil să:

- Încarci dataseturi de oriunde, fie Hugging Face Hub, laptopul tău sau un server remote de la compania ta.
- Modelezi datele tale folosind o combinație a funcțiilor `Dataset.map()` și `Dataset.filter()`.
- Schimbi rapid între data formats precum Pandas și NumPy folosind `Dataset.set_format()`.
- Creezi propriul tău dataset și să îl publici pe Hugging Face Hub.
- Încorporezi documentele tale folosind un model Transformer și construiești un motor de căutare semantică folosind FAISS.

În [Capitolul 7](/course/chapter7), vom pune toate acestea în practică, făcând o examinare amănunțită a principalelor sarcini NLP pentru care modelele Transformer sunt excelente. Înainte de a trece mai departe, puneți-vă cunoștințele despre 🤗 Datasets la încercare cu un quiz rapid!

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter5/7.mdx" />

### Ce fac dacă dataset-ul meu nu este pe Hub?[[what-if-my-dataset-isnt-on-the-hub]]
https://huggingface.co/learn/course/ro/chapter5/2.md

# Ce fac dacă dataset-ul meu nu este pe Hub?[[what-if-my-dataset-isnt-on-the-hub]]

<CourseFloatingBanner chapter={5}
  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/chapter5/section2.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter5/section2.ipynb"},
]} />

Știți cum să folosiți [Hugging Face Hub](https://huggingface.co/datasets) pentru a descărca dataseturi, dar vă veți găsi adesea lucrând cu date care sunt stocate fie pe laptopul dumneavoastră, fie pe un server remote. În această secțiune vă vom arăta cum poate fi utilizat 🤗 Datasets pentru a încărca dataseturi care nu sunt disponibile pe Hugging Face Hub.

<Youtube id="HyQgpJTkRdE"/>

## Lucrând cu dataseturi locale și remote[[working-with-local-and-remote-datasets]]

🤗 Datasets oferă scripturi de încărcare pentru a gestiona încărcarea dataseturilor locale și remote. Suportă mai multe formate de date comune, cum ar fi:

|    Data format     | Loading script |                         Example                         |
| :----------------: | :------------: | :-----------------------------------------------------: |
|     CSV & TSV      |     `csv`      |     `load_dataset("csv", data_files="my_file.csv")`     |
|     Text files     |     `text`     |    `load_dataset("text", data_files="my_file.txt")`     |
| JSON & JSON Lines  |     `json`     |   `load_dataset("json", data_files="my_file.jsonl")`    |
| Pickled DataFrames |    `pandas`    | `load_dataset("pandas", data_files="my_dataframe.pkl")` |

Așa cum se arată în tabel, pentru fiecare format de date trebuie doar să specificăm tipul scriptului de încărcare în funcția `load_dataset()`, alături de un argument `data_files` care specifică calea către unul sau mai multe fișiere. Să începem prin încărcarea unui dataset din fișiere locale; mai târziu vom vedea cum să facem același lucru cu fișiere remote.

## Încărcarea unui dataset local[[loading-a-local-dataset]]

Pentru acest exemplu vom folosi [datasetul SQuAD-it](https://github.com/crux82/squad-it/), care este un dataset la scară largă pentru întrebări și răspunsuri în italiană.

Seturile de antrenare și test sunt găzduite pe GitHub, deci le putem descărca cu o comandă simplă `wget`:

```python
!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-train.json.gz
!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-test.json.gz
```

Aceasta va descărca două fișiere comprimate numite *SQuAD_it-train.json.gz* și *SQuAD_it-test.json.gz*, pe care le putem decomprima cu comanda Linux `gzip`:

```python
!gzip -dkv SQuAD_it-*.json.gz
```

```bash
SQuAD_it-test.json.gz:	   87.4% -- replaced with SQuAD_it-test.json
SQuAD_it-train.json.gz:	   82.2% -- replaced with SQuAD_it-train.json
```

Putem vedea că fișierele comprimate au fost înlocuite cu _SQuAD_it-train.json_ și _SQuAD_it-test.json_, și că datele sunt stocate în formatul JSON.

<Tip>

✎ Dacă vă întrebați de ce există un caracter `!` în comenzile shell de mai sus, aceasta este pentru că le rulăm într-un notebook Jupyter. Pur și simplu eliminați prefixul dacă doriți să descărcați și să dezarhivați datasetul într-un terminal.

</Tip>

Pentru a încărca un fișier JSON cu funcția `load_dataset()`, trebuie doar să știm dacă avem de-a face cu JSON obișnuit (similar cu un dicționar imbricat) sau JSON Lines (JSON separat pe linii). Ca multe dataseturi de întrebări și răspunsuri, SQuAD-it folosește formatul imbricat, cu tot textul stocat într-un câmp `data`. Aceasta înseamnă că putem încărca datasetul specificând argumentul `field` după cum urmează:

```py
from datasets import load_dataset

squad_it_dataset = load_dataset("json", data_files="SQuAD_it-train.json", field="data")
```

În mod implicit, încărcarea fișierelor locale creează un obiect `DatasetDict` cu un split `train`. Putem vedea acest lucru prin inspectarea obiectului `squad_it_dataset`:

```py
squad_it_dataset
```

```python out
DatasetDict({
    train: Dataset({
        features: ['title', 'paragraphs'],
        num_rows: 442
    })
})
```

Aceasta ne arată numărul de rânduri și numele coloanelor asociate cu setul de antrenare. Putem vizualiza unul dintre exemple prin indexarea în splitul `train` după cum urmează:

```py
squad_it_dataset["train"][0]
```

```python out
{
    "title": "Terremoto del Sichuan del 2008",
    "paragraphs": [
        {
            "context": "Il terremoto del Sichuan del 2008 o il terremoto...",
            "qas": [
                {
                    "answers": [{"answer_start": 29, "text": "2008"}],
                    "id": "56cdca7862d2951400fa6826",
                    "question": "In quale anno si è verificato il terremoto nel Sichuan?",
                },
                ...
            ],
        },
        ...
    ],
}
```

Excelent, am încărcat primul nostru dataset local! Dar în timp ce acest lucru a funcționat pentru setul de antrenare, ceea ce dorim cu adevărat este să includem atât spliturile `train` cât și `test` într-un singur obiect `DatasetDict` astfel încât să putem aplica funcțiile `Dataset.map()` pe ambele splituri deodată. Pentru a face acest lucru, putem furniza un dicționar argumentului `data_files` care mapează fiecare nume de split la un fișier asociat cu acel split:

```py
data_files = {"train": "SQuAD_it-train.json", "test": "SQuAD_it-test.json"}
squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
squad_it_dataset
```

```python out
DatasetDict({
    train: Dataset({
        features: ['title', 'paragraphs'],
        num_rows: 442
    })
    test: Dataset({
        features: ['title', 'paragraphs'],
        num_rows: 48
    })
})
```

Acesta este exact ceea ce am dorit. Acum, putem aplica diverse tehnici de preprocesare pentru a curăța datele, tokeniza recenziile și așa mai departe.

<Tip>

Argumentul `data_files` al funcției `load_dataset()` este destul de flexibil și poate fi fie o singură cale de fișier, o listă de căi de fișiere, sau un dicționar care mapează numele spliturilor la căile fișierelor. De asemenea, puteți folosi glob pentru fișiere care se potrivesc unui model specificat conform regulilor folosite de shell-ul Unix (de exemplu, puteți face glob pentru toate fișierele JSON dintr-un director ca un singur split prin setarea `data_files="*.json"`). Consultați [documentația](https://huggingface.co/docs/datasets/loading#local-and-remote-files) 🤗 Datasets pentru mai multe detalii.

</Tip>

Scripturile de încărcare din 🤗 Datasets suportă de fapt decomprimarea automată a fișierelor de intrare, deci am fi putut să sărim peste folosirea `gzip` prin indicarea argumentului `data_files` direct către fișierele comprimate:

```py
data_files = {"train": "SQuAD_it-train.json.gz", "test": "SQuAD_it-test.json.gz"}
squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
```

Acest lucru poate fi util dacă nu doriți să decomprimați manual multe fișiere GZIP. Decomprimarea automată se aplică și altor formate comune precum ZIP și TAR, deci trebuie doar să pointați `data_files` către fișierele comprimate și sunteți gata!

Acum că știți cum să încărcați fișiere locale pe laptopul sau desktop-ul dumneavoastră, să aruncăm o privire la încărcarea fișierelor remote.

## Încărcarea unui dataset remote[[loading-a-remote-dataset]]

Dacă lucrați ca data scientist sau programator într-o companie, există o șansă bună ca dataseturile pe care doriți să le analizați să fie stocate pe un server remote. Din fericire, încărcarea fișierelor remote este la fel de simplă ca încărcarea celor locale! În loc să furnizați o cale către fișiere locale, pointați argumentul `data_files` al `load_dataset()` către unul sau mai multe URL-uri unde sunt stocate fișierele remote. De exemplu, pentru datasetul SQuAD-it găzduit pe GitHub, putem pur și simplu să pointăm `data_files` către URL-urile _SQuAD_it-*.json.gz_ după cum urmează:

```py
url = "https://github.com/crux82/squad-it/raw/master/"
data_files = {
    "train": url + "SQuAD_it-train.json.gz",
    "test": url + "SQuAD_it-test.json.gz",
}
squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
```

Aceasta returnează același obiect `DatasetDict` obținut mai sus, dar ne economisește pasul de a descărca și decomprima manual fișierele _SQuAD_it-*.json.gz_. Aceasta încheie incursiunea noastră în diversele modalități de încărcare a dataseturilor care nu sunt găzduite pe Hugging Face Hub. Acum că avem un dataset cu care să ne jucăm, să explorăm diverse tehnici de manipulare a datelor!

<Tip>

✏️ **Încercați!** Alegeți un alt dataset găzduit pe GitHub sau [UCI Machine Learning Repository](https://archive.ics.uci.edu/ml/index.php) și încercați să îl încărcați atât local cât și remote folosind tehnicile introduse mai sus. Pentru puncte bonus, încercați să încărcați un dataset care este stocat în format CSV sau text (consultați [documentația](https://huggingface.co/docs/datasets/loading#local-and-remote-files) pentru mai multe informații despre aceste formate).

</Tip>






<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter5/2.mdx" />

### Quiz la final de capitol [[quiz-la-final-de-capitol]]
https://huggingface.co/learn/course/ro/chapter2/9.md

# Quiz la final de capitol [[quiz-la-final-de-capitol]]

<CourseFloatingBanner
    chapter={2}
    classNames="absolute z-10 right-0 top-0"
/>

### 1. Care este ordinea pipeline-ului de modelare a limbajului?

<Question
	choices={[
		{
			text: "Primul, modelul, care gestionează textul și returnează predicții brute. Apoi, tokenizatorul interpretează aceste predicții și le convertește înapoi în text atunci când este necesar.",
			explain: "Modelul nu poate înțelege textul! Tokenizatorul trebuie mai întâi să tokenizeze textul și să îl convertească în ID-uri, astfel încât să poată fi înțeles de model."
		},
		{
			text: "Primul este tokenizatorul, care gestionează textul și returnează ID-uri. Modelul gestionează aceste ID-uri și generează o predicție, care poate fi un text.",
			explain: "Predicția modelului nu poate fi transformată imediat în text. Trebuie utilizat tokenizatorul pentru a converti predicția înapoi în text!"
		},
		{
			text: "Tokenizatorul gestionează textul și returnează ID-uri. Modelul gestionează aceste ID-uri și generează o predicție. Tokenizatorul poate fi apoi utilizat din nou pentru a converti aceste predicții înapoi în text.",
			explain: "Corect! Tokenizatorul poate fi utilizat atât pentru tokenizare, cât și pentru de-tokenizare.",
            correct: true
		}
	]}
/>

### 2. Câte dimensiuni are tensorul generat de modelul Transformer și care sunt acestea?

<Question
	choices={[
		{
			text: "2: Lungimea secvenței și mărimea lotului",
			explain: "Fals! Tensor-ul generat de model are a treia dimensiune: mărimea ascunsă."
		},
		{
			text: "2: Lungimea secvenței și mărimea ascunsă",
			explain: "Fals! Toate modelele Transformer gestionează loturi, chiar și cu o singură secvență; aceasta ar fi o dimensiune a lotului de 1!"
		},
		{
			text: "3: Lungimea secvenței, dimensiunea lotului și dimensiunea ascunsă",
			explain: "Corect!",
            correct: true
		}
	]}
/>

### 3. Care dintre următoarele este un exemplu de tokenizare a cuvintelor secundare?

<Question
	choices={[
		{
			text: "WordPiece",
			explain: "Da, acesta este un exemplu de tokenizare a cuvintelor secundare!",
            correct: true
		},
		{
			text: "Tokenizare bazată pe caractere",
			explain: "Tokenizarea bazată pe caractere nu este un tip de tokenizare a cuvintelor secundare."
		},
		{
			text: "Separarea pe baza spațiului și a punctuației",
			explain: "Corect, aceasta este o schemă de tokenizare bazată pe cuvinte!"
		},
		{
			text: "BPE",
			explain: "Da, acesta este un exemplu de tokenizare a cuvintelor secundare!",
            correct: true
        },
		{
			text: "Unigram",
			explain: "Da, acesta este un exemplu de tokenizare a cuvintelor secundare!",
            correct: true
        },
		{
			text: "Nimic din cele de mai sus",
			explain: "Incorect!"
        }
	]}
/>

### 4. Ce este un model head?

<Question
	choices={[
		{
			text: "O componentă a rețelei Transformer de bază care redirecționează tensorii către straturile lor corecte",
			explain: "Incorect! Nu există o astfel de componentă."
		},
		{
			text: "Cunoscut și sub denumirea de mecanism de auto-atenție, acesta adaptează reprezentarea unui token în funcție de celelalte token-uri din secvență",
			explain: "Incorect! Layerul de auto-atenție conține „heads” de atenție, dar nu acestea sunt adaptation heads."
		},
		{
			text: "O componentă suplimentară, alcătuită de obicei din unul sau mai multe straturi, pentru a converti predicțiile transformatorului într-o sarcină specifică de ieșire",
			explain: "Corect. Adaptation heads (sau simplu heads) apar în diverse forme: language modeling heads, question answering heads, sequence classification heads...",
			correct: true
		} 
	]}
/>

{#if fw === 'pt'}
### 5. Ce este un AutoModel?

<Question
	choices={[
		{
			text: "Un model care se antrenează automat pe baza datelor dumneavoastră",
			explain: "Incorect. Poate îl confundați cu produsul nostru <a href='https://huggingface.co/autotrain'>AutoTrain</a>?"
		},
		{
			text: "Un obiect care returnează arhitectura corespunzătoare bazată pe checkpoint.",
			explain: "Exact: <code>AutoModel</code> are nevoie doar să știe checkpoint-ul din care să se inițializeze pentru a returna arhitectura corectă.",
			correct: true
		},
		{
			text: "Un model care detectează automat limba utilizată pentru intrările sale pentru a încărca ponderile corecte",
			explain: "Incorect; deși unele checkpoint-uri și modele pot gestiona mai multe limbi, nu există instrumente integrate pentru selectarea automată a checkpoint-ului în funcție de limbă. Ar trebui să consultați <a href='https://huggingface.co/models'>Model Hub</a> pentru a găsi cel mai bun checkpoint pentru sarcina dumneavoastră!"
		} 
	]}
/>

{:else}
### 5. Ce este un TFAutoModel?

<Question
	choices={[
		{
			text: "Un model care se antrenează automat pe baza datelor dumneavoastră",
			explain: "Incorect. Poate îl confundați cu produsul nostru <a href='https://huggingface.co/autotrain'>AutoTrain</a>?"
		},
		{
			text: "Un obiect care returnează arhitectura corespunzătoare bazată pe checkpoint",
			explain: "Exact: <code>TFAutoModel</code> are nevoie doar să știe checkpoint-ul din care să se inițializeze pentru a returna arhitectura corectă.",
			correct: true
		},
		{
			text: "Un model care detectează automat limba utilizată pentru intrările sale pentru a încărca ponderile corecte",
			explain: "Incorect; deși unele checkpoint-uri și modele pot gestiona mai multe limbi, nu există instrumente integrate pentru selectarea automată a checkpoint-ului în funcție de limbă. Ar trebui să consultați <a href='https://huggingface.co/models'>Model Hub</a> pentru a găsi cel mai bun checkpoint pentru sarcina dumneavoastră!"
		} 
	]}
/>

{/if}

### 6. Care sunt tehnicile de care trebuie să țineți cont atunci când grupați secvențe de diferite lungimi?

<Question
	choices={[
		{
			text: "Trunchierea",
			explain: "Da, trunchierea este o modalitate corectă de a uniformiza secvențele astfel încât acestea să se încadreze într-o formă dreptunghiulară. Este totuși singura modalitate?",
			correct: true
		},
		{
			text: "Returning tensors",
			explain: "Deși celelalte tehnici vă permit să returnați tensori dreptunghiulari, doar a returna tensori nu ajută când combinați secvențele într-un lot."
		},
		{
			text: "Padding",
			explain: "Da, padding-ul este o modalitate corectă de a egaliza secvențele, astfel încât să se potrivească într-o formă dreptunghiulară. Este, însă, singura?",
			correct: true
		}, 
		{
			text: "Attention masking",
			explain: "Absolut! Măștile de atenție sunt extrem de importante atunci când gestionați secvențe de lungimi diferite. Totuși, nu este singura tehnică de care trebuie să țineți cont.",
			correct: true
		} 
	]}
/>

### 7. Care este scopul aplicării unei funcții SoftMax asupra logit-urilor produse de un model de clasificare a secvențelor?

<Question
	choices={[
		{
			text: "Le atenuează logiturile pentru a fi mai de încredere.",
			explain: "Nu, funcția SoftMax nu afectează fiabilitatea rezultatelor."
		},
		{
			text: "Aplică o limită inferioară și superioară astfel încât valorile să fie inteligibile.",
			explain: "Corect! Valorile rezultate sunt cuprinse între 0 și 1. Totuși, nu este singurul motiv pentru care folosim o funcție SoftMax.",
            correct: true
		},
		{
			text: "Suma totală a ieșirii devine 1, ducând la o interpretare probabilistică posibilă.",
			explain: "Corect! Totuși, nu este singurul motiv pentru care folosim o funcție SoftMax.",
            correct: true
		}
	]}
/>

### 8. În jurul cărei metode se concentrează cea mai mare parte a API-ului tokenizer?

<Question
	choices={[
		{
			text: "<code>encode</code>, deoarece poate codifica textul în ID-uri și ID-urile în predicții",
			explain: "Greșit! Deși metoda <code>encode</code> există pe tokenizers, ea nu există pe modele."
		},
		{
			text: "Apelarea directă a obiectului tokenizer.",
			explain: "Exact! Metoda <code>__call__</code> a tokenizatorului este foarte puternică și poate gestiona aproape orice. Este și metoda folosită pentru a obține predicții de la un model.",
			correct: true
		},
		{
			text: "<code>pad</code>",
			explain: "Greșit! Padding-ul este foarte util, dar este doar o parte a API-ului tokenizer."
		},
		{
			text: "<code>tokenize</code>",
			explain: "Metoda <code>tokenize</code> este, fără îndoială, una dintre cele mai utile, dar nu reprezintă nucleul API-ului tokenizer."
		}
	]}
/>

### 9. Ce conține variabila `result` în acest exemplu de cod?

```py
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
result = tokenizer.tokenize("Hello!")
```

<Question
	choices={[
		{
			text: "O listă de șiruri de caractere, fiecare șir fiind un token",
			explain: "Desigur! Convertiți aceste șiruri în ID-uri și transmiteți-le către un model!",
            correct: true
		},
		{
			text: "O listă de ID-uri",
			explain: "Incorect; pentru asta folosim metoda <code>__call__</code> sau <code>convert_tokens_to_ids</code>!"
		},
		{
			text: "Un șir care conține toate token-urile",
			explain: "Nu ar fi optim, deoarece scopul este de a separa șirul inițial în mai multe token-uri."
		}
	]}
/>

{#if fw === 'pt'}
### 10. Este ceva greșit în următorul cod?

```py
from transformers import AutoTokenizer, AutoModel

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
model = AutoModel.from_pretrained("gpt2")

encoded = tokenizer("Hey!", return_tensors="pt")
result = model(**encoded)
```

<Question
	choices={[
		{
			text: "Nu, pare corect.",
			explain: "Din păcate, asocierea unui model cu un tokenizator care a fost antrenat cu un alt checkpoint rareori este o idee bună. Modelul nu a fost antrenat să interpreteze ieșirea acestui tokenizator, așa că ieșirea modelului (dacă va rula chiar!) nu va avea sens."
		},
		{
			text: "Tokenizerul și modelul trebuie să provină întotdeauna din același checkpoint.",
			explain: "Corect!",
            correct: true
		},
		{
			text: "Este o bună practică să folosiți trunchierea și padding-ul cu tokenizatorul, întrucât fiecare intrare este un lot.",
			explain: "Este adevărat că orice intrare de model are nevoie să fie sub formă de lot. Totuși, trunchierea sau padding-ul acestei secvențe nu ar avea neapărat sens deoarece este singura intrare, iar aceste tehnici se folosesc pentru a trata o listă de propoziții."
		}
	]}
/>

{:else}
### 10. Este ceva greșit în următorul cod?

```py
from transformers import AutoTokenizer, TFAutoModel

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
model = TFAutoModel.from_pretrained("gpt2")

encoded = tokenizer("Hey!", return_tensors="pt")
result = model(**encoded)
```

<Question
	choices={[
		{
			text: "Nu, pare corect.",
			explain: "Din păcate, asocierea unui model cu un tokenizator care a fost antrenat cu un alt checkpoint rareori este o idee bună. Modelul nu a fost antrenat să interpreteze ieșirea acestui tokenizator, așa că ieșirea modelului (dacă va rula chiar!) nu va avea sens."
		},
		{
			text: "Tokenizerul și modelul trebuie să provină întotdeauna din același checkpoint.",
			explain: "Corect!",
            correct: true
		},
		{
			text: "Este o bună practică să folosiți trunchierea și padding-ul cu tokenizatorul, întrucât fiecare intrare este un lot.",
			explain: "Este adevărat că orice intrare de model trebuie să fie sub formă de batch. Totuși, trunchierea sau padding-ul acestei secvențe nu ar avea neapărat sens deoarece este singura intrare, iar aceste tehnici se folosesc pentru a grupa o listă de propoziții într-un batch."
		}
	]}
/>

{/if}

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter2/9.mdx" />

### Gestionarea secvențelor multiple[[gestionarea-secvențelor-multiple]]
https://huggingface.co/learn/course/ro/chapter2/5.md

# Gestionarea secvențelor multiple[[gestionarea-secvențelor-multiple]]

{#if fw === 'pt'}

<CourseFloatingBanner chapter={2}
  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/chapter2/section5_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section5_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={2}
  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/chapter2/section5_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section5_tf.ipynb"},
]} />

{/if}

{#if fw === 'pt'}
<Youtube id="M6adb1j2jPI"/>
{:else}
<Youtube id="ROxrFOEbsQE"/>
{/if}

În secțiunea anterioară, am explorat cel mai simplu caz de utilizare: realizarea de inferențe pe o singură secvență scurtă. Cu toate acestea, apar deja unele întrebări:

- Cum gestionăm secvențe multiple?
- Cum gestionăm secvențe multiple *de lungimi diferite*?
- Indicii vocabularului sunt singurele intrări care permit unui model să funcționeze bine?
- Există o secvență prea lungă?

Să vedem ce tipuri de probleme prezintă aceste întrebări și cum le putem rezolva folosind API-ul 🤗 Transformers.


## Modelele așteaptă un batch de intrări[[modelele-așteaptă-un-batch-de-intrări]]

În exercițiul anterior ați văzut cum secvențele sunt transformate în liste de numere. Să convertim această listă de numere într-un tensor și să o transmitem modelului:

{#if fw === 'pt'}
```py
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)

sequence = "I've been waiting for a HuggingFace course my whole life."

tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)
input_ids = torch.tensor(ids)
# Această linie va eșua.
model(input_ids)
```

```python out
IndexError: Dimension out of range (expected to be in range of [-1, 0], but got 1)
```
{:else}
```py
import tensorflow as tf
from transformers import AutoTokenizer, TFAutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)

sequence = "I've been waiting for a HuggingFace course my whole life."

tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)
input_ids = tf.constant(ids)
# Această linie va eșua.
model(input_ids)
```

```py out
InvalidArgumentError: Input to reshape is a tensor with 14 values, but the requested shape has 196 [Op:Reshape]
```
{/if}

Oh, nu! De ce a eșuat? Am urmat pașii din pipeline-ul din secțiunea 2.

Problema este că am trimis o singură secvență către model, în timp ce modelele 🤗 Transformers așteaptă în mod implicit mai multe propoziții. Aici am încercat să facem tot ce a făcut tokenizatorul în fundal atunci când l-am aplicat unei `secvențe`. Dar, dacă vă uitați cu atenție, veți vedea că tokenizatorul nu a convertit doar lista de ID-uri de intrare într-un tensor, ci a adăugat și o dimensiune peste aceasta:

{#if fw === 'pt'}
```py
tokenized_inputs = tokenizer(sequence, return_tensors="pt")
print(tokenized_inputs["input_ids"])
```

```python out
tensor([[  101,  1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172,
          2607,  2026,  2878,  2166,  1012,   102]])
```
{:else}
```py
tokenized_inputs = tokenizer(sequence, return_tensors="tf")
print(tokenized_inputs["input_ids"])
```

```py out
<tf.Tensor: shape=(1, 16), dtype=int32, numpy=
array([[  101,  1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662,
        12172,  2607,  2026,  2878,  2166,  1012,   102]], dtype=int32)>
```
{/if}

Să încercăm din nou și să adăugăm o nouă dimensiune:

{#if fw === 'pt'}
```py
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)

sequence = "I've been waiting for a HuggingFace course my whole life."

tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)

input_ids = torch.tensor([ids])
print("Input IDs:", input_ids)

output = model(input_ids)
print("Logits:", output.logits)
```
{:else}
```py
import tensorflow as tf
from transformers import AutoTokenizer, TFAutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)

sequence = "I've been waiting for a HuggingFace course my whole life."

tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)

input_ids = tf.constant([ids])
print("Input IDs:", input_ids)

output = model(input_ids)
print("Logits:", output.logits)
```
{/if}

Imprimăm ID-urile de intrare, precum și logit-urile rezultate - iată rezultatul:

{#if fw === 'pt'}
```python out
Input IDs: [[ 1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172,  2607, 2026,  2878,  2166,  1012]]
Logits: [[-2.7276,  2.8789]]
```
{:else}
```py out
Input IDs: tf.Tensor(
[[ 1045  1005  2310  2042  3403  2005  1037 17662 12172  2607  2026  2878
   2166  1012]], shape=(1, 14), dtype=int32)
Logits: tf.Tensor([[-2.7276208  2.8789377]], shape=(1, 2), dtype=float32)
```
{/if}

Batching-ul este acțiunea de a trimite mai multe propoziții prin model, toate odată. Dacă aveți o singură propoziție, puteți crea un lot cu o singură secvență: 

```
batched_ids = [ids, ids]
```

Acesta este un batch de două secvențe identice!

<Tip>

✏️ **Încercați!** Convertiți această listă `batched_ids` într-un tensor și treceți-o prin modelul dumneavoastră. Verificați dacă obțineți aceleași logits ca înainte (dar de două ori)!

</Tip>

Batching-ul permite modelului să funcționeze atunci când îi furnizați mai multe secvențe. Utilizarea mai multor secvențe este la fel de simplă ca și crearea unui lot cu o singură secvență. Există însă o a doua problemă. Atunci când încercați să combinați două (sau mai multe) propoziții, acestea pot avea lungimi diferite. Dacă ați mai lucrat vreodată cu tensori, știți că aceștia trebuie să aibă o formă dreptunghiulară, deci nu veți putea converti direct lista de ID-uri de intrare într-un tensor. Pentru a rezolva această problemă, de obicei *umplem* datele de intrare.

## Padding-ul datelor de intrare[[padding-ul-datelor-de-intrare]]

Următoarea serie de liste nu poate fi convertită într-un tensor:

```py no-format
batched_ids = [
    [200, 200, 200],
    [200, 200]
]
```

Pentru a ocoli acest lucru, vom folosi *padding-ul* pentru ca tensorii noștri să aibă o formă dreptunghiulară. Padding-ul (umplerea datelor) asigură că toate propozițiile noastre au aceeași lungime prin adăugarea unui cuvânt special numit *padding token* la propozițiile cu mai puține valori. De exemplu, dacă aveți 10 propoziții cu 10 cuvinte și 1 propoziție cu 20 de cuvinte, padding-ul va asigura că toate propozițiile au 20 de cuvinte. În exemplul nostru, tensorul rezultat arată astfel:

```py no-format
padding_id = 100

batched_ids = [
    [200, 200, 200],
    [200, 200, padding_id],
]
```

ID-ul token-ului de padding poate fi găsit în `tokenizer.pad_token_id`. Să-l folosim și să trimitem cele două propoziții prin model în mod separat și grupate împreună:

{#if fw === 'pt'}
```py no-format
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)

sequence1_ids = [[200, 200, 200]]
sequence2_ids = [[200, 200]]
batched_ids = [
    [200, 200, 200],
    [200, 200, tokenizer.pad_token_id],
]

print(model(torch.tensor(sequence1_ids)).logits)
print(model(torch.tensor(sequence2_ids)).logits)
print(model(torch.tensor(batched_ids)).logits)
```

```python out
tensor([[ 1.5694, -1.3895]], grad_fn=<AddmmBackward>)
tensor([[ 0.5803, -0.4125]], grad_fn=<AddmmBackward>)
tensor([[ 1.5694, -1.3895],
        [ 1.3373, -1.2163]], grad_fn=<AddmmBackward>)
```
{:else}
```py no-format
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)

sequence1_ids = [[200, 200, 200]]
sequence2_ids = [[200, 200]]
batched_ids = [
    [200, 200, 200],
    [200, 200, tokenizer.pad_token_id],
]

print(model(tf.constant(sequence1_ids)).logits)
print(model(tf.constant(sequence2_ids)).logits)
print(model(tf.constant(batched_ids)).logits)
```

```py out
tf.Tensor([[ 1.5693678 -1.3894581]], shape=(1, 2), dtype=float32)
tf.Tensor([[ 0.5803005  -0.41252428]], shape=(1, 2), dtype=float32)
tf.Tensor(
[[ 1.5693681 -1.3894582]
 [ 1.3373486 -1.2163193]], shape=(2, 2), dtype=float32)
```
{/if}

Este ceva în neregulă cu logit-urile din predicțiile noastre grupate: al doilea rând ar trebui să fie identic cu logit-urile pentru a doua propoziție, dar avem valori complet diferite!

Acest lucru se datorează faptului că principala caracteristică a modelelor Transformer sunt straturile de atenție care *contextualizează* fiecare token. Acestea vor lua în considerare token-urile de umplutură, deoarece se ocupă de toate token-urile unei secvențe. Pentru a obține același rezultat atunci când trecem propoziții individuale de diferite lungimi prin model sau atunci când trecem un batch cu aceleași propoziții și același umplutură aplicată, trebuie să spunem acelor straturi de atenție să ignore token-urile de umplutură. Acest lucru se realizează prin utilizarea unei măști de atenție (attention mask).

## Attention masks(măști de atenție)[[attention-masks]]

*Măștile de atenție* sunt tensori cu exact aceeași formă ca tensorul ID-urilor de intrare, completate cu 0 și 1: valorile 1 indică faptul că ar trebui să se acorde atenție token-urilor corespunzătoare, iar valorile 0 indică faptul că nu ar trebui să se acorde atenție token-urilor corespunzătoare (adică acestea ar trebui ignorate de straturile de atenție ale modelului).

Să completăm exemplul anterior cu o mască de atenție:

{#if fw === 'pt'}
```py no-format
batched_ids = [
    [200, 200, 200],
    [200, 200, tokenizer.pad_token_id],
]

attention_mask = [
    [1, 1, 1],
    [1, 1, 0],
]

outputs = model(torch.tensor(batched_ids), attention_mask=torch.tensor(attention_mask))
print(outputs.logits)
```

```python out
tensor([[ 1.5694, -1.3895],
        [ 0.5803, -0.4125]], grad_fn=<AddmmBackward>)
```
{:else}
```py no-format
batched_ids = [
    [200, 200, 200],
    [200, 200, tokenizer.pad_token_id],
]

attention_mask = [
    [1, 1, 1],
    [1, 1, 0],
]

outputs = model(tf.constant(batched_ids), attention_mask=tf.constant(attention_mask))
print(outputs.logits)
```

```py out
tf.Tensor(
[[ 1.5693681  -1.3894582 ]
 [ 0.5803021  -0.41252586]], shape=(2, 2), dtype=float32)
```
{/if}

Acum obținem aceeași logits pentru a doua propoziție din batch.

Observăm cum ultima valoare a celei de-a doua secvențe este un ID de padding, care este o valoare 0 în masca de atenție.

<Tip>

✏️ **Încercați!** Aplicați manual tokenizarea pe cele două propoziții utilizate în secțiunea 2 ("I've been waiting for a HuggingFace course my whole life." și "I hate this so much!"). Treceți-le prin model și verificați dacă obțineți aceeași logiți ca în secțiunea 2. Acum grupați-le împreună folosind token-ul de padding, apoi creați masca de atenție corespunzătoare. Verificați dacă obțineți aceleași rezultate atunci când parcurgeți modelul!

</Tip>

## Secvențe mai lungi[[secvențe-mai-lungi]]

Cu modelele Transformer, există o limită a lungimii secvențelor pe care le putem transmite modelelor. Majoritatea modelelor gestionează secvențe de până la 512 sau 1024 de token-uri și se vor bloca atunci când li se cere să proceseze secvențe mai lungi. Există două soluții la această problemă:

- Utilizați un model cu o lungime de secvență acceptată mai mare.
- Trunchiați secvențele.

Modelele au diferite lungimi de secvență acceptate, iar unele sunt specializate în tratarea secvențelor foarte lungi. [Longformer](https://huggingface.co/docs/transformers/model_doc/longformer) este un exemplu, iar altul este [LED](https://huggingface.co/docs/transformers/model_doc/led). Dacă lucrați la o sarcină care necesită secvențe foarte lungi, vă recomandăm să aruncați o privire la aceste modele.

În caz contrar, vă recomandăm să vă trunchiați secvențele prin specificarea parametrului `max_sequence_length`:
```py
sequence = sequence[:max_sequence_length]
```


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter2/5.mdx" />

### Introducere[[introducere]]
https://huggingface.co/learn/course/ro/chapter2/1.md

# Introducere[[introducere]]

<CourseFloatingBanner
    chapter={2}
    classNames="absolute z-10 right-0 top-0"
/>

După cum ați văzut în [Capitolul 1](/course/chapter1), modelele Transformer sunt de obicei foarte voluminoase. Fiind alcătuite din milioane până la zeci de *miliarde* de parametri, instruirea și implementarea acestor modele este o sarcină complicată. În plus, cu noi modele lansate aproape zilnic și fiecare având propria sa implementare, testarea tuturor acestora nu este o sarcină ușoară.

Biblioteca 🤗 Transformers a fost creată pentru a rezolva această problemă. Scopul său este de a oferi un singur API prin care orice model Transformer poate fi încărcat, antrenat și salvat. Principalele caracteristici ale bibliotecii sunt:

- **Simplitate în utilizare**: Descărcarea, încărcarea și utilizarea unui model NLP de ultimă generație pentru inferență pot fi realizate în doar două linii de cod.
- **Flexibilitate**: În esența lor, toate modelele sunt simple clase PyTorch `nn.Module` sau TensorFlow `tf.keras.Model` și pot fi manipulate ca orice alte modele în framework-urile lor respective de învățare automată (ML).
- **Simplitate**: Aproape că nu se fac abstractizări în întreaga bibliotecă. "All in one file" este un concept de bază: trecerea înainte a unui model este definită în întregime într-un singur fișier, astfel încât codul în sine să fie ușor de înțeles și de modificat.

Această ultimă caracteristică face "🤗 Transformers" destul de diferită de alte biblioteci ML. Modelele nu sunt construite pe module 
care sunt partajate între fișiere; în schimb, fiecare model are propriile sale straturi. În plus, pe lângă faptul că modelele sunt mai ușor de abordat și de înțeles, acest lucru vă permite să experimentați cu ușurință pe un model fără a le afecta pe celelalte.

Acest capitol va începe cu un exemplu end-to-end în care folosim împreună un model și un tokenizer pentru a replica funcția `pipeline()` introdusă în [Capitolul 1](/course/chapter1). În continuare, vom discuta despre API-ul modelului: vom analiza clasele de model și de configurare și vă vom arăta cum să încărcați un model și cum acesta procesează intrările numerice pentru a genera predicții. 

Apoi vom analiza API-ul tokenizer, care este cealaltă componentă principală a funcției `pipeline()`. Tokenizerii se ocupă de prima și ultima etapă de procesare, gestionând conversia de la text la intrări numerice pentru rețeaua neuronală și conversia înapoi la text atunci când este necesar. În cele din urmă, vă vom arăta cum să vă ocupați de trimiterea mai multor propoziții printr-un model în cadrul unui batch pregătit, apoi vom încheia totul cu o examinare mai atentă a funcției  `tokenizer()`.


<Tip>
⚠️  
Pentru a beneficia de toate funcțiile disponibile cu Model Hub și 🤗 Transformers, vă recomandăm <a href="https://huggingface.co/join">să vă creați un cont</a>.
</Tip>

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter2/1.mdx" />

### Modele[[modele]]
https://huggingface.co/learn/course/ro/chapter2/3.md

# Modele[[modele]]

{#if fw === 'pt'}

<CourseFloatingBanner chapter={2}
  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/chapter2/section3_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section3_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={2}
  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/chapter2/section3_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section3_tf.ipynb"},
]} />

{/if}

{#if fw === 'pt'}
<Youtube id="AhChOFRegn4"/>
{:else}
<Youtube id="d3JVgghSOew"/>
{/if}

{#if fw === 'pt'}
 
În această secțiune vom analiza mai detaliat crearea și utilizarea unui model. Vom folosi clasa `AutoModel`, care este utilă atunci când doriți să instanțiați orice model dintr-un checkpoint.

Clasa `AutoModel` și toate celelalte clase înrudite sunt, de fapt, simple wrappere ale gamei largi de modele disponibile în bibliotecă. Este un wrapper inteligent, deoarece poate ghici automat arhitectura modelului corespunzător pentru checkpoint-ul dumneavoastră și apoi instanțiază un model cu această arhitectură.

{:else}
 
În această secțiune vom analiza mai detaliat crearea și utilizarea unui model. Vom utiliza clasa `TFAutoModel`, care este utilă atunci când doriți să instanțați un model dintr-un checkpoint.

Clasa `TFAutoModel` și toate clasele înrudite cu aceasta sunt de fapt simple wrappere ale gamei largi de modele disponibile în bibliotecă. Este un wrapper inteligent, deoarece poate ghici automat arhitectura modelului corespunzător pentru checkpoint-ul dumneavoastră și apoi instanțiază un model cu această arhitectură.

{/if}

Cu toate acestea, dacă știți ce tip de model doriți să utilizați, puteți folosi direct clasa care definește arhitectura acestuia. Să aruncăm o privire la modul în care funcționează acest lucru cu un model BERT.

## Crearea unui Transformer[[crearea-unui-transformer]]

Primul lucru pe care va trebui să îl facem pentru a inițializa un model BERT este să încărcăm un obiect de configurare:

{#if fw === 'pt'}
```py
from transformers import BertConfig, BertModel

# Construirea configurației
config = BertConfig()

# Construirea modelului pornind de la configurație
model = BertModel(config)
```
{:else}
```py
from transformers import BertConfig, TFBertModel

# Construirea configurației
config = BertConfig()

#  Construirea modelului pornind de la configurație
model = TFBertModel(config)
```
{/if}

Configurația conține multe atribute care sunt utilizate pentru a construi modelul:

```py
print(config)
```

```python out
BertConfig {
  [...]
  "hidden_size": 768,
  "intermediate_size": 3072,
  "max_position_embeddings": 512,
  "num_attention_heads": 12,
  "num_hidden_layers": 12,
  [...]
}
```

Deși nu ați văzut încă ce fac toate aceste atribute, ar trebui să recunoașteți unele dintre ele: atributul `hidden_size` definește dimensiunea vectorului `hidden_states`, iar `num_hidden_layers` definește numărul de straturi pe care le are modelul Transformer.

### Diferite metode de încărcare[[diferite-metode-de-încărcare]]

Crearea unui model din configurația implicită îl inițializează cu valori aleatorii:


{#if fw === 'pt'}
```py
from transformers import BertConfig, BertModel

config = BertConfig()
model = BertModel(config)

# Modelul este inițializat aleatoriu!
```
{:else}
```py
from transformers import BertConfig, TFBertModel

config = BertConfig()
model = TFBertModel(config)

# Modelul este inițializat aleatoriu!
```
{/if}

Modelul poate fi utilizat în această stare, dar va produce rezultate neclare; mai întâi trebuie să fie antrenat. Am putea antrena modelul de la zero pentru sarcina în cauză, dar, după cum ați văzut în [Capitolul 1](/course/chapter1), acest lucru ar necesita mult timp și o mulțime de date și ar avea un impact semnificativ asupra mediului. Pentru a evita efortul inutil, este esențial să puteți partaja și reutiliza modelele care au fost deja antrenate.

Încărcarea unui model Transformer care este deja instruit este simplă - putem face acest lucru utilizând metoda `from_pretrained()`, care este analogă metodei `from_pretrained()` în PyTorch:

{#if fw === 'pt'}
```py
from transformers import BertModel

model = BertModel.from_pretrained("bert-base-cased")
```

După cum ați văzut mai devreme, am putea înlocui `BertModel` cu clasa `AutoModel` echivalentă. Vom face acest lucru în continuare, deoarece acest lucru produce un cod independent de checkpoint-uri; dacă codul dumneavoastră funcționează pentru un checkpoint, ar trebui să funcționeze fără probleme pentru altul. Acest lucru este valabil chiar dacă arhitectura este diferită, atât timp cât checkpoint-ul a fost instruit pentru o sarcină similară (de exemplu, o sarcină de analiză a sentimentelor).

{:else}
```py
from transformers import TFBertModel

model = TFBertModel.from_pretrained("bert-base-cased")
```

După cum ați văzut mai devreme, am putea înlocui `BertModel` cu clasa `AutoModel` echivalentă. Vom face acest lucru în continuare, deoarece acest lucru produce un cod independent de checkpoint-uri; dacă codul dumneavoastră funcționează pentru un checkpoint, ar trebui să funcționeze fără probleme pentru altul. Acest lucru este valabil chiar dacă arhitectura este diferită, atât timp cât checkpoint-ul a fost instruit pentru o sarcină similară (de exemplu, o sarcină de analiză a sentimentelor).

{/if}

În exemplul de cod de mai sus nu am utilizat `BertConfig` și, în loc de acesta, am încărcat un model preantrenat prin intermediul identificatorului `bert-base-cased`. Acesta este un checkpoint al modelului care a fost antrenat chiar de autorii BERT; puteți găsi mai multe detalii despre acesta în [model card](https://huggingface.co/bert-base-cased).

Acest model este acum inițializat cu toate weight-urile din checkpoint. Acesta poate fi utilizat direct pentru inferență pe sarcinile pe care a fost antrenat și poate fi, de asemenea, ajustat pe o sarcină nouă. Prin antrenarea cu weight-uri preantrenate în prealabil, putem obține rapid rezultate bune.

Greutățile au fost descărcate și stocate în memorie cache (astfel încât apelurile viitoare la metoda `from_pretrained()` nu le vor descărca din nou) în folderul cache, care are ca valoare implicită *~/.cache/huggingface/transformers*. Puteți personaliza folderul cache prin setarea variabilei de mediu `HF_HOME`.

Identificatorul utilizat pentru încărcarea modelului poate fi identificatorul oricărui model de pe Model Hub, atâta timp cât acesta este compatibil cu arhitectura BERT. Lista completă a checkpoint-urilor BERT disponibile poate fi găsită [aici](https://huggingface.co/models?filter=bert).


### Metode de salvare[[metode-de-salvare]]

Salvarea unui model este la fel de ușoară ca încărcarea unuia - folosim metoda `save_pretrained()`, care este analogă metodei `from_pretrained()`:

```py
model.save_pretrained("directory_on_my_computer")
```

Se salvează două fișiere pe disc:

{#if fw === 'pt'}
```
ls directory_on_my_computer

config.json pytorch_model.safetensors
```
{:else}
```
ls directory_on_my_computer

config.json tf_model.h5
```
{/if}

Dacă aruncați o privire la fișierul *config.json*, veți recunoaște atributele necesare pentru construirea arhitecturii modelului. Acest fișier conține, de asemenea, unele metadate, cum ar fi locul de origine al checkpoint-ului și ce versiune 🤗 Transformers ați folosit când ați salvat ultima dată checkpoint-ul.

{#if fw === 'pt'}
 
Fișierul *pytorch_model.safetensors* este cunoscut sub numele de *state dictionary*; acesta conține toate weight-urile modelului dumneavoastră. Cele două fișiere merg mână în mână; configurația este necesară pentru a cunoaște arhitectura modelului, în timp ce weight-urile modelului sunt parametrii modelului.

{:else}
 
Fișierul *tf_model.h5* este cunoscut sub numele de *state dictionary*; acesta conține toate weight-urile modelului dumneavoastră. Cele două fișiere merg mână în mână; configurația este necesară pentru a cunoaște arhitectura modelului, în timp ce weight-urile modelului sunt parametrii modelului.

{/if}

## Utilizarea unui model Transformer pentru inferență[[utilizarea-unui-model-transformer-pentru-inferență]]


Acum că știți cum să încărcați și să salvați un model, să încercăm să îl folosim pentru a face câteva predicții. Modelele Transformer pot procesa doar numere - numere pe care le generează tokenizatorul. Dar înainte de a discuta despre tokenizeri, să explorăm ce intrări acceptă modelul.

Tokenizerii se pot ocupa de transformarea intrărilor în tensori ai framework-ului corespunzător, dar pentru a vă ajuta să înțelegeți ce se întâmplă, vom arunca o privire rapidă la ceea ce trebuie făcut înainte de trimiterea intrărilor către model.

Să spunem că avem câteva secvențe:

```py
sequences = ["Hello!", "Cool.", "Nice!"]
```

Tokenizatorul le convertește în indici de vocabular care sunt denumiți în mod obișnuit *input IDs*. Fiecare secvență este acum o listă de numere! Rezultatul este:

```py no-format
encoded_sequences = [
    [101, 7592, 999, 102],
    [101, 4658, 1012, 102],
    [101, 3835, 999, 102],
]
```

Aceasta este o listă de secvențe codificate: o listă de liste. Tensorii acceptă numai forme dreptunghiulare (gândiți-vă la matrici). Acest "array" are deja o formă dreptunghiulară, astfel încât convertirea sa într-un tensor este ușoară:

{#if fw === 'pt'}
```py
import torch

model_inputs = torch.tensor(encoded_sequences)
```
{:else}
```py
import tensorflow as tf

model_inputs = tf.constant(encoded_sequences)
```
{/if}

### Utilizarea tensorilor ca intrări în model[[utilizarea-tensorilor-ca-intrări-în-model]]

Utilizarea tensorilor cu ajutorul modelului este extrem de simplă - trebuie doar să apelăm modelul cu intrările:

```py
output = model(model_inputs)
```

Deși modelul acceptă o mulțime de argumente diferite, doar ID-urile de intrare sunt necesare. Vom explica mai târziu ce fac celelalte argumente și când sunt necesare, 
dar mai întâi trebuie să aruncăm o privire mai atentă la tokenizerii care construiesc intrările pe care un model Transformer le poate înțelege.

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter2/3.mdx" />

### Să punem totul cap la cap[[să-punem-totul-cap-la-cap]]
https://huggingface.co/learn/course/ro/chapter2/6.md

# Să punem totul cap la cap[[să-punem-totul-cap-la-cap]]

{#if fw === 'pt'}

<CourseFloatingBanner chapter={2}
  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/chapter2/section6_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section6_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={2}
  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/chapter2/section6_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section6_tf.ipynb"},
]} />

{/if}

În ultimele secțiuni, ne-am străduit să facem cea mai mare parte a muncii manual. Am explorat modul în care funcționează tokenizatoarele și am analizat tokenizarea, conversia în ID-uri de intrare, padding, trunchiere și măști de atenție.

Cu toate acestea, după cum am văzut în secțiunea 2, API-ul 🤗 Transformers poate gestiona toate acestea pentru noi cu o funcție de nivel înalt în care ne vom adânci aici. Atunci când apelați `tokenizer` direct pe propoziție, primiți înapoi intrări care sunt gata să treacă prin modelul dvs:

```py
from transformers import AutoTokenizer

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

sequence = "I've been waiting for a HuggingFace course my whole life."

model_inputs = tokenizer(sequence)
```

Aici, variabila `model_inputs` conține tot ceea ce este necesar pentru ca un model să funcționeze bine. Pentru DistilBERT, aceasta include ID-urile de intrare, precum și masca de atenție. Alte modele care acceptă intrări suplimentare le vor avea, de asemenea, la ieșire prin obiectul `tokenizer`.

După cum vom vedea în câteva exemple de mai jos, această metodă este foarte puternică. În primul rând, poate tokeniza o singură secvență:

```py
sequence = "I've been waiting for a HuggingFace course my whole life."

model_inputs = tokenizer(sequence)
```

De asemenea, gestionează mai multe secvențe simultan, fără nicio modificare a API-ului:

```py
sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]

model_inputs = tokenizer(sequences)
```

Poate aplica padding în funcție de mai multe obiective:

```py
# Va umple secvențele până la lungimea maximă a secvenței
model_inputs = tokenizer(sequences, padding="longest")

# Va umple secvențele până la lungimea maximă a modelului
# (512 pentru BERT sau DistilBERT)
model_inputs = tokenizer(sequences, padding="max_length")

# Va umple secvențele până la lungimea maximă specificată
model_inputs = tokenizer(sequences, padding="max_length", max_length=8)
```

De asemenea, poate trunchia secvențele:

```py
sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]

# Va trunchia secvențele care sunt mai lungi decât lungimea maximă a modelului
# (512 pentru BERT sau DistilBERT)
model_inputs = tokenizer(sequences, truncation=True)

# Va trunchia secvențele care sunt mai lungi decât lungimea maximă specificată
model_inputs = tokenizer(sequences, max_length=8, truncation=True)
```

Obiectul `tokenizer` poate gestiona conversia în tensori specifici framework-ului, care pot fi apoi trimiși direct la model. De exemplu, în următorul exemplu de cod, solicităm tokenizatorului să returneze tensori din diferite framework-uri - `„pt”` returnează tensori PyTorch, `„tf”` returnează tensori TensorFlow, iar `„np”` returnează matrici NumPy:

```py
sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]

# Returnează tensori PyTorch
model_inputs = tokenizer(sequences, padding=True, return_tensors="pt")

# Returnează tensori TensorFlow
model_inputs = tokenizer(sequences, padding=True, return_tensors="tf")

# Returnează array-uri NumPy
model_inputs = tokenizer(sequences, padding=True, return_tensors="np")
```

## Token-uri speciale[[token-uri-speciale]]

Dacă aruncăm o privire la ID-urile de intrare returnate de tokenizer, vom vedea că sunt puțin diferite de cele pe care le-am avut mai devreme:

```py
sequence = "I've been waiting for a HuggingFace course my whole life."

model_inputs = tokenizer(sequence)
print(model_inputs["input_ids"])

tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)
print(ids)
```

```python out
[101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102]
[1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012]
```

Un token ID a fost adăugat la început, iar unul la sfârșit. Să decodificăm cele două secvențe de ID-uri de mai sus pentru a vedea despre ce este vorba:

```py
print(tokenizer.decode(model_inputs["input_ids"]))
print(tokenizer.decode(ids))
```

```python out
"[CLS] i've been waiting for a huggingface course my whole life. [SEP]"
"i've been waiting for a huggingface course my whole life."
```

Tokenizatorul a adăugat cuvântul special `[CLS]` la început și cuvântul special `[SEP]` la sfârșit. Acest lucru se datorează faptului că modelul a fost preantrenat cu aceste cuvinte, deci pentru a obține aceleași rezultate pentru inferență trebuie să le adăugăm și pe acestea. Rețineți că unele modele nu adaugă cuvinte speciale sau adaugă cuvinte diferite; de asemenea, modelele pot adăuga aceste cuvinte speciale doar la început sau doar la sfârșit. În orice caz, tokenizatorul știe care sunt cele așteptate și se va ocupa de acest lucru pentru dumneavoastră.

## Încheiere: De la tokenizer la model[[încheiere-de-la-tokenizator-la-model]]

Acum că am văzut toți pașii individuali pe care îi utilizează obiectul `tokenizer` atunci când este aplicat pe texte, să vedem o ultimă dată cum poate gestiona secvențe multiple (padding!), secvențe foarte lungi (trunchiere!) și mai multe tipuri de tensori cu API-ul său principal:

{#if fw === 'pt'}
```py
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]

tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
output = model(**tokens)
```
{:else}
```py
import tensorflow as tf
from transformers import AutoTokenizer, TFAutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]

tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="tf")
output = model(**tokens)
```
{/if}


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter2/6.mdx" />

### Implementarea Optimizată a Inferenței
https://huggingface.co/learn/course/ro/chapter2/8.md

# Implementarea Optimizată a Inferenței

În această secțiune, vom explora framework-uri avansate pentru optimizarea implementărilor LLM: Text Generation Inference (TGI), vLLM și llama.cpp. Aceste aplicații sunt utilizate în principal în medii de producție pentru a servi LLM-uri utilizatorilor. Această secțiune se concentrează pe modul de implementare a acestor framework-uri în producție, mai degrabă decât pe modul de utilizare pentru inferență pe o singură mașină.

Vom acoperi modul în care aceste instrumente maximizează eficiența inferenței și simplifică implementările de producție ale Modelelor de Limbaj de Mari Dimensiuni.

## Ghid de Selecție a Framework-ului

TGI, vLLM și llama.cpp servesc scopuri similare, dar au caracteristici distincte care le fac mai potrivite pentru diferite cazuri de utilizare. Să ne uităm la diferențele cheie dintre ele, concentrându-ne pe performanță și integrare.

### Gestionarea Memoriei și Performanța

**TGI** este proiectat să fie stabil și previzibil în producție, folosind lungimi fixe de secvență pentru a menține utilizarea memoriei consistentă. TGI gestionează memoria folosind Flash Attention 2 și tehnici de procesare continuă în loturi. Aceasta înseamnă că poate procesa calculele de atenție foarte eficient și poate menține GPU-ul ocupat prin alimentarea constantă cu lucru. Sistemul poate muta părți ale modelului între CPU și GPU când este necesar, ceea ce ajută la gestionarea modelelor mai mari.

<img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/tgi/flash-attn.png" alt="Flash Attention" />

<Tip title="Cum Funcționează Flash Attention">
Flash Attention este o tehnică care optimizează mecanismul de atenție în modelele transformer prin abordarea blocajelor de lățime de bandă a memoriei. Așa cum s-a discutat mai devreme în [Capitolul 1.8](/course/chapter1/8), mecanismul de atenție are complexitate și utilizare de memorie pătratică, făcându-l ineficient pentru secvențe lungi.

Inovația cheie constă în modul în care gestionează transferurile de memorie între High Bandwidth Memory (HBM) și cache-ul SRAM mai rapid. Atenția tradițională transferă în mod repetat date între HBM și SRAM, creând blocaje prin lăsarea GPU-ului inactiv. Flash Attention încarcă datele o dată în SRAM și efectuează toate calculele acolo, minimizând transferurile costisitoare de memorie.

Deși beneficiile sunt cele mai semnificative în timpul antrenamentului, utilizarea redusă de VRAM și eficiența îmbunătățită a Flash Attention o fac valoroasă și pentru inferență, permițând servirea LLM mai rapidă și mai scalabilă.
</Tip>

**vLLM** adoptă o abordare diferită prin utilizarea PagedAttention. La fel cum un computer își gestionează memoria în pagini, vLLM împarte memoria modelului în blocuri mai mici. Acest sistem inteligent înseamnă că poate gestiona cereri de dimensiuni diferite mai flexibil și nu risipește spațiul de memorie. Este deosebit de bun la partajarea memoriei între diferite cereri și reduce fragmentarea memoriei, ceea ce face întregul sistem mai eficient.

<Tip title="Cum Funcționează PagedAttention">
PagedAttention este o tehnică care abordează un alt blocaj critic în inferența LLM: gestionarea memoriei cache KV. Așa cum s-a discutat în [Capitolul 1.8](/course/chapter1/8), în timpul generării de text, modelul stochează cheile și valorile de atenție (cache KV) pentru fiecare token generat pentru a reduce calculele redundante. Cache-ul KV poate deveni enorm, în special cu secvențe lungi sau multiple cereri concurente.

Inovația cheie a vLLM constă în modul în care gestionează acest cache:

1. **Paginarea Memoriei**: În loc să trateze cache-ul KV ca un bloc mare, este împărțit în "pagini" de dimensiune fixă (similar cu memoria virtuală în sistemele de operare).
2. **Stocare Non-Contigua**: Paginile nu trebuie să fie stocate contiguu în memoria GPU, permițând o alocare mai flexibilă a memoriei.
3. **Gestionarea Tabelului de Pagini**: Un tabel de pagini urmărește care pagini aparțin cărei secvențe, permițând căutare și acces eficient.
4. **Partajarea Memoriei**: Pentru operații precum eșantionarea paralelă, paginile care stochează cache-ul KV pentru prompt pot fi partajate între multiple secvențe.

Abordarea PagedAttention poate duce la un throughput de până la 24 de ori mai mare comparativ cu metodele tradiționale, făcând-o o schimbare de paradigmă pentru implementările LLM de producție. Dacă vrei să înțelegi cu adevărat în profunzime cum funcționează PagedAttention, poți citi [ghidul din documentația vLLM](https://docs.vllm.ai/en/latest/design/kernel/paged_attention.html).
</Tip>

**llama.cpp** este o implementare C/C++ extrem de optimizată proiectată inițial pentru rularea modelelor LLaMA pe hardware de consum. Se concentrează pe eficiența CPU cu accelerație GPU opțională și este ideal pentru medii cu resurse limitate. llama.cpp folosește tehnici de cuantificare pentru a reduce dimensiunea modelului și cerințele de memorie menținând în același timp performanță bună. Implementează kerneluri optimizate pentru diverse arhitecturi CPU și suportă gestionarea de bază a cache-ului KV pentru generarea eficientă de token-uri.

<Tip title="Cum Funcționează Cuantificarea llama.cpp">
Cuantificarea în llama.cpp reduce precizia ponderilor modelului de la puncte mobile pe 32-bit sau 16-bit la formate de precizie mai mică precum întregi pe 8-bit (INT8), 4-bit sau chiar mai mic. Aceasta reduce semnificativ utilizarea memoriei și îmbunătățește viteza de inferență cu pierderi minime de calitate.

Caracteristicile cheie de cuantificare în llama.cpp includ:
1. **Multiple Niveluri de Cuantificare**: Suportă cuantificare pe 8-bit, 4-bit, 3-bit și chiar 2-bit
2. **Format GGML/GGUF**: Folosește formate tensoriale personalizate optimizate pentru inferența cuantificată
3. **Precizie Mixtă**: Poate aplica diferite niveluri de cuantificare la diferite părți ale modelului
4. **Optimizări Specifice Hardware**: Include căi de cod optimizate pentru diverse arhitecturi CPU (AVX2, AVX-512, NEON)

Această abordare permite rularea modelelor cu miliarde de parametri pe hardware de consum cu memorie limitată, făcând-o perfectă pentru implementări locale și dispozitive de margine.
</Tip>

### Implementarea și Integrarea

Să trecem la diferențele de implementare și integrare între framework-uri.

**TGI** excelează în implementarea la nivel enterprise cu caracteristicile sale gata pentru producție. Vine cu suport Kubernetes integrat și include tot ce ai nevoie pentru rularea în producție, precum monitorizarea prin Prometheus și Grafana, scalarea automată și caracteristici cuprinzătoare de siguranță. Sistemul include, de asemenea, logare de nivel enterprise și diverse măsuri de protecție precum filtrarea conținutului și limitarea ratei pentru a menține implementarea sigură și stabilă.

**vLLM** adoptă o abordare mai flexibilă, prietenoasă cu dezvoltatorii pentru implementare. Este construit cu Python în nucleu și poate înlocui cu ușurință API-ul OpenAI în aplicațiile tale existente. Framework-ul se concentrează pe livrarea performanței brute și poate fi personalizat pentru a se potrivi nevoilor tale specifice. Funcționează deosebit de bine cu Ray pentru gestionarea clusterelor, făcându-l o alegere excelentă când ai nevoie de performanță înaltă și adaptabilitate.

**llama.cpp** prioritizează simplitatea și portabilitatea. Implementarea sa de server este ușoară și poate rula pe o gamă largă de hardware, de la servere puternice la laptopuri de consum și chiar unele dispozitive mobile de înaltă performanță. Cu dependențe minime și un nucleu C/C++ simplu, este ușor de implementat în medii unde instalarea framework-urilor Python ar fi provocatoare. Serverul oferă un API compatibil cu OpenAI menținând în același timp un amprentă de resurse mult mai mică decât alte soluții.

## Începerea

Să explorăm cum să folosim aceste framework-uri pentru implementarea LLM-urilor, începând cu instalarea și configurarea de bază.

### Instalarea și Configurarea de Bază

<hfoptions id="inference-frameworks" >

<hfoption value="tgi" label="TGI">

TGI este ușor de instalat și utilizat, cu integrare profundă în ecosistemul Hugging Face.

Mai întâi, lansează serverul TGI folosind Docker:

```sh
docker run --gpus all \
    --shm-size 1g \
    -p 8080:80 \
    -v ~/.cache/huggingface:/data \
    ghcr.io/huggingface/text-generation-inference:latest \
    --model-id HuggingFaceTB/SmolLM2-360M-Instruct
```

Apoi interacționează cu acesta folosind InferenceClient de la Hugging Face:

```python
from huggingface_hub import InferenceClient

# Inițializează clientul pointând către endpoint-ul TGI
client = InferenceClient(
    model="http://localhost:8080",  # URL către serverul TGI
)

# Generarea de text
response = client.text_generation(
    "Spune-mi o poveste",
    max_new_tokens=100,
    temperature=0.7,
    top_p=0.95,
    details=True,
    stop_sequences=[],
)
print(response.generated_text)

# Pentru format de chat
response = client.chat_completion(
    messages=[
        {"role": "system", "content": "Ești un asistent util."},
        {"role": "user", "content": "Spune-mi o poveste"},
    ],
    max_tokens=100,
    temperature=0.7,
    top_p=0.95,
)
print(response.choices[0].message.content)
```

Alternativ, poți folosi clientul OpenAI:

```python
from openai import OpenAI

# Inițializează clientul pointând către endpoint-ul TGI
client = OpenAI(
    base_url="http://localhost:8080/v1",  # Asigură-te să incluzi /v1
    api_key="not-needed",  # TGI nu necesită o cheie API în mod implicit
)

# Completarea de chat
response = client.chat.completions.create(
    model="HuggingFaceTB/SmolLM2-360M-Instruct",
    messages=[
        {"role": "system", "content": "Ești un asistent util."},
        {"role": "user", "content": "Spune-mi o poveste"},
    ],
    max_tokens=100,
    temperature=0.7,
    top_p=0.95,
)
print(response.choices[0].message.content)
```
</hfoption>
<hfoption value="llama.cpp" label="llama.cpp">

llama.cpp este ușor de instalat și utilizat, necesitând dependențe minime și suportând atât inferența CPU cât și GPU.

Mai întâi, instalează și construiește llama.cpp:

```sh
# Clonează depozitul
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp

# Construiește proiectul
make

# Descarcă modelul SmolLM2-1.7B-Instruct-GGUF
curl -L -O https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct-GGUF/resolve/main/smollm2-1.7b-instruct.Q4_K_M.gguf
```

Apoi, lansează serverul (cu compatibilitate API OpenAI):

```sh
# Pornește serverul
./server \
    -m smollm2-1.7b-instruct.Q4_K_M.gguf \
    --host 0.0.0.0 \
    --port 8080 \
    -c 4096 \
    --n-gpu-layers 0  # Setează la un număr mai mare pentru a folosi GPU
```

Interacționează cu serverul folosind InferenceClient de la Hugging Face:

```python
from huggingface_hub import InferenceClient

# Inițializează clientul pointând către serverul llama.cpp
client = InferenceClient(
    model="http://localhost:8080/v1",  # URL către serverul llama.cpp
    token="sk-no-key-required",  # serverul llama.cpp necesită acest placeholder
)

# Generarea de text
response = client.text_generation(
    "Spune-mi o poveste",
    max_new_tokens=100,
    temperature=0.7,
    top_p=0.95,
    details=True,
)
print(response.generated_text)

# Pentru format de chat
response = client.chat_completion(
    messages=[
        {"role": "system", "content": "Ești un asistent util."},
        {"role": "user", "content": "Spune-mi o poveste"},
    ],
    max_tokens=100,
    temperature=0.7,
    top_p=0.95,
)
print(response.choices[0].message.content)
```

Alternativ, poți folosi clientul OpenAI:

```python
from openai import OpenAI

# Inițializează clientul pointând către serverul llama.cpp
client = OpenAI(
    base_url="http://localhost:8080/v1",
    api_key="sk-no-key-required",  # serverul llama.cpp necesită acest placeholder
)

# Completarea de chat
response = client.chat.completions.create(
    model="smollm2-1.7b-instruct",  # Identificatorul modelului poate fi orice deoarece serverul încarcă doar un model
    messages=[
        {"role": "system", "content": "Ești un asistent util."},
        {"role": "user", "content": "Spune-mi o poveste"},
    ],
    max_tokens=100,
    temperature=0.7,
    top_p=0.95,
)
print(response.choices[0].message.content)
```
</hfoption>
<hfoption value="vllm" label="vLLM">

vLLM este ușor de instalat și utilizat, cu atât compatibilitatea API OpenAI cât și o interfață Python nativă.

Mai întâi, lansează serverul compatibil OpenAI vLLM:

```sh
python -m vllm.entrypoints.openai.api_server \
    --model HuggingFaceTB/SmolLM2-360M-Instruct \
    --host 0.0.0.0 \
    --port 8000
```

Apoi interacționează cu acesta folosind InferenceClient de la Hugging Face:

```python
from huggingface_hub import InferenceClient

# Inițializează clientul pointând către endpoint-ul vLLM
client = InferenceClient(
    model="http://localhost:8000/v1",  # URL către serverul vLLM
)

# Generarea de text
response = client.text_generation(
    "Spune-mi o poveste",
    max_new_tokens=100,
    temperature=0.7,
    top_p=0.95,
    details=True,
)
print(response.generated_text)

# Pentru format de chat
response = client.chat_completion(
    messages=[
        {"role": "system", "content": "Ești un asistent util."},
        {"role": "user", "content": "Spune-mi o poveste"},
    ],
    max_tokens=100,
    temperature=0.7,
    top_p=0.95,
)
print(response.choices[0].message.content)
```

Alternativ, poți folosi clientul OpenAI:

```python
from openai import OpenAI

# Inițializează clientul pointând către endpoint-ul vLLM
client = OpenAI(
    base_url="http://localhost:8000/v1",
    api_key="not-needed",  # vLLM nu necesită o cheie API în mod implicit
)

# Completarea de chat
response = client.chat.completions.create(
    model="HuggingFaceTB/SmolLM2-360M-Instruct",
    messages=[
        {"role": "system", "content": "Ești un asistent util."},
        {"role": "user", "content": "Spune-mi o poveste"},
    ],
    max_tokens=100,
    temperature=0.7,
    top_p=0.95,
)
print(response.choices[0].message.content)
```
</hfoption>

</hfoptions>

### Generarea de Bază a Textului

Să ne uităm la exemple de generare de text cu framework-urile:

<hfoptions id="inference-frameworks" >

<hfoption value="tgi" label="TGI">

Mai întâi, implementează TGI cu parametri avansați:
```sh
docker run --gpus all \
    --shm-size 1g \
    -p 8080:80 \
    -v ~/.cache/huggingface:/data \
    ghcr.io/huggingface/text-generation-inference:latest \
    --model-id HuggingFaceTB/SmolLM2-360M-Instruct \
    --max-total-tokens 4096 \
    --max-input-length 3072 \
    --max-batch-total-tokens 8192 \
    --waiting-served-ratio 1.2
```

Folosește InferenceClient pentru generarea flexibilă de text:
```python
from huggingface_hub import InferenceClient

client = InferenceClient(model="http://localhost:8080")

# Exemplu de parametri avansați
response = client.chat_completion(
    messages=[
        {"role": "system", "content": "Ești un povestitor creativ."},
        {"role": "user", "content": "Scrie o poveste creativă"},
    ],
    temperature=0.8,
    max_tokens=200,
    top_p=0.95,
)
print(response.choices[0].message.content)

# Generarea brută de text
response = client.text_generation(
    "Scrie o poveste creativă despre explorarea spațiului",
    max_new_tokens=200,
    temperature=0.8,
    top_p=0.95,
    repetition_penalty=1.1,
    do_sample=True,
    details=True,
)
print(response.generated_text)
```

Sau folosește clientul OpenAI:
```python
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8080/v1", api_key="not-needed")

# Exemplu de parametri avansați
response = client.chat.completions.create(
    model="HuggingFaceTB/SmolLM2-360M-Instruct",
    messages=[
        {"role": "system", "content": "Ești un povestitor creativ."},
        {"role": "user", "content": "Scrie o poveste creativă"},
    ],
    temperature=0.8,  # Mai mare pentru mai multă creativitate
)
print(response.choices[0].message.content)
```
</hfoption>
<hfoption value="llama.cpp" label="llama.cpp">

Pentru llama.cpp, poți seta parametri avansați când lansezi serverul:

```sh
./server \
    -m smollm2-1.7b-instruct.Q4_K_M.gguf \
    --host 0.0.0.0 \
    --port 8080 \
    -c 4096 \            # Dimensiunea contextului
    --threads 8 \        # Thread-uri CPU de folosit
    --batch-size 512 \   # Dimensiunea lotului pentru evaluarea prompt-ului
    --n-gpu-layers 0     # Straturi GPU (0 = doar CPU)
```

Folosește InferenceClient:

```python
from huggingface_hub import InferenceClient

client = InferenceClient(model="http://localhost:8080/v1", token="sk-no-key-required")

# Exemplu de parametri avansați
response = client.chat_completion(
    messages=[
        {"role": "system", "content": "Ești un povestitor creativ."},
        {"role": "user", "content": "Scrie o poveste creativă"},
    ],
    temperature=0.8,
    max_tokens=200,
    top_p=0.95,
)
print(response.choices[0].message.content)

# Pentru generarea directă de text
response = client.text_generation(
    "Scrie o poveste creativă despre explorarea spațiului",
    max_new_tokens=200,
    temperature=0.8,
    top_p=0.95,
    repetition_penalty=1.1,
    details=True,
)
print(response.generated_text)
```

Sau folosește clientul OpenAI pentru generare cu control asupra parametrilor de eșantionare:

```python
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8080/v1", api_key="sk-no-key-required")

# Exemplu de parametri avansați
response = client.chat.completions.create(
    model="smollm2-1.7b-instruct",
    messages=[
        {"role": "system", "content": "Ești un povestitor creativ."},
        {"role": "user", "content": "Scrie o poveste creativă"},
    ],
    temperature=0.8,  # Mai mare pentru mai multă creativitate
    top_p=0.95,  # Probabilitatea eșantionării nucleus
    frequency_penalty=0.5,  # Reduce repetarea token-urilor frecvente
    presence_penalty=0.5,  # Reduce repetarea prin penalizarea token-urilor deja prezente
    max_tokens=200,  # Lungimea maximă de generare
)
print(response.choices[0].message.content)
```

Poți folosi, de asemenea, biblioteca nativă llama.cpp pentru un control și mai mare:

```python
# Folosind pachetul llama-cpp-python pentru accesul direct la model
from llama_cpp import Llama

# Încarcă modelul
llm = Llama(
    model_path="smollm2-1.7b-instruct.Q4_K_M.gguf",
    n_ctx=4096,  # Dimensiunea ferestrei de context
    n_threads=8,  # Thread-uri CPU
    n_gpu_layers=0,  # Straturi GPU (0 = doar CPU)
)

# Formatează prompt-ul conform formatului așteptat al modelului
prompt = """<|im_start|>system
Ești un povestitor creativ.
<|im_end|>
<|im_start|>user
Scrie o poveste creativă
<|im_end|>
<|im_start|>assistant
"""

# Generează răspunsul cu control precis al parametrilor
output = llm(
    prompt,
    max_tokens=200,
    temperature=0.8,
    top_p=0.95,
    frequency_penalty=0.5,
    presence_penalty=0.5,
    stop=["<|im_end|>"],
)

print(output["choices"][0]["text"])
```
</hfoption>
<hfoption value="vllm" label="vLLM">

Pentru utilizarea avansată cu vLLM, poți folosi InferenceClient:

```python
from huggingface_hub import InferenceClient

client = InferenceClient(model="http://localhost:8000/v1")

# Exemplu de parametri avansați
response = client.chat_completion(
    messages=[
        {"role": "system", "content": "Ești un povestitor creativ."},
        {"role": "user", "content": "Scrie o poveste creativă"},
    ],
    temperature=0.8,
    max_tokens=200,
    top_p=0.95,
)
print(response.choices[0].message.content)

# Pentru generarea directă de text
response = client.text_generation(
    "Scrie o poveste creativă despre explorarea spațiului",
    max_new_tokens=200,
    temperature=0.8,
    top_p=0.95,
    details=True,
)
print(response.generated_text)
```

Poți folosi, de asemenea, clientul OpenAI:

```python
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="not-needed")

# Exemplu de parametri avansați
response = client.chat.completions.create(
    model="HuggingFaceTB/SmolLM2-360M-Instruct",
    messages=[
        {"role": "system", "content": "Ești un povestitor creativ."},
        {"role": "user", "content": "Scrie o poveste creativă"},
    ],
    temperature=0.8,
    top_p=0.95,
    max_tokens=200,
)
print(response.choices[0].message.content)
```

vLLM oferă, de asemenea, o interfață Python nativă cu control fin:

```python
from vllm import LLM, SamplingParams

# Inițializează modelul cu parametri avansați
llm = LLM(
    model="HuggingFaceTB/SmolLM2-360M-Instruct",
    gpu_memory_utilization=0.85,
    max_num_batched_tokens=8192,
    max_num_seqs=256,
    block_size=16,
)

# Configurează parametrii de eșantionare
sampling_params = SamplingParams(
    temperature=0.8,  # Mai mare pentru mai multă creativitate
    top_p=0.95,  # Consideră masa de probabilitate de top 95%
    max_tokens=100,  # Lungimea maximă
    presence_penalty=1.1,  # Reduce repetarea
    frequency_penalty=1.1,  # Reduce repetarea
    stop=["\n\n", "###"],  # Secvențe de oprire
)

# Generează text
prompt = "Scrie o poveste creativă"
outputs = llm.generate(prompt, sampling_params)
print(outputs[0].outputs[0].text)

# Pentru interacțiuni în stil chat
chat_prompt = [
    {"role": "system", "content": "Ești un povestitor creativ."},
    {"role": "user", "content": "Scrie o poveste creativă"},
]
formatted_prompt = llm.get_chat_template()(
    chat_prompt
)  # Folosește șablonul de chat al modelului
outputs = llm.generate(formatted_prompt, sampling_params)
print(outputs[0].outputs[0].text)
```
</hfoption>

</hfoptions>

## Controlul Avansat al Generării

### Selecția Token-urilor și Eșantionarea

Procesul de generare a textului implică selectarea următorului token la fiecare pas. Acest proces de selecție poate fi controlat prin diverși parametri:

1. **Logit-uri Brute**: Probabilitățile inițiale de ieșire pentru fiecare token
2. **Temperatura**: Controlează aleatoriul în selecție (mai mare = mai creativ)
3. **Eșantionarea Top-p (Nucleus)**: Filtrează la token-urile de top care alcătuiesc X% din masa de probabilitate
4. **Filtrarea Top-k**: Limitează selecția la k token-uri cele mai probabile

Iată cum să configurezi acești parametri:

<hfoptions id="inference-frameworks" >

<hfoption value="tgi" label="TGI">

```python
client.generate(
    "Scrie o poveste creativă",
    temperature=0.8,  # Mai mare pentru mai multă creativitate
    top_p=0.95,  # Consideră masa de probabilitate de top 95%
    top_k=50,  # Consideră top 50 de token-uri
    max_new_tokens=100,  # Lungimea maximă
    repetition_penalty=1.1,  # Reduce repetarea
)
```
</hfoption>
<hfoption value="llama.cpp" label="llama.cpp">

```python
# Prin compatibilitatea API OpenAI
response = client.completions.create(
    model="smollm2-1.7b-instruct",  # Numele modelului (poate fi orice string pentru serverul llama.cpp)
    prompt="Scrie o poveste creativă",
    temperature=0.8,  # Mai mare pentru mai multă creativitate
    top_p=0.95,  # Consideră masa de probabilitate de top 95%
    frequency_penalty=1.1,  # Reduce repetarea
    presence_penalty=0.1,  # Reduce repetarea
    max_tokens=100,  # Lungimea maximă
)

# Prin accesul direct llama-cpp-python
output = llm(
    "Scrie o poveste creativă",
    temperature=0.8,
    top_p=0.95,
    top_k=50,
    max_tokens=100,
    repeat_penalty=1.1,
)
```
</hfoption>
<hfoption value="vllm" label="vLLM">

```python
params = SamplingParams(
    temperature=0.8,  # Mai mare pentru mai multă creativitate
    top_p=0.95,  # Consideră masa de probabilitate de top 95%
    top_k=50,  # Consideră top 50 de token-uri
    max_tokens=100,  # Lungimea maximă
    presence_penalty=0.1,  # Reduce repetarea
)
llm.generate("Scrie o poveste creativă", sampling_params=params)
```
</hfoption>

</hfoptions>

### Controlul Repetării

Ambele framework-uri oferă modalități de a preveni generarea repetitivă de text:

<hfoptions id="inference-frameworks" >

<hfoption value="tgi" label="TGI">
```python
client.generate(
    "Scrie un text variat",
    repetition_penalty=1.1,  # Penalizează token-urile repetate
    no_repeat_ngram_size=3,  # Previne repetarea de 3-grame
)
```
</hfoption>
<hfoption value="llama.cpp" label="llama.cpp">

```python
# Prin API OpenAI
response = client.completions.create(
    model="smollm2-1.7b-instruct",
    prompt="Scrie un text variat",
    frequency_penalty=1.1,  # Penalizează token-urile frecvente
    presence_penalty=0.8,  # Penalizează token-urile deja prezente
)

# Prin biblioteca directă
output = llm(
    "Scrie un text variat",
    repeat_penalty=1.1,  # Penalizează token-urile repetate
    frequency_penalty=0.5,  # Penalitate de frecvență adițională
    presence_penalty=0.5,  # Penalitate de prezență adițională
)
```
</hfoption>
<hfoption value="vllm" label="vLLM">

```python
params = SamplingParams(
    presence_penalty=0.1,  # Penalizează prezența token-urilor
    frequency_penalty=0.1,  # Penalizează frecvența token-urilor
)
```
</hfoption>

</hfoptions>

### Controlul Lungimii și Secvențele de Oprire

Poți controla lungimea generării și specifica când să se oprească:

<hfoptions id="inference-frameworks" >

<hfoption value="tgi" label="TGI">
```python
client.generate(
    "Generează un paragraf scurt",
    max_new_tokens=100,
    min_new_tokens=10,
    stop_sequences=["\n\n", "###"],
)
```
</hfoption>
<hfoption value="llama.cpp" label="llama.cpp">

```python
# Prin API OpenAI
response = client.completions.create(
    model="smollm2-1.7b-instruct",
    prompt="Generează un paragraf scurt",
    max_tokens=100,
    stop=["\n\n", "###"],
)

# Prin biblioteca directă
output = llm("Generează un paragraf scurt", max_tokens=100, stop=["\n\n", "###"])
```
</hfoption>
<hfoption value="vllm" label="vLLM">

```python
params = SamplingParams(
    max_tokens=100,
    min_tokens=10,
    stop=["###", "\n\n"],
    ignore_eos=False,
    skip_special_tokens=True,
)
```
</hfoption>

</hfoptions>

## Gestionarea Memoriei

Ambele framework-uri implementează tehnici avansate de gestionare a memoriei pentru inferența eficientă.

<hfoptions id="inference-frameworks" >

<hfoption value="tgi" label="TGI">
TGI folosește Flash Attention 2 și procesarea continuă în loturi:

```sh
# Implementarea Docker cu optimizarea memoriei
docker run --gpus all -p 8080:80 \
    --shm-size 1g \
    ghcr.io/huggingface/text-generation-inference:latest \
    --model-id HuggingFaceTB/SmolLM2-1.7B-Instruct \
    --max-batch-total-tokens 8192 \
    --max-input-length 4096
```
</hfoption>
<hfoption value="llama.cpp" label="llama.cpp">

llama.cpp folosește cuantificarea și dispunerea optimată a memoriei:

```sh
# Server cu optimizări de memorie
./server \
    -m smollm2-1.7b-instruct.Q4_K_M.gguf \
    --host 0.0.0.0 \
    --port 8080 \
    -c 2048 \               # Dimensiunea contextului
    --threads 4 \           # Thread-uri CPU
    --n-gpu-layers 32 \     # Folosește mai multe straturi GPU pentru modele mai mari
    --mlock \               # Blochează memoria pentru a preveni swapping-ul
    --cont-batching         # Activează procesarea continuă în loturi
```

Pentru modele prea mari pentru GPU-ul tău, poți folosi offloading-ul CPU:

```sh
./server \
    -m smollm2-1.7b-instruct.Q4_K_M.gguf \
    --n-gpu-layers 20 \     # Păstrează primele 20 de straturi pe GPU
    --threads 8             # Folosește mai multe thread-uri CPU pentru straturile CPU
```
</hfoption>
<hfoption value="vllm" label="vLLM">

vLLM folosește PagedAttention pentru gestionarea optimă a memoriei:

```python
from vllm.engine.arg_utils import AsyncEngineArgs

engine_args = AsyncEngineArgs(
    model="HuggingFaceTB/SmolLM2-1.7B-Instruct",
    gpu_memory_utilization=0.85,
    max_num_batched_tokens=8192,
    block_size=16,
)

llm = LLM(engine_args=engine_args)
```
</hfoption>

</hfoptions>

## Resurse

- [Documentația Text Generation Inference](https://huggingface.co/docs/text-generation-inference)
- [Depozitul GitHub TGI](https://github.com/huggingface/text-generation-inference)
- [Documentația vLLM](https://vllm.readthedocs.io/)
- [Depozitul GitHub vLLM](https://github.com/vllm-project/vllm)
- [Lucrarea PagedAttention](https://arxiv.org/abs/2309.06180)
- [Depozitul GitHub llama.cpp](https://github.com/ggerganov/llama.cpp)
- [Depozitul llama-cpp-python](https://github.com/abetlen/llama-cpp-python)

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter2/8.mdx" />

### Tokenizatoare[[tokenizatoare]]
https://huggingface.co/learn/course/ro/chapter2/4.md

# Tokenizatoare[[tokenizatoare]]

{#if fw === 'pt'}

<CourseFloatingBanner chapter={2}
  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/chapter2/section4_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section4_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={2}
  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/chapter2/section4_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section4_tf.ipynb"},
]} />

{/if}

<Youtube id="VFp38yj8h3A"/>


Tokenizerii sunt unele dintre componentele de bază ale pipeline-ului NLP. Acestea au un singur scop: să transforme textul în date care pot fi prelucrate de model. Modelele pot procesa doar numere, astfel încât tokenizerii trebuie să convertească intrările noastre de text în date numerice. În această secțiune, vom explora exact ce se întâmplă în pipeline-ul de tokenizare. 

În sarcinile NLP, datele care sunt în general prelucrate sunt text brut. Iată un exemplu de astfel de text:

```
Jim Henson was a puppeteer

```

Cu toate acestea, modelele pot procesa doar numere, deci trebuie să găsim o modalitate de a converti textul brut în numere. Asta fac tokenizerii și există o mulțime de modalități de a face acest lucru. Scopul este de a găsi cea mai relevantă reprezentare - adică cea care are cel mai mare sens pentru model - și, dacă este posibil, cea mai mică reprezentare.

Să aruncăm o privire la câteva exemple de algoritmi de tokenizare și să încercăm să răspundem la unele dintre întrebările pe care le puteți avea despre tokenizare.



## Word-based[[word-based]]

<Youtube id="nhJxYji1aho"/>

 
Primul tip de tokenizator care îmi vine în minte este _word-based_. În general, este foarte ușor de configurat și de utilizat, cu doar câteva reguli, și adesea produce rezultate satisfăcătoare. De exemplu, în imaginea de mai jos, obiectivul este de a împărți textul brut în cuvinte și de a găsi o reprezentare numerică pentru fiecare dintre ele:

<div class="flex justify-center">
  <img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/word_based_tokenization.svg" alt="An example of word-based tokenization."/>
  <img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/word_based_tokenization-dark.svg" alt="An example of word-based tokenization."/>
</div>

 
Există diferite moduri de a împărți textul. De exemplu, am putea folosi spațiu pentru a tokeniza textul în cuvinte prin aplicarea funcției `split()` din Python:

 
```py
tokenized_text = "Jim Henson was a puppeteer".split()
print(tokenized_text)
```

```python out
['Jim', 'Henson', 'was', 'a', 'puppeteer']
```

Există, de asemenea, variații ale tokenizatoarelor de cuvinte care au reguli suplimentare pentru punctuație. Cu acest tip de tokenizator, putem ajunge la "vocabulare" destul de mari, unde un vocabular este definit de numărul total de token-uri independente pe care le avem în corpus.

Fiecărui cuvânt i se atribuie un ID, începând de la 0 și mergând până la dimensiunea vocabularului. Modelul utilizează aceste ID-uri pentru a identifica fiecare cuvânt.

Dacă dorim să acoperim complet o limbă cu un tokenizator bazat pe cuvinte, va trebui să avem un identificator pentru fiecare cuvânt din limbă, ceea ce va genera o cantitate uriașă de token-uri. De exemplu, există peste 500 000 de cuvinte în limba engleză, astfel încât, pentru a construi o hartă de la fiecare cuvânt la un ID de intrare, ar trebui să ținem evidența unui număr atât de mare de ID-uri. În plus, cuvinte precum "dog" sunt reprezentate diferit de cuvinte precum "dogs", iar modelul nu va avea inițial nicio modalitate de a ști că "dog" și "dogs" sunt similare: va identifica cele două cuvinte ca neavând legătură. Același lucru este valabil și pentru alte cuvinte similare, precum "run" și "running", pe care modelul nu le va vedea inițial ca fiind similare.

În cele din urmă, avem nevoie de un token personalizat pentru a reprezenta cuvintele care nu se află în vocabularul nostru. Acesta este cunoscut sub numele de token "necunoscut", adesea reprezentat ca "[UNK]" sau "&lt;unk&gt;". În general, este un indiciu rău dacă vedeți că tokenizatorul produce o mulțime de astfel de token-uri, deoarece nu a fost capabil să recupereze reprezentarea sensibilă a unui cuvânt și veți pierde informații pe parcurs. Scopul elaborării vocabularului este de a face în așa fel încât tokenizatorul să tokenizeze cât mai puține cuvinte posibil în tokenul necunoscut.

O modalitate de a reduce numărul de token-uri necunoscute este de a merge un nivel mai adânc, folosind un tokenizator _character-based_.

## Character-based[[character-based]]

<Youtube id="ssLq_EK2jLE"/>


Tokenizatoarele character-based împart textul în caractere, și nu în cuvinte. Acest lucru are două beneficii principale:

- Dimensiunea vocabularului este mult mai mică.
- Există mult mai puține token-uri în afara vocabularului (necunoscute), deoarece fiecare cuvânt poate fi construit din caractere.

Dar și aici apar unele probleme legate de spații și punctuație:


<div class="flex justify-center">
  <img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/character_based_tokenization.svg" alt="An example of character-based tokenization."/>
  <img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/character_based_tokenization-dark.svg" alt="An example of character-based tokenization."/>
</div>

 
Nici această abordare nu este perfectă. Deoarece reprezentarea se bazează acum pe caractere și nu pe cuvinte, s-ar putea spune că, din punct de vedere intuitiv, este mai puțin relevantă: fiecare caracter nu înseamnă mult în sine, în timp ce în cazul cuvintelor situația este diferită. Totuși, acest lucru variază din nou în funcție de limbă; în chineză, de exemplu, fiecare caracter conține mai multe informații decât un caracter într-o limbă latină.

Un alt lucru care trebuie luat în considerare este faptul că ne vom trezi cu o cantitate foarte mare de token-uri care vor fi prelucrate de modelul nostru: în timp ce un cuvânt ar fi un singur token cu un tokenizator bazat pe cuvinte, acesta se poate transforma cu ușurință în 10 sau mai multe token-uri atunci când este convertit în caractere.

Pentru a obține ce este mai bun din ambele lumi, putem utiliza o a treia tehnică care combină cele două abordări: *subword tokenization*.

## Subword tokenization[[subword-tokenization]]

<Youtube id="zHvTiHr506c"/>

Algoritmii "Subword Tokenization " se bazează pe principiul conform căruia cuvintele utilizate frecvent nu ar trebui să fie împărțite în subcuvinte mai mici, dar cuvintele rare ar trebui să fie descompuse în subcuvinte semnificative.

De exemplu, " annoyingly " ar putea fi considerat un cuvânt rar și ar putea fi descompus în " annoying " și " ly ". Este probabil ca ambele să apară mai frecvent ca subcuvinte de sine stătătoare, în timp ce, în același timp, sensul cuvântului " annoyingly " este păstrat de sensul compus al cuvintelor " annoying " și " ly ".

Iată un exemplu care arată modul în care un algoritm de subword tokenization ar tokeniza secvența "Let's do tokenization!":

<div class="flex justify-center">
  <img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/bpe_subword.svg" alt="A subword tokenization algorithm."/>
  <img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/bpe_subword-dark.svg" alt="A subword tokenization algorithm."/>
</div>

Aceste subcuvinte oferă în cele din urmă o mulțime de semnificații semantice: în exemplul de mai sus, "tokenization" a fost împărțit în "token" și "ization", două cuvinte care au o semnificație semantică, fiind în același timp eficiente din punct de vedere al spațiului (sunt necesare doar două cuvinte pentru a reprezenta un cuvânt lung). Acest lucru ne permite să avem o acoperire relativ bună cu vocabulare de dimensiuni mici și aproape fără token-uri necunoscute.

Această abordare este utilă în special în limbile aglutinative, cum ar fi turca, în care se pot forma cuvinte complexe (aproape) arbitrar de lungi prin combinarea subcuvintelor.

### Și nu numai![[și-nu-numai]]

Nu este surprinzător faptul că există mult mai multe tehnici. Iată câteva:

- BPE la nivel de byte, utilizată în GPT-2
- WordPiece, utilizată în BERT
- SentencePiece sau Unigram, utilizate în mai multe modele multilingve

Acum ar trebui să cunoașteți destul de bine cum funcționează tokenizatoarele pentru a începe să utilizați API-ul.

## Încărcarea și salvarea[[încărcarea-și-salvarea]]

Încărcarea și salvarea tokenizatoarelor este la fel de simplă ca în cazul modelelor. De fapt, se bazează pe aceleași două metode: `from_pretrained()` și `save_pretrained()`. Aceste metode vor încărca sau salva algoritmul utilizat de tokenizator (un pic ca *arhitectura* modelului), precum și vocabularul său (un pic ca *weight-urile* modelului).

Încărcarea tokenizatorului BERT antrenat cu același checkpoint ca BERT se face în același mod ca și încărcarea modelului, cu excepția faptului că folosim clasa `BertTokenizer`:

```py
from transformers import BertTokenizer

tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
```

{#if fw === 'pt'}
Similar cu `AutoModel`, clasa `AutoTokenizer` va prelua clasa tokenizer corespunzătoare din bibliotecă pe baza numelui checkpoint-ului și poate fi utilizată direct cu orice checkpoint:

{:else}
Similar cu `TFAutoModel`, clasa `AutoTokenizer` va prelua clasa tokenizer corespunzătoare din bibliotecă pe baza numelui checkpoint-ului și poate fi utilizată direct cu orice checkpoint:

{/if}

```py
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
```

Acum putem utiliza tokenizatorul așa cum am arătat în secțiunea anterioară:

```python
tokenizer("Using a Transformer network is simple")
```

```python out
{'input_ids': [101, 7993, 170, 11303, 1200, 2443, 1110, 3014, 102],
 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0],
 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}
```

Salvarea unui tokenizer este identică cu salvarea unui model:

```py
tokenizer.save_pretrained("directory_on_my_computer")
```

Vom vorbi mai mult despre `token_type_ids` în [Capitolul 3](/course/chapter3) și vom explica cheia `attention_mask` puțin mai târziu. Mai întâi, să vedem cum sunt generate `input_ids`. Pentru a face acest lucru, va trebui să ne uităm la metodele intermediare ale tokenizatorului.

## Codificarea[[codificarea]]

<Youtube id="Yffk5aydLzg"/>

Traducerea textului în numere este cunoscută sub numele de codificare. Codificarea se face într-un proces în două etape: tokenizarea, urmată de conversia în ID-uri de intrare.

Traducerea textului în numere este cunoscută sub numele de codificare. Codificarea se face într-un proces în două etape: tokenizarea, urmată de conversia în ID-uri de intrare.

Al doilea pas este să convertim aceste token-uri în numere, astfel încât să putem construi un tensor din ele și să le introducem în model. Pentru a face acest lucru, tokenizatorul are un *vocabular*, care este partea pe care o descărcăm atunci când îl instanțiem cu metoda `from_pretrained()`. Din nou, trebuie să folosim același vocabular utilizat atunci când modelul a fost preantrenat.

Pentru a înțelege mai bine cele două etape, le vom explora separat. Rețineți că vom utiliza unele metode care efectuează părți ale pipeline-ului de tokenizare separat pentru a vă arăta rezultatele intermediare ale acestor etape, dar, în practică, ar trebui să apelați tokenizatorul direct pe intrările dvs. (așa cum se arată în secțiunea 2).

### Tokenizarea[[tokenizarea]]

Procesul de tokenizare este realizat prin metoda `tokenize()`:

```py
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")

sequence = "Using a Transformer network is simple"
tokens = tokenizer.tokenize(sequence)

print(tokens)
```

Rezultatul acestei metode este o listă de șiruri de caractere, sau token-uri:

```python out
['Using', 'a', 'transform', '##er', 'network', 'is', 'simple']
```

Acest tokenizator este un tokenizator de subcuvinte: împarte cuvintele până când obține token-uri care pot fi reprezentate de vocabularul său. Acesta este cazul aici cu `transformer`, care este împărțit în două token-uri: `transform` și `##er`

### De la token-uri la ID-uri de intrare[[de-la-token-uri-la-id-uri-de-intrare]]

Conversia în ID-uri de intrare este gestionată de metoda de tokenizare `convert_tokens_to_ids()`:

```py
ids = tokenizer.convert_tokens_to_ids(tokens)

print(ids)
```

```python out
[7993, 170, 11303, 1200, 2443, 1110, 3014]
```

Aceste rezultate, odată convertite în tensorul framework-ului corespunzător, pot fi apoi utilizate ca intrări într-un model, așa cum am văzut mai devreme în acest capitol.

<Tip>

✏️ **Încercați!** Replicați ultimii doi pași (tokenizarea și conversia în ID-uri de intrare) pe propozițiile de intrare pe care le-am folosit în secțiunea 2 ("I've been waiting for a HuggingFace course my whole life." și "I hate this so much!"). Verificați dacă obțineți aceleași ID-uri de intrare pe care le-am obținut mai devreme! 

</Tip>

## Decodificare[[decodificare]]

*Decodificarea* este inversă: din indicii vocabularului, dorim să obținem un șir de caractere. Acest lucru poate fi realizat cu metoda `decode()` după cum urmează:

```py
decoded_string = tokenizer.decode([7993, 170, 11303, 1200, 2443, 1110, 3014])
print(decoded_string)
```

```python out
'Using a Transformer network is simple'
```

Rețineți că metoda `decode` nu numai că convertește indicii înapoi în token-uri, dar și grupează token-urile care fac parte din aceleași cuvinte pentru a produce o propoziție inteligibilă. Acest comportament va fi extrem de util atunci când vom utiliza modele care prezic text nou (fie text generat de un prompt, fie pentru probleme sequence-to-sequence, precum traducerea sau rezumarea).

Până acum ar trebui să înțelegeți operațiunile atomice pe care le poate gestiona un tokenizator: tokenizarea, conversia în ID-uri și conversia ID-urilor înapoi într-un șir. Cu toate acestea, am atins doar vârful icebergului. În secțiunea următoare, vom aborda limitele metodei noastre și vom vedea cum să le depășim.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter2/4.mdx" />

### Utilizarea de bază este completă![[utilizarea-de-bază-este-completă]]
https://huggingface.co/learn/course/ro/chapter2/7.md

# Utilizarea de bază este completă![[utilizarea-de-bază-este-completă]]

<CourseFloatingBanner
    chapter={2}
    classNames="absolute z-10 right-0 top-0"
/>
 
Ați făcut o treabă excelentă dacă ați urmat cursul până aici! Pentru a sintetiza, în acest capitol:

- Am învățat elementele de bază ale unui model Transformer.
- Am învățat ce formează un pipeline de tokenizare.
- Am văzut cum să utilizați un model Transformer în practică.
- Am învățat cum să folosiți un tokenizer pentru a converti textul în tensori care sunt inteligibili pentru model.
- Am configurat împreună un tokenizer și un model pentru a ajunge de la text la predicții.
- Am învățat limitările ID-urilor de intrare și am învățat despre attention masks.
- Ne-am antrenat cu ajutorul metodelor versatile și configurabile ale tokenizatorului.

De acum înainte, ar trebui să puteți naviga liber prin documentația 🤗 Transformers: vocabularul va suna familiar și ați văzut deja metodele pe care le veți utiliza în majoritatea timpului.

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter2/7.mdx" />

### În spatele pipeline-ului[[în-spatele-pipeline-ului]]
https://huggingface.co/learn/course/ro/chapter2/2.md

# În spatele pipeline-ului[[în-spatele-pipeline-ului]]

{#if fw === 'pt'}

<CourseFloatingBanner chapter={2}
  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/chapter2/section2_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section2_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={2}
  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/chapter2/section2_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter2/section2_tf.ipynb"},
]} />

{/if}

<Tip>
 
Aceasta este prima secțiune în care conținutul este ușor diferit în funcție de utilizarea PyTorch sau TensorFlow. Schimbați comutatorul din partea de sus a titlului pentru a selecta platforma pe care o preferați!
</Tip>

{#if fw === 'pt'}
<Youtube id="1pedAIvTWXk"/>
{:else}
<Youtube id="wVN12smEvqg"/>
{/if}

Să începem cu un exemplu complet, aruncând o privire la ceea ce s-a întâmplat în spate atunci când am executat următorul cod în [Capitolul 1](/course/chapter1):


```python
from transformers import pipeline

classifier = pipeline("sentiment-analysis")
classifier(
    [
        "I've been waiting for a HuggingFace course my whole life.",
        "I hate this so much!",
    ]
)
```

și am obținut:

```python out
[{'label': 'POSITIVE', 'score': 0.9598047137260437},
 {'label': 'NEGATIVE', 'score': 0.9994558095932007}]
```

După cum am văzut în [Capitolul 1](/course/chapter1), acest pipeline grupează trei etape: preprocesarea, trecerea intrărilor prin model și postprocesarea:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/full_nlp_pipeline.svg" alt="The full NLP pipeline: tokenization of text, conversion to IDs, and inference through the Transformer model and the model head."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/full_nlp_pipeline-dark.svg" alt="The full NLP pipeline: tokenization of text, conversion to IDs, and inference through the Transformer model and the model head."/>
</div>

Să trecem rapid prin fiecare dintre acestea.

## Preprocesarea cu un tokenizator[[preprocesarea-cu-un-tokenizator]]

La fel ca alte rețele neuronale, modelele Transformer nu pot procesa direct text brut, astfel încât primul pas al pipeline-ului nostru este de a converti intrările de text în numere pe care modelul le poate înțelege. Pentru a face acest lucru, folosim un *tokenizer*, care va fi responsabil pentru:

- Împărțirea datelor de intrare în cuvinte, subcuvinte sau simboluri (cum ar fi punctuația) care se numesc *tokens*
- Maparea fiecărui token într-un număr întreg
- Adăugarea de intrări suplimentare care pot fi utile pentru model

Toată această preprocesare trebuie efectuată exact în același mod ca atunci când modelul a fost preantrenat, așa că mai întâi trebuie să descărcăm aceste informații din [Model Hub](https://huggingface.co/models). Pentru a face acest lucru, folosim clasa `AutoTokenizer` și metoda sa `from_pretrained()`. Folosind numele checkpoint-ului modelului nostru, aceasta va prelua automat datele asociate cu tokenizer-ul modelului și le va stoca în cache (astfel încât acestea să fie descărcate doar prima dată când executați codul de mai jos).

Deoarece punctul de control implicit al pipeline-ului `sentiment-analysis` este `distilbert-base-uncased-finetuned-sst-2-english` (puteți vedea fișa modelului [aici](https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english)), executăm următoarele:

```python
from transformers import AutoTokenizer

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
```

Odată ce avem tokenizatorul, putem să îi transmitem direct propozițiile noastre și vom primi înapoi un dicționar care este gata să fie introdus în modelul nostru! Singurul lucru rămas de făcut este să convertim lista de ID-uri de intrare în tensori.

Puteți utiliza 🤗 Transformers fără a trebui să vă faceți griji cu privire la cadrul ML utilizat ca backend; ar putea fi PyTorch sau TensorFlow, sau Flax pentru unele modele. Cu toate acestea, modelele Transformer acceptă numai *tensori * ca intrare. Dacă este prima dată când auziți despre tensori, vă puteți gândi la ei ca la array-uri NumPy. Un array NumPy poate fi un scalar (0D), un vector (1D), o matrice (2D) sau poate avea mai multe dimensiuni. Este de fapt un tensor; tensorii altor cadre ML se comportă similar și sunt de obicei la fel de simplu de instanțiat ca și array-urile NumPy.

Pentru a specifica tipul de tensori pe care dorim să îi primim înapoi (PyTorch, TensorFlow sau NumPy simplu), folosim argumentul `return_tensors`:


{#if fw === 'pt'}
```python
raw_inputs = [
    "I've been waiting for a HuggingFace course my whole life.",
    "I hate this so much!",
]
inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")
print(inputs)
```
{:else}
```python
raw_inputs = [
    "I've been waiting for a HuggingFace course my whole life.",
    "I hate this so much!",
]
inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="tf")
print(inputs)
```
{/if}

 
Nu vă faceți încă griji cu privire la padding și trunchiere; le vom explica mai târziu. Principalele lucruri de reținut aici sunt că puteți trece o propoziție sau o listă de propoziții, precum și specificarea tipului de tensori pe care doriți să îi primiți înapoi (dacă nu este specificat niciun tip, veți primi o listă de liste ca rezultat).
{#if fw === 'pt'}

Iată cum arată rezultatele ca tensori PyTorch:

```python out
{
    'input_ids': tensor([
        [  101,  1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172, 2607,  2026,  2878,  2166,  1012,   102],
        [  101,  1045,  5223,  2023,  2061,  2172,   999,   102,     0,     0,     0,     0,     0,     0,     0,     0]
    ]), 
    'attention_mask': tensor([
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
    ])
}
```
{:else}

Iată cum arată rezultatele ca tensori TensorFlow:

```python out
{
    'input_ids': <tf.Tensor: shape=(2, 16), dtype=int32, numpy=
        array([
            [  101,  1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172,  2607,  2026,  2878,  2166,  1012,   102],
            [  101,  1045,  5223,  2023,  2061,  2172,   999,   102,     0,     0,     0,     0,     0,     0,     0,     0]
        ], dtype=int32)>, 
    'attention_mask': <tf.Tensor: shape=(2, 16), dtype=int32, numpy=
        array([
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
        ], dtype=int32)>
}
```
{/if}

 
Rezultatul în sine este un dicționar care conține două chei, `input_ids` și `attention_mask`. `input_ids` conține două rânduri de numere întregi (unul pentru fiecare propoziție) care sunt identificatorii unici ai simbolurilor din fiecare propoziție. Vom explica ce este `attention_mask` mai târziu în acest capitol. 

## Parcurgerea modelului[[parcurgerea-modelului]]

{#if fw === 'pt'}
Putem descărca modelul nostru preantrenat în același mod în care am făcut-o cu tokenizatorul nostru. 🤗 Transformers oferă o clasă `AutoModel` care are și o metodă `from_pretrained()`:

```python
from transformers import AutoModel

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModel.from_pretrained(checkpoint)
```
{:else}
 
Putem descărca modelul nostru preantrenat în același mod în care am făcut-o cu tokenizatorul nostru. 🤗 Transformers oferă o clasă `TFAutoModel` care are și o metodă `from_pretrained`:

```python
from transformers import TFAutoModel

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = TFAutoModel.from_pretrained(checkpoint)
```
{/if}

 
În acest fragment de cod, am descărcat același checkpoint pe care l-am folosit anterior în pipeline-ul nostru (de fapt, ar fi trebuit să fie deja în cache) și am instanțiat un model cu acesta.

Această arhitectură conține doar modulul Transformer de bază: având în vedere anumite intrări, acesta produce ceea ce vom numi *stări ascunse*, cunoscute și ca *caracteristici*. Pentru fiecare intrare a modelului, vom extrage un vector multidimensional care reprezintă **înțelegerea contextuală a intrării respective de către modelul Transformer**.

Dacă acest lucru nu are sens, nu vă faceți griji. Vom explica totul mai târziu.

Deși aceste stări ascunse pot fi utile pe cont propriu, ele sunt de obicei intrări pentru o altă parte a modelului, cunoscută sub numele de *head*. În [Capitolul 1](/course/chapter1), diferitele sarcini ar fi putut fi efectuate cu aceeași arhitectură, dar fiecăreia dintre aceste sarcini îi va fi asociat un head diferit.

### Un vector multidimensional?[[un-vector-multidimensional]]

 
Vectorul emis de modulul Transformator este de obicei de dimensiuni mari. Acesta are în general trei dimensiuni:

- **Dimensiunea batch-ului**: Numărul de secvențe prelucrate simultan (2 în exemplul nostru).
- **Lungimea secvenței**: Lungimea reprezentării numerice a secvenței (16 în exemplul nostru).
- **Dimensiunea ascunsă**: Dimensiunea vectorială a fiecărei intrări a modelului.

Se spune că este multidimensional din cauza ultimei valori. Dimensiunea ascunsă poate fi foarte mare (768 este comună pentru modelele mai mici, iar în modelele mai mari aceasta poate ajunge la 3072 sau mai mult).

Putem vedea acest lucru dacă introducem în modelul nostru intrările pe care le-am preprocesat:

{#if fw === 'pt'}
```python
outputs = model(**inputs)
print(outputs.last_hidden_state.shape)
```

```python out
torch.Size([2, 16, 768])
```
{:else}
```py
outputs = model(inputs)
print(outputs.last_hidden_state.shape)
```

```python out
(2, 16, 768)
```
{/if}

 
Rețineți că ieșirile modelelor 🤗 Transformers se comportă ca `namedtuple`s sau dicționare. Puteți accesa elementele prin atribute (așa cum am făcut noi) sau prin cheie (`outputs[„last_hidden_state”]`), sau chiar prin index dacă știți exact unde se află lucrul pe care îl căutați (`outputs[0]`).

### Modele de head-uri: Înțelegerea numerelor[[modele-de-head-uri-înțelegerea-numerelor]]

Head-urile modelelor iau ca intrare vectorul multidimensional al stărilor ascunse și le proiectează pe o altă dimensiune. Acestea sunt de obicei compuse din unul sau câteva straturi liniare:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/transformer_and_head.svg" alt="A Transformer network alongside its head."/>
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter2/transformer_and_head-dark.svg" alt="A Transformer network alongside its head."/>
</div>

 
Rezultatul modelului Transformer este trimis direct la head-ul modelului pentru a fi prelucrat.

În această diagramă, modelul este reprezentat de stratul său de încorporare și de straturile următoare. Stratul de încorporare convertește fiecare ID de intrare din intrarea tokenizată într-un vector care reprezintă tokenul asociat. Straturile ulterioare manipulează acești vectori folosind mecanismul de atenție pentru a produce reprezentarea finală a propozițiilor.

Există multe arhitecturi diferite disponibile în 🤗 Transformers, fiecare fiind concepută în jurul abordării unei sarcini specifice. Iată o listă neexhaustivă:

- `*Model` (extragerea stărilor ascunse)
- `*ForCausalLM`
- `*ForMaskedLM`
- `*ForMultipleChoice`
- `*ForQuestionAnswering`
- `*ForSequenceClassification`
- `*ForTokenClassification`
- și altele 🤗

{#if fw === 'pt'}
 Pentru exemplul nostru, vom avea nevoie de un model cu un head de clasificare a secvențelor (pentru a putea clasifica propozițiile ca fiind pozitive sau negative). Așadar, nu vom utiliza clasa `AutoModel`, ci `AutoModelForSequenceClassification`:

```python
from transformers import AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
outputs = model(**inputs)
```
{:else}
 Pentru exemplul nostru, vom avea nevoie de un model cu un head de clasificare a secvențelor (pentru a putea clasifica propozițiile ca fiind pozitive sau negative). Așadar, nu vom utiliza clasa `TFAutoModel`, ci `TFAutoModelForSequenceClassification`:

```python
from transformers import TFAutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
outputs = model(inputs)
```
{/if}
 
Acum, dacă ne uităm la forma ieșirilor noastre, dimensiunea va fi mult mai mică: head-ul modelului ia ca intrare vectorii multidimensionali pe care i-am văzut înainte și scoate vectori care conțin două valori (una pentru fiecare etichetă):

```python
print(outputs.logits.shape)
```

{#if fw === 'pt'}
```python out
torch.Size([2, 2])
```
{:else}
```python out
(2, 2)
```
{/if}

 
Deoarece avem doar două propoziții și două etichete, rezultatul pe care îl obținem din modelul nostru este de forma 2 x 2.

## Postprocesarea rezultatului[[postprocesarea-rezultatului]]

Valorile pe care le obținem ca rezultat al modelului nostru nu au neapărat sens în sine. Să aruncăm o privire:

```python
print(outputs.logits)
```

{#if fw === 'pt'}
```python out
tensor([[-1.5607,  1.6123],
        [ 4.1692, -3.3464]], grad_fn=<AddmmBackward>)
```
{:else}
```python out
<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
    array([[-1.5606991,  1.6122842],
           [ 4.169231 , -3.3464472]], dtype=float32)>
```
{/if}

Modelul nostru a prezis `[-1.5607, 1.6123]` pentru prima propoziție și `[ 4.1692, -3.3464]` pentru cea de-a doua. Acestea nu sunt probabilități, ci *logits*, scorurile brute, nenormalizate, emise de ultimul strat al modelului. Pentru a fi convertite în probabilități, acestea trebuie să treacă printr-un strat [SoftMax](https://en.wikipedia.org/wiki/Softmax_function) (toate modelele 🤗 Transformers produc logits, deoarece funcția de pierdere pentru formare va fuziona în general ultima funcție de activare, cum ar fi SoftMax, cu funcția de pierdere reală, cum ar fi entropia încrucișată):

{#if fw === 'pt'}
```py
import torch

predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
print(predictions)
```
{:else}
```py
import tensorflow as tf

predictions = tf.math.softmax(outputs.logits, axis=-1)
print(predictions)
```
{/if}

{#if fw === 'pt'}
```python out
tensor([[4.0195e-02, 9.5980e-01],
        [9.9946e-01, 5.4418e-04]], grad_fn=<SoftmaxBackward>)
```
{:else}
```python out
tf.Tensor(
[[4.01951671e-02 9.59804833e-01]
 [9.9945587e-01 5.4418424e-04]], shape=(2, 2), dtype=float32)
```
{/if}

 
Acum putem vedea că modelul a prezis `[0.0402, 0.9598]` pentru prima propoziție și `[0.9995, 0.0005]` pentru cea de-a doua. Acestea sunt scoruri de probabilitate care pot fi recunoscute.

Pentru a obține etichetele corespunzătoare fiecărei poziții, putem inspecta atributul `id2label` din configurația modelului (mai multe despre acest lucru în secțiunea următoare):

```python
model.config.id2label
```

```python out
{0: 'NEGATIVE', 1: 'POSITIVE'}
```
Acum putem concluziona că modelul a prezis următoarele:
 
- Prima propoziție: NEGATIV: 0.0402, POZITIV: 0.9598
- A doua propoziție: NEGATIVĂ: 0.9995, POZITIVĂ: 0.0005

Am reprodus cu succes cele trei etape ale pipeline-ului: preprocesarea cu tokenizatoare, trecerea intrărilor prin model și postprocesarea! Acum haideți să analizăm în profunzime fiecare dintre aceste etape.

<Tip>

✏️  **Încercați!** Alegeți două (sau mai multe) texte proprii și treceți-le prin conducta `sentiment-analysis`. Apoi repetați pașii pe care i-ați văzut aici și verificați dacă obțineți aceleași rezultate!

</Tip>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter2/2.mdx" />

### Introducere[[introducere]]
https://huggingface.co/learn/course/ro/chapter0/1.md

# Introducere[[introducere]]

Bun venit la cursul Hugging Face! Această introducere te va ghida în configurarea environment-ului de lucru. Dacă abia începi cursul, îți recomandăm să arunci o privire mai întâi asupra [Capitolului 1](/course/chapter1), apoi să te întorci și să îți configurezi environment-ul pentru a putea încerca singur codul.

Toate bibliotecile pe care le vom folosi în acest curs sunt disponibile ca Python packages, așa că aici îți vom arăta cum să configurezi Python environment-ul și să instalezi librăriile specifice de care ai nevoie.

Vom acoperi două moduri de a-ți configura environment-ul, folosind un notebook Colab sau un Python environment. Simte-te liber să alegi pe cel care ți se potrivește cel mai bine. Pentru începători, recomandăm să începi folosind un notebook Colab.

Reține că nu vom acoperi sistemul Windows. Dacă folosești Windows, îți recomandăm să urmezi pașii folosind un notebook Colab. Dacă folosești o distribuție Linux sau macOS, poți folosi oricare dintre abordările descrise aici.

Majoritatea cursului se bazează pe faptul că ai un cont Hugging Face. Îți recomandăm să creezi unul acum: [crează un cont](https://huggingface.co/join).

## Folosirea unui notebook Google Colab[[folosind-un-notebook-google-colab]]

Folosirea unui notebook Colab este cea mai simplă configurare posibilă; deschide un notebook în browserul tău și începe imediat să scrii cod!

Dacă nu ești familiarizat cu Colab, îți recomandăm să începi urmând [introducerea](https://colab.research.google.com/notebooks/intro.ipynb). Colab îți permite să folosești hardware accelerat, cum ar fi GPU-uri sau TPU-uri, și este gratuit pentru sarcini mai mici.

Odată ce ești confortabil să te descurci în Colab, creează un nou notebook și începe cu configurarea:

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter0/new_colab.png" alt="Un notebook Colab gol" width="80%"/>
</div>

Următorul pas este să instalezi librăriile pe care le vom folosi în acest curs. Vom folosi `pip` pentru instalare, care este managerul de packages pentru Python. În notebook-uri, poți rula comenzi de sistem începând comanda cu caracterul `!`, așa că poți instala librăria 🤗 Transformers astfel:

```
!pip install transformers
```

Te poți asigura că pachetul a fost instalat corect importându-l în cadrul mediului tău Python:

```
import transformers
```

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter0/install.gif" alt="O animație care arată rezultatul celor două comenzi de mai sus: instalare și importarea" width="80%"/>
</div>

Aceasta instalează o versiune foarte ușoară a 🤗 Transformers. În special, nu sunt instalate framework-uri specifice de machine learning (cum ar fi PyTorch sau TensorFlow). Deoarece vom folosi multe caracteristici diferite ale librăriei, îți recomandăm să instalezi versiunea pentru development, care vine cu toate dependențele necesare pentru cam orice caz de utilizare imaginabil:

```
!pip install transformers[sentencepiece]
```

Aceasta va dura puțin timp, dar apoi vei fi gata de drum pentru restul cursului!

## Folosirea unui virtual environment Python[[folosirea-unui-virtual-environment-python]]

Dacă preferi să folosești un mediu virtual Python, primul pas este să instalezi Python pe sistemul tău. Îți recomandăm să urmezi [această ghidare](https://realpython.com/installing-python/) pentru a începe.

Odată ce ai Python instalat, ar trebui să poți rula comenzi Python în terminalul tău. Poți începe rulând următoarea comandă pentru a te asigura că este instalat corect înainte de a trece la pașii următori: `python --version`. Aceasta ar trebui să afișeze versiunea Python disponibilă acum pe sistemul tău.

Când rulezi o comandă Python în terminalul tău, cum ar fi `python --version`, ar trebui să te gândești la programul care rulează comanda ta ca la Python-ul "principal" de pe sistemul tău. Îți recomandăm să păstrezi această instalare principală liberă de orice pachete și să o folosești pentru a crea environment-uri separate pentru fiecare aplicație pe care lucrezi — în acest fel, fiecare aplicație poate avea propriile sale dependențe și pachete, și nu va trebui să te preocupi de problemele de compatibilitate potențiale cu alte aplicații.

În Python, acest lucru se face prin [*virtual environments*](https://docs.python.org/3/tutorial/venv.html), care sunt directory trees autonomi ce conțin fiecare o instalare Python cu o anumită versiune Python împreună cu toate pachetele de care are nevoie aplicația. Crearea unui astfel de mediu virtual poate fi realizată cu mai multe instrumente diferite, dar vom folosi pachetul oficial Python pentru acest scop, denumit [`venv`](https://docs.python.org/3/library/venv.html#module-venv).

În primul rând, creează folder în care dorești ca aplicația ta să locuiască — de exemplu, ai putea dori să faci un nou folder numit *transformers-course* în rădăcina folderului tău personal:

```
mkdir ~/transformers-course
cd ~/transformers-course
```

Din interiorul acestui folder, creează un virtual environment folosind modulul Python `venv`:

```
python -m venv .env
```

Acum ar trebui să ai un folder numit *.env* în folderul tău altfel gol:

```
ls -a
```

```out
.      ..    .env
```

Poți să intri și să ieși din environment folosind comenzile `activate` și `deactivate`:

```
# Activează mediul virtual
source .env/bin/activate

# Dezactivează virtual environment-ul
deactivate
```

Te poți asigura că environment-ul este activat rulând comanda `which python`: dacă aceasta indică către virtual environment, atunci l-ai activat cu succes!

```
which python
```

```out
/home/<user>/transformers-course/.env/bin/python
```

### Instalarea dependențelor[[instalarea-dependențelor]]

La fel ca în secțiunea anterioară despre utilizarea instanțelor Google Colab, acum va trebui să instalezi pachetele necesare pentru a continua. Din nou, poți instala versiunea pentru development a 🤗 Transformers folosind managerul de pachete `pip`:

```
pip install "transformers[sentencepiece]"
```

Acum ești gata să începi!

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter0/1.mdx" />

### Sumar[[sumar]]
https://huggingface.co/learn/course/ro/chapter1/9.md

# Sumar[[sumar]]

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

În acest capitol, ați învățat cum să abordați diferite sarcini NLP utilizând funcția `pipeline()` de nivel înalt din 🤗 Transformers. De asemenea, ați descoperit cum să căutați și să utilizați modele în Hub, precum și cum să testați acestee modelee direct în browser folosind API-ul de inferență.

Am discutat despre modul în care funcționează modelele Transformer dintr-o perspectivă generală și despre importanța transfer learning-ului și fine-tuning-ului. Un aspect cheie este faptul că puteți utiliza întreaga arhitectură sau doar encoder-ul sau decoder-ul, în funcție de tipul de sarcină pe care doriți să o rezolvați. Următorul tabel rezumă acest aspect:

| Model           | Exemple                                    | Task-uri                                                                           |
|-----------------|--------------------------------------------|----------------------------------------------------------------------------------|
| Encoder         | ALBERT, BERT, DistilBERT, ELECTRA, RoBERTa | Clasificarea propozițiilor, recunoașterea entităților denumite, Extractive QA    |
| Decoder         | CTRL, GPT, GPT-2, Transformer XL           | Generarea de text                                                                |
| Encoder-decoder | BART, T5, Marian, mBART                    | Rezumare, traducere, răspunsuri generative la întrebări                          |


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter1/9.mdx" />

### Modele Encoder[[modele-encoder]]
https://huggingface.co/learn/course/ro/chapter1/5.md

# Modele Encoder[[modele-encoder]]

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

<Youtube id="MUqNwgPjJvQ" />

Modelele Encoder utilizează doar encoderul unui model Transformer. La fiecare etapă, layer-urile de atenție pot accesa toate cuvintele din propoziția inițială. Aceste modele sunt adesea caracterizate ca având o atenție „bidirecțională” și sunt adesea numite *modele auto-encoding*.

Preantrenarea acestor modele se bazează, de obicei, pe alterarea unei propoziții date (de exemplu, prin mascarea unor cuvinte aleatorii) și pe sarcina modelului de a găsi sau reconstrui propoziția inițială.

Modelele Encoder sunt cele mai potrivite pentru sarcinile care necesită înțelegerea întregii propoziții, cum ar fi clasificarea propozițiilor, recunoașterea entităților numite (și, mai general, clasificarea cuvintelor) și Extractive QA.

Printre reprezentanții acestei familii de modele se numără:

- [ALBERT](https://huggingface.co/docs/transformers/model_doc/albert)
- [BERT](https://huggingface.co/docs/transformers/model_doc/bert)
- [DistilBERT](https://huggingface.co/docs/transformers/model_doc/distilbert)
- [ELECTRA](https://huggingface.co/docs/transformers/model_doc/electra)
- [RoBERTa](https://huggingface.co/docs/transformers/model_doc/roberta)


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter1/5.mdx" />

### Introducere[[introducere]]
https://huggingface.co/learn/course/ro/chapter1/1.md

# Introducere[[introducere]]

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

## Bun venit la 🤗 curs![[bun-venit-la-curs]]

<Youtube id="00GKzGyWFEs" />

Acest curs are scopul de a vă învăța despre procesarea limbajelor naturale (NLP) folosind biblioteci din ecosistemul [Hugging Face](https://huggingface.co/) — [🤗 Transformers](https://github.com/huggingface/transformers), [🤗 Datasets](https://github.com/huggingface/datasets), [🤗 Tokenizers](https://github.com/huggingface/tokenizers), și [🤗 Accelerate](https://github.com/huggingface/accelerate) — precum și [Hugging Face Hub](https://huggingface.co/models). Cursul este complet gratuit și nu conține reclame.

## Înțelegerea NLP și a LLM-urilor[[înțelegerea-nlp-și-a-llm-urilor]]

Deși acest curs a fost inițial axat pe NLP (Procesarea Limbajului Natural), el a evoluat pentru a pune accentul pe Large Language Models (LLM-uri), care reprezintă cele mai recente progrese din domeniu.

**Care este diferența?**
- **NLP (Procesarea Limbajului Natural)** este domeniul mai larg care se concentrează pe permiterea computerelor să înțeleagă, interpreteze și să genereze limbajul uman. NLP include multe tehnici și sarcini, cum ar fi analiza sentimentelor, recunoașterea entităților numite și traducerea automată.
- **LLM-uri (Modele Mari de Limbaj)** sunt un subset puternic al modelelor NLP, caracterizate prin dimensiuni masive, volume mari de date de antrenament și abilitatea de a îndeplini o gamă largă de sarcini lingvistice cu un antrenament specific minim. Modele precum seriile Llama, GPT sau Claude sunt exemple de LLM-uri care au revoluționat ceea ce este posibil în domeniul NLP.

Pe parcursul acestui curs, vei învăța atât concepte tradiționale din NLP, cât și tehnici de ultimă generație legate de LLM-uri, deoarece înțelegerea fundamentelor NLP este esențială pentru a lucra eficient cu LLM-uri.

## La ce să te aștepți?[[la-ce-sa-te-aștepți]]

Aceasta este o scurtă prezentare a cursului:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/summary.svg" alt="Brief overview of the chapters of the course.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/summary-dark.svg" alt="Brief overview of the chapters of the course.">
</div>

- Capitolele 1-4 oferă o introducere în conceptele fundamentale ale bibliotecii 🤗 Transformers. Până la finalul acestei părți a cursului, veți fi familiarizați cu modul în care funcționează modelele Transformer și veți ști cum să utilizați un model din [Hugging Face Hub](https://huggingface.co/models), să îl ajustați pe un set de date și să vă partajați rezultatele pe Hub!
- Capitolele 5-8 predau elementele de bază ale Datasets 🤗 și ale Tokenizatoarelor 🤗 înainte de a vă scufunda în sarcinile NLP. Până la sfârșitul acestei părți, veți fi capabil să abordați singur cele mai frecvente probleme NLP.
- Capitolele 9-12 trec dincolo de NLP și explorează modul în care modelele Transformer pot fi utilizate pentru a aborda sarcini din domeniul procesării semnalelor vorbirii și computer vision. Pe parcurs, veți învăța cum să construiți și să partajați demo-uri ale modelelor dumneavoastră și cum să le optimizați pentru mediul de producție. Până la finalul acestei părți, veți fi gata să aplicați 🤗 Transformers la (aproape) orice problemă de machine learning!

Acest curs:

* Necesită o bună cunoaștere a limbajului Python.
* Este recomandat să fie parcurs după un curs introductiv de deep learning, cum ar fi [fast.ai's](https://www.fast.ai/) [Practical Deep Learning for Coders](https://course.fast.ai/) sau unul dintre cursurile oferite de [DeepLearning.AI](https://www.deeplearning.ai/).
* Nu se așteaptă la cunoștințe anterioare despre [PyTorch](https://pytorch.org/) sau [TensorFlow](https://www.tensorflow.org/), deși o familiaritate cu oricare dintre acestea va fi de ajutor.

După ce ați completat acest curs, vă recomandăm să accesați [Natural Language Processing Specialization](https://www.coursera.org/specializations/natural-language-processing?utm_source=deeplearning-ai&utm_medium=institutions&utm_campaign=20211011-nlp-2-hugging_face-page-nlp-refresh) de la DeepLearning.AI, care acoperă o gamă largă de modele NLP clasice, cum ar fi naive Bayes și LSTMs, despre care este bine să știți!

## Cine suntem noi?[[cine-suntem-noi]]

Despre autori:

[**Abubakar Abid**](https://huggingface.co/abidlabs) și-a susținut doctoratul la Stanford, în domeniul Applied Machine Learning. În timpul doctoratului său, a fondat [Gradio](https://github.com/gradio-app/gradio), o bibliotecă Python open-source care a fost utilizată pentru a construi peste 600.000 de demo-uri de Machine Learning. Gradio a fost achiziționată de Hugging Face, unde Abubakar activează acum ca lider al echipei de Machine Learning.

[**Ben Burtenshaw**](https://huggingface.co/burtenshaw) este Machine Learning Engineer la Hugging Face. A obținut doctoratul în Procesarea Limbajului Natural la Universitatea din Antwerp, unde a aplicat modele Transformer pentru a genera povești pentru copii cu scopul de a îmbunătăți abilitățile de literație. De atunci, s-a concentrat pe materiale educaționale și instrumente pentru comunitatea largă.

[**Matthew Carrigan**](https://huggingface.co/Rocketknight1) este inginer de Machine Learning la Hugging Face. Locuiește în Dublin, Irlanda și anterior a fost inginer ML la Parse.ly și înainte de asta cercetător postdoctoral la Trinity College Dublin. El consideră că atingerea AGI nu se va realiza prin scalarea arhitecturilor existente, dar este optimist în legătură cu viitorul roboților.

[**Lysandre Debut**](https://huggingface.co/lysandre) este inginer de Machine Learning la Hugging Face și a lucrat la biblioteca 🤗 Transformers încă din primele etape de dezvoltare. Scopul său este de a face NLP accesibil pentru toată lumea prin dezvoltarea de instrumente cu un API foarte simplu.

[**Sylvain Gugger**](https://huggingface.co/sgugger) este inginer de cercetare în Machine Learning la Hugging Face și unul dintre principalii întreținători ai bibliotecii 🤗 Transformers. Anterior a fost cercetător științific la fast.ai și a fost coautor la _[Deep Learning for Coders with fastai and PyTorch](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/)_ cu Jeremy Howard. Obiectivul principal al cercetării sale este de a face învățarea profundă mai accesibilă, prin proiectarea și îmbunătățirea tehnicilor care permit modelelor să se antreneze rapid pe resurse limitate.

[**Dawood Khan**](https://huggingface.co/dawoodkhan82) este inginer de Machine Learning la Hugging Face. Este originar din New York și a absolvit Universitatea din New York, unde a studiat Informatica. După ce a lucrat ca inginer iOS timp de câțiva ani, Dawood a renunțat la poziția sa pentru a înființa Gradio împreună cu colegii săi co-fondatori. Gradio a fost în cele din urmă achiziționat de Hugging Face.

[**Merve Noyan**](https://huggingface.co/merve) este un susținător al dezvoltatorilor la Hugging Face, lucrând la dezvoltarea de instrumente și la crearea de conținut în jurul acestora pentru a facilita învățarea automată pentru toată lumea. 

[**Lucile Saulnier**](https://huggingface.co/SaulLu) este inginer de Machine Learning la Hugging Face, dezvoltând și susținând utilizarea de instrumente open source. De asemenea, este implicată activ în multe proiecte de cercetare în domeniul procesării limbajului natural, cum ar fi formarea colaborativă și BigScience.

[**Lewis Tunstall**](https://huggingface.co/lewtun) este inginer de Machine Learning la Hugging Face, concentrându-se pe dezvoltarea de instrumente open-source și pe asigurarea accesibilității acestora pentru întreaga comunitate. De asemenea, este coautor al cărții O'Reilly [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/).

[**Leandro von Werra**](https://huggingface.co/lvwerra) este inginer de Machine Learning în cadrul echipei open-source de la Hugging Face și, de asemenea, coautor al cărții O'Reilly [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/). El are mai mulți ani de experiență în industrie, aducând proiecte NLP în stadiul de producție, lucrând pe întreaga stivă de învățare automată.

## FAQ[[faq]]

Iată câteva răspunsuri la întrebări frecvente:

- **Acest curs permite obținerea unei certificări?**
Momentan nu avem nicio certificare pentru acest curs. Cu toate acestea, lucrăm la un program de certificare pentru ecosistemul Hugging Face - rămâneți pe fază!

- **Cât timp ar trebui să dedic acestui curs?**
Fiecare capitol al acestui curs este gândit să fie parcurs într-o săptămână, necesitând aproximativ 6–8 ore de lucru. Totuși, puteți avansa în ritmul propriu și finaliza cursul în timpul care vi se potrivește cel mai bine.

- **Unde pot să pun o întrebare dacă am una?**
Dacă aveți o întrebare legată de orice secțiune a cursului, faceți clic pe bannerul „Pune o întrebare” din partea de sus a paginii pentru a fi redirecționat automat către secțiunea corespunzătoare din [forumurile Hugging Face](https://discuss.huggingface.co/):

<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/forum-button.png" alt="Link to the Hugging Face forums" width="75%">

De asemenea, o listă de [idei de proiecte](https://discuss.huggingface.co/c/course/course-event/25) este disponibilă pe forumuri, în cazul în care doriți să exersați mai mult după ce ați terminat cursul.

- **De unde pot obține codul pentru curs?**
Pentru fiecare secțiune, faceți clic pe bannerul din partea de sus a paginii pentru a rula codul în Google Colab sau Amazon SageMaker Studio Lab:

<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/notebook-buttons.png" alt="Link to the Hugging Face course notebooks" width="75%">

Notebook-urile Jupyter care conțin întregul cod al cursului sunt găzduite în repo-ul [`huggingface/notebooks`](https://github.com/huggingface/notebooks). Dacă doriți să le generați local, consultați instrucțiunile din repo-ul [`course`](https://github.com/huggingface/course#-jupyter-notebooks) de pe GitHub.


- **Cum pot contribui la curs?**
Există multe modalități de a contribui la curs! Dacă găsiți o greșeală de tipar sau o eroare, vă rugăm să creați o cerere în repo-ul [`course`](https://github.com/huggingface/course). Dacă doriți să ajutați la traducerea cursului în limba dumneavoastră maternă, consultați instrucțiunile [aici](https://github.com/huggingface/course#translating-the-course-into-your-language).

- **Care au fost alegerile făcute pentru fiecare traducere?**
Fiecare traducere are un glosar și un fișier `TRANSLATING.txt` care detaliază alegerile care au fost făcute pentru jargonul de Machine Learning etc. Puteți găsi un exemplu în limba germană [aici](https://github.com/huggingface/course/blob/main/chapters/de/TRANSLATING.txt).


- **Pot reutiliza acest curs?**
Desigur! Cursul este distribuit sub licența [Apache 2 license](https://www.apache.org/licenses/LICENSE-2.0.html), o licență permisivă. Aceasta permite reutilizarea materialului cu condiția să acordați credit autorilor, să includeți un link către licență și să menționați eventualele modificări efectuate. Acest lucru poate fi realizat în orice mod rezonabil, atâta timp cât nu implicați în mod fals că licențiatorul susține în mod explicit persoana sau utilizarea dvs. Dacă doriți să citați acest curs, vă rugăm să folosiți următorul formatBibTeX:

```
@misc{huggingfacecourse,
  author = {Hugging Face},
  title = {The Hugging Face Course, 2022},
  howpublished = "\url{https://huggingface.co/course}",
  year = {2022},
  note = "[Online; accessed <today>]"
}
```

## Să începem!
Sunteți gata să începeți? În acest capitol, veți învăța:

* Cum să utilizați funcția `pipeline()` pentru a rezolva sarcini NLP precum generarea și clasificarea textului
* Despre arhitectura Transformer
* Cum să faceți distincția între arhitecturile și cazurile de utilizare ale encoderului, decoderului și encoder-decoder.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter1/1.mdx" />

### Ce pot face modelele Transformer[[ce-pot-face-modelele-transformer]]
https://huggingface.co/learn/course/ro/chapter1/3.md

# Ce pot face modelele Transformer[[ce-pot-face-modelele-transformer]]

<CourseFloatingBanner chapter={1}
  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/chapter1/section3.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter1/section3.ipynb"},
]} />

În această parte, vom explora ce pot face modelele Transformer și vom folosi primul nostru instrument din biblioteca 🤗 Transformers: funcția `pipeline()`.

<Tip>
👀 Vedeți butonul <em>Open in Colab</em> din dreapta sus? Faceți clic pe el pentru a deschide un notebook Google Colab cu toate exemplele de cod din această secțiune. Acest buton va fi prezent în orice secțiune care conține exemple de cod. 
Dacă doriți să executați exemplele local, vă recomandăm să aruncați o privire la <a href="/course/chapter0">setup</a>.
</Tip>

## Modelele Transformer sunt peste tot![[modelele-transformer-sunt-peste-tot]]

Modelele Transformer sunt utilizate pentru a rezolva toate tipurile de sarcini NLP, precum cele menționate în secțiunea anterioară. Iată câteva dintre companiile și organizațiile care utilizează modelele Hugging Face și Transformer, care de asemenea contribuie la dezvoltarea comunității prin partajarea modelelor lor:

<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/companies.PNG" alt="Companies using Hugging Face" width="100%">

Biblioteca [🤗 Transformers](https://github.com/huggingface/transformers) oferă funcționalitatea de a crea și utiliza aceste modele partajate. [Model Hub](https://huggingface.co/models) conține mii de modele preinstruite pe care oricine le poate descărca și utiliza. De asemenea, vă puteți încărca propriile modele pe Hub!

<Tip>
⚠️ Hub-ul Hugging Face nu este limitat la modelele Transformer. Oricine poate partaja orice fel de modele sau seturi de date pe care le dorește! <a href="https://huggingface.co/join">Creați un cont huggingface.co</a> pentru a beneficia de toate funcțiile disponibile!
</Tip>

Înainte de a analiza funcționarea internă a modelelor Transformer , să ne oprim asupra unor exemple privind modul în care acestea pot fi utilizate pentru a rezolva unele probleme interesante de NLP.

## Lucrul cu pipelines[[lucrul-cu-pipelines]]

<Youtube id="tiZFewofSLM" />

Obiectul cel mai elementar din biblioteca 🤗 Transformers este funcția `pipeline()`. Aceasta conectează un model cu etapele sale necesare de preprocesare și postprocesare, permițându-ne să introducem direct orice text și să obținem un răspuns inteligibil:

```python
from transformers import pipeline

classifier = pipeline("sentiment-analysis")
classifier("I've been waiting for a HuggingFace course my whole life.")
```

```python out
[{'label': 'POSITIVE', 'score': 0.9598047137260437}]
```

Putem adăuga chiar și  mai multe propoziții!

```python
classifier(
    ["I've been waiting for a HuggingFace course my whole life.", "I hate this so much!"]
)
```

```python out
[{'label': 'POSITIVE', 'score': 0.9598047137260437},
 {'label': 'NEGATIVE', 'score': 0.9994558095932007}]
```

În mod implicit, acest pipeline selectează un anumit model preinstruit care a fost ajustat pentru a analiza emoțiile dintr-un text în limba engleză. Modelul este descărcat și pus în cache atunci când creați obiectul `classifier`. Dacă rulați din nou comanda, modelul din memoria cache va fi utilizat în locul acestuia și nu este nevoie să descărcați din nou modelul.

Atunci când transmiteți un text către un pipeline, sunt necesari trei pași:

1. Textul este preprocesat într-un format pe care modelul îl poate înțelege.
2. Datele de intrare preprocesate sunt transmise modelului.
3. Predicțiile modelului sunt postprocesate, astfel încât să le puteți înțelege.


Unele dintre [pipeline-urile disponibile](https://huggingface.co/transformers/main_classes/pipelines) în prezent sunt:

- `feature-extraction` (obține reprezentarea vectorială a unui text)
- `fill-mask`
- `ner` (named entity recognition/recunoașterea entităților numite)
- `question-answering`
- `sentiment-analysis`
- `summarization`
- `text-generation`
- `translation`
- `zero-shot-classification`

Să aruncăm o privire la câteva dintre ele!

## Zero-shot classification[[zero-shot-classification]]

Vom începe prin a aborda o sarcină mai dificilă în care trebuie să clasificăm texte care nu au fost etichetate. Acesta este un scenariu comun în proiectele din lumea reală, deoarece adnotarea textului este de obicei costisitoare în timp și necesită expertiză în domeniu. Pentru acest caz de utilizare, pipeline-ul `zero-shot-classification` este foarte puternic: vă permite să specificați ce etichete să utilizați pentru clasificare, astfel încât să nu fie nevoie să vă bazați pe etichetele modelului preinstruit. Ați văzut deja cum modelul poate clasifica o propoziție ca fiind pozitivă sau negativă folosind aceste două etichete - dar poate, de asemenea, clasifica textul folosind orice alt set de etichete doriți.

```python
from transformers import pipeline

classifier = pipeline("zero-shot-classification")
classifier(
    "This is a course about the Transformers library",
    candidate_labels=["education", "politics", "business"],
)
```

```python out
{'sequence': 'This is a course about the Transformers library',
 'labels': ['education', 'business', 'politics'],
 'scores': [0.8445963859558105, 0.111976258456707, 0.043427448719739914]}
```

Acest pipeline se numește _zero-shot_ deoarece nu trebuie să reglați modelul pe datele dvs. pentru a o utiliza. Aceasta poate returna direct scoruri de probabilitate pentru orice listă de etichete doriți!

<Tip>

✏️ **Încercați** Jucați-vă cu propriile secvențe și etichete și vedeți cum se comportă modelul.

</Tip>


## Text generation[[text-generation]]

Să vedem acum cum se utilizează un pipeline pentru a genera un text. Ideea principală aici este că furnizați o solicitare, iar modelul o va completa automat prin generarea textului rămas. Acest lucru este similar cu funcția de text previzibil care se găsește pe multe telefoane. Generarea textului implică caracter aleatoriu, deci este normal să nu obțineți aceleași rezultate ca cele prezentate mai jos.

```python
from transformers import pipeline

generator = pipeline("text-generation")
generator("In this course, we will teach you how to")
```

```python out
[{'generated_text': 'In this course, we will teach you how to understand and use '
                    'data flow and data interchange when handling user data. We '
                    'will be working with one or more of the most commonly used '
                    'data flows — data flows of various types, as seen by the '
                    'HTTP'}]
```

Puteți controla câte secvențe diferite sunt generate cu argumentul `num_return_sequences` și lungimea totală a textului de ieșire cu argumentul `max_length`.

<Tip>

✏️ **Încercați!** Utilizați argumentele `num_return_sequences` și `max_length` pentru a genera două propoziții a câte 15 cuvinte fiecare.

</Tip>


## Utilizarea oricărui model de pe Hub într-un pipeline[[utilizarea-oricărui-model-de-pe-hub-într-un-pipeline]]

Exemplele anterioare au utilizat modelul implicit pentru sarcina în cauză, dar puteți alege, de asemenea, un anumit model din Hub pentru a-l utiliza într-un pipeline pentru o sarcină specifică - de exemplu, generarea de text. Accesați [Model Hub](https://huggingface.co/models) și faceți clic pe eticheta corespunzătoare din stânga pentru a afișa numai modelele acceptate pentru sarcina respectivă. Ar trebui să ajungeți la o pagină precum [aceasta](https://huggingface.co/models?pipeline_tag=text-generation).

Să încercăm modelul [`distilgpt2`](https://huggingface.co/distilgpt2)! Iată cum să îl puteți încărca în același pipeline ca înainte:

```python
from transformers import pipeline

generator = pipeline("text-generation", model="distilgpt2")
generator(
    "In this course, we will teach you how to",
    max_length=30,
    num_return_sequences=2,
)
```

```python out
[{'generated_text': 'In this course, we will teach you how to manipulate the world and '
                    'move your mental and physical capabilities to your advantage.'},
 {'generated_text': 'In this course, we will teach you how to become an expert and '
                    'practice realtime, and with a hands on experience on both real '
                    'time and real'}]
```

Puteți să vă îmbunătățiți căutarea unui model făcând clic pe etichetele de limbaj și să alegând un model care să genereze text într-o altă limbă. Model Hub conține chiar și puncte de control pentru modele multilingve care acceptă mai multe limbi.

După ce selectați un model făcând clic pe el, veți vedea că există un widget care vă permite să îl încercați direct online. În acest fel, puteți testa rapid capacitățile modelului înainte de a-l descărca.

<Tip>

✏️ ** Încercați!** Utilizați filtrele pentru a găsi un model de generare a textului pentru o altă limbă. Nu ezitați să explorați widget-ul și să îl utilizați într-un pipeline!

</Tip>

### API-ul de inferență[[api-ul-de-inferență]]

Toate modelele pot fi testate direct prin browser utilizând API-ul de inferență, care este disponibil pe site-ul Hugging Face [website] (https://huggingface.co/). Puteți interacționa cu modelul direct pe această pagină introducând text personalizat și urmărind cum procesează datele de intrare.

API-ul de inferență care alimentează widget-ul este, de asemenea, disponibil ca produs plătit, ceea ce este util dacă aveți nevoie de el pentru fluxurile dvs. de lucru. Consultați [pagina de prețuri](https://huggingface.co/pricing) pentru mai multe detalii.

## Mask filling[[mask-filling]]

Următorul pipeline pe care il veți încerca este `fill-mask`. Ideea acestei sarcini este de a completa golurile dintr-un text dat:

```python
from transformers import pipeline

unmasker = pipeline("fill-mask")
unmasker("This course will teach you all about <mask> models.", top_k=2)
```

```python out
[{'sequence': 'This course will teach you all about mathematical models.',
  'score': 0.19619831442832947,
  'token': 30412,
  'token_str': ' mathematical'},
 {'sequence': 'This course will teach you all about computational models.',
  'score': 0.04052725434303284,
  'token': 38163,
  'token_str': ' computational'}]
```

Argumentul `top_k` controlează câte posibilități doriți să fie afișate. Rețineți că aici modelul completează cuvântul special `<mask>`, care este adesea denumit *mask token*. Alte modele de umplere a măștii ar putea avea token-uri de mască diferite, astfel încât este întotdeauna bine să verificați cuvântul de mască adecvat atunci când explorați alte modele. O modalitate de verificare este să vă uitați la cuvântul mască utilizat în widget.

<Tip>

✏️ **Încercați!** Căutați modelul `bert-base-cased` pe Hub și identificați-i cuvântul mască în widget-ul Inference API. Ce prezice acest model pentru propoziția din exemplul nostru `pipeline` de mai sus?

</Tip>

## Named entity recognition[[named-entity-recognition]]

Named Entity Recognition (NER) este o sarcină în care modelul trebuie să găsească care părți din textul de intrare corespund unor entități precum persoane, locații sau organizații. Să ne uităm la un exemplu:

```python
from transformers import pipeline

ner = pipeline("ner", grouped_entities=True)
ner("My name is Sylvain and I work at Hugging Face in Brooklyn.")
```

```python out
[{'entity_group': 'PER', 'score': 0.99816, 'word': 'Sylvain', 'start': 11, 'end': 18}, 
 {'entity_group': 'ORG', 'score': 0.97960, 'word': 'Hugging Face', 'start': 33, 'end': 45}, 
 {'entity_group': 'LOC', 'score': 0.99321, 'word': 'Brooklyn', 'start': 49, 'end': 57}
]
```

Aici, modelul a identificat corect că Sylvain este o persoană (PER), Hugging Face o organizație (ORG), iar Brooklyn o locație (LOC).

Trecem opțiunea `grouped_entities=True` în funcția de creare a pipeline-ului pentru a-i spune pipeline-ului să regrupeze părțile propoziției care corespund aceleiași entități: aici, modelul a grupat corect „Hugging” și „Face” ca o singură organizație, chiar dacă numele este format din mai multe cuvinte. De fapt, după cum vom vedea în capitolul următor, preprocesarea chiar împarte unele cuvinte în părți mai mici. De exemplu, `Sylvain` este împărțit în patru părți: `S`, `##yl`, `##va`, și `##in`. În etapa de postprocesare, pipeline-ul a reușit să regrupeze aceste părți.

<Tip>

✏️ **Încercați!** Căutați în Hub-ul de modele un model capabil să facă etichetarea părții de vorbire (de obicei abreviată ca POS) în limba engleză. Ce prezice acest model pentru propoziția din exemplul de mai sus?

</Tip>

## Question answering[[question-answering]]

Pipeline-ul "question-answering" răspunde la întrebări folosind informații dintr-un context dat:

```python
from transformers import pipeline

question_answerer = pipeline("question-answering")
question_answerer(
    question="Where do I work?",
    context="My name is Sylvain and I work at Hugging Face in Brooklyn",
)
```

```python out
{'score': 0.6385916471481323, 'start': 33, 'end': 45, 'answer': 'Hugging Face'}
```

Rețineți că acest pipeline funcționează prin extragerea informațiilor din contextul furnizat; el nu generează răspunsul.

## Summarization[[summarization]]

Rezumarea este sarcina de a reduce un text într-un altul mai scurt, păstrând toate (sau majoritatea) aspectelor importante menționate în el. Iată un exemplu:

```python
from transformers import pipeline

summarizer = pipeline("summarization")
summarizer(
    """
    America has changed dramatically during recent years. Not only has the number of 
    graduates in traditional engineering disciplines such as mechanical, civil, 
    electrical, chemical, and aeronautical engineering declined, but in most of 
    the premier American universities engineering curricula now concentrate on 
    and encourage largely the study of engineering science. As a result, there 
    are declining offerings in engineering subjects dealing with infrastructure, 
    the environment, and related issues, and greater concentration on high 
    technology subjects, largely supporting increasingly complex scientific 
    developments. While the latter is important, it should not be at the expense 
    of more traditional engineering.

    Rapidly developing economies such as China and India, as well as other 
    industrial countries in Europe and Asia, continue to encourage and advance 
    the teaching of engineering. Both China and India, respectively, graduate 
    six and eight times as many traditional engineers as does the United States. 
    Other industrial countries at minimum maintain their output, while America 
    suffers an increasingly serious decline in the number of engineering graduates 
    and a lack of well-educated engineers.
"""
)
```

```python out
[{'summary_text': ' America has changed dramatically during recent years . The '
                  'number of engineering graduates in the U.S. has declined in '
                  'traditional engineering disciplines such as mechanical, civil '
                  ', electrical, chemical, and aeronautical engineering . Rapidly '
                  'developing economies such as China and India, as well as other '
                  'industrial countries in Europe and Asia, continue to encourage '
                  'and advance engineering .'}]
```

La fel ca în cazul generării de text, puteți specifica o lungime `max_length` sau `min_length` pentru rezultat.

## Translation[[translation]]

Pentru traducere, puteți utiliza un model predefinit dacă introduceți o combinație de limbi în numele sarcinii (cum ar fi `„translation_en_to_fr”`), dar cel mai simplu este să alegeți modelul pe care doriți să îl utilizați în [Model Hub](https://huggingface.co/models). Aici vom încerca să traducem din franceză în engleză:

```python
from transformers import pipeline

translator = pipeline("translation", model="Helsinki-NLP/opus-mt-fr-en")
translator("Ce cours est produit par Hugging Face.")
```

```python out
[{'translation_text': 'This course is produced by Hugging Face.'}]
```

Ca și în cazul generării și rezumării textului, puteți specifica `max_length` sau `min_length` pentru rezultat.
<Tip>

✏️ **Încercați!** Căutați modele de traducere în alte limbi și încercați să traduceți propoziția anterioară în câteva limbi diferite.

</Tip>

Pipeline-urile prezentate până în acest moment au în principal scop demonstrativ. Ele au fost programate pentru sarcini specifice și nu pot efectua variații ale acestora. În capitolul următor, veți afla ce se află în interiorul unei funcții `pipeline()` și cum să îi personalizați comportamentul.

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter1/3.mdx" />

### Modele Decoder[[modele-decoder]]
https://huggingface.co/learn/course/ro/chapter1/6.md

# Modele Decoder[[modele-decoder]]

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

<Youtube id="d_ixlCubqQw" />

Modelele Decoder utilizează doar decoder-ul unui model Transformer. În fiecare etapă, pentru un cuvânt dat, layerele de atenție pot accesa doar cuvintele poziționate înaintea acestuia în propoziție. Aceste modele sunt adesea numite *modele autoregresive*.

Preantrenarea modelelor de decodare se axează de obicei pe prezicerea următorului cuvânt din propoziție.

Aceste modele sunt cele mai potrivite pentru sarcinile care implică generarea de text.

Printre reprezentanții acestei familii de modele se numără:

- [CTRL](https://huggingface.co/transformers/model_doc/ctrl)
- [GPT](https://huggingface.co/docs/transformers/model_doc/openai-gpt)
- [GPT-2](https://huggingface.co/transformers/model_doc/gpt2)
- [Transformer XL](https://huggingface.co/transformers/model_doc/transfo-xl)

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter1/6.mdx" />

### Quiz de sfârșit de capitol[[test-de-sfârșit-de-capitol]]
https://huggingface.co/learn/course/ro/chapter1/10.md

# Quiz de sfârșit de capitol[[test-de-sfârșit-de-capitol]]

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

Acest capitol a acoperit o mulțime de subiecte! Nu vă faceți griji dacă nu ați înțeles toate detaliile; capitolele următoare vă vor ajuta să înțelegeți cum funcționează lucrurile în mecanismele lor interne.

Mai întâi, însă, să testăm ceea ce ați învățat în acest capitol!


### 1. Explorați Hub-ul și căutați checkpoint-ul `roberta-large-mnli`. Ce sarcină îndeplinește acesta?


<Question
	choices={[
		{
			text: "Sumarizare",
			explain: "Uitați-vă din nou pe pagina <a href=\"https://huggingface.co/roberta-large-mnli\">roberta-large-mnli</a>."
		},
		{
			text: "Clasificare de text",
			explain: "Mai precis, clasifică dacă două propoziții sunt legate logic folosind trei etichete (contradicție, neutru, implicație) — o sarcină numită și <em>inferență în limbaj natural</em>.",
			correct: true
		},
		{
			text: "Generare de text",
			explain: "Uitați-vă din nou pe pagina <a href=\"https://huggingface.co/roberta-large-mnli\">roberta-large-mnli</a>."
		}
	]}
/>

### 2. Ce va returna următorul cod?

```py
from transformers import pipeline

ner = pipeline("ner", grouped_entities=True)
ner("My name is Sylvain and I work at Hugging Face in Brooklyn.")
```

<Question
	choices={[
		{
			text: "Va returna scoruri de clasificare pentru această propoziție, cu etichetele \"pozitiv\" sau \"negativ\".",
			explain: "Aceasta este incorect — acesta ar fi un pipeline de <code>sentiment-analysis</code>."
		},
		{
			text: "Va returna un text generat care completează această propoziție.",
			explain: "Aceasta este incorect — ar fi un pipeline de <code>text-generation</code>.",
		},
		{
			text: "Va returna cuvintele care reprezintă persoane, organizații sau locații.",
			explain: "În plus, cu <code>grouped_entities=True</code>, va grupa împreună cuvintele care aparțin aceleiași entități, precum \"Hugging Face\".",
			correct: true
		}
	]}
/>

### 3. Ce ar trebui să înlocuiască ... în acest exemplu de cod?

```py
from transformers import pipeline

filler = pipeline("fill-mask", model="bert-base-cased")
result = filler("...")
```

<Question
	choices={[
		{
			text: "This &#60;mask> has been waiting for you.",
			explain: "Aceasta este incorect. Verificați cardul modelului <code>bert-base-cased</code> și încercați să identificați greșeala."
		},
		{
			text: "This [MASK] has been waiting for you.",
			explain: "Corect! Token-ul de mască al acestui model este [MASK].",
			correct: true
		},
		{
			text: "This man has been waiting for you.",
			explain: "Aceasta este incorect. Acest pipeline completează cuvinte mascate, deci are nevoie de un token de mască undeva."
		}
	]}
/>

### 4. De ce nu va funcționa acest cod?

```py
from transformers import pipeline

classifier = pipeline("zero-shot-classification")
result = classifier("This is a course about the Transformers library")
```

<Question
	choices={[
		{
			text: "Acest pipeline necesită să fie furnizate etichete pentru a clasifica acest text.",
			explain: "Corect — codul corect trebuie să includă <code>candidate_labels=[...]</code>.",
			correct: true
		},
		{
			text: "Acest pipeline necesită mai multe propoziții, nu doar una.",
			explain: "Aceasta este incorect, deși atunci când este utilizat corespunzător, acest pipeline poate primi o listă de propoziții de procesat (ca toate celelalte pipeline-uri)."
		},
		{
			text: "Biblioteca 🤗 Transformers este defectă, ca de obicei.",
			explain: "Nu vom onora acest răspuns cu un comentariu!"
		},
		{
			text: "Acest pipeline necesită intrări mai lungi; aceasta este prea scurtă.",
			explain: "Aceasta este incorect. Rețineți că un text foarte lung va fi trunchiat atunci când este procesat de acest pipeline."
		}
	]}
/>

### 5. Ce înseamnă „transfer learning"?

<Question
	choices={[
		{
			text: "Transferul cunoștințelor unui model preantrenat către un model nou prin antrenarea acestuia pe același set de date.",
			explain: "Nu, aceasta ar însemna două versiuni ale aceluiași model."
		},
		{
			text: "Transferul cunoștințelor unui model preantrenat către un model nou prin inițializarea celui de-al doilea model cu ponderile primului model.",
			explain: "Corect: când al doilea model este antrenat pe o nouă sarcină, acesta *transferă* cunoștințele primului model.",
			correct: true
		},
		{
			text: "Transferul cunoștințelor unui model preantrenat către un model nou prin construirea celui de-al doilea model cu aceeași arhitectură ca primul model.",
			explain: "Arhitectura este doar modul în care este construit modelul; nu există cunoștințe împărtășite sau transferate în acest caz."
		}
	]}
/>

### 6. Adevărat sau fals? De obicei, un model lingvistic nu are nevoie de etichete pentru preinstruire.

<Question
	choices={[
		{
			text: "Adevărat",
			explain: "Preantrenarea este de obicei <em>auto-supervizată</em>, ceea ce înseamnă că etichetele sunt create automat din intrări (cum ar fi prezicerea următorului cuvânt sau completarea unor cuvinte mascate).",
			correct: true
		},
		{
			text: "Fals",
			explain: "Incorect."
		}
	]}
/>

### 7. Selectați propoziția care descrie cel mai bine termenii „model", „arhitectură" și „greutăți".

<Question
	choices={[
		{
			text: "Dacă un model este o clădire, arhitectura sa este planul și greutățile sunt oamenii care locuiesc în interior.",
			explain: "Urmând această metaforă, greutățile ar fi cărămizile și alte materiale utilizate pentru construirea clădirii."
		},
		{
			text: "O arhitectură este o hartă pentru a construi un model, iar greutățile sale sunt orașele reprezentate pe hartă.",
			explain: "Problema cu această metaforă este că o hartă reprezintă de obicei o realitate existentă (există doar un singur oraș în Franța numit Paris). Pentru o arhitectură dată, sunt posibile multiple greutăți."
		},
		{
			text: "O arhitectură este o succesiune de funcții matematice pentru a construi un model, iar greutățile sale sunt parametrii acelor funcții.",
			explain: "Același set de funcții matematice (arhitectură) poate fi utilizat pentru a construi modele diferite prin utilizarea unor parametri diferiți (greutăți).",
			correct: true
		}
	]}
/>


### 8. Care dintre aceste tipuri de modele le-ați folosi pentru a completa prompt-urile cu text generat?

<Question
	choices={[
		{
			text: "Un model encoder",
			explain: "Un model encoder generează o reprezentare a întregii propoziții care este mai potrivită pentru sarcini precum clasificarea."
		},
		{
			text: "Un model decoder",
			explain: "Modelele decoder sunt perfect potrivite pentru generarea de text din prompt-uri.",
			correct: true
		},
		{
			text: "Un model sequence-to-sequence",
			explain: "Modelele sequence-to-sequence sunt mai potrivite pentru sarcini în care doriți să generați propoziții în relație cu propozițiile de intrare, nu cu un prompt dat."
		}
	]}
/>

### 9. Care dintre aceste tipuri de modele le-ați folosi pentru a rezuma texte?

<Question
	choices={[
		{
			text: "Un model encoder",
			explain: "Un model encoder generează o reprezentare a întregii propoziții care este mai potrivită pentru sarcini precum clasificarea."
		},
		{
			text: "Un model decoder",
			explain: "Modelele decoder sunt bune pentru generarea de text de ieșire (cum ar fi rezumatele), dar nu au capacitatea de a exploata un context precum întregul text de rezumat."
		},
		{
			text: "Un model sequence-to-sequence",
			explain: "Modelele sequence-to-sequence sunt perfect potrivite pentru o sarcină de sumarizare.",
			correct: true
		}
	]}
/>

### 10. Care dintre aceste tipuri de modele le-ați utiliza pentru a clasifica intrările de text în funcție de anumite etichete?

<Question
	choices={[
		{
			text: "Un model encoder",
			explain: "Un model encoder generează o reprezentare a întregii propoziții care este perfect potrivită pentru o sarcină precum clasificarea.",
			correct: true
		},
		{
			text: "Un model decoder",
			explain: "Modelele decoder sunt bune pentru generarea de texte de ieșire, nu pentru extragerea unei etichete dintr-o propoziție."
		},
		{
			text: "Un model sequence-to-sequence",
			explain: "Modelele sequence-to-sequence sunt mai potrivite pentru sarcini în care doriți să generați text bazat pe o propoziție de intrare, nu o etichetă.",
		}
	]}
/>

### 11. Ce sursă posibilă poate avea prejudecata observată într-un model?

<Question
	choices={[
		{
			text: "Modelul este o versiune fine-tuned a unui model preantrenat și a preluat prejudecata de la acesta.",
			explain: "Când aplicați Transfer Learning, prejudecata din modelul preantrenat utilizat persistă în modelul fine-tuned.",
			correct: true
		},
		{
			text: "Datele pe care a fost antrenat modelul sunt părtinitoare.",
			explain: "Aceasta este cea mai evidentă sursă de prejudecată, dar nu singura.",
			correct: true
		},
		{
			text: "Metrica pe care modelul o optimiza este părtinitoare.",
			explain: "O sursă mai puțin evidentă de prejudecată este modul în care modelul este antrenat. Modelul dvs. va optimiza orb pentru orice metrică ați ales, fără alte considerații.",
			correct: true
		}
	]}
/>


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter1/10.mdx" />

### Prejudecăți și limitări[[prejudecăți-și-limitări]]
https://huggingface.co/learn/course/ro/chapter1/8.md

# Prejudecăți și limitări[[prejudecăți-și-limitări]]

<CourseFloatingBanner chapter={1}
  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/chapter1/section8.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter1/section8.ipynb"},
]} />

În cazul în care intenționați să utilizați un model preantrenat sau o versiune ajustată în producție, vă rugăm să fiți conștienți de faptul că, deși aceste modele sunt instrumente puternice, ele au anumite limitări. Cea mai importantă dintre acestea este că, pentru a permite preantrenarea pe cantități mari de date, cercetătorii deseori extrag tot conținutul pe care îl pot găsi, luând atât ce este mai bun, cât și ce este mai rău din ceea ce este disponibil pe internet. 

Pentru a oferi o ilustrare rapidă, să ne întoarcem la exemplul unui pipeline `fill-mask` cu modelul BERT:
```python
from transformers import pipeline

unmasker = pipeline("fill-mask", model="bert-base-uncased")
result = unmasker("This man works as a [MASK].")
print([r["token_str"] for r in result])

result = unmasker("This woman works as a [MASK].")
print([r["token_str"] for r in result])
```

```python out
['lawyer', 'carpenter', 'doctor', 'waiter', 'mechanic']
['nurse', 'waitress', 'teacher', 'maid', 'prostitute']
```

Atunci când i se cere să completeze cuvântul care lipsește din aceste două propoziții, modelul dă un singur răspuns fără gen (chelner/ chelneriță). Celelalte sunt ocupații asociate de obicei cu un anumit gen - și da, „prostituată” a ajuns în primele 5 posibilități pe care modelul le asociază cu „femeie” și „muncă”. Acest lucru se întâmplă chiar dacă BERT este unul dintre puținele modele Transformer care nu este construit prin extragerea de date de pe întregul internet, ci mai degrabă folosind date aparent neutre (este antrenat pe seturile de date [English Wikipedia](https://huggingface.co/datasets/wikipedia) și [BookCorpus](https://huggingface.co/datasets/bookcorpus)). 

Prin urmare, atunci când utilizați aceste instrumente, este important să conștientizați că modelul original pe care îl utilizați ar putea genera cu ușurință conținut sexist, rasist sau homofob. Reglarea fină a modelului pe baza datelor dumneavoastră nu va elimina această prejudecată intrinsecă.

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter1/8.mdx" />

### Cum funcționează Transformers?[[cum-funcționează-transformers]]
https://huggingface.co/learn/course/ro/chapter1/4.md

# Cum funcționează Transformers?[[cum-funcționează-transformers]]

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

În această secțiune, vom analiza arhitectura modelelor Transformer.

## Un pic despre istoria modelelor Transformer[[un-pic-despre-istoria-modelelor-transformer]]

Iată câteva puncte de referință în (scurta) istorie a modelelor Transformer:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers_chrono.svg" alt="A brief chronology of Transformers models.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers_chrono-dark.svg" alt="A brief chronology of Transformers models.">
</div>

[Arhitectura Transformer](https://arxiv.org/abs/1706.03762) a fost introdusă în iunie 2017. Cercetarea inițială s-a concentrat pe sarcinile de traducere. Ulterior, au fost introduse mai multe modele influente, inclusiv:

- **Iunie 2018**: [GPT](https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf), primul model Transformer preinstruit, utilizat pentru fine-tuning-ul diferitor sarcini NLP și a obținut rezultate de top

- **Octombrie 2018**: [BERT](https://arxiv.org/abs/1810.04805), un alt model mare preantrenat, conceput pentru a produce rezumate mai bune ale propozițiilor (mai multe despre el în capitolul următor!)

- **Februarie 2019**: [GPT-2](https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf), o versiune îmbunătățită (și mai mare) a GPT care nu a fost lansată public imediat din motive etice

- **Octombrie 2019**: [DistilBERT](https://arxiv.org/abs/1910.01108), o versiune distilată a BERT care este cu 60% mai rapidă, cu 40% mai ușoară în memorie și care păstrează încă 97% din performanța BERT

- **Octombrie 2019**: [BART](https://arxiv.org/abs/1910.13461) și [T5](https://arxiv.org/abs/1910.10683), două modele mari preantrenate folosind aceeași arhitectură ca modelul original Transformer (primul care face acest lucru)

- **Mai 2020**, [GPT-3](https://arxiv.org/abs/2005.14165), o versiune și mai mare a GPT-2, care este capabilă să se descurce bine într-o varietate de sarcini, fără a fi nevoie de o reglare fină (numită _zero-shot learning_)

Această listă este departe de a fi completă și este menită doar să evidențieze câteva dintre diferitele tipuri de modele Transformer. În linii mari, acestea pot fi grupate în trei categorii:

- Tip GPT (numite și modele de Transformer autoregresive)
- Tip BERT (numite și modele de Transformer _auto-encoding_) 
- Tip BART/T5 (denumite și modele de Transformer _sequence-to-sequence_)

Vom analiza aceste familii în detaliu mai târziu.

## Transformers sunt modele de limbaj[transformers-sunt-modele-de-limbaj]]

Toate modelele Transformer menționate mai sus (GPT, BERT, BART, T5 etc.) au fost antrenate ca *modele lingvistice*. Aceasta înseamnă că au fost antrenate pe volume mari de text brut într-un mod autosupravegheat. Învățarea autosupravegheată este un tip de antrenare în care obiectivul este calculat automat din datele de intrare ale modelului. Aceasta înseamnă că nu este nevoie de oameni pentru a eticheta datele!

Acest tip de model dezvoltă o înțelegere statistică a limbii pe care a fost antrenat, dar nu este foarte util pentru sarcini practice specifice. Din acest motiv, modelul general preantrenat trece apoi printr-un proces numit *transfer learning*. În timpul acestui proces, modelul este ajustat într-un mod supravegheat - adică folosind etichete notate de oameni - pentru o anumită sarcină.

Un exemplu de sarcină este prezicerea următorului cuvânt dintr-o propoziție după citirea a *n* cuvinte anterioare. Aceasta se numește *modelare cauzală a limbajului* deoarece rezultatul depinde de intrările trecute și prezente, dar nu și de cele viitoare.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/causal_modeling.svg" alt="Example of causal language modeling in which the next word from a sentence is predicted.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/causal_modeling-dark.svg" alt="Example of causal language modeling in which the next word from a sentence is predicted.">
</div>

Un alt exemplu este *modelarea limbajului mascat*, în care modelul prezice un cuvânt mascat din propoziție.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/masked_modeling.svg" alt="Example of masked language modeling in which a masked word from a sentence is predicted.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/masked_modeling-dark.svg" alt="Example of masked language modeling in which a masked word from a sentence is predicted.">
</div>

## Modelele Transformer sunt mari[[modelele-transformer-sunt-mari]]

Cu excepția câtorva cazuri excepționale (cum ar fi DistilBERT), strategia generală pentru a obține performanțe mai bune constă în creșterea dimensiunilor modelelor, precum și a cantității de date pe care acestea sunt preantrenate.

<div class="flex justify-center">
<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/model_parameters.png" alt="Number of parameters of recent Transformers models" width="90%">
</div>

Din păcate, antrenarea unui model, în special a unuia mare, necesită o cantitate mare de date. Acest lucru devine foarte costisitor în termeni de timp și resurse de calcul. Aceasta se reflectă chiar și în impactul asupra mediului, după cum se poate vedea în graficul următor.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/carbon_footprint.svg" alt="The carbon footprint of a large language model.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/carbon_footprint-dark.svg" alt="The carbon footprint of a large language model.">
</div>

<Youtube id="ftWlj4FBHTg"/>

Iar acest lucru arată un proiect pentru un model (foarte mare) condus de o echipă care încearcă în mod conștient să reducă impactul de mediu al preinstruirii. Amprenta lăsată de efectuarea multor teste pentru a obține cei mai buni hiperparametri ar fi și mai mare.

Imaginați-vă dacă de fiecare dată când o echipă de cercetare, o organizație studențească sau o companie ar dori să antreneze un model, ar face-o de la zero. Acest lucru ar conduce la costuri globale uriașe și inutile!

Acesta este motivul pentru care partajarea modelelor lingvistice este esențială: partajarea ponderilor antrenate și construirea pe baza ponderilor deja antrenate reduce costul global de calcul și amprenta de carbon a comunității.

Apropo, puteți evalua amprenta de carbon a formării modelelor dvs. prin intermediul mai multor instrumente. De exemplu [ML CO2 Impact](https://mlco2.github.io/impact/) sau [Code Carbon]( https://codecarbon.io/) care este integrat în 🤗 Transformers. Pentru a afla mai multe despre acest lucru, puteți citi această [postare pe blog](https://huggingface.co/blog/carbon-emissions-on-the-hub) care vă va arăta cum să generați un fișier `emissions.csv` cu o estimare a amprentei formării dvs., precum și [documentația](https://huggingface.co/docs/hub/model-cards-co2) din 🤗 Transformers care abordează acest subiect.


## Transfer Learning[[transfer-learning]]

<Youtube id="BqqfQnyjmgg" />

*Preinstruirea* este acțiunea de formare a unui model de la zero: ponderile sunt inițializate aleatoriu, iar formarea începe fără nicio cunoaștere prealabilă.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/pretraining.svg" alt="The pretraining of a language model is costly in both time and money.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/pretraining-dark.svg" alt="The pretraining of a language model is costly in both time and money.">
</div>

Această preinstruire se face de obicei pe cantități foarte mari de date. Prin urmare, este nevoie de un corpus foarte mare de date, iar formarea poate dura până la câteva săptămâni.

*Reglarea-fină*, pe de altă parte, este instruirea efectuată **după** ce un model a fost preinstruit. Pentru a efectua reglarea fină, obțineți mai întâi un model lingvistic preinstruit, apoi efectuați o instruire suplimentară cu un set de date specific sarcinii dumneavoastră. Stați - de ce să nu instruiți de la început modelul pentru cazul dvs. final de utilizare (**scratch**)? Există câteva motive:

* Modelul preinstruit a fost deja instruit pe un set de date care are unele similitudini cu setul de date pentru reglarea fină. Procesul de reglare fină este astfel capabil să profite de cunoștințele dobândite de modelul inițial în timpul preantrenării (de exemplu, în cazul problemelor de NLP, modelul preantrenat va avea un anumit tip de înțelegere statistică a limbajului pe care îl utilizați pentru sarcina dumneavoastră). 
* Deoarece modelul preinstruit a fost deja instruit pe o mulțime de date, fine-tuning-ul necesită mult mai puține date pentru a obține rezultate decente.
* Din același motiv, timpul și resursele necesare pentru a obține rezultate bune sunt mult mai reduse.

De exemplu, se poate folosi un model preantrenat pe limba engleză și ajustat ulterior pe un corpus arXiv, obținându-se astfel un model adaptat domeniului științific/cercetare. Fine-tuning-ul necesita doar o cantitate limitată de date: cunoștințele dobândite de modelul preantrenat sunt „transferate”, de unde și termenul de *transfer learning*.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/finetuning.svg" alt="The fine-tuning of a language model is cheaper than pretraining in both time and money.">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/finetuning-dark.svg" alt="The fine-tuning of a language model is cheaper than pretraining in both time and money.">
</div>

Prin urmare, reglarea fină a unui model are costuri mai reduse în ceea ce privește timpul, datele, resursele financiare și impactul asupra mediului. De asemenea, este mai rapid și mai ușor să experimentați pe diferite scheme de fine-tuning, deoarece antrenarea este mai puțin constrângătoare decât o preantrenare completă.

Acest proces va obține, de asemenea, rezultate mai bune decât formarea de la zero (cu excepția cazului în care dispuneți de o mulțime de date), motiv pentru care ar trebui să încercați întotdeauna să valorificați un model preformat - unul cât mai apropiat posibil de sarcina pe care o aveți la îndemână - și să îl reglați fin.

## Arhitectura generală[[arhitectura-generală]]

În această secțiune, vom analiza arhitectura generală a modelului Transformer. Nu vă faceți griji dacă nu înțelegeți anumite concepte; mai târziu există secțiuni detaliate care acoperă fiecare dintre componente.

<Youtube id="H39Z_720T5s" />

## Introducere[[introducere]]

Modelul este compus în principal din două blocuri:

* **Codificator (stânga)**: Codificatorul primește o intrare și construiește o reprezentare a acesteia (caracteristicile sale). Aceasta înseamnă că modelul este optimizat pentru a dobândi cunoștințe din datele de intrare.
* **Decodificator (dreapta)**: Decodificatorul utilizează reprezentarea codificatorului (caracteristicile) împreună cu alte intrări pentru a genera o secvență țintă. Aceasta înseamnă că modelul este optimizat pentru a genera ieșiri.

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers_blocks.svg" alt="Architecture of a Transformers models">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers_blocks-dark.svg" alt="Architecture of a Transformers models">
</div>

Fiecare dintre aceste părți poate fi utilizată independent, în funcție de sarcină: 

* **Modeluri exclusiv de codare**: Bune pentru sarcini care necesită înțelegerea datelor de intrare, cum ar fi clasificarea propozițiilor și recunoașterea entităților numite.
* **Modele exclusiv decodificatoare**: Bune pentru sarcini generative, cum ar fi generarea de text.
* **Modele encoder-decoder** sau **modele sequence-to-sequence**: Bune pentru sarcinile generative care necesită o intrare, cum ar fi traducerea sau rezumarea.

Vom analiza aceste arhitecturi în mod independent în secțiunile următoare.

## Attention layers(straturi de atenție)[[attention-layers]]

O caracteristică cheie a modelelor Transformer este faptul că acestea sunt construite cu straturi speciale numite *attention layers*. De fapt, titlul lucrării de prezentare a arhitecturii Transformer a fost [„Attention Is All You Need”] (https://arxiv.org/abs/1706.03762)! Vom explora detaliile attention layers-urilor mai târziu în curs; pentru moment, tot ce trebuie să știți este că acest strat va spune modelului să acorde o atenție specifică anumitor cuvinte din propoziția pe care i-ați transmis-o (și să le ignore mai mult sau mai puțin pe celelalte) atunci când se ocupă de reprezentarea fiecărui cuvânt.

Pentru a pune acest lucru în context, luați în considerare sarcina de a traduce un text din engleză în franceză. Având în vedere intrarea „Vă place acest curs”, un model de traducere va trebui să se ocupe și de cuvântul adiacent „You” pentru a obține traducerea corectă pentru cuvântul „like”, deoarece în franceză verbul „like” se conjugă diferit în funcție de subiect. Cu toate acestea, restul propoziției nu este util pentru traducerea acestui cuvânt. În aceeași ordine de idei, la traducerea cuvântului „this”, modelul va trebui, de asemenea, să acorde atenție cuvântului „course”, deoarece „this” se traduce diferit în funcție de faptul dacă substantivul asociat este masculin sau feminin. Din nou, celelalte cuvinte din propoziție nu vor conta pentru traducerea cuvântului „course”. În cazul unor propoziții mai complexe (și al unor reguli gramaticale mai complexe), modelul ar trebui să acorde o atenție deosebită cuvintelor care ar putea apărea mai departe în propoziție pentru a traduce corect fiecare cuvânt.

Același concept se aplică oricărei sarcini asociate cu limbajul natural: un cuvânt în sine are o semnificație, dar această semnificație este profund afectată de context, care poate fi orice alt cuvânt (sau cuvinte) înainte sau după cuvântul studiat.

Acum, că aveți o idee despre ce sunt attention layers, să analizăm mai îndeaproape arhitectura Transformer.

## Arhitectura originală[[arhitectura-originală]

Arhitectura Transformer a fost concepută inițial pentru traducere. În timpul formării, codificatorul primește intrări (propoziții) într-o anumită limbă, în timp ce decodificatorul primește aceleași propoziții în limba țintă dorită. În codificator, attention layers pot utiliza toate cuvintele dintr-o propoziție (deoarece, după cum tocmai am văzut, traducerea unui anumit cuvânt poate depinde de ceea ce se află înainte și după el în propoziție). Cu toate acestea, decodificatorul funcționează secvențial și poate acorda atenție numai cuvintelor din propoziție pe care le-a tradus deja (deci, numai cuvintelor anterioare cuvântului generat în prezent). De exemplu, atunci când am prezis primele trei cuvinte din ținta tradusă, le dăm decodificatorului, care utilizează apoi toate intrările codificatorului pentru a încerca să prezică al patrulea cuvânt.

Pentru a accelera lucrurile în timpul formării (atunci când modelul are acces la propozițiile țintă), decodorul primește întreaga țintă, dar nu i se permite să utilizeze cuvintele viitoare (dacă ar avea acces la cuvântul din poziția 2 atunci când încearcă să prezică cuvântul din poziția 2, problema nu ar fi foarte dificilă!) De exemplu, atunci când încearcă să prezică al patrulea cuvânt, stratul de atenție va avea acces doar la cuvintele de la pozițiile 1 și 3.

Arhitectura originală a Transformer arăta astfel, cu codificatorul în stânga și decodificatorul în dreapta:

<div class="flex justify-center">
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers.svg" alt="Architecture of a Transformers models">
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter1/transformers-dark.svg" alt="Architecture of a Transformers models">
</div>

Rețineți că primul attention layer dintr-un bloc decodor acordă atenție tuturor intrărilor (trecute) către decodor, dar al doilea attention layer utilizează ieșirea codorului. Astfel, acesta poate accesa întreaga propoziție de intrare pentru a prezice cel mai bine cuvântul curent. Acest lucru este foarte util deoarece diferite limbi pot avea reguli gramaticale care pun cuvintele în ordine diferită sau un context furnizat mai târziu în propoziție poate fi util pentru a determina cea mai bună traducere a unui cuvânt dat.

*attention mask* poate fi, de asemenea, utilizată în codificator/decodificator pentru a împiedica modelul să acorde atenție anumitor cuvinte speciale - de exemplu, cuvântul special de umplutură utilizat pentru a face ca toate intrările să aibă aceeași lungime atunci când se grupează propozițiile.

##  Architectura vs. punctele de control[[architectura-vs-punctele-de-control]]

Pe măsură ce analizăm modelele Transformer în acest curs, veți vedea mențiuni despre *arhitecturi* și *puncte de control*, precum și despre *modele*. Toți acești termeni au semnificații ușor diferite: 

* **Arhitectură**: Acesta este scheletul modelului - definiția fiecărui strat și a fiecărei operațiuni care are loc în cadrul modelului. 
* **Puncte de control**: Acestea sunt ponderile care vor fi încărcate într-o anumită arhitectură.
* **Model**: Acesta este un termen generic care nu este la fel de precis ca „arhitectură” sau „punct de control”: poate însemna ambele. Acest curs va specifica *arhitectură* sau *punct de control* atunci când este necesar pentru a reduce ambiguitatea.

De exemplu, BERT este o arhitectură, în timp ce `bert-base-cased`, un set de ponderi antrenate de echipa Google pentru prima versiune a BERT, este un punct de control. Cu toate acestea, se poate spune „modelul BERT” și „modelul `bert-base-cased`”.

<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter1/4.mdx" />

### Modele Sequence-to-sequence[modele-sequence-to-sequence]
https://huggingface.co/learn/course/ro/chapter1/7.md

# Modele Sequence-to-sequence[modele-sequence-to-sequence]

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

<Youtube id="0_4KEb08xrE" />

Modelele Encoder-Decoder (denumite și *modele sequence-to-sequence*) utilizează ambele părți ale arhitecturii Transformer. În fiecare etapă, layerele de atenție ale encoder-ului pot accesa toate cuvintele din propoziția inițială, în timp ce layerele de atenție ale decoder-ului pot accesa doar cuvintele poziționate înaintea unui anumit cuvânt din intrare.

Preantrenarea acestor modele se poate face folosind obiectivele modelelor de codificare sau de decodificare, dar de obicei implică ceva un pic mai complex. De exemplu, [T5](https://huggingface.co/t5-base) este prenatrenat prin înlocuirea unor intervale aleatorii de text (care pot conține mai multe cuvinte) cu un singur cuvânt special mascat, iar obiectivul este apoi de a prezice textul pe care îl înlocuiește acest cuvânt mascat.

Modelele Sequence-to-sequence sunt cele mai potrivite pentru sarcinile care se învârt în jurul generării de noi propoziții în funcție de o intrare dată, cum ar fi rezumarea, traducerea sau răspunsul generativ la întrebări.

Printre reprezentanții acestei familii de modele se numără:
- [BART](https://huggingface.co/transformers/model_doc/bart)
- [mBART](https://huggingface.co/transformers/model_doc/mbart)
- [Marian](https://huggingface.co/transformers/model_doc/marian)
- [T5](https://huggingface.co/transformers/model_doc/t5)


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter1/7.mdx" />

### Procesarea limbajului natural[[procesarea-limbajului-natural]]
https://huggingface.co/learn/course/ro/chapter1/2.md

# Procesarea limbajului natural[[procesarea-limbajului-natural]]

<CourseFloatingBanner
    chapter={1}
    classNames="absolute z-10 right-0 top-0"
/>

Înainte de a trece la modelele Transformer, haideți să facem o prezentare scurtă despre ce este procesarea limbajului natural și de ce ne interesează.

## Ce este NLP?[[ce-este-nlp]]

NLP este un domeniu al lingvisticii și al învățării automate axat pe înțelegerea a tot ceea ce este legat de limbajul uman. Scopul sarcinilor NLP nu este doar înțelegerea individuală a cuvintelor, ci și capacitatea de a înțelege contextul acelor cuvinte.

În continuare urmează o listă a sarcinilor obișnuite de NLP, cu câteva exemple pentru fiecare:

- **Clasificarea propozițiilor întregi**: Determinarea caracterului unei recenzii, detectarea dacă un e-mail este spam, determinarea dacă o propoziție este corectă gramatical sau dacă două propoziții sunt legate logic sau nu
- **Clasificarea fiecărui cuvânt dintr-o propoziție**: Identificarea componentelor gramaticale ale unei propoziții (substantiv, verb, adjectiv) sau a entităților nominalizate (persoană, locație, organizație)
- **Generarea conținutului de text**: Completarea unui prompt cu text generat automat, completarea spațiilor goale dintr-un text cu cuvinte mascate
- **Extragerea unui răspuns dintr-un text**: Având o întrebare și un context, se extrage răspunsul la întrebare pe baza informațiilor furnizate în context
- **Generarea unei propoziții noi dintr-un text de intrare**: Traducerea unui text într-o altă limbă, rezumarea textului

NLP nu se limitează însă la textul scris. De asemenea, NLP abordează provocări complexe în domeniul recunoașterii vorbirii și al viziunii computerizate, cum ar fi generarea unei transcrieri a unui fragment audio sau a unei descrieri a unei imagini.

## Ascensiunea a Large Language Models (LLM-uri)[[rise-of-llms]]

În ultimii ani, domeniul NLP a fost revoluționat de Large Language Models (LLM-uri). Aceste modele, care includ arhitecturi precum GPT (Generative Pre-trained Transformer) și [Llama](https://huggingface.co/meta-llama), au transformat ceea ce este posibil în procesarea limbajului.

LLM-urile se caracterizează prin:
- **Mărime**: Conțin milioane, miliarde sau chiar sute de miliarde de parametri
- **Capacități generale**: Pot realiza multiple sarcini fără antrenament specific pentru fiecare sarcină
- **Învățare în context**: Pot învăța din exemplele oferite în prompt
- **Abilități emergente**: Pe măsură ce aceste modele cresc în dimensiune, demonstrează capacități care nu au fost programate sau anticipate în mod explicit

Apariția LLM-urilor a schimbat paradigma de la construirea unor modele specializate pentru sarcini NLP specifice la utilizarea unui singur model mare care poate fi direcționat prin prompturi sau ajustat pentru a aborda o gamă largă de sarcini lingvistice. Acest lucru a făcut procesarea sofisticată a limbajului mai accesibilă, dar a introdus și noi provocări în domenii precum eficiența, etica și implementarea.

## De ce este o provocare?[[de-ce-este-o-provocare]]

Computerele nu procesează informațiile în același mod ca oamenii. De exemplu, atunci când citim propoziția „Mi-e foame”, îi putem înțelege cu ușurință semnificația. În mod similar, având în vedere două propoziții precum „Mi-e foame” și „Sunt trist”, suntem capabili să determinăm cu ușurință cât de asemănătoare sunt acestea. Pentru modelele de învățare automată (ML), astfel de sarcini sunt mai dificile. Textul trebuie prelucrat într-un mod care să permită modelului să învețe din conținutul său. Și deoarece limbajul este complex, trebuie să ne gândim cu atenție la modul optim de a realiza această procesare. Au fost efectuate numeroase cercetări cu privire la modul de reprezentare a textului și vom analiza câteva metode în capitolul următor.


<EditOnGithub source="https://github.com/huggingface/course/blob/main/chapters/ro/chapter1/2.mdx" />
