用python参加Kaggle的经验总结

from: http://www.jianshu.com/p/32def2294ae6

最近挤出时间,用python在kaggle上试了几个project,有点体会,记录下。

Step1: Exploratory Data Analysis

EDA,也就是对数据进行探索性的分析,一般就用到pandas和matplotlib就够了。EDA一般包括:

  1. 每个feature的意义,feature的类型,比较有用的代码如下
  2. 看是否存在missing value
  3. 每个特征下的数据分布,可以用boxplot或者hist来看
  4. 如果想看几个feature之间的联立情况,则可以用pandas的groupby,
    temp = pd.crosstab([df.Pclass, df.Sex], df.Survived.astype(bool))
    temp.plot(kind=’bar’, stacked=True, color=[‘red’,’blue’], grid=False)

在这步完成之后,要对以下几点有大致了解

  • 理解每个特征的意义
  • 要知道哪些特征是有用的,这些特征哪些是直接可以用的,哪些需要经过变换才能用,为之后的特征工程做准备

Step2: Data Preprocessing

数据预处理,就是将数据处理下,为模型输入做准备,其中包括:

  • 处理missing value:这里学问有点深,如果各位有好的经验可以跟我交流下。以我浅薄的经验来说我一般会分情况处理
    1. 如果missing value占总体的比例非常小,那么直接填入平均值或者众数
    2. 如果missing value所占比例不算小也不算大,那么可以考虑它跟其他特征的关系,如果关系明显,那么直接根据其他特征填入;也可以建立简单的模型,比如线性回归,随机森林等。
    3. 如果missing value所占比例大,那么直接将miss value当做一种特殊的情况,另取一个值填入
  • 处理Outlier:这个就是之前EDA的作用了,通过画图,找出异常值
  • 处理categorical feature:一般就是通过dummy variable的方式解决,也叫one hot encode,可以通过pandas.get_dummies()或者 sklearn中preprocessing.OneHotEncoder(), 我个人倾向于用pandas的get_dummies()
    看个例子吧,

    dummy variable

    将一列的month数据展开为了12列,用0、1代表类别。
    另外在处理categorical feature有两点值得注意:

    1. 如果特征中包含大量需要做dummy variable处理的,那么很可能导致得到一个稀疏的dataframe,这时候最好用下PCA做降维处理。
    2. 如果某个特征有好几万个取值,那么用dummy variable就并不现实了,这时候可以用Count-Based Learning.
    3. (更新)近期在kaggle成功的案例中发现,对于类别特征,在模型中加入tf-idf总是有效果的。
    4. 还有个方法叫“Leave-one-out” encoding,也可以处理类别特征种类过多的问题,实测效果不错。

Step 3: Feature Engineering

理论上来说,特征工程应该也归属于上一步,但是它太重要了,所以将它单独拿出来。kaggle社区对特征工程的重要性已经达成了共识,可以说最后结果的好坏,大部分就是由特征工程决定的,剩下部分应该是调参Ensemble决定。特征工程的好坏主要是由domain knowledge决定的,但是大部分人可能并不具备这种知识,那么只能尽可能多的根据原来feature生成新的feature,然后让模型选择其中重要的feature。这里就又涉及到feature selection,
有很多方法,比如backward,forward selection等等。我个人倾向于用random forest的feature importance这里有论文介绍了这种方法。

Step 4: Model Selection and Training

  • 最常用的模型是Ensemble Model,比如 Random Forest,Gradient Boosting。当然在开始的时候,可以用点简单的模型,一方面是可以作为底线threshold,另一方面也可以在最后作为Ensemble Model。
    当然还有大名鼎鼎的xgboost,这个我也没有深入的研究,只是简单的用python调用了下,接下来如果有时间,要好好深入研究下。
  • 选择完模型之后,就是要训练模型,主要就是调参,每种模型都有自己最关键的几个参数,sklearn中GridSearchCV可以设置需要比较的几种参数组合,然后用cross validation来选出最优秀的参数组合。大概用法为:

Step 5: Model Ensemble

