本文會盡可能詳細的介紹BERT的結(jié)構(gòu),預(yù)訓(xùn)練方法細節(jié)
一 BERT的結(jié)構(gòu)
BERT的具體結(jié)構(gòu)如下圖所示,其只用到了transformer的encoder部分,由多層tranformer的encoder堆疊而成,因此我們可以看到BERT并未在結(jié)構(gòu)上進行了創(chuàng)新,其主要創(chuàng)新點在于預(yù)訓(xùn)練的部分


BERT將輸入文本轉(zhuǎn)化為為向量表示其實是由三部分相加構(gòu)成(圖2),token embeddings,segment embeddings,和 posistion embeddings 。由于需要相加操作,這三個embedding的大小都是一致的,embedding size 通常和BERT的隱層大小保持一致。這三部分參數(shù)都是通過訓(xùn)練得到的,segment emdeddings 是一個 2 X embedding size的矩陣,第一行對應(yīng)句子A的embedding,第二行對應(yīng)句子B的embedding,需要注意的是這里得到position emedding參數(shù)的方式和transformer不一樣,在transormer里,postition embedding是由sin/con函數(shù)直接得到,不需要訓(xùn)練。圖2出現(xiàn)的<cls>和<sep>將會在下文中進行解釋。
二 BERT的框架
框架分為兩步,一是預(yù)訓(xùn)練,二是fine-tuning

2.1 預(yù)訓(xùn)練BERT
預(yù)訓(xùn)練的語料是用大量的無標簽數(shù)據(jù)構(gòu)成的,具體地預(yù)訓(xùn)練任務(wù)有兩個,masked LM 和預(yù)測是否為下個句子,每個輸入序列可以由一個或兩個有上下文聯(lián)系的句子構(gòu)成,句子之前用<SEP>分隔,在每個輸入序列的最前面加<CLS> token
2.1.1 masked LM(MLM) —— BERT論文中的一個重要創(chuàng)新點
隨機mask句子中的一部分詞(15%),然后預(yù)測這些mask詞的實際值,注意此時輸出序列任何輸入序列一樣長,只不過計算loss的時候只計算mask位置的,如原始句子: I eat [mask] apple,輸出 I eat an apple.
論文中還提到對句子中選擇的15%mask的詞,其中80%會被mask,10%會保留原來的詞,10%會被隨機替換成另一個詞(random token)
tensorflow實現(xiàn)bert的代碼中將原始數(shù)據(jù)復(fù)制了10份,再將句子中選擇15%的mask(10份復(fù)制的 8份mask, 1份保留,1份隨機替換)
:為什么要將10%被選擇mask的詞保留原來的詞
如果將選擇的mask的詞100%都mask,由于在finetune的階段,所有詞都已知,沒有<mask>,如果模型只在帶<mask>的句子上訓(xùn)練過,那么模型就只知道根據(jù)其他詞的信息來預(yù)測當前詞,而不會利用這個詞本身的信息(被mask的詞),導(dǎo)致?lián)p失一部分信息
:為什么10%會被隨機替換成另一個詞
因為如果都用原來的詞,預(yù)訓(xùn)練時,模型會學(xué)到如果當前詞是mask,就根據(jù)其他詞的信息推斷這個詞,如果是正常的,那么可能會直接照抄原來的單詞,不利于模型學(xué)習(xí)單詞間的依賴關(guān)系。使用了random token有助于模型在任何位置的token上都把當前token信息和上下文信息相結(jié)合。
2.1.2 預(yù)測是否為下個句子(Next Sentence Prediction, NSP)
構(gòu)建語料:每個訓(xùn)練例子有句子A,B,B有50%的幾率是A的下一句,標記上是下一句標簽,50%的幾率是隨機選的,標記上非下一句標簽,構(gòu)建輸入序列的時候是形如 <cls>A<sep>B 這樣的格式,見圖2
2.2 fine-tune
用第一步預(yù)訓(xùn)練得到的參數(shù)初始化BERT,再將BERT接入到適合下游任務(wù)的結(jié)構(gòu)中。
舉個最簡單的例子,對于分類任務(wù)來說,將softmax層接在<CLS> token輸入對應(yīng)最后一個隱層輸出(這個一般視為概括了整個句子的信息)或者在softmax層之前再接單層全連接網(wǎng)絡(luò)
從圖二中看到BERT在預(yù)訓(xùn)練的時候一個輸入序列是由兩個句子組成,那么如果需要finetune的下游任務(wù)為單句的話輸入格式要怎么處理?
A:BERT預(yù)訓(xùn)練的輸入序列用了兩個句子的拼接是為了NSP任務(wù),當要finetune的任務(wù)是單句的時候,輸入格式直接為<CLS> My dog is cute
使用BERT-base進行fine-tune需要12G以上的GPU,預(yù)訓(xùn)練BERT需要28G以上GPU
三 BERT實際應(yīng)用
3.1 如何用于長文本,如在閱讀理解任務(wù)中
答案是用sliding windows,即把文檔分成有重疊的若干段,每一段都當成獨立的文檔送入BERT,最后再對這些獨立文檔得到的結(jié)果進行整合
sliding windows只能用于訓(xùn)練,測試階段可通過設(shè)置batchsize為將長文本讀取
3.2 如何用于多文檔
現(xiàn)有的主要思路是先用retrieval選一些相關(guān)的doc,再當做閱讀理解的問題做
參考資料
知乎上的一一篇文章 https://zhuanlan.zhihu.com/p/46652512
kaggle上的一篇高分教程 https://www.kaggle.com/abhinand05/bert-for-humans-tutorial-baseline
google提供的BERT預(yù)訓(xùn)練模型(有英文也有中文的)https://github.com/google-research/bert#pre-trained-models
bert版本的pytorch https://github.com/huggingface/transformers
pytorch版bert使用教程https://zhuanlan.zhihu.com/p/66057193
pytorch版bert中文使用博客https://blog.csdn.net/ccbrid/article/details/88732857
包括了BERT結(jié)構(gòu)的解析https://cloud.tencent.com/developer/article/1389555