数据挖掘十大算法(六):AdaBoost算法

一、引言

集成方法(ensemble method)通过组合多个学习器来完成学习任务,颇有点“三个臭皮匠顶个诸葛亮”的意味。集成方法主要包括Bagging和Boosting两种方法,随机森林算法是基于Bagging思想的机器学习算法。AdaBoost算法和GBDT(Gradient Boost Decision Tree,梯度提升决策树)算法是基于Boosting思想的机器学习算法。Bagging和Boosting都是将已有的分类或回归算法通过一定方式组合起来,形成一个性能更加强大的分类器,更准确的说这是一种分类算法的组装方法,即将弱分类器组装成强分类器的方法。

Bagging

Bagging对训练数据采用自举采样(boostrap sampling),即有放回地采样数据,主要思想:

1. 从原始样本集中抽取训练集。每轮从原始样本集中使用Bootstraping的方法抽取n个训练样本(在训练集中,有些样本可能被多次抽取到,而有些样本可能一次都没有被抽中)。共进行k轮抽取,得到k个训练集。(k个训练集之间是相互独立的)

2. 每次使用一个训练集得到一个模型,k个训练集共得到k个模型。(注:这里并没有具体的分类算法或回归方法,我们可以根据具体问题采用不同的分类或回归方法,如决策树、感知器等)

3. 对分类问题:将上步得到的k个模型采用投票的方式得到分类结果;对回归问题,计算上述模型的均值作为最后的结果。(所有模型的重要性相同)
在这里插入图片描述

Boosting

Boosting是一种与Bagging很类似的技术。Boosting的思路则是采用重赋权(re-weighting)法迭代地训练基分类器,主要思想:

每一轮的训练数据样本赋予一个权重,并且每一轮样本的权值分布依赖上一轮的分类结果。
基分类器之间采用序列式的线性加权方式进行组合。

在这里插入图片描述

下面是将决策树与这些算法框架进行结合所得到的新的算法:

Bagging + 决策树 = 随机森林
AdaBoost + 决策树 = 提升树
Gradient Boosting + 决策树 = GBDT

集成方法众多,本文主要关注Boosting方法中的一种最流行的版本,即AdaBoost。

二、AdaBoost算法思想

AdaBoost算法是基于Boosting思想的机器学习算法,其中AdaBoost是Adaptive Boosting的缩写,AdaBoost是一种迭代型的算法,其核心思想是针对同一个训练集训练不同的学习算法,即弱学习算法,然后将这些弱学习算法集合起来,构造一个更强的最终学习算法。
在这里插入图片描述
在这里插入图片描述
解释说明:
1. 在弱学习算法的权重中,该式是随 误差率 减小而增大。即误差率小的分类器,在最终分类器的 重要程度大。
2. yi代表第i个样本对应的类别(1或-1),ht(xi)表示弱分类器对样本xi的分类(1或-1)。若果分对,yi*Gm(xi)的值为1,反之为-1。其中Zm是归一化因子,使得所有样本对应的权值之和为1.
3. sign函数,该函数用于求数值的正负。数值大于0,为1。小于0,为-1.等于0,为0.
4. 疑问:如何处理多分类问题?

三、手写AdaBoost

代码介绍:
下面的demo构建的是单层决策树,如下例。

在这里插入图片描述
蓝横线上边的是一个类别,蓝横线下边是一个类别。显然,此时有一个蓝点分类错误,计算此时的分类误差,误差为1/5 = 0.2。这个横线与坐标轴的y轴的交点,就是我们设置的阈值,通过不断改变阈值的大小,找到使单层决策树的分类误差最小的阈值。同理,竖线也是如此,找到最佳分类的阈值,就找到了最佳单层决策树。
就是在横向/纵向找到一个切分线就行,本质就是二分类问题。

# coding:UTF-8

from numpy import *


def loadSimpleData():
    datMat = mat([[1., 2.1],
                  [2., 1.1],
                  [1.3, 1.],
                  [1., 1.],
                  [2., 1.]])
    classLabels = mat([1.0, 1.0, -1.0, -1.0, 1.0])
    return datMat, classLabels


def singleStumpClassipy(dataMat, dim, threshold, thresholdIneq):
    '''
    :param dataMat: 数据矩阵
    :param dim: feature的维度
    :param threshold: 阈值
    :param thresholdIneq: 标志
    :return:
    '''
    classMat = ones((shape(dataMat)[0], 1))  # 初始化
    if thresholdIneq == 'left':  # 在threshold左侧的为'-1
        classMat[dataMat[:, dim] <= threshold] = -1.0   # 如果小于阈值赋值为-1
    else:
        classMat[dataMat[:, dim] > threshold] = -1.0    # 如果大于阈值赋值为-1

    return classMat


def singleStump(dataArr, classLabels, D):
    '''
    :param dataArr: 数据矩阵
    :param classLabels: 数据标签
    :param D: 样本权重
    :return:
        bestStump:最佳单层决策树信息
        minError:最小误差
        bestClasEst:最佳的分类结果
    '''
    dataMat = mat(dataArr)
    labelMat = mat(classLabels).T
    m, n = shape(dataMat)  # m是样本个数,n是特征个数

    numSteps = 10.0
    bestStump = {}
    bestClasEst = zeros((m, 1))
    minError = inf

    for i in range(n):  # 对每一个特征
        # 取第i列特征的最小值和最大值,以确定步长
        rangeMin = dataMat[:, i].min()
        rangeMax = dataMat[:, i].max()
        stepSize = (rangeMax - rangeMin) / numSteps

        for j in range(-1, int(numSteps) + 1):
            # 不确定是哪个属于类'-1',哪个属于类'1',分两种情况
            for inequal in ['left', 'right']:
                threshold = rangeMin + j * stepSize  # 得到每个划分的阈值
                predictionClass = singleStumpClassipy(dataMat, i, threshold, inequal)

                errorMat = ones((m, 1))
                errorMat[predictionClass == labelMat] = 0
                weightedError = D.T * errorMat   # D是每个样本的权重

                if weightedError < minError:
                    minError = weightedError
                    bestClasEst = predictionClass.copy()
                    bestStump['dim'] = i
                    bestStump['threshold'] = threshold
                    bestStump['inequal'] = inequal

    return bestStump, minError, bestClasEst


def adaBoostTrain(dataArr, classLabels, G):
    weakClassArr = []
    m = shape(dataArr)[0]  # 样本个数
    # 初始化D,即每个样本的权重
    D = mat(ones((m, 1)) / m)
    aggClasEst = mat(zeros((m, 1)))

    for i in range(G):  # G表示的是迭代次数
        print('\n')
        print('Epoch:{}'.format(i + 1))
        bestStump, minError, bestClasEst = singleStump(dataArr, classLabels, D)

        print('样本权重:', D.T)

        # 计算分类器的权重
        alpha = float(0.5 * log((1.0 - minError) / max(minError, 1e-16)))
        print('该轮分类器权重:', alpha)
        bestStump['alpha'] = alpha
        weakClassArr.append(bestStump)
        print('单轮预测结果:', bestClasEst.T)


        # 重新计算每个样本的权重D
        expon = multiply(-1 * alpha * mat(classLabels).T, bestClasEst)
        D = multiply(D, exp(expon))
        D = D / D.sum()


        aggClasEst += alpha * bestClasEst
        print('加权预测结果为:', aggClasEst)

        aggErrors = multiply(sign(aggClasEst) != mat(classLabels).T, ones((m, 1)))
        errorRate = aggErrors.sum() / m
        print('错误率为:', errorRate)

        if errorRate == 0.0:
            print('错误率已经为0,系统自动弹出!')
            break
    return weakClassArr


def adaBoostClassify(testData, weakClassify):
    dataMat = mat(testData)

    m = shape(dataMat)[0]  # 测试样本数量
    aggClassEst = mat(zeros((m, 1)))
    for i in range(len(weakClassify)):  # weakClassify是一个列表
        classEst = singleStumpClassipy(dataMat, weakClassify[i]['dim'], weakClassify[i]['threshold'],
                                       weakClassify[i]['inequal'])

        aggClassEst += weakClassify[i]['alpha'] * classEst

    return sign(aggClassEst)   # 返回的是加权的符号


if __name__ == '__main__':
    datMat, classLabels = loadSimpleData()
    weakClassArr = adaBoostTrain(datMat, classLabels, 30)
    print("weakClassArr汇总信息:", weakClassArr)

    # test
    print('进入测试阶段:')
    result = adaBoostClassify([1, 1], weakClassArr)
    print('最终的预测结果为:', result)

输出结果:
在这里插入图片描述
在这里插入图片描述

四、Sklearn之AdaBoost

sklearn.ensemble模块提供了很多集成方法,AdaBoost、Bagging、随机森林等。本文使用的是AdaBoostClassifier
在这里插入图片描述
在这里插入图片描述
base_estimator:可选参数,默认为DecisionTreeClassifier。理论上可以选择任何一个分类或者回归学习器,不过需要支持样本权重。我们常用的一般是CART决策树或者神经网络MLP。默认是决策树,即AdaBoostClassifier默认使用CART分类树DecisionTreeClassifier,而AdaBoostRegressor默认使用CART回归树DecisionTreeRegressor。另外有一个要注意的点是,如果我们选择的AdaBoostClassifier算法是SAMME.R,则我们的弱分类学习器还需要支持概率预测,也就是在scikit-learn中弱分类学习器对应的预测方法除了predict还需要有predict_proba。

algorithm:可选参数,默认为SAMME.R。scikit-learn实现了两种Adaboost分类算法,SAMME和SAMME.R。两者的主要区别是弱学习器权重的度量,SAMME使用对样本集分类效果作为弱学习器权重,而SAMME.R使用了对样本集分类的预测概率大小来作为弱学习器权重。由于SAMME.R使用了概率度量的连续值,迭代一般比SAMME快,因此AdaBoostClassifier的默认算法algorithm的值也是SAMME.R。我们一般使用默认的SAMME.R就够了,但是要注意的是使用了SAMME.R, 则弱分类学习器参数base_estimator必须限制使用支持概率预测的分类器。SAMME算法则没有这个限制。

n_estimators:整数型,可选参数,默认为50。弱学习器的最大迭代次数,或者说最大的弱学习器的个数。一般来说n_estimators太小,容易欠拟合,n_estimators太大,又容易过拟合,一般选择一个适中的数值。默认是50。在实际调参的过程中,我们常常将n_estimators和下面介绍的参数learning_rate一起考虑。

learning_rate:浮点型,可选参数,默认为1.0。每个弱学习器的权重缩减系数,取值范围为0到1,对于同样的训练集拟合效果,较小的v意味着我们需要更多的弱学习器的迭代次数。通常我们用步长和迭代最大次数一起来决定算法的拟合效果。所以这两个参数n_estimators和learning_rate要一起调参。一般来说,可以从一个小一点的v开始调参,默认是1。

random_state:整数型,可选参数,默认为None。如果RandomState的实例,random_state是随机数生成器; 如果None,则随机数生成器是由np.random使用的RandomState实例。

# encoding=utf-8

import pandas as pd
import time

from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

from sklearn.ensemble import AdaBoostClassifier

if __name__ == '__main__':

    print("Start read data...")
    time_1 = time.time()

    raw_data = pd.read_csv('../data/train_binary.csv', header=0) 
    data = raw_data.values
    
    features = data[::, 1::]
    labels = data[::, 0]

    # 随机选取33%数据作为测试集,剩余为训练集
    train_features, test_features, train_labels, test_labels = train_test_split(features, labels, test_size=0.33, random_state=0)

    time_2 = time.time()
    print('read data cost %f seconds' % (time_2 - time_1))
   

    print('Start training...') 
    # n_estimators表示要组合的弱分类器个数;
    # algorithm可选{‘SAMME’, ‘SAMME.R’},默认为‘SAMME.R’,表示使用的是real boosting算法,‘SAMME’表示使用的是discrete boosting算法
    clf = AdaBoostClassifier(n_estimators=100, algorithm='SAMME.R')

    clf.fit(train_features, train_labels)
    time_3 = time.time()
    print('training cost %f seconds' % (time_3 - time_2))


    print('Start predicting...')
    test_predict = clf.predict(test_features)
    time_4 = time.time()
    print('predicting cost %f seconds' % (time_4 - time_3))


    score = accuracy_score(test_labels, test_predict)
    print("The accruacy score is %f" % score)

输出结果:
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值