三种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左右


登录后回复