机器学习入门-stacking思想和案例

Posted 人工智能与数据分析

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了机器学习入门-stacking思想和案例相关的知识,希望对你有一定的参考价值。

直接复制过来的,侵删。附上原作者的链接

http://wulc.me/2018/01/21/stacking%20%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%80%9D%E6%83%B3%E5%8F%8A%E4%BB%A3%E7%A0%81%E5%AE%9E%E7%8E%B0/

本文主要介绍机器学习中的一种集成学习的方法stacking,本文首先介绍stacking这种方法的思想,然后提供一种实现stacking的思路,能够简单的扩展stacking中的基本模型。

stacking的基本思想

stacking就是将一系列模型的输出结果作为新特征输入到其他模型,这种方法由于实现了模型的层叠,即第一层的模型输出作为第二层模型的输入,第二层模型的输出作为第三层模型的输入,依次类推,最后一层模型输出的结果作为最终结果。本文以两层的stacking为例进行说明。

stacking 的思想也很好理解,这里以论文审稿为例,首先是三个审稿人分别对论文进行审稿,然后分别返回审稿意见给总编辑,总编辑会结合审稿人的意见给出最终的判断,即是否录用。对应于stacking,这里的三个审稿人就是第一层的模型,其输出(审稿人意见)会作为第二层模型(总编辑)的输入,然后第二层模型会给出最终的结果。

stacking 的思想很好理解,但是在实现时需要注意不能有泄漏(leak)的情况,也就是说对于训练样本中的每一条数据,基模型输出其结果时并不能用这条数据来训练。否则就是用这条数据来训练,同时用这条数据来测试,这样会造成最终预测时的过拟合现象,即经过stacking后在训练集上进行验证时效果很好,但是在测试集上效果很差。

为了解决这个泄露问题,需要通过K-Fold方法分别输出各部分样本的结果,这里以5-fold为例,具体步骤如下:
1、将数据划分为5个部分,每次用其中1部分做验证集,其余4部分做训练集,则共训练出5个模型
2、对于训练集,每次训练出一个模型,通过该模型对没有用来训练的验证集进行预测,将预测结果作为验证集的样本的第二层输入,则依次遍历5次后,每个训练样本都可得到其输出结果作为第二层模型的输入。
3、对于测试集,每次训练出一个模型时,都用这个模型对其进行预测,则最终测试集的每个样本都会有5个输出结果,对这些结果取平均作为该样本的第二层输入。

上述过程图示如下:

除此之外,用stacking或者说ensemble这一类方法时还需要注意以下两点:
1、Base model之间的相关性要尽可能的小,从而能够互补模型之间的优势
2、Base Model 之间的性能表现不能差距太大,太差的模型会拖后腿

代码实现

由于需要stacking中每个基模型都需要对数据集进行划分后进行交叉训练,如果为每个模型都写这部分的代码会显得冗余,因此这里提供了一种简单实现stacking的思路。

具体做法就是先实现一个父类,父类中实现了交叉训练的方法,因为这个方法对所有模型都是一致的,然后声明两个方法:train和predict,由于采用的基模型不同,这两个方法的具体实现也不同,因此需要在子类中实现,下面以python为例进行详解。

import numpy as np
from sklearn.model_selection import KFold

class BasicModel(object):
    '''parent class of basic models'''

    def train(self, x_train, y_train, x_val, y_val):
        """return a trained model and eval metric of validation data"""
        pass

    def predict(self, model, x_test):
        """return the predicted result of test data"""
        pass


    def get_oof(self,x_train,y_train,x_test,n_folds=5):
        """K-fold stacking"""
        num_train,num_test = x_train.shape[0],x_test.shape[0]
        oof_train = np.zeros((num_train,))
        oof_test = np.zeros((num_test,))
        oof_test_all_fold = np.zeros((num_test,n_folds))
        aucs = []
        KF = KFold(n_splits=n_folds,random_state=2021)
        for i,(train_index,val_index) in enumerate(KF.split(x_train)):
            print('{0} fold, train {1}, val {2}'.format(i,
                                                        len(train_index),
                                                        len(val_index)))
            x_tra, y_tra = x_train[train_index], y_train[train_index]
            x_val, y_val = x_train[val_index], y_train[val_index]
            model,auc = self.train(x_tra,y_tra,x_val,y_val)
            aucs.append(auc)
            oof_train[val_index] = self.predict(model,x_val)
            oof_test_all_fold[:,i] = self.predict(model,x_test)
        oof_test = np.mean(oof_test_all_fold,axis=1)
        print('all aucs {0}, average {1}'.format(aucs, np.mean(aucs)))
        return oof_train, oof_test

