一、BERT介紹
論文:BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding
簡介:BERT是基於Transformer的深度雙向語言表征模型,基本結構如圖所示,本質上是利用Transformer結構構造了一個多層雙向的Encoder網絡。Transformer是Google在2017年提出的基於自注意力機制(Self-attention)的深層模型,在包括機器翻譯在內的多項NLP任務上效果顯著,超過RNN且訓練速度更快。

從上圖可見,Bert其實和ELMO及GPT存在千絲萬縷的關系,比如如果我們把GPT預訓練階段換成雙向語言模型,那么就得到了Bert;而如果我們把ELMO的特征抽取器換成Transformer,那么我們也會得到Bert。與GPT不同之處:在預訓練階段采用了類似ELMO的雙向語言模型;另外一點是語言模型的數據規模要比GPT大。
Bert最關鍵兩點:
- 特征抽取器采用Transformer;
- 預訓練的時候采用雙向語言模型。
二、BERT模型結構

上圖中$E_i$是指的單個字或詞, $T_i$指的是最終計算得出的隱藏層, Transformer中的注意力矩陣和注意力加權, 經過這樣的操作之后, 序列里面的每一個字, 都含有這個字前面的信息和后面的信息, 這就是雙向的理解, 在這里, 一句話中每一個字, 經過注意力機制和加權之后, 當前這個字等於用這句話中其他所有字重新表達了一遍, 每個字含有了這句話中所有成分的信息.

根據參數設置的不同,Google 論文中提出了Base和Large兩種BERT模型。

三、預訓練
BERT預訓練過程包含兩個不同的預訓練任務,分別是Masked Language Model和Next Sentence Prediction任務。
1.Masked Language Model(MLM)
隨機把一些單詞變為[Mask],然后預測這些被遮蓋的詞來訓練雙向語言模型,並且使每個詞的表征參考上下文信息。假設輸入里面的第二個詞匯是被蓋住的,把其對應的embedding輸入到一個多分類模型中,來預測被蓋住的單詞。之后做Loss的時候只計算被遮蓋部分的Loss。

實際操作方式如下:
【1】隨機把一句話中15%的token替換成以下內容:
- 這些token中有80%的幾率被替換成[mask];
- 有10%的幾率被替換成任意一個其他的token;
- 有10%的幾率原封不動

【2】之后讓模型預測和還原被遮蓋掉或替換掉的部分,模型最終輸出的隱藏層的計算結果的難度是:
$X_{hidden}$: [batch size, seq len, embedding dim]
我們初始化一個映射層的權重$W_{vocab}$:
$W_{vocab}$: [embedding dim, vocab size]
我們用$W_{vocab}$完成隱藏維度到字向量數量的映射, 只要求$X_{hidden}$和$W_{vocab}$的矩陣乘(點積):
$X_{hidden}W_{vocab}$: [batch size, seq len, vocab size]
之后把上面的計算結果在vocab size(最后一個)維度做$softmax$歸一化, 是每個字對應的vocab size的和為1, 我們就可以通過vocab size里概率最大的字來得到模型的預測結果, 就可以和我們准備好的$Label$做損失($Loss$)並反傳梯度了. 注意做損失的時候, 只計算在第1步里當句中隨機遮蓋或替換的部分, 其余部分不做損失, 對於其他部分, 模型輸出什么東西, 我們不在意.
這樣做會產生兩個缺點:
- 會造成預訓練和微調時的不一致,因為在微調時[MASK]總是不可見的;
- 由於每個Batch中只有15%的詞會被預測,因此模型的收斂速度比起單向的語言模型會慢,訓練花費的時間會更長。
解決方法:
- 對於第一個缺點的解決辦法是,把80%需要被替換成[MASK]的詞進行替換,10%的隨機替換為其他詞,10%保留原詞。由於Transformer Encoder並不知道哪個詞需要被預測,哪個詞是被隨機替換的,這樣就強迫每個詞的表達需要參照上下文信息。
- 對於第二個缺點目前沒有有效的解決辦法,但是從提升收益的角度來看,付出的代價是值得的。
2.Next Sentence Prediction(NSP)
首先我們拿到屬於上下文的一對句子, 也就是兩個句子, 之后我們要在這兩段連續的句子里面加一些特殊$token$: $[cls]$上一句話,$[sep]$下一句話.$[sep]$
也就是在句子開頭加一個$[cls]$, 在兩句話之中和句末加$[sep]$, 具體地就像下圖一樣:

