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

1.Boosting

Boosting 是一族可将弱学习器提升为强学习器的算法。这族算法的工作机制类似:先从初始训练集训练出一个基学习器,再根据基学习器的表现对训练样本分布进行调整,使得先前基学习器做错的训练样本在后续受到更多关注,然后基于调整后的样本分布来训练下一个基学习器;如此重复进行,直至基学习器数目达到事先指定的值 T , 最终将这 T 个基学习器进行加权结合。Boosting 算法工作机制如下图所示:

这里写图片描述

样本加权如下图所示,m 代表分类器序号,在第1个分类器中,样本初始化权重相等;经过第1个分类器训练,将被第1个分类器错分的样本权重增加,即下图中样本点被放大;随着不断的训练,被错分的样本权重不断增大,使得被错分的样本受到分类器更多的重视。

这里写图片描述

1.1 AdaBoost

优 点 :泛化错误率低,易编码,可以应用在大部分分类器上,无参数调整。

缺 点 :对离群点敏感。

适用数据类型:数值型和标称型数据。

——《机器学习实战》 P 116

AdaBoost是adaptive boosting(自适应boosting )的缩写,是模型为加法模型、损失函数为指数函数、学习算法为前向分布算法的二分类学习方法。为了从所有弱分类器中得到最终的分类结果 ,AdaBoost为每个分类器都分配了一个权重值 α ,这些 α 值是基于每个弱分类器的误差率进行计算的。其中,加权误差率 e 的定义为:

e = P ( G m ( x i ) y i ) = i = 1 N ω m i I ( G m ( x i ) y i )

其中 G m ( x i ) 为基本分类器。其系数为:
α m = 1 2 l n 1 e m e m

样本的权值分布为:
ω m + 1 , i = ω m i Z m e ( α m y i G m ( x i ) )

其中 y i 为样本标记值,对于二分类, y i { 1 , 1 } Z m 为规范化因子,使得ω_{m+1,i}相加为1。
Z m = i = 1 N ω m i e ( α m y i G m ( x i ) )

最终分类器为:
G ( x ) = s i g n ( f ( x ) ) = s i g n ( m = 1 M α m G m ( x ) )

ω m + 1 , i 可知被基本分类器 G m ( x ) 误分类样本的权值得以扩大,而被正确分类样本的权值得以缩小。因此,误分类样本将在下一轮学中起更大的作用。训练数据没有改变,而不断改变训练数据权值的分布,使得训练数据在基本分类器的学习中起不同的作用。

α m 可知其随着 e m 的减小而增大,所以分类误差率越小的基本分类器在最终分类器中的作用越大。

1.2 GBDT

1.2.1 提升树

提升树是以分类树或回归树为基本分类器的提升方法。提升树被认为是统计学习中性能最好的方法之一。

以决策树为其函数的提升方法称为提升树(boosting tree)。提升树模型可表示为决策树的加法模型:

f m ( x ) = m = 1 M T ( x ; Θ m )

其中, T ( x ; Θ m ) 表示决策树; Θ m 为决策树的参数;M为树的个数。

提升树采用前向分布算法,第m步的模型为:

f m ( x ) = f m 1 ( x ) + T ( x ; Θ m )

f m 1 ( x ) 为当前模型,通过经验风险极小化确定下一棵决策树的参数 Θ m
Θ ^ m = a r g min Θ m i = 1 N L ( y i , f m 1 ( x i ) + T ( x i ; Θ m ) )

L 为损失函数,当采用平方误差损失函数时,即:
L ( y , f ( x ) ) = ( y f ( x ) ) 2

即提升树损失为:
L ( y i , f m 1 ( x i ) + T ( x i ; Θ m ) ) = [ y f m 1 ( x ) T ( x i ; Θ m ) ] 2 = [ r T ( x i ; Θ m ) ] 2

r = y f m 1 ( x ) 为当前模型的残差(residual)。对回归问题的提升树算法,只需简单地拟合当前模型的残差。

1.2.2 梯度提升

GBDT(gradient boosting decission tree,梯度提升树),由Freidman提出,利用最速下降法的近似方法,其关键是利用损失函数的负梯度在当前模型的值:

[ L ( y , f ( x i ) ) ) f ( x i ) ] f ( x ) = f m 1 ( x )

作为回归问题提升树算法中的残差的近似值,拟合一个回归树。

