前言
本篇筆記所紀錄的 Transformers 並不是 Attention 論文中所提到的 “Transformer” 模型,而是由 Higginface 團隊所開發的 Transformers
套件。
這則筆記的重點在於:
- 為什麼要使用 Transformers
- Transformers 的兩大元件:
- Tokenizer
- Model
- 使用 Transformers 實作 Feature Extraction.
近年來 NLP 在 Attention 概念提出以後,各種模型如同雨後春筍般噴發:
(圖片來源: https://medium.com/@hamdan.hussam/from-bert-to-albert-pre-trained-langaug-models-5865aa5c3762)
然而在使用不同的模型時,每個模型的架構、參數的載入方式都不相同,大大的提高了模型間的轉換成本。舉例來說: 進行假新聞分類任務時,可能會想要嘗試從「BERT」轉換到「XLNet」,在其餘架構不變的前提下,光是進行 Pre-train Language Model 的更換可能就會花費不少時間。
透過 Transformers
這個套件,能夠將轉換成本降到最低! 使用這套框架能夠直接對多種模型進行操作,各種模型的架構及參數都已經被封裝在套件中,只需要了解此框架的機制,即可快速套用到數十種不同的模型!
直白的說:學習這個套件,就能同時學會使用多種主流的語言模型。
附上 Transfomers
的官方網站:
以及 Transformers
當前所支援的模型清單:
Hugging Face - On a mission to solve NLP, one commit at a time.
如何使用Transformers
我將 Transformers
套件分成兩個部分:
- Tokenizer : 將文字斷詞後轉換為 Index.
- Pre-trained Model : 接受轉換的 Index, 輸出 Word Representation.
下面這張流程圖,是從文字轉換成 Word Representation的過程,我們藉此來了解這兩個元件的作用(反黃)到底是什麼 :
Tokenizer
1. 初始化
Tokenizer 的功用是將文字進行斷詞及轉換為 Index,不同的 Pre-trained Language Model 所使用的斷詞方式以及 Index 也不盡相同,所以在使用前需要先進行「初始化」。
from transformers import AutoTokenizer, BertTokenizer
# 常見模型具有自己的Tokenizer
tokenizer_bert = BertTokenizer.from_pretrained("bert-base-cased")
# AutoTokenizer 則是通用型
tokenizer_other = AutoTokenizer.from_pretrained("emilyalsentzer/Bio_ClinicalBERT")
針對較常見的語言模型 (BERT, GPT2, XLM, XLNet…), Transformers
有提供專屬的 Tokenizer 可以使用。
除此之外,還有額外定義一個 AutoTokenizer
,可以讓使用者互相分享、使用訓練好的模型。 透過 tokenizer.from_pretrained(<model_name>)
即可下載並且載入該模型的相關設定。
在初次使用模型時, transformers
會自動下載該模型的權重及相關設定,需要花費一點時間 (時間長度端看模型的大小),下載完成後,往後的執行將會從本地端的快取資料夾載入,就不需要再進行下載了。
2. 基本使用
tokenizer_bert = BertTokenizer.from_pretrained("bert-base-cased")
test_input = "I don't want to work."
encode_result = tokenizer.encode(test_input) # 使用 encode() 來對文字進行斷詞、編碼。
print(encode_result)
# [101, 146, 1274, 112, 189, 1328, 1106, 1250, 119, 102]
decode_result = tokenizer.decode(encode_result) # 使用 decode() 來將 id 轉換回文字。
print(decode_result)
# "[CLS] I don't want to work. [SEP]"
從中我們觀察到兩件事情:
encode_result
的長度好像跟test_input
的長度不相同!:這是因為有些模型會使用 Word Piece Tokenizer,也就是在斷詞過程中將詞彙進行拆解,所以會導致 Encode 過的結果長度與原始 Input 不同!
Decode後的結果好像跟原本的不太一樣…:
因為在
tokenizer.encode()
過程中會自動加入 [CLS]、[SEP]等特殊標記,這是由於 BERT 模型的機制而自動產生,其餘模型的 Tokenizer 不一定會有,也可以在encode過程中加入參數來移除:encode_result = tokenizer.encode(test_input, add_special_tokens=False) # 編碼過程中,移除特殊符號 print(encode_result) # [146, 1274, 112, 189, 1328, 1106, 1250, 119] decode_result = tokenizer.decode(encode_result) # 使用 decode() 來將 id 轉換回文字。 print(decode_result) # "I don't want to work."
除此之外,在進行資料前處理時,另外一個麻煩的問題是在產生 “batch” 時,必須要讓每一筆資料的長度相同(也就是要進行 “Padding” ),假設最大長度設定為 64
:
- 長度不足的句子需要補齊 “[PAD]“符號,使長度達到 64。
- 長度過長的句子需要只保留前 64 個字,超過的部分捨棄不用。
這件事情如果自己處理,會有點小麻煩,幸好 Tokenizer 都幫忙做好了.
test_input = "I don't want to work."
encode_result = tokenizer.encode(test_input, max_length=64, pad_to_max_length=True)
print(encode_result)
# [101, 146, 1274, 112, 189, 1328, 1106, 1250, 119, 102, 0, 0, 0,.... 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
decode_result = tokenizer.decode(encode_result)
print(decode_result)
# "[CLS] I don't want to work. [SEP] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD]...[PAD]"
透過 Tokenizer 進行斷詞及編碼後,我們就能將其送入 Pre-trained Model 中取得 Representation 了!
Pre-trained Language Model
1. 初始化
from transformers import AutoModel, BertModel
model_bert = BertModel.from_pretrained("bert-base-cased")
model_other = AutoModel.from_pretrained("emilyalsentzer/Bio_ClinicalBERT")
與 Tokenizer
的機制相同, Transformers
中也包含了各種預先定義好的 Model (例如 BertModel
),以及方便使用其他開源模型的泛用Model (AutoModel
)。
在使用前也需要透過 model.from_pretrained(<model_name>)
來指定要載入的模型。
與 Tokenizer
相同,初次使用模型時會需要進行下載,所以需要等待一段時間,以 xlnet-large-cased
為例,大約有 3.3G 需要進行下載。
值得注意的是:Tokenizer
及 Model
是有先後關係的:
- 文字進入
Tokenizer
轉換。 - 轉換後的Index送入
Model
中取得 Word Representation。
所以 Tokenizer
及 Model
的 <model_name>
必需要一致,以免發生錯誤。
2. 基本使用
tokenizer_bert = BertTokenizer.from_pretrained("bert-base-cased")
test_input = "I don't want to work."
encode_result = tokenizer.encode(test_input, max_length=64, pad_to_max_length=True) # 使用 encode() 來對文字進行斷詞、編碼。
print(encode_result)
# [101, 146, 1274, 112, 189, 1328, 1106, 1250, 119, 102, 0, 0, ..., 0]
model_bert = BertModel.from_pretrained("bert-base-cased")
output = model_bert(encode_result) # 等同於將 encode_result 送入 model 中進行 forward propagation.
last_hidden_layer = output[0] # 這就是 “test_input" 的 word representation.
print(last_hidden_layer.shape)
# (1, 64, 768) => (batch, words, word vector dimension)
transformers
中的所有 model
都是繼承 torch.nn.Module
而來,在使用上其實跟 Pytorch 的 Model 相當類似! 所以在上述的語法中將 encode_result
送入 model_bert
中,就等同於將 encode_result
送入 model
中進行 Forward Propagation,最後即可得到 Word Representation.
至於為什麼 last_hidden_layer = output[0]
,可以參考 BertModel 的 Return 欄位,回傳的結果有許多內容,而第一個就是「最後一層的 Hidden States」也就是我們要的 Word Representation.
附上 BertModel
的官方文件連結:
BERT - transformers 2.11.0 documentation
Transformers 的優勢
了解 Transformers
的基本運作原理後,再回頭強調一次它的優勢!:
可視為封裝好的 Language Model :
Transformers
的 Model 繼承了torch.nn.Module
,也就是說在設計整個任務的過程中,不需要自己去定義 “Pre-trained Language Model” 的結構、也不需要自行載入權重,**只需要將其視為一個Pytorch 預先定義好的 Model :**實作一個Transformers
的Model、載入相關權重,接下來只需要組裝就好了!model_bert = BertModel.from_pretrained("bert-base-cased") model_ln1 = nn.Linear(768, 3) output = model_bert(encode_result) # 等同於將 encode_result 送入 model 中進行 forward propagation. last_hidden_layer = output[0] output = model_ln1(last_hidden_layer) # 可以直接再將 BertModel 的 Output送入其他自定義的結構。
了解到如何組裝、如何使用後,除了將其拿來做 Feature Extraction 外,也可以用相同的概念組裝成 Transfer Learning 的模型架構!不過這超出這則筆記想要紀錄的範圍了~
另外,習慣使用 Tensorflow 的朋友也別傷心,
transformers
也有提供 tensorflow 版本的 Model (例如tfBertModel
)!同樣的架構能使用多種模型 → 方便抽換
由於
Transformers
中包含許多模型:有部分是官方持續新增,有些則是使用者互相釋出,導致使用這套架構就能快速切換多種模型。MODEL = "bert-base-cased" model_language = AutoModel.from_pretrained(MODEL) model_ln1 = nn.Linear(768, 3) output = model_language(encode_result) # 等同於將 encode_result 送入 model 中進行 forward propagation. last_hidden_layer = output[0] output = model_ln1(last_hidden_layer) # 可以直接再將 model_language 的 Output送入其他自定義的結構。
相同的程式碼,只需要更換模型變數即可達到「抽換底層 Pre-trained Langauge Model的效果」
MODEL = "xlnet-large-cased" model_language = AutoModel.from_pretrained(MODEL) model_ln1 = nn.Linear(768, 3) output = model_language(encode_result) # 等同於將 encode_result 送入 model 中進行 forward propagation. last_hidden_layer = output[0] output = model_ln1(last_hidden_layer) # 可以直接再將 model_language 的 Output送入其他自定義的結構。
後記
了解 Transformers
這個套件後,在進行 NLP 的相關任務上節省了許多時間,可以快速地切換Pre-trained Language Model 來進行各種嘗試。
此外,在Survey新的Language Model時,如果在Github上面看到它支援 transformers
,會備感欣慰QQ,頓時感到世界的美好~
感謝你的閱讀,希望你也能跟我一起感受世界的美好!XD
有任何問題歡迎一起交流! 下次見!