NLP 的巨人肩膀(下):從 CoVe 到 BERT

PaperWeekly發表於2018-12-17

戈多會來嗎?

在前文深度長文:NLP的巨人肩膀(上)中,我們介紹了好幾種獲取句子表徵的方法,然而值得注意的是,我們並不是只對如何獲取更好的句子表徵感興趣。

其實更有趣的是,這些方法在評估他們各自模型效能的時候所採取的方法,回過頭去進行梳理,我們發現,無論是稍早些的 InferSent,還是 2018 年提出的 Quick-thoughtsMulti-task Learning 獲取通用句子表徵的方法,他們無一例外都使用了同一種思路:將得到的句子表徵,在新的分類任務上進行訓練,而此時的模型一般都只用一個全連線層,然後接上softmax進行分類。

分類器足夠簡單,足夠淺層,相比那些在這些分類任務上設計的足夠複雜的模型來說簡直不值一提。然而令人大跌眼鏡的是,這些簡單的分類器都能夠比肩甚至超越他們各自時代的最好結果,這不能不說是個驚喜。而創造這些驚喜的背後功臣,就是遷移學習更進一步地,遷移學習的本質,就是給爬上“巨人的肩膀”提供了一架結實的梯子。 

具體的,在這些句子級別的任務中,屬於 InferSentQuick-thoughts 這些模型的“巨人肩膀”便是他們各自使用的訓練資料,遷移學習最後給他們搭了一個梯子,然而這個梯子並沒有很好上,磕磕絆絆,人類 AI 算是站在第一級梯子上,試探性的伸出了一隻腿,另一隻腿即將跨出,只可惜並不知道是否有他們苦苦等待了五年之久的戈多?

一絲曙光

2017 年,Salesforce 的 Bryan McCann 等人發表了一篇文章 Learned in Translation: Contextualized Word Vectors,論文首先用一個 Encoder-Decoder 框架在機器翻譯的訓練語料上進行預訓練,而後用訓練好的模型,只取其中的 Embedding 層和 Encoder 層,同時在一個新的任務上設計一個 task-specific 模型,再將原先預訓練好的 Embedding 層和 Encoder 層的輸出作為這個 task-specific 模型的輸入,最終在新的任務場景下進行訓練。

他們嘗試了很多不同的任務,包括文字分類,Question Answering,Natural Language Inference 和 SQuAD 等,並在這些任務中,與 GloVe 作為模型的輸入時候的效果進行比較。實驗結果表明他們提出的 Context Vectors 在不同任務中都帶來了不同程度效果的提升。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

和上文中提到的諸如 Skip-thoughts 方法有所不同的是,CoVe 更側重於如何將現有資料上預訓練得到的表徵遷移到新任務場景中,而之前的句子級任務中大多數都只把遷移過程當做一個評估他們表徵效果的手段,因此觀念上有所不同。

那麼,CoVe 似乎通過監督資料上的預訓練,取得了讓人眼前一亮的結果,是否可以進一步地,撇去監督資料的依賴,直接在無標記資料上預訓練呢?

ELMo

2018 年的早些時候,AllenNLP 的 Matthew E. Peters 等人在論文 Deep contextualized word representations(該論文同時被 ICLR 和 NAACL 接受,並且獲得了 NAACL 最佳論文獎,可見其含金量)中首次提出了 ELMo,它的全稱是 Embeddings from Language Models,從名稱上可以看出,ELMo 為了利用無標記資料,使用了語言模型,我們先來看看它是如何利用語言模型的。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

基本框架是一個雙層的 Bi-LSTM,不過在第一層和第二層之間加入了一個殘差結構(一般來說,殘差結構能讓訓練過程更穩定)。做預訓練的時候,ELMo 的訓練目標函式為:

NLP 的巨人肩膀(下):從 CoVe 到 BERT

這個式子很清晰,前後有兩個概率,第一個概率是來自於正向的由左到右的 RNN 結構,在每一個時刻上的 RNN 輸出(也就是這裡的第二層 LSTM 輸出),然後再接一個 Softmax 層將其變為概率含義,就自然得到了NLP 的巨人肩膀(下):從 CoVe 到 BERT;與此類似,第二個概率來自反向的由右到左的 RNN 結構,每一個時刻 RNN 的輸出經過 Softmax 層後也能得到一個概率大小,表示從某個詞的下文推斷該詞的概率大小。 

ELMo 的基本框架便是 2-stacked biLSTM + Residual 的結構,不過和普通 RNN 結構的不同之處在於,ELMo 借鑑了 2016 年 Google Brain 的 Rafal Jozefowicz 等人發表的 Exploring the Limits of Language Modeling,其主要改進在於輸入層和輸出層不再是 word,而是變為了一個 char-based CNN 結構,ELMo 在輸入層和輸出層考慮了使用同樣的這種結構,該結構如下圖示:

NLP 的巨人肩膀(下):從 CoVe 到 BERT

這樣做有什麼好處呢?因為輸入層和輸出層都使用了這種 CNN 結構,我們先來看看輸出層使用這種結構怎麼用,以及有什麼優勢。我們都知道,在 CBOW 中的普通 Softmax 方法中,為了計算每個詞的概率大小,使用的如下公式的計算方法:

NLP 的巨人肩膀(下):從 CoVe 到 BERT

說白了,也就是先通過向量點乘的形式計算得到 logits,然後再通過 softmax 變成概率意義,這本質上和普通分類沒什麼區別,只不過是一個較大的 |V| 分類問題

現在我們假定 char-based CNN 模型是現成已有的,對於任意一個目標詞都可以得到一個向量表示 CNN(tk) ,當前時刻的 LSTM 的輸出向量為 h,那麼便可以通過同樣的方法得到目標詞的概率大小:

NLP 的巨人肩膀(下):從 CoVe 到 BERT

在原論文中,把這種先經過 CNN 得到詞向量,然後再計算 Softmax 的方法叫做 CNN Softmax。利用 CNN 解決有三點優勢值得注意:

1. CNN 能減少普通做 Softmax 時全連線層中的必須要有的 |V|h 的引數規模,只需保持 CNN 內部的引數大小即可。一般來說,CNN 中的引數規模都要比 |V|h 的引數規模小得多;

2. CNN 可以解決 OOV (Out-of-Vocabulary)問題,這個在翻譯問題中尤其頭疼;

3. 在預測階段,CNN 對於每一個詞向量的計算可以預先做好,更能夠減輕 inference 階段的計算壓力。

補充一句,普通 Softmax 在大詞典上的計算壓力,都是因為來自於這種方法需要把一個神經網路的輸出通過全連線層對映為單個值(而每個類別需要一個對映一次。

一次 h 大小的計算規模,|V| 次對映便需要總共 |V|*h 這麼多次的對映規模),對於每個類別的對映引數都不同,而 CNN Softmax 的好處就在於能夠做到對於不同的詞,對映引數都是共享的,這個共享便體現在使用的 CNN 中的引數都是同一套,從而大大減少引數的規模。 

同樣的,對於輸入層,ELMo 也是用了一樣的 CNN 結構,只不過引數不一樣而已。和輸出層中的分析類似,輸入層中 CNN 的引入同樣可以減少引數規模。不過 Exploring the Limits of Language Modeling 文中也指出了訓練時間會略微增加,因為原來的 look-up 操作可以做到更快一些,對 OOV 問題也能夠比較好的應對,從而把詞典大小不再限定在一個固定的詞典大小上。

最終 ELMo 的主要結構便如下圖(b)所示,可見輸入層和輸出層都是一個 CNN,中間使用 Bi-LSTM 框架,至於具體細節便如上兩張圖中所示。

NLP 的巨人肩膀(下):從 CoVe 到 BERT最後,在大規模語料上訓練完成的這種 CNN-BIG-LSTM 模型,怎麼用呢?其實,如果把每一層的輸出結果拿出來,這裡大概有三層的詞向量可以利用:輸入層 CNN 的輸出,即是 LSTM 的輸入向量,第一層 LSTM 的輸出和第二層的輸出向量。

又因為 LSTM 是雙向的,因此對於任意一個詞,如果 LSTM 的層數為 L 的話,總共可獲得的向量個數為 2L+1,表示如下:

NLP 的巨人肩膀(下):從 CoVe 到 BERT

到這裡還只是把 ELMo 的向量給抽取出來了。對於每一個詞,可以根據下面的式子得到它的向量,其中 γ 是一個 scale 因子,加入這個因子主要是想將 ELMo 的向量與具體任務的向量分佈拉平到同一個分佈水平,這時候便需要這麼一個縮放因子了。

另外, sj 便是針對每一層的輸出向量,利用一個 softmax 的引數來學習不同層的權值引數,因為不同任務需要的詞語意義粒度也不一致,一般認為淺層的表徵比較傾向於句法,而高層輸出的向量比較傾向於語義資訊。因此通過一個 softmax 的結構讓任務自動去學習各層之間的權重,自然也是比較合理的做法。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

NLP 的巨人肩膀(下):從 CoVe 到 BERT

前面我們說過,無論是基於傳統統計的 N-gram 還是普通神經網路的 NNLM 結構,都會有一個很嚴重的問題,那就是計算複雜度隨著上下文視窗 N 大小的增大急劇上升。其中 N-gram 是指數上升,NNLM 是以 |d| × N 的形式增加,|d| 是詞向量的維度,雖然 NNLM 已經改觀了很多,但依然是一個斜率很大的線性增加關係。

後來 CBOW 和 Skip-gram 以及再後來的 GloVe 終於做到了計算複雜度與所選視窗大小無關,只與詞典大小和詞向量維度相關。

不過需要指出的是,這裡討論的計算複雜度只是預測單個詞的計算時間複雜度,如果是求整個輸入序列的話,還是避免不了要與序列長度相關,在這一點上和下面要分析的 RNN 在橫向的時間序列上有一個時間複雜度,其原因是一致的。

並且近些年得益於硬體持續的摩爾定律發揮威力,機器的計算能力也有長足的進步,因此在這兩方面因素的作用下,以 word2vec 為代表的方法大放光彩,引領了一波 NLP 的發展浪潮。 

然而,在今天看來,無論 word2vec 中的模型、還是 GloVe 的模型,都過於簡單,它們都受限於所使用的模型表徵能力,某種意義上都只能得到比較偏上下文共現意義上的詞向量,並且也很少考慮過詞序對於詞的意義的影響(比如 CBOW 從其名稱來看就是一個 bag-of-words,在模型的輸入中沒有詞序的概念)。

理論上,RNN 結構的計算複雜度,跟兩個方向上都有關係,一方面是縱向上,另一方面是橫向上,縱向上主要是 RNN 結構本身的時間複雜度,這個複雜度只與 RNN 結構內部的 hidden state 維度以及模型結構的複雜度,在 ELMo 中的話還跟詞典大小相關(因為最後一層還是一個詞典大小上的分類問題,以及輸入也需要維護一個詞典大小的 loop up 操作)。

在橫向上的計算複雜度,就主要是受制於輸入序列的長度,而 RNN 結構本身因為在時間序列上共享引數,其自身計算複雜度這一部分不變,因而總的 ELMo 結構計算複雜度主要有詞典大小、隱藏層輸出維度大小、模型的結構複雜度以及最後的輸入序列長度。

前三者可以認為和之前的模型保持一致,最後的輸入序列長度,也只是與其保持線性關係,雖然係數是單個 RNN 單元的計算複雜度,斜率依然很大(通常 RNN 結構的訓練都比較費時),但是在機器效能提升的情況下,這一部分至少不是阻礙詞向量技術發展的最關鍵的因素了。

因此,在新的時代下,機器效能得到更進一步提升的背景下,演算法人員都急需一種能夠揭示無論詞還是句子更深層語義的方法出現,我想 ELMo 正是順應了這種時代的需要而華麗誕生。 

ELMo 的思想足夠簡單,相比它的前輩們,可以說 ELMo 並沒有本質上的創新,連模型也基本是引用和拼接別人的工作,它的思想在很多年前就已經有人在用,並沒有特別新奇的地方。

這似乎從反面證明了真正漂亮的工作從來不是突出各自的模型有多麼絢麗,只有無其他亮點的論文,才需要依靠描摹了高清足夠喜人眼球的圖片去吸引評審人的注意力。因此從這個角度去看,似乎可以得出一個啼笑皆非的結論:論文的漂亮程度與論文圖的漂亮程度呈反比。

但同時它的效果又足夠驚豔,它的出現,在 2018 年初,這個也許在 NLP 歷史上並沒有多麼顯眼的年頭,掀起了一陣不小的波瀾,至少在 6 項 NLP 任務上橫掃當時的最好結果,包括 SQuAD,SNLI,SRL,NER,Coref 和 SST-5。 

而後來的故事以及可預見的將來裡,這或許僅僅只是一個開始,就如山洪海嘯前的一朵清秀的漣漪。

ULMFit

差不多和 ELMo 同期,另一個同樣非常驚豔的工作也被提出來,這個團隊是致力於將深度學習普及和易用的 Fast AI,而論文兩位共同作者之一的 Jeremy Howard,其實就是 Fast AI 的創始人,是 Kaggle 之前的 president 和首席科學家,並且親自參與過 Kaggle 上很多比賽,長期排在排行榜的第一。

在論文 Universal Language Model Fine-tuning for Text Classification 中,他們提出了ULMFit 結構,其實這本質上是他們提出的一個方法,而不是具體的某種結構或模型。只不過正如論文標題所言,他們主要把它應用於文字分類問題中。

和 ELMo 相同的地方在於,ULMFit 同樣使用了語言模型,並且預訓練的模型主要也是LSTM,基本思路也是預訓練完成後在具體任務上進行 finetune,但不同之處也有很多。 

首先,ULMFit 的預訓練和 finetune 過程主要分為三個階段,分別是在大規模語料集上(比如 Wikitext 103,有 103 million 個詞)先預訓練,然後再將預訓練好的模型在具體任務的資料上利用語言模型來 finetune(第一次 finetune,叫做 LM finetune),再根據具體任務設計的模型上,將預訓練好的模型作為這個任務模型的多層,再一次 finetune(第二次 finetune,如果是分類問題的話可以叫做 Classifier finetune),整個過程如下所示:

NLP 的巨人肩膀(下):從 CoVe 到 BERT

NLP 的巨人肩膀(下):從 CoVe 到 BERT

其次,所使用的模型來自於 2017 年 Salesforce 的論文 Regularizing and Optimizing LSTM Language Models,在這篇文章中,他們提出了 AWD-LSTM,正如名字中所揭示的,這個框架更多的是一種訓練方法,主要思想分為兩大塊,其中 Averaged SGD 是指先將模型訓練到一定 epoch,然後再將其後的每一輪權值進行平均後,得到最終的權值,用公式表示就是,普通的 SGD 方法權值更新過程為:

NLP 的巨人肩膀(下):從 CoVe 到 BERT其中 k 代表迭代次數,而 f 則是 loss function,這就是普通的一個 SGD 權值更新迭代式子,那麼 ASGD 則把它變成了:

NLP 的巨人肩膀(下):從 CoVe 到 BERT

其中 T 是一個閾值,而 K 是總共的迭代次數,這個式子的意思就是把迭代到第 T 次之後,對該引數在其後的第 T 輪到最後一輪之間的所有值求平均,從而得到最後模型的該引數值。而相應的,普通的 SGD 則是直接取 w = wk 作為最後模型的引數值。 

除了使用 ASGD 方法訓練模型之外,在普通 LSTM 上一個時刻和下一個時刻之間的隱藏層之間是有連線的,並且這個連線通過一個全連線的矩陣相連,而這個模型則用了 DropConnect 的方法隨機 drop 掉一些連線,從而減少了一些過擬合的風險,當然在輸入層到隱藏層之間也有正常的 dropout 操作。 

第三,微調方法設計非常精妙。作者提出了幾種微調的技巧,它們是:discriminative fine-tuning, slanted triangular learning rates 以及 gradual unfreezing,分別來看一下。 

discriminative fine-tune 的基本思想是針對不同層在訓練更新引數的時候,賦予不同的學習率。這裡的出發點是,對於 NLP 的深度學習模型來說,不同層的表徵有不同的物理含義,比如淺層偏句法資訊,高層偏語義資訊,因此對於不同層的學習率不同,自然就是比較合理的了。具體公式如下:

