NLP 課程文件

管道背後的機制

Hugging Face's logo
加入 Hugging Face 社群

並獲得增強版文件體驗

開始使用

管道背後的機制

Ask a Question Open In Colab Open In Studio Lab
這是第一個部分,內容在使用 PyTorch 和 TensorFlow 時略有不同。切換標題頂部的開關以選擇您喜歡的平臺!

讓我們從一個完整的示例開始,看看我們在 第 1 章 中執行以下程式碼時,幕後發生了什麼

from transformers import pipeline

classifier = pipeline("sentiment-analysis")
classifier(
    [
        "I've been waiting for a HuggingFace course my whole life.",
        "I hate this so much!",
    ]
)

並獲得

[{'label': 'POSITIVE', 'score': 0.9598047137260437},
 {'label': 'NEGATIVE', 'score': 0.9994558095932007}]

正如我們在 第 1 章 中所見,此管道將三個步驟組合在一起:預處理、將輸入傳遞到模型中以及後處理

The full NLP pipeline: tokenization of text, conversion to IDs, and inference through the Transformer model and the model head.

讓我們快速回顧一下每個步驟。

使用分詞器進行預處理

與其他神經網路一樣,Transformer 模型無法直接處理原始文字,因此我們管道的第一步是將文字輸入轉換為模型可以理解的數字。為此,我們使用一個分詞器,它負責

  • 將輸入拆分為稱為token 的單詞、子詞或符號(如標點符號)
  • 將每個 token 對映到一個整數
  • 新增可能對模型有用的其他輸入

所有這些預處理都需要以與模型預訓練時完全相同的方式進行,因此我們首先需要從 模型中心 下載這些資訊。為此,我們使用 AutoTokenizer 類及其 from_pretrained() 方法。使用我們模型的檢查點名稱,它將自動獲取與模型分詞器相關聯的資料並將其快取(因此它只在您第一次執行以下程式碼時下載)。

由於 sentiment-analysis 管道的預設檢查點是 distilbert-base-uncased-finetuned-sst-2-english(您可以在此處檢視其模型卡 此處),我們執行以下程式碼

from transformers import AutoTokenizer

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

一旦我們有了分詞器,我們就可以直接將我們的句子傳遞給它,並且我們將獲得一個可以直接饋送到模型的字典!剩下的唯一工作是將輸入 ID 列表轉換為張量。

您可以使用 🤗 Transformers,而無需擔心哪種 ML 框架用作後端;它可能是 PyTorch 或 TensorFlow,或者對於某些模型而言是 Flax。但是,Transformer 模型只接受張量作為輸入。如果這是您第一次聽說張量,您可以將其視為 NumPy 陣列。NumPy 陣列可以是標量 (0D)、向量 (1D)、矩陣 (2D) 或具有更多維度。它實際上是一個張量;其他 ML 框架的張量行為類似,並且通常與例項化 NumPy 陣列一樣簡單。

為了指定我們希望獲取的張量型別(PyTorch、TensorFlow 或純 NumPy),我們使用 return_tensors 引數

raw_inputs = [
    "I've been waiting for a HuggingFace course my whole life.",
    "I hate this so much!",
]
inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")
print(inputs)

暫時不要擔心填充和截斷;我們將在後面解釋這些。這裡要記住的主要事項是,您可以傳遞一個句子或一個句子列表,以及指定您希望獲取的張量型別(如果未傳遞型別,您將得到一個列表列表作為結果)。

以下是結果在 PyTorch 張量中的樣子

{
    'input_ids': tensor([
        [  101,  1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172, 2607,  2026,  2878,  2166,  1012,   102],
        [  101,  1045,  5223,  2023,  2061,  2172,   999,   102,     0,     0,     0,     0,     0,     0,     0,     0]
    ]), 
    'attention_mask': tensor([
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
    ])
}

輸出本身是一個字典,包含兩個鍵,input_idsattention_maskinput_ids 包含兩行整數(每個句子一行),它們是每個句子中 token 的唯一識別符號。我們將在本章後面解釋 attention_mask 是什麼。

透過模型

我們可以用與分詞器相同的方式下載預訓練模型。🤗 Transformers 提供了一個 AutoModel 類,它也具有 from_pretrained() 方法

from transformers import AutoModel

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModel.from_pretrained(checkpoint)

在此程式碼片段中,我們下載了之前在管道中使用的相同檢查點(它實際上應該已經被快取了),並使用它例項化了一個模型。

此架構只包含基本的 Transformer 模組:給定一些輸入,它輸出我們稱之為隱藏狀態的東西,也稱為特徵。對於每個模型輸入,我們將檢索一個高維向量,表示Transformer 模型對該輸入的語境理解

