三种Ensemble的办法(Bagging, boosting, stacking)
-
最近看到了一个相当好的博客,分享给大家
https://towardsdatascience.com/ensemble-methods-bagging-boosting-and-stacking-c9214a10a205
-
预测值和实际值的差距,可以用两部分解释。一部分是Variance,一部分是Bias。
机器学习中一个很著名的东西叫做Bias–variance tradeoff,指的是模型拟合中往往要平衡这两种错误。- Bias指的是 模型的假设和实际的区别。
- Variance指的是模型过于依赖于训练集。大家喜闻乐见的Overfitting就是Variance太高导致的模型预测性下降。
Bagging 和 stacked ensemble办法本质上都是把不同的模型结合到一起来降低模型的Variance,从而获得Variance更低的模型。
我对这两种ensemble的评价是:magical。很多时候不需要做过于复杂的处理,仅仅取得几个模型预测的均值,就能得到比任意单个模型更好的表现。
而Boosting本质是通过加总不同的模型降低模型的Bias。
下面对每一种进行详细的介绍。
-
Bagging (bootstrap aggregating)。Bagging指的是用不同的方法取训练集和测试集,用同样的算法,得到不同的模型后取模型预测的均值(回归问题),或者选择被“投票”最多的模型(分类问题) 。Bagging一个关键的假设是:模型拟合不同数据集的过程的独立的。至于这是什么意思,和Boosting对比就明白啦~继续向下读哦。
-
Boosting
和Bagging不同,Boosting是一个模型加于另一个模型上的。Boosting有两种主要方法。一种是Adaptive Boosting,一种是gradient boosting. Gradient boosting 主要用的算法是三个:Lightgbm, Xgboost和Catboost. -
Stacked ensemble
Stacking和上述两种方法不同的地方在于,它是把不同算法得到的预测值进行平均。平均又有两种方法,一种是简单平均,另一种是加权平均。
简单平均顾名思义,是把不同模型得到的预测值求平均数。听上去简单,实际操作中往往能取得非常好的效果。但是问题在于,各个模型的好坏不一,我们想给好的预测值能高的权重。
所以,一个获得权重的办法是用简单模型的预测值参与回归,求得最优权重
-
最近在研究lightgbm,给大家一个Gradient boosting + Bagging的例子2nd solution using lightgbm
我自己也根据这个思路完成了对Lightgbm的第一次尝试。
如何为lightgbm准备Input
Lightgbm需要把categorical variable都变成整数形式。可以用scikit learn里面的Label Encoder或者Ordinal Encoder. 区别是Ordinal Encoder可以同时转化多列,而Label Encoder只能一次转化一列。因此,也有人说Lable Encoder是用来转化y的。
Lightgbm对于categorical variable有自己的一套处理方法。因为大多数的encoder (one-hot encoder)等都是更适用于线性模型。
举个例子。假设数据中有三种水果香蕉,苹果和鸭梨。平均甜度(因变量y)分别为10,5,2。
先按照甜度排序:香蕉,苹果,鸭梨。每次树选择从哪里分的时候,在lightgbm种有两种情况。
香蕉一组,苹果和鸭梨一组。
香蕉和苹果一组,鸭梨一组。
仔细一品,和Target encoder有点相似。将数据集转化为lightgbm dataset
d_train = lgb.Dataset(X, label=y, feature_name=features, categorical_feature=vars_ind_categorical, #这里写转化为整数形式的categorical variable free_raw_data=False)
-
决定参数
lightgbm可以选择几种不同的算法,参数示例如下。其中比较适合用hyperopt或者GridSearch来调参的是max_depth还有num_leaves。这两个参数越高,模型就越容易过拟合。boosting_type也是非常重要的一个参数。它决定了学习的模式是什么样的。
params = { "boosting_type": "dart" , "objective": "binary" , "metric": "auc" , "learning_rate" : 0.05 , "max_depth" : 7 , "num_leaves": 100 , "feature_fraction": 0.6 , "bagging_fraction": 0.6 , "min_data_in_leaf" : 150 , "bagging_seed": 2018 , "num_threads": 4 } num_boost_round = 5000 #dart模式下没有Early stopping # https://github.com/microsoft/LightGBM/issues/1893 # 原因是使用dart的时候,前面的树会被更新掉 !!! #early_stopping_rounds = 20
-
训练模型
model = lgb.train(params, d_train, num_boost_round = num_boost_round, valid_sets=[d_val], #early_stopping_rounds = early_stopping_rounds, verbose_eval=500 # 控制boost几次显示一次结果 )
-
查看预测结果
pred_train = model.predict(X_train, num_iteration=model.best_iteration)
-
查看变量重要性
import matplotlib.pyplot as plt %matplotlib inline ### 只查看重要性高于1000的feature Visual_features_sub = Visual_features.loc[Visual_features.feature_importance>10000] plt.figure(figsize=(18 ,10)) plt.bar(Visual_features_sub.feature_name, Visual_features_sub.feature_importance, align='center', alpha=0.5) plt.title('Feature importance graph')
-
Bagging
重点来啦!如何把Bagging加入进来
首先,封装上述过程到一个函数中,并把train test split 的seed变成函数的变量之一def train_gbrt(df_train,df_test,features, seed): from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(df_train[features], df_train.target, test_size=0.05, random_state=seed) d_train = lgb.Dataset(X_train, label=y_train, feature_name=features, categorical_feature=vars_ind_categorical, free_raw_data=False) d_val = lgb.Dataset(X_test, label=y_test, feature_name=features, categorical_feature=vars_ind_categorical, free_raw_data=False) # 注意这里用了gbrt,因为gbrt比dart更快,更加适合于用来做bagging params = { "boosting_type": "gbrt" , "objective": "binary" , "metric": "auc" , "learning_rate" : 0.0159 , "max_depth" : 24 , "num_leaves": 494 , "feature_fraction": 0.6 , "bagging_fraction": 0.6 , "min_data_in_leaf" : 250 , "bagging_seed": 2018 , "num_threads": 4 } num_boost_round = 15000#40000 early_stopping_rounds = 800 model = lgb.train(params, d_train, num_boost_round = num_boost_round, valid_sets=[d_val], early_stopping_rounds = early_stopping_rounds, verbose_eval=1000 ) pred_design = model.predict(X_train, num_iteration=model.best_iteration) pred_val = model.predict(X_test, num_iteration=model.best_iteration) from sklearn import metrics score_train = metrics.roc_auc_score(y_train, pred_design) score_val = metrics.roc_auc_score(y_test, pred_val) print(""" Score val :{} Score train: {} """.format(score_val, score_train)) pred_train = model.predict(df_train[features], num_iteration=model.best_iteration) pred_test = model.predict(df_test[features], num_iteration=model.best_iteration) return model, pred_train, pred_test
-
第二步:用不同的seed跑二十次模型
models = [] pred_trains = [] pred_tests = [] for seed in range(500, 520): model, pred_train, pred_test = train_gbrt(df_train,df_test,features, seed) models.append(model) pred_trains.append(pred_train) pred_tests.append(pred_test)
第三步:求得不同模型预测均值
pred_trains_average = np.array(pred_trains).mean(axis = 0)
-
根据我实际的测试,Bagging后的结果确实比单次的结果好很多。大概能提升AUC 0.01左右