首页 > 编程笔记 > Python笔记 阅读:4

Stacking集成模型的原理和实现详解

集成学习一般有两种方式:
还有一种方式就是 Stacking,它结合了 Boosting 和 Bagging 两种集成方式,Stacking 是利用多个基学习器学习原数据,然后将这几个基学习器学习到的数据交给第二层模型进行拟合。

Stacking原理

Stacking 通过模型对原数据拟合的堆叠进行建模,首先通过基学习器学习原数据,然后这几个基学习器都会对原数据进行输出,接着将这几个模型的输出按照列的方式进行堆叠,构成了(m,p)维的新数据,m 代表样本数,p 代表基学习器的个数,然后将新的样本数据交给第二层模型进行拟合,如下图所示:


图 1 Stacking思想原理

图 1 就是 Stacking 思想的原理示意图,为了防止模型过拟合,所以使用 K 折交叉验证。

图 1 首先将特征 x 和标签 y 分别输入到 3 个模型中,然后这 3 个模型分别学习,接着针对 x 给出预测值,并给出概率。此处使用预测值,然后将 3 个模型的输出值按照列的方式进行堆叠,这就形成了新的样本数据,接着将新的样本数据作为标签 x,新数据的标签仍然为原数据的标签 y,将新数据的标签 x、y 交给第二层的模型进行拟合,这个模型是用来融合前一轮 3 个模型的结果。

Stacking 易产生过拟合,所以需对上述方法进行改进,使用 K 折交叉验证的方式,不同之处就是图 1 的每个模型训练了所有的数据,然后输出 y 形成新的数据,使用 K 折交叉验证,每次只训练 K-1 折,然后将剩下 1 折的预测值作为新的数据,这就有效地防止了过拟合。

如果每个模型训练所有的数据,然后用这个模型去预测 y 值,那么生成新数据的 y 非常精确,和真实值差不多,为了增强模型的泛化能力,每次只训练其中一部分数据,然后用剩余部分数据进行预测,如下图所示。


图 2 增强模型的泛化能力

图 2 先利用 K 折交叉验证,将数据分成 4 折切分,就会形成 4 组数据集,其中黄色(带笑脸)的代表训练集,绿色(带三角形)的为验证集,然后将每组的训练集交给模型进行训练,接着对验证集进行预测,就会得到对应验证集(4 种)的输出,然后将每个模型对各自组的验证集预测的结果按照行的方式进行堆叠,会获得完整样本数据的预测值。这只是针对一个模型,与学习器同理,每个模型按照这个方式获得预测值,然后将其按照列合并。

Stacking模型实现

本节以 Boston 数据集为例,利用 Stacking 解决回归问题。

【实例】利用 Stacking 分析 Boston 数据集回归问题。
from sklearn import datasets
from sklearn.model_selection import KFold
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import GradientBoostingRegressor as GBDT
from sklearn.ensemble import ExtraTreesRegressor as ET
from sklearn.ensemble import RandomForestRegressor as RF
from sklearn.ensemble import AdaBoostRegressor as ADA
from sklearn.metrics import r2_score
import pandas as pd
import numpy as np

boston=datasets.load_boston()
X=boston.data
Y=boston.target
df=pd.DataFrame(X, columns=boston.feature_names)
df.head()
运行程序,输出如下:

#数据集划分
X_train, X_test, Y_train, Y_test=train_test_split(X, Y, random_state=123)
#标准化
transfer=StandardScaler()
X_train=transfer.fit_transform(X_train)
X_test=transfer.transform(X_test)
print("训练样例数:"+str(X_train.shape[0]))
print("测试样例数:"+str(X_test.shape[0]))
print("X_train样例:"+str(X_train.shape))
print("Y_train样例:"+str(Y_train.shape))
运行程序,输出如下:

训练样例数:379
测试样例数:127
X_train样例:(379, 13)
Y_train样例:(379,)