如果這聽起來沒有意義,不要擔心。我們將在後面解釋所有內容。

雖然這些隱藏狀態本身可能很有用,但它們通常是模型另一個部分的輸入,稱為頭部。在 第 1 章 中,不同的任務可以用相同的架構執行,但每個任務都將與一個不同的頭部相關聯。

高維向量?

Transformer 模組輸出的向量通常很大。它通常具有三個維度

  • 批次大小:一次處理的序列數量(在我們示例中為 2)。
  • 序列長度:序列的數字表示的長度(在我們示例中為 16)。
  • 隱藏大小:每個模型輸入的向量維度。

由於最後一個值,它被稱為“高維”。隱藏大小可以非常大(較小的模型通常為 768,而較大的模型可以達到 3072 或更大)。

如果我們將預處理後的輸入饋送到我們的模型,我們可以看到這一點。

outputs = model(**inputs)
print(outputs.last_hidden_state.shape)
torch.Size([2, 16, 768])

請注意,🤗 Transformers 模型的輸出類似於namedtuple或字典。您可以透過屬性(如我們所做的那樣)或透過鍵(outputs["last_hidden_state"])訪問元素,甚至可以透過索引訪問(outputs[0]),如果您確切地知道要查詢的內容在哪裡。

模型頭部:從數字中獲取意義

模型頭部將高維隱藏狀態向量作為輸入,並將它們投影到不同的維度。它們通常由一個或幾個線性層組成。

A Transformer network alongside its head.

Transformer 模型的輸出直接傳送到模型頭部進行處理。

在此圖中,模型由其嵌入層和後續層表示。嵌入層將標記化輸入中的每個輸入 ID 轉換為代表關聯標記的向量。後續層使用注意力機制操作這些向量,以產生句子的最終表示。

🤗 Transformers 中提供了許多不同的架構,每種架構都是圍繞解決特定任務而設計的。以下是非詳盡的列表。

  • *模型(檢索隱藏狀態)
  • *ForCausalLM
  • *ForMaskedLM
  • *ForMultipleChoice
  • *ForQuestionAnswering
  • *ForSequenceClassification
  • *ForTokenClassification
  • 以及其他 🤗

對於我們的示例,我們將需要一個帶有序列分類頭的模型(能夠將句子分類為正面或負面)。因此,我們實際上不會使用AutoModel類,而是使用AutoModelForSequenceClassification

from transformers import AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
outputs = model(**inputs)

現在,如果我們檢視輸出的形狀,維度將低得多:模型頭部將我們之前看到的那些高維向量作為輸入,並輸出包含兩個值的向量(每個標籤一個)。

print(outputs.logits.shape)
torch.Size([2, 2])

由於我們只有兩個句子和兩個標籤,因此從模型中獲得的結果的形狀為 2 x 2。

後處理輸出

從模型中獲得的輸出值本身不一定有意義。讓我們看看。

print(outputs.logits)
tensor([[-1.5607,  1.6123],
        [ 4.1692, -3.3464]], grad_fn=<AddmmBackward>)

我們的模型預測第一個句子為[-1.5607, 1.6123],第二個句子為[ 4.1692, -3.3464]。這些不是機率,而是logits,即模型最後一層輸出的原始非標準化分數。要轉換為機率,它們需要透過SoftMax層(所有 🤗 Transformers 模型都會輸出 logits,因為訓練的損失函式通常會將最後一層啟用函式(例如 SoftMax)與實際損失函式(例如交叉熵)融合)。

import torch

predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
print(predictions)
tensor([[4.0195e-02, 9.5980e-01],
        [9.9946e-01, 5.4418e-04]], grad_fn=<SoftmaxBackward>)

現在我們可以看到,模型預測第一個句子的機率為[0.0402, 0.9598],第二個句子的機率為[0.9995, 0.0005]。這些是可以識別的機率分數。

要獲得與每個位置對應的標籤,我們可以檢查模型配置的id2label屬性(下一節將詳細介紹)。

model.config.id2label
{0: 'NEGATIVE', 1: 'POSITIVE'}

現在我們可以得出結論,模型預測如下:

  • 第一個句子:負面:0.0402,正面:0.9598
  • 第二個句子:負面:0.9995,正面:0.0005

我們已經成功地重現了管道的三個步驟:使用標記器進行預處理、將輸入傳遞到模型以及後處理!現在讓我們花一些時間深入研究這些步驟中的每一個。

✏️ 試試看! 選擇您自己的兩個(或更多)文字,並將其透過sentiment-analysis管道執行。然後自己複製您在這裡看到的步驟,並檢查您是否獲得了相同的結果!

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