集成学习的概念

当数据集有问题,或者网络学习能力不足,或准确度不够时,我们可以采取集成学习的方法,来提升性能。说得通俗一些,就是发挥团队的智慧,根据团队中不同背景、不同能力的成员的独立意见,通过某种决策方法来解决一个问题。所以集成学习也称为多分类器系统(multi-classifier system)、基于委员会的学习(committee-based learning)等。

图16-36是一个简单的集成学习的示意图。

集成学习 Ensemble Learning - 图1

图中有两个组件:

Individual Learner 个体学习器

如果所有的个体学习器都是同一类型的学习器,即同质模式,比如都用神经网路,称为“基学习器”(base learner),相应的学习算法称为“基学习算法”(base learning algorithm)。

在传统的机器学习中,个体学习器可以是不同的,比如用决策树、支持向量机等,此时称为异质模式。

Aggregator 结合模块

个体学习器的输出,通过一定的结合策略,在结合模块中有机结合在一起,可以形成一个能力较强的学习器,所以有时称为强学习器,而相应地称个体学习器为弱学习器。

个体学习器之间是否存在依赖关系呢?这取决于产生个体学习器的方法:

  • Boosting系列算法,一系列的个体学习器需要一个个地串行生成,有前后依赖关系。
  • Bagging算法和随机森林算法(Random Forest),个体学习器可以独立或并行生成,没有依赖关系。

我们只讨论使用神经网络的同质个体学习方法,和Bagging集成算法。由于神经网络的复杂性,即使使用相同的网络参数,由于初始化的不同或者训练数据的不同,也可以得到差别很大的模型。

Bagging法集成学习的基本流程

图16-37是Bagging集成学习的示意图。

集成学习 Ensemble Learning - 图2

  1. 首先是数据集的使用,采用自助采样法(Bootstrap Sampling)。假设原始数据集Training Set中有1000个样本,我们从中随机取一个样本的拷贝放到Training Set-1中,此样本不会从原始数据集中被删除,原始数据集中还有1000个样本,而不是999个,这样下次再随机取样本时,此样本还有可能被再次选到。如此重复m次(此例m=1000),我们可以生成Training Set-1。一共重复N次(此例N=9),可以得到N个数据集。
  2. 然后搭建一个神经网络模型,可以参数相同。在N个数据集上,训练出N个模型来。
  3. 最后再进入Aggregator。N值不能太小,否则无法提供差异化的模型,也不能太大而带来训练模型的时间花销,一般来说取5到10就能满足要求。

生成数据集

  1. def GenerateDataSet(count=9):
  2. mdr = MnistImageDataReader(train_image_file, train_label_file, test_image_file, test_label_file, "vector")
  3. mdr.ReadLessData(1000)
  4. for i in range(count):
  5. X = np.zeros_like(mdr.XTrainRaw)
  6. Y = np.zeros_like(mdr.YTrainRaw)
  7. list = np.random.choice(1000,1000)
  8. k=0
  9. for j in list:
  10. X[k] = mdr.XTrainRaw[j]
  11. Y[k] = mdr.YTrainRaw[j]
  12. k = k+1
  13. # end for
  14. np.savez("level6_" + str(i)+".npz", data=X, label=Y)
  15. # end for

在上面的代码中,我们假设只有1000个手写数据样本,用np.random.choice(1000,1000)函数来可重复地选取1000个数字,分别取出对应的图像数据X和标签数据Y,命名为level6_N.npz,N=[1,9],保存到9个npz文件中。

假设数据集中有m个样本,这样的采样方法,某个样本在第一次被选择的概率是1/m,那么不被选择的概率就是1-1/m,则选择m次后,不被采样到的概率是(1-\frac{1}{m})^m,取极限值:

$$
\lim_{m \rightarrow \infty} (1-\frac{1}{m})^m \simeq \frac{1}{e} = 0.368
$$

即,对于一个新生成的训练数据集来说,原始数据集中的样本没有被使用的概率为36.8%。

训练个体学习器神经网络

这一步很简单,依然用我们在Level0中的过拟合网络,来训练9个神经网络。为了体现“弱学习器”的概念,我们可以在训练每个神经网络时,只迭代10个epoch。

  1. nets = []
  2. net_count = 9
  3. for i in range(net_count):
  4. dataReader = LoadData(i)
  5. net = train(dataReader)
  6. nets.append(net)

上述代码在一个9次的循环中,依次加载我们在前面生成的9个数据集,把训练好的9个net保存到一个列表中,后面测试时使用。

集成方法选择

平均法

在回归任务中,输出为一个数值,可以使用平均法来处理多个神经网络的输出值。下面公式中的h_i(x)表示第i个神经网络的输出,H(x)表示集成后的输出。

  • 简单平均法:所有值加起来除以N。 $$H(x)=\frac{1}{N} \sum_{i=1}^N h_i(x)$$
  • 加权平均法:给每个输出值一个人为定义的权重。 $$H(x)=\sum_{i=1}^N w_i \cdot h_i(x)$$

权重值如何给出呢?假设第一个神经网络的准确率为80%,第二个为85%,我们可以令:

$$
w1=0.8
$$

$$
w2=0.85
$$

