Introduction
(点击可阅读)中我们按照一次完整比赛流程的顺序介绍了一些Kaggle比赛技巧.在这篇文章中将结合以上技巧介绍一次完整的进入前5%的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 起来。这是考虑到后面有一系列操作,如果不合并的话就要重复写两次了。对所有文本能做 Stemming 和 Tokenizing,同时手工做了一部分格式统一化(比如涉及到数字和单位的)和同义词替换。3. Feature
*Attribute Features是否包含某个特定的属性(品牌、尺寸、颜色、重量、内用/外用、是否有能源之星认证等)这个特定的属性是否匹配Meta Features各个文本域的长度是否包含属性域品牌(将所有的品牌做数值离散化)产品 ID简单匹配搜索词是否在产品标题、产品介绍或是产品属性中出现搜索词在产品标题、产品介绍或是产品属性中出现的数量和比例*搜索词中的第 i 个词是否在产品标题、产品介绍或是产品属性中出现搜索词和产品标题、产品介绍以及产品属性之间的文本相似度BOWCosine SimilairtyTF-IDF Cosine SimilarityJaccard Similarity*Edit DistanceWord2Vec Distance(由于效果不好,最后没有使用,但似乎是因为用的不对)值得一提的是,上面打了 * 的 Feature 都是我在最后一批加上去的。问题是,使用这批 Feature 训练得到的 Model 反而比之前的要差,而且还差不少。我一开始是以为因为 Feature 的数量变多了所以一些参数需要重新调优,但在浪费了很多时间做 Grid Search 以后却发现还是没法超过之前的分数。这可能就是之前提到的 Feature 之间的相互作用导致的问题。当时我设想过一个看到过好几次的解决方案,就是将使用不同版本 Feature 的 Model 通过 Ensemble 组合起来。但最终因为时间关系没有实现。事实上排名靠前的队伍分享的解法里面基本都提到了将不同的 Preprocessing 和 Feature Engineering 做 Ensemble 是获胜的关键。
4. Model
我一开始用的是 RandomForestRegressor,后来在 Windows 上折腾 Xgboost 成功了就开始用 XGBRegressor。XGB 的优势非常明显,同样的数据它只需要不到一半的时间就能跑完,节约了很多时间。
比赛中后期我基本上就是一边台式机上跑
这次比赛数据分布很不独立,所以期间多次遇到改进的 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 上有几百名的差距),而后者的训练实在太慢了。最后还是只用了这四个。
值得一提的是,在开始做
在比赛最后两天,因为身心疲惫加上想不到还能有什么显著的改进,我做了一件事情:用 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
比赛结束后一些队伍分享了他们的解法,从中我学到了一些我没有做或是做的不够好的地方:
至于 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
给新手的一些建议:
选择一个感兴趣的比赛。如果你对相关领域原本就有一些洞见那就更理想了。