LLM 課程文件
管道背後
並獲得增強的文件體驗
開始使用
管道背後
讓我們從一個完整的示例開始,看看在第 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 模型無法直接處理原始文字,所以我們管道的第一步是將文字輸入轉換為模型能夠理解的數字。為此,我們使用分詞器,它負責:
- 將輸入分割成單詞、子詞或符號(如標點符號),這些都被稱為標記。
- 將每個標記對映到一個整數。
- 新增可能對模型有用的額外輸入。
所有這些預處理都必須與模型預訓練時的方式完全相同,所以我們首先需要從模型中心下載這些資訊。為此,我們使用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,而無需擔心後端使用的是哪種機器學習框架;對於某些模型,它可能是 PyTorch 或 Flax。然而,Transformer 模型只接受張量作為輸入。如果這是您第一次聽說張量,您可以將它們視為 NumPy 陣列。NumPy 陣列可以是標量(0D)、向量(1D)、矩陣(2D),或具有更多維度。它實際上是一個張量;其他機器學習框架的張量行為類似,並且通常像 NumPy 陣列一樣容易例項化。
為了指定我們希望返回的張量型別(PyTorch 或普通 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
包含兩行整數(每句話一行),它們是每句話中標記的唯一識別符號。我們將在本章後面解釋 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 中有許多不同的架構,每種架構都旨在解決特定的任務。以下是一個不完全列表:
*Model
(檢索隱藏狀態)*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]
。這些不是機率,而是對數,是模型最後一層輸出的原始、未歸一化的分數。要將其轉換為機率,它們需要經過一個SoftMax層(所有🤗 Transformers 模型都輸出對數,因為訓練的損失函式通常會將最後一個啟用函式(如 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
管道執行它們。然後自己重複您在這裡看到的步驟,並檢查您是否獲得了相同的結果!