一张图看懂集成学习之Stacking

作者: 小墨 分类: 传统机器学习,集成学习 发布时间: 2021-10-16 17:41 访问量:13,543
FavoriteLoading收藏

一、原理详解

Stacking与bagging和boosting不同的是它可以由不同的基学习器组成,比如基学习器可以是随机森林、支持向量机、神经网络、梯度提升树等混合组成,而bagging和boosting通常基学习器是同一种类别,只是局部参数不同而已,这样有时会显得乏力。

Stacking主要由初极学习器和次级学习期组成,其中原始数据首先通过初学习期得到输出,然后该输出作为次级学习期的输入,这两者的标签y始终为原始数据的标签y,即y值一样。以上图为例来说明数据的传输过程,假设初级学习器有两种,分别为XGB和RF

  1. 使用交叉验证来训练每个初级学习器。假设训练集共100个样本,使用5折交叉验证,则训练集被分为4份,那么每次选其中的4份作为训练,1份作为测试,每次会得到(20,1)的测试结果,5次下来共得到(5,20,1)的预测结果,将其进行纵向拼接,会得到(100,1)的矩阵X_meta_train,即次级模型的输入。那么(100,1)的矩阵刚好是训练集在该初级学习器的预测结果。

  2. 在训练每个初级学习器的时候,测试样本在每一折交叉验证的时候都会进行测试,以1中所举的例子来说,假设有50个测试集,那么5折交叉验证下来会得到5个(50,1)的矩阵,对其取平均值,得到(50,1)的矩阵X_meta_test。

  3. 如果有两个初级学习器的话,那么最终在训练次级学习器前会得到2个(100,1)和(50,1)的矩阵,对其进行横向拼接,得到(100,2)和(50,2)的矩阵,分别作为次级学习器的训练集X和测试集X。

  4. 最终得到所需的模型。

二、代码实现

#随机森林模型
def random_forest(min_samples_split, n_estimators):
    return RandomForestRegressor(min_samples_split=min_samples_split, n_estimators=n_estimators,max_depth=8,min_samples_leaf=1)
#支持向量机SVM
def SVM(kernel,gamma,C):
    return svm.SVR(kernel=kernel,gamma=gamma,C=C,degree=2)
#GBM模型
def GBM(num_leves,n_estimators):
    return lgb.LGBMRegressor(objective='regression',num_leaves=num_leves,n_estimators=n_estimators,max_depth=8)
#归一化
def mean_std(df):
    mean=df.iloc[:,1:].mean(axis=0)
    std=df.iloc[:,1:].std(axis=0)+1e-5
    df.iloc[:,1:]=(df.iloc[:,1:]-mean)/std
    return df,mean[-1],std[-1]
#定义stacking训练方法
def get_stacking(clf, x_train, y_train, x_test,mean,std,n_folds=10):
    train_num, test_num = x_train.shape[0], x_test.shape[0]
    second_level_train_set = np.zeros((train_num,))
    second_level_test_set = np.zeros((test_num,))
    test_nfolds_sets = np.zeros((test_num, n_folds))
    kf = KFold(n_splits=n_folds)

    for i,(train_index, test_index) in enumerate(kf.split(x_train)):
        x_tra, y_tra = x_train[train_index], y_train[train_index]
        x_tst, y_tst =  x_train[test_index], y_train[test_index]
        #模型训练
        clf.fit(x_tra, y_tra)
        #结果预测
        pre_result=clf.predict(x_tst)
        pre_result=pre_result*std+mean
        #标签值
        y_tst=y_tst*std+mean
        second_level_train_set[test_index] = pre_result
        y_test=clf.predict(x_test)
        y_test=y_test*std+mean
        test_nfolds_sets[:,i] = y_test

    second_level_test_set[:] = test_nfolds_sets.mean(axis=1)
    return second_level_train_set, second_level_test_set