我們看到上圖中兩句話是[cls] my dog is cute [sep] he likes playing [sep], [cls]我的狗很可愛[sep]他喜歡玩耍[sep], 除此之外, 我們還要准備同樣格式的兩句話, 但他們不屬於上下文關系的情況; [cls]我的狗很可愛[sep]企鵝不擅長飛行[sep], 可見這屬於上下句不屬於上下文關系的情況;在實際的訓練中, 我們讓上面兩種情況出現的比例為1:1, 也就是一半的時間輸出的文本屬於上下文關系, 一半時間不是.
我們進行完上述步驟之后, 還要隨機初始化一個可訓練的 segment embeddings , 見上圖中, 作用就是用 embeddings 的信息讓模型分開上下句, 我們一把給上句全 0 的 tokentoken , 下句啊全 1 的 token , 讓模型得以判斷上下句的起止位置, 例如:
[cls] 我的狗很可愛 [sep] 企鵝不擅長飛行 [sep] 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
上面 0 和 1 就是 segment embeddings .
注意力機制就是, 讓每句話中的每一個字對應的那一條向量里, 都融入這句話所有字的信息, 那么我們在最終隱藏層的計算結果里, 只要取出$[cls]token$所對應的一條向量, 里面就含有整個句子的信息, 因為我們期望這個句子里面所有信息都會往$[cls]token$所對應的一條向量里匯總:
模型最終輸出的隱藏層的計算結果的維度是:
$X_{hidden}$: [batch_size, seq_len, embedding_dim]$
我們要取出$[cls]token$所對應的一條向量, $[cls]$對應着seq len維度的第$0$條:
$cls\_vector = X_{hidden}[:, 0, :]$,
$cls\_vector \in \mathbb{R}^{batch\_size, embedding\_dim} $
之后我們再初始化一個權重, 完成從embedding dim維度到1的映射, 也就是邏輯回歸, 之后用$sigmoid$函數激活, 就得到了而分類問題的推斷.
我們用$\hat{y}$來表示模型的輸出的推斷, 他的值介於$(0, 1)$之間:
$\hat{y} = sigmoid(Linear(cls vector)) \quad \hat{y} \in (0, 1)$
3.BERT訓練參數和技巧
BERT論文中, 推薦的模型參數為: 基准模型transformer_block=12, embedding_dimension=768, num_heads=12, Total Param eters=110M), 可見其中共有1.1億參數, 除此之外, 還有比基准模型還大的高性能模型, 參數量為3億, 要想訓練並使用這么大參數的模型, 需要充裕的計算資源。
訓練技巧:
- 因為我們是按單個字為單位訓練BERT, 所以在Masked LM里面, 把句子中的英文單詞分出來, 將英文單詞所在的區域一起遮蓋掉, 讓模型預測這個部分;
- 很多句子里含有數字, 顯然在Masked LM中, 讓模型准確地預測數據是不現實的, 所以我們把原文中的數字(包括整數和小數)都替換成一個特殊token, #NUM#, 這樣模型只要預測出這個地方應該是某些數字就可以來。
四、BERT完成下游任務

NLP四個任務:序列標注、分類任務、句子關系推斷、生成式任務
1.句子關系推斷
對於句子關系類任務,和GPT類似,加上一個起始和終結符號,句子之間加個分隔符即可。對於輸出來說,把第一個起始符號對應的Transformer最后一層位置上面串接一個softmax分類層即可。
舉例:給定一個前提/假設,得到推論是否正確:

為了訓練一個理解句子間關系的模型,引入一個下一句預測任務。這一任務的訓練語料可以從語料庫中抽取句子對包括兩個句子A和B來進行生成,其中50%的概率B是A的下一個句子,50%的概率B是語料中的一個隨機句子。NSP任務預測B是否是A的下一句。NSP的目的是獲取句子間的信息,這點是語言模型無法直接捕捉的。
Google的論文結果表明,這個簡單的任務對問答和自然語言推理任務十分有益,但是后續一些新的研究[15]發現,去掉NSP任務之后模型效果沒有下降甚至還有提升。我們在預訓練過程中也發現NSP任務的准確率經過1-2個Epoch訓練后就能達到98%-99%,去掉NSP任務之后對模型效果並不會有太大的影響。
2.分類任務
如果是分類任務,在句子前面加一個標志,將其經過Bert得到的embedding輸出到二分類模型中,得到分類結果。二分類模型從頭開始學,而Bert在預訓練的基礎上進行微調(fine-tuning)。

文中還有很多其他的應用,如單詞分類:

3.序列標注
輸入部分和單句分類是一樣的,只需要輸出部分Transformer最后一層每個單詞對應位置都進行分類即可。


4.生成式任務
對於機器翻譯或者文本摘要,聊天機器人這種生成式任務,同樣可以稍作改造即可引入Bert的預訓練成果。只需要附着在S2S結構上,encoder部分是個深度Transformer結構,decoder部分也是個深度Transformer結構。根據任務選擇不同的預訓練數據初始化encoder和decoder即可。這是相當直觀的一種改造方法。當然,也可以更簡單一點,比如直接在單個Transformer結構上加裝隱層產生輸出也是可以的。
舉例:抽取式QA,抽取式的意思是輸入一個原文和問題,輸出兩個整數start和end,代表答案在原文中的起始位置和結束位置,兩個位置中間的結果就是答案。
解決QA問題:把問題 - 分隔符 - 原文輸入到BERT中,每一個單詞輸出一個黃顏色的embedding,這里還需要學習兩個(一個橙色一個藍色)的向量,這兩個向量分別與原文中每個單詞對應的embedding進行點乘,經過softmax之后得到輸出最高的位置。正常情況下start <= end,但如果start > end的話,說明是矛盾的case,此題無解。

五、BERT總結
1.優點
- 考慮雙向信息(LM只考慮單向)
- 長距離依賴(long-term dependence)
2.缺點
- 測試數據沒有[mask],discrepancy,削弱了一定的泛化能力。
- 缺乏生成能力(LM具有生成能力)
- 針對每一個mask預測時,沒有考慮mask間的相關性(例:w1 w2 [mask] w4 w5 [mask] w7,第一個mask為w3,第二個mask為w6,理想情況是需要考慮w3和w6間相關性的,bert里面沒有考慮這點,二者是獨立的)
類似於朴素貝葉斯,沒有考慮特征間的相關性。
六、應用
1.獲得句向量
使用BERT模型生成句子序列向量 - 不著人間風雨門 - 博客園
參考文獻:
【1】美團BERT的探索和實踐
【3】aespresso/a_journey_into_math_of_ml: 漢語自然語言處理視頻教程-開源學習資料
【6】Dissecting BERT Part 1: The Encoder - Dissecting BERT - Medium
【7】使用BERT模型生成句子序列向量 - 不著人間風雨門 - 博客園
【8】NLP:自回歸(Autoregressive LM)與自編碼語言模型(Autoencoder LM)_人工智能_小馬哥的博客-CSDN博客
