Buckets:

rtrm's picture
|
download
raw
11.8 kB
# 管道的內部
{#if fw === 'pt'}
{:else}
{/if}
> [!TIP]
> 這是第一部分,根據您使用 PyTorch 或者 TensorFlow,內容略有不同。點擊標題上方的平臺,選一個您喜歡的吧!
{#if fw === 'pt'}
{:else}
{/if}
讓我們從一個完整的示例開始,看看在[Chapter 1](/course/chapter1)中執行以下代碼時在幕後發生了什麼
```python
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!",
]
)
```
獲得:
```python out
[{'label': 'POSITIVE', 'score': 0.9598047137260437},
{'label': 'NEGATIVE', 'score': 0.9994558095932007}]
```
正如我們在[Chapter 1](/course/chapter1)中看到的,此管道將三個步驟組合在一起:預處理、通過模型傳遞輸入和後處理:
讓我們快速瀏覽一下這些內容。
## 使用分詞器進行預處理
與其他神經網絡一樣,Transformer 模型無法直接處理原始文本, 因此我們管道的第一步是將文本輸入轉換為模型能夠理解的數字。 為此,我們使用*tokenizer*(標記器),負責:
- 將輸入拆分為單詞、子單詞或符號(如標點符號),稱為標記(*token*)
- 將每個標記(token)映射到一個整數
- 添加可能對模型有用的其他輸入
所有這些預處理都需要以與模型預訓練時完全相同的方式完成,因此我們首先需要從[Model Hub](https://huggingface.co/models)中下載這些信息。為此,我們使用`AutoTokenizer`類及其`from_pretrained()`方法。使用我們模型的檢查點名稱,它將自動獲取與模型的標記器相關聯的數據,並對其進行緩存(因此只有在您第一次運行下面的代碼時才會下載)。
因為`sentiment-analysis`(情緒分析)管道的默認檢查點是`distilbert-base-uncased-finetuned-sst-2-english`(你可以看到它的模型卡[here](https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english)),我們運行以下程序:
```python
from transformers import AutoTokenizer
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
```
一旦我們有了標記器,我們就可以直接將我們的句子傳遞給它,然後我們就會得到一本字典,它可以提供給我們的模型!剩下要做的唯一一件事就是將輸入ID列表轉換為張量。
您可以使用🤗 Transformers,而不必擔心哪個 ML 框架被用作後端;它可能是 PyTorch 或 TensorFlow,或 Flax。但是,Transformers型號只接受*張量*作為輸入。如果這是你第一次聽說張量,你可以把它們想象成NumPy數組。NumPy數組可以是標量(0D)、向量(1D)、矩陣(2D)或具有更多維度。它實際上是張量;其他 ML 框架的張量行為類似,通常與 NumPy 數組一樣易於實例化。
要指定要返回的張量類型(PyTorch、TensorFlow 或 plain NumPy),我們使用`return_tensors`參數:
{#if fw === 'pt'}
```python
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)
```
{:else}
```python
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="tf")
print(inputs)
```
{/if}
現在不要擔心填充和截斷;我們稍後會解釋這些。這裡要記住的主要事情是,您可以傳遞一個句子或一組句子,還可以指定要返回的張量類型(如果沒有傳遞類型,您將得到一組列表)。
{#if fw === 'pt'}
以下是PyTorch張量的結果:
```python out
{
'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, 0, 0, 0, 0, 0, 0, 0, 0, 0]
])
}
```
{:else}
以下是 TensorFlow 張量的結果:
```python out
{
'input_ids': ,
'attention_mask':
}
```
{/if}
輸出本身是一個包含兩個鍵的字典,`input_ids``attention_mask``input_ids`包含兩行整數(每個句子一行),它們是每個句子中標記的唯一標記(token)。我們將在本章後面解釋什麼是`attention_mask`
## 瀏覽模型
{#if fw === 'pt'}
我們可以像使用標記器一樣下載預訓練模型。🤗 Transformers提供了一個`AutoModel`類,該類還具有`from_pretrained()`方法:
```python
from transformers import AutoModel
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModel.from_pretrained(checkpoint)
```
{:else}
我們可以像使用標記器一樣下載預訓練模型。🤗 Transformers提供了一個`TFAutoModel`類,該類還具有`from_pretrained()`方法:
```python
from transformers import TFAutoModel
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = TFAutoModel.from_pretrained(checkpoint)
```
{/if}
在這個代碼片段中,我們下載了之前在管道中使用的相同檢查點(它實際上應該已經被緩存),並用它實例化了一個模型。
這個架構只包含基本轉換器模塊:給定一些輸入,它輸出我們將調用的內容*隱藏狀態(hidden states)*,亦稱*特徵(features)*。對於每個模型輸入,我們將檢索一個高維向量,表示**Transformer模型對該輸入的上下文理解**
如果這不合理,不要擔心。我們以後再解釋。
雖然這些隱藏狀態本身可能很有用,但它們通常是模型另一部分(稱為*頭部(head)*)的輸入。 在[Chapter 1](/course/chapter1)中,可以使用相同的體系結構執行不同的任務,但這些任務中的每個任務都有一個與之關聯的不同頭。
### 高維向量?
Transformers 模塊的向量輸出通常較大。它通常有三個維度:
- **Batch size**: 一次處理的序列數(在我們的示例中為2)。
- **Sequence length**: 序列的數值表示的長度(在我們的示例中為16)。
- **Hidden size**: 每個模型輸入的向量維度。
由於最後一個值,它被稱為「高維」。隱藏的大小可能非常大(768通常用於較小的型號,而在較大的型號中,這可能達到3072或更大)。
如果我們將預處理的輸入輸入到模型中,我們可以看到這一點:
{#if fw === 'pt'}
```python
outputs = model(**inputs)
print(outputs.last_hidden_state.shape)
```
```python out
torch.Size([2, 16, 768])
```
{:else}
```py
outputs = model(inputs)
print(outputs.last_hidden_state.shape)
```
```python out
(2, 16, 768)
```
{/if}
注意🤗 Transformers 模型的輸出與`namedtuple`或詞典相似。您可以通過屬性(就像我們所做的那樣)或鍵(`輸出["last_hidden_state"]`)訪問元素,甚至可以通過索引訪問元素,前提是您確切知道要查找的內容在哪裡(`outputs[0]`)。
### 模型頭:數字的意義
模型頭將隱藏狀態的高維向量作為輸入,並將其投影到不同的維度。它們通常由一個或幾個線性層組成:
Transformers 模型的輸出直接發送到模型頭進行處理。
在此圖中,模型由其嵌入層和後續層表示。嵌入層將標記化輸入中的每個輸入ID轉換為表示關聯標記(token)的向量。後續層使用注意機制操縱這些向量,以生成句子的最終表示。
🤗 Transformers中有許多不同的體系結構,每種體系結構都是圍繞處理特定任務而設計的。以下是一個非詳盡的列表:
- `*Model` (retrieve the hidden states)
- `*ForCausalLM`
- `*ForMaskedLM`
- `*ForMultipleChoice`
- `*ForQuestionAnswering`
- `*ForSequenceClassification`
- `*ForTokenClassification`
- 以及其他 🤗
{#if fw === 'pt'}
對於我們的示例,我們需要一個帶有序列分類頭的模型(能夠將句子分類為肯定或否定)。因此,我們實際上不會使用`AutoModel`類,而是使用`AutoModelForSequenceClassification`
```python
from transformers import AutoModelForSequenceClassification
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
outputs = model(**inputs)
```
{:else}
For our example, we will need a model with a sequence classification head (to be able to classify the sentences as positive or negative). So, we won't actually use the `TFAutoModel` class, but `TFAutoModelForSequenceClassification`:
```python
from transformers import TFAutoModelForSequenceClassification
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
outputs = model(inputs)
```
{/if}
現在,如果我們觀察輸入的形狀,維度將低得多:模型頭將我們之前看到的高維向量作為輸入,並輸出包含兩個值的向量(每個標籤一個):
```python
print(outputs.logits.shape)
```
{#if fw === 'pt'}
```python out
torch.Size([2, 2])
```
{:else}
```python out
(2, 2)
```
{/if}
因為我們只有兩個句子和兩個標籤,所以我們從模型中得到的結果是2 x 2的形狀。
## 對輸出進行後處理
我們從模型中得到的輸出值本身並不一定有意義。我們來看看,
```python
print(outputs.logits)
```
{#if fw === 'pt'}
```python out
tensor([[-1.5607, 1.6123],
[ 4.1692, -3.3464]], grad_fn=)
```
{:else}
```python out
```
{/if}
我們的模型預測第一句為`[-1.5607, 1.6123]`,第二句為`[ 4.1692, -3.3464]`。這些不是概率,而是*logits*,即模型最後一層輸出的原始非標準化分數。要轉換為概率,它們需要經過[SoftMax](https://en.wikipedia.org/wiki/Softmax_function)層(所有🤗Transformers模型輸出logits,因為用於訓練的損耗函數通常會將最後的激活函數(如SoftMax)與實際損耗函數(如交叉熵)融合):
{#if fw === 'pt'}
```py
import torch
predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
print(predictions)
```
{:else}
```py
import tensorflow as tf
predictions = tf.math.softmax(outputs.logits, axis=-1)
print(predictions)
```
{/if}
{#if fw === 'pt'}
```python out
tensor([[4.0195e-02, 9.5980e-01],
[9.9946e-01, 5.4418e-04]], grad_fn=)
```
{:else}
```python out
tf.Tensor(
[[4.01951671e-02 9.59804833e-01]
[9.9945587e-01 5.4418424e-04]], shape=(2, 2), dtype=float32)
```
{/if}
現在我們可以看到,模型預測第一句為`[0.0402, 0.9598]`,第二句為`[0.9995, 0.0005]`。這些是可識別的概率分數。
為了獲得每個位置對應的標籤,我們可以檢查模型配置的`id2label`屬性(下一節將對此進行詳細介紹):
```python
model.config.id2label
```
```python out
{0: 'NEGATIVE', 1: 'POSITIVE'}
```
現在我們可以得出結論,該模型預測了以下幾點:
- 第一句:否定:0.0402,肯定:0.9598
- 第二句:否定:0.9995,肯定:0.0005
我們已經成功地複製了管道的三個步驟:使用標記化器進行預處理、通過模型傳遞輸入以及後處理!現在,讓我們花一些時間深入瞭解這些步驟中的每一步。
> [!TIP]
> ✏️ **試試看!** 選擇兩個(或更多)你自己的文本並在管道中運行它們。然後自己複製在這裡看到的步驟,並檢查是否獲得相同的結果!

Xet Storage Details

Size:
11.8 kB
·
Xet hash:
bc293e5d0e1932a70ba7067ee6268450785c3d28c0d6fa4ccdb5e2bba22fc616

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.