1.3 XGBoost

相对于传统的GBDT,XGBoost使用了二阶信息,可以更快的在训练集上收敛。

由于“随机森林族”本身具备过拟合的优势,因此XGBoost仍然一定程度的具有该特性。

XGBoost的实现中使用了并行/多核计算,因此训练速度快;同时他的原生语言为C/C++,这是它速度快的原因。

2.Boosting算法实现

2.1 AdaBoost

实现代码如下:

"""
单层决策树分类
Args:
    dataMatrix - 数据矩阵
    dimen - 特征列
    threshVal - 阈值
    threshIneq - 标志位
Returns: retArray - 分类结果
Author:  dkjkls
Blog:    https://blog.csdn.net/dkjkls
Modify:  2018/4/4 1:11
"""
def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):
    retArray = np.ones((np.shape(dataMatrix)[0],1))         # 初始化retArray为1
    if threshIneq == 'lt':
        retArray[dataMatrix[:,dimen] <= threshVal] = -1.0   # 若小于等于阈值,赋值为-1
    else:
        retArray[dataMatrix[:,dimen] > threshVal] = -1.0    # 若大于阈值,赋值为-1
    return retArray

"""
基于权重向量生成单层决策树  
Args:
    dataArr - 数据矩阵
    classLabels - 类别标签
    D - 权重向量
Returns:
    bestStump - 最佳单层决策树信息
    minError - 最小误差
    bestClassEst - 最优分类结果
Author:  dkjkls
Blog:    https://blog.csdn.net/dkjkls
Modify:  2018/4/8 23:07
"""
def buildStump(dataArr, classLabels, D):
    dataMatrix = np.mat(dataArr); labelMat = np.mat(classLabels).T  # 转换数据及数据标签为numpy矩阵
    m, n = np.shape(dataMatrix)                                     # 数据集的行数和列数
    numSteps = 10.0                                                 # 遍历的步数
    bestStump = {}                                                  # 最佳单层决策树信息
    bestClassEst = np.mat(np.zeros((m, 1)))                         # 初始化最佳预测值
    minError = np.inf                                               # 初始化最小误差为正无穷大
    for i in range(n):                                              # 遍历特征
        rangeMin = dataMatrix[:, i].min()                           # 特征中的最小值
        rangeMax = dataMatrix[:, i].max()                           # 特征中的最大值
        stepSize = (rangeMax - rangeMin) / numSteps                 # 计算步长
        for j in range(-1, int(numSteps) + 1):                      # 遍历步长
            for inequal in ['lt', 'gt']:                            # 遍历大于、小于
                threshVal = (rangeMin + float(j) * stepSize)        # 计算阈值
                predictedVals = stumpClassify(dataMatrix, i, threshVal, inequal)  # 计算分类结果
                errArr = np.mat(np.ones((m, 1)))                    # 初始化误差矩阵
                errArr[predictedVals == labelMat] = 0               # 分类正确的赋值为0
                weightedError = D.T * errArr                        # 计算加权错误率
                if weightedError < minError:                        # 找到误差最小的分类方式,并赋值
                    minError = weightedError
                    bestClassEst = predictedVals.copy()
                    bestStump['dim'] = i
                    bestStump['thresh'] = threshVal
                    bestStump['ineq'] = inequal
    return bestStump, minError, bestClassEst

"""
基于单层决策树的AdaBoost训练    
Args:
    dataArr - 数据矩阵
    classLabels - 类别标签
    numIt - 迭代次数
Returns:
    weakClassArr - 分类器数组
    aggClassEst - 类别估计累计值
Author:  dkjkls
Blog:    https://blog.csdn.net/dkjkls
Modify:  2018/4/8 23:55
"""
def adaBoostTrainDS(dataArr,classLabels,numIt=40):
    weakClassArr = []                                                   # 分类器数组
    m = np.shape(dataArr)[0]                                            # 数据集的特征值个数
    D = np.mat(np.ones((m,1))/m)                                        # 初始化权重向量
    aggClassEst = np.mat(np.zeros((m,1)))                               # 初始化类别估计累计值
    for i in range(numIt):                                              # 根据迭代次数遍历
        bestStump,error,classEst = buildStump(dataArr,classLabels,D)    # 基于权重向量生成单层决策树
        alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16)))  # 计算权重alpha,防止发生除零溢出
        bestStump['alpha'] = alpha                                      # 记录权重值
        weakClassArr.append(bestStump)                                  # 存储最好的单层决策树
        expon = np.multiply(-1 * alpha * np.mat(classLabels).T, classEst) # 计算e的指数项
        D = np.multiply(D, np.exp(expon))                               # 计算新的权重向量
        D = D / D.sum()                                                 # 权重向量归一化
        aggClassEst += alpha * classEst                                 # 计算类别估计累计值
        aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(classLabels).T, np.ones((m, 1))) # 计算误差
        errorRate = aggErrors.sum() / m
        print("total error: ", errorRate)
        if errorRate == 0.0: break                                      # 误差为0则退出循环
    return weakClassArr, aggClassEst

