深度長文:NLP的巨人肩膀(上)

PaperWeekly發表於2018-12-10

我們都知道,牛頓說過一句名言"If I have seen further, it is by standing on the shoulders of giants". 無可否認,牛頓取得了無與匹敵的成就,人類歷史上最偉大的科學家之一,但同樣無可否認的是,牛頓確實吸收了大量前人的研究成果,諸如哥白尼、伽利略和開普勒等人,正因如此,聯合國為了紀念伽利略首次將望遠鏡用作天文觀測四百週年,2009 年的時候,通過了”國際天文年“的決議,並選中《巨人的肩膀》(Shoulders Of Giants)作為主題曲。我想這大概是告訴後人,“吃水不忘挖井人”,牛頓的成就固然偉大,他腳下的“巨人肩膀”伽利略也同樣不能忘了。 

也許,“巨人肩膀”無處不在,有些人善於發現,有些人選擇性失明,而牛頓的偉大之處在於他能夠發現,這是偉大其一,更為重要的是,他還能自己造了梯子爬上“巨人的肩膀”,併成為一個新的“巨人肩膀”,這是偉大其二。 

而回到這篇文章的主題,作為平凡人的我們,暫且先把如何發現並造了梯子云雲撇開不談,讓我們先來捋一捋現在 NLP 當中可能的“巨人肩膀”在哪裡,以及有哪些已經造好的“梯子”可供攀登,餘下的,就留給那些立志成為“巨人肩膀”的人文志士們吧。

通常來說,NLP 中監督任務的基本套路都可以用三個積木來進行歸納:

  • 文字資料蒐集和預處理

  • 將文字進行編碼和表徵

  • 設計模型解決具體任務

深度長文:NLP的巨人肩膀(上)

其中資料處理階段自不用說,各個任務按照各自的邏輯去處理和得到相應的輸入。而其中的第二階段 Encoder 模組與第三階段的 Task-specific Model 模組,通常來說,界限並不是特別清晰,二者之間互有滲透。

回顧過去基於深度學習的 NLP 任務可以發現,幾乎絕大多數都比較符合這三層概念。比如很多生成任務的 Seq2Seq 框架中不外乎都有一個 Encoder 和一個 Decoder。對應到這裡,Decoder 更像是一個 Task-specific Model,然後相應的將 Encoder 做一些細微調整,比如引入 Attention 機制等等。

對於一些文字分類任務的結構,則 Encoder 模組與 Task-specific Model 模組的區分更為明顯和清晰,Encoder 負責提取文字特徵,最後接上一些全連線層和 Softmax 層便可以當做 Task-specific Model 模組,如此便完成了一個文字分類任務。 

既然很多的 NLP 任務都可以用這三個模組來進行歸納,並且其中資料層和 Task-specific Model 模組層可能根據具體任務的不同需要做一些相應設計,而 Encoder 層便可以作為一個相對比較通用的模組來使用。那麼自然會有一個想法,能不能類似於影像領域中的 ImageNet 上預訓練的各種模型,來做一個 NLP 中預訓練好的 Encoder 模組,然後我拿來直接利用就好了?

應該說,這個想法並不難想到,NLP 人也花了一些時間去思考究竟該如何設計一些更通用的可以遷移利用的東西,而不是所有的任務都要“from scratch”。因為如何儘量利用已有的知識、經驗和工具,避免重複造輪子,想盡一切辦法“站在巨人的肩膀上”快速發展,我想大概也是最樸素的“發展是硬道理”的體現。 

為了更好地釐清這個思路,我們先來看看人類和 CV(Computer Vision計算機視覺)曾經都是如何“站在巨人的肩膀上”的。

人類的巨人肩膀

大約在 20 萬年前,人類祖先 FOXP2 基因的 2 處(相對於其他原始猿類,如下圖)極其微小卻又至為關鍵的突變,讓人類祖先逐漸擁有了語言能力,從此人類逐漸走上了一條不同於其他所有動物的文明演化之路。

深度長文:NLP的巨人肩膀(上)

而人類語言以及隨後產生的文字也是人類區別於其他動物的一個至關重要的特徵,它使得人類協同合作的能力變得極為強大,且讓代際間經驗與文化的傳承效率大大提升。知名博主 Tim Urban——連大佬 Elon Musk 都是他的鐵桿粉絲——在 2017 年 4 月發表的巨長博文中(其實也是為了商業互吹 Musk 的 Neuralink),Tim 分析了為什麼語言能夠加速人類文明的發展,並最終從幾十萬年前智慧生命競賽跑道上所有一切潛在對手中大比分強勢勝出。

在這個過程中,語言起了非常關鍵的作用。在語言產生前,人類社會發展非常緩慢,表現為代際間的傳承效率非常低下,而自從語言誕生以後,人類社會的發展速度得到極大的提升,這主要體現在父輩的生存技能和經驗能夠通過快速有效的方式傳承給子輩,不僅如此,在同輩之間通過語言的溝通和交流,寶貴的經驗也能夠通過語言這種高效的媒介迅速傳播到原始部落的每一個角落。於是,人類的每一代都能夠得以在他們的父輩打下的江山上,一日千里,終成這顆藍色星球上的無二霸主。

深度長文:NLP的巨人肩膀(上)


深度長文:NLP的巨人肩膀(上)

不過,我覺得 Urban 想說卻並沒有說出來的是,即便在人類語言誕生之前,人類祖先也可以通過可能已經先於語言而誕生的學習與認知能力,做到以“代”為單位來進行傳承與進化,只不過不同於基因進化,這是一種地球生命全新的進化方式,在效率上已經比其他生物的進化效率高的多得多。

