一文讀懂BERT(原理篇)

一文讀懂BERT(原理篇)

2018年的10月11日,Google發佈的論文《Pre-training of Deep Bidirectional Transformers for Language Understanding》,成功在 11 項 NLP 任務中取得 state of the art 的結果,贏得自然語言處理學界的一片讚譽之聲。

本文是對近期關於BERT論文、相關文章、代碼進行學習後的知識梳理,僅爲自己學習交流之用。因筆者精力有限,如果文中因引用了某些文章觀點未標出處還望作者海涵,也希望各位一起學習的讀者對文中不恰當的地方進行批評指正。

1)資源梳理

2)NLP發展史

關於NLP發展史,特別推薦weizier大佬的NLP的巨人肩膀。學術性和文學性都很棒,縱覽NLP近幾年的重要發展,對各算法如數家珍,深入淺出,思路清晰,文不加點,一氣呵成。
現對NLP發展脈絡簡要梳理如下:
在這裏插入圖片描述

  • 2001 - Neural language models(神經語言模型)
  • 2008 - Multi-task learning(多任務學習)
  • 2013 - Word embeddings(詞嵌入)
  • 2013 - Neural networks for NLP(NLP神經網絡)
  • 2014 - Sequence-to-sequence models
  • 2015 - Attention(注意力機制)
  • 2015 - Memory-based networks(基於記憶的網絡)
  • 2018 - Pretrained language models(預訓練語言模型)

2001 - 神經語言模型

第一個神經語言模型是Bengio等人在2001年提出的前饋神經網絡,如圖所示:
在這裏插入圖片描述這個模型將從表C中查找到的n個單詞作爲輸入向量表徵。這種向量被現在的學者們稱做“詞嵌入”。這些詞嵌入級聯後被輸入到一個隱藏層中,該隱藏層的輸出又被輸入到softmax層。更多關於模型的信息

語言建模通常是應用RNN時的第一步,是一種非監督學習形式。儘管它很簡單,但卻是本文後面討論的許多技術發展的核心:

  • 詞嵌入:word2vec 的目標是簡化語言建模。

  • sequence-to-sequence 模型:這種模型通過一次預測一個單詞生成一個輸出序列。

  • 預訓練語言模型:這些方法使用來自語言模型的表述進行遷移學習。

反過來講,這意味着近年來 NLP 的許多重要進展都可以歸結爲某些形式的語言建模。爲了“真正”理解自然語言,僅僅從文本的原始形式中學習是不夠的。我們需要新的方法和模型。

2008- 多任務學習

多任務學習是在多個任務上訓練的模型之間共享參數的一種通用方法。在神經網絡中,可以通過給不同層施以不同的權重,來很容易地實現多任務學習。多任務學習的概念最初由Rich Caruana 在1993年提出,並被應用於道路跟蹤和肺炎預測(Caruana,1998)。直觀地說,多任務學習鼓勵模型學習對許多任務有用的表述。這對於學習一般的、低級的表述形式、集中模型的注意力或在訓練數據有限的環境中特別有用。詳情請看這篇文章

在2008年,Collobert 和 Weston 將多任務學習首次應用於 NLP 的神經網絡。在他們的模型中,查詢表(或單詞嵌入矩陣)在兩個接受不同任務訓練的模型之間共享,如下面的圖所示。
在這裏插入圖片描述

2013- 詞嵌入

用稀疏向量表示文本,即所謂的詞袋模型在 NLP 有着悠久的歷史。正如上文中介紹的,早在 2001年就開始使用密集向量表示詞或詞嵌入。Mikolov等人在2013年提出的創新技術是通過去除隱藏層,逼近目標,進而使這些單詞嵌入的訓練更加高效。雖然這些技術變更本質上很簡單,但它們與高效的word2vec配合使用,便能使大規模的詞嵌入訓練成爲可能。

Word2vec有兩種風格,如下面的圖所示:連續字袋 CBOW 和 skip-gram。不過他們的目標不同:一個是根據周圍的單詞預測中心單詞,而另一個則相反。
在這裏插入圖片描述雖然這些嵌入在概念上與使用前饋神經網絡學習的嵌入在概念上沒有區別,但是在一個非常大的語料庫上訓練之後,它們就能夠捕獲諸如性別、動詞時態和國家-首都關係等單詞之間的特定關係,如下圖所示。
!

2013 - NLP 神經網絡

2013 年和 2014 年是 NLP 問題開始引入神經網絡模型的時期。使用最廣泛的三種主要的神經網絡是:循環神經網絡、卷積神經網絡和遞歸神經網絡。

循環神經網絡(RNNs) 循環神經網絡是處理 NLP 中普遍存在的動態輸入序列的一個最佳的技術方案。Vanilla RNNs (Elman,1990)很快被經典的長-短期記憶網絡(Hochreiter & Schmidhuber,1997)所取代,它被證明對消失和爆炸梯度問題更有彈性。在 2013 年之前,RNN 仍被認爲很難訓練;Ilya Sutskever 的博士論文爲改變這種現狀提供了一個關鍵性的例子。下面的圖對 LSTM 單元進行了可視化顯示。雙向 LSTM(Graves等,2013)通常用於處理左右兩邊的上下文。
在這裏插入圖片描述卷積神經網絡(CNNs) 卷積神經網絡本來是廣泛應用於計算機視覺領域的技術,現在也開始應用於語言(Kalchbrenner等,2014;Kim等,2014)。文本的卷積神經網絡只在兩個維度上工作,其中濾波器(卷積核)只需要沿着時間維度移動。下面的圖顯示了NLP中使用的典型 CNN。
在這裏插入圖片描述卷積神經網絡的一個優點是它們比 RNN 更可並行化,因爲其在每個時間步長的狀態只依賴於本地上下文(通過卷積運算),而不是像 RNN 那樣依賴過去所有的狀態。使用膨脹卷積,可以擴大 CNN 的感受野,使網絡有能力捕獲更長的上下文(Kalchbrenner等,2016)。CNN 和 LSTM 可以組合和疊加(Wang等,2016),卷積也可以用來加速 LSTM(Bradbury等, 2017)。

