並獲得增強版文件體驗
開始使用
管道背後的機制
讓我們從一個完整的示例開始,看看我們在 第 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 章 中所見,此管道將三個步驟組合在一起:預處理、將輸入傳遞到模型中以及後處理
讓我們快速回顧一下每個步驟。
使用分詞器進行預處理
與其他神經網路一樣,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_ids 和 attention_mask。input_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]),如果您確切地知道要查詢的內容在哪裡。
模型頭部:從數字中獲取意義
模型頭部將高維隱藏狀態向量作為輸入,並將它們投影到不同的維度。它們通常由一個或幾個線性層組成。
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管道執行。然後自己複製您在這裡看到的步驟,並檢查您是否獲得了相同的結果!