地球上自生命誕生以來一直被奉為圭臬的基因進化法則,往往都是以一個物種為單位,上一代花了生命代價學習到的生存技能需要不斷的通過非常低效的“優勝劣汰,適者生存”的叢林法則,寫進到該物種生物的基因中才算完事,而這往往需要幾萬年乃至幾百萬年才能完成。而在這個過程中,比其他物種強得多的學習能力是人類制勝的關鍵。

上面兩個事實,前者說明了語言是加速文明進化的潤滑劑,而後者說明了強大的學習能力是人類走出一條有人類特色的發展之路,從而脫離基因進化窠臼的最為重要的因素。

也就是說,對於人類而言,他們的父輩,同輩,以及一切同類,乃至大自然萬事萬物都是他們的“巨人肩膀”;而語言和學習能力則是人類能夠站上“巨人肩膀”的“梯子”。

回到本文的主題,對於人類的鋼鐵“兒子”AI 來說,CV 和 NLP 是當前 AI 最火爆的兩個領域之二,一個要解決鋼鐵“兒子”的視覺功能,一個要解決鋼鐵“兒子”的語言或認知能力,那麼什麼又是這個鋼鐵“兒子”的“巨人肩膀”和“梯子”呢?我們先來看看 CV 中的情況。

CV的巨人肩膀

ImageNet 是 2009 年由李飛飛團隊鄧嘉等人提出,並迅速發展成為 CV 領域最知名的比賽 ILSVRC。從 2010 年舉辦第一屆,到 2017 年李飛飛宣佈最後一屆,前後總共舉辦 8 年,這八年間先後在這個比賽中湧現了一大批推動 AI 領域尤其是 CV 領域大發展的演算法和模型。特別值得一提的是 2012 年 Hinton 團隊提出了 AlexNet,超過當時第二名效果 41%,一下子引爆了 AI 領域,因此 2012 年也被稱為“深度學習元年”。

深度長文:NLP的巨人肩膀(上)


深度長文:NLP的巨人肩膀(上)

隨之而來,大家發現如果用已經在 ImageNet 中訓練好的模型,並用這些模型中的引數來初始化新任務中的模型,可以顯著的提升新任務下的效果。這種站在“巨人肩膀”上的方法已經被成功運用到很多 CV 任務中,諸如物體檢測和語義分割等。不僅如此,更重要的是,這種充分使用預訓練模型的方法可以非常便利地遷移到一些獲取標註資料較為困難的新場景中,從而極大的改善模型對標註資料數量的要求,並降低標註資料的成本。

因此,利用大規模資料集預訓練模型進行遷移學習的方法被認為是 CV 中的標配。以至於 2018 年的早些時候,大神何凱明所在的 FAIR 團隊利用 Instgram 中數十億張帶有使用者標籤的圖片進行預訓練,而後將其在 ImageNet 的比賽任務中進行 fine-tune,取得了最好的成績(arXiv:1805.00932)。只不過,由於預訓練的資料過於龐大,該工作動用了 336 塊 GPU 預訓練了 22 天,不得不說實在都是土豪們才玩得動的遊戲,這一點和下文要介紹的 NLP 中的預訓練步驟何其相似。 

不過為何這種預訓練的模式能夠有效?這背後有什麼更深刻的內涵嗎?為此,Google Brain 團隊將 2014 年的 ImageNet 冠軍 GoogleNet 的中間層進行了視覺化,可以發現模型的較低層學習到的主要是物體的邊緣,往高層後逐步就變成了成型的物體了。一般來說,物體的邊緣和紋路都是一些比較通用的視覺特徵,因此將這一部分對應的模型引數用來初始化 task-specific 模型中的引數,意味著模型就不需要再從頭開始學習這些特徵,從而大大提升了訓練效率和效能。

深度長文:NLP的巨人肩膀(上)

總結起來就是,CV 中的“巨人肩膀”是 ImageNet 以及由之而來 Google 等公司或團隊在大規模資料集上預訓練得到的模型,而“梯子”便是 transfer learning 之下的 fine-tuning。

尋找NLP的巨人肩膀

和 CV 領域中深度學習的驚豔表現相比,對於 NLP 任務來講,深度學習的應用一直沒有帶來讓人眼前特別一亮的表現。ImageNet 中的圖片分類任務,深度學習早已超越人類的分類準確率,而這一目標對於 NLP 中的深度學習來說,似乎成了不太可能完成的任務,尤其是在那些需要深層語義理解的任務當中,更是如此。

但即便如此,困難從來未曾阻止人類想要教給他“兒子”理解“長輩”的話並開口說“人話”的雄心,憂心忡忡的人類家長恨不得也給 AI 來一次 FOXP2 基因突變——正像 20 萬年前上帝的一次神來之筆給人類帶來了語言能力一樣。 

2018 年 9 月,DeepMind 主辦的 Deep Learning Indaba 2018 大會在南非舉行,ULMFit 的作者之一 Sebastian Ruder 在大會上做了一個很精彩的名為 Frontiers of Natural Language Processing 的報告,前後分為兩個部分:第一部分梳理近些年 NLP 的發展;第二部分探討了當前 NLP 遇到的一些困難。

在參考這個報告的同時,回到本文最開頭,這裡將主要著重於 NLP 中最為重要的 Encoder 模組,並拋去具體的模型之爭(諸如 CNN,RNN 和 Transformer 等),想要努力梳理出一條 NLP 任務中如何更有效站上“巨人肩膀”的一些模式出來。 

本質上,自然語言理解 NLU 的核心問題其實就是如何從語言文字的表象符號中抽取出來蘊含在文字背後的真實意義,並將其用計算機能夠讀懂的方式表徵出來。當然這通常對應的是數學語言,表徵是如此重要,以至於 2012 年的時候 Yoshua Bengio 作為第一作者發表了一篇表徵學習的綜述 Representation Learning: A Review and New Perspectives,並隨後在 2013 年和深度學習三大巨頭的另一位巨頭 Yann LeCun 牽頭創辦 ICLR,這一會議至今才過去 5 年時間,如今已是 AI 領域最負盛名的頂級會議之一。可以說,探究 NLP 或 NLU 的歷史,同樣也是探究文字如何更有效表徵的歷史。

