轉自我的部落格。

本部落格記錄了對“問題相似度計算”這個問題的初步探索嘗試,分享了自己參加比賽:拍拍貸——第三屆魔鏡杯大賽的經歷和一些心得,介紹了

基於規則

基於深度學習

兩個角度的方法,希望對讀者能有所啟發。這個比賽參賽的選手很多,牛人也很多,筆者最終只取得了34名的成績,剛剛進入前10%(共有350多隻隊伍提交了結果)。

0。 背景

“問題相似度計算”這個問題,顧名思義,就是判斷兩個問題是否表達相同的含義,是NLP領域很重要的一個任務(雖然很重要,但是我之前從來沒有實際接觸過,囧)。早期在問答(QA)任務,特別是社群問答(CQA,比如百度知道、知乎等)中,這個任務非常重要,由於自然語言的靈活性,不同的人對於相同的問題可能使用不同的表述方式,那麼對於某個使用者提出的問題,可以使用“問題相似度計算”技術,和所有的已有問題進行計算,如果已有問題中存在某個問題和使用者提出的問題意思是一樣的,那麼那個已有問題的答案就是使用者需要的答案。

近年來,我們國家的服務業在不斷髮展,客服成了我們日常生活中經常打交道的物件,在客服場景中,“問題相似度計算”這個技術也是非常核心的技術,一方面這個技術是實現智慧客服的基礎,另一方面,這個技術也可以幫助人工客服實現使用者問題和標準問題庫中相似問題的快速匹配,從而為客戶提供更好的服務。而我這次參加的比賽,就是在金融客服領域的“問題相似度計算”任務。 事實上,在魔鏡杯大賽進行的同時,阿里也釋出了一個“金融大腦-金融智慧NLP服務”比賽,其任務也是“問題相似度計算”, 從這裡也可以看出這個任務在實際商業場景中的重要性。

下面,我們就簡單介紹一下魔鏡杯比賽的基本情況。

1。 任務

1。1 任務定義

魔鏡杯比賽的任務非常明確,就是給定一個句子q1和另一個句子q2,系統自動判斷這兩個句子的含義是相同(label=1)還是不同(label=0)。

1。2 資料集

為了讓選手可以訓練問題相似度計算模型,魔鏡杯釋出了包含25萬多個訓練樣本的訓練集,每個訓練樣本由(label, q1, q2)組成。訓練樣本中的正例(label=1,也就是說兩個句子含義相同) 和負例(label=0,兩個句子含義不同)的數目分別是12萬多和13萬多,基本平衡。而魔鏡杯釋出的測試集包含17萬多個樣本,只給出了q1,q2,需要選手自動輸出label。

對於這種比賽,資料保護是主辦方要考慮的一個問題,不同的比賽風格不同,比如我上一篇部落格提到的汽車大師比賽,是利用虛擬化技術,資料放在雲端上,無法下載到本地,因此也必須提供雲端計算資源。而魔鏡杯的方法我感覺非常有特色,那就是先把資料進行“加密”然後再公開,允許選手把資料下載到本地。

下圖

是魔鏡杯釋出的資料形式,訓練集和測試集中的句子並不是以自然語言文字的形式給出的,而是給出了id化的詞序列和字序列兩種方式,詞序列由詞id組成,字序列由字id組成,無法直接看懂原句到底是什麼。魔鏡杯提供了2萬個詞左右的詞典和3千個字左右的字典,並且提供了300維的詞向量和300維的字向量。

問題相似度計算”問題初探

變態的文字資料表示

2。 思路

看到這個問題,我產生了兩方面的思路,一是規則方法,二是機器學習方法。首先來看規則方法:

2。1 規則方法

本文所謂規則方法,是指不利用統計學習方法,而僅利用“含義相同”和“含義不同”這兩種關係的特點進行推理的方法。

“含義相同” 和 “含義不同” 在本任務中都屬於二元關係,可以作用於兩個問題例項q1和q2,構成一個三元組(q1, r, q2),這裡指定r1表示“含義相同”,r0表示“含義不同”。下面我們可以討論下r1和r0的性質。

性質1: r1和r0都有對稱性:即 (q1,r1,q2) 可以匯出 (q2,r1,q1),縮寫為 (q1,r1,q2) => (q2,r1,q1); 同樣, (q1,r0,q2) => (q2,r0,q1)。 這個很好理解。

性質2: r1有傳遞性: (q1,r1,q2) and (q2,r1,q3) => (q1,r1,q3),這個也很好理解,如果q1和q2含義相同,q2和q3含義相同,那麼q1和q3含義也相同,事實上這時q1,q2,q3構成了一個同義集, 集合中任意兩個問句的含義都相同。

性質3: r0有

特殊

的傳遞性(不知道這個性質較叫什麼): (q1,r1,q2) and (q2,r0,q3) => (q1,r0,q3),解釋一下,如果q1和q2含義相同,但是q2和q3含義不同,那麼q1和q3的含義也就不同,其實也很容易理解。

好了,在明確了r1和r0的特殊性質後,我們可以利用其性質,進行“關係補全”。訓練集實際上提供了許多問題例項以及這些例項之間的關係,利用上述的性質2和性質3,我們可以基於訓練集給出的例項和部分例項之間的關係,補全更多的例項之間的關係,具體做法可以分為兩步:

利用性質2,尋找同義集合:對於訓練集中所有“含義相同”的訓練樣本,可以根據性質2,將問句聚合成為許多個同義集合,同義集合中任意兩個問句的含義都相同;

利用性質3,尋找同義集合間的“含義不同關係”: 這個話說起來有些繞,但是其實也很好理解,舉個例子。對於兩個同義集合S1和S2,我們一開始不知道這兩個集合之間的關係,如果在訓練集中我們發現了一個樣本(q1, r1, q2),並且q1屬於S1,q2屬於S2,那麼根據性質2,我們可以匯出S1和S2可以合併為一個大的同義集合;那麼如果在訓練集中我們發現了一個樣本(q1, r0, q2),並且q1屬於S1,q2屬於S2,那麼根據性質3,實際上S1和S2之間就存在“含義不同”的關係,對於S1中的任意一個問句q1‘和S2中的任意一個問句q2’,都存在“含義不同的關係”:(q1‘, r0, q2’)。

透過上述這兩步,我們可以得到很多同義集合S1,S2,。。。, 同時還知道了一些同義集合之間存在“含義不同”關係。這時,對於測試集中的一個樣本q1, q2,我們就可以利用上述資訊,進行判斷了:

if q1, q2屬於同一個同義集合:

return 1 #含義相同

else:

# 假定 q1屬於S1, q2屬於S2

if (S1, r0, S2):

return 0 #含義不同

else:

return -1 #我不知道

這時可能會有同學說,哎,你這個規則方法不行啊,存在“我不知道”的情況呢,那確實,所以才需要基於統計學習的方法嘛。

在比賽中,我使用基於規則的方法對基於統計機器學習方法輸出的結果進行修正,如果基於規則的方法知道答案,那就用規則方法的答案,否則就使用統計方法的答案。

透過統計,基於規則的方法可以處理測試集中

10.54%

的樣本,這個比例不算低了吧。

2。2 機器學習方法

之前提到過,我並不是很熟悉這個任務, 碰巧前段時間看過FAIR的一篇利用SNLI資料集有監督地訓練句子向量表示的論文“Supervised Learning of Universal Sentence Representations from Natural Language Inference Data”, 感覺任務比較類似,所以就以這個論文的方法為基礎進行了嘗試。

SNLI任務是判斷兩個句子間的語義關係的,比如蘊含(entailment)、矛盾(contradiction)、中性(neutral)。上述的論文使用的方法是這樣,先使用encoder對兩個句子進行編碼,得到向量表示u,v, 然後將u,v, |u-v|, u*v 這四個向量連起來,經過一個MLP網路,輸出三分類。結果如下圖所示。

問題相似度計算”問題初探

論文對比了多種encoder的效果, 包括:

LSTM/GRU-last:使用LSTM或GRU對句子進行編碼,然後選最後一個時刻的狀態作為句向量表示, 可以單向也可以雙向

BiLSTM with mean/max pooling: 使用雙向LSTM對句子進行編碼,然後透過mean/max pooling 得到句子的定長向量表示

Self Attention: 在BiLSTM的結果基礎上,使用self-attention機制作為pooling方法得到句子的定長向量表示

Hierarchical ConvNet: 使用深層(4層)CNN網路對句子進行編碼, 然將每層經過max pooling後的定長向量連起來,作為句子的向量表示。

透過實驗,該論文給出的結論是:

BiLSTM + max pooling 效果最好。

這篇論文在GitHub上有官方的開原始碼InferSent, 那麼直接拿來用就好了。

在上述程式碼基礎上,我做的工作有: 1。 原始碼使用GloVe (V1) or fastText 作為預訓練的embedding,並且程式碼中並沒有finetune embedding, 我嘗試加入了finetune embedding,結果效果並不好; 2。 由於這個網路結果對於q1和q2來說不是對稱的,因此我在訓練時會隨機調整q1和q2的順序; 3。 在魔鏡資料集上測試了各個encoder的效能,結果發現“BLSTMprojEncoder + max pooling” 效果最好,BLSTMprojEncoder的主要區別是加入了一個線性層將BiLSTM的狀態進行了對映,然後再經過max/mean pooling; 4。 對LSTM的層數設定進行了嘗試,發現2層效果會更好,但是3層的效果會下降。 5。 之前使用的都是基於詞方法,我同時嘗試了基於字的方法,發現效果差不多; 6。 加入cross validation, 將訓練集分成了10份, 以9份作為訓練集, 1份作為開發集, 訓練了10個模型, 然後ensemble。

3。 程式碼

我的程式碼也公佈在了GitHub上。

最終我的結果是三個模型ensemble的結果,這三個模型分別是: 1。 2layer BLSTMprojEncoder + word feature + maxpooling + cv 2。 1layer BLSTMprojEncoder + word feature + maxpooling + cv 3。 1layer BLSTMprojEncoder + char feature + maxpooling + cv

當然最後的結果還使用規則系統的方法修正了一下。

4。 心得

下面總結一下吧:

1。 由於我的程式碼設計的有問題,只能要麼使用word feature,要麼使用char feature, 只能在ensemble時融合兩種資訊,可能在一個模型裡融合這兩種資訊效果會更好;

2。 另一個問題是由於對InferSent程式碼的修改非常少,只能用max pooling或者mean pooling,感覺如果max pooling和mean pooling的結果連線起來一起用可能會更好;

3。 在調優時,我一開始使用SGD, 發現SGD對於不同的batch size效果不同,我掃描搜尋了一下發現batch size不能太大,大概是128左右; 後來嘗試了一下ADAM, 發現正好相反, ADAM對於大的batch size 效果比較好, 而小batch size 效果反而差。 這個結論不知道對於其他資料集或者任務是否適用。

4。 可能有同學發現了,基於規則的方法實際上可以增加了訓練樣本,是的,但是由於我是利用業餘時間隨便搞搞,在最後一天草草嘗試了一下資料擴充發現沒有取得很好的效果,事後感覺應該早一點試試資料擴充的,可能能進一步提高結果。