前面讲到了增强学习的基本原理以及两种方法Boosting,现在就介绍我们的主角AdaBoost。

集成学习的两个关注点
  • 在每一轮如何改变训练数据的权值或者概率分布
  • 如何将弱分类器组合成一个强分类器
AdaBoost思想

对于第一个问题:每一个训练样本都被赋予一个权重,表明它被某个分类器选入训练集的概率。然后提高被前一轮若分类器错误分类的样本的权值,降低那些被错误分类的样本的权值。这样一来没有被正确分类的数据由于其权值增大,而在后面的分类中受到更大的关注,这也是AdaBoost的自适应的体现。
而关于分类器的组合策略,AdaBoost采用的是加权多数表决法。具体做法是加大分类误差率小的分类器的权值,减小分类误差器大的的权值。
AdaBoost算法可以分为下面三步:

  1. 给每一个训练样本赋值一个初始化权值,假设有N个数据集,那么将权值设置成1/N.

  2. 然后利用弱分类器 hi 训练数据,然后计算出每一个弱分类器的错误率,选择错误率最低的弱分类器,然后将错误分类的样本权值增大,正确分类的样本的权值减小。然后计算出分类器的权值 a ,如此迭代下去。

  3. 最后将训练得到的弱分类器组合成一个强分类器,具体做法是加大分类误差率小的分类器的权值,减小分类误差器大的的权值。

AdaBoost算法过程

假设给定一个二分类数据训练集

T=(x1,y1),(x2,y2)...(xN,yN)

其中 yi{+1,1} ,AdaBoost算法利用下面一些列弱算法或者基本若分类,并将这些弱分类器组合成一个强分类器。
有一些列弱分类器 Gi(x) ,最后要求输出强分类器 G(x)
具体步骤如下:
(1)初始化训练数据权值分布

D1=(w11,...,w1i...,wiN),w1i=1N,i=1,2,...N

(2)对 m=1,2,3...M
(a)使用具有权值分布 Dm 的训练数据集学习,得到基本分类器
Gm(x){+1,1}

(b)计算 Gm(x) 在训练集上面的分类误差
em=P(Gm(xi)yi)=i=1NwmiI(Gm(xi)yi)

i=1Nwmi=1

由上述式子可知, Gm(x) 在训练集上的错误率 em 其实就是被 Gm(x) 划分错误的样本的权值之和

(c)计算分类器 Gm(x) 的系数

am=12log1emem

(d)更新训练后的权值分布
Dm+1=(wm+1,1....wm+1,i...wm+1,N)

wm+1,i=wm+1,iZmeamyiGm(xi),i=1,2....N

其中, Zm 是规范化因子,是为了保证 Ni=1wmi=1
Zm=2em1em

