#
Ce să faci când primești o eroare

Î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, unde vom explora cum să depanați faza de antrenament în sine.

Am pregătit un repository model șablon 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. Pentru a face acest lucru, mai întâi conectați-vă rulând fie următoarele într-un notebook Jupyter:

    from huggingface_hub import notebook_login

    notebook_login()

sau următoarele în terminalul dumneavoastră preferat:

    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:

    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

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

    from transformers import pipeline

    model_checkpoint = get_full_repo_name("distillbert-base-uncased-finetuned-squad-d5716d28")
    reader = pipeline("question-answering", model=model_checkpoint)

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

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 pentru mai multe detalii despre cum funcționează `pipeline` în culise.)

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

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:

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

💡 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 (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 care ar putea fi folosite ca punct de plecare pentru rezolvarea problemei.

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:

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:

Bun, aceasta a dat un rezultat. Acum să încercăm să descărcăm din nou modelul cu ID-ul corect:

    model_checkpoint = get_full_repo_name("distilbert-base-uncased-finetuned-squad-d5716d28")
    reader = pipeline("question-answering", model=model_checkpoint)

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

    from huggingface_hub import list_repo_files

    list_repo_files(repo_id=model_checkpoint)

    ['.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`, 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, putem descărca configurația modelului cu clasa `AutoConfig`:

    from transformers import AutoConfig

    pretrained_checkpoint = "distilbert-base-uncased"
    config = AutoConfig.from_pretrained(pretrained_checkpoint)

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

Apoi putem împinge aceasta în repository-ul nostru de model cu funcția `push_to_hub()` a configurației:

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

    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)

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

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:

    tokenizer = reader.tokenizer
    model = reader.model

Apoi avem nevoie de o întrebare, așa că să vedem dacă framework-urile noastre preferate sunt suportate:

    question = "Which frameworks can I use?"

Așa cum am văzut în Capitolul 7, 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:

    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}")

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

sau într-un terminal:

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:

    inputs["input_ids"][:5]

    [101, 2029, 7705, 2015, 2064]

Aceasta cu siguranță arată ca o `list` Python obișnuită, dar să verificăm din nou tipul:

    type(inputs["input_ids"])

    list

Da, aceasta este cu siguranță o `list` Python. Deci ce a mers prost? Amintiți-vă din Capitolul 2 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 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:

Răspunsul recomandă să adăugăm `return_tensors='pt'` la tokenizer, așa că să vedem dacă aceasta funcționează pentru noi:

    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}")

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

< > Update on GitHub