Model Ensemble有Bagging,Boosting,Stacking,其中Bagging和Boosting都算是Bootstraping的应用。Bootstraping的概念是对样本每次有放回的抽样,抽样K个,一共抽N次。

  • Bagging:每次从总体样本中随机抽取K个样本来训练模型,重复N次,得到N个模型,然后将各个模型结果合并,分类问题投票方式结合,回归则是取平均值,e.g.Random Forest。
  • Boosting:一开始给每个样本取同样的权重,然后迭代训练,每次对训练失败的样本调高其权重。最后对多个模型用加权平均来结合,e.g. GBDT。
  • Bagging与Boosting的比较:在深入理解Bagging和Boosting后发现,bagging其实是用相同的模型来训练随机抽样的数据,这样的结果是各个模型之间的bias差不多,variance也差不多,通过平均,使得variance降低(由算平均方差的公式可知),从而提高ensemble model的表现。而Boosting其实是一种贪心算法,不断降低bias。
  • Stacking: 训练一个模型来组合其他各个模型。首先先训练多个不同的模型,然后再以之前训练的各个模型的输出为输入来训练一个模型,以得到一个最终的输出。使用过stacking之后,发现其实stacking很像神经网络,通过很多模型的输出,构建中间层,最后用逻辑回归讲中间层训练得到最后的结果。这里贴一个例子供参考。

    Step 6: Two Little Tips

    最后是我的两点心得吧

  • 设置random seed,使得你的模型reproduce,以Random Foreset举例:

  • 每个project组织好文件层次和布局,既方便与其他人交流,也方便自己。比如在一个project下,分设3个文件夹,一个是input,放训练数据、测试数据,一个model,放模型文件,最后一个submission文件,放你生成要提交的结果文件。
    具体的可以参考这里

最后的回顾和展望

这篇文章是参加kaggle之后的第一次总结,描述了下kaggle的步骤,通用的知识点和技巧。希望在未来一个月中,能把xgboost和stacking研究应用下,然后再来update。希望大家有什么想法都能跟我交流下~~

update: 更新了关于类别特征的处理方式以及Boosting和Bagging的看法,还有stacking的内容。

 

第一次接触kaggle比赛,是在听完台大林轩田老师的机器学习基石和技法课程之后。都说实践出真知,为了系统的巩固下机器学习实战技能,成为一名合格的数据挖掘工程师,我踏入了kaggle大门。

参加比赛很耗费时间和精力,由于本人已经工作,只能利用业余时间有选择的参加,希望能从中学到东西。我选择的比赛都是有监督的学习(当然用到了非监督方法,比如Bag of Words Meets Bags of Popcorn就用到了Collapsed Gibbs LDA),概括起来就是分类/排序/回归。下面就跟大家分享下近两个月我参加的比赛,欢迎一起探讨。

工欲善其事,必先利其器

首先介绍下我经常使用的机器学习工具:

  1. scikit-learn. 涵盖了基本能想到的各种机器学习算法,由于本人python党,我把它当作matlab和R的替代品。
  2. xgboost. 华盛顿大学机器学习大牛陈天奇出品的GBRT框架,果然是刷比赛利器。
  3. liblinear/libsvm. 台大林智仁团队的佳作,工业界很多也在用。
  4. pandas. 处理数据python包,DataFrame那叫一个好用。

机器学习流程