梯子出現之前 

猶如生命的誕生之初,混沌而原始。在 word2vec 誕生之前,NLP 中並沒有一個統一的方法去表示一段文字,各位前輩和大師們發明了許多的方法:從 one-hot 表示一個詞到用 bag-of-words 來表示一段文字,從 k-shingles 把一段文字切分成一些文字片段,到漢語中用各種序列標註方法將文字按語義進行分割,從 tf-idf 中用頻率的手段來表徵詞語的重要性,到 text-rank 中借鑑 page-rank 的方法來表徵詞語的權重,從基於 SVD 純數學分解詞文件矩陣的 LSA,到 pLSA 中用概率手段來表徵文件形成過程並將詞文件矩陣的求解結果賦予概率含義,再到 LDA 中引入兩個共軛分佈從而完美引入先驗......

語言模型 

而以上這些都是相對比較傳統的方法,在介紹基於深度學習的方法之前,先來看看語言模型。實際上,語言模型的本質是對一段自然語言的文字進行預測概率的大小,即如果文字用 Si 來表示,那麼語言模型就是要求 P(Si) 的大小,如果按照大數定律中頻率對於概率無限逼近的思想,求這個概率大小,自然要用這個文字在所有人類歷史上產生過的所有文字集合中,先求這個文字的頻率 P(Si) ,而後便可以通過如下公式來求得:

深度長文:NLP的巨人肩膀(上)

這個公式足夠簡單,但問題是全人類所有歷史的語料這種統計顯然無法實現,因此為了將這個不可能的統計任務變得可能,有人將文字不當做一個整體,而是把它拆散成一個個的詞,通過每個詞之間的概率關係,從而求得整個文字的概率大小。假定句子長度為 T,詞用 x 表示,即:

深度長文:NLP的巨人肩膀(上)

然而,這個式子的計算依然過於複雜,我們一般都會引入馬爾科夫假設:假定一個句子中的詞只與它前面的 n 個詞相關,特別地,當 n=1 的時候,句子的概率計算公式最為簡潔:

深度長文:NLP的巨人肩膀(上)

並且把詞頻的統計用來估計這個語言模型中的條件概率,如下:

深度長文:NLP的巨人肩膀(上)

這樣一來,語言模型的計算終於變得可行。然而,這種基於統計的語言模型卻存在很多問題: 

第一,很多情況下深度長文:NLP的巨人肩膀(上)的計算會遇到特別多零值,尤其在 n 取值較大時,這種資料稀疏導致的計算為 0 的現象變得特別嚴重。所以統計語言模型中一個很重要的方向便是設計各種平滑方法來處理這種情況。 

第二, 另一個更為嚴重的問題是,基於統計的語言模型無法把 n 取得很大,一般來說在 3-gram 比較常見,再大的話,計算複雜度會指數上升。這個問題的存在導致統計語言模型無法建模語言中上下文較長的依賴關係。 

第三,統計語言模型無法表徵詞語之間的相似性。 

NNLM 

這些缺點的存在,迫使 2003 年 Bengio 在他的經典論文 A Neural Probabilistic Language Model 中,首次將深度學習的思想融入到語言模型中,並發現將訓練得到的 NNLM(Neural Net Language Model,神經網路語言模型)模型的第一層引數當做詞的分散式表徵時,能夠很好地獲取詞語之間的相似度。

深度長文:NLP的巨人肩膀(上)

撇去正則化項,NNLM 的極大目標函式對數似然函式,其本質上是個 N-Gram 的語言模型,如下所示:

深度長文:NLP的巨人肩膀(上)其中,歸一化之前的概率大小(也就是 logits)為:

深度長文:NLP的巨人肩膀(上)

x 實際上就是將每個詞對映為 m 維的向量,然後將這 n-1 個詞的向量 concat 起來,組合成一個 (n-1)*m 維的向量。

這裡可以將 NNLM 的網路結構拆分為三個部分:第一部分,從詞到詞向量的對映,通過 C 矩陣完成對映引數個數為 |V| * m; 第二部分,從 x 到隱藏層的對映,通過矩陣 H,這裡的引數個數為 |H| * m * (n-1); 第三部分,從隱藏層到輸出層的對映,通過矩陣 U,引數個數為 |V| * |H|;第四部分,從 x 到輸出層的對映,通過矩陣 W,引數個數為 |V| * m * (n-1)。

因此,如果算上偏置項的引數個數(其中輸出層為 |V|,輸入層到隱藏層為 |H|)的話,NNLM 的引數個數為:

深度長文:NLP的巨人肩膀(上)

可見 NNLM 的引數個數是所取視窗大小 n 的線性函式,這便可以讓 NNLM 能對更長的依賴關係進行建模。不過 NNLM 的最主要貢獻是非常有創見性地將模型的第一層特徵對映矩陣當做詞的分散式表示,從而可以將一個詞表徵為一個向量形式,這直接啟發了後來的 word2vec 的工作。

這許多的方法和模型正如動盪的春秋戰國,諸子百家群星閃耀,然而卻也誰都未能有絕對的實力統一當時的學術界。即便到了秦始皇,雖武力空前強大,踏平六國,也有車同軌書同文的壯舉,卻依然未能把春秋以降的五彩繽紛的思想學術界統一起來。直到歷史再歷一百年,漢武帝終於完成了這個空前絕後的大手筆,“罷黜百家獨尊儒術”,這也直接成為了華夏文化的核心基礎。不禁要問,如果把 NNLM 當做文化統一前的“書同文”苗頭,那 NLP 中的“獨尊儒術”在哪裡?

歷史突破——梯子來了 