遞歸神經網絡 RNN 和 CNN 都將語言視爲一個序列。然而,從語言學的角度來看,語言本質上是層次化的:單詞被組合成高階短語和從句,這些短語和從句本身可以根據一組生產規則遞歸地組合。將句子視爲樹而不是序列的語言學啓發思想產生了遞歸神經網絡(Socher 等人, 2013),如下圖所示
在這裏插入圖片描述遞歸神經網絡從下到上構建序列的表示,這一點不同於從左到右或從右到左處理句子的 RNN。在樹的每個節點上,通過組合子節點的結果來計算新的結果。由於樹也可以被視爲在 RNN 上強加不同的處理順序,所以 LSTM 自然地也被擴展到樹上(Tai等,2015)。

RNN 和 LSTM 可以擴展到使用層次結構。單詞嵌入不僅可以在本地學習,還可以在語法語境中學習(Levy & Goldberg等,2014);語言模型可以基於句法堆棧生成單詞(Dyer等,2016);圖卷積神經網絡可以基於樹結構運行(Bastings等,2017)。

2014-sequence-to-sequence 模型

2014 年,Sutskever 等人提出了 sequence-to-sequence 模型。這是一個使用神經網絡將一個序列映射到另一個序列的通用框架。在該框架中,編碼器神經網絡逐符號處理一個句子,並將其壓縮爲一個向量表示;然後,一個解碼器神經網絡根據編碼器狀態逐符號輸出預測值,並將之前預測的符號作爲每一步的輸入,如下圖所示。
!
機器翻譯是對這個框架比較成功的應用。2016 年,谷歌宣佈將開始用神經 MT 模型取代基於單片短語的 MT 模型(Wu等,2016)。根據 Jeff Dean 的說法,這意味着用 500 行神經網絡模型替換 50 萬行基於短語的MT代碼。

由於其靈活性,這個框架現在是自然語言生成任務的首選框架,其中不同的模型承擔了編碼器和解碼器的角色。重要的是,解碼器模型不僅可以解碼一個序列,而且可以解碼任意表徵。例如,可以基於圖像生成標題(Vinyals等,2015)(如下圖所示)、基於表生成文本(Lebret等,2016)和基於應用程序中源代碼更改描述(Loyola等,2017)。
在這裏插入圖片描述sequence-to-sequence 學習甚至可以應用於 NLP 中輸出具有特定結構的結構化預測任務。爲了簡單起見,輸出被線性化,如下面的圖所示,用於進行選區解析。神經網絡已經證明了在有足夠數量的訓練數據進行選區分析(Vinyals等,2015)和命名實體識別(Gillick等, 2016)的情況下,直接學習可以產生這種線性化輸出的能力。
在這裏插入圖片描述

2015- 注意力機制

注意力機制(Bahdanau 等,2015)是神經網絡機器翻譯(NMT)的核心創新之一,也是使 NMT模型勝過經典的基於短語的MT系統的關鍵思想。sequence-to-sequence模型的主要瓶頸是需要將源序列的全部內容壓縮爲一個固定大小的向量。注意力機制通過允許解碼器回頭查看源序列隱藏狀態來緩解這一問題,然後將其加權平均作爲額外輸入提供給解碼器,如下面的圖所示
在這裏插入圖片描述
注意力機制有很多不同的形式(Luong等,2015)。這裏有一個簡短的概述。注意力機制廣泛適用於任何需要根據輸入的特定部分做出決策的任務,並且效果不錯。它已被應用於一致性解析(Vinyals等,2015)、閱讀理解(Hermann等,2015)和一次性學習(Vinyals等,2016)等諸多領域。輸入甚至不需要是一個序列,即可以包含其他表示,如圖像字幕(Xu等,2015),如下圖所示。注意力機制的一個額外的功能是,它提供了一種少見的功能,我們可以通過檢查輸入的哪些部分與基於注意力權重的特定輸出相關來了解模型的內部工作方式。
在這裏插入圖片描述

2015 - 基於記憶的網絡

注意力機制可以看作是模糊記憶的一種形式。記憶由模型的隱藏狀態組成,模型選擇從記憶中檢索內容。研究者們提出了許多具有更明確記憶的模型。這些模型有不同的變體,如神經圖靈機(Graves等,2014)、記憶網絡(Weston等,2015)和端到端記憶網絡(Sukhbaatar等,2015)、動態記憶網絡(Kumar等,2015)、神經微分計算機(Graves等,2016)和循環實體網絡(Henaff等,2017)。

記憶的訪問通常基於與當前狀態的相似度,類似於注意力,通常可以寫入和讀取。模型在如何實現和利用內存方面有所不同。例如,端到端記憶網絡多次處理輸入,並更新記憶以實現多個推理步驟。神經圖靈機也有一個基於位置的尋址,這允許他們學習簡單的計算機程序,如排序。基於記憶的模型通常應用於一些特定任務中,如語言建模和閱讀理解。在這些任務中,長時間保存信息應該很有用。記憶的概念是非常通用的:知識庫或表可以充當記憶,而記憶也可以根據整個輸入或它的特定部分填充。

2018 - 預訓練語言模型

預訓練的詞嵌入與上下文無關,僅用於初始化模型中的第一層。一系列監督型任務被用於神經網絡的預訓練。相反,語言模型只需要無標籤的文本;因此,訓練可以擴展到數十億個tokens, new domains, new languages。預訓練語言模型於 2015 年被首次提出(Dai & Le,2015);直到最近,它們才被證明在各種任務中效果還是不錯的。語言模型嵌入可以作爲目標模型中的特徵(Peters等,2018),或者使用語言模型對目標任務數據進行微調(Ramachandranden等,2017; Howard & Ruder,2018)。添加語言模型嵌入可以在許多不同的任務中提供比最先進的技術更大的改進,如下面的圖所示。

