機器學習教程:帶你從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 的具體做法都可以在這裡看到。我只簡單總結一下和指出重要的點。

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

3. Feature

  • *Attribute Features
  • 是否包含某個特定的屬性(品牌、尺寸、顏色、重量、內用/外用、是否有能源之星認證等)
  • 這個特定的屬性是否匹配
  • Meta Features
  • 各個文本域的長度
  • 是否包含屬性域
  • 品牌(將所有的品牌做數值離散化)
  • 產品 ID
  • 簡單匹配
  • 搜索詞是否在產品標題、產品介紹或是產品屬性中出現
  • 搜索詞在產品標題、產品介紹或是產品屬性中出現的數量和比例
  • *搜索詞中的第 i 個詞是否在產品標題、產品介紹或是產品屬性中出現
  • 搜索詞和產品標題、產品介紹以及產品屬性之間的文本相似度
  • BOWCosine Similairty
  • TF-IDF Cosine Similarity
  • Jaccard Similarity
  • *Edit Distance
  • Word2Vec 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 有以下四個:

  • RandomForestRegressor
  • ExtraTreesRegressor
  • GradientBoostingRegressor
  • XGBRegressor

第二層的 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

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

7.2 Issues Raised

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

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

7.3 Beginner Tips

給新手的一些建議:

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

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

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


分享到:


相關文章: