Transformers 入門

理念

Transformers是一個為NLP的研究人員尋求使用/研究/擴展大型Transformers模型的庫。

該庫的設計有兩個強烈的目標:

  • 儘可能簡單和快速使用: 我們儘可能限制了要學習的面向對象抽象的類的數量,實際上幾乎沒有抽象,每個模型只需要使用三個標準類:配置、模型和tokenizer, 所有這些類都可以通過使用公共的from_pretrained()實例化方法從預訓練實例以簡單統一的方式初始化,該方法將負責從庫中下載,緩存和加載相關類提供的預訓練模型或你自己保存的模型。 因此,這個庫不是構建神經網絡模塊的工具箱。如果您想擴展/構建這個庫,只需使用常規的Python/PyTorch模塊,並從這個庫的基類繼承,以重用諸如模型加載/保存等功能。
  • 提供最先進的模型與性能儘可能接近的原始模型: 我們為每個架構提供了至少一個例子,該例子再現了上述架構的官方作者提供的結果 代碼通常儘可能地接近原始代碼,這意味著一些PyTorch代碼可能不那麼pytorch化,因為這是轉換TensorFlow代碼後的結果。

其他幾個目標:

  • 儘可能一致地暴露模型的內部: 我們使用一個API來訪問所有的隱藏狀態和注意力權重, 對tokenizer和基本模型的API進行了標準化,以方便在模型之間進行切換。
  • 結合一個主觀選擇的有前途的工具微調/調查這些模型: 向詞彙表和嵌入項添加新標記以進行微調的簡單/一致的方法, 簡單的方法面具和修剪變壓器頭。

主要概念

該庫是建立在三個類型的類為每個模型:

  • model類是目前在庫中提供的8個模型架構的PyTorch模型(torch.nn.Modules),例如BertModel
  • configuration類,它存儲構建模型所需的所有參數,例如BertConfig。您不必總是自己實例化這些配置,特別是如果您使用的是未經任何修改的預訓練的模型,創建模型將自動負責實例化配置(它是模型的一部分)
  • tokenizer類,它存儲每個模型的詞彙表,並在要輸送到模型的詞彙嵌入索引列表中提供用於編碼/解碼字符串的方法,例如BertTokenizer

所有這些類都可以從預訓練模型來實例化,並使用兩種方法在本地保存:

  • from_pretraining()允許您從一個預訓練版本實例化一個模型/配置/tokenizer,這個預訓練版本可以由庫本身提供(目前這裡列出了27個模型),也可以由用戶在本地(或服務器上)存儲,
  • save_pretraining()允許您在本地保存模型/配置/tokenizer,以便可以使用from_pretraining()重新加載它。

我們將通過一些簡單的快速啟動示例來完成這個快速啟動之旅,看看如何實例化和使用這些類。其餘的文件分為兩部分:

  • 主要的類詳細介紹了三種主要類(配置、模型、tokenizer)的公共功能/方法/屬性,以及一些作為訓練工具提供的優化類,
  • 包引用部分詳細描述了每個模型體系結構的每個類的所有變體,特別是調用它們時它們期望的輸入和輸出。

快速入門:使用

這裡有兩個例子展示了一些Bert和GPT2類以及預訓練模型。

有關每個模型類的示例,請參閱完整的API參考。

BERT示例

讓我們首先使用BertTokenizer從文本字符串準備一個標記化的輸入(要輸入給BERT的標記嵌入索引列表)

<code>import torch
from transformers import BertTokenizer, BertModel, BertForMaskedLM

# 可選:如果您想了解發生的信息,請按以下步驟logger
import logging
logging.basicConfig(level=logging.INFO)

# 加載預訓練的模型標記器(詞彙表)
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')

# 標記輸入
text = "[CLS] Who was Jim Henson ? [SEP] Jim Henson was a puppeteer [SEP]"
tokenized_text = tokenizer.tokenize(text)

# 用“BertForMaskedLM”掩蓋我們試圖預測的標記`
masked_index = 8
tokenized_text[masked_index] = '[MASK]'
assert tokenized_text == ['[CLS]', 'who', 'was', 'jim', 'henson', '?', '[SEP]', 'jim', '[MASK]', 'was', 'a', 'puppet', '##eer', '[SEP]']

