Pytorch
TensorFlow

#
Debugging-ul pipeline-ului de antrenament

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

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:

    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ă:

    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

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:

    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:

    {'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ă:

    model.compile(optimizer="adam")

Acum vom folosi loss-ul intern al modelului, și această problemă ar trebui să fie rezolvată!

✏️ 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?

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!

      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

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

    model(batch)

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

    model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint)
    model(batch)

Când rulăm asta, primim:

    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):

    import numpy as np

    loss = model(batch).loss.numpy()
    indices = np.flatnonzero(np.isnan(loss))
    indices

    array([ 1,  2,  5,  7,  9, 10, 11, 13, 14])

Să ne uităm la eșantioanele din care au venit acești indici:

    input_ids = batch["input_ids"].numpy()
    input_ids[indices]

    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:

    labels = batch['labels'].numpy()
    labels[indices]

    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:

    model.config.num_labels

    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)

      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

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.

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:

    from tensorflow.keras.optimizers import Adam

    model = TFAutoModelForSequenceClassification.from_pretrained(model_checkpoint)
    model.compile(optimizer=Adam(5e-5))

💡 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.

Acum, putem încerca să încadrăm modelul cu noua rată de învățare îmbunătățită:

    model.fit(train_dataset)

    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

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

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.

Î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.

###
TensorFlow flămând flămând 🦛

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!

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:

    input_ids = batch["input_ids"].numpy()
    tokenizer.decode(input_ids[0])

Apoi puteți să îl comparați cu prima etichetă, astfel:

    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

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:

    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)

💡 Dacă datele voastre de antrenament sunt dezechilibrate, asigurați-vă să construiți un batch de date de antrenament care conține toate etichetele.

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.

⚠️ 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.

###
Nu ajustați nimic până nu aveți o primă linie de bază

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

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.

Iată câteva resurse suplimentare care se pot dovedi utile:

- “Reproducibility as a vehicle for engineering best practices” de Joel Grus
- “Checklist for debugging neural networks” de Cecelia Shao
- “How to unit test machine learning code” de Chase Roberts
- “A Recipe for Training Neural Networks” 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.

< > Update on GitHub