拿到一个比赛,我的一般套路是:

  1. 读懂比赛介绍,明确是哪类问题:分类/排序/回归。
  2. 数据特征处理。这个是最耗时也是最重要的,正所谓“数据和特征决定了效果上限,模型和算法决定了逼近这个上限的程度”。其实这点我还有很大欠缺,汗!
  3. Cross validation数据集切分。数据集很大完全可以hold out一份作为测试集(不是待提交结果的测试集,此处是用来CV的),数据集偏小就需要K-fold或者Leave-one-out了,如果训练集有时序关系,还要注意测试集选取最后时间片的。这点我自我批评,有时为了省事,直接就提交结果做CV了。咳咳,这有点像imagenet比赛作弊了,只是我没用小号增加提交次数。
  4. 常用算法/默认参数跑结果作为baseline。这个需要一些经验和直觉,一般来说Tree Based的Random Forest和GBRT效果都不会太烂,如果特征维度很大很稀疏这时就需要试试线性SVM和LR了。
  5. 接下来就是调参了,这个我也没用太多经验,一般就是GridSearchCV或者RandomizedSearchCV。有人推荐Hyperopt库,接下来调研下。
  6. 迭代。为了取得比较好的结果,下面就是2/3/4/5不断迭代了。
  7. Blending.上面说的都是单模型,最后让你结果更general/low variance,提升一个档次的就是结果ensemble了(不是指gbrt/rf的ensemble,是多种模型的融合)。这里我一般就是简单的多种模型结果的averaging(weighted)or voting,这里推荐一篇ensemble selection paper(http://www.cs.cornell.edu/~alexn/papers/shotgun.icml04.revised.rev2.pdf)。

比赛实战

1. Bike Sharing Demand

Forecast use of a city bikeshare system. https://www.kaggle.com/c/bike-sharing-demand

这是一个回归问题,最后预测租车数量。这里需要注意一点,最后总数实际等于casual+registered。原始共10个特征,包括datetime特征,season/holiday等类别特征,temp/atemp等数值特征,没有特征缺失值。评价指标为RMSLE,其实就是RMSE原来的p和a加1取ln。

当时正在研究GBRT,所以使用了xgboost。由于使用RMSLE,xgboost自带的loss是square loss,eval_metric是RMSE,这时两种选择1.修改xgboost代码,派生新的优化objective,求新objective的gradient(一阶导)/hessian(二阶导),派生新的eval_metric;2.训练数据的y做ln(y+1)转化,最后预测时再做exp(y^)-1就转回来了。当然2简单了,我也是这么实施的。

关于数据特征处理,datetime转成y/m/d/h/dayofweek,y/m等类别特征由于有连续性,这里没有做one-hot编码。经过cv最后cut掉了日/season。

Xgboost参数其实没有怎么去调,shrinkage=0.1,tree_num=1000,depth=6,其他默认。

效果三次提升拐点分别是:1.RMSE转换为RMLSE(square loss转为square log loss),说明预测值的范围很大,log转化后bound更tight了;2.cut了日/season特征;3.转换为对casual和registered的分别回归问题,在加和。最后RMLSE结果为0.36512,public LB最好为30位,最终private LB为28,还好说明没有overfit。

2. Bag of Words Meets Bags of Popcorn

Use Google’s Word2Vec for movie reviews. https://www.kaggle.com/c/word2vec-nlp-tutorial

这是一个文本情感二分类问题。25000的labeled训练样本,只有一个raw text 特征”review“。评价指标为AUC,所以这里提交结果需要用概率,我开始就掉坑里了,结果一直上不来。

比赛里有教程如何使用word2vec进行二分类,可以作为入门学习材料。我没有使用word embeddinng,直接采用BOW及ngram作为特征训练,效果还凑合,后面其实可以融合embedding特征试试。对于raw text我采用TfidfVectorizer(stop_words=’english’, ngram_range=(1,3), sublinear_tf=True, min_df=2),并采用卡方检验进行特征选择,经过CV,最终确定特征数为200000。

单模型我选取了GBRT/NB/LR/linear SVC。GBRT一般对于维度较大比较稀疏效果不是很好,但对于该数据表现不是很差。NB采用MultinomialNB效果也没有想象的那么惊艳。几个模型按效果排序为linear SVC(0.95601)>LR(0.94823)>GBRT(0.94173)>NB(0.93693),看来线性SVM在文本上还是很强悍的。

后续我又采用LDA生成主题特征,本来抱着很大期望,现实还是那么骨感,采用上述单模型AUC最好也只有0.93024。既然单独使用主题特征没有提高,那和BOW融合呢?果然work了!后面试验证实特征融合还是linear SVC效果最好,LDA主题定为500,而且不去除停用词效果更好,AUC为0.95998

既然没有时间搞单模型了,还有最后一招,多模型融合。这里有一个原则就是模型尽量多样,不一定要求指标最好。最终我选取5组不是很差的多模型结果进行average stacking,AUC为0.9611563位。最终private LB跌倒了71st,应该融合word enbedding试试,没时间细搞了。

3. Titanic: Machine Learning from Disaster

Predict survival on the Titanic. https://www.kaggle.com/c/titanic

硬二分类问题,不需要预测概率,给出0/1即可,评价指标为accuracy。说句题外话,网上貌似有遇难者名单,LB上好几个score 1.0的。有坊间说,score超过90%就怀疑作弊了,不知真假,不过top300绝大多数都集中在0.808-0.818。这个题目我后面没有太多的改进想法了,求指导啊~

数据包括数值和类别特征,并存在缺失值。类别特征这里我做了one-hot-encode,缺失值是采用均值/中位数/众数需要根据数据来定,我的做法是根据pandas打印出列数据分布来定。

模型我采用了DT/RF/GBDT/SVC,由于xgboost输出是概率,需要指定阈值确定0/1,可能我指定不恰当,效果不好0.78847。效果最好的是RF,0.81340。这里经过筛选我使用的特征包括’Pclass’,’Gender’, ‘Cabin’,’Ticket’,’Embarked’,’Title’进行onehot编码,’Age’,’SibSp’,’Parch’,’Fare’,’class_age’,’Family’ 归一化。我也尝试进行构建一些新特征和特征组合,比如title分割为Mr/Mrs/Miss/Master四类或者split提取第一个词,添加fare_per_person等,pipeline中也加入feature selection,但是效果都没有提高,求指导~

4. San Francisco Crime Classification

Predict the category of crimes that occurred in the city by the bay. https://www.kaggle.com/c/sf-crime

这是一个多分类问题,一般三种处理方法:one vs all, one vs one, softmax,信息损失逐渐递减。87809条训练数据,数据包括datetime/类别/数值特征,没有缺失值,label共39种。评价指标为logloss,这里要说下和AUC的区别,AUC更强调相对排序

我抽取后特征包括year,m,d,h,m,dow,district,address,x,y,模型选择softmax objective的LR和xgboost。这两个模型对特征挑食,有不同的偏好,LR喜好0/1类别或者locale到0-1的数值特征,而xgboost更喜好原始的数值特征,而且对缺失值也能很好的处理。所以对于LR就是2个归一化的数值特征和8个待one-hot编码的特征,对于xgboost是8个原始数值特征(包括year/m/d等,具有连续性)和2个待one-hot编码的特征。

LR效果要略好于xgboost效果,logloss分别为2.28728/2.28869,最好位置为3rd,目前跌到4th,后面找时间再搞一搞。

5. Caterpillar Tube Pricing

Model quoted prices for industrial tube assemblies. https://www.kaggle.com/c/caterpillar-tube-pricing

这也是一个回归问题,预测tube报价。30213条训练样本,特征分散在N个文件中,需要你left join起来。评价指标为RMLSE,哈哈,是不是很熟悉?对,跟bike sharing的一样,所以怎么转换知道了吧?看,做多了套路trick也就了然了。感觉这个需要领域知识,但其实有些特征我是不知道含义的,anyway,先merge所有特征不加domain特征直接搞起。

这是我见过小样本里特征处理最麻烦的(后面的CTR大数据处理更耗时),它特征分散在多个文件中,还好我们有神器pandas,直接left join搞定。这里有些trick需要注意,比如comp_*文件要用append不能join,这样正好是一个全集,否则就会多个weight特征了。特征存在缺失值,这里我全部采用0值,不知是否恰当?

模型我主要试了RF和xgboost,RF tree_num=1000,其他默认值,RMLSE=0.255201,主要精力放在了xgboost上,调了几次参数(depth=58,col_sample=0.75,sample=0.85,shrinkage=0.01,tree_num=2000),最好RMLSE=0.231220,最好位置120th,目前跌倒206th了,看来需要好好搞搞特征了!

http://nanjunxiao.github.io/2015/07/30/Kaggle%E5%AE%9E%E6%88%98%E4%B8%80/