自 NNLM 於 2003 年被提出後,後面又出現了很多類似和改進的工作,諸如 LBL,C&W 和 RNNLM 模型等等,這些方法主要從兩個方面去優化 NNLM 的思想:其一是 NNLM 只用了左邊的 n-1 個詞,如何利用更多的上下文資訊便成為了很重要的一個優化思路(如 Mikolov 等人提出的 RNNLM);其二是 NNLM 的一個非常大的缺點是輸出層計算量太大,如何減小計算量使得大規模語料上的訓練變得可行,這也是工程應用上至關重要的優化方向(如 Mnih 和 Hinton 提出的 LBL 以及後續的一系列模型)。 

為了更好理解 NNLM 之後以及 word2vec 誕生前的情況,我們先來看看現有的幾個主要模型都有哪些優缺點。 

NNLM 雖然將 N-Gram 的階 n 提高到了 5,相比原來的統計語言模型是一個很大的進步,但是為了獲取更好的長程依賴關係,5 顯然是不夠的。再者,因為 NNLM 只對詞的左側文字進行建模,所以得到的詞向量並不是語境的充分表徵。

還有一個問題就更嚴重了,NNLM 的訓練依然還是太慢,在論文中,Bengio 說他們用了 40 塊 CPU,在含有 1400 萬個詞,只保留詞頻相對較高的詞之後詞典大小為 17964 個詞,只訓練了 5 個 epoch,但是耗時超過 3 周。按這麼來算,如果只用一塊 CPU,可能需要 2 年多,這還是在僅有 1400 萬個詞的語料上。如此耗時的訓練過程,顯然嚴重限制了 NNLM 的應用。 

2007 年 Mnih 和 Hinton 提出的 LBL 以及後續的一系列相關模型,省去了 NNLM 中的啟用函式,直接把模型變成了一個線性變換,尤其是後來將 Hierarchical Softmax 引入到 LBL 後,訓練效率進一步增強,但是表達能力不如 NNLM 這種神經網路的結構。

2008 年 Collobert 和 Weston 提出的 C&W 模型不再利用語言模型的結構,而是將目標文字片段整體當做輸入,然後預測這個片段是真實文字的概率,所以它的工作主要是改變了目標輸出。由於輸出只是一個概率大小,不再是詞典大小,因此訓練效率大大提升,但由於使用了這種比較“別緻”的目標輸出,使得它的詞向量表徵能力有限。

2010 年 Mikolov(對,還是同一個 Mikolov)提出的 RNNLM 主要是為了解決長程依賴關係,時間複雜度問題依然存在。

深度長文:NLP的巨人肩膀(上)

而這一切,似乎都在預示著一個新時代的來臨。

CBOW 和 Skip-gram 

2013 年,Tomas Mikolov 連放幾篇劃時代的論文,其中最為重要的是兩篇,Efficient estimation of word representations in vector space 首次提出了 CBOW和Skip-gram模型,進一步地在 Distributed Representations of Words and Phrases and their Compositionality 中,又介紹了幾種優化訓練的方法,包括 Hierarchical Softmax(當然,這個方法早在 2003 年,Bengio 就在他提出 NNLM 論文中的 Future Work 部分提到了這種方法,並於 2005 年把它系統化發表了一篇論文),Negative Sampling 和 Subsampling 技術。

放出兩篇論文後,當時仍在谷歌工作的 Mikolov 又馬不停蹄放出了大殺器——word2vec 工具,並在其中開源了他的方法。順便提一下的是,很多人以為 word2vec 是一種模型和方法,其實 word2vec 只是一個工具,背後的模型是 CBOW 或者 Skip-gram,並且使用了 Hierarchical Softmax 或 Negative Sampling 這些訓練的優化方法。

所以準確說來,word2vec 並不是一個模型或演算法,只不過 Mikolov 恰好在當時把他開源的工具包起名叫做 word2vec 而已。不過為了敘述簡單,在下文我將用 word2vec 來指代上面提到 Mikolov 兩篇論文中的一整個相關的優化思想。 

word2vec 對於前人的優化,主要是兩方面的工作:模型的簡化和訓練技巧的優化。我們先來看看模型的簡化方面,也就是耳熟能詳的 CBOW 和 Skip-gram。 

對於 CBOW 而言,我們可以從它的名字上一窺究竟,它的全稱是 Continuous Bag-of-Words,也就是連續的詞袋模型,為什麼取這個名字,先來看看它的目標函式

深度長文:NLP的巨人肩膀(上)

首先,CBOW 沒有隱藏層,本質上只有兩層結構,輸入層將目標詞語境 c 中的每一個詞向量簡單求和(當然,也可以求平均)後得到語境向量,然後直接與目標詞的輸出向量求點積,目標函式也就是要讓這個與目標詞向量的點積取得最大值,對應的與非目標詞的點積儘量取得最小值。

從這可以看出,CBOW 的第一個特點是取消了 NNLM 中的隱藏層,直接將輸入層和輸出層相連;第二個特點便是在求語境 context 向量時候,語境內的詞序已經丟棄(這個是名字中 Continuous 的來源);第三,因為最終的目標函式仍然是語言模型目標函式,所以需要順序遍歷語料中的每一個詞(這個是名字中 Bag-of-Words 的來源)。

因此有了這些特點(尤其是第二點和第三點),Mikolov 才把這個簡單的模型取名叫做 CBOW,簡單卻有效的典範。

深度長文:NLP的巨人肩膀(上)

需要注意的是這裡每個詞對應到兩個詞向量,在上面的公式中都有體現,其中 e(wt) 是詞的輸入向量,而 e'(wt) 則是詞的輸出向量,或者更準確的來講,前者是 CBOW 輸入層中跟詞 wt 所在位置相連的所有邊的權值(其實這就是詞向量)組合成的向量,而是輸出層中與詞 wt 所在位置相連的所有邊的權值組合成的向量,所以把這一向量叫做輸出向量。 

同樣地,和 CBOW 對應,Skip-gram 的模型基本思想和 CBOW 非常類似,只是換了一個方向:CBOW 是讓目標詞的輸出向量 e'(wt) 擬合語境的向量 x ;而 Skip-gram 則是讓語境中每個詞的輸出向量儘量擬合當前輸入詞的向量 e(wt),和 CBOW 的方向相反,因此它的目標函式如下:

深度長文:NLP的巨人肩膀(上)

可以看出目標函式中有兩個求和符號,最裡面的求和符號的意義便是讓當前的輸入詞分別和該詞對應語境中的每一個詞都儘量接近,從而便可以表現為該詞與其上下文儘量接近。

深度長文:NLP的巨人肩膀(上)

和 CBOW 類似,Skip-gram 本質上也只有兩層:輸入層和輸出層,輸入層負責將輸入詞對映為一個詞向量,輸出層負責將其經過線性對映計算得到每個詞的概率大小。

再細心一點的話,其實無論 CBOW 還是 Skip-gram,本質上都是兩個全連線層的相連,中間沒有任何其他的層。因此,這兩個模型的引數個數都是 2 × |e| × |V| ,其中 |e| 和 |V| 分別是詞向量的維度和詞典的大小,相比上文中我們計算得到 NNLM 的引數個數 |V|(1+|H|+|e|n) + |H|(1+|e|n-|e|) 已經大大減小,且與上下文所取詞的個數無關了,也就是終於避免了 N-gram 中隨著階數 N 增大而使得計算複雜度急劇上升的問題。 

然而,Mikolov 大神說了,這些公式是“impractical”的,他的言下之意是計算複雜度依然和詞典大小有關,而這通常都意味著非常非常大,以下是他的原話:

..., and W is the number of words in the vocabulary. This formulation is impractical because the cost of computing ∇ log p(wO|wI ) is proportional to W, which is often large (10^5–10^7 terms).

不得不說,大神就是大神,將模型已經簡化到了只剩兩個全連線層(再脫就沒了),依然不滿足,還“得寸進尺”地打起了詞典的“小算盤”,那麼 Mikolov 的“小算盤”是什麼呢? 

他在論文中首先提到了 Hierachical Softmax,認為這是對 full softmax 的一種優化手段,而 Hierachical Softmax 的基本思想就是首先將詞典中的每個詞按照詞頻大小構建出一棵 Huffman 樹,保證詞頻較大的詞處於相對比較淺的層,詞頻較低的詞相應的處於 Huffman 樹較深層的葉子節點,每一個詞都處於這棵 Huffman 樹上的某個葉子節點。

第二,將原本的一個 |V| 分類問題變成了 log |V| 次的二分類問題,做法簡單說來就是,原先要計算 P(wt|ct) 的時候,因為使用的是普通的 softmax,勢必要求詞典中的每一個詞的概率大小。

為了減少這一步的計算量,在 Hierachical Softmax 中,同樣是計算當前詞 wt 在其上下文中的概率大小,只需要把它變成在 Huffman 樹中的路徑預測問題就可以了,因為當前詞 wt 在 Huffman 樹中對應到一條路徑,這條路徑由這棵二叉樹中從根節點開始,經過一系列中間的父節點,最終到達當前這個詞的葉子節點而組成,那麼在每一個父節點上,都對應的是一個二分類問題(本質上就是一個 LR 分類器),而 Huffman 樹的構造過程保證了樹的深度為 log |V|,所以也就只需要做 log |V| 次二分類便可以求得 P(wt|ct) 的大小,這相比原來 |V| 次的計算量,已經大大減小了。

接著,Mikolov 又提出了負取樣的思想,而這一思想也是受了 C&W 模型中構造負樣本方法啟發,同時參考了 Noise Contrastive Estimation (NCE) 的思想,用 CBOW 的框架簡單來講就是,負取樣每遍歷到一個目標詞,為了使得目標詞的概率 P(wt|ct) 最大,根據 softmax 函式的概率公式,也就是讓分子中的深度長文:NLP的巨人肩膀(上)最大,而分母中其他非目標詞的深度長文:NLP的巨人肩膀(上)最小。

普通 softmax 的計算量太大就是因為它把詞典中所有其他非目標詞都當做負例了,而負取樣的思想特別簡單,就是每次按照一定概率隨機取樣一些詞當做負例,從而就只需要計算這些負取樣出來的負例了,那麼概率公式便相應變為:

深度長文:NLP的巨人肩膀(上)

仔細和普通 softmax 進行比較便會發現,將原來的 |V| 分類問題變成了 K 分類問題,這便把詞典大小對時間複雜度的影響變成了一個常數項,而改動又非常的微小,不可謂不巧妙。

除此之外,Mikolov 還提到了一些其他技巧,比如對於那些超高頻率的詞,尤其是停用詞,可以使用 Subsampling 的方法進行處理,不過這已經不是 word2vec 最主要的內容了。

自此,經過模型和訓練技巧的雙重優化,終於使得大規模語料上的訓練成為了現實,更重要的是,得到的這些詞向量能夠在語義上有非常好的表現,能將語義關係通過向量空間關係表徵出來。

深度長文:NLP的巨人肩膀(上)

word2vec 的出現,極大促進了 NLP 的發展,尤其是促進了深度學習在 NLP 中的應用(不過有意思的是,word2vec 演算法本身其實並不是一個深度模型,它只有兩層全連線),利用預訓練好的詞向量來初始化網路結構的第一層幾乎已經成了標配,尤其是在只有少量監督資料的情況下,如果不拿預訓練的 embedding 初始化第一層,幾乎可以被認為是在蠻幹。

在此之後,一大批 word embedding 方法大量湧現,比較知名的有 GloVe 和 fastText 等等,它們各自側重不同的角度,並且從不同的方向都得到了還不錯的 embedding 表徵。

