Stacking的思路及编码

 在kaggle上打比赛的时候发现,利用ensemble的思路对Base model进行集成是一个对最终模型影响较大的环节,能够有效提升分数,同时还能降低overfitting的风险.
 Ensemble思路包括以下几种:

  • Bagging: 用不同的训练数据集的随机子集来对指定个数的Base Model进行训练,最后决策由每个Base Model投票决定.典型应用如名头响亮的随机森林Random Forest.
  • Boosting: 相比 bagging,Boosting的思路更加复杂一些.基本的思路在于迭代训练Base Model,每一轮迭代出的新模型都是基于上一代模型所犯错误修正得来,手段在于加重错误样本的权重.典型应用诸如AdaBoostGradient Boosting等.通常来说,相比Bagging会有更好的效果,但是也是更加容易overfitting的.
  • Blending: 使用不相交的数据训练(无放回采样,bootstrap=False)出不同的Base Model,然后将输出结果进行加权平均.
  • stacking: 相比其他的Ensemble模型,stacking选择了全新的思路.不再直接聚合所有预测器的结果,而是直接利用Base Model的预测结果作为数据源来训练一个更高层次(我也不知道这么描述对不对 :-)的模型.

Stacking的详解:

 根据在kaggle上的经验,Stacking方法相比其他的方法往往能取得更好的一个效果(不知道是不是我以偏概全了 :-).

  1. 关于Stacking的思路
     正如上面所说,stacking的思想在于直接利用Base Model(Layer_1)的预测结果作为更高层次模型(Layer_2,....Layer_n)的训练集.
    下图清晰地展示了这一思路.Blending指的是根据Prediction训练出高层模型(通常称为metal learner 或者 blender)后进行的预测操作.


    Stacking的思路及编码_第1张图片
    Stacking的简单思路图.png
  2. Stacking应用技巧
     在使用Stacking时,如果直接把所有的数据全都喂给Base Model当然是一种很不正确的操作,这会导致严重的过拟合.因此为了高效使用Stacking,通常的做法是使用交叉验证的思路对数据集进行拆分,然后分别训练Base Model.
     在拆分时会留下其中1份(hold-out-set)用来做预测.预测结果是由Base Model预测所得,结果需要保存下来留作第二层的训练使用.在这样一个迭代训练的过程中,每个模型都将被训练n次(n = cv,即交叉验证的折数),做n次预测,将n次预测的结果拼接起来就是一列完整的预测数据.最终我们就可以得到一个训练集行数 * Base Model数量的矩阵,这个矩阵就会作为第二层Model的训练数据. 第二层Model训练完成后,将每个Base Model对测试数据(不同于hold-out-set)的预测拿来让它进行预测,就可以得到最后的输出.

Stacking的思路及编码_第2张图片
kaggle-guide-stacking-diagram.jpg

 以上都还只是两层的Stacking,也就是说还能构造三层 . 四层 甚至五层的stacking.


Stacking的思路及编码_第3张图片
image.png
  1. 以下是实现代码,并给出了我的注解,方便阅读
class Ensemble(object):
    def __init__(self, n_folds, stacker, base_models):
        self.n_folds = n_folds
        self.stacker = 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)              # 测试集
        
         # 创建交叉验证对象fold
        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)))

        # 循环生成S_train 和 S_test 
        for i, clf in enumerate(self.base_models):          # 外层遍历每一个base_model
            S_test_i = np.zeros((T.shape[0], len(folds)))    # 初始化每一个模型的预测集

            for j, (train_idx, test_idx) in enumerate(folds):    # 内层遍历每一份hold-one-out数据
                X_train = X[train_idx]                      # 交叉验证下的训练集
                y_train = y[train_idx]
                X_holdout = X[test_idx]                  # 交叉验证下的hold-one-out数据
                # y_holdout = y[test_idx]
                clf.fit(X_train, y_train)                  # 训练拟合
                y_pred = clf.predict(X_holdout)[:]         # 拿到hold-one-out上的预测结果
                S_train[test_idx, i] = y_pred              # 放入二层训练集S_train中
                S_test_i[:, j] = clf.predict(T)[:]         # 拿到二层测试集
           
            # 因为每个模型都会被训练并预测测试集n_fold次,为了保证二层测试集与训练集同规模,需要对其取均值
            S_test[:, i] = S_test_i.mean(1)     
       
        self.stacker.fit(S_train, y)                      # 二层stacker堆叠器训练
        y_pred = self.stacker.predict(S_test)[:]          # 拿到最终的预测结果
        return y_pred

参考自 https://dnc1994.com/2016/04/rank-10-percent-in-first-kaggle-competition/

你可能感兴趣的:(Stacking的思路及编码)