#stacking最优参数确定
def stacking_parameter_sure(X,Y,mean,std):
    #参数范围设置
    start1=2
    end1=5
    start2=2
    end2=5
    min_samples_split_list=[i for i in range(start1,end1)]
    n_estimators_list=[i for i in range(start2,end2)]
    #交叉验证
    kf = KFold(n_splits=10, shuffle=True, random_state=12345)
    R2=[]
    for min_samples_split in min_samples_split_list:
        r2_list=[]
        for n_estimaters in n_estimators_list:
            temp_r2_list = []
            for train_index, test_index in kf.split(X):
                model = random_forest(min_samples_split=min_samples_split,n_estimators=n_estimaters)
                x_train, x_test, y_train, y_test = X[train_index], X[test_index], \
                                                   Y[train_index], Y[test_index]
                model.fit(x_train,y_train)
                y_pre = model.predict(x_test)
                y_pre=y_pre*std+mean
                y_test=y_test*std+mean
                r2=r2_score(y_test,y_pre)
                print(r2)
                temp_r2_list.append(r2)
            r2_list.append(np.mean(temp_r2_list))
        R2.append(r2_list)
    R2_view(np.array(R2),[i for i in range(start1,end1)],[i for i in range(start2,end2)],'N','S')
#模型保存
def save_model(model,model_name):
    if not os.path.exists('stacking'):
        os.mkdir('stacking')
    with open('stacking/'+model_name+'.pickle', 'wb') as f:
        pickle.dump(model, f)
#stacking最优参数确定
def stacking_predict_model(file_path,is_pre=False):
    df=pd.read_excel(file_path,header=0)
    df.dropna(how='any', inplace=True)
    df, mean, std = mean_std(df)
    train_data = df.values[:, 1:-1]
    train_target = df.values[:, -1]
    x_train, x_test, y_train, y_test = train_test_split(train_data, train_target, test_size=0.1, random_state=12345)
    #定义初级分类器
    RF=random_forest(3, 10)
    SVR=SVM('rbf',0.1,1)
    gbm=GBM(3,10)
    train_sets = []
    test_sets = []
    for clf in [RF,SVR,gbm]:
        train_set, test_set = get_stacking(clf, x_train, y_train, x_test,mean,std)
        train_sets.append(train_set)
        test_sets.append(test_set)

    meta_train = np.concatenate([result_set.reshape(-1,1) for result_set in train_sets], axis=1)
    meta_test = np.concatenate([y_test_set.reshape(-1,1) for y_test_set in test_sets], axis=1)
    if is_pre:
        #预测结果
        dt_model = random_forest(2,3)
        dt_model.fit(meta_train, y_train)
        y_pre = dt_model.predict(meta_test)
        y_pre=y_pre*std+mean
        y_test=y_test*std+mean
        r2 = r2_score(y_test, y_pre)
        print(r2)
        #模型保存
        save_model(RF,'rf')
        save_model(SVR,'svr')
        save_model(gbm,'gbm')
        save_model(dt_model,'main')
    else:
        # 使用决策树作为我们的次级分类器
        stacking_parameter_sure(meta_train, y_train, mean, std)
#stacking预测结果生成
def stacking_predict_write(file_path,is_nor=True):
    df=pd.read_excel(file_path,header=0)
    df.dropna(how='any', inplace=True)
    df_copy=df.copy()
    if is_nor:
        df, mean, std = mean_std(df)
    else:
        mean = 0
        std = 1 + 1e-5
    x=df.values[:,1:-1]
    with open('stacking/rf.pickle', 'rb') as f:
        RF_model = pickle.load(f)
        f.close()
    with open('stacking/svr.pickle', 'rb') as f:
        svr_model = pickle.load(f)
        f.close()
    with open('stacking/gbm.pickle', 'rb') as f:
        gbm_model = pickle.load(f)
        f.close()
    with open('stacking/main.pickle', 'rb') as f:
        main_model = pickle.load(f)
        f.close()
    # 测试读取后的Model
    train_sets=[]
    train_sets.append(RF_model.predict(x)*std+mean)
    train_sets.append(svr_model.predict(x) * std + mean)
    train_sets.append(gbm_model.predict(x) * std + mean)
    meta_train = np.concatenate([result_set.reshape(-1, 1) for result_set in train_sets], axis=1)
    predict=main_model.predict(meta_train)
    predict=predict*std+mean
    df_copy['预测结果']=predict
    df_copy.to_csv('stacking.csv',encoding='GBK')

最后在训练模型时,直接调用stacking_predict_model(file_path,is_pre=False)函数,is_pre为False时,通过网格搜索确定最优超参数,is_pre为True时,训练以及保存所有模型。

     

如果觉得小墨的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

一条评论

发表评论