GloVe

先來看看 GloVe 的損失函式:

深度長文:NLP的巨人肩膀(上)

其中 Xij 是兩個詞 i 和 j 在某個視窗大小中的共現頻率(不過 GloVe 對其做了一些改進,共現頻率相應有一個衰減係數,使得距離越遠的詞對共現頻率越小一些), f(Xij) 是一個權重係數,主要目的是共現越多的 pair 對於目標函式貢獻應該越大,但是又不能無限制增大,所以對共現頻率過於大的 pair 限定最大值,以防訓練的時候被這些頻率過大的 pair 主導了整個目標函式

剩下的就是演算法的核心部分了,兩個b值是兩個偏置項,撇去不談,那麼剩下的深度長文:NLP的巨人肩膀(上)其實就是一個普通的均方誤差函式, wi 是當前詞的向量, wj 對應的是與其在同一個視窗中出現的共現詞的詞向量,兩者的向量點乘要去儘量擬合它們共現頻率的對數值。

從直觀上理解,如果兩個詞共現頻率越高,那麼其對數值當然也越高,因而演算法要求二者詞向量的點乘也越大,而兩個詞向量的點乘越大,其實包含了兩層含義:

第一,要求各自詞向量的模越大,通常來說,除去頻率非常高的詞(比如停用詞),對於有明確語義的詞來說,它們的詞向量模長會隨著詞頻增大而增大,因此兩個詞共現頻率越大,要求各自詞向量模長越大是有直覺意義的,比如“魑魅魍魎”假如能被拆分成兩個詞,那麼“魑魅”和“魍魎”這兩個詞的共現頻率相比““魑魅”和其他詞的共現頻率要大得多,對應到“魑魅”的詞向量,便會傾向於在某個詞向量維度上持續更新,進而使得它的模長也會比較偏大。

第二,要求這兩個詞向量的夾角越小,這也是符合直覺的,因為出現在同一個語境下頻率越大,說明這兩個詞的語義越接近,因而詞向量的夾角也偏向于越小。

深度長文:NLP的巨人肩膀(上)

此外,可以從 GloVe 使用的損失函式中發現,它的訓練主要是兩個步驟:統計共現矩陣和訓練獲取詞向量,這個過程其實是沒有我們通常理解當中的模型的。

更遑論神經網路它整個的演算法框架都是基於矩陣分解的做法來獲取詞向量的,本質上和諸如 LSA 這種基於 SVD 的矩陣分解方法沒有什麼不同,只不過 SVD 分解太過於耗時,運算量巨大,相同點是 LSA 也是輸入共現矩陣,不過一般主要以詞-文件共現矩陣為主,另外,LSA 中的共現矩陣沒有做特殊處理,而 GloVe 考慮到了對距離較遠的詞對做相應的懲罰等等。

然而,相比 word2vec,GloVe 卻更加充分的利用了詞的共現資訊word2vec 中則是直接粗暴的讓兩個向量的點乘相比其他詞的點乘最大,至少在表面上看來似乎是沒有用到詞的共現資訊,不像 GloVe 這裡明確的就是擬合詞對的共現頻率。

不過更有意思的還是,GloVe 和 word2vec 似乎有種更為內在的聯絡,再來看看他們的目標函式有什麼不一樣,這是 Skip-gram 的目標函式(這裡在原來的基礎上加上了對於語料的遍歷 N):

深度長文:NLP的巨人肩膀(上)

而這個目標函式是按照語料的順序去遍歷,如果先把語料當中的相關詞進行合併,然後按照詞典序進行遍歷,便可以證明 Skip-gram 實際上和 GloVe 的優化目標一致,有興趣的可以參考證明細節(arXiv:1611.05962),這裡不再贅述。

fastText 

word2vec 和 GloVe 都不需要人工標記的監督資料,只需要語言內部存在的監督訊號即可以完成訓練。而與此相對應的,fastText 則是利用帶有監督標記的文字分類資料完成訓練。

本質上沒有什麼特殊的,模型框架就是 CBOW,只不過與普通的 CBOW 有兩點不一樣,分別是輸入資料和預測目標的不同,在輸入資料上,CBOW 輸入的是一段區間中除去目標詞之外的所有其他詞的向量加和或平均,fastText 為了利用更多的語序資訊,將 bag-of-words 變成了 bag-of-features,也就是下圖中的輸入 x 不再僅僅是一個詞,還可以加上 bigram 或者是 trigram 的資訊等等。

第二個不同在於,CBOW 預測目標是語境中的一個詞,而 fastText 預測目標是當前這段輸入文字的類別,正因為需要這個文字類別,因此才說 fastText 是一個監督模型。

而相同點在於,fastText 的網路結構和 CBOW 基本一致,同時在輸出層的分類上也使用了 Hierachical Softmax 技巧來加速訓練。

深度長文:NLP的巨人肩膀(上)這裡的深度長文:NLP的巨人肩膀(上)便是語料當中第 n 篇文件的第 i 個詞以及加上 N-gram 的特徵資訊。從這個損失函式便可以知道 fastText 同樣只有兩個全連線層,分別是 A 和 B,其中 A 便是最終可以獲取的詞向量資訊。

深度長文:NLP的巨人肩膀(上)

fastText 最大的特點在於快,論文中對這一點也做了詳細的實驗驗證,在一些分類資料集上,fastText 通常都可以把 CNN 結構的模型要耗時幾小時甚至幾天的時間,急劇減少到只需要消耗幾秒鐘,不可謂不“fast”。

不過說個八卦,為什麼 fastText 結構和 CBOW 如此相似。感興趣的讀者想要繼續深挖的話,還可以看看 2015 年 ACL 的一篇論文 Deep Unordered Composition Rivals Syntactic Methods for Text Classification,結構又是何其相似,並且比 fastText 的論文探討的更為深入一些,但是 fastText 是 2016 年的文章,剩下的大家自己去想好了。

