機器學習教程:帶你從Kaggle入門到殺入前5%(下)

Introduction

(點擊可閱讀)中我們按照一次完整比賽流程的順序介紹了一些Kaggle比賽技巧.在這篇文章中將結合以上技巧介紹一次完整的進入前5%的Kaggle參賽經歷.參加的比賽名字叫做

Home Depot Search Relevance, 代碼已經上傳至github,關注我頭條號後發送私信"Kaggle"獲取代碼.

首先簡單介紹這個比賽。Task 是判斷用戶搜索的關鍵詞和網站返回的結果之間的相關度有多高。相關度是由 3 個人類打分取平均得到的,每個人可能打 1 ~ 3 分,所以這是一個迴歸問題。數據中包含用戶的搜索詞,返回的產品的標題和介紹,以及產品相關的一些屬性比如品牌、尺寸、顏色等。使用的評分基準是 RMSE。

這個比賽非常像 Crowdflower Search Results Relevance 那場比賽。不過那邊用的評分基準是 Quadratic Weighted Kappa,把 1 誤判成 4 的懲罰會比把 1 判成 2 的懲罰大得多,所以在最後 Decode Prediction 的時候會更麻煩一點。除此以外那次比賽沒有提供產品的屬性。

1. EDA

由於加入比賽比較晚,當時已經有相當不錯的 EDA 了。尤其是這個。從中我得到的啟發有:

同一個搜索詞/產品都出現了多次,數據分佈顯然不 i.i.d.。文本之間的相似度很有用。產品中有相當大一部分缺失屬性,要考慮這會不會使得從屬性中得到的 Feature 反而難以利用。產品的 ID 對預測相關度很有幫助,但是考慮到訓練集和測試集之間的重疊度並不太高,利用它會不會導致 Overfitting?

2. Preprocessing

這次比賽中我的 Preprocessing 和 Feature Engineering 的具體做法都可以在這裡看到。我只簡單總結一下和指出重要的點。

利用 Forum 上的 Typo Dictionary 修正搜索詞中的錯誤。統計屬性的出現次數,將其中出現次數多又容易利用的記錄下來。將訓練集和測試集合並,並與產品描述和屬性 Join 起來。這是考慮到後面有一系列操作,如果不合並的話就要重複寫兩次了。對所有文本能做 StemmingTokenizing,同時手工做了一部分格式統一化(比如涉及到數字和單位的)同義詞替換

3. Feature

*Attribute Features是否包含某個特定的屬性(品牌、尺寸、顏色、重量、內用/外用、是否有能源之星認證等)這個特定的屬性是否匹配Meta Features各個文本域的長度是否包含屬性域品牌(將所有的品牌做數值離散化)產品 ID簡單匹配搜索詞是否在產品標題、產品介紹或是產品屬性中出現搜索詞在產品標題、產品介紹或是產品屬性中出現的數量和比例*搜索詞中的第 i 個詞是否在產品標題、產品介紹或是產品屬性中出現搜索詞和產品標題、產品介紹以及產品屬性之間的文本相似度BOWCosine SimilairtyTF-IDF Cosine SimilarityJaccard Similarity*Edit DistanceWord2Vec Distance(由於效果不好,最後沒有使用,但似乎是因為用的不對)
Latent Semantic Indexing:通過將 BOW/TF-IDF Vectorization 得到的矩陣進行 SVD 分解,我們可以得到不同搜索詞/產品組合的 Latent 標識。這個 Feature 使得 Model 能夠在一定程度上對不同的組合做出區別,從而解決某些產品缺失某些 Feature 的問題。

值得一提的是,上面打了 * 的 Feature 都是我在最後一批加上去的。問題是,使用這批 Feature 訓練得到的 Model 反而比之前的要差,而且還差不少。我一開始是以為因為 Feature 的數量變多了所以一些參數需要重新調優,但在浪費了很多時間做 Grid Search 以後卻發現還是沒法超過之前的分數。這可能就是之前提到的 Feature 之間的相互作用導致的問題。當時我設想過一個看到過好幾次的解決方案,就是將使用不同版本 Feature 的 Model 通過 Ensemble 組合起來。但最終因為時間關係沒有實現。事實上排名靠前的隊伍分享的解法裡面基本都提到了將不同的 Preprocessing 和 Feature Engineering 做 Ensemble 是獲勝的關鍵。

4. Model

我一開始用的是 RandomForestRegressor,後來在 Windows 上折騰 Xgboost 成功了就開始用 XGBRegressor。XGB 的優勢非常明顯,同樣的數據它只需要不到一半的時間就能跑完,節約了很多時間。

比賽中後期我基本上就是一邊臺式機上跑

Grid Search,一邊在筆記本上繼續研究 Feature。

這次比賽數據分佈很不獨立,所以期間多次遇到改進的 Feature 或是 Grid Search 新得到的參數訓練出來的模型反而 LB 分數下降了。由於被很多前輩教導過要相信自己的 CV,我的決定是將 5-Fold 提到 10-Fold,然後以 CV 為標準繼續前進。

5. Ensemble

最終我的 Ensemble 的 Base Model 有以下四個:

RandomForestRegressorExtraTreesRegressorGradientBoostingRegressorXGBRegressor

