概述

  • Stacking(有时候也称之为stacked generalization,堆叠泛化)是指训练一个模型用于组合 (combine)其他各个模型。即首先我们先训练多个不同的模型,然后再以之前训练的各个模型的输出为输入来训练一个模型,以得到一个最终的输出。
  • 如果可以选用任意一个组合算法,那么理论上,Stacking可以表示前面提到的各种Ensemble方法。然而,实际中,我们通常使用单层logistic回归作为组合模型。
  • 注意:Stacking有两层,一层是不同的基学习器(classifiers/regressors),第二个是用于组合基学习器的元学习器(meta_classifier/meta_regressor)

Stacking的基本步骤如下:

  1. 将训练数据集划分为多个子集,通常是两个或更多个。
  2. 对于每个子集,使用不同的基础模型进行训练和预测,得到每个基础模型的预测结果。
  3. 将这些预测结果作为新的特征,组合成一个新的训练数据集。
  4. 使用这个新的训练数据集来训练一个元模型,例如逻辑回归、决策树等。
  5. 最后,使用训练好的元模型来对测试数据进行预测。

通过Stacking,基础模型的不同特点和优势可以得到充分的发挥,从而提高整体模型的性能。然而,Stacking也需要谨慎使用,因为它可能会增加模型的复杂性和计算成本,并且在数据集较小或不平衡的情况下可能会导致过拟合问题。因此,在应用Stacking时需要进行适当的调参和验证,以确保获得最佳的性能和泛化能力。

Stacking原理讲解

直观理解

截图_20230716183708.png

图中上半部分是用一个基础模型进行5折交又验证,如:用XGBoost作为基础模型Model1,5折交又验证就是先拿出四折作为training learn(蓝色部分),另外一折作为testing predict(橙色部分)。注意:在stacking中此部分数据会用到整个traing data。如:假设我们整个training data包含10000行数据,testing data包含2500行数据,那么每一次交叉验证其实就是对training data进行划分,在每一次的交叉验证中training learn将会是8000行,testing predict是2000行。

对testing predict进行预测。在整个第一次的交叉验证完成之后我们将会得到关于当前testing predict的预测值,这将会是一个一维2000行的数据,记为a1.注意!在这部分操作完成后,我们还要对数据集原来的整个testingdata进行预测,这个过程会生成2500个预测值,这部分预测值将会作为下一层模型testing data的一部分,记为b1(绿色部分)。因为我们进行的是5折交叉验证,所以以上提及的过程将会进行五次,最终会生成针对testing data数据预测的5列2000行的数据a1,a2,a3,a4,a5,对testingset的预测会是5列2500行数据b1,b2,b3,b4,b5。

在完成对Model1的整个步骤之后,我们可以发现a1,a2,a3,a4,a5其实就是对原来整个training data的预测值,将他们拼凌起来,会形成一个10000行一列的矩阵,记为A1.而对于b1.b2,b3,b4.b5这部分数据,我们将各部分相加取平均值,得到一个2500行一列的矩阵,记为B1

以上就是stacking中一个模型的完整流程,stacking中同一层通常包含多个模型,假设还有Model2:LR Model3:RF,Model4:GBDT,Model5:SVM,对于这四个模型,我们可以重复以上的步骤,在整个流程结束之后,我们可以得到新的A2,A3,A4A5,B2,B3,B4B5矩阵

在此之后,我们把A1,A2,A3,A4A5并列合并得到一个10000行五列的矩阵作为新的training data,B1,B2,B3.B4.B5并列合并得到一个2500行五列的矩阵作为新的testing data。让下一层的模型(元学习器),基于他们进一步训练

Stacking(叠加)是一种集成学习方法,通过将多个基础模型的预测结果结合起来,以提高整体预测的准确性。其基本原理如下:

  1. 数据集划分:首先,将训练数据集划分为多个子集,通常是两个或更多个。这些子集可以按照不同的方式划分,例如随机划分或使用交叉验证。
  2. 基础模型训练和预测:对于每个子集,使用不同的基础模型进行训练和预测。这些基础模型可以是任何机器学习算法,如决策树、支持向量机、神经网络等。每个基础模型都会对子集进行训练,并生成对未知数据的预测结果。
  3. 特征组合:将每个基础模型的预测结果作为新的特征,组合成一个新的训练数据集。这些预测结果可以作为原始特征的补充,提供更多的信息来训练元模型。
  4. 元模型训练:使用新的训练数据集来训练一个元模型。元模型可以是任何机器学习算法,如逻辑回归、随机森林、梯度提升等。元模型的目标是学习如何结合基础模型的预测结果,以最大化整体模型的准确性。
  5. 预测:最后,使用训练好的元模型来对测试数据进行预测。元模型会根据基础模型的预测结果进行组合和加权,生成最终的预测结果。

