集成学习之Stacking详解

青旅半醒 2023-07-22 09:15 30阅读 0赞

1、Stacking原理

stacking 就是当用初始训练数据学习出若干个基学习器后,将这几个学习器的预测结果作为新的训练集,来学习一个新的学习器。
在这里插入图片描述
最初的想法是:
1:用数据集D来训练h1,h2,h3…,

2:用这些训练出来的初级学习器在数据集D上面进行预测得到次级训练集。

3:用次级训练集来训练次级学习器。

但是这样的实现是有很大的缺陷的。在原始数据集D上面训练的模型,然后用这些模型再D上面再进行预测得到的次级训练集肯定是非常好的。会出现过拟合的现象。
次级训练集的构成不是直接由模型在训练集D上面预测得到,而是使用交叉验证的方法,将训练集D分为k份,对于每一份,用剩余数据集训练模型,然后预测出这一份的结果。重复上面步骤,直到每一份都预测出来。这样就不会出现上面的过拟合这种情况。并且在构造次级训练集的过程当中,顺便把测试集的次级数据也给构造出来了。

对于我们所有的初级训练器,都要重复上面的步骤,才构造出来最终的次级训练集和次级测试集。

2、Stacking 的基本思想

将个体学习器结合在一起的时候使用的方法叫做结合策略。对于分类问题,我们可以使用投票法来选择输出最多的类。对于回归问题,我们可以将分类器输出的结果求平均值。

上面说的投票法和平均法都是很有效的结合策略,还有一种结合策略是使用另外一个机器学习算法来将个体机器学习器的结果结合在一起,这个方法就是Stacking。

在stacking方法中,我们把个体学习器叫做初级学习器,用于结合的学习器叫做次级学习器或元学习器(meta-learner),次级学习器用于训练的数据叫做次级训练集。次级训练集是在训练集上用初级学习器得到的。

我们贴一张周志华老师《机器学习》一张图来说一下stacking学习算法。
在这里插入图片描述
过程1-3 是训练出来个体学习器,也就是初级学习器。

过程5-9是 使用训练出来的个体学习器来得预测的结果,这个预测的结果当做次级学习器的训练集。

过程11 是用初级学习器预测的结果训练出次级学习器,得到我们最后训练的模型。

如果想要预测一个数据的输出,只需要把这条数据用初级学习器预测,然后将预测后的结果用次级学习器预测便可。

3、Stacking分类应用

这里我们用二分类的例子做介绍。
例如我们用 RandomForestClassifier, ExtraTreesClassifier, GradientBoostingClassifier 作为第一层学习器(当然这里我们可以添加更多的分类器,也可以用不同的特征组合但是同样的学习方法作为基分类器):

  1. clfs = [
  2. RandomForestClassifier(n_estimators = n_trees, criterion = 'gini'),
  3. ExtraTreesClassifier(n_estimators = n_trees * 2, criterion = 'gini'),
  4. GradientBoostingClassifier(n_estimators = n_trees),
  5. ]

接着要训练第一层学习器,并得到第二层学习器所需要的数据,这里会用到 k 折交叉验证。我们首先会将数据集进行一个划分,比如使用80%的训练数据来训练,20%的数据用来测试。

  1. dev_cutoff = len(Y) * 4/5
  2. X_dev = X[:dev_cutoff]
  3. Y_dev = Y[:dev_cutoff]
  4. X_test = X[dev_cutoff:]
  5. Y_test = Y[dev_cutoff:]

然后对训练数据通过交叉验证训练 clf,并得到第二层的训练数据 blend_train,同时,在每个基分类器的每一折交叉验证中,我们都会对测试数据进行一次预测,以得到我们blend_test,二者的定义如下:

  1. blend_train = np.zeros((X_dev.shape[0], len(clfs))) # Number of training data x Number of classifiers
  2. blend_test = np.zeros((X_test.shape[0], len(clfs))) # Number of testing data x Number of classifiers

按照上面说的,blend_train基于下面的方法得到,注意,下图是对于一个分类器来说的,所以每个分类器得到的blend_train的行数与用于训练的数据一样多,所以blend_train的shape为X_dev.shape[0]*len(clfs),即训练集长度 * 基分类器个数:
在这里插入图片描述
而对于第二轮的测试集blend_test来说,由于每次交叉验证的过程中都要进行一次预测,假设我们是5折交叉验证,那么对于每个分类器来说,得到的blend_test的shape是测试集行数 * 交叉验证折数,此时的做法是,对axis=1方向取平均值,以得到测试集行数 * 1 的测试数据,所以总的blend_test就是测试集行数 * 基分类器个数,可以跟blend_train保持一致:

