NLP大賽冠軍總結:300萬知乎多標籤文本分類任務(附深度學習源碼)

NLP大賽冠軍總結:300萬知乎多標籤文本分類任務(附深度學習源碼)


 
 
 

 

 

七月,酷暑難耐,認識的幾位同窗參加知乎看山杯,均取得不錯的排名。當時天池AI醫療大賽初賽結束,官方正在爲複賽進行平臺調試,複賽時間一拖再拖。看着幾位同窗在比賽中排名都還很不錯,因而決定抽空試一試。結果一發不可收拾,又找了兩個同窗一塊兒組隊(隊伍init)以致於整個暑假都投入到這個比賽之中,並最終以必定的優點奪得第一名。html

1. 比賽介紹

這是一個文本多分類的問題:目標是「參賽者根據知乎給出的問題及話題標籤的綁定關係的訓練數據,訓練出對未標註數據自動標註的模型」。通俗點講就是:當用戶在知乎上提問題時,程序要可以根據問題的內容自動爲其添加話題標籤。一個問題可能對應着多個話題標籤,以下圖所示。python

 

這是一個文本多分類,多label的分類問題(一個樣本可能屬於多個類別)。總共有300萬條問題-話題對,超過2億詞,4億字,共1999個類別。架構

1.1 數據介紹

參考 https://biendata.com/competition/zhihu/data/機器學習

https://biendata.com/competition/zhihu/rules/?next_url=%2Fcompetition%2Fzhihu%2Fdata%2F函數

 

總的來講就是:工具

  • 數據通過脫敏處理,看到的不是「如何評價2017知乎看山杯機器學習比賽」,而是「w2w34w234w54w909w2343w1'這種通過映射的詞的形式,或者是」c13c44c4c5642c782c934c02c2309c42c13c234c97c8425c98c4c340'這種通過映射的字的形式。學習

  • 由於詞和字通過脫敏處理,因此沒法使用第三方的詞向量,官方特意提供了預訓練好的詞向量,即char_embedding.txt和word_embedding.txt ,都是256 維。測試

  • 主辦方提供了1999個類別的描述和類別之間的父子關係(好比機器學習的父話題是人工智能,統計學和計算機科學),但這個知識沒有用上。優化

  • 訓練集包含300萬條問題的標題(title),問題的描述(description)和問題的話題(topic)人工智能

  • 測試集包含21萬條問題的標題(title),問題的描述(description),須要給出最有可能的5個話題(topic)

1.2 數據處理

數據處理主要包括兩部分:

  • char_embedding.txt 和 word_embedding.txt 轉爲numpy格式,這個很簡單,直接使用word2vec的python工具便可

  • 對於不一樣長度的問題文本,pad和截斷成同樣長度的(利用pad_sequence 函數,也能夠本身寫代碼pad)。過短的就補空格,太長的就截斷。操做圖示以下:

 

 

 

 

1.3 數據加強

文本中數據加強不太常見,這裏咱們使用了shuffle和drop兩種數據加強,前者打亂詞順序,後者隨機的刪除掉某些詞。效果舉例如圖:

 

 

 

1.4 評價指標

每一個預測樣本,提供最有可能的五個話題標籤,計算加權後的準確率和召回率,再計算F1值。注意準確率是加權累加的,意味着越靠前的正確預測對分數貢獻越大,同時也意味着準確率可能高於1,可是F1值計算的時候分子沒有乘以2,因此0.5是很難達到的。

 

2 模型介紹

建議你們先閱讀這篇文章,瞭解文本多分類問題幾個經常使用模型:用深度學習(CNN RNN Attention)解決大規模文本分類問題

https://zhuanlan.zhihu.com/p/25928551

 

2.1 通用模型結構

文本分類的模型不少,此次比賽中用到的模型基本上都遵循如下的架構:

 

 

 

 

 

基本思路就是,詞(或者字)通過embedding層以後,利用CNN/RNN等結構,提取局部信息、全局信息或上下文信息,利用分類器進行分類,分類器的是由兩層全鏈接層組成的。

在開始介紹每一個模型以前,這裏先下幾個結論:

  1. 若是你的模型分數不夠高,試着把模型變得更深更寬更復雜

  2. 當模型複雜到必定程度的時候,不一樣模型的分數差距很小

  3. 當模型複雜達到必定程度,繼續變複雜難以繼續提高模型的分數

 

2.2 TextCNN

這是最經典的文本分類模型,這裏就不細說了,模型架構以下圖:

 

 

 

 

和原始的論文的區別就在於:

  • 使用兩層卷積

  • 使用更多的卷積核,更多尺度的卷積核

  • 使用了BatchNorm

  • 分類的時候使用了兩層的全鏈接

總之就是更深,更復雜。不過卷積核的尺寸設計的不夠合理,致使感覺野差距過大。

2.3 TextRNN

沒找到論文,我就憑感受實現了一下:

 

 

 

 

 

相比於其餘人的作法,這裏的不一樣點在於:

  • 使用了兩層的雙向LSTM。

  • 分類的時候不是隻使用最後一個隱藏元的輸出,而是把全部隱藏元的輸出作K-MaxPooling再分類。

2.4 TextRCNN

參考原論文的實現,和RNN相似,也是兩層雙向LSTM,可是須要和Embedding層的輸出Concat(相似於resnet的shortcut直連)。

 

 

 

 

2.5 TextInception

這個是我本身提出來的,參照TextCNN的思想(多尺度卷積核),模仿Inception的結構設計出來的,一層的Inception結構以下圖所示,比賽中用了兩層的Inception結構,最深有4層卷積,比TextCNN更深。

 

 

 

 

2.6 訓練方法

要點:

  • 基於詞和基於字的模型要分開訓,而後融合,一塊兒訓的效果很差

  • 使用官方給的word-embedding.txt和char-embedding.txt初始化Embedding層的權重

  • 剛開始訓練的時候Embedding層的學習率爲0,其它層的學習率爲1e-3,採用Adam優化器(一開始的時候卷積層都是隨機初始化的,反向傳播獲得的Embedding層的梯度受到卷積層的影響,至關於噪聲)

  • 訓練1-2個epoch以後,Embedding層的學習率設爲2e-4

  • 每一個epoch或者半個epoch統計一次在驗證集的分數

    • 若是分數上升,保存模型,並記下保存路徑

    • 若是分數降低,加載上一個模型的保存路徑,並下降學習率爲一半(從新初始化優化器,清空動量信息,而不是隻修改學習率----使用PyTorch的話新建一個新優化器便可)

2.7 各個模型分數計算

訓練的時候,每一個模型要麼只訓練基於詞(word)的模型,要麼只訓練基於字(char)的模型。各個模型的分數都差很少,這裏再也不單獨列出來了,只區分訓練的模型的類型和數據加強與否。

 

 

 

 

能夠看出來

  • 基於詞的模型效果遠遠好於基於字的(說明中文分詞頗有必要)。

  • 數據加強對基於詞(word)的模型有必定的提高,可是對於基於字(char)的模型主要是起到反作用。

  • 各個模型之間的分數差距不大。

 

2.8 模型融合

像這種模型比較簡單,數據量相對比較小的比賽,模型融合是比賽獲勝的關鍵。

在這裏,我只使用到了最簡單的模型融合方法-----機率等權重融合。對於每一個樣本,單模型會給出一個1999維的向量,表明着這個模型屬於1999個話題的機率。融合的方式就是把每個模型輸出的向量直接相加,而後選擇機率最大的5個話題提交。結構如圖所示:

 

 

 

 

下面咱們再來看看兩個模型融合的分數:

 

 

 

 

第一列的對比模型採用的是RNN(不採用數據加強,使用word做爲訓練數據),第二列是四個不一樣的模型(不一樣的結構,或者是不一樣的數據)。

咱們能夠得出如下幾個結論:

  • 從第一行和第二行的對比之中咱們能夠看出,模型差別越大提高越多(RNN和RCNN比較類似,由於他們底層都採用了雙向LSTM提取特徵),雖然RCNN的分數比Inception要高,Inception對模型融合的提高更大。

  • 從第一行和第四行的對比之中咱們能夠看出,數據的差別越大,融合的提高越多,雖然基於字(char)訓練的模型分數比較低,可是和基於詞訓練的模型進行融合,仍是能有極大的提高。

  • 採用數據加強,有助於提高數據的差別性,對模型融合的提高幫助也很大。

總結: 差別性越大,模型融合效果越好。沒有差別性,創造條件也要製造差別性。

另外模型融合還有個規律:越往上越難提高,有些模型在你分數較低的時候,對融合提高很明顯,當你分數較高的時候就沒什麼幫助,甚至會有干擾

2.9 MultiModel

其實模型融合的方式,咱們換一種角度考慮,其實就是一個很大的模型,每個分支就像多通道的TextCNN同樣。那麼咱們能不能訓練一個超級大的模型?答案是能夠的,可是效果每每不好。由於模型過於複雜,太難以訓練。這裏我嘗試了兩種改進的方法。