通过Stacking,不同基础模型的优势和特点可以得到充分的发挥,从而提高整体模型的性能和泛化能力。然而,Stacking也需要进行适当的调参和验证,以避免过拟合和提高模型的稳定性。

训练阶段

截图_20230716195032.png

预测阶段

截图_20230716195059.png

Stacking优缺点

Stacking作为一种集成学习方法,具有以下优点和缺点:

优点:

  1. 提高预测性能:通过结合多个基础模型的预测结果,Stacking可以显著提高整体模型的准确性和泛化能力。它可以充分利用不同模型的优势和特点,从而提供更准确的预测结果。
  2. 灵活性:Stacking可以集成各种类型的模型,包括线性模型、非线性模型、树模型等。这使得Stacking非常灵活,可以适应不同类型的数据和问题。
  3. 可解释性:与其他集成方法相比,Stacking在生成最终预测时使用了多个模型的预测结果。这可以提供更多的信息来解释预测结果,使得模型的解释性更强。

缺点:

  1. 计算资源和时间消耗:由于Stacking涉及到多个模型的训练和预测,因此需要更多的计算资源和时间。这可能限制了Stacking在大规模数据集和实时预测任务中的应用。
  2. 风险过拟合:如果不适当地使用Stacking,可能会导致过拟合问题。当基础模型过于复杂或训练数据集过小时,Stacking容易过度依赖训练数据,导致在测试数据上的性能下降。
  3. 超参数选择:Stacking需要选择合适的模型和超参数配置,以获得最佳的性能。这可能需要进行大量的实验和调参,增加了模型的复杂性和训练的难度。

综上所述,Stacking作为一种集成学习方法,在提高预测性能和灵活性方面具有明显的优势。然而,它也存在一些限制,如计算资源和时间消耗、风险过拟合以及超参数选择的挑战。因此,在应用Stacking时需要权衡其优缺点,并根据具体问题和数据情况进行适当的调整和优化。

Stacking安装

Stacking在sklearn库中没有API接口,可以使用mlxtend库

安装方式:

Stacking API

  • StackingClassifier(classifiers, meta_classifier, use_probas=False, average_probas=False, verbose=0, use_features_in_secondary=False)

参数:

  • classifiers:基分类器,数组形式,[cl1, cl2, cl3]。每个基分类器的属性被存储在类属性self.clfs_.
  • meta_classifier:目标分类器,即将前面分类器合起来的分类器
  • use_probas:bool(default:False),如果设置为True,那么目标分类器的输入就是前面分类输出的类别概率值而不是类别标签
  • average_probas:bool(default:False),用来设置上一个参数当使用概率值输出的时候是否使用平均值。
  • verbose:int, optional(default=0)。用来控制使用过程中的日志输出,当verbose=0时,什么也不输出,verbose=1,输出回归器的序号和名字。verbose=2,输出详细的参数信息。verbose>2,自动将verbose设置为小于等于2的,verbose-2.
  • use_features_in_secondary: bool(default:False)。如果设置为True,那么最终的目标分类器就被基分类器差生的数据和最初的数据集同时训练。如果设置为False,最终的分类器只会使用基分类器产生的数据训练。

属性:

  • clfs:每个基分类器的属性,listshape为[n_classifiers]。
  • meta_clf_:最终目标分类器的属性

方法:

  • fit(x,y)
  • fit_transform(X, y=None, fit_params)
  • get_params(deep=True),如果是使用sklearn的Gridsearch方法,那么返回分类器的各项参数。
  • predict(X)
  • predict_proba(X)
  • score(x, y, sample_weight=None),对于给定数据集和给定label,返回评价accuracy
  • set_params(params),设置分类器的参数,params的设置方法和sklearn的格式一样

注:回归的API StackingRegressor(regressors, meta_regressor)基本类似

Stacking 项目案例应用

截图_20230716200644.png

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl

mpl.rcParams['font.sans-serif'] = [u'simHei']

import time

from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline

from mlxtend.classifier import StackingClassifier

from mlxtend.feature_selection import ColumnSelector