在這裏插入圖片描述
預訓練的語言模型已經被證明可以用更少的數據進行學習。由於語言模型只需要無標記的數據,因此對於標記數據稀缺的低資源語言尤其有用。

其他里程碑事件

其他一些技術發展沒有上面提到的那樣流行,但仍然有廣泛的影響。

  • 基於字符的表示
    在字符上使用 CNN 或 LSTM 以獲得基於字符的詞表示的做法現在相當普遍,特別是對於形態信息重要或有許多未知單詞的豐富的語言和任務,效果更加明顯。據我所知,序列標籤使用基於字符的表示(Lample 等人,2016;普蘭克等人,2016),可以減輕在計算成本增加的情況下必須處理固定詞彙表的需要,並支持完全基於字符的 NMT (Ling 等人, 2016;Lee 等人,2017)。

  • 對抗學習
    對抗學習方法已經在 ML 領域掀起了風暴,在 NLP 中也有不同形式的應用。對抗性的例子越來越被廣泛使用,它不僅是作爲一種工具來探究模型和理解它們的失敗案例,而且也使自身更加魯棒(Jia & Liang, 2017)。(虛擬)對抗性訓練,即最壞情況擾動(Miyato 等人,2017)和領域對抗性損失(Ganin 等人, 2016;Kim 等人,2017),同樣可以使模型更加魯棒。生成對抗網絡(GANs)對於自然語言生成還不是很有效(Semeniuta 等人, 2018),但在匹配分佈時很有用(Conneau 等人, 2018)。

  • 強化學習
    強化學習已被證明對具有時間依賴性的任務有效,例如在訓練期間選擇數據(Fang 等人, 2017;Wu 等人, 2018)和建模對話(Liu 等人, 2018)。RL 對於直接優化不可微的末端度量(如 ROUGE 或 BLEU)也有效,反而在彙總中優化替代損失(如交叉熵)(Paulus 等人, 2018;Celikyilmaz 等人,2018)和機器翻譯場景效果就不明顯了(Ranzato 等人,2016)。類似地,逆向強化學習在過於複雜而無法指定數據的情況下也很有用,比看圖說話任務(Wang 等人, 2018)。

3)BERT:一切過往, 皆爲序章

Attention機制講解

attention是一種能讓模型對重要信息重點關注並充分學習吸收的技術,它不算是一個完整的模型,應當是一種技術,能夠作用於任何序列模型中。

Seq2Seq

在開始講解Attention之前,我們先簡單回顧一下Seq2Seq模型,傳統的機器翻譯基本都是基於Seq2Seq模型來做的,該模型分爲encoder層與decoder層,並均爲RNN或RNN的變體構成,如下圖所示:
在這裏插入圖片描述
在encode階段,第一個節點輸入一個詞,之後的節點輸入的是下一個詞與前一個節點的hidden state,最終encoder會輸出一個context,這個context又作爲decoder的輸入,每經過一個decoder的節點就輸出一個翻譯後的詞,並把decoder的hidden state作爲下一層的輸入。該模型對於短文本的翻譯來說效果很好,但是其也存在一定的缺點,如果文本稍長一些,就很容易丟失文本的一些信息,爲了解決這個問題,Attention應運而生。

Attention

Attention,正如其名,注意力,該模型在decode階段,會選擇最適合當前節點的context作爲輸入。Attention與傳統的Seq2Seq模型主要有以下兩點不同。

1)encoder提供了更多的數據給到decoder,encoder會把所有的節點的hidden state提供給decoder,而不僅僅只是encoder最後一個節點的hidden state。
在這裏插入圖片描述
2)decoder並不是直接把所有encoder提供的hidden state作爲輸入,而是採取一種選擇機制,把最符合當前位置的hidden state選出來,具體的步驟如下

  • 確定哪一個hidden state與當前節點關係最爲密切

  • 計算每一個hidden state的分數值(具體怎麼計算我們下文講解)

  • 對每個分數值做一個softmax的計算,這能讓相關性高的hidden state的分數值更大,相關性低的hidden state的分數值更低

這裏我們以一個具體的例子來看下其中的詳細計算步驟:
在這裏插入圖片描述
把每一個encoder節點的hidden states的值與decoder當前節點的上一個節點的hidden state相乘,如上圖,h1、h2、h3分別與當前節點的上一節點的hidden state進行相乘(如果是第一個decoder節點,需要隨機初始化一個hidden state),最後會獲得三個值,這三個值就是上文提到的hidden state的分數,注意,這個數值對於每一個encoder的節點來說是不一樣的,把該分數值進行softmax計算,計算之後的值就是每一個encoder節點的hidden states對於當前節點的權重,把權重與原hidden states相乘並相加,得到的結果即是當前節點的hidden state。可以發現,其實Atttention的關鍵就是計算這個分值。

明白每一個節點是怎麼獲取hidden state之後,接下來就是decoder層的工作原理了,其具體過程如下:

第一個decoder的節點初始化一個向量,並計算當前節點的hidden state,把該hidden state作爲第一個節點的輸入,經過RNN節點後得到一個新的hidden state與輸出值。注意,這裏和Seq2Seq有一個很大的區別,Seq2Seq是直接把輸出值作爲當前節點的輸出,但是Attention會把該值與hidden state做一個連接,並把連接好的值作爲context,並送入一個前饋神經網絡,最終當前節點的輸出內容由該網絡決定,重複以上步驟,直到所有decoder的節點都輸出相應內容。
在這裏插入圖片描述

Attention模型並不只是盲目地將輸出的第一個單詞與輸入的第一個詞對齊。實際上,它在訓練階段學習瞭如何在該語言對中對齊單詞(示例中是法語和英語)。Attention函數的本質可以被描述爲一個查詢(query)到一系列(鍵key-值value)對的映射。
在這裏插入圖片描述
在計算attention時主要分爲三步,第一步是將query和每個key進行相似度計算得到權重,常用的相似度函數有點積,拼接,感知機等;然後第二步一般是使用一個softmax函數對這些權重進行歸一化;最後將權重和相應的鍵值value進行加權求和得到最後的attention。目前在NLP研究中,key和value常常都是同一個,即key=value。
在這裏插入圖片描述

Transrofmer模型講解

接下來我將介紹《Attention is all you need》這篇論文。這篇論文是google機器翻譯團隊在2017年6月放在arXiv上,最後發表在2017年nips上,到目前爲止google學術顯示引用量爲2203,可見也是受到了大家廣泛關注和應用。這篇論文主要亮點在於
1)不同於以往主流機器翻譯使用基於RNN的seq2seq模型框架,該論文用attention機制代替了RNN搭建了整個模型框架。
2)提出了多頭注意力(Multi-headed attention)機制方法,在編碼器和解碼器中大量的使用了多頭自注意力機制(Multi-headed self-attention)。
3)在WMT2014語料中的英德和英法任務上取得了先進結果,並且訓練速度比主流模型更快。

《Attention Is All You Need》是一篇Google提出的將Attention思想發揮到極致的論文。這篇論文中提出一個全新的模型,叫 Transformer,拋棄了以往深度學習任務裏面使用到的 CNN 和 RNN ,Bert就是基於Transformer構建的,這個模型廣泛應用於NLP領域,例如機器翻譯,問答系統,文本摘要和語音識別等等方向。關於Transrofmer模型的理解特別推薦一位國外博主文章《The Illustrated Transformer》

Transformer總體結構

和Attention模型一樣,Transformer模型中也採用了 encoer-decoder 架構。但其結構相比於Attention更加複雜,論文中encoder層由6個encoder堆疊在一起,decoder層也一樣。
在這裏插入圖片描述每一個encoder和decoder的內部簡版結構如下圖
在這裏插入圖片描述
對於encoder,包含兩層,一個self-attention層和一個前饋神經網絡,self-attention能幫助當前節點不僅僅只關注當前的詞,從而能獲取到上下文的語義。decoder也包含encoder提到的兩層網絡,但是在這兩層中間還有一層attention層,幫助當前節點獲取到當前需要關注的重點內容。

現在我們知道了模型的主要組件,接下來我們看下模型的內部細節。首先,模型需要對輸入的數據進行一個embedding操作,(也可以理解爲類似w2c的操作),enmbedding結束之後,輸入到encoder層,self-attention處理完數據後把數據送給前饋神經網絡,前饋神經網絡的計算可以並行,得到的輸出會輸入到下一個encoder。
在這裏插入圖片描述

Self-Attention

接下來我們詳細看一下self-attention,其思想和attention類似,但是self-attention是Transformer用來將其他相關單詞的“理解”轉換成我們正常理解的單詞的一種思路,我們看個例子:
The animal didn't cross the street because it was too tired
這裏的it到底代表的是animal還是street呢,對於我們來說能很簡單的判斷出來,但是對於機器來說,是很難判斷的,self-attention就能夠讓機器把it和animal聯繫起來
在這裏插入圖片描述
接下來我們看下詳細的處理過程。

1、首先,self-attention會計算出三個新的向量,在論文中,向量的維度是512維,我們把這三個向量分別稱爲Query、Key、Value,這三個向量是用embedding向量與一個矩陣相乘得到的結果,這個矩陣是隨機初始化的,維度爲(64,512)注意第二個維度需要和embedding的維度一樣,其值在BP的過程中會一直進行更新,得到的這三個向量的維度是64低於embedding維度的。

在這裏插入圖片描述
那麼Query、Key、Value這三個向量又是什麼呢?這三個向量對於attention來說很重要,當你理解了下文後,你將會明白這三個向量扮演者什麼的角色。

2、計算self-attention的分數值,該分數值決定了當我們在某個位置encode一個詞時,對輸入句子的其他部分的關注程度。這個分數值的計算方法是Query與Key做點乘,以下圖爲例,首先我們需要針對Thinking這個詞,計算出其他詞對於該詞的一個分數值,首先是針對於自己本身即q1·k1,然後是針對於第二個詞即q1·k2
在這裏插入圖片描述
3、接下來,把點成的結果除以一個常數,這裏我們除以8,這個值一般是採用上文提到的矩陣的第一個維度的開方即64的開方8,當然也可以選擇其他的值,然後把得到的結果做一個softmax的計算。得到的結果即是每個詞對於當前位置的詞的相關性大小,當然,當前位置的詞相關性肯定會會很大
在這裏插入圖片描述
4、下一步就是把Value和softmax得到的值進行相乘,並相加,得到的結果即是self-attetion在當前節點的值。
在這裏插入圖片描述
在實際的應用場景,爲了提高計算速度,我們採用的是矩陣的方式,直接計算出Query, Key, Value的矩陣,然後把embedding的值與三個矩陣直接相乘,把得到的新矩陣Q與K相乘,乘以一個常數,做softmax操作,最後乘上V矩陣
在這裏插入圖片描述
在這裏插入圖片描述
這種通過 query 和 key 的相似性程度來確定 value 的權重分佈的方法被稱爲scaled dot-product attention。其實scaled dot-Product attention就是我們常用的使用點積進行相似度計算的attention,只是多除了一個(爲K的維度)起到調節作用,使得內積不至於太大。
在這裏插入圖片描述

Multi-Headed Attention

這篇論文更厲害的地方是給self-attention加入了另外一個機制,被稱爲“multi-headed” attention,該機制理解起來很簡單,就是說不僅僅只初始化一組Q、K、V的矩陣,而是初始化多組,tranformer是使用了8組,所以最後得到的結果是8個矩陣。
在這裏插入圖片描述
在這裏插入圖片描述
這給我們留下了一個小的挑戰,前饋神經網絡沒法輸入8個矩陣呀,這該怎麼辦呢?所以我們需要一種方式,把8個矩陣降爲1個,首先,我們把8個矩陣連在一起,這樣會得到一個大的矩陣,再隨機初始化一個矩陣和這個組合好的矩陣相乘,最後得到一個最終的矩陣。
在這裏插入圖片描述
這就是multi-headed attention的全部流程了,這裏其實已經有很多矩陣了,我們把所有的矩陣放到一張圖內看一下總體的流程。
在這裏插入圖片描述
多頭attention(Multi-head attention)整個過程可以簡述爲:Query,Key,Value首先進過一個線性變換,然後輸入到放縮點積attention(注意這裏要做h次,其實也就是所謂的多頭,每一次算一個頭,而且每次Q,K,V進行線性變換的參數W是不一樣的),然後將h次的放縮點積attention結果進行拼接,再進行一次線性變換得到的值作爲多頭attention的結果。可以看到,google提出來的多頭attention的不同之處在於進行了h次計算而不僅僅算一次,論文中說到這樣的好處是可以允許模型在不同的表示子空間裏學習到相關的信息,後面還會根據attention可視化來驗證。
在這裏插入圖片描述
那麼在整個模型中,是如何使用attention的呢?如下圖,首先在編碼器到解碼器的地方使用了多頭attention進行連接,K,V,Q分別是編碼器的層輸出(這裏K=V)和解碼器中都頭attention的輸入。其實就和主流的機器翻譯模型中的attention一樣,利用解碼器和編碼器attention來進行翻譯對齊。然後在編碼器和解碼器中都使用了多頭自注意力self-attention來學習文本的表示。Self-attention即K=V=Q,例如輸入一個句子,那麼裏面的每個詞都要和該句子中的所有詞進行attention計算。目的是學習句子內部的詞依賴關係,捕獲句子的內部結構。

在這裏插入圖片描述
對於使用自注意力機制的原因,論文中提到主要從三個方面考慮(每一層的複雜度,是否可以並行,長距離依賴學習),並給出了和RNN,CNN計算複雜度的比較。可以看到,如果輸入序列n小於表示維度d的話,每一層的時間複雜度self-attention是比較有優勢的。當n比較大時,作者也給出了一種解決方案self-attention(restricted)即每個詞不是和所有詞計算attention,而是隻與限制的r個詞去計算attention。在並行方面,多頭attention和CNN一樣不依賴於前一時刻的計算,可以很好的並行,優於RNN。在長距離依賴上,由於self-attention是每個詞和所有詞都要計算attention,所以不管他們中間有多長距離,最大的路徑長度也都只是1。可以捕獲長距離依賴關係。
在這裏插入圖片描述
現在我們已經接觸了attention的header,讓我們重新審視我們之前的例子,看看例句中的“it”這個單詞在不同的attention header情況下會有怎樣不同的關注點(這裏不同顏色代表attention不同頭的結果,顏色越深attention值越大)。
在這裏插入圖片描述
當我們對“it”這個詞進行編碼時,一個注意力的焦點主要集中在“animal”上,而另一個注意力集中在“tired”(兩個heads)
但是,如果我們將所有注意力添加到圖片中,可能有點難理解:
在這裏插入圖片描述

Positional Encoding

到目前爲止,transformer模型中還缺少一種解釋輸入序列中單詞順序的方法。爲了處理這個問題,transformer給encoder層和decoder層的輸入添加了一個額外的向量Positional Encoding,維度和embedding的維度一樣,這個向量採用了一種很獨特的方法來讓模型學習到這個值,這個向量能決定當前詞的位置,或者說在一個句子中不同的詞之間的距離。這個位置向量的具體計算方法有很多種,論文中的計算方法如下
在這裏插入圖片描述
其中pos是指當前詞在句子中的位置,i是指向量中每個值的index,可以看出,在偶數位置,使用正弦編碼,在奇數位置,使用餘弦編碼。最後把這個Positional Encoding與embedding的值相加,作爲輸入送到下一層。
在這裏插入圖片描述
爲了讓模型捕捉到單詞的順序信息,我們添加位置編碼向量信息(POSITIONAL ENCODING),位置編碼向量不需要訓練,它有一個規則的產生方式(上圖公式)。

如果我們的嵌入維度爲4,那麼實際上的位置編碼就如下圖所示:
在這裏插入圖片描述
那麼生成位置向量需要遵循怎樣的規則呢?

觀察下面的圖形,每一行都代表着對一個矢量的位置編碼。因此第一行就是我們輸入序列中第一個字的嵌入向量,每行都包含512個值,每個值介於1和-1之間。我們用顏色來表示1,-1之間的值,這樣方便可視化的方式表現出來:
在這裏插入圖片描述
這是一個20個字(行)的(512)列位置編碼示例。你會發現它咋中心位置被分爲了2半,這是因爲左半部分的值是一由一個正弦函數生成的,而右半部分是由另一個函數(餘弦)生成。然後將它們連接起來形成每個位置編碼矢量。

Layer normalization

在transformer中,每一個子層(self-attetion,ffnn)之後都會接一個殘差模塊,並且有一個Layer normalization
在這裏插入圖片描述
在進一步探索其內部計算方式,我們可以將上面圖層可視化爲下圖:
在這裏插入圖片描述
殘差模塊相信大家都很清楚了,這裏不再講解,主要講解下Layer normalization。Normalization有很多種,但是它們都有一個共同的目的,那就是把輸入轉化成均值爲0方差爲1的數據。我們在把數據送入激活函數之前進行normalization(歸一化),因爲我們不希望輸入數據落在激活函數的飽和區。

說到 normalization,那就肯定得提到 Batch Normalization。BN的主要思想就是:在每一層的每一批數據上進行歸一化。我們可能會對輸入數據進行歸一化,但是經過該網絡層的作用後,我們的數據已經不再是歸一化的了。隨着這種情況的發展,數據的偏差越來越大,我的反向傳播需要考慮到這些大的偏差,這就迫使我們只能使用較小的學習率來防止梯度消失或者梯度爆炸。

BN的具體做法就是對每一小批數據,在批這個方向上做歸一化。如下圖所示:
在這裏插入圖片描述
可以看到,右半邊求均值是沿着數據 batch_size的方向進行的,其計算公式如下:
在這裏插入圖片描述
那麼什麼是 Layer normalization 呢?它也是歸一化數據的一種方式,不過 LN 是在每一個樣本上計算均值和方差,而不是BN那種在批方向計算均值和方差!在這裏插入圖片描述
下面看一下 LN 的公式:
在這裏插入圖片描述
到這裏爲止就是全部encoders的內容了,如果把兩個encoders疊加在一起就是這樣的結構,在self-attention需要強調的最後一點是其採用了殘差網絡中的short-cut結構,目的是解決深度學習中的退化問題。
在這裏插入圖片描述

Decoder層

在這裏插入圖片描述
上圖是transformer的一個詳細結構,相比本文一開始結束的結構圖會更詳細些,接下來,我們會按照這個結構圖講解下decoder部分。

可以看到decoder部分其實和encoder部分大同小異,不過在最下面額外多了一個masked mutil-head attetion,這裏的mask也是transformer一個很關鍵的技術,我們一起來看一下。

Mask

mask 表示掩碼,它對某些值進行掩蓋,使其在參數更新時不產生效果。Transformer 模型裏面涉及兩種 mask,分別是 padding mask 和 sequence mask。

其中,padding mask 在所有的 scaled dot-product attention 裏面都需要用到,而 sequence mask 只有在 decoder 的 self-attention 裏面用到。

Padding Mask

什麼是 padding mask 呢?因爲每個批次輸入序列長度是不一樣的也就是說,我們要對輸入序列進行對齊。具體來說,就是給在較短的序列後面填充 0。但是如果輸入的序列太長,則是截取左邊的內容,把多餘的直接捨棄。因爲這些填充的位置,其實是沒什麼意義的,所以我們的attention機制不應該把注意力放在這些位置上,所以我們需要進行一些處理。

具體的做法是,把這些位置的值加上一個非常大的負數(負無窮),這樣的話,經過 softmax,這些位置的概率就會接近0!

而我們的 padding mask 實際上是一個張量,每個值都是一個Boolean,值爲 false 的地方就是我們要進行處理的地方。

Sequence mask

文章前面也提到,sequence mask 是爲了使得 decoder 不能看見未來的信息。也就是對於一個序列,在 time_step 爲 t 的時刻,我們的解碼輸出應該只能依賴於 t 時刻之前的輸出,而不能依賴 t 之後的輸出。因此我們需要想一個辦法,把 t 之後的信息給隱藏起來。

那麼具體怎麼做呢?也很簡單:產生一個上三角矩陣,上三角的值全爲0。把這個矩陣作用在每一個序列上,就可以達到我們的目的。

  • 對於 decoder 的 self-attention,裏面使用到的 scaled dot-product attention,同時需要padding mask 和 sequence mask 作爲 attn_mask,具體實現就是兩個mask相加作爲attn_mask。
  • 其他情況,attn_mask 一律等於 padding mask。

編碼器通過處理輸入序列啓動。然後將頂部編碼器的輸出轉換爲一組注意向量k和v。每個解碼器將在其“encoder-decoder attention”層中使用這些注意向量,這有助於解碼器將注意力集中在輸入序列中的適當位置:
在這裏插入圖片描述
完成編碼階段後,我們開始解碼階段。解碼階段的每個步驟從輸出序列(本例中爲英語翻譯句)輸出一個元素。
以下步驟重複此過程,一直到達到表示解碼器已完成輸出的符號。每一步的輸出在下一個時間步被送入底部解碼器,解碼器像就像我們對編碼器輸入所做操作那樣,我們將位置編碼嵌入並添加到這些解碼器輸入中,以表示每個字的位置。
在這裏插入圖片描述

輸出層

當decoder層全部執行完畢後,怎麼把得到的向量映射爲我們需要的詞呢,很簡單,只需要在結尾再添加一個全連接層和softmax層,假如我們的詞典是1w個詞,那最終softmax會輸入1w個詞的概率,概率值最大的對應的詞就是我們最終的結果。
在這裏插入圖片描述

BERT原理詳解

從創新的角度來看,bert其實並沒有過多的結構方面的創新點,其和GPT一樣均是採用的transformer的結構,相對於GPT來說,其是雙向結構的,而GPT是單向的,如下圖所示
在這裏插入圖片描述
elmo:將上下文當作特徵,但是無監督的語料和我們真實的語料還是有區別的,不一定的符合我們特定的任務,是一種雙向的特徵提取。

openai gpt就做了一個改進,也是通過transformer學習出來一個語言模型,不是固定的,通過任務 finetuning,用transfomer代替elmo的lstm。
openai gpt其實就是缺少了encoder的transformer。當然也沒了encoder與decoder之間的attention。

openAI gpt雖然可以進行fine-tuning,但是有些特殊任務與pretraining輸入有出入,單個句子與兩個句子不一致的情況,很難解決,還有就是decoder只能看到前面的信息。
其次bert在多方面的nlp任務變現來看效果都較好,具備較強的泛化能力,對於特定的任務只需要添加一個輸出層來進行fine-tuning即可。

結構

先看下bert的內部結構,官網最開始提供了兩個版本,L表示的是transformer的層數,H表示輸出的維度,A表示mutil-head attention的個數
在這裏插入圖片描述
如今已經增加了多個模型,中文是其中唯一一個非英語的模型。
在這裏插入圖片描述
從模型的層數來說其實已經很大了,但是由於transformer的殘差(residual)模塊,層數並不會引起梯度消失等問題,但是並不代表層數越多效果越好,有論點認爲低層偏向於語法特徵學習,高層偏向於語義特徵學習。

預訓練模型

首先我們要了解一下什麼是預訓練模型,舉個例子,假設我們有大量的維基百科數據,那麼我們可以用這部分巨大的數據來訓練一個泛化能力很強的模型,當我們需要在特定場景使用時,例如做文本相似度計算,那麼,只需要簡單的修改一些輸出層,再用我們自己的數據進行一個增量訓練,對權重進行一個輕微的調整。

預訓練的好處在於在特定場景使用時不需要用大量的語料來進行訓練,節約時間效率高效,bert就是這樣的一個泛化能力較強的預訓練模型。

BERT的預訓練過程

接下來我們看看BERT的預訓練過程,BERT的預訓練階段包括兩個任務,一個是Masked Language Model,還有一個是Next Sentence Prediction。

Masked Language Model

MLM可以理解爲完形填空,作者會隨機mask每一個句子中15%的詞,用其上下文來做預測,例如:my dog is hairy → my dog is [MASK]

此處將hairy進行了mask處理,然後採用非監督學習的方法預測mask位置的詞是什麼,但是該方法有一個問題,因爲是mask15%的詞,其數量已經很高了,這樣就會導致某些詞在fine-tuning階段從未見過,爲了解決這個問題,作者做了如下的處理:

  • 80%的時間是採用[mask],my dog is hairy → my dog is [MASK]

  • 10%的時間是隨機取一個詞來代替mask的詞,my dog is hairy -> my dog is apple

  • 10%的時間保持不變,my dog is hairy -> my dog is hairy

那麼爲啥要以一定的概率使用隨機詞呢?這是因爲transformer要保持對每個輸入token分佈式的表徵,否則Transformer很可能會記住這個[MASK]就是"hairy"。至於使用隨機詞帶來的負面影響,文章中解釋說,所有其他的token(即非"hairy"的token)共享15%*10% = 1.5%的概率,其影響是可以忽略不計的。Transformer全局的可視,又增加了信息的獲取,但是不讓模型獲取全量信息。
注意:

  • 有參數dupe_factor決定數據duplicate的次數。
  • 其中,create_instance_from_document函數,是構造了一個sentence-pair的樣本。對每一句,先生成[CLS]+A+[SEP]+B+[SEP],有長(0.9)有短(0.1),再加上mask,然後做成樣本類object。
  • create_masked_lm_predictions函數返回的tokens是已經被遮擋詞替換之後的tokens
  • masked_lm_labels則是遮擋詞對應位置真實的label。

Next Sentence Prediction

選擇一些句子對A與B,其中50%的數據B是A的下一條句子,剩餘50%的數據B是語料庫中隨機選擇的,學習其中的相關性,添加這樣的預訓練的目的是目前很多NLP的任務比如QA和NLI都需要理解兩個句子之間的關係,從而能讓預訓練的模型更好的適應這樣的任務。
個人理解:

  • Bert先是用Mask來提高視野範圍的信息獲取量,增加duplicate再隨機Mask,這樣跟RNN類方法依次訓練預測沒什麼區別了除了mask不同位置外;

  • 全局視野極大地降低了學習的難度,然後再用A+B/C來作爲樣本,這樣每條樣本都有50%的概率看到一半左右的噪聲;

  • 但直接學習Mask A+B/C是沒法學習的,因爲不知道哪些是噪聲,所以又加上next_sentence預測任務,與MLM同時進行訓練,這樣用next來輔助模型對噪聲/非噪聲的辨識,用MLM來完成語義的大部分的學習。
    在這裏插入圖片描述

輸入

bert的輸入可以是單一的一個句子或者是句子對,實際的輸入值是segment embedding與position embedding相加,具體的操作流程可參考上面的transformer講解。

BERT的輸入詞向量是三個向量之和:

Token Embedding:WordPiece tokenization subword詞向量。
Segment Embedding:表明這個詞屬於哪個句子(NSP需要兩個句子)。
Position Embedding:學習出來的embedding向量。這與Transformer不同,Transformer中是預先設定好的值。

在這裏插入圖片描述

總結

在這裏插入圖片描述
BERT的去除實驗表明,雙向LM和NSP帶了的提升最大。

在這裏插入圖片描述
另一個結論是,增加模型參數數量可以提升模型效果。

在這裏插入圖片描述
BERT預訓練模型的輸出結果,無非就是一個或多個向量。下游任務可以通過精調(改變預訓練模型參數)或者特徵抽取(不改變預訓練模型參數,只是把預訓練模型的輸出作爲特徵輸入到下游任務)兩種方式進行使用。BERT原論文使用了精調方式,但也嘗試了特徵抽取方式的效果,比如在NER任務上,最好的特徵抽取方式只比精調差一點點。但特徵抽取方式的好處可以預先計算好所需的向量,存下來就可重複使用,極大提升下游任務模型訓練的速度。
在這裏插入圖片描述
後來也有其他人針對ELMo和BERT比較了這兩種使用方式的精度差異。下面列出基本結論:

在這裏插入圖片描述
在這裏插入圖片描述
總結下BERT的主要貢獻:

  • 引入了Masked LM,使用雙向LM做模型預訓練。
  • 爲預訓練引入了新目標NSP,它可以學習句子與句子間的關係。
  • 進一步驗證了更大的模型效果更好: 12 --> 24 層。
  • 爲下游任務引入了很通用的求解框架,不再爲任務做模型定製。
  • 刷新了多項NLP任務的記錄,引爆了NLP無監督預訓練技術。

BERT是谷歌團隊糅合目前已有的NLP知識集大成者,刷新11條賽道彰顯了無與倫比的實力,且極容易被用於多種NLP任務。宛若一束煙花點亮在所有NLP從業者心中。更爲可貴的是谷歌選擇了開源這些,讓所有從業者看到了在各行各業落地的更多可能性。

BERT優點

  • Transformer Encoder因爲有Self-attention機制,因此BERT自帶雙向功能

  • 因爲雙向功能以及多層Self-attention機制的影響,使得BERT必須使用Cloze版的語言模型Masked-LM來完成token級別的預訓練

  • 爲了獲取比詞更高級別的句子級別的語義表徵,BERT加入了Next Sentence Prediction來和Masked-LM一起做聯合訓練

  • 爲了適配多任務下的遷移學習,BERT設計了更通用的輸入層和輸出層

  • 微調成本小

BERT缺點

  • task1的隨機遮擋策略略顯粗獷,推薦閱讀《Data Nosing As Smoothing In Neural Network Language Models》

  • [MASK]標記在實際預測中不會出現,訓練時用過多[MASK]影響模型表現;

  • 每個batch只有15%的token被預測,所以BERT收斂得比left-to-right模型要慢(它們會預測每個token)

  • BERT對硬件資源的消耗巨大(大模型需要16個tpu,歷時四天;更大的模型需要64個tpu,歷時四天。

關於BERT最新的各領域應用推薦張俊林的Bert時代的創新(應用篇)

思考

  • 個人並不認爲文章是模型的改進,更認可爲任務的設計改進。

  • 論文作者只比較了有沒有task1的影響,並沒有針對task2對比試驗。提升是否來自好的預訓練任務設計沒有明說。

  • bert對nlp領域目前已有知識的有效“整合”,在硬件配置足夠的情況下能提高nlp多領域性能

BERT適用場景

第一,如果NLP任務偏向在語言本身中就包含答案,而不特別依賴文本外的其它特徵,往往應用Bert能夠極大提升應用效果。典型的任務比如QA和閱讀理解,正確答案更偏向對語言的理解程度,理解能力越強,解決得越好,不太依賴語言之外的一些判斷因素,所以效果提升就特別明顯。反過來說,對於某些任務,除了文本類特徵外,其它特徵也很關鍵,比如搜索的用戶行爲/鏈接分析/內容質量等也非常重要,所以Bert的優勢可能就不太容易發揮出來。再比如,推薦系統也是類似的道理,Bert可能只能對於文本內容編碼有幫助,其它的用戶行爲類特徵,不太容易融入Bert中。

第二,Bert特別適合解決句子或者段落的匹配類任務。就是說,Bert特別適合用來解決判斷句子關係類問題,這是相對單文本分類任務和序列標註等其它典型NLP任務來說的,很多實驗結果表明了這一點。而其中的原因,我覺得很可能主要有兩個,一個原因是:很可能是因爲Bert在預訓練階段增加了Next Sentence Prediction任務,所以能夠在預訓練階段學會一些句間關係的知識,而如果下游任務正好涉及到句間關係判斷,就特別吻合Bert本身的長處,於是效果就特別明顯。第二個可能的原因是:因爲Self Attention機制自帶句子A中單詞和句子B中任意單詞的Attention效果,而這種細粒度的匹配對於句子匹配類的任務尤其重要,所以Transformer的本質特性也決定了它特別適合解決這類任務。

從上面這個Bert的擅長處理句間關係類任務的特性,我們可以繼續推理出以下觀點:

既然預訓練階段增加了Next Sentence Prediction任務,就能對下游類似性質任務有較好促進作用,那麼是否可以繼續在預訓練階段加入其它的新的輔助任務?而這個輔助任務如果具備一定通用性,可能會對一類的下游任務效果有直接促進作用。這也是一個很有意思的探索方向,當然,這種方向因爲要動Bert的第一個預訓練階段,所以屬於NLP屆土豪們的工作範疇,窮人們還是散退、旁觀、鼓掌、叫好爲妙。

第三,Bert的適用場景,與NLP任務對深層語義特徵的需求程度有關。感覺越是需要深層語義特徵的任務,越適合利用Bert來解決;而對有些NLP任務來說,淺層的特徵即可解決問題,典型的淺層特徵性任務比如分詞,POS詞性標註,NER,文本分類等任務,這種類型的任務,只需要較短的上下文,以及淺層的非語義的特徵,貌似就可以較好地解決問題,所以Bert能夠發揮作用的餘地就不太大,有點殺雞用牛刀,有力使不出來的感覺。

這很可能是因爲Transformer層深比較深,所以可以逐層捕獲不同層級不同深度的特徵。於是,對於需要語義特徵的問題和任務,Bert這種深度捕獲各種特徵的能力越容易發揮出來,而淺層的任務,比如分詞/文本分類這種任務,也許傳統方法就能解決得比較好,因爲任務特性決定了,要解決好它,不太需要深層特徵。

第四,Bert比較適合解決輸入長度不太長的NLP任務,而輸入比較長的任務,典型的比如文檔級別的任務,Bert解決起來可能就不太好。主要原因在於:Transformer的self attention機制因爲要對任意兩個單詞做attention計算,所以時間複雜度是n平方,n是輸入的長度。如果輸入長度比較長,Transformer的訓練和推理速度掉得比較厲害,於是,這點約束了Bert的輸入長度不能太長。所以對於輸入長一些的文檔級別的任務,Bert就不容易解決好。結論是:Bert更適合解決句子級別或者段落級別的NLP任務。

如果有小夥伴堅持看到這裏的話深表感謝,本來要繼續寫源碼分析和具體的實踐了。時間關係,等下週再抽時間寫源碼分析與實踐部分吧。本文僅用於筆者自己總結自己BERT學習之路,期間引用很多專家學者的觀點思路,深表感謝。第一次駕馭這麼長的技術文章,每個知識點都想寫點,感覺越寫越亂。若有讀者在閱讀本文期間有不好的閱讀體驗深表歉意。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章