机器学习教程:带你从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"获取代码.