LLM 課程文件

模型

Hugging Face's logo
加入 Hugging Face 社群

並獲得增強的文件體驗

開始使用

模型

Ask a Question Open In Colab Open In Studio Lab

在本節中,我們將仔細研究模型的建立和使用。我們將使用 AutoModel 類,它在您想要從檢查點例項化任何模型時非常方便。

建立 Transformer

我們首先檢查例項化 AutoModel 時會發生什麼

from transformers import AutoModel

model = AutoModel.from_pretrained("bert-base-cased")

與分詞器類似,from_pretrained() 方法將從 Hugging Face Hub 下載並快取模型資料。如前所述,檢查點名稱對應於特定的模型架構和權重,在本例中是一個具有基本架構(12 層,768 隱藏層大小,12 個注意力頭)和帶大小寫輸入的 BERT 模型(這意味著大寫/小寫區分很重要)。Hub 上有許多可用的檢查點 — 您可以在此處探索它們。

AutoModel 類及其關聯類實際上是簡單的包裝器,旨在為給定的檢查點獲取適當的模型架構。它是一個“自動”類,意味著它將為您猜測適當的模型架構並例項化正確的模型類。但是,如果您知道要使用的模型型別,可以直接使用定義其架構的類

from transformers import BertModel

model = BertModel.from_pretrained("bert-base-cased")

載入和儲存

儲存模型就像儲存分詞器一樣簡單。實際上,模型也具有相同的 save_pretrained() 方法,該方法用於儲存模型的權重和架構配置

model.save_pretrained("directory_on_my_computer")

這會將兩個檔案儲存到您的磁碟

ls directory_on_my_computer

config.json pytorch_model.bin

如果您檢視 *config.json* 檔案,您將看到構建模型架構所需的所有必要屬性。此檔案還包含一些元資料,例如檢查點的來源以及您上次儲存檢查點時使用的 🤗 Transformers 版本。

*pytorch_model.bin* 檔案稱為狀態字典;它包含您模型的所有權重。這兩個檔案協同工作:需要配置檔案來了解模型架構,而模型權重是模型的引數。

要重用儲存的模型,請再次使用 from_pretrained() 方法

from transformers import AutoModel

model = AutoModel.from_pretrained("directory_on_my_computer")

🤗 Transformers 庫的一個很棒的功能是能夠輕鬆地與社群共享模型和分詞器。為此,請確保您在 Hugging Face 上擁有一個帳戶。如果您使用筆記本,可以輕鬆地登入

from huggingface_hub import notebook_login

notebook_login()

否則,在您的終端中執行

huggingface-cli login

然後您可以使用 push_to_hub() 方法將模型推送到 Hub

model.push_to_hub("my-awesome-model")

這將把模型檔案上傳到 Hub,位於您名稱空間下名為 *my-awesome-model* 的倉庫中。然後,任何人都可以使用 from_pretrained() 方法載入您的模型!

from transformers import AutoModel

model = AutoModel.from_pretrained("your-username/my-awesome-model")

您可以使用 Hub API 完成更多操作

  • 從本地倉庫推送模型
  • 更新特定檔案而無需重新上傳所有內容
  • 新增模型卡以記錄模型的能力、侷限性、已知偏差等。

有關此內容的完整教程,請參閱文件,或檢視高階第 4 章

編碼文字

Transformer 模型透過將輸入轉換為數字來處理文字。在這裡,我們將準確地瞭解您的文字被分詞器處理時會發生什麼。我們已經在第 1 章中看到,分詞器將文字分割成標記,然後將這些標記轉換為數字。我們可以透過一個簡單的分詞器來檢視這種轉換

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")

encoded_input = tokenizer("Hello, I'm a single sentence!")
print(encoded_input)
{'input_ids': [101, 8667, 117, 1000, 1045, 1005, 1049, 2235, 17662, 12172, 1012, 102], 
 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}

我們得到一個包含以下欄位的字典

  • input_ids:您的 token 的數字表示
  • token_type_ids:這些告訴模型輸入中的哪一部分是句子 A,哪一部分是句子 B(在下一節中會詳細討論)
  • attention_mask:這表示哪些 token 應該被關注,哪些不應該(稍後會詳細討論)

我們可以解碼 input ID 以取回原始文字

tokenizer.decode(encoded_input["input_ids"])
"[CLS] Hello, I'm a single sentence! [SEP]"

您會注意到分詞器添加了模型所需的特殊標記——[CLS][SEP]。並非所有模型都需要特殊標記;當模型使用它們進行預訓練時,它們才會被使用,在這種情況下,分詞器需要新增它們,因為該模型期望這些標記。

您可以一次編碼多個句子,可以透過批次處理它們(我們稍後會討論)或透過傳遞列表