上面最重要的就是进行K-fold训练的get_oof方法,该方法最终返回训练集和测试集在基模型上的预测结果,也就是两个一维向量,长度分别是训练集和测试集的样本数

下面以两个基模型为例进行stacking,分别是xgboost和lightgbm,这两个模型都只需要实现basicmodel中的train和predict方法

xgboost

import xgboost as xgb
class XGBClassifier(BasicModel):
    def __init__(self):
        """set parameters"""
        self.num_rounds=1000
        self.early_stopping_rounds = 15
        self.params = {
            'objective''binary:logistic',
            'eta'0.1,
            'max_depth'8,
            'eval_metric''auc',
            'seed'0,
            'silent' : 0
         }
    def train(self, x_train, y_train, x_val, y_val):
        print('train with xgb model')
        xgbtrain = xgb.DMatrix(x_train, y_train)
        xgbval = xgb.DMatrix(x_val, y_val)
        watchlist = [(xgbtrain,'train'), (xgbval, 'val')]
        model = xgb.train(self.params,
                          xgbtrain,
                          self.num_rounds,
                          watchlist,
                          early_stopping_rounds = self.early_stopping_rounds)
        return model, float(model.eval(xgbval).split()[1].split(':')[1])

    def predict(self, model, x_test):
        print('test with xgb model')
        xgbtest = xgb.DMatrix(x_test)
        return model.predict(xgbtest)

lightgbm

import lightgbm as lgb


class LGBClassifier(BasicModel):
    def __init__(self):
        self.num_boost_round = 2000
        self.early_stopping_rounds = 15
        self.params = {
            'task''train',
            'boosting_type''dart',
            'objective''binary',
            'metric': {'auc''binary_logloss'},
            'num_leaves'80,
            'learning_rate'0.05,
            # 'scale_pos_weight': 1.5,
            'feature_fraction'0.5,
            'bagging_fraction'1,
            'bagging_freq'5,
            'max_bin'300,
            'is_unbalance': True,
            'lambda_l2'5.0,
            'verbose': -1
        }

    def train(self, x_train, y_train, x_val, y_val):
        print('train with lgb model')
        lgbtrain = lgb.Dataset(x_train, y_train)
        lgbval = lgb.Dataset(x_val, y_val)
        model = lgb.train(self.params,
                          lgbtrain,
                          valid_sets=lgbval,
                          verbose_eval=self.num_boost_round,
                          num_boost_round=self.num_boost_round)
        early_stopping_rounds = self.early_stopping_rounds)
        return model, model.best_score['valid_0']['auc']

    def predict(self, model, x_test):
        print('test with lgb model')
        return model.predict(x_test, num_iteration=model.best_iteration)

下一个步骤就是将这两个基模型的输出作为第二层模型的输入,这里选用的第二层模型是线性回归。

首先需要将各个基模型的输出reshape和concatenate成合适的大小

lgb_classifier = LGBClassifier()
lgb_oof_train, lgb_oof_test = lgb_classifier.get_oof(x_train, y_train, x_test)

xgb_classifier = XGBClassifier()
xgb_oof_train, xgb_oof_test = xgb_classifier.get_oof(x_train, y_train, x_test)

input_train = [xgb_oof_train, lgb_oof_train]
input_test = [xgb_oof_test, lgb_oof_test]

stacked_train = np.concatenate([f.reshape(-1, 1) for f in input_train], axis=1)
stacked_test = np.concatenate([f.reshape(-1, 1) for f in input_test], axis=1)

然后用第二层模型进行训练和预测

from sklearn.linear_model import LinearRegression

final_model = LinearRegression()
final_model.fit(stacked_train, y_train)
test_prediction = final_model.predict(stacked_test)


以上是关于机器学习入门-stacking思想和案例的主要内容,如果未能解决你的问题,请参考以下文章

机器学习保姆级入门案例-波士顿房价预测

MyBatis学习简介及入门案例

带你入门Python数据挖掘与机器学习(附代码实例)

波士顿房价预测——机器学习入门级案例

带你入门机器学习

机器学习入门-随机森林温度预测的案例