###1、读取数据
datas = pd.read_csv('iris.data', sep=',', header=None, names=['X1', 'X2', 'X3', 'X4', 'Y'])
# print(datas.head())
# print(datas.info())

### 2、数据清洗

### 3、获取特征属性X和目标属性Y
X = datas.iloc[:, :-1]
Y = datas.iloc[:, -1]
# print(X.shape)
# print(Y.shape)
# print(Y.value_counts())  ##看下目标属性的值

# LabelEncoder  0,1, 2
labelencoder = LabelEncoder()
# print(Y.ravel())
Y = labelencoder.fit_transform(Y)
# print(Y)

### 4、分割数据集
x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=0.2, random_state=28)
# print(x_train.shape)
# print(y_train.shape)

### 5、特征工程

### 模型构建
# a、构造基学习器 knn、RF、softmax、GBDT。。。。
knn = KNeighborsClassifier(n_neighbors=7)
softmax = LogisticRegression(C=0.1, solver='lbfgs', multi_class='multinomial', fit_intercept=False)
gbdt = GradientBoostingClassifier(learning_rate=0.1, n_estimators=100, max_depth=3)
rf = RandomForestClassifier(max_depth=5, n_estimators=150)

# b、元学习器
lr = LogisticRegression(C=0.1, solver='lbfgs', multi_class='multinomial')

### stacking学习器
'''
1、最基本的使用方法,用前面基学习器的输出作为元学习器的输入
2、使用基学习器的输出类别的概率值作为元学习器输入,use_probas=True,若average_probas=True,那么这些基分类器对每一个类别产生的概率进行平均,否者直接拼接
 classifier1  = [0.2,0.5,0.3]
 classifier2  = [0.3,0.3,0.4]
  average_probas=True: [0.25,0.4,0.35]
  average_probas=Flase: [0.2,0.5,0.3,0.3,0.3,0.4]
  
3、对训练集中的特征维度进行操作,每次训练不同的基学习器的时候用不同的特征,比如我再训练KNN的时候只用前两个特征,训练RF的时候用其他的几个特征
    通过pipline来实现
'''
'''
classifiers, 基学习器
meta_classifier, 元学习器
use_probas=False, 
drop_last_proba=False,
average_probas=False, 
verbose=0,
use_features_in_secondary=False,
store_train_meta_features=False,
use_clones=True
'''

###方式一
stacking01 = StackingClassifier(classifiers=[knn, softmax, gbdt, rf],
                                meta_classifier=lr)


###方式二
stacking02 = StackingClassifier(classifiers=[knn, softmax, gbdt, rf],
                                meta_classifier=lr,
                                use_probas=True,
                                average_probas=False)

###方式三
# 基学习器
pipe_knn = Pipeline([('x', ColumnSelector([0, 1])),
                     ('knn', knn)])
pipe_softmax = Pipeline([('x', ColumnSelector([2, 3])),
                         ('softmax', softmax)])
pipe_rf = Pipeline([('x', ColumnSelector([0, 3])),
                    ('rf', rf)])
pipe_gbdt = Pipeline([('x', ColumnSelector([1, 2])),
                      ('gbdt', gbdt)])
##stacking
stacking03 = StackingClassifier(classifiers=[pipe_knn, pipe_softmax, pipe_rf, pipe_gbdt],
                                meta_classifier=lr)

###模型训练与比较
scores_train = []
scores_test = []
models = []
times = []

for clf, modelname in zip([knn, softmax, gbdt, rf, stacking01, stacking02, stacking03],
                          ['knn', 'softmax', 'gbdt', 'rf', 'stacking01', 'stacking02', 'stacking03']):
    print('start:%s' % (modelname))
    start = time.time()
    clf.fit(x_train, y_train)
    end = time.time()
    print('耗时:{}'.format(end - start))
    score_train = clf.score(x_train, y_train)
    score_test = clf.score(x_test, y_test)
    scores_train.append(score_train)
    scores_test.append(score_test)
    models.append(modelname)
    times.append(end - start)
