Pytorch
TensorFlow

#
Gestionarea secvențelor multiple

Î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

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

    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)

    IndexError: Dimension out of range (expected to be in range of [-1, 0], but got 1)

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:

    tokenized_inputs = tokenizer(sequence, return_tensors="pt")
    print(tokenized_inputs["input_ids"])

    tensor([[  101,  1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172,
              2607,  2026,  2878,  2166,  1012,   102]])

Să încercăm din nou și să adăugăm o nouă dimensiune:

    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)

Imprimăm ID-urile de intrare, precum și logit-urile rezultate - iată rezultatul:

    Input IDs: [[ 1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172,  2607, 2026,  2878,  2166,  1012]]
    Logits: [[-2.7276,  2.8789]]

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!

✏️ Î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)!

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

Următoarea serie de liste nu poate fi convertită într-un tensor:

    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:

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

    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)

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

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)

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:

    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)

    tensor([[ 1.5694, -1.3895],
            [ 0.5803, -0.4125]], grad_fn=<AddmmBackward>)

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.

✏️ Î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!

##
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 este un exemplu, iar altul este 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`:

    sequence = sequence[:max_sequence_length]

< > Update on GitHub