"""
AdaBoost分类函数
Args:
    datToClass - 待分类样例
    classifierArr - 训练好的弱分类器
Returns: 分类结果
Author:  dkjkls
Blog:    https://blog.csdn.net/dkjkls
Modify:  2018/4/9 1:17
"""
def adaClassify(datToClass,classifierArr):
    dataMatrix = np.mat(datToClass)             # 转换待分类样例为numpy矩阵
    m = np.shape(dataMatrix)[0]                 # 待分类样例个数
    aggClassEst = np.mat(np.zeros((m,1)))       # 初始化类别估计累计值
    for i in range(len(classifierArr)):         # 遍历所有弱分类器
        classEst = stumpClassify(dataMatrix,classifierArr[i]['dim'],
                                 classifierArr[i]['thresh'],
                                 classifierArr[i]['ineq'])  # 计算类别估计值
        aggClassEst += classifierArr[i]['alpha']*classEst   # 根据权重累加得到类别估计累计值
        print(aggClassEst)
    return np.sign(aggClassEst)


if __name__ == '__main__':
    dataArr,classLabels = loadSimpData()
    weakClassArr, aggClassEst = adaBoostTrainDS(dataArr, classLabels)
    print(adaClassify([[0,0],[5,5]], weakClassArr))

使用sklearn.ensemble 实现 AdaBoost 代码如下:

from sklearn.ensemble import AdaBoostClassifier

# 决策树
base_estimator = DecisionTreeClassifier(criterion='gini', max_depth=3, min_samples_split=4)
# AdaBoost学习器,10棵树,学习率为0.1
model = AdaBoostClassifier(base_estimator=base_estimator, n_estimators=10, learning_rate=0.1)
# 训练AdaBoost学习器
model.fit(x_train, y_train)
# AdaBoost学习器预测训练集
y_train_pred = model.predict(x_train)

2.2 GBDT

使用sklearn.ensemble 实现 GBDT 代码如下:

from sklearn.ensemble import GradientBoostingClassifier

# GBDT,100棵树,学习率0.1,最大深度2
gb = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1, max_depth=2)
# GBDT训练
gb.fit(x_train, y_train.ravel())
# GBDT预测训练集
y_train_pred = gb.predict(x_train)

2.3 XGBoost

使用xgboost实现代码如下:

import xgboost as xgb

# 设置参数
# max_depth - 树的深度为3
# eta - 学习率,为1时是原始模型,过小的学习率会造成计算次数增多,可防止过拟合,通过减少每一步的权重,可以提高模型的鲁棒性。典型值0.01-0.2
# silent-是否输出中间结果
# objective-定义需要被最小化的损失函数(分类问题的逻辑回归)
param = {'max_depth': 3, 'eta': 1, 'silent': 1, 'objective': 'binary:logistic'}
watchlist = [(data_test, 'eval'), (data_train, 'train')]    # 输出过程中的错误率
n_round = 7 # 7颗决策树
# 训练XGBoost
bst = xgb.train(param, data_train, num_boost_round=n_round, evals=watchlist)
# XGBoost预测数据集
y_hat = bst.predict(data_test)

3 Boosting实战

3.1 AdaBoost

使用Adaboost对鸢尾花数据两特征组合进行分类,实现代码如下:

import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import AdaBoostClassifier
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
    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=(11, 8), facecolor='#FFFFFF')
    for i, pair in enumerate(feature_pairs):
        # 准备数据
        x_train = x_prime_train[pair]
        x_test = x_prime_test[pair]

        # 决策树
        base_estimator = DecisionTreeClassifier(criterion='gini', max_depth=3, min_samples_split=4)
        # AdaBoost学习器,10棵树,学习率为0.1
        model = AdaBoostClassifier(base_estimator=base_estimator, n_estimators=10, learning_rate=0.1)
        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('\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.5)
        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)
        plt.scatter(x_test[pair[0]], x_test[pair[1]], c=y_test, s=100, marker='*', edgecolors='k', cmap=cm_dark)
        plt.xlabel(iris_feature[pair[0]], fontsize=14)
        plt.ylabel(iris_feature[pair[1]], fontsize=14)
        plt.xlim(x1_min, x1_max)
        plt.ylim(x2_min, x2_max)
        plt.grid(b=True)
    plt.suptitle('Adaboost对鸢尾花数据两特征组合的分类结果', fontsize=18)
    plt.tight_layout(1, rect=(0, 0, 1, 0.95))    # (left, bottom, right, top)
    plt.show()

输出结果如下:

特征: 花萼长度  +  花萼宽度
    训练集准确率: 87.6190%
    测试集准确率: 75.5556%

特征: 花萼长度  +  花瓣长度
    训练集准确率: 99.0476%
    测试集准确率: 88.8889%

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

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

特征: 花萼宽度  +  花瓣宽度
    训练集准确率: 98.0952%
    测试集准确率: 93.3333%

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

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

3.2 GBDT & XGBoost

分别使用XGBoost、GBDT、随机森林、逻辑回归对鸢尾花数据进行分类,实现代码如下:

import numpy as np
import pandas as pd
import xgboost as xgb
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegressionCV
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.metrics import accuracy_score


if __name__ == "__main__":
    path = 'iris.data'                     # 数据文件路径
    data = pd.read_csv(path, header=None)   # 读取数据
    x, y = data[list(range(4))], data[4]    # 取特征值
    y = pd.Categorical(y).codes             # 取标签值
    # 拆分数据为训练集(50%)和测试集(50%)
    x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=1, test_size=50)

    # 转换为xgboost中的矩阵格式
    data_train = xgb.DMatrix(x_train, label=y_train)
    data_test = xgb.DMatrix(x_test, label=y_test)
    watch_list = [(data_test, 'eval'), (data_train, 'train')]   # 输出过程中的错误率
    # 3分类的softmax训练模型
    param = {'max_depth': 8, 'eta': 0.8, 'silent': 1, 'objective': 'multi:softmax', 'num_class': 3}

    # 训练XGBoost
    bst = xgb.train(param, data_train, num_boost_round=6, evals=watch_list)
    # XGBoost预测数据集
    y_hat = bst.predict(data_test)
    result = y_test == y_hat
    print('测试集正确率:\t', float(np.sum(result)) / len(y_hat))
    print('END.....\n')

    # 分别用GBDT、随机森林和逻辑回归训练
    models = [
        # GBDT 30棵决策树,学习率为0.1,树的最大深度为5
        ('GBDT', GradientBoostingClassifier(n_estimators=30, learning_rate=0.1, max_depth=5)),
        # 随机森林 30棵决策树,特征选择用gini系数,树的最大深度为5
        ('RandomForest', RandomForestClassifier(n_estimators=30, criterion='gini', max_depth=5)),
        ('LogisticRegression', LogisticRegressionCV(Cs=np.logspace(1, 100, 100), cv=3))
    ]
    for name, model in models:
        model.fit(x_train, y_train)
        print(name, '训练集正确率:', accuracy_score(y_train, model.predict(x_train)))
        print(name, '测试集正确率:', accuracy_score(y_test, model.predict(x_test)))

输出结果如下:

[0] eval-merror:0.02    train-merror:0.02
[1] eval-merror:0.02    train-merror:0.02
[2] eval-merror:0.02    train-merror:0.02
[3] eval-merror:0.04    train-merror:0.01
[4] eval-merror:0.04    train-merror:0.01
[5] eval-merror:0.04    train-merror:0.01
测试集正确率:  0.96
END.....

GBDT 训练集正确率: 1.0
GBDT 测试集正确率: 0.96
RandomForest 训练集正确率: 1.0
RandomForest 测试集正确率: 0.96
LogisticRegression 训练集正确率: 0.97
LogisticRegression 测试集正确率: 0.92

4.参考

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

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

猜你喜欢

转载自blog.csdn.net/dkjkls/article/details/80026327