NLP 的巨人肩膀(下):從 CoVe 到 BERT

這裡的NLP 的巨人肩膀(下):從 CoVe 到 BERT便是不同層 l 有不同的學習率,原文也給出了具體的選擇:先指定最後一層的學習率,然後根據下式得到前面層的學習率,基本思想是讓淺層的學習率要更小一些。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

而對於 slanted triangular learning rates 來說,主要思想便是在 finetune 的第一階段,希望能夠先穩定住原來已經在大規模語料集上預訓練好的引數,所以選擇一個比較小的 finetune 學習率。而後希望能夠逐步加大學習率,使得學習過程能夠儘量快速。當訓練接近尾聲時,逐步減小學習率,這樣讓模型逐漸平穩收斂

這個思想,個人覺得大概借鑑了 2017 年穀歌提出 Transformer 時用到的 warm up 的學習率調節方法,這個方法也是在訓練的時候先將學習率逐步增大,然後再逐步減小。因此,這樣一個三段論式的學習過程,用圖表示如下:

NLP 的巨人肩膀(下):從 CoVe 到 BERT

另一個 finetune 的技巧是 gradual unfreezing,主要思想是把預訓練模型在新任務上 finetune 時,逐層解凍模型,也就是先 finetune 最後一層,然後再解凍倒數第二層,把倒數第二層和最後一層一起 finetune,然後再解凍第三層。以此類推,逐層往淺層推進,最終 finetune 整個模型或者終止到某個中間層。這樣做的目的也是為了 finetune 過程能夠更平穩。 

當然,值得提出的是,因為 ULMFiT 中包含了兩次 finetune,即在新任務上用語言模型 finetune 和在新任務上 finetune 訓練一個最終的 task-specifi-model(比如分類器)。

而論文中主要把 discriminative fine-tuning 和 slanted triangular learning rates 這兩個技巧用在了語言模型的 finetune 階段,把最後一個 gradual unfreezing 的技巧應用在最終 task-specifi-model 的 finetune 階段。 

通過上面的這些方法,ULMFiT 最終在分類任務上表現驚豔,尤其是隻需要 100 個標記資料,就能夠學習到一個表現非常 comparable 的分類器。不得不說,這個過程中預訓練的語言模型,對最終表現起到了至關重要的作用。

GPT

大規模語料集上的預訓練語言模型這把火被點燃後,整個業界都在驚呼,原來預訓練的語言模型遠不止十年前 Bengio 和五年前 Mikolov 只為得到一個詞向量的威力。

然而很快在 2018 年 6 月,不再屬於“鋼鐵俠”馬斯克的 OpenAI,發了一個大新聞,相關論文是Improving Language Understanding by Generative Pre-Training,往這把火勢正猛的烈焰上加了一劑猛料,從而將這把火推向了一個新的高潮。 

OpenAI 的猛料配方里,第一劑主料便是谷歌於 2017 年中提出的 Transformer框架Attention Is All You Need)。因此,讓我們先來弄明白 Transformer 裡面都有什麼東西。

私以為,Transformer 裡最為核心的機制是 Self-attention,正因為 Self-attention 的存在,才使得 Transformer 在做類似翻譯問題的時候,可以讓其 Encoder 不用做序列輸入,而是將整個序列一次全輸入,並且超長序列的輸入也變得可能。而具體到 Self-attention 中,可以用下圖表示

NLP 的巨人肩膀(下):從 CoVe 到 BERT

簡單說來,輸入為句子的矩陣,先分別通過三個全連線矩陣將輸入矩陣變化為三個矩陣,分別為 Q, K 和 V,然後通過 Q 和 K 計算得到一些權值,將這些權值加權求和到 V 矩陣上,便可以得到一個新的矩陣表示。

而 Self-attention 中的多頭機制便是將這樣的操作分別進行多次,讓句子的表徵充分學習到不同的側重點,最終將這些多頭學習出來的表徵 concat 到一起,然後再同一個全連線網路,便可以得到這個句子最終 Self-attention 下新的表示。

將其中的每一個頭的操作過程用公式表示如下,需要注意的是 softmax 是針對矩陣的 row 方向進行操作得到的。所以,說白了,這個公式表示的意思就是針對 V 進行加權求和,加權權值通過 Q 和 K 的點乘得到。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

不過其實 Self-attention 和普通 attention 機制在形式上幾乎完全等價。主要區別在於,對於普通的 attention 機制,輸入可能有多個,並且下式在求得NLP 的巨人肩膀(下):從 CoVe 到 BERT中的NLP 的巨人肩膀(下):從 CoVe 到 BERT實際上是一個全連線網路,將式子右邊的部分(也就是 attention 的輸入)對映為一個值,從而可以根據這個值計算 attention 的權值大小。

除此之外,普通 attention 和 self-attention 並沒有本質不同,最大的區別還是在於在自我輸入上計算 attention 權值大小。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

在 Transformer 的 Encoder 中,還有一些其他設計,比如加入 position embedding(因為 Transformer 的 Encoder 中不是時序輸入詞序列,因此 position embedding 也是主要位置資訊);Residual 結構,使得模型的訓練過程更為平穩;此外還有 normalization 層,接著便是 feed forward 層(本質上是一個兩層的全連線網路,中間加一個 ReLu 的啟用函式)。

Decoder 的結構與此類似,只不過在進行 decode 的時候,會將 Encoder 這邊的輸出作為 Decoder 中 Self-attention 時的 K 和 V。 

NLP 的巨人肩膀(下):從 CoVe 到 BERT

對於 decode 過程,具體來看,大致過程如下。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

Decoder 實際上還有很多細節,一般來說,訓練時 Decoder 中的輸入可以用矩陣形式一次完成當前整個序列的 decode 過程,因為 ground truth 已經提前知道,只需做好每個詞的 mask 就好(為了避免待預測的詞影響到當前的輸入)。

然而在做 inference 的時候,Decoder 必須按照序列輸入,因為在生成每一個詞的時候,必須先生成它的前一個詞,無法一次將整個序列全部生成(當然理論上也可以,但是效果並不好)。

在矩陣運算過程中,Decoder 中有許多 mask 操作,參與運算的三個矩陣 Q,K 和 V 都要做許多 mask 操作,主要有兩方面作用:一方面是消除輸入句子本身長度之外的 padding 影響,另一方面是 decode r 必須要求不能提前看到待生成的詞。

除了 mask 操作,另外值得注意的是,和 Encoder 中只有一種型別 Self-attention 不同的是,Decoder 的 attention 實際包含兩部分:

第一部分是帶有 mask 的 Self-attention,通過 mask 將 decode 階段的 attention 限定只會 attention 到已經生成過的詞上,因此叫做 Mask Self-attention。

第二部分是普通的 Self-attention 操作,不過這時的 K 和 V 矩陣已經替換為 Encoder 的輸出結果,所以本質上並非一個 Self-attention。 NLP 的巨人肩膀(下):從 CoVe 到 BERT

下面的動圖很好地表現了 decoding 過程,生成每一個詞的時候,既和 Encoder 的輸出資訊有關,也和已經生成過的詞相關。

大體介紹完 Transformer 後,再看看 GPT 中是怎麼用 Transformer 的。按照論文的說法,GPT 使用的 Transformer 是隻用了 Decoder,因為對於語言模型來講,確實不需要 Encoder 的存在。

而具體模型,他們參考了 2018 年早些時候谷歌的 Generating Wikipedia by Summarizing Long Sequences,GPT 名稱中的 Generative 便是源自這篇文章,二者都有用到生成式方法來訓練模型,也就是生成式 Decoder。

關於這篇論文中提到的 T-DMCA,實際上就是一個 Decoder,只不過這篇文章中要做超長的序列輸入(可以長達 11000 個詞),為了能夠高效節省時間和記憶體的處理如此長的序列,做了一些 Memory-Compressed 工作,主要是兩方面:

一方面是把一個 batch 內部的序列按長度進行分組,然後分別在每個組內部進行 self-attention 操作,避免將一些很短的句子也 padding 到整個語料的最大長度;另一方面,通過 CNN 操作,把 K 和 V 壓縮到序列長度更小的一個矩陣,同時保持 Q 不變,這樣也能在相當程度上減少計算量。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

除了這些具體的模型細節外,GPT 本質上就是用了語言模型目標函式來優化和訓練 Transformer-Decoder,這和上文提到過的語言模型保持一致。

利用語言模型目標函式預訓練完成後,便可以在具體任務上進行 finetune,和 ULMFiT 中的 finetune 分為兩個階段的方法不一樣的是,GPT 直接把這兩個過程糅合到一個目標函式中,如:

NLP 的巨人肩膀(下):從 CoVe 到 BERT

其中 L2 是 task-specific 的目標函式, L1 則是語言模型目標函式。論文中說這種聯合學習方式能夠讓訓練效果更好。而在具體如何做遷移學習的方面,GPT 大概也同樣借鑑了上面提到的Generating Wikipedia by Summarizing Long Sequences 中的做法,非常巧妙地將整個遷移學習的框架做到非常精簡和通用。

分類問題中,直接在原序列的開始和末尾新增表示開始和末尾的符號,在 Text Entailment 問題中,將 Premise 和 Hypothesis 通過一箇中間分隔符“$”連線起來成為一個序列,然後同樣在開頭和末尾新增標記符號。

文字相似問題中,因為序列 1 和序列 2 沒有先後關係,因此將先後關係相反的兩個序列作為輸入。在 Question Aswering 中,將 query 和每個候選的 answer 都分別連線成一個序列作為輸入,最後按各自的打分進行排序。

因此,這套輸入的表示方法,基本可以使用同一個輸入框架來表徵許多文字問題(以至於後來的BERT 直接借用了這套做法)。

除此之外,在輸出層,只需要接入一個很簡單的全連線層或 MLP,根本不需要非常複雜的模型設計。而整個 finetune 階段,新加入的引數極少,只有輸出層以及輸入層中新增的一些特殊標記(比如分隔符)。 

正是因為有了輸入層和輸出層的這種通用化設計考慮,一旦中間的 Transformer(當然,正如前文所說,這裡的 Transformer 在使用語言模型進行預訓練時只有 Decoder 部分。在將其當做文字特徵提取器的時候,相應的也可以很便利的將其變成 Encoder)表徵能力足夠強大,遷移學習在 NLP 任務中的威力也會變得更為強大。 

果不其然,GPT 在公佈的結果中,一舉重新整理了 12 項 NLP 任務中的 9 項榜單,效果不可謂不驚豔。然而對於 OpenAI 來講,GPT 底層模型使用的是谷歌提出的 Tranformer,正是依靠了 Transformer 的強大表徵能力,使得最終的效果有了一個堅實的基礎。

然而僅僅過了四個月之後的 BERT 橫空出世,同樣也是用了 Transformer,同樣是谷歌,甚至很多思想也是直接借鑑 GPT。GPT 作為與 BERT 氣質最為接近的工作,同時也是 BERT 的前輩,得到的待遇差別如此之大,不知道 GPT 是否有些可惜和遺憾。

相比 BERT,GPT 並沒有帶來特別巨大的反響,他的驚豔亮相,迅速變為水裡的一聲悶響,掀起了一陣漣漪後迅速消散,將整個舞臺讓位於正值青春光豔照人的 BERT,頗有點“成也蕭何敗也蕭何”的味道。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

BERT

如果要用一句時下正流行的話來形容 BERT 的出現,這句話大概再恰當不過: 一切過往, 皆為序章。

2018 年 10 月 11 日,這似乎是個絕對平凡的日子。說句題外話,無獨有偶,OpenAI 在其部落格放出 GPT 的時間恰好不多不少是 4 個整月前,即 2018 年 6 月 11 日),然而 Google AI 的 Jacob Devlin 和他的合作者們悄悄地在 arXiv 上放上了他們的最新文章,名為 BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding

隨後先是在 Twitter 上引發了一波浪潮,同樣是 Google AI 團隊的 Thang Luong 在其 Twitter 上直言這項工作是一個劃時代的工作。然後在中文社群也迅速發酵,那幾天 NLP 從(搬)業(磚)人員的朋友圈基本被 BERT 刷屏,即便時至一個多月後的今日,這一現象也基本不見消退的跡象,幾乎大家見面閒聊開口必談 BERT,似乎做 NLP 的大可不必使用華夏光輝燦爛文明之一的“吃過了嗎?”,而替換為今日的“BERT 跑起來了嗎?”,可見一斑。 

不得不說,Jacob 和他的小夥伴們真是大手筆,和 4 個月前他的前輩 GPT 至少一樣,乃至野心更大。除了 GPT 刷過的那些榜之外,BERT 還新增了一項任務 SQuAD(這大概是當前 NLP 最為火熱的任務之一),也許這裡的每一項任務都可能是一個研究小組的活命本錢,甚至還有可能是某個評教授職稱的救命稻草。

然而,BERT 絲毫不放在眼裡,直接將它的鐵蹄踏入 11 項 NLP 任務,將途中目見耳聞所遭遇的一切蕩平無餘,留給在這之前仍在苦苦掙扎於某個排行榜中的人們無盡的錯愕和唏噓,而 BERT 似乎不曾心軟和遲疑片刻。 

很快,大概過了不到一個月,Google AI 把他們已經預訓練好的 BERT 模型公佈出來,包括英語的 base 和 large 模型,另外針對其他語言也放出了中文(僅有的一個非英語的單個模型)和一個 10 2種語言的混合語言預訓練模型,這再次觸發和引爆了 NLP 界的集體高潮。

不過,為何 BERT 能如此引人注目,不妨來一探究竟。私以為,BERT 最主要的幾個特徵分別是:

  • 利用了真雙向的 Transformer;

  • 為了利用雙向資訊,改進了普通語言模型成為完形填空式的 Mask-LM (Mask-Language Model);

  • 利用 Next Sentence Prediction 任務學習句子級別資訊;

  • 進一步完善和擴充套件了 GPT 中設計的通用任務框架,使得 BERT 能夠支援包括:句子對分類任務、單句子分類任務、閱讀理解任務和序列標註任務。

為了更深入理解 BERT,我們分別來看看他的這些特徵。

首先看看 BERT 如何使用雙向 Transformer。其實很好理解,用一句話來回答為什麼 BERT 使用雙向 Transformer:BERT 用了 Transformer 的 Encoder 框架。

但是,我想這樣的答案自然是要扣分的,更“求生欲”一點的答案是:因為 Encoder 中用了 Self-attention 機制,而這個機制會將每一個詞在整個輸入序列中進行加權求和得到新的表徵。

更通俗的說法是每一個詞在經過 Self-attention之後,其新的表徵將會是整個輸入序列中所有詞(當然也包括它本身)的加權求和,每一個詞都達到了“我中有你,你中有我”的境界。

如果經過更多的 transformer 的 block(意味著經過更多 Self-attention),那麼互相交融的程度將會更高,類似於 BERT 這樣深的結構(Base 模型是 12層,Large 模型是 24層),經過所有 block 後,彼時,遑論“我中有你,你中有我”了,一切早已是“我在哪裡”和“我是誰”這樣的哲學式命題了,也就是連“你我”的界限都早已混沌的狀態了。

因此,說 BERT 是雙向的語言模型結構,不僅絲毫不過分,它實質上是一種將序列中每一個詞之間無比交融在一塊的模型。更可貴的是,交融的姿勢還可以多種多樣,這可從 Large 版本 BERT 的多頭機制中 Head 個數多達 16 個,體會出這種交融的程度,可謂絲絲入扣。

這裡僅給出一例,下圖只是兩個 head 學習到的交融模式,如果多達 16 個 head,這樣的交融模式還要重複 16次,可見一斑。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

而相應的在 ELMo 與 GPT 中,它們並沒有用上這種交融模式,也就是它們本質上還是一個單向的模型,ELMo 稍微好一點,將兩個單向模型的資訊 concat起 來。GPT 則只用了單向模型,這是因為它沒有用上 Transformer Encoder、只用了 Decdoer 的天生基因決定的。

其實,很多人就把這種 left-to-right 的 Transformer 框架叫做 Decoder,因為事實上 Decoder 就是如此(具體做的時候需要提前把未來待生成的詞做好 mask,細節上通過上三角矩陣來實現),這也是 OpenAI 把他們的模型叫做"Generative"的原因所在。 

然而,使用雙向 Transformer 會有一個問題。正如上面的分析,即便對於 Base 版 BERT 來說,經過 12 個 block,每個 block 內部都有 12 個多頭注意力機制,到最後一層的輸出,序列中每個位置上對應的詞向量資訊,早已融合了輸入序列中所有詞的資訊。

而普通的語言模型中,是通過某個詞的上下文語境預測當前詞的概率。如果直接把這個套用到 Transformer 的 Encoder 中,會發現待預測的輸出和序列輸入已經糅合在一塊了,說白了就是 Encoder 的輸入已經包含了正確的監督資訊了,相當於給模型洩題了,如此說來普通語言模型目標函式無法直接套用。

那麼,如何解決 Self-attention 中帶來了表徵效能卓越的雙向機制,卻又同時帶來了資訊洩露的這一問題? 

BERT 的作者很快聯想到了,如果我把原來要預測整個句子的輸出,改為只預測這個句子中的某個詞,並且把輸入中這個詞所在位置挖空,這樣一來,豈不就不會存在洩露真題的問題了?

Jacob 是這樣想的(實際上是參考了很早之前提出的 Cloze 問題),這位以單人徒手搭建大工程著稱的牛人行動力超強,立馬就把這一方法吸收和實現到 BERT 中,他們的想法也特別簡單:

  • 輸入序列依然和普通Transformer保持一致,只不過把挖掉的一個詞用"[MASK]"替換;

  • Transformer 的 Encoder 部分按正常進行;

  • 輸出層在被挖掉的詞位置,接一個分類層做詞典大小上的分類問題,得到被 mask 掉的詞概率大小。

正是因為加了 mask,因此 BERT 才把這種方法叫做 Masked-LM,整個過程如下所示:

NLP 的巨人肩膀(下):從 CoVe 到 BERT

這就直接把普通語言模型中的生成問題(正如 GPT 中把它當做一個生成問題一樣,雖然其本質上也是一個序列生成問題),變為一個簡單的分類問題,並且也直接解決了 Encoder 中多層 Self-attention 的雙向機制帶來的洩密問題(單層 Self-attention 是真雙向,但不會帶來洩密問題,只有多層累加的 Self-attention 才會帶來洩密問題),使得語言模型中的真雙向機制變為現實。

不過,BERT 針對如何做“[MASK]”,做了一些更深入的研究,它做了如下處理:

  • 選取語料中所有詞的 15% 進行隨機 mask;

  • 選中的詞在 80% 的概率下被真實 mask;

  • 選中的詞在 10% 的概率下不做 mask,而被隨機替換成其他一個詞;

  • 選中的詞在 10% 的概率下不做 mask,仍然保留原來真實的詞。

至於為什麼要這麼做,BERT 也給出了足夠感性的解釋。對於要做 mask,這個原因上面已經分析了,就是為了解決雙向機制的洩密問題而設計的;而為什麼還要有一部分概率不做真正的 mask,而是輸入一個實際的詞,這樣做的好處是儘量讓訓練和 finetune 時輸入保持一致,因為 finetune 輸入中是沒有“[MASK]”標記的。

對於保留為原來的真實詞,也就是真的有 10% 的情況下是洩密的(佔所有詞的比例為 15% * 10% = 1.5%),作者說這樣能夠給模型一定的 bias,相當於是額外的獎勵,將模型對於詞的表徵能拉向詞的真實表徵。

筆者個人理解是:因為輸入層是待預測詞的真實 embedding,在輸出層中的該詞位置得到的 embedding,是經過層層 Self-attention 後得到的,這部分 embedding 裡肯定多少保留有部分輸入 embedding 的資訊,而這部分的多少就是通過輸入一定比例真實詞所帶來的額外獎勵,最終會使得模型的輸出向量朝輸入層的真實 embedding 有一個偏移。如果全用 mask 的話,模型只需保證輸出層的分類準確,對於輸出層的向量表徵並不關心,因此可能會導致最終的向量輸出效果並不好。

最後,BERT 對選中的詞在 10% 概率下不做 mask,而是被隨機替換成為一個其他詞,這樣做的目的,BERT 也給出了他們的解釋:因為模型不知道哪些詞是被 mask 的,哪些詞是 mask 了之後又被替換成了一個其他的詞,這會迫使模型儘量在每一個詞上都學習到一個全域性語境下的表徵,因而也能讓 BERT 獲得更好的語境相關的詞向量(這正是解決一詞多義的最重要特性)。

其實更感性的解釋是,因為模型不知道哪裡有坑,所以隨時都要提心吊膽,保持高度的警惕。正如一馬平川的大西北高速公路,通常認為都是路線直,路面狀況好,但如果掉以輕心,一旦有了突發情況,往往也最容易出事故,魯棒性不高;而反倒是山間小路,明確告訴了每一位司機路面隨時都有坑,並且沒法老遠就提前知道,所以即便老司機也只能小心翼翼的把穩方向盤慢慢開,這樣做反倒魯棒性更高。

正所謂《左傳》中所言“居安思危,思則有備,有備無患,敢以此規”,BERT 的這種設計又何嘗不是“居安思危”的典範,Jacob 給出的原文解釋如下:

The Transformer encoder does not know which words it will be asked to predict or which have been replaced by random words, so it is forced to keep a distributional contextual representation of every input token.

然而可惜的是,Jacob 並沒有在論文中做更細緻的實驗,來證明這些言論的正確性,因此可能會存在其他的更優的比例組合。 

除了用上 Mask-LM 的方法使得雙向 Transformer 下的語言模型成為現實,BERT 還利用和借鑑了 Skip-thoughts 方法中的句子預測問題,來學習句子級別的語義關係。具體做法則是將兩個句子組合成一個序列,組合方式會按照下面將要介紹的方式,然後讓模型預測這兩個句子是否為先後近鄰的兩個句子,也就是會把"Next Sentence Prediction"問題建模成為一個二分類問題

訓練的時候,資料中有 50% 的情況這兩個句子是先後關係,而另外 50% 的情況下,這兩個句子是隨機從語料中湊到一起的,也就是不具備先後關係,以此來構造訓練資料。句子級別的預測思路和之前介紹的 Skip-thoughts 基本一致,當然更本質的思想來源還是來自於 word2vec 中的 skip-gram 模型。 

在預訓練階段,因為有兩個任務需要訓練:Mask-LM 和 Next Sentence Prediction,因此 BERT 的預訓練過程實質上是一個 Multi-task Learning。

BERT損失函式由兩部分組成,第一部分是來自 Mask-LM 的單詞級別分類任務,另一部分是句子級別的分類任務。通過這兩個任務的聯合學習,可以使得 BERT 學習到的表徵既有 token 級別資訊,同時也包含了句子級別的語義資訊。具體損失函式如下:

NLP 的巨人肩膀(下):從 CoVe 到 BERT

其中 θ 是 BERT 中 Encoder 部分的引數,θ1 是 Mask-LM 任務中在 Encoder 上所接的輸出層中的引數,θ2 則是句子預測任務中在 Encoder 接上的分類器引數。因此,在第一部分的損失函式中,如果被 mask 的詞集合為 M,因為它是一個詞典大小 |V| 上的多分類問題,那麼具體說來有:

NLP 的巨人肩膀(下):從 CoVe 到 BERT

在句子預測任務中,也是一個分類問題損失函式

NLP 的巨人肩膀(下):從 CoVe 到 BERT

因此,兩個任務聯合學習的損失函式是:

NLP 的巨人肩膀(下):從 CoVe 到 BERT

具體的預訓練工程實現細節方面,BERT 還利用了一系列策略,使得模型更易於訓練,比如對於學習率的 warm-up 策略(和上文提到的 ULMFiT 以及 Transformer 中用到的技巧類似),使用的啟用函式不再是普通的 ReLu,而是 GeLu,也是用了 dropout 等常見的訓練技巧。

此外,BERT 使用的語料比 GPT 也要大得多(GPT 用的是 BooksCorpus,800 million 個詞,BERT 除了 BooksCorpus 之外,還加入了 Wikipedia 資料集,2500 million 個詞,總共有 3.3 billion 個詞)。 

最後,當 BERT 預訓練完成後,針對如何利用預訓練好的模型,遷移到特定任務背景下,BERT 在 GPT 的基礎上,將這個非常重要的工作進行了更深入的設計。由於中間的 Encoder 對於所有任務幾乎都可以直接利用,因此這部分的設計主要分為兩方面:輸入層和輸出層。

在輸入層方面,思路和 GPT 基本類似,如果輸入只有一個句子,則直接在句子前後新增句子的起始標記位和句子的結束符號。在 BERT 中,起始標記都用“[CLS]”來表示,結束標記符用"[SEP]"表示,對於兩個句子的輸入情況,除了起始標記和結束標記之外,兩個句子間通過"[SEP]"來進行區分。

除了這些之外,BERT 還用兩個表示當前是句子 A 或句子 B 的向量來進行表示。對於句子 A 來說,每一詞都會新增一個同樣的表示當前句子為句子 A 的向量,如果有句子 B 的話,句子 B 中的每個詞也會新增一個表示當前句子為句子 B 的向量。

當然,和 Transformer 中一樣,為了引入序列中詞的位置資訊,也用了 position embedding,因此,最終的輸入層大概是:

NLP 的巨人肩膀(下):從 CoVe 到 BERT

除了輸入層要儘量做到通用之外,根據不同任務設計不同的輸出層也變得尤為重要,BERT 主要針對四類任務考慮和設計了一些非常易於移植的輸出層,這四類任務分別是:單個序列文字分類任務、兩個序列文字分類任務、閱讀理解任務和序列標註任務,句子或答案選擇任務。 

對於單序列文字分類任務和序列對的文字分類任務使用框架基本一致,只要輸入層按照上面的方法做好表示即可。

這兩個分類任務都是利用 Encoder 最後一層的第一個時刻“[CLS]”對應的輸出作為分類器的輸入,這個時刻可以得到輸入序列的全域性表徵,並且因為監督訊號從這個位置反饋給模型,因而實際上在 finetune 階段也可以使得這一表徵儘量傾向於全域性的表徵。

當然,即便如此,同樣也是可以通過一些簡單易行的辦法將其他時刻甚至其他層內的表徵拿來用的,個人認為並不需要侷限在這一個時刻上的表徵。另外, finetune 階段,在 BERT Encoder 基礎上,這些分類任務因為只需要接一個全連線層,因此增加的引數只有 H × K ,其中 H 是 Encoder 輸出層中隱狀態的維度,K 是分類類別個數。

對於 SQuAD 1.1 任務來說,需要在給定段落中找到正確答案所在區間,這段區間通過一個起始符與終止符來進行標記,因此只需預測輸入序列中哪個 token 所在位置是起始符或終止符即可。

這個過程只需維護兩個向量,分別是起始符和終止符的向量,不過與其說是向量,還不如說是兩個對應的全連線層,只不過這兩個全連線有些特殊,在這兩個全連線層中,輸入層的節點個數為 BERT Encoder 輸出的節點個數,也就是 H,而這兩個全連線層的輸出層都只有一個節點。

這兩個全連線層的作用就是要把序列輸出的向量通過這兩個全連線層對映為一個實數值,可以等同於打分,然後再根據這個打分在序列的方向上選取最大值作為起始符和終止符。因此這個過程實際上只增加了 H × 2 的引數量,可謂比分類任務的引數增加還要少(至少和 2 分類任務的引數一致)。

這裡有兩個需要注意的地方:其一,無論對於起始符和終止符也好,它們的判斷和普通分類任務不一樣,不是直接針對每個 token 進行是否是起始符的分類(如果是這樣的話,引數量要增加 H × 4),而是類似於在全體輸入序列上進行排序,因此針對起始符的判斷來講只需要一個 H × 1 的全連線就可以了。

第二個需要注意的是,訓練時無須擔心終止符是否在起始符後面,因為即便模型訓練時預測是終止符在起始符前面,這一錯誤現象會通過損失函式反饋給模型,然而在做 inference 時,就必須保證終止符一定要在起始符後面。 

BERT 也考慮瞭如何在序列標註任務上進行 finetune,對於序列中的每個 token 而言,實際上就是一個分類任務。和前面提到的普通分類任務不一樣的是,這裡的分類需要針對序列中的每個詞做分類,引數增加在 H × K ,這裡的 K 是序列標註中標註的種類個數。

最後對於 SWAG 任務來講,因為需要在給定一個句子後,從四個候選句子中選擇一個最有可能是該句子的下一個句子,這裡面通常包含了一些常識推理。

BERT 的做法是將前置句子和四個候選句子分別進行組合成一個句子對,並按照之前討論過的方法輸入到 Encoder,這樣便得到四個 pair 的句子表徵,然後只需要維護一個向量(或者說是一個多到 1 的全連線層),便可以給每一個候選句子進行打分,從而得到四個候選句子中最有可能是下一個的句子。

這個全連線只需要增加 H × 1 的引數量,這一點和 SQuAD 1.1 任務的情況類似。此外,這種做法和 GPT 對於 Question Selection 的做法也比較類似,理論上應該也可以應用到 WikiQA 這種資料集上的,finetune 階段同樣只需要增加 H × 1 的引數量。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

最後,我們再次總結 BERT 的幾個主要特點: 

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

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

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

  • 為了適配多工下的遷移學習BERT 設計了更通用的輸入層和輸出層。

然後,我們再來看看 BERT 都站在了哪些“巨人肩膀”上: 

  • 針對第一點,雙向功能是 Transformer Encoder 自帶的,因此這個“巨人肩膀”是 Transformer;

  • 第二點中 Masked-LM 的巨人肩膀是語言模型、CBOW 以及 Cloze問題;

  • 第三點中 Next Sentence Prediction 的“巨人肩膀”是 Skip-gram、Skip-thoughts 和 Quick-thoughts 等工作;

  • 第四點中,對輸入層和輸出層的改造,借鑑了 T-DMCA 以及 GPT 的做法。

再來看看預訓練使用的資料方面,ELMo 使用的是 1B Word Benchmark 資料集,詞數為 10 億,然而句子間的順序是隨機打亂的,無法建模句子級別的依賴關係,所以 GPT 和 BERT 都棄用這個資料集;GPT 使用的是 BooksCorpus,詞數為 8 億;BERT 除了使用 BooksCorpus 之外,還加入了 25 億級別的 Wikipedia 資料集,因此整體使用的資料集為 33 億個詞。可見 BERT 使用的資料集是最大的。 

模型方面,ELMo 使用的是一個 Bi-LSTM 結構,輸入層和輸出層使用 CNN 來進行建模。而 GPT 使用了 12 個 block,隱藏層節點數為 768,多頭為 12,這也是 BERT 的 base 版本(base 版本就是為了跟 GPT 做對比),引數量按照 BERT base 版本估計為 110M。而 BERT 再次把模型變得更大了,使用 24 個 block,並且隱藏層節點數與多頭都相應擴大,作者似乎想要表明他們設計的模型是有史以來最大的模型之一。 

在計算資源比拼方面,GPT 在 8 塊 GPU 上預訓練了一個月。引數規模和 GPT 差不多的 BERT base 版本,在 16 塊 TPU 上只花了 4 天,BERT 在論文中提到此處時,還不失時機給它們家的 TPU 放了個連結,打了一波廣告。對於引數規模為 GPT 三倍之多的 BERT large 版本,在 64 塊 TPU 上訓練耗時也依然達到 4 天之久。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

因此,總結來看,BERT 的出現既踩在了一眾前輩們的“巨人肩膀”上,通過精心設計和一些訓練技巧造出了一個龐大模型,最後又捨得花錢砸下巨大資源,給眾人上演了一場炫目無比的煙花秀。與其說 BERT 帶來了一次翻天覆地的革新,不如說 BERT 團隊糅合能力超群,以及還有一點也非常重要:有錢。

然而,和普通的煙花秀不一樣,普通煙花易逝,而 BERT 說不定可真是將一朵煙花怒放在每一位 NLP 演算法人員心中,它先是讓我們看了一場橫掃 11 項任務的表演秀(也是實力秀),宣告世人這場煙花不一般。

