HuggingFace-transformers系列的介绍以及在下游任务中的使用
這篇博客主要面向對Bert系列在Pytorch上應用感興趣的同學,將涵蓋的主要內容是:Bert系列有關的論文,Huggingface的實現,以及如何在不同下游任務中使用預訓練模型。
看過這篇博客,你將了解:
- Transformers實現的介紹,不同的Tokenizer和Model如何使用。
- 如何利用HuggingFace的實現自定義你的模型,如果你想利用這個庫實現自己的下游任務,而不想過多關注其實現細節的話,那么這篇文章將會成為很好的參考。
所需的知識
安裝Huggface庫(需要預先安裝pytorch)
在閱讀這篇文章之前,如果你能將以下資料讀一遍,或者看一遍的話,在后續的閱讀過程中將極大地減少你陷入疑惑的概率。
- 視頻類內容:根據排序觀看更佳
- 李宏毅關于Elmo, Bert, GPT的講解
- Goebels關于transformerXL的講解
- Kilcher關于XLnet的講解
- McCormick關于ALBERT的講解
或者,你更愿意去看論文的話:
- 相關論文:根據排序閱讀更佳
- arXiv:1810.04805, BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding, Authors: Jacob Devlin, Ming-Wei Chang, Kenton Lee, Kristina Toutanova
- arXiv:1901.02860, Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context, Authors: Zihang Dai, Zhilin Yang, Yiming Yang, William W. Cohen, Jaime Carbonell, Quoc V. Le and Ruslan Salakhutdinov.
- XLNet論文
- ALBERT論文
HuggingFace模型加載+下游任務使用
項目組件
一個完整的transformer模型主要包含三部分:
Config,控制模型的名稱、最終輸出的樣式、隱藏層寬度和深度、激活函數的類別等。將Config類導出時文件格式為 json格式,就像下面這樣:
{"attention_probs_dropout_prob": 0.1,"hidden_act": "gelu","hidden_dropout_prob": 0.1,"hidden_size": 768,"initializer_range": 0.02,"intermediate_size": 3072,"max_position_embeddings": 512,"num_attention_heads": 12,"num_hidden_layers": 12,"type_vocab_size": 2,"vocab_size": 30522 }當然,也可以通過config.json來實例化Config類,這是一個互逆的過程。
Tokenizer,這是一個將純文本轉換為編碼的過程。注意,Tokenizer并不涉及將詞轉化為詞向量的過程,僅僅是將純文本分詞,添加[MASK]標記、[SEP]、[CLS]標記,并轉換為字典索引。Tokenizer類導出時將分為三個文件,也就是:
-
vocab.txt
詞典文件,每一行為一個詞或詞的一部分
-
special_tokens_map.json 特殊標記的定義方式
{"unk_token": "[UNK]", "sep_token": "[SEP]", "pad_token": "[PAD]", "cls_token": "[CLS]", "mask_token": "[MASK]"} -
tokenizer_config.json 配置文件,主要存儲特殊的配置。
Model,也就是各種各樣的模型。除了初始的Bert、GPT等基本模型,針對下游任務,還定義了諸如BertForQuestionAnswering等下游任務模型。模型導出時將生成config.json和pytorch_model.bin參數文件。前者就是1中的配置文件,這和我們的直覺相同,即config和model應該是緊密聯系在一起的兩個類。后者其實和torch.save()存儲得到的文件是相同的,這是因為Model都直接或者間接繼承了Pytorch的Module類。從這里可以看出,HuggingFace在實現時很好地尊重了Pytorch的原生API。
導入Bert系列基本模型的方法
通過官網自動導入
官方文檔中初始教程提供的方法為:
# Load pre-trained model (weights) # model = BertModel.from_pretrained('bert-base-uncased')這個方法需要從官方的s3數據庫下載模型配置、參數等信息(代碼中已配置好位置)。這個方法雖然簡單,但是在國內并不可用。當然你可以先嘗試一下,不過會有很大的概率無法下載模型。
手動下載模型信息并導入
在HuggingFace官方模型庫上找到需要下載的模型,點擊模型鏈接, 這個例子使用的是bert-base-uncased模型
點擊List all files in model,將其中的文件一一下載到同一目錄中。例如,對于XLNet:
# List of model files config.json 782.0B pytorch_model.bin 445.4MB special_tokens_map.json 202.0B spiece.model 779.3KB tokenizer_config.json 2.0B但是這種方法有時也會不可用。如果您可以將Transformers預訓練模型上傳到迅雷等網盤的話,請在評論區告知,我會添加在此博客中,并為您添加博客友鏈。
通過下載好的路徑導入模型:
import transformers MODEL_PATH = r"D:\transformr_files\bert-base-uncased/" # a.通過詞典導入分詞器 tokenizer = transformers.BertTokenizer.from_pretrained(r"D:\transformr_files\bert-base-uncased\bert-base-uncased-vocab.txt") # b. 導入配置文件 model_config = transformers.BertConfig.from_pretrained(MODEL_PATH) # 修改配置 model_config.output_hidden_states = True model_config.output_attentions = True # 通過配置和路徑導入模型 model = transformers.BertModel.from_pretrained(MODEL_PATH,config = model_config)利用分詞器分詞
利用分詞器進行編碼
-
對于單句:
# encode僅返回input_ids tokenizer.encode("i like you") Out : [101, 1045, 2066, 2017, 102] -
對于多句:
# encode_plus返回所有編碼信息 tokenizer.encode_plus("i like you", "but not him") Out : {'input_ids': [101, 1045, 2066, 2017, 102, 2021, 2025, 2032, 102],'token_type_ids': [0, 0, 0, 0, 0, 1, 1, 1, 1],'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}
模型的所有分詞器都是在PreTrainedTokenizer中實現的,分詞的結果主要有以下內容:
{ input_ids: list[int], token_type_ids: list[int] if return_token_type_ids is True (default) attention_mask: list[int] if return_attention_mask is True (default) overflowing_tokens: list[int] if a max_length is specified and return_overflowing_tokens is True num_truncated_tokens: int if a max_length is specified and return_overflowing_tokens is True special_tokens_mask: list[int] if add_special_tokens if set to True and return_special_tokens_mask is True }編碼解釋:
- 'input_ids':顧名思義,是單詞在詞典中的編碼
- 'token_type_ids', 區分兩個句子的編碼
- 'attention_mask', 指定對哪些詞進行self-Attention操作
- 'overflowing_tokens', 當指定最大長度時,溢出的單詞
- 'num_truncated_tokens', 溢出的token數量
- 'return_special_tokens_mask',如果添加特殊標記,則這是[0,1]的列表,其中0指定特殊添加的標記,而1指定序列標記
將分詞結果輸入模型,得到編碼
# 添加batch維度并轉化為tensor input_ids = torch.tensor([input_ids]) token_type_ids = torch.tensor([token_type_ids]) # 將模型轉化為eval模式 model.eval() # 將模型和數據轉移到cuda, 若無cuda,可更換為cpu device = 'cuda' tokens_tensor = input_ids.to(device) segments_tensors = token_type_ids.to(device) model.to(device)# 進行編碼 with torch.no_grad():# See the models docstrings for the detail of the inputsoutputs = model(tokens_tensor, token_type_ids=segments_tensors)# Transformers models always output tuples.# See the models docstrings for the detail of all the outputs# In our case, the first element is the hidden state of the last layer of the Bert modelencoded_layers = outputs # 得到最終的編碼結果encoded_layersBert最終輸出的結果為:
sequence_output, pooled_output, (hidden_states), (attentions)以輸入序列長度為14為例
| 0 | sequence_output | torch.Size([1, 14, 768]) | 輸出序列 |
| 1 | pooled_output | torch.Size([1, 768]) | 對輸出序列進行pool操作的結果 |
| 2 | (hidden_states) | tuple,13*torch.Size([1, 14, 768]) | 隱藏層狀態(包括Embedding層),取決于modelconfig中output_hidden_states |
| 3 | (attentions) | tuple,12*torch.Size([1, 12, 14, 14]) | 注意力層,取決于參數中output_attentions |
Bert總結
這一節我們以Bert為例對模型整體的流程進行了了解。之后的很多模型都基于Bert,并基于Bert進行了少量的調整。其中的輸出和輸出參數也有很多重復的地方。
利用預訓練模型在下游任務上微調
如開頭所說,這篇文章重點在于"如何進行模型的調整以及輸入輸出的設定", 以及"Transformer的實現進行簡要的提及", 所以,我們不會去介紹、涉及如何寫train循環等話題,而僅僅專注于模型。也就是說,我們將止步于跑通一個模型,而不計批量數據預處理、訓練、驗證等過程。
同時,這里更看重如何基于Bert等初始模型在實際任務上進行微調,所以我們不會僅僅地導入已經在下游任務上訓練好的模型參數,因為在這些模型上使用的方法和上一章的幾乎完全相同。
這里的輸入和輸入以模型的預測過程為例。
問答任務 via Bert
模型的構建:
from transformers import BertTokenizer, BertForQuestionAnswering import torchMODEL_PATH = r"D:\transformr_files\bert-base-uncased/" # 實例化tokenizer tokenizer = BertTokenizer.from_pretrained(r"D:\transformr_files\bert-base-uncased\bert-base-uncased-vocab.txt") # 導入bert的model_config model_config = transformers.BertConfig.from_pretrained(MODEL_PATH) # 首先新建bert_model bert_model = transformers.BertModel.from_pretrained(MODEL_PATH,config = model_config) # 最終有兩個輸出,初始位置和結束位置(下面有解釋) model_config.num_labels = 2 # 同樣根據bert的model_config新建BertForQuestionAnswering model = BertForQuestionAnswering(model_config) model.bert = bert_model一般情況下,一個基本模型對應一個Tokenizer, 所以并不存在對應于具體下游任務的Tokenizer。這里通過bert_model初始化BertForQuestionAnswering。
任務輸入:問題句,答案所在的文章?"Who was Jim Henson?", "Jim Henson was a nice puppet"
任務輸出:答案?"a nice puppet"
# 設定模式 model.eval() question, text = "Who was Jim Henson?", "Jim Henson was a nice puppet" # 獲取input_ids編碼 input_ids = tokenizer.encode(question, text) # 手動進行token_type_ids編碼,可用encode_plus代替 token_type_ids = [0 if i <= input_ids.index(102) else 1 for i in range(len(input_ids))] # 得到評分, start_scores, end_scores = model(torch.tensor([input_ids]), token_type_ids=torch.tensor([token_type_ids])) # 進行逆編碼,得到原始的token all_tokens = tokenizer.convert_ids_to_tokens(input_ids) #['[CLS]', 'who', 'was', 'jim', 'henson', '?', '[SEP]', 'jim', 'henson', 'was', 'a', 'nice', 'puppet', '[SEP]']模型輸入:inputids, token_type_ids
模型輸出:start_scores, end_scores 形狀都為torch.Size([1, 14]),其中14為序列長度,代表每個位置是開始/結束位置的概率。
將模型輸出轉化為任務輸出:
# 對輸出的答案進行解碼的過程 answer = ' '.join(all_tokens[torch.argmax(start_scores) : torch.argmax(end_scores)+1]) # assert answer == "a nice puppet" # 這里因為沒有經過微調,所以效果不是很好,輸出結果不佳。 print(answer) # 'was jim henson ? [SEP] jim henson was a nice puppet [SEP]'文本分類任務(情感分析等) via XLNet
模型的構建:
from transformers import XLNetConfig, XLNetModel, XLNetTokenizer, XLNetForSequenceClassification import torch # 定義路徑,初始化tokenizer XLN_PATH = r"D:\transformr_files\XLNetLMHeadModel" tokenizer = XLNetTokenizer.from_pretrained(XLN_PATH) # 加載配置 model_config = XLNetConfig.from_pretrained(XLN_PATH) # 設定類別數為3 model_config.num_labels = 3 # 直接從xlnet的config新建XLNetForSequenceClassification(和上一節方法等效) cls_model = XLNetForSequenceClassification.from_pretrained(XLN_PATH, config=model_config)任務輸入:句子?"i like you, what about you"
任務輸出:句子所屬的類別?class1
# 設定模式 model.eval() token_codes = tokenizer.encode_plus("i like you, what about you")模型輸入:inputids, token_type_ids
模型輸出:logits, hidden states, 其中logits形狀為torch.Size([1, 3]), 其中的3對應的是類別的數量。當訓練時,第一項為loss。
其他的任務,將繼續更新
其他的模型和之前的兩個大致是相同的,你可以自己發揮。我會繼續在相關的庫上進行實驗,如果發現用法不一樣的情況,將會添加在這里。
參考
本文章主要對HuggingFace庫進行了簡要介紹。具體安裝等過程請參見官方github倉庫。
總結
以上是生活随笔為你收集整理的HuggingFace-transformers系列的介绍以及在下游任务中的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 预训练模型transformers综合总
- 下一篇: 第14章 用BERT实现中文语句分类