Kaggle网站的比赛(Competition)可以分为两大类:
一般来说,主要分为以下几步:
Data Exploration
在这一步要做的基本就是EDA(Exploratory Data Analysis),也就是对数据进行探索性的分析。通常我们会用pandas来载入数据,并做一些简单的可视化来理解数据。
Visualization
Python中用matplotlib和seaborn提供的绘图功能就可以满足需求了。
Statistical Tests
我们可以对数据进行一些统计上的测试来验证一些假设的显著性。虽然大部分情况下,靠可视化就可以得到比较明确的结论,但有一些定量结果总是更理想。
Data Preprocessing
Outlier
这是经过Scaling的坐标数据。可以发现右上角存在一些离群点,去除以后分布比较正常
Dummy Variables
对于Categorical Variable,常用的做法就是One-hot encoding。即对这一变量创建一组新的伪变量,对应其所有可能的取值。这些变量中只有这条数据对应的取值为1,其他都为0。比如,将原本有7种可能取值的Weekdays变量转换成7个Dummy Variables。但是,当变量可能取值的范围很大,这个方法就不太适用了。
Feature Engineering
有人总结Kaggle比赛以“Feature为主,调参和Ensemble为辅”。Feature Engineering能做到什么程度,取决于对数据领域的了解程度,怎么构造有用的Feature,是一个不断学习和提高的过程。
一般来说,当一个变量从直觉上来说对所要完成的目标有帮助,就可以将其作为Feature。至于它是否有效,最简单的方式就是通过图表来直观感受。
Feature Selection
总的来说,我们应该生成尽量多的Feature,相信Model能够挑出最有用的Feature。但Feature越少,训练越快。
有些Feature之间可能存在线性关系,影响Model的性能。通过挑选出最重要的Feature,可以将它们之间进行各种运算和操作的结果作为新的Feature,可能带来意外的提高。
Feature Selection最实用的方法也就是看RandomForest训练完以后得到的Feature Importance了。看Feature Importance对于某些数据经过脱敏处理的比赛尤其重要,这可以免得你浪费大把时间在琢磨一个不重要的变量的意义上。
Feature Encoding
这里用一个例子来说明在一些情况下Raw Feature可能需要经过一些转换才能起到比价好的效果。
假设有一个Categorical Variable一共有几万个取值可能,那么创建Dummy Variables的方法就不可行了。这时一个比较好的方法是根据Feature Importance或者这些取值本身在数据中的出现频率,为最重要(比如说前95%的Importance)那些取值创建DummyVariables,而所有其他取值都归到一个“其他”类里面。
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的有效性。
Model Training
在训练时,我们主要希望通过调整参数来得到一个性能不错的模型。一个模型往往有很多参数,但其中比较重要的一般不会太多。比如对sklearn的RandomForestClassifier来说,比较重要的就是随机森林中树的数量n_estimators以及在训练每棵树时最多选择的特征数量max_features。所以,我们需要对自己使用的模型有足够的了解,知道每个参数对性能的影响是怎样的。
通常我们会通过一个叫做Grid Search的过程来确定一组最佳的参数。其实这个过程说白了就是根据给定的参数候选对所有的组合进行暴力搜索。
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。
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越多训练也就越慢,需要根据实际情况进行取舍。
Ensemble Generation
Ensemble Learning是将多个不同的Base Model组合成一个Ensemble Model的方法。它可以同时降低最终模型的Bias和Variance,从而在提高分数的同时又降低Overfitting的风险。
常见的Ensemble方法有这么几种:
Bagging:使用训练数据的不同随机子集来训练每个Base Model,最后进行每个Base Model权重相同的Vote。也即Random Forest的原理。
Boosting:迭代地训练Base Model,每次根据上一个迭代中预测错误的情况修改训练样本的权重。也即Gradient Boosting的原理。比Bagging效果好,但更容易Overfit。
Blending:用不相交的数据训练不同的Base Model,将它们的输出取(加权)平均。实现简单,但对训练数据利用少了。
Stacking
相比Blending,Stacking能更好地利用训练数据。以5-Fold Stacking为例,它的基本原理如下:
整个过程很像Cross Validation。首先将训练数据分为5份,接下来一共5个迭代,每次迭代时,将4 份数据作为Traning Set对每个Base Model进行训练,然后在剩下一份Hold-Out Set上进行预测。同时也要将其在测试数据上的预测保存下来。这样,每个Base Model在每次迭代时会对训练数据的其中1份做出预测,对测试数据全部做出预测。5个迭代都完成以后,我们就获得一个#训练数据行数 x #Base Model数量的矩阵,这个矩阵接下来就作为第二层的Model的训练数据。当第二层的Model训练完以后,将之前保存的Base Model对测试数据的预测(因为每个Base Model被训练了5次,对测试数据的全体做了5次越策,所以对着5次求一个平均值,从而得到一个形状与第二层训练数据相同的矩阵)拿出来让它进行预测,就得到最后的输出。
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]
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已经足够了。
从理论上讲,Ensemble要成功,有两个要素:
Base Model之间的相关性要尽可能的小。这就是为什么非Tree-based Model往往表现不是最好但哈市要将它们包括在Ensemble里面的原因。Ensemble的Diversity越大,最终Model的Bias就越低。
Base Model之间的性能表现不能差距太大。这其实是一个Trade-of,在实践中很可能表现相近的Model只有寥寥几个而且它们之间相关性还不低。但是实践告诉我们即使在这种情况下Ensemble还是能大幅度提高成绩。
Pipeline
可以看出Kaggle比赛的Workflow还是比较复杂的。尤其是Model Selection和Ensemble。理想情况下,我们需要搭建一个高自动化的Pipeline,它可以做到:
模块化Feature Transform,只需写很少的代码就能将新的Feature更新到训练集中。
自动化Grid Serach,只要预先设定好使用的Model和参数的候选,技能自动搜索并记录最佳的Model。
自动化Ensemble Generation,每隔一段时间将现有最好的K个Model拿来做Ensemble。
对新手来说,第一点可能意义还不是太大,因为Feature的数量总是人脑管理里的过来的;第三点问题也不大,因为往往就是在最后做几次Ensemble。但是第二点还是很有意义的,手工记录每个Model的表现不仅浪费时间而且容易产生混乱。
转载链接:mjiansun