這裡面大概一個特別重要的原因就是 fastText 的作者之一便是 3 年前 CBOW 的提出者 Mikolov 本人,只不過昔日的 Mikolov 還在谷歌,如今 3 年時間一晃而過,早已是 Facebook 的人了。

爬上第一級梯子的革命意義 

為什麼說 word2vec 的出現極大促進了 NLP 領域的發展? 

通常以為,word2vec 一類的方法會給每一個詞賦予一個向量的表徵,不過似乎從來沒有人想過這個辦法為什麼行之有效?難道是皇帝的新衣?按理來說,包括 NNLM 在內,這些方法的主要出發點都是將一個詞表示成詞向量,並將其語義通過上下文來表徵。

其實,這背後是有理論基礎的,1954 年 Harris 提出分佈假說(distributional hypothesis),這一假說認為:上下文相似的詞,其語義也相似,1957 年 Firth 對分佈假說進行了進一步闡述和明確:詞的語義由其上下文決定(a word is characterized by the company it keeps),30 年後,深度學習的祖師爺 Hinton 也於 1986 年嘗試過詞的分散式表示。 

word2vec 的貢獻遠不止是給每一個詞賦予一個分散式的表徵,私以為,它帶來了一種全新的 NLP 模型建立方法。在這之前,大多數 NLP 任務都要在如何挖掘更多文字語義特徵上花費大量時間,甚至這一部分工作佔去了整個任務工作量的絕大部分。

而以 word2vec 為代表的 distributed representation 方法大量湧現後(尤其是因為大規模語料上的預訓練詞向量成為現實,並且被證明確實行之有效之後),演算法人員發現利用 word2vec 在預訓練上學習到的詞向量,初始化他們自己模型的第一層,會帶來極大效果的提升,以至於這五年以來,幾乎業內的預設做法便是要用無論 word2vec 還是 GloVe 預訓練的詞向量,作為模型的第一層,如果不這麼做,大約只有兩個原因:1)你很土豪,有錢標註大量監督資料;2)你在蠻幹。

而這一個思想,絕不是如它表象所顯示的一樣,似乎和過去做文字特徵沒什麼太大區別,是的,表象確實是這樣,無非是把一個詞用了一堆數字來表徵而已,這和離散化的特徵有什麼本質區別嗎?

有,因為它開啟了一種全新的 NLP 模型訓練方式——遷移學習基本思想便是利用一切可以利用的現成知識,達到快速學習的目的,這和人類的進化歷程何其相似。

雖然咿咿呀呀囫圇吞棗似的剛開始能夠說得三兩個詞,然而這是“NLP 的一小步,人類 AI 的一大步”。正如人類語言產生之初,一旦某個原始人類的喉部發出的某個音節,經無比智慧和刨根問底考證的史學家研究證實了它具有某個明確的指代意義(無論它指代什麼,即便是人類的排洩物),這便無比莊嚴的宣示著一個全新物種的誕生,我想遷移學習在 NLP 中的這一小步,大概與此同擔當。

梯子的一級半

除了在 word 級別的 embedding 方法上有大量模型和演算法的湧現,同樣地,在 char 級別、句子級別和段落級別同樣有大量模型提出。 

word2vec 開源隨後的第一年,也就是在 2014 年,還是 Mikolov,在他和另一位作者合作的一篇論文 Distributed Representations of Sentences and Documents 中,提出了可以借鑑 word2vec 思想的兩種結構:PV-DM 和 PV-DBOW,分別對應 word2vec 中的 CBOW 和 Skip-gram。

PV-DM 和 PV-DBOW 

PV-DM 的全稱是 Distributed Memory Model of Paragraph Vectors,和 CBOW 類似,也是通過上下文預測下一個詞,不過在輸入層的時候,同時也維護了一個文件 ID 對映到一個向量的 look-up table,模型的目的便是將當前文件的向量以及上下文向量聯合輸入模型,並讓模型預測下一個詞。

訓練結束後,對於現有的文件,便可以直接通過查表的方式快速得到該文件的向量,而對於新的一篇文件,那麼則需要將已有的 look-up table 新增相應的列,然後重新走一遍訓練流程,只不過此時固定好其他的引數,只調整 look-up table,收斂後便可以得到新文件對應的向量了。

PV-DBOW 的全稱則是 Distributed Bag of Words version of Paragraph Vector,和 Skip-gram 類似,通過文件來預測文件內的詞,訓練的時候,隨機取樣一些文字片段,然後再從這個片段中取樣一個詞,讓 PV-DBOW 模型來預測這個詞。

以此分類任務作為訓練方法,說白了,本質上和 Skip-gram 是一樣的。這個方法有個致命的弱點,就是為了獲取新文件的向量,還得繼續走一遍訓練流程,並且由於模型主要是針對文件向量預測詞向量的過程進行建模,其實很難去表徵詞語之間的更豐富的語義結構,所以這兩種獲取文件向量的方法都未能大規模應用開來。

深度長文:NLP的巨人肩膀(上)Skip-thoughts 

2015 年,多倫多大學的 Kiros 等人提出了一個很有意思的方法叫 Skip-thoughts。同樣也是借鑑了 Skip-gram 的思想,但是和 PV-DBOW 中利用文件來預測詞的做法不一樣的是,Skip-thoughts 直接在句子間進行預測,也就是將 Skip-gram 中以詞為基本單位,替換成了以句子為基本單位,具體做法就是選定一個視窗,遍歷其中的句子,然後分別利用當前句子去預測和輸出它的上一句和下一句。

對於句子的建模利用的 RNN 的 sequence 結構,預測上一個和下一個句子時候,也是利用的一個 sequence 的 RNN 來生成句子中的每一個詞,所以這個結構本質上就是一個 Encoder-Decoder 框架,只不過和普通框架不一樣的是,Skip-thoughts 有兩個 Decoder。