在这里插入图片描述
得到blend_train 和 blend_test的代码如下:

  1. for j, clf in enumerate(clfs):
  2. print 'Training classifier [%s]' % (j)
  3. blend_test_j = np.zeros((X_test.shape[0], len(skf))) # Number of testing data x Number of folds , we will take the mean of the predictions later
  4. for i, (train_index, cv_index) in enumerate(skf):
  5. print 'Fold [%s]' % (i)
  6. # This is the training and validation set
  7. X_train = X_dev[train_index]
  8. Y_train = Y_dev[train_index]
  9. X_cv = X_dev[cv_index]
  10. Y_cv = Y_dev[cv_index]
  11. clf.fit(X_train, Y_train)
  12. # This output will be the basis for our blended classifier to train against,
  13. # which is also the output of our classifiers
  14. blend_train[cv_index, j] = clf.predict(X_cv)
  15. blend_test_j[:, i] = clf.predict(X_test)
  16. # Take the mean of the predictions of the cross validation set
  17. blend_test[:, j] = blend_test_j.mean(1)

接着我们就可以用 blend_train, Y_dev 去训练第二层的学习器 LogisticRegression(当然也可以是别的分类器,比如lightGBM,XGBoost):

  1. bclf = LogisticRegression()
  2. bclf.fit(blend_train, Y_dev)

最后,基于我们训练的二级分类器,我们可以预测测试集 blend_test,并得到 score:

  1. Y_test_predict = bclf.predict(blend_test)
  2. score = metrics.accuracy_score(Y_test, Y_test_predict)
  3. print 'Accuracy = %s' % (score)

如果是多分类怎么办呢,我们这里就不能用predict方法啦,我么要用的是predict_proba方法,得到基分类器对每个类的预测概率代入二级分类器中训练,修改的部分代码如下:

  1. blend_train = np.zeros((np.array(X_dev.values.tolist()).shape[0], num_classes*len(clfs)),dtype=np.float32) # Number of training data x Number of classifiers
  2. blend_test = np.zeros((np.array(X_test.values.tolist()).shape[0], num_classes*len(clfs)),dtype=np.float32) # Number of testing data x Number of classifiers
  3. # For each classifier, we train the number of fold times (=len(skf))
  4. for j, clf in enumerate(clfs):
  5. for i, (train_index, cv_index) in enumerate(skf):
  6. print('Fold [%s]' % (i))
  7. # This is the training and validation set
  8. X_train = X_dev[train_index]
  9. Y_train = Y_dev[train_index]
  10. X_cv = X_dev[cv_index]
  11. X_train = np.concatenate((X_train, ret_x),axis=0)
  12. Y_train = np.concatenate((Y_train, ret_y),axis=0)
  13. clf.fit(X_train, Y_train)
  14. blend_train[cv_index, j*num_classes:(j+1)*num_classes] = clf.predict_proba(X_cv)
  15. blend_test[:, j*num_classes:(j+1)*num_classes] += clf.predict_proba(X_test)
  16. blend_test = blend_test / float(n_folds)

上面的代码修改的主要就是blend_train和blend_test的shape,可以看到,对于多分类问题来说,二者的第二维的shape不再是基分类器的数量,而是class的数量*基分类器的数量,这是大家要注意的,否则可能不会得到我们想要的结果。

构造stacking方法

我们写一个stacking方法,下面是它的实现代码:

  1. import numpy as np
  2. from sklearn.model_selection import KFold
  3. def get_stacking(clf, x_train, y_train, x_test, n_folds=10):
  4. """
  5. 这个函数是stacking的核心,使用交叉验证的方法得到次级训练集
  6. x_train, y_train, x_test 的值应该为numpy里面的数组类型 numpy.ndarray .
  7. 如果输入为pandas的DataFrame类型则会把报错"""
  8. train_num, test_num = x_train.shape[0], x_test.shape[0]
  9. second_level_train_set = np.zeros((train_num,))
  10. second_level_test_set = np.zeros((test_num,))
  11. test_nfolds_sets = np.zeros((test_num, n_folds))
  12. kf = KFold(n_splits=n_folds)
  13. for i,(train_index, test_index) in enumerate(kf.split(x_train)):
  14. x_tra, y_tra = x_train[train_index], y_train[train_index]
  15. x_tst, y_tst = x_train[test_index], y_train[test_index]
  16. clf.fit(x_tra, y_tra)
  17. second_level_train_set[test_index] = clf.predict(x_tst)
  18. test_nfolds_sets[:,i] = clf.predict(x_test)
  19. second_level_test_set[:] = test_nfolds_sets.mean(axis=1)
  20. return second_level_train_set, second_level_test_set
  21. #我们这里使用5个分类算法,为了体现stacking的思想,就不加参数了
  22. from sklearn.ensemble import (RandomForestClassifier, AdaBoostClassifier,
  23. GradientBoostingClassifier, ExtraTreesClassifier)
  24. from sklearn.svm import SVC
  25. rf_model = RandomForestClassifier()
  26. adb_model = AdaBoostClassifier()
  27. gdbc_model = GradientBoostingClassifier()
  28. et_model = ExtraTreesClassifier()
  29. svc_model = SVC()
  30. #在这里我们使用train_test_split来人为的制造一些数据
  31. from sklearn.datasets import load_iris
  32. from sklearn.model_selection import train_test_split
  33. iris = load_iris()
  34. train_x, test_x, train_y, test_y = train_test_split(iris.data, iris.target, test_size=0.2)
  35. train_sets = []
  36. test_sets = []
  37. for clf in [rf_model, adb_model, gdbc_model, et_model, svc_model]:
  38. train_set, test_set = get_stacking(clf, train_x, train_y, test_x)
  39. train_sets.append(train_set)
  40. test_sets.append(test_set)
  41. meta_train = np.concatenate([result_set.reshape(-1,1) for result_set in train_sets], axis=1)
  42. meta_test = np.concatenate([y_test_set.reshape(-1,1) for y_test_set in test_sets], axis=1)
  43. #使用决策树作为我们的次级分类器
  44. from sklearn.tree import DecisionTreeClassifier
  45. dt_model = DecisionTreeClassifier()
  46. dt_model.fit(meta_train, train_y)
  47. df_predict = dt_model.predict(meta_test)
  48. print(df_predict)
  49. 输出结果如下(因为是随机划分的,所以每次运行结果可能不一样):
  50. [1 0 1 1 1 2 1 2 2 2 0 0 1 2 2 1 0 2 1 0 0 1 1 0 0 2 0 2 1 2]