encoded_input = tokenizer("How are you?", "I'm fine, thank you!")
print(encoded_input)
{'input_ids': [[101, 1731, 1132, 1128, 136, 102], [101, 1045, 1005, 1049, 2503, 117, 5763, 1128, 136, 102]], 
 'token_type_ids': [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], 
 'attention_mask': [[1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]}

請注意,當傳遞多個句子時,分詞器會為每個字典值返回每個句子的列表。我們還可以要求分詞器直接從 PyTorch 返回張量

encoded_input = tokenizer("How are you?", "I'm fine, thank you!", return_tensors="pt")
print(encoded_input)
{'input_ids': tensor([[  101,  1731,  1132,  1128,   136,   102],
         [  101,  1045,  1005,  1049,  2503,   117,  5763,  1128,   136,   102]]), 
 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0],
         [0, 0, 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]])}

但有一個問題:這兩個列表長度不同!陣列和張量需要是矩形,因此我們不能簡單地將這些列表轉換為 PyTorch 張量(或 NumPy 陣列)。分詞器為此提供了一個選項:填充。

填充輸入

如果我們要求分詞器填充輸入,它會透過向比最長句子短的句子新增特殊的填充標記,使所有句子的長度相同

encoded_input = tokenizer(
    ["How are you?", "I'm fine, thank you!"], padding=True, return_tensors="pt"
)
print(encoded_input)
{'input_ids': tensor([[  101,  1731,  1132,  1128,   136,   102,     0,     0,     0,     0],
         [  101,  1045,  1005,  1049,  2503,   117,  5763,  1128,   136,   102]]), 
 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
         [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), 
 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
         [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])}

現在我們有了矩形張量!請注意,填充標記已編碼為 ID 為 0 的輸入 ID,它們的注意力掩碼值也為 0。這是因為這些填充標記不應由模型分析:它們不屬於實際句子。

截斷輸入

張量可能變得太大而無法由模型處理。例如,BERT 僅使用最多 512 個標記的序列進行預訓練,因此它無法處理更長的序列。如果您有比模型能處理的更長的序列,您需要使用 truncation 引數截斷它們

encoded_input = tokenizer(
    "This is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long sentence.",
    truncation=True,
)
print(encoded_input["input_ids"])
[101, 1188, 1110, 170, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1179, 5650, 119, 102]

透過結合填充和截斷引數,您可以確保您的張量具有所需的精確大小

encoded_input = tokenizer(
    ["How are you?", "I'm fine, thank you!"],
    padding=True,
    truncation=True,
    max_length=5,
    return_tensors="pt",
)
print(encoded_input)
{'input_ids': tensor([[  101,  1731,  1132,  1128,   102],
         [  101,  1045,  1005,  1049,   102]]), 
 'token_type_ids': tensor([[0, 0, 0, 0, 0],
         [0, 0, 0, 0, 0]]), 
 'attention_mask': tensor([[1, 1, 1, 1, 1],
         [1, 1, 1, 1, 1]])}

新增特殊標記

特殊標記(或至少是它們的概念)對 BERT 和派生模型尤為重要。這些標記被新增以更好地表示句子邊界,例如句子的開頭 ([CLS]) 或句子之間的分隔符 ([SEP])。讓我們看一個簡單的例子

encoded_input = tokenizer("How are you?")
print(encoded_input["input_ids"])
tokenizer.decode(encoded_input["input_ids"])
[101, 1731, 1132, 1128, 136, 102]
'[CLS] How are you? [SEP]'

這些特殊標記由分詞器自動新增。並非所有模型都需要特殊標記;它們主要在模型預訓練時使用,在這種情況下,分詞器會新增它們,因為模型期望它們。

為什麼這一切都是必要的?

這是一個具體的例子。考慮這些編碼序列

sequences = [
    "I've been waiting for a HuggingFace course my whole life.",
    "I hate this so much!",
]

分詞後,我們得到

encoded_sequences = [
    [
        101,
        1045,
        1005,
        2310,
        2042,
        3403,
        2005,
        1037,
        17662,
        12172,
        2607,
        2026,
        2878,
        2166,
        1012,
        102,
    ],
    [101, 1045, 5223, 2023, 2061, 2172, 999, 102],
]

這是一個編碼序列的列表:一個列表的列表。張量只接受矩形形狀(想想矩陣)。這個“陣列”已經是矩形形狀,所以將其轉換為張量很容易

import torch

model_inputs = torch.tensor(encoded_sequences)

使用張量作為模型輸入

將張量與模型一起使用非常簡單——我們只需使用輸入呼叫模型即可

output = model(model_inputs)

儘管模型接受許多不同的引數,但只有輸入 ID 是必需的。我們稍後會解釋其他引數的作用以及何時需要它們,但首先我們需要仔細研究構建 Transformer 模型可以理解的輸入的分詞器。

< > 在 GitHub 上更新

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