# 將標記轉換為詞彙索引
indexed_tokens = tokenizer.convert_tokens_to_ids(tokenized_text)
# 定義與第一句和第二句相關的句子A和B索引(見論文)
segments_ids = [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]

# 將輸入轉換為PyTorch張量
tokens_tensor = torch.tensor([indexed_tokens])
segments_tensors = torch.tensor([segments_ids])/<code>

讓我們看看如何使用BertModel在隱藏狀態下對輸入進行編碼:

<code># 加載預訓練模型(權重)
model = BertModel.from_pretrained('bert-base-uncased')

# 將模型設置為評估模式
# 在評估期間有可再現的結果這是很重要的!

model.eval()

# 如果你有GPU,把所有東西都放在cuda上
tokens_tensor = tokens_tensor.to('cuda')
segments_tensors = segments_tensors.to('cuda')
model.to('cuda')

#預測每個層的隱藏狀態特徵
with torch.no_grad():
# 有關輸入的詳細信息,請參見models文檔字符串
outputs = model(tokens_tensor, token_type_ids=segments_tensors)
# Transformer模型總是輸出元組。
# 有關所有輸出的詳細信息,請參見模型文檔字符串。在我們的例子中,第一個元素是Bert模型最後一層的隱藏狀態
encoded_layers = outputs[0]
# 我們已將輸入序列編碼為形狀(批量大小、序列長度、模型隱藏維度)的FloatTensor
assert tuple(encoded_layers.shape) == (1, len(indexed_tokens), model.config.hidden_size)/<code>

以及如何使用BertForMaskedLM預測屏蔽的標記:

<code># 加載預訓練模型(權重)
model = BertForMaskedLM.from_pretrained('bert-base-uncased')
model.eval()

# 如果你有GPU,把所有東西都放在cuda上
tokens_tensor = tokens_tensor.to('cuda')
segments_tensors = segments_tensors.to('cuda')
model.to('cuda')

# 預測所有標記
with torch.no_grad():
outputs = model(tokens_tensor, token_type_ids=segments_tensors)
predictions = outputs[0]

# 確認我們能預測“henson”
predicted_index = torch.argmax(predictions[0, masked_index]).item()
predicted_token = tokenizer.convert_ids_to_tokens([predicted_index])[0]
assert predicted_token == 'henson'/<code>

OpenAI GPT-2

下面是一個快速開始的例子,使用GPT2Tokenizer和GPT2LMHeadModel類以及OpenAI的預訓練模型來預測文本提示中的下一個標記。

首先,讓我們使用GPT2Tokenizer

<code>import torch
from transformers import GPT2Tokenizer, GPT2LMHeadModel

# 可選:如果您想了解發生的信息,請按以下步驟logger
import logging
logging.basicConfig(level=logging.INFO)

# 加載預訓練模型(權重)
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')

# 編碼輸入
text = "Who was Jim Henson ? Jim Henson was a"
indexed_tokens = tokenizer.encode(text)

# 轉換為PyTorch tensor
tokens_tensor = torch.tensor([indexed_tokens])/<code>

讓我們看看如何使用GPT2LMHeadModel生成下一個跟在我們的文本後面的token:

<code># 加載預訓練模型(權重)
model = GPT2LMHeadModel.from_pretrained('gpt2')

# 將模型設置為評估模式
# 在評估期間有可再現的結果這是很重要的!

model.eval()

# 如果你有GPU,把所有東西都放在cuda上
tokens_tensor = tokens_tensor.to('cuda')
model.to('cuda')

# 預測所有標記
with torch.no_grad():
outputs = model(tokens_tensor)
predictions = outputs[0]

# 得到預測的下一個子詞(在我們的例子中,是“man”這個詞)
predicted_index = torch.argmax(predictions[0, -1, :]).item()
predicted_text = tokenizer.decode(indexed_tokens + [predicted_index])
assert predicted_text == 'Who was Jim Henson? Jim Henson was a man'/<code>

每個模型架構(Bert、GPT、GPT-2、Transformer XL、XLNet和XLM)的每個模型類的示例,可以在文檔中找到。

使用過去的GPT-2