第二層的 Model 還是用的 XGB

因為 Base Model 之間的相關都都太高了(最低的一對也有 0.9),我原本還想引入使用 gblinear 的 XGBRegressor 以及 SVR,但前者的 RMSE 比其他幾個 Model 高了 0.02(這在 LB 上有幾百名的差距),而後者的訓練實在太慢了。最後還是隻用了這四個。

值得一提的是,在開始做

Stacking 以後,我的 CV 和 LB 成績的提高就是完全同步的了。

在比賽最後兩天,因為身心疲憊加上想不到還能有什麼顯著的改進,我做了一件事情:用 20 個不同的隨機種子來生成 Ensemble,最後取 Weighted Average。這個其實算是一種變相的 Bagging。其意義在於按我實現 Stacking 的方式,我在訓練 Base Model 時只用了 80% 的訓練數據,而訓練第二層的 Model 時用了 100% 的數據,這在一定程度上增大了 Overfitting 的風險。而每次更改隨機種子可以確保每次用的是不同的 80%,這樣在多次訓練取平均以後就相當於逼近了使用 100% 數據的效果。這給我帶來了大約 0.0004 的提高,也很難受說是真的有效還是隨機性了。

比賽結束後我發現我最好的單個 Model 在 Private LB 上的得分是 0.46378,而最終 Stacking 的得分是 0.45849。這是 174 名和 98 名的差距。也就是說,我單靠 Feature 和調參進到了 前 10%,而 Stacking 使我進入了前 5%。

6. Lessons Learned

比賽結束後一些隊伍分享了他們的解法,從中我學到了一些我沒有做或是做的不夠好的地方:

產品標題的組織方式是有 Pattern 的,比如一個產品是否帶有某附件一定會用 With/Without XXX 的格式放在標題最後。使用外部數據,比如 WordNet,Reddit 評論數據集等來訓練同義詞和上位詞(在一定程度上替代 Word2Vec)詞典。基於字母而不是單詞的 NLP Feature。這一點我讓我十分費解,但請教以後發現非常有道理。舉例說,排名第三的隊伍在計算匹配度時,將搜索詞和內容中相匹配的單詞的長度也考慮進去了。這是因為他們發現越長的單詞約具體,所以越容易被用戶認為相關度高。此外他們還使用了逐字符的序列比較(difflib.SequenceMatcher),因為這個相似度能夠衡量視覺上的相似度。像這樣的 Feature 的確不是每個人都能想到的。標註單詞的詞性,找出中心詞,計算基於中心詞的各種匹配度和距離。這一點我想到了,但沒有時間嘗試。將產品標題/介紹中 TF-IDF 最高的一些 Trigram 拿出來,計算搜索詞中出現在這些 Trigram 中的比例;反過來以搜索詞為基底也做一遍。這相當於是從另一個角度抽取了一些 Latent 標識。一些新穎的距離尺度,比如 Word Movers Distance除了 SVD 以外還可以用上 NMF。最重要的 Feature 之間的 Pairwise Polynomial Interaction
針對數據不 i.i.d. 的問題,在 CV 時手動構造測試集與驗證集之間產品 ID 不重疊和重疊的兩種不同分割,並以與實際訓練集/測試集的分割相同的比例來做 CV 以逼近 LB 的得分分佈

至於 Ensemble 的方法,我暫時還沒有辦法學到什麼,因為自己只有最簡單的 Stacking 經驗。

7. Summary

7.1 Takeaways

比較早的時候就開始做 Ensemble 是對的,這次比賽到倒數第三天我還在糾結 Feature。很有必要搭建一個 Pipeline,至少要能夠自動訓練並記錄最佳參數。Feature 為王。我花在 Feature 上的時間還是太少。可能的話,多花點時間去手動查看原始數據中的 Pattern。

7.2 Issues Raised

我認為在這次比賽中遇到的一些問題是很有研究價值的:

在數據分佈並不 i.i.d. 甚至有 Dependency 時如何做靠譜的 CV。如何量化 Ensemble 中 Diversity vs. Accuracy 的 Trade-off。如何處理 Feature 之間互相影響導致性能反而下降。

7.3 Beginner Tips

給新手的一些建議:

選擇一個感興趣的比賽。如果你對相關領域原本就有一些洞見那就更理想了。
根據我描述的方法開始探索、理解數據並進行建模。通過 Forum 和 Scripts 學習其他人對數據的理解和構建 Feature 的方式。如果之前有過類似的比賽,可以去找當時獲獎者的 Interview 和 Blog Post 作為參考,往往很有用。在得到一個比較不錯的 LB 分數(比如已經接近前 10%)以後可以開始嘗試做 Ensemble。如果覺得自己有希望拿到獎金,開始找人組隊吧!到比賽結束為止要繃緊一口氣不能斷,儘量每天做一些新嘗試。比賽結束後學習排名靠前的隊伍的方法,思考自己這次比賽中的不足和發現的問題,可能的話再花點時間將學到的新東西用實驗進行確認,為下一次比賽做準備。好好休息!

相關實驗代碼已經上傳至github,關注我頭條號後發送私信"Kaggle"獲取代碼.