Pytorch
TensorFlow

#
Menangani Banyak Urutan

Pada bagian sebelumnya, kita telah menjelajahi kasus penggunaan paling sederhana: melakukan inferensi pada satu urutan pendek. Namun, sejumlah pertanyaan muncul:

- Bagaimana cara menangani beberapa urutan sekaligus?
- Bagaimana jika panjang urutannya berbeda-beda?
- Apakah indeks kosa kata satu-satunya input yang dibutuhkan model untuk bekerja optimal?
- Apakah ada batas maksimal panjang urutan?

Mari kita lihat jenis masalah yang ditimbulkan oleh pertanyaan-pertanyaan ini, dan bagaimana kita bisa menyelesaikannya dengan API 🤗 Transformers.

##
Model Mengharapkan Input dalam Bentuk Batch

Pada latihan sebelumnya, Anda telah melihat bagaimana urutan diubah menjadi daftar angka. Mari kita konversi daftar angka ini menjadi tensor dan kirim ke model:

    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)
    # Baris ini akan gagal.
    model(input_ids)

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

Kenapa ini gagal? Padahal kita sudah mengikuti langkah-langkah seperti pada pipeline di bagian 2.

Masalahnya adalah kita mengirimkan satu urutan (sequence) ke model, padahal model 🤗 Transformers secara default mengharapkan beberapa kalimat (multiple sentences). Di sini, kita mencoba melakukan semua yang dilakukan tokenizer secara otomatis di balik layar ketika kita menerapkannya pada sebuah `sequence`. Namun, jika Anda memperhatikan dengan seksama, Anda akan melihat bahwa tokenizer tidak hanya mengubah daftar ID input menjadi tensor, tapi juga menambahkan satu dimensi di atasnya:

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

Mari kita coba lagi dengan menambahkan dimensi batch:

    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)

Kita mencetak ID input serta logits yang dihasilkan — berikut adalah output-nya:

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

Batching adalah proses mengirim banyak kalimat sekaligus ke model. Jika hanya satu kalimat, Anda tetap bisa membentuk batch dengan satu urutan:

    batched_ids = [ids, ids]

Ini adalah batch berisi dua urutan identik!

✏️ Coba sendiri! Ubah `batched_ids` menjadi tensor dan kirim ke model Anda. Periksa apakah Anda mendapatkan logits yang sama seperti sebelumnya (tapi dua kali)!

Batching memungkinkan model untuk bekerja ketika Anda memberikannya beberapa kalimat sekaligus. Menggunakan beberapa urutan (sequence) semudah membuat batch dari satu urutan. Namun, ada masalah kedua. Saat Anda mencoba menggabungkan dua (atau lebih) kalimat dalam satu batch, panjangnya mungkin berbeda-beda. Jika Anda pernah bekerja dengan tensor sebelumnya, Anda tahu bahwa tensor harus memiliki bentuk yang persegi panjang, sehingga Anda tidak bisa langsung mengonversi daftar ID input menjadi tensor. Untuk mengatasi masalah ini, biasanya kita melakukan padding pada input.

##
Padding Input

Berikut adalah contoh daftar yang tidak bisa langsung dikonversi menjadi tensor:

    batched_ids = [
        [200, 200, 200],
        [200, 200]
    ]

Untuk mengatasi hal ini, kita akan menggunakan padding agar tensor kita memiliki bentuk yang persegi panjang. Padding memastikan bahwa semua kalimat memiliki panjang yang sama dengan menambahkan kata khusus yang disebut padding token ke kalimat-kalimat yang memiliki jumlah kata lebih sedikit. Sebagai contoh, jika Anda memiliki 10 kalimat dengan 10 kata dan 1 kalimat dengan 20 kata, padding akan memastikan semua kalimat memiliki 20 kata. Dalam contoh kita, tensor yang dihasilkan akan terlihat seperti ini:

    padding_id = 100

    batched_ids = [
        [200, 200, 200],
        [200, 200, padding_id],
    ]

Token padding dapat ditemukan melalui `tokenizer.pad_token_id`. Mari kita coba kirim dua kalimat secara individual dan dalam batch:

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

Ada sesuatu yang salah dengan logits pada prediksi batch kita: baris kedua seharusnya sama dengan logits untuk kalimat kedua, tetapi yang kita dapat justru nilai-nilai yang benar-benar berbeda!

Ini terjadi karena fitur utama dari model Transformer adalah lapisan attention yang mengontekstualisasi setiap token. Lapisan-lapisan ini akan mempertimbangkan token padding karena mereka memperhatikan semua token dalam sebuah urutan. Untuk mendapatkan hasil yang sama ketika memproses kalimat satu per satu dengan panjang berbeda, maupun ketika memproses batch yang berisi kalimat-kalimat tersebut dengan padding, kita perlu memberi tahu lapisan attention agar mengabaikan token padding. Hal ini dilakukan dengan menggunakan attention mask.

##
Attention Mask

Attention mask adalah tensor yang memiliki bentuk (shape) yang sama persis dengan tensor input ID, dan diisi dengan angka 0 dan 1: angka 1 menunjukkan bahwa token yang bersangkutan harus diperhatikan (attended to), sedangkan angka 0 menunjukkan bahwa token tersebut tidak perlu diperhatikan (yaitu, harus diabaikan oleh lapisan attention dalam model).

Lengkapi contoh sebelumnya dengan attention mask:

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

Sekarang hasil untuk kalimat kedua di batch cocok dengan hasil individualnya.

Perhatikan bahwa nilai terakhir pada urutan kedua adalah token padding, dan di attention mask diberi nilai 0.

✏️ Coba sendiri! Tokenisasi manual dua kalimat dari bagian 2:

- “I’ve been waiting for a HuggingFace course my whole life.”
- “I hate this so much!”
  Kirim secara individual ke model, lalu gabungkan dalam batch dengan padding dan attention mask. Pastikan hasilnya tetap sama!

##
Urutan Lebih Panjang

Dengan model Transformer, ada batasan panjang urutan (sequence) yang bisa kita berikan ke model. Sebagian besar model hanya dapat menangani urutan hingga 512 atau 1024 token, dan akan mengalami kegagalan jika diminta memproses urutan yang lebih panjang. Ada dua solusi untuk masalah ini:

- Gunakan model yang mendukung panjang urutan yang lebih panjang.
- Potong (truncate) urutan Anda.

Model memiliki dukungan panjang urutan yang berbeda-beda, dan beberapa memang dirancang khusus untuk menangani urutan yang sangat panjang. Longformer adalah salah satu contohnya, dan lainnya adalah LED. Jika Anda mengerjakan tugas yang memerlukan urutan sangat panjang, kami menyarankan Anda untuk melihat model-model tersebut.

Jika tidak, kami menyarankan Anda untuk memotong (truncate) urutan Anda dengan menentukan parameter `max_sequence_length`:

    sequence = sequence[:max_sequence_length]

< > Update on GitHub