机器学习竞赛技巧
Kaggle?是目前最大的 Data Scientist 聚集地。很多公司會拿出自家的數據并提供獎金,在 Kaggle 上組織數據競賽。我最近完成了第一次比賽,在 2125 個參賽隊伍中排名第 98 位(~ 5%)。因為是第一次參賽,所以對這個成績我已經很滿意了。在 Kaggle 上一次比賽的結果除了排名以外,還會顯示的就是 Prize Winner,10% 或是 25% 這三檔。所以剛剛接觸 Kaggle 的人很多都會以 25% 或是 10% 為目標。在本文中,我試圖根據自己第一次比賽的經驗和從其他 Kaggler 那里學到的知識,為剛剛聽說 Kaggle 想要參賽的新手提供一些切實可行的沖刺 10% 的指導。
本文的英文版見這里。
Kaggler 絕大多數都是用 Python 和 R 這兩門語言的。因為我主要使用 Python,所以本文提到的例子都會根據 Python 來。不過 R 的用戶應該也能不費力地了解到工具背后的思想。
首先簡單介紹一些關于 Kaggle 比賽的知識:
-
不同比賽有不同的任務,分類、回歸、推薦、排序等。比賽開始后訓練集和測試集就會開放下載。
-
比賽通常持續 2 ~ 3 個月,每個隊伍每天可以提交的次數有限,通常為 5 次。
-
比賽結束前一周是一個 Deadline,在這之后不能再組隊,也不能再新加入比賽。所以想要參加比賽請務必在這一 Deadline 之前有過至少一次有效的提交。
-
一般情況下在提交后會立刻得到得分的反饋。不同比賽會采取不同的評分基準,可以在分數欄最上方看到使用的評分方法。
-
反饋的分數是基于測試集的一部分計算的,剩下的另一部分會被用于計算最終的結果。所以最后排名會變動。
-
LB?指的就是在 Leaderboard 得到的分數,由上,有?Public LB?和?Private LB?之分。
-
自己做的 Cross Validation 得到的分數一般稱為?CV?或是?Local CV。一般來說?CV?的結果比?LB?要可靠。
-
新手可以從比賽的?Forum?和?Scripts?中找到許多有用的經驗和洞見。不要吝嗇提問,Kaggler 都很熱情。
那么就開始吧!
P.S. 本文假設讀者對 Machine Learning 的基本概念和常見模型已經有一定了解。 Enjoy Reading!
General Approach
在這一節中我會講述一次 Kaggle 比賽的大致流程。
Data Exploration
在這一步要做的基本就是?EDA (Exploratory Data Analysis),也就是對數據進行探索性的分析,從而為之后的處理和建模提供必要的結論。
通常我們會用?pandas?來載入數據,并做一些簡單的可視化來理解數據。
Visualization
通常來說?matplotlib?和?seaborn?提供的繪圖功能就可以滿足需求了。
比較常用的圖表有:
-
查看目標變量的分布。當分布不平衡時,根據評分標準和具體模型的使用不同,可能會嚴重影響性能。
-
對?Numerical Variable,可以用?Box Plot?來直觀地查看它的分布。
-
對于坐標類數據,可以用?Scatter Plot?來查看它們的分布趨勢和是否有離群點的存在。
-
對于分類問題,將數據根據 Label 的不同著不同的顏色繪制出來,這對 Feature 的構造很有幫助。
-
繪制變量之間兩兩的分布和相關度圖表。
這里有一個在著名的 Iris 數據集上做了一系列可視化的例子,非常有啟發性。
Statistical Tests
我們可以對數據進行一些統計上的測試來驗證一些假設的顯著性。雖然大部分情況下靠可視化就能得到比較明確的結論,但有一些定量結果總是更理想的。不過,在實際數據中經常會遇到非 i.i.d. 的分布。所以要注意測試類型的的選擇和對顯著性的解釋。
在某些比賽中,由于數據分布比較奇葩或是噪聲過強,Public LB?的分數可能會跟?Local CV?的結果相去甚遠。可以根據一些統計測試的結果來粗略地建立一個閾值,用來衡量一次分數的提高究竟是實質的提高還是由于數據的隨機性導致的。
Data Preprocessing
大部分情況下,在構造 Feature 之前,我們需要對比賽提供的數據集進行一些處理。通常的步驟有:
-
有時數據會分散在幾個不同的文件中,需要 Join 起來。
-
處理?Missing Data。
-
處理?Outlier。
-
必要時轉換某些?Categorical Variable?的表示方式。
-
有些 Float 變量可能是從未知的 Int 變量轉換得到的,這個過程中發生精度損失會在數據中產生不必要的?Noise,即兩個數值原本是相同的卻在小數點后某一位開始有不同。這對 Model 可能會產生很負面的影響,需要設法去除或者減弱 Noise。
這一部分的處理策略多半依賴于在前一步中探索數據集所得到的結論以及創建的可視化圖表。在實踐中,我建議使用?iPython Notebook?進行對數據的操作,并熟練掌握常用的 pandas 函數。這樣做的好處是可以隨時得到結果的反饋和進行修改,也方便跟其他人進行交流(在 Data Science 中?Reproducible Results?是很重要的)。
下面給兩個例子。
Outlier
這是經過 Scaling 的坐標數據。可以發現右上角存在一些離群點,去除以后分布比較正常。
Dummy Variables
對于 Categorical Variable,常用的做法就是?One-hot encoding。即對這一變量創建一組新的偽變量,對應其所有可能的取值。這些變量中只有這條數據對應的取值為 1,其他都為 0。
如下,將原本有 7 種可能取值的?Weekdays?變量轉換成 7 個 Dummy Variables。
要注意,當變量可能取值的范圍很大(比如一共有成百上千類)時,這種簡單的方法就不太適用了。這時沒有有一個普適的方法,但我會在下一小節描述其中一種。
Feature Engineering
有人總結 Kaggle 比賽是?“Feature 為主,調參和 Ensemble 為輔”,我覺得很有道理。Feature Engineering 能做到什么程度,取決于對數據領域的了解程度。比如在數據包含大量文本的比賽中,常用的 NLP 特征就是必須的。怎么構造有用的 Feature,是一個不斷學習和提高的過程。
一般來說,當一個變量從直覺上來說對所要完成的目標有幫助,就可以將其作為 Feature。至于它是否有效,最簡單的方式就是通過圖表來直觀感受。比如:
Feature Selection
總的來說,我們應該生成盡量多的 Feature,相信 Model 能夠挑出最有用的 Feature。但有時先做一遍 Feature Selection 也能帶來一些好處:
-
Feature 越少,訓練越快。
-
有些 Feature 之間可能存在線性關系,影響 Model 的性能。
-
通過挑選出最重要的 Feature,可以將它們之間進行各種運算和操作的結果作為新的 Feature,可能帶來意外的提高。
Feature Selection 最實用的方法也就是看 Random Forest 訓練完以后得到的?Feature Importance?了。其他有一些更復雜的算法在理論上更加 Robust,但是缺乏實用高效的實現,比如這個。從原理上來講,增加 Random Forest 中樹的數量可以在一定程度上加強其對于 Noisy Data 的 Robustness。
看 Feature Importance 對于某些數據經過脫敏處理的比賽尤其重要。這可以免得你浪費大把時間在琢磨一個不重要的變量的意義上。
Feature Encoding
這里用一個例子來說明在一些情況下 Raw Feature 可能需要經過一些轉換才能起到比較好的效果。
假設有一個 Categorical Variable 一共有幾萬個取值可能,那么創建 Dummy Variables 的方法就不可行了。這時一個比較好的方法是根據 Feature Importance 或是這些取值本身在數據中的出現頻率,為最重要(比如說前 95% 的 Importance)那些取值(有很大可能只有幾個或是十幾個)創建 Dummy Variables,而所有其他取值都歸到一個“其他”類里面。
Model Selection
準備好 Feature 以后,就可以開始選用一些常見的模型進行訓練了。Kaggle 上最常用的模型基本都是基于樹的模型:
-
Gradient Boosting
-
Random Forest
-
Extra Randomized Trees
以下模型往往在性能上稍遜一籌,但是很適合作為 Ensemble 的 Base Model。這一點之后再詳細解釋。(當然,在跟圖像有關的比賽中神經網絡的重要性還是不能小覷的。)
-
SVM
-
Linear Regression
-
Logistic Regression
-
Neural Networks
以上這些模型基本都可以通過?sklearn?來使用。
當然,這里不能不提一下?Xgboost。Gradient Boosting?本身優秀的性能加上?Xgboost?高效的實現,使得它在 Kaggle 上廣為使用。幾乎每場比賽的獲獎者都會用?Xgboost?作為最終 Model 的重要組成部分。在實戰中,我們往往會以 Xgboost 為主來建立我們的模型并且驗證 Feature 的有效性。順帶一提,在 Windows 上安裝?Xgboost?很容易遇到問題,目前已知最簡單、成功率最高的方案可以參考我在這篇帖子中的描述。
Model Training
在訓練時,我們主要希望通過調整參數來得到一個性能不錯的模型。一個模型往往有很多參數,但其中比較重要的一般不會太多。比如對?sklearn?的?RandomForestClassifier?來說,比較重要的就是隨機森林中樹的數量?n_estimators?以及在訓練每棵樹時最多選擇的特征數量?max_features。所以我們需要對自己使用的模型有足夠的了解,知道每個參數對性能的影響是怎樣的。
通常我們會通過一個叫做?Grid Search?的過程來確定一組最佳的參數。其實這個過程說白了就是根據給定的參數候選對所有的組合進行暴力搜索。
| 1 2 3 | param_grid = {'n_estimators': [300, 500], 'max_features': [10, 12, 14]} model = grid_search.GridSearchCV(estimator=rfr, param_grid=param_grid, n_jobs=1, cv=10, verbose=20, scoring=RMSE) model.fit(X_train, y_train) |
順帶一提,Random Forest 一般在?max_features?設為 Feature 數量的平方根附近得到最佳結果。
這里要重點講一下 Xgboost 的調參。通常認為對它性能影響較大的參數有:
-
eta:每次迭代完成后更新權重時的步長。越小訓練越慢。
-
num_round:總共迭代的次數。
-
subsample:訓練每棵樹時用來訓練的數據占全部的比例。用于防止 Overfitting。
-
colsample_bytree:訓練每棵樹時用來訓練的特征的比例,類似?RandomForestClassifier?的?max_features。
-
max_depth:每棵樹的最大深度限制。與 Random Forest 不同,Gradient Boosting 如果不對深度加以限制,最終是會 Overfit 的。
-
early_stopping_rounds:用于控制在 Out Of Sample 的驗證集上連續多少個迭代的分數都沒有提高后就提前終止訓練。用于防止 Overfitting。
一般的調參步驟是:
將訓練數據的一部分劃出來作為驗證集。
先將?eta?設得比較高(比如 0.1),num_round?設為 300 ~ 500。
用 Grid Search 對其他參數進行搜索
逐步將?eta?降低,找到最佳值。
以驗證集為 watchlist,用找到的最佳參數組合重新在訓練集上訓練。注意觀察算法的輸出,看每次迭代后在驗證集上分數的變化情況,從而得到最佳的?early_stopping_rounds。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | X_dtrain, X_deval, y_dtrain, y_deval = cross_validation.train_test_split(X_train, y_train, random_state=1026, test_size=0.3) dtrain = xgb.DMatrix(X_dtrain, y_dtrain) deval = xgb.DMatrix(X_deval, y_deval) watchlist = [(deval, 'eval')] params = { ? ?'booster': 'gbtree', ? ?'objective': 'reg:linear', ? ?'subsample': 0.8, ? ?'colsample_bytree': 0.85, ? ?'eta': 0.05, ? ?'max_depth': 7, ? ?'seed': 2016, ? ?'silent': 0, ? ?'eval_metric': 'rmse' } clf = xgb.train(params, dtrain, 500, watchlist, early_stopping_rounds=50) pred = clf.predict(xgb.DMatrix(df_test)) |
最后要提一點,所有具有隨機性的 Model 一般都會有一個?seed?或是?random_state?參數用于控制隨機種子。得到一個好的 Model 后,在記錄參數時務必也記錄下這個值,從而能夠在之后重現 Model。
Cross Validation
Cross Validation?是非常重要的一個環節。它讓你知道你的 Model 有沒有 Overfit,是不是真的能夠 Generalize 到測試集上。在很多比賽中?Public LB?都會因為這樣那樣的原因而不可靠。當你改進了 Feature 或是 Model 得到了一個更高的?CV?結果,提交之后得到的?LB?結果卻變差了,一般認為這時應該相信 CV 的結果。當然,最理想的情況是多種不同的?CV?方法得到的結果和?LB?同時提高,但這樣的比賽并不是太多。
在數據的分布比較隨機均衡的情況下,5-Fold CV?一般就足夠了。如果不放心,可以提到?10-Fold。但是 Fold 越多訓練也就會越慢,需要根據實際情況進行取舍。
很多時候簡單的?CV?得到的分數會不大靠譜,Kaggle 上也有很多關于如何做?CV?的討論。比如這個。但總的來說,靠譜的?CV?方法是 Case By Case 的,需要在實際比賽中進行嘗試和學習,這里就不再(也不能)敘述了。
Ensemble Generation
Ensemble Learning?是指將多個不同的 Base Model 組合成一個 Ensemble Model 的方法。它可以同時降低最終模型的 Bias 和 Variance(證明可以參考這篇論文,我最近在研究類似的理論,可能之后會寫新文章詳述),從而在提高分數的同時又降低 Overfitting 的風險。在現在的 Kaggle 比賽中要不用 Ensemble 就拿到獎金幾乎是不可能的。
常見的 Ensemble 方法有這么幾種:
-
Bagging:使用訓練數據的不同隨機子集來訓練每個 Base Model,最后進行每個 Base Model 權重相同的 Vote。也即 Random Forest 的原理。
-
Boosting:迭代地訓練 Base Model,每次根據上一個迭代中預測錯誤的情況修改訓練樣本的權重。也即 Gradient Boosting 的原理。比 Bagging 效果好,但更容易 Overfit。
-
Blending:用不相交的數據訓練不同的 Base Model,將它們的輸出取(加權)平均。實現簡單,但對訓練數據利用少了。
-
Stacking:接下來會詳細介紹。
從理論上講,Ensemble 要成功,有兩個要素:
-
Base Model 之間的相關性要盡可能的小。這就是為什么非 Tree-based Model 往往表現不是最好但還是要將它們包括在 Ensemble 里面的原因。Ensemble 的 Diversity 越大,最終 Model 的 Bias 就越低。
-
Base Model 之間的性能表現不能差距太大。這其實是一個?Trade-off,在實際中很有可能表現相近的 Model 只有寥寥幾個而且它們之間相關性還不低。但是實踐告訴我們即使在這種情況下 Ensemble 還是能大幅提高成績。
Stacking
相比 Blending,Stacking 能更好地利用訓練數據。以 5-Fold Stacking 為例,它的基本原理如圖所示:
整個過程很像 Cross Validation。首先將訓練數據分為 5 份,接下來一共 5 個迭代,每次迭代時,將 4 份數據作為 Training Set 對每個 Base Model 進行訓練,然后在剩下一份 Hold-out Set 上進行預測。同時也要將其在測試數據上的預測保存下來。這樣,每個 Base Model 在每次迭代時會對訓練數據的其中 1 份做出預測,對測試數據的全部做出預測。5 個迭代都完成以后我們就獲得了一個?#訓練數據行數 x #Base Model 數量?的矩陣,這個矩陣接下來就作為第二層的 Model 的訓練數據。當第二層的 Model 訓練完以后,將之前保存的 Base Model 對測試數據的預測(因為每個 Base Model 被訓練了 5 次,對測試數據的全體做了 5 次預測,所以對這 5 次求一個平均值,從而得到一個形狀與第二層訓練數據相同的矩陣)拿出來讓它進行預測,就得到最后的輸出。
這里給出我的實現代碼:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | class Ensemble(object): ? ?def __init__(self, n_folds, stacker, base_models): ? ? ? ?self.n_folds = n_folds ? ? ? ?self.stacker = stacker ? ? ? ?self.base_models = base_models ? ?def fit_predict(self, X, y, T): ? ? ? ?X = np.array(X) ? ? ? ?y = np.array(y) ? ? ? ?T = np.array(T) ? ? ? ?folds = list(KFold(len(y), n_folds=self.n_folds, shuffle=True, random_state=2016)) ? ? ? ?S_train = np.zeros((X.shape[0], len(self.base_models))) ? ? ? ?S_test = np.zeros((T.shape[0], len(self.base_models))) ? ? ? ?for i, clf in enumerate(self.base_models): ? ? ? ? ? ?S_test_i = np.zeros((T.shape[0], len(folds))) ? ? ? ? ? ?for j, (train_idx, test_idx) in enumerate(folds): ? ? ? ? ? ? ? ?X_train = X[train_idx] ? ? ? ? ? ? ? ?y_train = y[train_idx] ? ? ? ? ? ? ? ?X_holdout = X[test_idx] ? ? ? ? ? ? ? ?# y_holdout = y[test_idx] ? ? ? ? ? ? ? ?clf.fit(X_train, y_train) ? ? ? ? ? ? ? ?y_pred = clf.predict(X_holdout)[:] ? ? ? ? ? ? ? ?S_train[test_idx, i] = y_pred ? ? ? ? ? ? ? ?S_test_i[:, j] = clf.predict(T)[:] ? ? ? ? ? ?S_test[:, i] = S_test_i.mean(1) ? ? ? ?self.stacker.fit(S_train, y) ? ? ? ?y_pred = self.stacker.predict(S_test)[:] ? ? ? ?return y_pred |
獲獎選手往往會使用比這復雜得多的 Ensemble,會出現三層、四層甚至五層,不同的層數之間有各種交互,還有將經過不同的 Preprocessing 和不同的 Feature Engineering 的數據用 Ensemble 組合起來的做法。但對于新手來說,穩穩當當地實現一個正確的 5-Fold Stacking 已經足夠了。
*Pipeline
可以看出 Kaggle 比賽的 Workflow 還是比較復雜的。尤其是 Model Selection 和 Ensemble。理想情況下,我們需要搭建一個高自動化的 Pipeline,它可以做到:
-
模塊化 Feature Transform,只需寫很少的代碼就能將新的 Feature 更新到訓練集中。
-
自動化 Grid Search,只要預先設定好使用的 Model 和參數的候選,就能自動搜索并記錄最佳的 Model。
-
自動化 Ensemble Generation,每個一段時間將現有最好的 K 個 Model 拿來做 Ensemble。
對新手來說,第一點可能意義還不是太大,因為 Feature 的數量總是人腦管理的過來的;第三點問題也不大,因為往往就是在最后做幾次 Ensemble。但是第二點還是很有意義的,手工記錄每個 Model 的表現不僅浪費時間而且容易產生混亂。
Crowdflower Search Results Relevance?的第一名獲得者?Chenglong Chen?將他在比賽中使用的 Pipeline?公開了,非常具有參考和借鑒意義。只不過看懂他的代碼并將其中的邏輯抽離出來搭建這樣一個框架,還是比較困難的一件事。可能在參加過幾次比賽以后專門抽時間出來做會比較好。
Home Depot Search Relevance
在這一節中我會具體分享我在?Home Depot Search Relevance?比賽中是怎么做的,以及比賽結束后從排名靠前的隊伍那邊學到的做法。
首先簡單介紹這個比賽。Task 是判斷用戶搜索的關鍵詞和網站返回的結果之間的相關度有多高。相關度是由 3 個人類打分取平均得到的,每個人可能打 1 ~ 3 分,所以這是一個回歸問題。數據中包含用戶的搜索詞,返回的產品的標題和介紹,以及產品相關的一些屬性比如品牌、尺寸、顏色等。使用的評分基準是?RMSE。
這個比賽非常像?Crowdflower Search Results Relevance?那場比賽。不過那邊用的評分基準是Quadratic Weighted Kappa,把 1 誤判成 4 的懲罰會比把 1 判成 2 的懲罰大得多,所以在最后 Decode Prediction 的時候會更麻煩一點。除此以外那次比賽沒有提供產品的屬性。
EDA
由于加入比賽比較晚,當時已經有相當不錯的 EDA 了。尤其是這個。從中我得到的啟發有:
-
同一個搜索詞/產品都出現了多次,數據分布顯然不 i.i.d.。
-
文本之間的相似度很有用。
-
產品中有相當大一部分缺失屬性,要考慮這會不會使得從屬性中得到的 Feature 反而難以利用。
-
產品的 ID 對預測相關度很有幫助,但是考慮到訓練集和測試集之間的重疊度并不太高,利用它會不會導致 Overfitting?
Preprocessing
這次比賽中我的 Preprocessing 和 Feature Engineering 的具體做法都可以在這里看到。我只簡單總結一下和指出重要的點。
利用 Forum 上的?Typo Dictionary?修正搜索詞中的錯誤。
統計屬性的出現次數,將其中出現次數多又容易利用的記錄下來。
將訓練集和測試集合并,并與產品描述和屬性 Join 起來。這是考慮到后面有一系列操作,如果不合并的話就要重復寫兩次了。
對所有文本能做?Stemming?和?Tokenizing,同時手工做了一部分格式統一化(比如涉及到數字和單位的)和同義詞替換。
Feature
-
*Attribute Features
-
是否包含某個特定的屬性(品牌、尺寸、顏色、重量、內用/外用、是否有能源之星認證等)
-
這個特定的屬性是否匹配
-
-
Meta Features
-
各個文本域的長度
-
是否包含屬性域
-
品牌(將所有的品牌做數值離散化)
-
產品 ID
-
-
簡單匹配
-
搜索詞是否在產品標題、產品介紹或是產品屬性中出現
-
搜索詞在產品標題、產品介紹或是產品屬性中出現的數量和比例
-
*搜索詞中的第 i 個詞是否在產品標題、產品介紹或是產品屬性中出現
-
-
搜索詞和產品標題、產品介紹以及產品屬性之間的文本相似度
-
BOW?Cosine 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 是獲勝的關鍵。
Model
我一開始用的是?RandomForestRegressor,后來在 Windows 上折騰?Xgboost?成功了就開始用?XGBRegressor。XGB?的優勢非常明顯,同樣的數據它只需要不到一半的時間就能跑完,節約了很多時間。
比賽中后期我基本上就是一邊臺式機上跑?Grid Search,一邊在筆記本上繼續研究 Feature。
這次比賽數據分布很不獨立,所以期間多次遇到改進的 Feature 或是?Grid Search?新得到的參數訓練出來的模型反而?LB?分數下降了。由于被很多前輩教導過要相信自己的?CV,我的決定是將 5-Fold 提到 10-Fold,然后以?CV?為標準繼續前進。
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%。
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 經驗。
Summary
Takeaways
比較早的時候就開始做 Ensemble 是對的,這次比賽到倒數第三天我還在糾結 Feature。
很有必要搭建一個 Pipeline,至少要能夠自動訓練并記錄最佳參數。
Feature 為王。我花在 Feature 上的時間還是太少。
可能的話,多花點時間去手動查看原始數據中的 Pattern。
Issues Raised
我認為在這次比賽中遇到的一些問題是很有研究價值的:
在數據分布并不 i.i.d. 甚至有 Dependency 時如何做靠譜的?CV。
如何量化 Ensemble 中?Diversity vs. Accuracy?的 Trade-off。
如何處理 Feature 之間互相影響導致性能反而下降。
Beginner Tips
給新手的一些建議:
選擇一個感興趣的比賽。如果你對相關領域原本就有一些洞見那就更理想了。
根據我描述的方法開始探索、理解數據并進行建模。
通過 Forum 和 Scripts 學習其他人對數據的理解和構建 Feature 的方式。
如果之前有過類似的比賽,可以去找當時獲獎者的 Interview 和 Blog Post 作為參考,往往很有用。
在得到一個比較不錯的?LB?分數(比如已經接近前 10%)以后可以開始嘗試做 Ensemble。
如果覺得自己有希望拿到獎金,開始找人組隊吧!
到比賽結束為止要繃緊一口氣不能斷,盡量每天做一些新嘗試。
比賽結束后學習排名靠前的隊伍的方法,思考自己這次比賽中的不足和發現的問題,可能的話再花點時間將學到的新東西用實驗進行確認,為下一次比賽做準備。
好好休息!
Reference
Beating Kaggle the Easy Way - Dong Ying
Solution for Prudential Life Insurance Assessment - Nutastray
Search Results Relevance Winner’s Interview: 1st place, Chenglong Chen
總結
- 上一篇: Blending and Bagging
- 下一篇: Stacking