最近正在預訓練乙個中文pytorch版本的bert,模型部分**是基於huggingface發布的版本,預訓練過程還是參考google的**。
值得吐槽的是huggingface之前發布的分支名叫pytorch-pretrain-bert,後來又加上了gpt2、xlnet一系列當紅模型,結果索性改了個名字叫做pytorch-transformers。
下面是正文部分,整個正文我按照資料生成、預訓練、finetune以及其他閒雜部分組織,如有不當的地方還請大家指正。
這一部分主要解析預訓練bert所需要的資料獲取和處理,主要**來自google官方**。
google處理好之後的資料是採用換行來分句子,採用空行來分割上下文,我簡單粗暴得直接採用了json檔案,這個只影響後續檔案的讀取方式。舉個例子,google處理完之後檔案應該是人如下形式:
知乎
在知乎尋找知識一定是搞錯了什麼。
微博同上。
下一步,是構造預訓練任務所需要的訓練資料。眾所周知,bert預訓練任務有兩個:masked language model和next sentence prediction。簡單描述為:在一句話中mask掉幾個單詞然後對mask掉的單詞做**、判斷兩句話是否為上下文的關係,而這兩個訓練任務是同時進行的,也就意味著訓練語料的形式大約為:
[cls]谷歌和[mask][mask]都是不存在的。[sep]同時,[mask]也是不存在的。[sep]
先看幾個比較重要(或者**沒有提及)的超引數:
dupe_factor 這意味著我們對同乙份維基語料可以生成多次的資料,因為mask具有一定的隨機性,所以當文字比較少時可以設定較大的重複次數來取得較多的訓練樣本。
max_predictions_per_seq,乙個樣本中最多有多少個token被mask掉,這個值應該接近最大序列長度*mask概率。
short_seq_prob,並不是乙個樣本多長就生成多長的樣本,也以一定概率生成短句,提高多樣性。
# 重複幾次生成
for _ in range(dupe_factor):
#對每篇文章
for document_index in range(len(all_documents)):
instances.extend(
create_instances_from_document(
all_documents, document_index, max_seq_length, short_seq_prob,
masked_lm_prob, max_predictions_per_seq, vocab_words, rng))
從當前文章中抽取句子,直到整篇文章抽完了或者token長度超過指定值(如128);如果next sentence為true的話直接做mask,如果為false就從另一篇文章中抽取一些句子組成next sentence,最後再加上[cls][sep]。
就說幾點我覺得值得注意的:
每篇文章並不一定只是生成乙個樣本。假如有一篇文章比較長,有1000個token,規定文字最長為128,那麼這篇文章將會有好多樣本。
這裡sentence的概念並不是自然意義上的一句話,而是連續的token,一般可能是很多句話而非一句話。
如果取樣出來的句子長度超過了限制,隨機從頭或者尾去掉比較長sentence的一些token。
bert模型是基於transformer的encoder,主要模型結構就是transformer的堆疊。
當我們組建好bert模型之後,只要把對應的token餵給bert,每一層transformer層吐出相應數量的hidden vector,一層層傳遞下去,直到最後輸出。模型就這麼簡單,專治花裡胡哨,這大概就是谷歌的暴力美學。
bert模型和transformer的關係
我們的任務有兩個,乙個是完形填空式的**,乙個是上下句關係的**。
模型輸入
由於使用了transformer的模型結構,transformer可以並行乙個序列,對於它來說每個詞的位置沒有意義,因為self attention對所有輸入一直看待,就算打亂順序輸入到模型也沒有任何差別,所以需要加上乙個位置embedding。值得注意的時候,這個也直接讓模型的finetune有了長度限制。因為預訓練的時候,position embedding也是跟著一起訓練的,在預訓練的時候只訓練了128或者512的position embedding,如果在下游任務的輸入過長,一般就做截斷處理。(或者讓position embedding跟下游任務的一起fine tune? 好像沒人這麼做,可能embedding位置離下游位置太遠了梯度已經很小了,而且512也夠用了吧。)
segment embedding實際上就是乙個2*hidden_size的embedding而已。
這裡embedding都是隨機初始化的,所以直接加起來得到最終的輸入,也不存在 什麼拼接,最終實際一條樣本的輸入就是[seq_len*hidden_size]。
模型認為輸出的每個位置和輸入的位置是一一對應的,在[cls]位置,我們認為這個位置集中了兩個句子之間的關係,而每個單詞位置的hidden state代表了**的單詞。
所以做單詞**的時候,先過乙個hidden到hidden的線性層,再和訓練的word_embedding做乘法(需要轉置)。對每乙個位置的輸出是乙個維度為hidden_size的vector,word_embedding維度為[vocab_size*hidden_size],變換之後得到乙個維度為vocab_size的vector,softmax後得到對這個位置單詞的**。
做next sentence**更簡單,只需要取[cls]的hidden state,經過[hidden,2]線性層得到對兩句話關係的**。
預訓練於fine tuning
由於模型訓練的時候,mask任務讓模型盡力去理解上下文關係,next sentence任務則是句子間關係。我們可以認為乙個訓練好的bert模型在每個詞的位置都充分融合了上下文資訊,在[cls]位置包含了句子關係資訊。
所以針對不同的下游任務,只需要在整個大框架下用一些簡單的資料進行簡單調整。以squad1.1任務為例子,輸入為[[cls]+問題+[sep]+正文+[sep]],假設答案span位置在[5,7],那麼我們只選擇第五個位置的hidden vector
這部分由於不太想寫就先寫這麼多。warmup學習率:
(1.0-is_warmup)*learning_rate+is_warmup*warmup_learning_rate
BERT 中文預訓練模型使用
只要是從事文字處理相關工作的人應該都知道 google 最新發布的 bert 模型,該模型屬於是詞向量的預訓練模型,一經提出便橫掃各大 nlp 任務,最近 google 公司如約推出了中文詞向量的預訓練模型,不得不說這是一件非常有良心的事情,在此膜拜和感謝 google 公司。那麼如何使用 bert...
基於bert預訓練模型的比較
基於bert預訓練模型的比較 electra roberta bert wwm albert xlnet 預訓練任務替換詞檢驗 遮掩詞 與bert相同 遮掩詞 sop permutation lm pretraining model生成器 判別器的模式,生成器與判別器的網路結構均為transform...
BERT的通俗理解 預訓練模型 微調
1 預訓練模型 bert是乙個預訓練的模型,那麼什麼是預訓練呢?舉例子進行簡單的介紹 假設已有a訓練集,先用a對網路進行預訓練,在a任務上學會網路引數,然後儲存以備後用,當來乙個新的任務b,採取相同的網路結構,網路引數初始化的時候可以載入a學習好的引數,其他的高層引數隨機初始化,之後用b任務的訓練資...