(3构建基本分类器的线性组合
f(x)=m=1MamGm(x)

最终得到的分类器为
G(x)=sign(f(x))=sign(m=1MamGm(x))

关于权值更新具体的推导补充:
1. 当样本分类错误时候有, yiGm(i)=1

Dm+1,i=Dm,iZmeam=Dm2em(1em)em1em=Dm,i2(1em)

2. 当样本分对时有, yiGm(i)=1
Dm+1,i=Dm,iZmeam=Dm2em(1em)1emem=Dm,i2em

下面是AdaBoost算法的示意图,左边数数据集,中间是分类器,而右边的数字表示的是每一个分类器的权值 a <script type="math/tex" id="MathJax-Element-30">a</script>
这里写图片描述

推荐大家看一下AdaBoost算法原理分析与实例这篇博客,里面作者举了一个例子,可以帮助我们更好的理解AdaBoost的过程。

下面我们结合代码分析一下。

基于单层决策树构建弱分类器

from numpy import *
import numpy as np
import matplotlib.pyplot as plt

def loadDataSet():
    datMat = np.matrix([[1.,2.1],
                       [2.,1.1],
                       [1.3,1.],
                       [1.,1.],
                       [2.,1.]])
    classLabels = [1.0,1.0,-1.0,-1.0,1.0]
    return datMat,classLabels

def showDataSet(datMat,classLabels):
    xcord0 = [];ycord0 = []
    xcord1 = [];ycord1 = []
    colors = []
    makers = []
    for i in range(len(classLabels)):
        if classLabels[i]==1.0:
            xcord0.append(datMat[i,0])
            ycord0.append(datMat[i,1])
        else:
            xcord1.append(datMat[i,0])
            ycord1.append(datMat[i,1])
    fig=plt.figure()
    ax=fig.add_subplot(111)
    ax.scatter(xcord0,ycord0,marker='o',s=90,c='blue')
    ax.scatter(xcord1,ycord1,marker='o',s=90,c='red')
    plt.title('DataSet')
    plt.show()

这里写图片描述

如图所示,我们找不出一条平行于坐标轴的直线将两种颜色的点划分开,这就是单层决策树难以解决的一个著名的问题,但是我们可以通过使用多颗单层决策树,构建一个能够对该数据正确分类的分类器。
接下来我们构建最优的单层决策树:
算法的伪代码如下

将最小错误率设置为无穷大
对数据集中每一个特征(第一层循环)
    对每一个步长(第二层循环):
        对每一个不等号:
            建立一颗单层决策树并且用加权数据集对它进行测试
            如果错误率低于minError,则将当中单层决策树设为最佳决策树
返回最佳单层决策树

伪代码

'''
单层决策树分类函数
函数参数:
    dataMatrix - 数据矩阵
    dimen -  第dimen列,也就是第几个特征
    threshVal - 阈值
    threshIneq - 标志
返回值:
    retArray - 分类结果
'''
def stumpClassify(datMatrix,dimen,threshVal,threshIneq):
    retArray = np.ones((np.shape(datMatrix)[0],1))
    if threshIneq =='lt':#初始化retArray为1
        retArray[datMatrix[:,dimen]<= threshVal] = -1.0#如果小于阈值,则赋值为-1
    else:
        retArray[datMatrix[:,dimen]>threshVal] = -1.0#如果大于阈值,则赋值为-1
    return retArray

'''
找到数据集上最佳的单层决策树
函数参数:
    dataArr - 数据矩阵
    classLabels - 数据标签
    D - 样本权重
返回值:
    bestStump - 最佳单层决策树
    minError - 最小误差
    besetClasEst - 最佳的分类器    
'''

def buildStump(dataArr,classLabels,D):
    dataMatrix = np.mat(dataArr)
    labelMat = np.mat(classLabels).T
    m,n=np.shape(dataMatrix)
    numSteps = 10.0
    bestStump = {}
    bestClaEst = 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)#计算阈值
                predictVals = stumpClassify(dataMatrix,i,threshVal,inequal)#计算分类的结果
                errArr = np.mat(np.ones((m,1)))#初始化误差矩阵
                #判断计算值和实际是否有误差,
                #如果没有误差可以则设置为0,若有误差则设置为1
                errArr[predictVals==labelMat]=0#
                weightedError = D.T*errArr#计算误差
                #print('split: dim %d,thresh % .2f,thresh inequal: %s,the weighted error is %.3f' %(i,threshVal,inequal,weightedError))
                if weightedError<minError:#找到最小的误差分类
                    minError = weightedError
                    bestClaEst=predictVals.copy()
                    bestStump['dim']=i
                    bestStump['thresh']=threshVal
                    bestStump['ineq']=inequal
    return bestStump,minError,bestClaEst

这里一个单层决策树就构建完成,通过AdaBoost构建一个完整的决策树
实现的思想如下:

对每次迭代:
    利用buildStump()函数找到最佳的单侧决策树
    将最佳单侧决策树加入到单侧决策树数组
    计算权值alpha
    更新权值向量D
    更新累计类别的估计值
    如果错误率等于0,退出循环
基于单层决策树的AdaBoost训练过程
'''
使用AdaBoost算法提升弱分类器性能
函数参数:
    dataArr - 数据矩阵
    classLabels - 数据标签
    numIt - 最大迭代次数
返回值:
    weakClassArr - 训练好的样本
    aggClassEst - 类别估计累计值
'''
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应为分母权值不能为0
        bestStump['alpha'] = alpha#单层决策树里面保存权值
        weakClassArr.append(bestStump)#储存单层决策树
        #print("ClassEst: ",classEst.T)
        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    #计算类别估计值
        #print("aggClassEst",aggClassEst.T)
        aggErrors = np.multiply(np.sign(aggClassEst)!=np.mat(classLabels).T,np.ones((m,1)))#计算误差
        errorRate = aggErrors.sum()/m
        #print('total error: ',errorRate,"\n",)
        if errorRate == 0.0:
            break
    return weakClassArr,aggClassEst

这段代码的作用是首先根据产生弱分类器然后将弱分类器装在一个集合中,然后更新样本的权值

测试算法:基于AdaBoost的分类
'''
AdaBoost分类器
函数参数:
    datToClass - 待分类样例
    classifierArr - 训练好的分类器
返回值:
    分类结果
'''

def adaClassify(datToClass,classifierArr):
    datMatrix = np.mat(datToClass)
    m=np.shape(datToClass)[0]
    aggClassEst = np.mat(np.zeros((m,1)))
    for i in range(len(classifierArr)):#遍历所有的分类器
        #构建单层决策树
        classEst = stumpClassify(datMatrix,classifierArr[i]['dim'],\
            classifierArr[i]['thresh'],classifierArr[i]['ineq'])
        aggClassEst+=classifierArr[i]['alpha']*classEst #将分类器合并
    return np.sign(aggClassEst)

利用已经有的分类器求出分类结果,最后将分类结果进行合并。
github地址:https://github.com/geroge-gao/MachineLeaning
参考资料:

  • 机器学习实战
  • 统计学习方法
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