0.前言
二月份上旬參加了一個關于景區評分預測的競賽,即根據游客對某景區的評論,預測該游客給該景區打幾分。比賽還在繼續,感興趣的朋友點這里。
這是我第一次接觸中文文本處理,半個月來最大的收獲是了解了很多和NLP相關的知識,并且在各種試錯中學到了一些對個人而言比較有用的建模經驗。
雖然目前的模型和排在前列的隊伍還有很大差距,但能夠在2周時間里將模型得分提升近10%,并且一度沖進排行榜前8%(排名時刻在變,現在大概已經不是這個名次),我已經很開心了。
比賽還未結束,但我決定就此打住了。一來我參賽的主要目的是學習,而現在我已經學到了很多東西;二來我已經看到了自己目前的局限以及和大神之間的差距,就算再繼續下去模型性能也難以有質的飛躍;三來是心疼自己的電腦,模型再跑下去我怕本子會爆炸。
但,在此之前我需要做個總結。
這篇文章主要記錄自己的建模思路,以及在數據處理、建模過程中得到的一些經驗。文章結構如下:
- 建模總結,包括:1)使用的庫,2)特征工程,3)模型結構,4)反思
- 數據處理和建模過程中學到的一些小tip
- 對評論進行探索性分析時發現的一些有趣的小事
1. 建模總結
1.1 使用的庫
- 使用軟件:
Python 3.6
- 基本數據處理:
pandas
,numpy
- 中文分詞:
jieba
,re
- Word Embeddings:
sklearn
,gensim
- 算法調用:
sklearn
,keras
,xgboost
,lightgbm
1.2 特征工程
原始訓練集中的數據一共只有2列:游客評論(文本)和其對應的打分(1-5分)。
基于這些數據,通過特征工程和Stacking,我的模型里一共用到了75個特征,包括10個人工特征、64個基于Word Embeddings的特征、1個二元分類預測特征。(實際構建的特征不止75個,但最終用到的一共是75個。)
1.2.1 人工特征
這部分的特征提取于文本本身,我用“人工特征”這個詞主要是為了和后面的特征做區分。
10個特征具體說明如下:
- word count:文本里的詞數,比如“我喜歡你”這句話里共有“我”、“喜歡”、“你”3個詞。
- noun ratio:文本里的名詞數量占比。
- verb ratio:文本里的動詞數量占比。
- punctuation ratio:文本里的標點符號數量占比。
- char count:文本字數(不包括標點符號)。
- mean word length:文本里每個詞的平均字數。
- unique word ratio:文本里非重復詞的數量占比,比如“我超級超級喜歡你”這句話里共有“我”、“超級”、“喜歡”、“你”4個非重復詞,那么這句話的非重復詞數量占比為0.8(4/5)。
- pos word ratio:文本里的積極詞數量占比,這里引入了第三方積極詞詞典。
- neg word ratio:文本里的消極詞數量占比,這里引入了第三方消極詞詞典。
- neg pos ratio:文本里的積極詞和消極詞數量比例。
1.2.2 基于Word Embeddings的特征
說到文本處理,Word Embeddings(即“詞嵌入”)是繞不過去的,這部分的特征主要建立在Word Embeddings上。
所使用的Word Embeddings如下:
- 基于詞的Count Vectors
- 基于字符的Count Vectors
- 基于詞的TF-IDF Vectors
- 基于字符的TF-IDF Vectors
- Word2Vec
由于Word Embeddings的結果往往維度過高,不利于做計算,所以我會先用一些簡單算法將其“降維”,將這些算法得到的低維矩陣或預測值作為新特征加入到后續模型中。
這里用到的算法有:
- 樸素貝葉斯(多分類)
- 邏輯回歸(多分類)
- 嶺回歸
- SVD
- LSTM
- fastText
1.2.3 二元分類預測特征
- binary prediction:用上述所有特征和light gbm構建的二元分類預測值。
思路:因為數據集里各個分值的評論分布很不均勻,打分為1或2的評論分別只占了0.5%和1%,在這種數據極度不平衡的情況下,少數類的預測準確率不會太高,于是我想干脆把目標特征簡化成兩類——分數1或2的評論為少數類、其他為多數類——并對這個二元分類做預測,然后將預測結果(評論為少數類的概率)作為新特征加入到后續模型中,這樣也許能改善情況。
1.3 模型結構
從第一個模型到最后一個模型,我大概改了30個版本,以下是最終版本:
順帶說一句,嶺回歸屬于回歸算法,得出的結果為實數,然而目標特征實際上為整數(1-5分),所以最終還需要對預測值進行四舍五入,將實數化整。(當然,四舍五入不一定是最優的處理方式,我這里只是貪圖方便。)
1.4 反思
我的模型得分在排行榜里雖然屬于中等偏上,但和排在前幾位的團隊比起來差距還是不小,思考之后覺得有以下幾個方面需要改進:
文本處理做得不夠精細。因為這個預測模型完全是基于文本之上的,所以文本處理的好壞與否,直接決定特征和模型的質量。在中文文本處理中,(我個人認為)最重要的環節是分詞,這部分我用了結巴分詞,結巴分詞速度很快,但對口語化的文本處理得不是很理想,需要引入第三方詞典(詞典是另一個關鍵點)。因為是第一次接觸中文分詞,經驗不足,所以在分詞部分我沒有做太復雜的處理,因此有可能導致一些信息丟失。
深度學習做得不好。在構建基于Word Embeddings的特征時,我用到了LSTM和fastText,這兩者都屬于深度學習框架下的算法。我個人目前對深度學習的了解還不深,調用算法的時候基本屬于“照著葫蘆畫瓢”,模型框架搭得比較粗糙(心態上也只是“試試看”),所以得出來得結果也不是非常理想。之后還需要花時間對深度學習進行一個系統的學習。
模型結構較亂。仔細看我的模型結構,就會發現整個結構其實挺亂的,因為我并不是一開始就設計好結構,而是邊走邊搭建模型、想到什么做什么,這么做的后果是:當預測結果不理想的時候,回溯找原因會比較費時,改造模型也會比較麻煩,此外,不恰當的特征、算法組合可能會產生不必要的性能損失。所以建模之前,應該要先設計好大致的結構框架。
2. 數據處理與建模小Tip
以下是我在處理數據、建模的過程中,腦子里出現“噢,原來如此!”或者“要是能這樣就好了!”的瞬間,現在總結出來做Tips:
做Word Embeddings的時候,python中無論是
sklearn
、keras
還是別的庫,全都是基于英文文本的,也就是說所輸入的文本應該(或者說最好)是像英文那樣詞與詞之間用空格隔開的。這種情況下,如果想用這些庫給中文文本做Word Embeddings,可以先做分詞,然后把每個詞按順序用空格隔開來模擬英文文本形式,再調用這些庫做進一步的處理。在做中文分詞的時候要考慮好中英混合的情況,中文和英文在分詞處理上有很大的不同,一旦出現中英混合的文本,分詞復雜度就大大增加了。
用
sklearn
做Count Vectors或TF-IDF Vectors的時候,有幾點可以嘗試:1)去掉stop words和不去掉stop words的都來一遍,看哪個效果更好;2)word和char都試試,有時候char比word效果要好;3)n-grams的范圍可以多試試,有時候(1, 1)比較好,有時候(1, n)比較好;4)如果設置max features,數值不要設置得太小。在提升模型分數方面,比起調參,效果更顯著的是改進特征工程和做Stacking。
一定一定要做Stacking,且最后一層的組合模型選用簡單算法效果較好。
Light GBM比XGBoost速度快太多,如果數據量很大,推薦前者而不是后者。
如果預測目標是整數型,且數值范圍不大(比如1-5),分類和回歸都試試,或者二者結合,可能有意想不到的收獲。
對于需要重復調用的代碼,請務必先寫好def。
數據量很大的時候,多線程和分布式是個好東西。(這一點還在努力學習中……)
3. 一些關于評論的有趣小發現
NLP的一個有趣之處在于,通過對文本進行處理,可以讓一些隱藏在文本中的、一眼望過去難以發現的小秘密浮出水面。(這也是我為什么喜歡數據分析的原因。)
比如可以發現一些無效評論:
再比如,我們可以對比一下最高分和最低分評論的關鍵詞(基于TF-IDF):
有時候做預測,反過來還可以發現一些更有趣的事情。
比如發現一些雖然給了高分但其實對景區并不滿意的游客:
對于這些評論,雖然其打分為最高分5分,但根據語境,這些游客大多對景區印象不好。(我的模型也給出了較低分值,說明它對語義的理解還是比較不錯的。)
還有一些迷之低分:
有趣的發現還有不少,感興趣的可以參加比賽自己分析。
4. 結束語
這篇文章總結了我自己參加預測競賽的建模過程和心得,以及分析文本之后得到的一些有趣的小發現。
總的來說,這是一次有趣的嘗試,希望以后遇到類似情況的時候可以少走一些彎路。
以上。