print('scores_train:', scores_train)
print('scores_test', scores_test)
print('models:', models)
print('开始画图----------')
plt.figure(num=1)
plt.plot([0, 1, 2, 3, 4, 5, 6], scores_train, 'r', label=u'训练集')
plt.plot([0, 1, 2, 3, 4, 5, 6], scores_test, 'b', label=u'测试集')
plt.title(u'鸢尾花数据不同分类器准确率比较', fontsize=16)
plt.xticks([0, 1, 2, 3, 4, 5, 6], models, rotation=0)
plt.legend(loc='lower left')
plt.figure(num=2)
plt.plot([0, 1, 2, 3, 4, 5, 6], times)
plt.title(u'鸢尾花数据不同分类器训练时间比较', fontsize=16)
plt.xticks([0, 1, 2, 3, 4, 5, 6], models, rotation=0)
plt.legend(loc='lower left')
plt.show()

截图_20230716200720.png
测试数据X的格式:(200, 6)
训练数据Y的类型:<class ‘pandas.core.series.Series’>
stacking训练集评分: 0.9691522388409968
stacking测试集评分: 0.8584740371166596
线性回归训练集评分: 0.5726771680655705
线性回归测试集评分: 0.5583119218346266

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import time
from sklearn.model_selection import train_test_split


from mlxtend.regressor import StackingRegressor
from sklearn.linear_model import LinearRegression
from sklearn.linear_model import Ridge
from sklearn.svm import SVR
from sklearn.neighbors import KNeighborsRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import GridSearchCV

mpl.rcParams['font.sans-serif'] = [u'simHei']

# 1. 加载数据(数据一般存在于磁盘或者数据库)
path = 'household_power_consumption_1000_2.txt'
df = pd.read_csv(path, sep=';')

# 2. 数据清洗
df.replace('?', np.nan, inplace=True)
df = df.dropna(axis=0, how='any')

# 3. 根据需求获取最原始的特征属性矩阵X和目标属性Y
def date_format(dt):
    date_str = time.strptime(' '.join(dt), '%d/%m/%Y %H:%M:%S')
    return [date_str.tm_year, date_str.tm_mon, date_str.tm_mday, date_str.tm_hour, date_str.tm_min, date_str.tm_sec]

X = df.iloc[:, 0:2]
X = X.apply(lambda row: pd.Series(date_format(row)), axis=1)
print(X.shape)
Y = df.iloc[:, 4].astype(np.float32)

# 4. 数据分割
x_train, x_test, y_train, y_test = train_test_split(X, Y, train_size=0.8, random_state=28)
print("训练数据X的格式:{}, 以及类型:{}".format(x_train.shape, type(x_train)))
print("测试数据X的格式:{}".format(x_test.shape))
print("训练数据Y的类型:{}".format(type(y_train)))

##初始化基模型
linear  = LinearRegression(fit_intercept=True)
ridge = Ridge(alpha=0.1)
knn = KNeighborsRegressor(weights='distance')
rf = RandomForestRegressor(n_estimators=100,max_depth=3)

##组合模型/元模型
svr_rbf = SVR(kernel='rbf',gamma=0.1,C=0.1)
###stacking
stackingreg = StackingRegressor(regressors=[linear,ridge,knn,rf],meta_regressor=svr_rbf)



# params = {'linearregression__fit_intercept': [True,False],
#           'ridge__alpha': [0.01,0.1, 1.0, 10.0],
#           'kneighborsregressor__n_neighbors':[1,3,5,7,9],
#           'randomforestregressor__n_estimators':[50,100,150],
#           'randomforestregressor__max_depth':[1,3,5,7,9],
#           'meta_regressor__C': [0.1, 1.0, 10.0],
#           'meta_regressor__gamma': [0.1, 1.0, 10.0]}
#

# grid = GridSearchCV(estimator=stackingreg,
#                     param_grid=params,
#                     cv=5,
#                     refit=True)
# print(stackingreg.get_params().keys())
# """
#
# """
# # import sys
# # sys.exit(0)
# grid.fit(x_train, y_train)
# print("最优参数:{}".format(grid.best_params_))
""""

"""
# print("最优参数对应的最优模型:{}".format(grid.best_estimator_))
# print("最优模型对应的这个评估值:{}".format(grid.best_score_))

# ## 网格调参后进行训练
stackingreg.fit(x_train,y_train)
print(stackingreg.score(x_train,y_train))
print(stackingreg.score(x_test,y_test))

# ###画图展示
y_hat = stackingreg.predict(x_test)
plt.plot(range(len(x_test)),y_test,'r',label=u'真实值')
plt.plot(range(len(x_test)),y_hat,'b',label=u'测试值')
plt.legend()
plt.title('时间和电压回归预测')
plt.show()

linear.fit(x_train,y_train)
print(linear.score(x_train,y_train))
print(linear.score(x_test,y_test))
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