完整代码: https://github.com/cindycindyhi/kaggle-Titanic
特征工程系列:
之前的三篇博文已经进行了一次还算完整的特征工程,分析字符串类型的变量获取新变量,对数值变量进行规范化,获取派生属性并进行维规约。现在我们已经有了一个特征集,可以进行训练模型了。
由于这是一个分类问题,可以使用L1 SVM 随机森林等分类算法,随机森林是一个非常简单而且实用的分类模型,可调的变量很少。它的一个非常重要的变量是树的个数,树的个数增加到一定大小后会使耗时加大,但是精度不会增加很多。
经过之前的特征工程,现在已经有237个特征,数目过多的特征会使模型过拟合,幸运的是,随机森林在训练之后可以产生一个各个特征重要性的数据集,我们可以利用这个数据集,确定一个阈值,选出来对模型训练帮助最大的一些属性,这里使用的随机森林的参数都是默认值。
1 X = input_df.values[:, 1::] 2 y = input_df.values[:, 0] 3 survived_weight = .75 4 y_weights = np.array([survived_weight if s == 0 else 1 for s in y]) 5 6 print "Rough fitting a RandomForest to determine feature importance..." 7 forest = RandomForestClassifier(oob_score=True, n_estimators=10000) 8 forest.fit(X, y, sample_weight=y_weights) 9 feature_importance = forest.feature_importances_ 10 feature_importance = 100.0 * (feature_importance / feature_importance.max()) 11 12 fi_threshold = 18 13 important_idx = np.where(feature_importance > fi_threshold)[0] 14 important_features = features_list[important_idx] 15 print "\n", important_features.shape[0], "Important features(>", \ 16 fi_threshold, "% of max importance)...\n"#, \ 17 #important_features 18 sorted_idx = np.argsort(feature_importance[important_idx])[::-1] 19 #get the figure about important features 20 pos = np.arange(sorted_idx.shape[0]) + .5 21 plt.subplot(1, 2, 2) 22 plt.title('Feature Importance') 23 plt.barh(pos, feature_importance[important_idx][sorted_idx[::-1]], \ 24 color='r',align='center') 25 plt.yticks(pos, important_features[sorted_idx[::-1]]) 26 plt.xlabel('Relative Importance') 27 plt.draw() 28 plt.show()
代码有点长,但主要分成两块,一是模型训练,二是根据训练得到的特征重要性筛选重要特征并画图。
得到的特征重要性大于18的属性如下图所示:
可以看到Tiltle_Mr Title_id Gender这三个属性相当重要。而与Title有关的属性都是我们对姓名进行分析得到的,可见一些字符串属性中可能会藏有非常重要的信息,在特种工程中要非常重视而不是将其抛弃。因为我们的原始属性非常少,所以产生的重要属性大都是原始属性的数学组合,派生变量可能并不是必需的,这主要和模型有关,但大多数时候派生变量是没有什么坏处的。对于随机森林这种训练数据想对容易的模型来说,可能一些原始的属性直接用来进行训练也会产生很好的效果,但是作为一道学习题,当然是什么处理办法都要尝试一遍,积累经验啦。
对于随机森林如何得到变脸重要性的,可以看一下scikit learn 的官方文档 scikit-learn.org/stable/auto_examples/ensemble/plot_forest_importances.html#example-ensemble-plot-forest-importances-py
当然在得到重要的特征后,我们就要把不重要的特征去掉了,以提高模型的训练速度(阈值可调的小一点,以保留更多的特征)
1 X = X[:, important_idx][:, sorted_idx] 2 submit_df = submit_df.iloc[:,important_idx].iloc[:,sorted_idx]
现在我们就得到了最终的数据集,终于可以正式用来训练模型了。
上面部分都是用的随机森林的默认参数,但是模型的参数是可调的,我们要调整参数以获得更好的训练。scikit learn 提供了两种参数优化的方法,也是其他工具通用的方法,一是GridSearch,另一个是RandomizedSearch。在这两种情况下,都可以指定每个参数的取值范围,创建一个字典。将参数字典提供给search方法,它就会执行模型所指定的值的组合。对于GridSearch,它测试参数每一个可能的组合。 RandomizedSearch允许指定有多少不同的组合要测试,然后随机选择组合。如果正在使用模型关键参数很多,RandomizedSearch很有用可以帮助节省时间。
1 sqrtfeat = int(np.sqrt(X.shape[1])) 2 minsampsplit = int(X.shape[0]*0.015) 3 # (adapted from http://scikit-learn.org/stable/auto_examples/randomized_search.html) 4 def report(grid_scores, n_top=5): 5 params = None 6 top_scores = sorted(grid_scores, key=itemgetter(1), reverse=True)[:n_top] 7 for i, score in enumerate(top_scores): 8 print("Parameters with rank: {0}".format(i + 1)) 9 print("Mean validation score: {0:.4f} (std: {1:.4f})".format( 10 score.mean_validation_score, np.std(score.cv_validation_scores))) 11 print("Parameters: {0}".format(score.parameters)) 12 print("") 13 14 if params == None: 15 params = score.parameters 16 17 return params 18 # Simple grid test 19 grid_test1 = { "n_estimators" : [1000, 2500, 5000], 20 "criterion" : ["gini", "entropy"], 21 "max_features" : [sqrtfeat-1, sqrtfeat, sqrtfeat+1], 22 "max_depth" : [5, 10, 25], 23 "min_samples_split" : [2, 5, 10,minsampsplit ] } 24 25 forest = RandomForestClassifier(oob_score=True) 26 27 print "Hyperparameter optimization using GridSearchCV..." 28 grid_search = GridSearchCV(forest, grid_test1, n_jobs=-1, cv=10) 29 grid_search.fit(X, y) 30 best_params_from_grid_search = scorereport.report(grid_search.grid_scores_)
经训练得到的参数为 params_score = { "n_estimators" : 10000, "max_features" : sqrtfeat, "min_samples_split" : minsampsplit },结果还是很符合经验结果预测的。
怎么评价这个训练后的模型呢? Learning Curves。《机器学习实战》这本书里有讲,Andrew Ng的公开课里也讲过。主要是偏差方差折衷与测试误差和训练误差的关系。我们应该调整模型来达到测试误差的最小值。sklearn.learning_curve模块可以完成这个功能。 Learning Curves曲线最后表明我们的模型需要更多的数据训练。
在训练时要注意的是,因为幸存者相对未幸存的人数较少,所以数据是不均衡的,可以通过上抽样或下抽样或者调整样本的权重,来获得均衡的训练样本。
然后我们就可以用forest来预测测试集了,bingo!