学习笔记【机器学习重点与实战】——4 集成学习-Bagging

1.集成学习

集成学习 (ensemble learning)通过构建并结合多个学习器来完成学习任务,有时也被称为多分类器系统 (multi-classifier system) 、基于委员会的学习 (committee-based learning) 等。

图 1 为集成学习的一般结构:先产生一组”个体学习器” (individual learner) ,再用某种策略将它们结合起来。个体学习器通常由一个现有的学习算法从训练数据产生,例如线性回归、逻辑回归、决策树算法、 BP 神经网络算法等,此时集成中只包含同种类型的个体学习器,例如”决策树集成”中全是决策树,”神经网络集成”中全是神经网络,这样的集成是”同质”的 (homogeneous)。同质集成中的个体学习器亦称”基学习器” (base learner),相应的学习算法称为”基学习算法” (base learning algorithm)。集成也可包含不同类型的个体学习器,例如同时包含决策树和神经网络,这样的集成是”异质”的 (heterogenous)。异质集成中的个体学习器由不同的学习算法生成,这时就不再有基学习算法;相应的,个体学习器一般不称为基学习器,常称为”组件学习器” (component learner)或直接称为个体学习器。

——摘自《机器学习》P171

集成学习将多个弱学习器进行结合,通过对样本加权、学习器加权,获得比单一学习器显著优越的泛化性能的强学习器。

这里写图片描述

图1 集成学习示意图

然而我们必须注意到,上面的分析有一个关键假设:基学习器的误差相互独立。在现实任务中,个体学习器是为解决同一个问题训练出来的,它们显然不可能相互独立!事实上,个体学习器的”准确性”和”多样性”本身就存在冲突。一般的,准确性很高之后,要增加多样性就需牺牲准确性。事实上,如何产生并结合”好而不同“的个体学习器,恰是集成学习研究的核心。

根据个体学习器的生成方式,目前的集成学习方法大致可分为两大类,即个体学习器间存在强依赖关系、必须串行生成的序列化方法(如图2所示),以及个体学习器间不存在强依赖关系、可同时生成的并行化方法;前者的代表是 Boosting(如图2所示),后者的代表是 Bagging(如图3所示) 和”随机森林” (Random Forest) 。

——摘自《机器学习》P173

Boosting是一种与Bagging很类似的技术。不论是在Boosting还是Bagging当中,所使用的多个分类器的类型都是一致的。但是在前者当中,不同的分类器是通过串行训练而获得的,每个新分类器都根据已训练出的分类器的性能来进行训练。Boosting是通过集中关注被已有分类器错分的那些数据来获得新的分类器。

由于Boosting分类的结果是基于所有分类器的加权求和结果的,因此Boosting与Bagging不太一样 。Bagging中的分类器权重是相等的,而Boosting中的分类器权重并不相等,每个权重代表的是其对应分类器在上一轮迭代中的成功度。

这里写图片描述

图2 Boosting示意图

这里写图片描述

图3 Bagging示意图

本篇文章先介绍Bagging 和”随机森林” (Random Forest) 。Boosting详见下篇文章。

2.Bagging与随机森林

2.1 Bagging

Bagging[Breiman,1996] 是并行式集成学习方法最著名的代表。给定包含 m 个样本的数据集,我们先随机取出一个样本放入采样集中,再把该样本放回初始数据集,使得下次采样时该样本仍有可能被选中,这样,经过 m 次随机采样操作,我们得到含 m 个样本的采样集,初始训练集中有的样本在采样集里多次出现,有的则从未出现。

Bagging的策略如下:

  1. 从样本集中重采样(有重复的)选出n个样本
  2. 在所有属性上,对这n个样本建立分类器(ID3、C4.5、CART、SVM、Logistic回归等)
  3. 重复1、2 m 次,即获得了 m 个分类器
  4. 将数据放在这m个分类器上,最后根据这 m 个分类器的投票结果,决定数据属于哪一类

2.1.1 OOB数据

Bagging采用的重采样,即为评估方法中的自助法(bootstrapping)。可以做一个简单的估计,样本在m次采样中始终不被采到的概率是(11m)m,取极限得到:

limm(11m)m1e0.368

即通过自主法采样,初始数据集中有约36.8%的样本未出现在采样数据集中,将这些未参与模型训练的数据称为袋外数据(包外估计,out-of-bag estimate)。

2.1.2 投票机制

对分类任务来说,学习器将从类别标记集合中预测出一个标记,最常见的结合策略是使用投票法 (voting)。主要包括:

  • 绝对多数投票法 (majority voting) :若某标记得票过半数,则预测为该标记;否则拒绝预测。
  • 相对多数投票法 (plurality voting) :预测为得票最多的标记,若同时有多个标记获最高票,则从中随机选取
    一个。
  • 加权投票法 (weighted yoting) :与加权平均法类似。

2.2 随机森林

随机森林(Random Forest,简称 RF)[Breiman,2001a] 是 Bagging的一个扩展变体。其在以决策树为基学习器构建 Bagging 集成的基础上,进一步在决策树的训练过程中引入了随机属性选择。

随机森林的策略如下:

  1. 从样本集中用Bootstrap采样选出n个样本
  2. 从所有属性中随机选择k个属性,选择最佳分割属性作为节点建立CART决策树
  3. 重复1、2两个步骤,即建立了m棵CART决策树
  4. m个CART形成随机森林,通过投票表决结果,决定数据属于哪一类

k 控制了随机性的引入程度;若令 k = d , 则基决策树的构建与传统决策树相同;若令 k = 1 , 则是随机选择一个属性用于划分 ; 一般情况下,推荐值 k=log2d[Breiman,2001]

随机森林简单、容易实现、计算开销小,在很多现实任务中展现出强大的性能,被誉为”代表集成学习技术水平的方法”。可以看出,随机森林对 Bagging 只做了小改动 ,但是与 Bagging 中基学习器的”多样性”仅通过样本扰动(通过对初始训练集采样)而来不同,随机森林中基学习器的多样性不仅来自样本扰动,还来自属性扰动,这就使得最终集成的泛化性能可通过个体学习器之间差异度的增加而进一步提升。

随机森林的收敛性与 Bagging 相似。如图 4 所示,随机森林的起始性能往往相对较差, 特别是在集成中只包含一个基学习器时这很容易理解,因为通过引入属性扰动,随机森林中个体学习器的性能往往有所降低。然而,随着个体学习器数目的增加,随机森林通常会收敛到更低的泛化误差。值得一提的是,随机森林的训练效率常优于Bagging,因为在个体决策树的构建过程中 ,Bagging 使用 的是 “确定型” 决策树,在选择划分属性时要对结点的所有属性进行考察 ,而随机森林使用的” 随机型”决策树则只需考察一个属性子集。

这里写图片描述

图4 集成规模对随机森林和Bagging的影响

sklearn中随机森林实现代码如下:

from sklearn.ensemble import RandomForestRegressor

# 随机森林 - 100棵树,用entropy衡量质量,最大深度为5,使用OOB数据预测
model = RandomForestClassifier(n_estimators=100, criterion='entropy', max_depth=5, oob_score=True)
model.fit(x, y)                 # 生成随机森林
y_hat = model.predict(x_test)   # 预测测试集

3.Bagging与随机森林实战

3.1 Bagging

通过自定义函数,模拟待拟合数据。

实现代码如下:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from sklearn.linear_model import RidgeCV
from sklearn.ensemble import BaggingRegressor
from sklearn.tree import DecisionTreeRegressor
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures


def f(x):
    '''
    模拟函数
    :param x: x值
    :return: y值
    '''
    return 0.5 * np.exp(-(x + 3) ** 2) + np.exp(-x ** 2) + 1.5 * np.exp(-(x - 3) ** 2)

if __name__ == "__main__":
    np.random.seed(0)
    N = 200
    x = np.random.rand(N) * 10 - 5  # [-5,5)
    x = np.sort(x)
    y = f(x) + 0.05 * np.random.randn(N)
    x.shape = -1, 1

    degree = 6              # 多项式阶数
    n_estimators = 100      # 树的个数
    max_samples = 0.5       # 50% 的样本随机子集

    # 交叉验证岭回归
    ridge = RidgeCV(alphas=np.logspace(-3, 2, 20), fit_intercept=False)
    # 多项式转换的岭回归
    ridged = Pipeline([('poly', PolynomialFeatures(degree=degree)), ('Ridge', ridge)])
    bagging_ridged = BaggingRegressor(ridged, n_estimators=n_estimators, max_samples=max_samples)
    # 决策树,最大深度为12层
    dtr = DecisionTreeRegressor(max_depth=12)

    regs = [
        ('DecisionTree', dtr),
        ('Ridge(%d Degree)' % degree, ridged),
        ('Bagging Ridge(%d Degree)' % degree, bagging_ridged),
        ('Bagging DecisionTree', BaggingRegressor(dtr, n_estimators=n_estimators, max_samples=max_samples))]

    # 作图
    mpl.rcParams['font.sans-serif'] = ['SimHei']
    mpl.rcParams['axes.unicode_minus'] = False
    plt.figure(figsize=(8, 6), facecolor='w')
    plt.plot(x, y, 'ro', mec='k', label='训练数据')
    x_test = np.linspace(1.1*x.min(), 1.1*x.max(), 1000)
    # 绘制真实值曲线
    plt.plot(x_test, f(x_test), color='k', lw=3, ls='-', label='真实值')
    clrs = '#FF2020', 'm', 'y', 'g'

    # 分别训练4个模型
    for i, (name, reg) in enumerate(regs):
        reg.fit(x, y)
        label = '%s, $R^2$=%.3f' % (name, reg.score(x, y))
        y_test = reg.predict(x_test.reshape(-1, 1))
        # 绘制训练模型曲线
        plt.plot(x_test, y_test, color=clrs[i], lw=(i+1)*0.5, label=label, zorder=6-i)

    # 作图
    plt.legend(loc='upper left', fontsize=11)
    plt.xlabel('X', fontsize=12)
    plt.ylabel('Y', fontsize=12)
    plt.title('回归曲线拟合:samples_rate(%.1f), n_trees(%d)' % (max_samples, n_estimators), fontsize=15)
    plt.ylim((-0.2, 1.1*y.max()))
    plt.tight_layout(2)
    plt.grid(b=True, ls=':', color='#606060')
    plt.show()

输出图形如下:

这里写图片描述

由图可知,Ridge(岭回归)作为强分类器,Bagging对其不会产生太好效果。但Bagging可减小过拟合,所以通常在强分类器和复杂模型上使用时表现的很好(例如,完全决策树,fully developed decision trees),相比之下 boosting 方法则在弱模型上表现更好(例如,浅层决策树,shallow decision trees)。

3.2 鸢尾花数据集随机森林分类

实现代码如下:

import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split


if __name__ == "__main__":
    mpl.rcParams['font.sans-serif'] = ['SimHei']
    mpl.rcParams['axes.unicode_minus'] = False

    iris_feature = '花萼长度', '花萼宽度', '花瓣长度', '花瓣宽度'
    path = 'iris.data'                          # 数据文件路径
    data = pd.read_csv(path, header=None)       # 读取数据
    x_prime = data[list(range(4))]              # 取特征值
    y = pd.Categorical(data[4]).codes           # 取标签值
    # 拆分数据为训练集(70%)和测试集(30%)
    x_prime_train, x_prime_test, y_train, y_test = train_test_split(x_prime, y, train_size=0.7, random_state=0)

    # 特征组合
    feature_pairs = [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]
    plt.figure(figsize=(8, 6), facecolor='#FFFFFF')
    for i, pair in enumerate(feature_pairs):
        # 准备数据
        x_train = x_prime_train[pair]
        x_test = x_prime_test[pair]

        # 随机森林   100棵树,用entropy衡量质量,最大深度6,使用OOB数据预测
        model = RandomForestClassifier(n_estimators=100, criterion='entropy', max_depth=6, oob_score=True)
        model.fit(x_train, y_train)

        # 画图
        N, M = 500, 500  # 横纵各采样多少个值
        x1_min, x2_min = x_train.min()
        x1_max, x2_max = x_train.max()
        t1 = np.linspace(x1_min, x1_max, N)
        t2 = np.linspace(x2_min, x2_max, M)
        x1, x2 = np.meshgrid(t1, t2)  # 生成网格采样点
        x_show = np.stack((x1.flat, x2.flat), axis=1)  # 测试点

        # 训练集上的预测结果
        y_train_pred = model.predict(x_train)
        acc_train = accuracy_score(y_train, y_train_pred)
        y_test_pred = model.predict(x_test)
        acc_test = accuracy_score(y_test, y_test_pred)
        print('特征:', iris_feature[pair[0]], ' + ', iris_feature[pair[1]])
        print('OOB Score:', model.oob_score_)
        print('\t训练集准确率: %.4f%%' % (100*acc_train))
        print('\t测试集准确率: %.4f%%\n' % (100*acc_test))

        # 输出图形
        cm_light = mpl.colors.ListedColormap(['#A0FFA0', '#FFA0A0', '#A0A0FF'])
        cm_dark = mpl.colors.ListedColormap(['g', 'r', 'b'])
        y_hat = model.predict(x_show)
        y_hat = y_hat.reshape(x1.shape)
        plt.subplot(2, 3, i+1)
        plt.contour(x1, x2, y_hat, colors='k', levels=[0, 1], antialiased=True, linestyles='--', linewidths=1)
        plt.pcolormesh(x1, x2, y_hat, cmap=cm_light)  # 预测值
        plt.scatter(x_train[pair[0]], x_train[pair[1]], c=y_train, s=20, edgecolors='k', cmap=cm_dark, label='训练集')
        plt.scatter(x_test[pair[0]], x_test[pair[1]], c=y_test, s=100, marker='*', edgecolors='k', cmap=cm_dark, label='测试集')
        plt.xlabel(iris_feature[pair[0]], fontsize=12)
        plt.ylabel(iris_feature[pair[1]], fontsize=12)
        plt.xlim(x1_min, x1_max)
        plt.ylim(x2_min, x2_max)
        plt.grid(b=True, ls=':', color='#606060')
    plt.suptitle('随机森林对鸢尾花数据两特征组合的分类结果', fontsize=15)
    plt.tight_layout(1, rect=(0, 0, 1, 0.95))    # (left, bottom, right, top)
    plt.show()

输出结果如下:

特征: 花萼长度  +  花萼宽度
OOB Score: 0.7333333333333333
    训练集准确率: 89.5238%
    测试集准确率: 64.4444%

特征: 花萼长度  +  花瓣长度
OOB Score: 0.9333333333333333
    训练集准确率: 99.0476%
    测试集准确率: 93.3333%

特征: 花萼长度  +  花瓣宽度
OOB Score: 0.9238095238095239
    训练集准确率: 98.0952%
    测试集准确率: 95.5556%

特征: 花萼宽度  +  花瓣长度
OOB Score: 0.9333333333333333
    训练集准确率: 100.0000%
    测试集准确率: 93.3333%

特征: 花萼宽度  +  花瓣宽度
OOB Score: 0.9047619047619048
    训练集准确率: 98.0952%
    测试集准确率: 91.1111%

特征: 花瓣长度  +  花瓣宽度
OOB Score: 0.9523809523809523
    训练集准确率: 99.0476%
    测试集准确率: 97.7778%

输出图形如下:
这里写图片描述

4.参考

  1. 机器学习升级版视频 - 邹博
  2. 《机器学习 - 周志华》第8章 集成学习
  3. 《机器学习实战》第7章 利用AdaBoost元算法提高分类性能

===============文档信息================
学习笔记由博主整理编辑,供非商用学习交流用
如本文涉及侵权,请随时留言博主,必妥善处置
版权声明:非商用自由转载-保持署名-注明出处
署名(BY) :dkjkls(dkj卡洛斯)
文章出处:http://blog.csdn.net/dkjkls

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页