在今天看來,這個框架還有很多不完善或者可以改進的地方(作者也在論文中分別提到了這些 future works),比如輸入的 Encoder 可以引入 attention 機制,從而讓 Decoder 的輸入不再只是依賴 Encoder 最後一個時刻的輸出;Encoder 和 Decoder 可以利用更深層的結構;Decoder 也可以繼續擴大,可以預測上下文中更多的句子;RNN 也不是唯一的選擇,諸如 CNN 以及 2017 年穀歌提出的 Transformer 結構也可以利用進來,後來果不其然谷歌的 BERT 便借鑑了這一思路,當然這是後話了,留下暫且不表。

深度長文:NLP的巨人肩膀(上)

Quick-thoughts 

2018 年的時候,在 Skip-thoughts 的基礎上,Google Brain 的 Logeswaran 等人將這一思想做了進一步改進,他們認為 Skip-thoughts 的 Decoder 效率太低,且無法在大規模語料上很好的訓練(這是 RNN 結構的通病)。

所以他們把 Skip-thoughts 的生成任務改進成為了一個分類任務,具體說來就是把同一個上下文視窗中的句子對標記為正例,把不是出現在同一個上下文視窗中的句子對標記為負例,並將這些句子對輸入模型,讓模型判斷這些句子對是否是同一個上下文視窗中,很明顯,這是一個分類任務。可以說,僅僅幾個月之後的 BERT 正是利用的這種思路。而這些方法都和 Skip-thoughts 一脈相承。

深度長文:NLP的巨人肩膀(上)InferSent 

除了 Skip-thoughts 和 Quick-thoughts 這兩種不需要人工標記資料的模型之外,還有一些從監督資料中學習句子表示的方法。

比如 2017 年 Facebook 的研究人員 Conneau 等人提出的 InferSent 框架,它的思想特別簡單,先設計一個模型在史丹佛的 SNLI(Stanford Natural Language Inference)資料集上訓練,而後將訓練好的模型當做特徵提取器,以此來獲得一個句子的向量表示,再將這個句子的表示應用在新的分類任務上,來評估句子向量的優劣。框架結構如下圖所示:

深度長文:NLP的巨人肩膀(上)

這個框架最底層是一個 Encoder,也就是最終要獲取的句子向量提取器,然後將得到的句子向量通過一些向量操作後得到句子對的混合語義特徵,最後接上全連線層並做 SNLI 上的三分類任務。

做過句子匹配任務的一定知道,這個框架是一個最基本(甚至也是最簡陋)的句子匹配框架。對於底層的 Encoder 來說,論文作者分別嘗試了 7 種模型,然後分別以這些模型作為底層的 Encoder 結構在 SNLI 上進行監督訓練。

訓練完成後,在新的分類任務上進行評估,最後發現當 Encoder 使用 BiLSTM with max pooling 結構時,對於句子的表徵效能最好。對具體細節感興趣的可以參考他們的論文Supervised Learning of Universal Sentence Representations from Natural Language Inference Data

General Purpose Sentence Representation 

此外,除了 InferSent 這種單個任務的監督學習外,最新的工作逐漸將多工的聯合學習應用到獲取句子的表徵中。

例如 Subramanian 等人發表在 ICLR 2018 上的 Learning General Purpose Distributed Sentence Representations via Large Scale Multi-task Learning,就提出了利用四種不同的監督任務來聯合學習句子的表徵,這四種任務分別是:Natural Language Inference,Skip-thougts,Neural Machine Translation 以及 Constituency Parsing 等。

作者的出發點也特別簡單,通用的句子表徵應該通過側重點不同的任務來聯合學習到,而不是隻有一個特定任務來學習句子表徵,後來作者在論文中的實驗也確實證明了這點。

實驗的具體做法是,先用聯合學習的方法在上述四個任務上進行訓練,訓練結束後,將模型的輸出作為句子的表徵(或者把這個聯合學習的模型作為特徵提取器),然後直接在這個表徵上接上非常簡單的全連線層做分類器,並且同時保證最底層的特徵提取器中引數不動(也就是隻把它當做特徵提取器),再在新的分類任務上做訓練(只訓練最後接上的全連線層分類器),最後根據訓練出來的簡單分類器在各自分類任務的測試集上做評估。

最後作者驚喜的發現很多工上他們的簡單分類器都要超過當時的最好結果,並且他們還發現聯合訓練中不同的任務對於句子表徵中的不同方面有不同的貢獻。

Universal Sentence Encoder 

同樣在 2018 年,谷歌的 Daniel Cer 等人在論文 Universal Sentence Encoder 中提出的思路基本和 General Purpose Sentence Representation 的工作一樣,只不過作者提出了利用 Transformer 和 DAN(上文提到過的和 CBOW 與 fastText 都神似的 Deep Unordered Composition Rivals Syntactic Methods for Text Classification)兩種框架作為句子的 Encoder。

Transformer 結構更為複雜,引數更多,訓練也相對比較耗時,但是一般來說效果會更好一些。對應的,DAN 結構簡單,只有兩個隱藏層(甚至可以減小為只需要一個隱藏層),引數比較少,訓練相對比較省時省資源,但是一般來說效果會差一些(並不是絕對,論文中也發現某些場景下 DAN 的效果甚至更好)。然後作者既在無標記資料上訓練,也在監督資料上訓練,最後在十個分類任務上進行遷移學習的評估。此外,作者還放出了他們預訓練好的 Encoder,可以供遷移學習的句子特徵提取器使用。

預訓練 Encoder:https://tfhub.dev/google/universal-sentence-encoder/2

深度長文:NLP的巨人肩膀(上)

深度長文:NLP的巨人肩膀(上)

相關文章