NLP 課程文件

處理多個序列

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

批處理是同時將多個句子透過模型傳送的操作。如果您只有一個句子,則只需使用單個序列構建一個批次即可

batched_ids = [ids, ids]

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

✏️ 試試看!將此batched_ids列表轉換為張量,並將其透過您的模型。檢查您是否獲得了與之前相同的 logits(但出現兩次)!

批處理允許模型在您向其饋送多個句子時工作。使用多個序列就像使用單個序列構建一個批次一樣簡單。不過,還有一個問題。當您嘗試將兩個(或更多)句子一起批處理時,它們可能具有不同的長度。如果您之前使用過張量,您就會知道它們需要具有矩形形狀,因此您將無法直接將輸入 ID 列表轉換為張量。為了解決此問題,我們通常會對輸入進行填充

填充輸入

以下列表列表無法轉換為張量

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 節中使用的兩個句子上手動應用分詞(“I’ve been waiting for a HuggingFace course my whole life.” 和 “I hate this so much!”)。將它們傳遞給模型,並檢查是否得到了與第 2 節中相同的 logits。現在使用填充標記將它們批處理在一起,然後建立正確的注意力掩碼。檢查在透過模型時是否獲得了相同的結果!

更長的序列

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

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

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

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

sequence = sequence[:max_sequence_length]

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