更為可貴的是,當眾人驚魂未定之時,很快,BERT 又把他們排練煙花秀的一切裝置(模型)全都公之於眾,甚至把排練得到的效果最好的佈景(預訓練引數)和點火器(finetune 超引數)也全都奉獻了出來,似乎願景也足夠偉大:讓每個人都可以上演一場自己的煙花秀,將預訓練好的模型在複雜任務上簡單進行一次 finetune,便立馬能夠得到一個非常高的 baseline。

沉寂了五年之久的 NLP 界,似乎迎來了一個新的時代。

如何爬上梯子的第二級

預訓練語言模型的優勢在於:

  • 近乎無限量的優質資料

  • 無需人工標註

  • 一次學習多次複用

  • 學習到的表徵可在多個任務中進行快速遷移

問題是如何利用這些預訓練好的模型,Google 們已經給我們提供巨人的肩膀了,那“梯子”在哪呢?我們怎麼爬上去這些“巨人肩膀”?也就是如何使用這些預訓練好的模型。一般來說,可以有三種方式來使用。它們分別是:

1. 將預訓練模型當做一個特徵提取器,直接將預訓練模型的輸出層去掉,然後使用去掉輸出層之後的最後一層輸出作為特徵,輸入到我們自己精心設計好的 Task-specific 模型中去。在訓練過程中,作為特徵提取器的部分(比如 BERT Encoder)的引數是不變的。

另外,特徵提取器並不一定只能用最後一層的輸出向量,也可以使用中間的某些層,甚至也可以借鑑 ELMo 的做法,在各層之間利用一個 softmax 來學習各自的權值,或者也可以嘗試一些更為複雜的各層組合方式。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

2. 將預訓練模型整體接入 Task-specific 模型,繼而重新在新的資料集上整體重新訓練。當然訓練技巧可以有很多種,比如 ULMFiT 的三角學習率和逐層解凍或者是 Transformer 的 warmup 策略(上文都有提到),這些訓練技巧非常重要,需要好好把控,否則很容易學崩了,甚至讓原有預訓練語言模型的優勢都被新的 finetune 抹去了,因此需要實驗設計一個比較好的 finetune 策略。

此外,和特徵提取器的接入方式類似,預訓練模型的接入不一定只能接最後層的輸出,可以嘗試更復雜的接入方式,比如 DenseNet;

NLP 的巨人肩膀(下):從 CoVe 到 BERT

3. 和上面兩種極端情況相反,或者說綜合了上面兩種方式的方案,即保留預訓練模型的一部分,另外一部分則和 Task-specific 模型一起 finetune。

在某些情況下,這可能是個比較合理的選擇,比如預訓練模型比較深(NLP 模型通常來講比 CV 模型都要淺很多),以及訓練資料不算太多的情況,這個時候一方面要保證預訓練模型在大規模語料上曾經學習到的表徵,另一方面因為又要做新資料下的遷移,但是資料量比較少,重新 finetune 整個模型可能不太合適,容易導致模型的魯棒性不高,那麼似乎選擇最後的一些層進行選擇性的 finetune 會是比較好的方案。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

上面這三種方案,既包括了 BERT 所言的 feature-based 使用方法, 也包括了 BERT 的 finetune 方法。另外 GPT 和 BERT 也給我們提供了很好的輸入層與輸出層通用包,再加上一些訓練技巧,一般的 NLP 任務應該是足夠應付了。

然而,GPT 和 BERT 即便重新整理了十多項NLP任務,但似乎各自默契的都不曾嘗試過在生成問題中大展身手。比如二者的主要征戰沙場 GLUE 連名字中都直指這個“膠水”排行榜主要就是語言理解問題。

除了 GLUE 外,GPT 額外刷過的資料集有 4 個:SNLI, SciTail, RACE 和 Stroy Cloze,其中 SNLI 和 SciTail 都是自然語言推斷問題,本質上是一個句子級別的分類問題,RACE 和 Stroy Cloze是 QA 或閱讀理解問題,本質上可以認為分別是一個 token 級別的分類以及句子級別的打分排序問題。

BERT 額外刷過的三個資料集中 SQuAD 本質上是一個 token 級別的分類問題,NER 也是如此,SWAG 則是從候選句子中選擇正確的推斷,是一個句子級別的打分排序問題。

GPT 刷了 12 項資料集(打破其中 9 項紀錄),BERT 刷了 11 項資料集(打破其中 11 項),然而無一例外這些資料集全是自然語言理解領域的問題,而對於 NLP 中另一個龐大領域自然語言生成,不約而同地選擇了放棄,是未曾注意,還是選擇視而不見,背後的原因我無從猜測。

不過,無論 GPT 和 BERT 是什麼原因沒有選擇在生成問題中去嘗試,私以為,想要把 GPT 和 BERT 的預訓練模型遷移到生成問題中,大約應該也不是一件非常困難的事。

首先 GPT 本質上就是一個生成模型,而 BERT 雖使用的是 Encoder,但把它改造成一個 Decoder 應該不是什麼特別困難的事,因為本質上 Encoder 和 Decoder 基本框架相同,只不過 Decoder 需要做好兩件事情:

第一件事情是 Decoder 中也有 Self-attention 層,和 Encoder 中的 Self-attention 不同的是,Decoder 需要做好 mask,以避免 attention 到未來待預測的詞上去。第二件事情是 Decoder 中有一個交叉 attention,用於獲取來自於 Encoder 側的資訊。

但是,這裡還涉及到 Encoder-Decoder 整體這種 Seq2Seq 框架是否要保留的問題。如果使用 GPT 用的 T-DMCA 這種僅用 Decoder 做生成問題的,那麼改造相對會更易行,如果要保留 Encoder-Decoder 這種雙結構,那麼改造可能會相對複雜一些。

樂觀一點,如果這種改造非常成功(似乎已經具備有充分的理由來相信這一點),那麼 NLP 似乎會迎來一個階段性的大一統時代:也就是一個基本模型做到一個閉環。其流程是:從文字到語義表徵,再從語義表徵重新生成文字。這樣看來,至少在 NLP 領域(CV 等其他 AI 領域,留給各位看官自行腦洞好了),未來大約還是很值得期待的一件事情。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

那麼以谷歌為代表的這些工作,會不會像五年以前一樣,也即將開啟一個新的時代呢?能不能讓 AI 成功爬上第二級梯子呢?讓我們拭目以待。

後現代啟示錄?

回過頭來看此文,通篇大體都是反觀 AI 兒子理解他人說的話寫的書,無非都是些人類母親的育兒筆記,徒當以史為鑑可以知興替罷了。 

實事求是的說,人類母親的教學成果目前看來,至少還不算太賴,寄養在各大公司(以 Google, Facebook 和 OpenAI 等為代表)的 AI 兒子們也開始理解母親的一些稍微簡單些的指令,甚至也能咿咿呀呀的開口說話了,不能不說這十多年的 AI 成長大家有目共睹。

但成果畢竟屬於過去,望子成龍的人類母親對於兒子的今後該如何安排?畢竟兒子以後還要上清北常青藤,畢竟七大姑八大姨的飯桌上,人類母親還指望著兒子來長臉的不是?

不過非要問人類母親的育兒計劃,首先那些有權有勢的託兒所所長們(以 Google, Facebook 和 OpenAI 等為代表)如果真雄心萬丈也一定不願透露半點風聲,其次可能人類母親自己也弄的不是太清楚,無非是走一步瞧一步罷了。然而沒什麼能夠阻擋人類對於未來的好奇心,追本溯源按圖索驥之下,說不定也能窺的一些蛛絲馬跡。 

因而,無妨抬抬頭看看星空,也看看未來,以期 AI 的今後也能刷刷朋友圈,感嘆一下年華易逝,甚而至於能體會得到人類母親養兒育女的艱難,哪怕只有絲毫,大概人類母親也就不枉費了一片可能和人類歷史一樣悠久的望子成龍之心。

閒話少敘,讓我們開開腦洞,大言不慚的看看今後 NLP 的一些可能性。只不過我人微言輕,暫且幻想一下皇帝的金鋤頭吧。