第一種方法,利用預訓練好的單模型初始化複雜模型的某一部分參數,模型架構如圖所示:

 

 

 

 

 

可是這種作法會帶來一個問題: 模型過擬合很嚴重,難以學習到新的東西。由於單模型在訓練集上的分數都接近0.5,已經逼近理論上的極限分數,這時候很難接着學習到新的內容。這裏採起的應對策略是採用較高的初始學習率,強行把模型從過擬合點拉出來,使得模型在訓練集上的分數迅速下降到0.4左右,而後再下降學習率,緩慢學習,提高模型的分數。

第二種作法是修改預訓練模型的embedding矩陣爲官方給的embedding權重。這樣共享embedding的作法,可以必定程度上抑制模型過擬合,減小參數量。雖然CNN/RNN等模型的參數過擬合,可是因爲相對應的embedding沒有過擬合,因此模型一開始分數就會降低許多,而後再緩慢提高。這種作法更優。在最後提交模型復現成績的時候,我只提交了七個這種模型,裏面包含着不一樣子模型的組合,通常包含3-4個子模型。這種方式生成的權重文件也比較小(600M-700M左右),上傳到網盤相對來講更方便。

 

 

 

 

2.10 失敗的模型或沒什麼用的方法

MultiMode只是我諸多嘗試的方法中比較成功的一個,其它方法大多以失敗了結(或者效果不明顯)

  • 數據多折訓練:由於過擬合嚴重,想着先拿一半數據訓,容許它充分過擬合,而後再拿另一半數據訓。效果不如以前的模型。

  • Attention Stack,參考了這篇文章,其實本質上至關於調權重,可是效果有限,還麻煩,因此最後直接用等權重融合(權重全設爲1)。

     

  • Stack,太費時費力,浪費了很多時間,也有多是實現有誤,提高有限,沒有繼續研究下去。

  • Boost,和第二名Koala的方法很像,先訓一個模型,而後再訓第二個模型和第一個模型的輸出相加,可是固定第一個模型的參數。至關於不停的修正上一個模型誤判的(能夠嘗試計算一下梯度,你會發現第一個模型已經判對的樣本,即便第二個模型判別錯了,第二個模型的梯度也不會很大,即第二個模型不會花費太多時間學習這個樣本)。可是效果很差,緣由:過擬合很嚴重,第一個模型在訓練集上的分數直接就逼近0.5,致使第二個模型什麼都沒學到。Koala隊伍最終就是憑藉着這個Boost模型拿到了第二名,我過早放棄,沒能在這個方法上有所突破十分遺憾。

  • TTA(測試時數據加強),至關於在測試的時候人爲的製造差別性,對單模型的效果通常,對融合幾乎沒有幫助。

  • Hyperopt進行超參數查詢,主要用來查詢模型融合的權重,效果通常,最後就也沒有使用了,就手動稍微調了一下。

  • label設權重,對於正樣本給予更高的權重,訓練模型,而後和正常權重的模型進行融合,在單模型上可以提高2-3個千分點(十分巨大),可是在最後的模型融合是效果頗有限(0.0002),並且須要調整權重比較麻煩,遂捨棄。

  • 用分類獲得的詞向量做爲下一個模型的embedding的初始值,由於官方給的word embedding是用無監督的word2vec訓練的,和有監督的分類問題仍是有必定誤差的。沒有深刻研究下去,對單模型應該是有提高,可是對融合可能沒什麼幫助。

3 結束語

我以前雖然學過CS224D的課程,也作了前兩次的做業,可是除此以外幾乎歷來沒寫過天然語言處理相關的代碼,能拿第一離不開隊友的支持,和同窗們不斷的激勵。

此次比賽入門對我幫助最大的兩篇文章是用深度學習(CNN RNN Attention)解決大規模文本分類問題

https://zhuanlan.zhihu.com/p/25928551

和deep-learning-nlp-best-practices

http://ruder.io/deep-learning-nlp-best-practices/index.html

第一篇是北郵某學長(但我並不認識~)寫的,介紹了許多文本分類的模型(CNN/RNN/RCNN),對我入門幫助很大。

第二篇是國外某博士寫的,當時我已經把分數刷到前三,在家看到了這篇文章,歎爲觀止,解釋了我不少的疑惑,提到的不少經驗總結和個人狀況也確實相符。https://zhuanlan.zhihu.com/p/28923961

相關文章
相關標籤/搜索