以及其他一些模型(GPT、XLNet、Transfo XL、CTRL),使用past或mems屬性,這些屬性可用於防止在使用順序解碼時重新計算鍵/值對。它在生成序列時很有用,因為注意力機制的很大一部分得益於以前的計算。

下面是一個使用帶past的GPT2LMHeadModel和argmax解碼的完整工作示例(只能作為示例,因為argmax decoding引入了大量重複):

<code>from transformers import GPT2LMHeadModel, GPT2Tokenizer
import torch

tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
model = GPT2LMHeadModel.from_pretrained('gpt2')

generated = tokenizer.encode("The Manhattan bridge")

context = torch.tensor([generated])
past = None

for i in range(100):
print(i)
output, past = model(context, past=past)
token = torch.argmax(output[..., -1, :])

generated += [token.tolist()]
context = token.unsqueeze(0)

sequence = tokenizer.decode(generated)

print(sequence)/<code>

由於以前所有標記的鍵/值對都包含在past,因此模型只需要一個標記作為輸入。

Model2Model示例

編碼器-解碼器架構需要兩個標記化輸入:一個用於編碼器,另一個用於解碼器。假設我們想使用Model2Model進行生成性問答,從標記將輸入模型的問答開始。

<code>import torch
from transformers import BertTokenizer, Model2Model

# 可選:如果您想了解發生的信息,請按以下步驟logger
import logging
logging.basicConfig(level=logging.INFO)

# 加載預訓練模型(權重)
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')

# 編碼輸入(問題)
question = "Who was Jim Henson?"
encoded_question = tokenizer.encode(question)

# 編碼輸入(答案)

answer = "Jim Henson was a puppeteer"
encoded_answer = tokenizer.encode(answer)

# 將輸入轉換為PyTorch張量
question_tensor = torch.tensor([encoded_question])
answer_tensor = torch.tensor([encoded_answer])/<code>

讓我們看看如何使用Model2Model獲取與此(問題,答案)對相關聯的loss值:

<code>#為了計算損失,我們需要向解碼器提供語言模型標籤(模型生成的標記id)。
lm_labels = encoded_answer
labels_tensor = torch.tensor([lm_labels])

# 加載預訓練模型(權重)
model = Model2Model.from_pretrained('bert-base-uncased')

# 將模型設置為評估模式
# 在評估期間有可再現的結果這是很重要的!
model.eval()

# 如果你有GPU,把所有東西都放在cuda上
question_tensor = question_tensor.to('cuda')
answer_tensor = answer_tensor.to('cuda')
labels_tensor = labels_tensor.to('cuda')
model.to('cuda')

# 預測每個層的隱藏狀態特徵
with torch.no_grad():
# 有關輸入的詳細信息,請參見models文檔字符串
outputs = model(question_tensor, answer_tensor, decoder_lm_labels=labels_tensor)
# Transformers模型總是輸出元組。
# 有關所有輸出的詳細信息,請參見models文檔字符串

# 在我們的例子中,第一個元素是LM損失的值
lm_loss = outputs[0]/<code>

此損失可用於對Model2Model的問答任務進行微調。假設我們對模型進行了微調,現在讓我們看看如何生成答案:

<code># 讓我們重複前面的問題
question = "Who was Jim Henson?"
encoded_question = tokenizer.encode(question)
question_tensor = torch.tensor([encoded_question])

# 這次我們試圖生成答案,所以我們從一個空序列開始
answer = "[CLS]"
encoded_answer = tokenizer.encode(answer, add_special_tokens=False)
answer_tensor = torch.tensor([encoded_answer])

# 加載預訓練模型(權重)
model = Model2Model.from_pretrained('fine-tuned-weights')
model.eval()

# 如果你有GPU,把所有東西都放在cuda上
question_tensor = question_tensor.to('cuda')
answer_tensor = answer_tensor.to('cuda')
model.to('cuda')

# 預測所有標記
with torch.no_grad():
outputs = model(question_tensor, answer_tensor)
predictions = outputs[0]

# 確認我們能預測“jim”
predicted_index = torch.argmax(predictions[0, -1]).item()
predicted_token = tokenizer.convert_ids_to_tokens([predicted_index])[0]
assert predicted_token == 'jim'/<code>


分享到:


相關文章: