目錄html
本文翻譯自Jay Alammar的博客The Illustrated BERT, ELMo, and co. (How NLP Cracked Transfer Learning)git
2018年是機器學習模型處理文本(更準確地說,是天然語言處理或簡稱NLP)的轉折點。咱們對以何種方式捕捉潛在語義和關係的來表示單詞和句子這一問題的理解也在迅速發展。此外,NLP社區中也有人分享了許多了很是強大的模型供你免費下載並在本身的模型和pipeline中使用(它被稱爲NLP的ImageNet moment,參考了多年前計算機視覺的快速發展)。github
其中一個最新的里程碑就是BERT的發佈,這被人們看做是NLP新時代的開始。BERT是一個打破了許多基於語言的任務中的記錄。在論文發佈後不久,該模型的團隊還開源了模型的代碼,並提供了模型的下載版本,這些模型已經在大數據集上進行過了預訓練。這是一個重大的進步,由於它使任何想要構建天然語言處理的機器學習模型的人均可以將這個強大的預訓練好的模型做爲一個隨時可用的組件使用——從而節省了從頭開始訓練模型所需的時間、精力、知識和資源。網絡
使用BERT的兩個步驟。第一步:下載預訓練好的模型;第二步:在特定任務上進行微調架構
BERT也是基於最近在NLP社區中涌現的許多聰明的想法,包括 Semi-supervised Sequence Learning (by Andrew Dai 和 Quoc Le), ELMo (by Matthew Peters 和來自 AI2 and UW CSE的研究人員), ULMFiT (by fast.ai 創始人 Jeremy Howard 和大牛 Sebastian Ruder), OpenAI transformer (by OpenAI 研究員Radford, Narasimhan, Salimans, and Sutskever), 以及Transformer (Vaswani et al)等.機器學習
要正確理BERT是什麼,咱們須要瞭解許多概念。倒不如先看看BERT有哪些用途。ide
最直接的想法就是使用BERT進行單個文本的分類。這個模型看起來是這樣的:工具
要訓練這樣的模型,您須要重點訓練一個分類器,在訓練階段對BERT模型的改動很是小。這種訓練過程稱爲微調(fine-tuning),其根源在於 Semi-supervised Sequence Learning 和ULMFiT。性能
對於不熟悉這個概念的人來講,因爲咱們討論的是分類器,因此在這個任務中涉及到的是機器學習中的監督學習。這意味着咱們須要一個標記好的數據集來訓練這樣的模型。以垃圾郵件分類爲例,標記的數據集將是一個電子郵件消息列表和一個標籤(標註「垃圾郵件」或「非垃圾郵件」)。學習
相似的任務場景還有:
如今您已經有了一個BERT的用例,接下來讓咱們進一步瞭解它是如何工做的。
Google在論文中提到了兩個不一樣模型規模的BERT:
BERT基本上就是一個訓練好的Transformer編碼器棧。關於Transformer的內容能夠看看 圖解Transformer這篇博文。Transformer是BERT的基礎,下面咱們也會涉及到這個概念。
兩種規模的BERT模型都有許多編碼器層 (在論文中稱爲「Transformer塊」) – BERT Base有12個這樣的結構,BERT Large有24個。編碼器中也有前饋網絡 (BERT Base中的是768個隱層神經元,BERT Large中的是1024個隱層神經元), 以及注意力層中使用了比Transformer那篇論文中更多的「頭」 (BERT Base有12個「頭」,BERT Large中有16個)。
輸入序列的第一個token是一個特殊的符號[CLS],這裏的CLS表明class。
就像Transformer的編碼器同樣,BERT以一串單詞做爲輸入,這些單詞不斷地想編碼器棧上層流動。每一層都要通過自注意力層和前饋網絡,而後在將其交給下一個編碼器。
在體系結構方面,到目前爲止,仍是與Transformer是相同的(除了一些超參數以外)。接下來在輸出端,咱們會看到其和Transformer的不一樣之處。
每一個位置對應地輸出一個維度爲hidden_size(BERT Base中爲768)的向量。對於以前提到的句子分類的例子,咱們只關注第一個位置的輸出(也就是被咱們用[CLS]符號代替的位置)。
輸出的這個向量如今能夠用做咱們選擇的分類器的輸入。論文利用一個單層神經網絡做爲分類器,就能取得較好的分類效果。
若是你有更多的標籤(例如,若是你是一個電子郵件服務提供商,你須要將電子郵件標記爲「垃圾郵件」、「非垃圾郵件」、「社交」和「促銷」等等),你只需調整分類器這部分的網絡,使其具備更多的輸出神經元,而後經過softmax。
對於有CV背景的人來講,這種向量傳遞應該讓人想起像VGGNet這樣的網絡的卷積部分和網絡結構最後的全鏈接層之間發生的事情。
這些新的探索帶來了文本編碼方式的新轉變。到目前爲止,在絕大多數的NLP模型中,詞嵌入一直是一個主要的文本表示方法。Word2Vec、Glove等方法已普遍應用於此類任務。下面先讓咱們回顧一下如何使用它們。
爲了要讓機器學習模型可以處理單詞,咱們須要以數字的形式表示文本,以便模型在計算中使用。經過使用Word2Vec,咱們能夠用一個向量來表明單詞,而這一貫量還捕捉了必定的語義信息(如「斯德哥爾摩」和「瑞典」的關係至關於「開羅」與「埃及」的關係)以及語法信息,或基於語法的關係(例如,「had」和「has」的關係與「was」和「is」的關係是同樣的)。
人們很快意識到,使用大量文本數據進行預訓練學習詞嵌入是一個好主意,而不是在小數據集上模型從零開始訓練。你能夠下載預訓練的Word2Vec或GloVe。下面是GloVe訓練獲得的「stick」對應的向量表示(嵌入向量維度爲200)。
因爲維度很大,在後面的文章中會用下面這種形狀表明向量:
若是咱們是使用GloVe訓練好的向量做爲一個詞,好比「stick」的表示,那麼無論在什麼上下文中,這個表示都是同樣的。在一些研究中 (Peters et. al., 2017, McCann et. al., 2017, Peters et. al., 2018 in the ELMo paper ),研究人員認爲像「stick」這樣的詞其實有不少意思,具體是什麼意思取決於在什麼語境中用它。那麼爲何不基於其上下文語境來學習一個詞的嵌入表示呢?也就是即學習到這個詞的上下文的語義,有學習到其餘的語境信息。就此,語境化的詞嵌入模型應運而生。
語境化的詞嵌入模型可以基於一個單詞的上下文的意思給出單詞的向量表示[RIP Robin Williams](https://www.youtube.com/watch?v=OwwdgsN9wF8)
ELMo沒有爲每一個單詞使用固定的嵌入,而是在爲每一個單詞分配嵌入以前查看整個句子。它使用針對特定任務的雙向LSTM來建立這些嵌入。
ELMo爲在語境中進行預訓練提供了重要的思路。ELMo LSTM可以在大數據集上進行訓練,而後做爲其餘模型的一個部分處理其餘的天然語言任務。
ELMo的祕訣是什麼?
ELMo經過訓練預測單詞序列中的下一個單詞來理解語言——這項任務被稱爲語言建模。這很方便,由於咱們有的是大量的文本數據,這樣的模型能夠從這些數據中學習,而不須要額外的標籤。
ELMo進行預訓練的一個步驟:給定輸入「Let’s stick to」, 預測接下來一個詞,這就是語言模型的任務。當模型在大語料上進行預訓練,他就會學習其中的語言模式。它不太可能準確地直接地猜出這個例子中的下一個單詞。更實際一點說,在「hang」這樣的單詞以後,它將爲「out」這樣的單詞分配更高的機率(組成 「hang out」) 而不是給「camera」分配更高的機率。
咱們能夠看到每一個LSTM時間步的隱狀態從ELMo的「頭部」後面探出來。這些向量會在預訓練結束後的嵌入過程當中會派上用場。
ELMo實際上更進一步,訓練了一個雙向的LSTM——這樣它的語言模型不只能預測下一個詞,還有預測上一個詞。
[Great slides](https://www.slideshare.net/shuntaroy/a-review-of-deep-contextualized-word-representations-peters-2018) on ELMo
ELMo經過將隱藏狀態(和初始嵌入)以某種方式(拼接以後加權求和)組合在一塊兒,提出了語境化的詞嵌入。
ULM-FiT引入了一些方法來有效地利用模型在預訓練中學到的東西——不只僅是嵌入,還有語境化的嵌入表示。ULM-FiT引入了一個語言模型和一套針對各類任務有效地對語言模型進行微調的流程。
NLP終於找到了一種方法,能夠像CV那樣進行遷移學習了。
Transformer的論文和代碼的發佈,以及它在機器翻譯等任務上取得的成果,開始使一些業內人士認爲它是LSTM的替代品。Transformer比LSTM更能處理長期依賴。
Transformer的編碼器-譯碼器結構使其成爲機器翻譯的理想模型。可是你會如何使用它來進行句子分類呢?你將如何針對其餘特定任務對語言模型進行微調呢?
事實證實,咱們能夠不用一個完整的Transformer來遷移學習並進行微調。咱們能夠只用Transformer的解碼器就能夠了。解碼器是一個很好的選擇,由於它能屏蔽掉後來的詞(當進行逐詞生成翻譯時,這是一個頗有用的特性)。
The OpenAI Transformer 是由Transformer的解碼器棧組成的
這個模型堆疊了12個解碼器層。因爲在這種設計中沒有編碼器,所以這些解碼器層也不會有Transformer原文中的那種編碼器-解碼器注意力子層。可是,仍然仍是有自注意力層。
有了這種結構,咱們能夠繼續在相同的語言建模任務上進行訓練模型:使用大量(未標記的)文原本預測下一個單詞。只是,把7000本書的文本扔給模型,讓它學習!書籍很是適合這類任務,由於它容許模型學習相關聯的信息,而當您使用tweet或文章進行訓練時,您沒法得到這些信息。
The OpenAI Transformer如今已經準備好被訓練成可以預測下一個單詞了
既然OpenAI Transformer已經通過了預訓練,並且它的各個層也通過了調整,咱們就能夠開始在下游任務中使用它了。讓咱們先來看看句子分類(將郵件信息分爲「垃圾郵件」或「非垃圾郵件」):
How to use a pre-trained OpenAI transformer to do sentence clasification
OpenAI的論文列出了許多用於處理不一樣類型任務輸入的輸入變換。下圖顯示了模型的結構和執行不一樣任務時的輸入變換。
openAI Transformer爲咱們提供了一個基於Transformer的可微調的預訓練的模型。可是把LSTM換成Transformer仍是讓有些東西丟失了。ELMo的語言模型是雙向的,而openAI Transformer則只訓練一個從左到右的語言模型。那麼咱們可否創建一個既能從左到右預測又能從右到左預測(同時受上、下文的制約)的基於Transformer的模型呢?
「咱們將使用Transformer編碼器」,BERT說。
「這太瘋狂了」,有人說,「每一個人都知道雙向條件做用會讓每一個詞在多層次的語境中間接地看到本身。」
「咱們將使用掩碼」,BERT自信地說。
BERT遮罩住15%輸入序列中15%的token,而後讓模型預測這些遮罩住的位置是什麼單詞
找到合適的任務來訓練Transformer的編碼器棧是一個複雜的問題,BERT採用了早期文獻(完形填空任務)中的「帶掩碼的語言模型」概念來解決這個問題。
除了屏蔽15%的輸入,BERT還混入一些東西,以改進模型的微調方式。有時它會隨機地將一個單詞替換成另外一個單詞,並讓模型預測該位置的正確單詞。
若是你還記得OpenAI Transformer處理不一樣任務時所作的輸入變換,你會注意到一些任務須要模型處理關於兩個句子的信息(例如,一個句子是不是另外一個句子的複述;再例如假設一個維基百科條目做爲輸入,一個關於這個條目的問題做爲另外一個輸入,咱們能回答這個問題嗎?)
爲了讓BERT更好地處理多個句子之間的關係,預訓練的過程還有一個額外的任務:給定兩個句子(A和B), B多是接在A後面出現的句子嗎?
BERT預訓練的第二個任務是兩個句子的分類任務。
BERT論文展現了BERT在不一樣任務上的應用。
微調的方法並非使用BERT的惟一方法。就像ELMo同樣,你也可使用預訓練好的BERT來建立語境化的詞嵌入。而後,您能夠將這些嵌入表示餵給現有的模型——論文中也提到,在NER這類任務中,這種用法的最終效果也沒有比用微調的方法的結果差不少。
哪一種向量做爲語境化嵌入的效果最好?我認爲這取決於具體任務。論文比較了6中選擇(與微調後的96.4分模型相比):
試用BERT的最好方法是經過在谷歌Colab上託管的BERT FineTuning with Cloud TPUs notebook。若是你以前從未使用過Cloud TPU,那麼這也是嘗試它們的一個很好的開始,並且BERT代碼也能夠在TPU、CPU和GPU上工做。
下一步能夠看看 BERT代碼實現:
class BertModel
)中定義,並且和原生的Transformer encoder很是類似。create_model()
方法。你也能夠參考 BERT的PyTorch實現。AllenNLP 用這個代碼讓其餘模型也可以用BERT的嵌入表示 。