一般來說,語言學流派大約可以分為五個流派:歷史比較語言學、結構主義語言學、轉換-生成語言學、系統功能語言學和認知語言學這五個大的學派,其中歷史比較語言學年代比較久遠了,注重蒐集多種語言歷史資料,通過對比歸納總結等等方法,進而理解語言的演化和發展歷程,歷史比較語言學的主要貢獻在於它把語言學從其他學科裡抽離出來成為一門獨立的學科。

而結構主義語言學(大約興起於20世紀20年代左右)的開山鼻祖索緒爾(同時他也是現代語言學之父),對於語言的理解,他有一個非常經典的類比,他認為語言就像是一盤象棋,棋盤上的每一個棋子都有特定的遊戲規則,棋子只不過是這些特定遊戲規則的一個實物標記而已,如果某天弄丟了某一個棋子,我們立馬可以拿任何一個小物件來代替這個棋子的功能,只要遊戲規則不變,那麼棋局依然能夠照常進行,棋子替換前後基本不會有任何差別。

索緒爾認為語言裡的基本單位諸如詞語習語等等都只不過是棋盤上的棋子(signifiant,能指),重要的不是棋子本身,而是棋子背後對應的一整套規則,也就是語言裡的各種結構或約定俗稱的指代或用法(signifié,所指)。象棋的整個規則便是我們所使用的一整套語言系統,比如象棋的規則便是我們的漢語,而國際象棋的規則便是英語,兩種象棋各自使用的棋子各有不同,規則自然也有所區別。

索緒爾之後,結構主義語言學又進一步發展成為三個獨立子學派,但它們的共同點都是和索緒爾一脈相承的,也就是都認為語言本質上是一個符號系統,符號只是表面,更重要的是挖掘和研究符號系統內在的規則和聯絡。結構主義語言學興起後,標誌著現代語言學的誕生,並且在一段時期內不僅顯著的影響了後來的語言學流派,還影響了其他的一些人文學科。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

到了 1957 年,喬姆斯基出版《句法結構》一書,標誌著轉換生成語言學的誕生。喬姆斯基的主要觀點則認為人類天生有一套普遍語法,我們日常的語言系統只不過是這套普遍語法的形式化體現而已

因此他認為語言中存在著深層結構和表層結構,深層結構與意義和概念相關,而表層結構則與語音和符號相關,這兩層結構由一套轉換語法連線起來,也就是由深層結構生成表層結構。他為此還特別舉了一些例子,為了說明兩個句子表層結構類似乃至完全一樣,但是深層語義完全不相關的現象,比如:Flying planes can be dangerous.

這個句子可以因為同一份表層結構對應到兩套不同的深層結構,而表達出完全不一樣的意義,第一種是 flying 作為形容詞,那麼此時句子想表達的意義是在天上正在飛行的飛機很危險;而第二種是把"flying planes"解構成一種動賓結構,那麼這個句子表達的意思就成了開飛機這個動作很危險。

喬姆斯基舉這個例子,就是為了說明傳統的結構主義語言學對於這種句子根本無能為力,因為它們的表層結構是完全一致的,從而得出語言有一種更為本質的深層結構的結論。

但是個人理解,與其說是語言學的深層結構,還不如說是人類認知體系或者客觀世界的結構化表示,因為深層結構必然對應到人類的認知體系。並且喬姆斯基過於強調了普遍語法的天生性,而忽略了後天語言的塑造和習得。

後來喬姆斯基的眾多弟子(喬治萊考夫等人)也公開宣判與自己老師喬姆斯基的語言學觀點決裂,並各自獨立發展出了一些新的語言學理論,比如喬治萊考夫的認知語言學流派。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

我們可以發現從歷史比較語言學,到結構主義語言學,再到轉換生成語言學,這三個流派基本可以認為是一脈相承的,只不過所想要揭示的東西越來越深層,從歷史比較語言學著重於研究語言學的一些表現形式和演化形態,到結構主義語言學著重於研究符號系統背後的結構,再到轉換生成語言學認為結構主義語言學揭示的只是語言的表層結構,因而自己提出了深層結構的概念。

而到了 20 世紀八九十年代,以韓禮德為代表的一些語言學家,逐漸注意到無論是結構主義還是轉換生成語言學,它們都片面的強調語言學的理論形式,而忽視了語言的社會的功能屬性,韓禮德認為這語言的結構和語法等等理論形式,以及社會功能屬性是語言的兩個方面,這二者都很重要,他想要把二者統一起來。

因此發展了他自己的語言學觀點,他提出了系統語法和功能語法的概念,前者強調語言的內在結構和形式理論,這點和索緒爾的結構主義一脈相承,但他同時還提出了功能語法,著重語言的社會屬性,說明語言是社會交流和溝通的媒介,不過由於在系統語法上的影響力遠不如喬姆斯基等流派的影響力,功能語法的建樹倒是別的流派所缺乏的,因此把韓禮德的學說就叫做系統功能語言學了。

而上文提到的喬姆斯基的弟子之一喬治萊考夫,認為他的老師對於人類天生具有普遍語法的觀點是大錯特錯,尤其是轉換生成語法中認為深層結構由一些有限的普遍語法組成,著重語言規則的形式化。

喬治萊考夫嚴厲抨擊了他老師的觀點,與轉換生成語法著重形式和規則的探討相反,認知語言學認為語言不是人類的一個獨立系統,人類的一切語言行為都只不過是人類對於客觀世界和精神世界親身體驗的外化,也就是說,語言無論把它叫做符號也好,叫做語法結構也好,叫做系統也好,都逃離不了語言是人類認知系統的一部分,語言並不獨立存在。

人類在長期的自然演化中,形成了一套自己獨有的認知系統,而通過這套認知系統,人類可以獲得對於身邊客觀環境以及自己內部精神世界的一套獨特體驗,這套體驗如果對映到外在的語音或文上,便形成了我們人類的語言系統。因此人類的語言與人類其他認知能力沒有本質上的區別。

不過,認知語言學並沒有一味地架空自己的語言學觀念,它也適時地道出了語法規則和人類的認知系統有一定的對應性,言下之意便是說語法規則是人類認知系統抽象化的外在體現。

可以看出,在這一點上,認知語言學比轉換生成語言學又更進了一步,直接從研究人類的認知角度來研究人類的語言學。喬治萊考夫在語言學中引入了隱喻的概念,認為隱喻是人類最重要的認知活動之一,也是語言中最普遍的現象。

NLP 的巨人肩膀(下):從 CoVe 到 BERT

然而回到當下的自然語言處理,似乎現在談認知,有點為時尚早。在自然語言處理的發展歷程中,除了喬姆斯基的轉換生成語言學曾經在上世紀六七十年代大放異彩,一度成為當時自然語言處理的主流方法,除此之外,似乎理論語言學界一直和自然語言處理脫鉤比較嚴重,這一方面揭示了人類語言系統的複雜,另一方面,也預示著現在的自然語言處理缺乏嚴密的理論支援。

說白了,現在的 NLP 界,尤其以 BERT 為代表的做法,實際上是簡單粗暴的代名詞,它們把人類語言習得過程中的輕量、泛化和低功耗,用大資料、大模型和大計算量炮轟的蕩然無存

站在眼力所及之處的滿目瘡痍上,我們依然無法讓機器真正明白“喵星人”這個詞對於人類而言,其背後究竟意味著一個怎樣的複雜情緒和體驗(更有趣的是,即便對於每個人類個體而言,其背後的體驗也是千差萬別的,可見語言籠闊了符號性、認知性、社會性和個體性之後的複雜程度)。

然而在身後留下一片廢墟和屍骨的 BERT,正準備重建一個偉大新帝國的 BERT,它究竟是要選擇開心呢還是難過?對於人類母親而言,這大概會是個問題。

不過無論如何,帝國的建立總是艱難的。讓人類母親足可欣慰的是,即便步履維艱,託兒所裡的 AI 兒子們依然一步一步堅定的踩上了簡陋的梯子。

站在巨人的肩膀上回首,我們發現,和人類母親的語言習得方式依靠某一個機緣巧合的基因突變不同,AI 踏出了一條只屬於它自己的路,未來的巨人肩膀和梯子在哪裡,我們是否需要選用新的巨人肩膀和梯子,這都是成長的必經之痛,一切都留給未來去證實或證偽吧。

相關文章