下面代码定义第一层模型并训练:
model_num=4
models=[GBDT(n_estimators=100),
             RF(n_estimators=100),
             ET(n_estimators=100),
             ADA(n_estimators=100)]
#第二层模型训练和测试数据集
#第一层每个模型交叉验证将训练集的预测值作为训练数据, 将测试集预测值的平均作为测试数据
X_train_stack=np.zeros((X_train.shape[0], len(models)))
X_test_stack=np.zeros((X_test.shape[0], len(models)))

#第一层训练:10折Stacking
n_folds=10
kf=KFold(n_splits=n_folds)
#kf.split返回划分的索引
for i, model in enumerate(models):
    X_stack_test_n=np.zeros((X_test.shape[0], n_folds))#(test样本数, 10组索引)
    for j, (train_index, test_index)in enumerate(kf.split(X_train)):
        tr_x=X_train[train_index]
        tr_y=Y_train[train_index]
        model.fit(tr_x, tr_y)
        #生成Stacking训练数据集
        X_train_stack[test_index, i]=model.predict(X_train[test_index])
        X_stack_test_n[:, j]=model.predict(X_test)
    #生成Stacking测试数据集
    X_test_stack[:, i]=X_stack_test_n.mean(axis=1)
#查看构建的新数据集
print("X_train_stack样例:"+str(X_train_stack.shape))
print("X_test_stack样例:"+str(X_test_stack.shape))

X_train_stack样例:(379, 4)
X_test_stack样例:(127, 4)
至此,数据集便构建完毕了,接下来进入第二层模型。第二层定义了一个普通的线性模型。

注意,为了防止过拟合,这个模型应该简单些。

#第二层训练
from keras import models
from keras.models import Sequential
from keras.layers import Dense
model_second=Sequential()
model_second.add(Dense(units=1,input_dim=X_train_stack.shape[1]))
model_second.compile(loss='mean_squared_error',optimizer='adam')
model_second.fit(X_train_stack,Y_train,epochs=500)
pred=model_second.predict(X_test_stack)
print("R2:",r2_score(Y_test,pred))
Epoch 1/500
379/379[==============================]-0s 600us/step-loss:8292.2789
Epoch 2/500
379/379[==============================]-0s 63us/step-loss:8088.2254
...
379/379[==============================]-0s 97us/step-loss:10.8623
Epoch 500/500
379/379[==============================]-0s 76us/step-loss:10.8617
R2:0.8617723461580289

#模型评估
from sklearn.metrics import mean_absolute_error
Y_test=np.array(Y_test)
print('M AE:%f',mean_absolute_error(Y_test,pred))
for i in range(len(Y_test)):
    print("Real:%f,Predict:%f"%(Y_test[i],pred[i]))
运行程序,输出如下:

M AE:%f 2.252628333925262
Real:15.000000,Predict:26.671183
Real:26.600000,Predict:26.258823
Real:45.400000,Predict:47.117538
...
Real:13.600000,Predict:15.016025
Real:22.000000,Predict:21.429218
Real:22.200000,Predict:22.229422


如果直接用 Sklearn 中的线性回归,代码为:
from sklearn.linear_model import LinearRegression

model_second=LinearRegression()
model_second.fit(X_train_stack,Y_train)
pred=model_second.predict(X_test_stack)
print("R2:",r2_score(Y_test,pred))
#模型评估
from sklearn.metrics import mean_absolute_error
Y_test=np.array(Y_test)
print('平均绝对误差:%f',mean_absolute_error(Y_test,pred))
for i in range(len(Y_test)):
    print("真实:%f,预测:%f"%(Y_test[i],pred[i]))
运行程序,输出如下:

R2:0.8378818580721008
平均绝对误差:%f 2.125589477978677
真实:15.000000,预测:37.333546
真实:26.600000,预测:26.755009
...
真实:13.600000,预测:12.868088
真实:22.000000,预测:20.509868
真实:22.200000,预测:21.524763

相关文章