LLM 課程文件

處理多個序列

Hugging Face's logo
加入 Hugging Face 社群

並獲得增強的文件體驗

開始使用

處理多個序列

Ask a Question Open In Colab Open In Studio Lab

在上一節中,我們探討了最簡單的用例:對單個短序列進行推理。然而,一些問題已經浮現:

  • 我們如何處理多個序列?
  • 我們如何處理不同長度的多個序列?
  • 詞彙表索引是唯一能讓模型良好工作的輸入嗎?
  • 序列是否存在過長的情況?

讓我們看看這些問題會帶來哪些挑戰,以及我們如何使用 🤗 Transformers API 來解決它們。

模型需要批次輸入

在前面的練習中,你看到了序列如何被轉換為數字列表。現在,讓我們將這個數字列表轉換為張量併發送給模型。

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)
# This line will fail.
model(input_ids)
IndexError: Dimension out of range (expected to be in range of [-1, 0], but got 1)

噢不!為什麼失敗了?我們不是按照第2節中管道的步驟操作了嗎?

問題在於我們向模型傳送了一個單獨的序列,而 🤗 Transformers 模型預設期望多條句子。這裡我們嘗試了分詞器在幕後對 `sequence` 進行的所有操作。但如果你仔細觀察,你會發現分詞器不僅僅將輸入ID列表轉換為張量,它還在其之上添加了一個維度。

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

讓我們再試一次,並新增一個新維度。

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)

我們列印輸入 ID 以及結果 logits — 輸出如下:

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

批處理(Batching)是指一次性將多條句子透過模型。如果你只有一條句子,你也可以只用一條序列構建一個批次。

batched_ids = [ids, ids]

這是包含兩個相同序列的批次!

✏️ 試一試! 將這個 `batched_ids` 列表轉換為張量並傳遞給你的模型。檢查你是否獲得了和之前相同的 logits(但結果是兩倍)!

批次處理使得模型在接收多條句子時能夠正常工作。使用多個序列就像用單個序列構建一個批次一樣簡單。然而,還有第二個問題。當你嘗試將兩條(或更多)句子批次處理在一起時,它們的長度可能不同。如果你以前使用過張量,你會知道它們需要是矩形形狀的,因此你無法直接將輸入 ID 列表轉換為張量。為了解決這個問題,我們通常會對輸入進行填充(pad)

填充輸入

以下列表巢狀列表無法轉換為張量:

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

為了解決這個問題,我們將使用填充來使我們的張量具有矩形形狀。填充透過向值較少的句子新增一個特殊的單詞,稱為填充標記,來確保所有句子具有相同的長度。例如,如果你有 10 個句子,每個句子有 10 個單詞,以及 1 個句子有 20 個單詞,填充將確保所有句子都有 20 個單詞。在我們的例子中,生成的張量看起來像這樣:

padding_id = 100

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

填充標記 ID 可以在 `tokenizer.pad_token_id` 中找到。我們用它來分別處理我們的兩個句子,並進行批次處理。

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

我們的批處理預測中的 logits 有些問題:第二行應該與第二句的 logits 相同,但我們得到了完全不同的值!

這是因為 Transformer 模型的主要特點是注意力層會語境化每個標記。這些層會考慮填充標記,因為它們關注序列中的所有標記。為了在分別透過模型傳遞不同長度的單個句子,或透過模型傳遞包含相同句子和應用填充的批次時獲得相同的結果,我們需要告訴這些注意力層忽略填充標記。這透過使用注意力掩碼來完成。

注意力掩碼

注意力掩碼是與輸入 ID 張量形狀完全相同的張量,填充了 0 和 1:1 表示對應的標記應該被關注,而 0 表示對應的標記不應該被關注(即,它們應該被模型的注意力層忽略)。

讓我們用注意力掩碼完成上一個例子:

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

現在我們得到了批次中第二句話的相同 logits。

請注意,第二個序列的最後一個值是填充 ID,在注意力掩碼中其值為 0。

✏️ 試一試! 手動對第 2 節中使用的兩句話(“我一生都在等待 HuggingFace 課程。”和“我太討厭這個了!”)進行分詞。將它們傳入模型,檢查你是否獲得了與第 2 節中相同的 logits。現在使用填充標記將它們批次處理,然後建立正確的注意力掩碼。檢查透過模型後你是否獲得了相同的結果!

更長的序列

對於 Transformer 模型,我們可以傳遞給模型的序列長度是有限制的。大多數模型處理最長可達 512 或 1024 個標記的序列,如果要求處理更長的序列就會崩潰。這個問題有兩種解決方案:

  • 使用支援更長序列長度的模型。
  • 截斷你的序列。

模型支援的序列長度不同,有些模型專門處理非常長的序列。Longformer 是一個例子,另一個是 LED。如果你正在處理需要非常長序列的任務,我們建議你看看這些模型。

否則,我們建議你透過指定 `max_sequence_length` 引數來截斷你的序列。

sequence = sequence[:max_sequence_length]
< > 在 GitHub 上更新

© . This site is unofficial and not affiliated with Hugging Face, Inc.