构造stacking类

事实上还可以构造一个stacking的类,它拥有fit和predict方法

  1. from sklearn.model_selection import KFold
  2. from sklearn.base import BaseEstimator, RegressorMixin, TransformerMixin, clone
  3. import numpy as np
  4. #对于分类问题可以使用 ClassifierMixin
  5. class StackingAveragedModels(BaseEstimator, RegressorMixin, TransformerMixin):
  6. def __init__(self, base_models, meta_model, n_folds=5):
  7. self.base_models = base_models
  8. self.meta_model = meta_model
  9. self.n_folds = n_folds
  10. # 我们将原来的模型clone出来,并且进行实现fit功能
  11. def fit(self, X, y):
  12. self.base_models_ = [list() for x in self.base_models]
  13. self.meta_model_ = clone(self.meta_model)
  14. kfold = KFold(n_splits=self.n_folds, shuffle=True, random_state=156)
  15. #对于每个模型,使用交叉验证的方法来训练初级学习器,并且得到次级训练集
  16. out_of_fold_predictions = np.zeros((X.shape[0], len(self.base_models)))
  17. for i, model in enumerate(self.base_models):
  18. for train_index, holdout_index in kfold.split(X, y):
  19. self.base_models_[i].append(instance)
  20. instance = clone(model)
  21. instance.fit(X[train_index], y[train_index])
  22. y_pred = instance.predict(X[holdout_index])
  23. out_of_fold_predictions[holdout_index, i] = y_pred
  24. # 使用次级训练集来训练次级学习器
  25. self.meta_model_.fit(out_of_fold_predictions, y)
  26. return self
  27. #在上面的fit方法当中,我们已经将我们训练出来的初级学习器和次级学习器保存下来了
  28. #predict的时候只需要用这些学习器构造我们的次级预测数据集并且进行预测就可以了
  29. def predict(self, X):
  30. meta_features = np.column_stack([
  31. np.column_stack([model.predict(X) for model in base_models]).mean(axis=1)
  32. for base_models in self.base_models_ ])
  33. return self.meta_model_.predict(meta_features)

发表评论

表情:
评论列表 (有 0 条评论,30人围观)

还没有评论,来说两句吧...

相关阅读

    相关 机器学习集成学习

    一、介绍 集成学习(Ensemble Learning)是一种机器学习技术,通过结合多个学习器(例如决策树、神经网络、支持向量机等)的预测结果,来达到更好的分类或回归预测

    相关 集成学习Stacking详解

    1、Stacking原理 stacking 就是当用初始训练数据学习出若干个基学习器后,将这几个学习器的预测结果作为新的训练集,来学习一个新的学习器。 ![在这里插入

    相关 集成学习Stacking方法

    集成学习主要分为 bagging, boosting 和 stacking方法。本文主要是介绍stacking方法及其应用。但是在总结之前还是先回顾一下继承学习。 这部分主要

    相关 STLstack实现详解

    STL栈介绍 stack是先进先出,没有迭代器,只允许push和pop操作以及读取和修改栈顶top元素的操作。由于stack利用了底层容器实现工作,所以称之为配接器而已。

    相关 [机器学习] 集成学习 stacking

    首先我们先训练多个不同的模型,然后把之前训练的各个模型的输出作为输入来训练一个新的最终分类器的模型,以得到一个最终的输出。但在实际中,我们通常使用logistic回归作为组合策