这样准确率高的网络会得到较大的权重值。

投票法

对于分类任务,将会从类别标签集合{c_1, c_2, …,c_n}中预测出一个值,多个神经网络可能会预测出不一样的值,此时可以采样投票法。

  • 绝对多数投票法(majority voting)
    当有半数以上的神经网路预测出同一个类别标签时,我们可以认为此预测有效。如果少于半数,则可以认为预测无效。
    比如9个神经网络,5个预测图片上的数字为7,则最终结果就是7。如果有4个神经网络预测为7,3个预测为4,2个预测为1,则认为预测失败。

  • 加权投票法(weighted voting)
    与加权平均法类似。

  • 相对多数投票法(plurality voting)
    即得票最多的标签获胜。如果有多个标签获得相同的票数,随机选一个。

我们在代码中使用了相对多数投票法,具体过程如下。

假设9个神经网络对于同一张图片的预测结果为表16-5所示。

表16-5 9个神经网络对某张图片的预测结果

神经网络ID 1 2 3 4 5 6 7 8 9
预测输出 7 4 7 4 7 7 9 7 7

可以看到,在9个结果中,有6个结果预测为7,2个预测为4,1个预测为9,我们选择多数投票法,最终的预测结果为7。

为了验证真实的准确率,我们可以用MNIST的测试集中的10000个样本,来测试这9个模型,得到10000行上面表格中的数据,最后再统计最终的准确率。

此处代码比较复杂,最关键的一行语句是:

  1. ra[i] = np.argmax(np.bincount(predict_array[:,i]))

先使用np.bincount得到9个神经网络的预测结果中,每个结果出现的次数,得到:

$$
[0,1,0,0,2,0,0,6,0,1]
$$

其含义是:数字0出现了0次,数字1出现了1次,……数字4出现了2次,……,数字7出现了6次,等等。然后再用np.argmax([0,1,0,0,2,0,0,6,0,1])得到最大的数字6的下标,结果为7。这样就可以得到9个神经网络的投票结果为该图片上的数字是7,因为有6个神经网络认为是7,占相对多数。

学习法

学习法,就是用另外一个神经网络,通过训练的方式,把9个神经网路的输出结果作为输入,把图片的真实数字作为标签,得到一个强学习器。

假设9个神经网络的表现如表16-6所示。

表16-6 9个神经网络对于原始数据集中的1000个样本的预测结果

神经网络ID 1 2 3 4 5 6 7 8 9 标签值
预测输出1 7 4 7 4 7 7 9 7 7 7
预测输出2 4 4 7 4 7 4 9 4 7 4
预测输出N 0 9 0 0 5 0 0 6 0 0
预测输出1000 7 2 2 6 2 2 2 5 2 2

接下来我们可以建立一个两层的神经网络,输入层为9,用于接收9个神经网络的预测输出,隐层神经元数量不要大于16,输出层为10分类,标签值为上述表格中的最后一列。

scikit_learn中的ensemble

集成学习 Ensemble Learning - 图3

在keras中可以将模型转换为sklearn的回归或分类模型,传入sklearn的函数中。

  1. from keras.wrappers.scikit_learn import KerasRegressor, KerasClassifier
  2. def build_model1():
  3. model = Sequential()
  4. model.add(Dense(128, activation='relu', input_shape=(13, )))
  5. model.add(Dense(64, activation='relu'))
  6. model.add(Dense(1, activation='linear'))
  7. model.compile(optimizer='adam',
  8. loss='mean_squared_error')
  9. return model
  10. def build_model2():
  11. model = Sequential()
  12. model.add(Dense(64, activation='relu', input_shape=(784, )))
  13. model.add(Dense(32, activation='relu'))
  14. model.add(Dense(10, activation='softmax'))
  15. model.compile(optimizer='Adam',
  16. loss='categorical_crossentropy',
  17. metrics=['accuracy'])
  18. return model
  19. model = KerasRegressor(build_fn=build_model1, epochs=100, batch_size=64)
  20. model._estimator_type = "regressor"
  21. model = KerasClassifier(build_fn=build_model2, epochs=2, batch_size=64)
  22. model._estimator_type = "classifier"

运行结果

我们使用了相对多数投票法,其测试结果为表16-7所示。

表16-7 9个神经网络的预测准确率

神经网络ID 准确率
1 0.8526
2 0.8482
3 0.8438
4 0.8327
5 0.8410
6 0.8452
7 0.8443
8 0.8397
9 0.8389

9个神经网络的准确率如上表所示,最大的为0.8526,最小的为0.8327。用投票法得到的最后的准确率为0.8751,得到了提升,达到了集成学习的目的。

从偏差-方差的角度看,Bagging主要起到降低方差的作用。在前面我们分析过,单个学习器的过拟合是高方差造成的,我们训练多个这样的学习器,随机选择的样本数据如果分布均匀的话,每个学习器在针对单个测试样本时都会有高方差风险,从而产生泛化误差。但是由于我们拥有9个神经网络,采用集成法后,一定程度上缓解了高方差的现象。

代码位置

ch16, Level7

首先运行Level6_BootstrappingGenerator.py以获得子数据集,然后运行Level6_BaggingLearner.py来实现集成学习。