手把手实现AdaBoost算法

手把手实现AdaBoost算法

标签: 机器学习 Python

主要参考资料:

  • Peter HARRINGTON.机器学习实战[M].李锐,李鹏,曲亚东,王斌译.北京:人民邮电出版社, 2013.
  • 李航.统计学习方法[M].北京:清华大学出版社, 2012

1.AdaBoost算法简介

当一个分类器正确率不是那么的高时,我们称其为“弱分类器”,或者说该分类器的学习方法为“弱学习方法”。与之对应的,存在“强分类器”和“强学习方法”。强学习方法的正确率很高。

非常幸运,数学上有证明,强学习方法和弱学习方法是等价的,即一个方法是强学习方法的充要条件是该方法为弱学习方法。因此只要我们找到了一个弱学习方法,就可以把它转化为强学习方法。AdaBoost算法(Adaptive Boosting)就是把弱学习方法转化为强学习方法的典型算法,其英文直译为“自适应增强”,基础的AdaBoost算法用于处理二分类问题。

AdaBoost算法的大部分时间都花在训练上,分类器将多次在同一数据集上训练若干弱分类器。每个弱分类器都有一个系数,分类的时候,将弱分类器的分类结果乘以对应的系数然后求和,最后根据“和”决定强分类器的分类结果。

《统计学习方法》中有一句话对于AdaBoost算法的原理总结的很到位——“三个臭皮匠顶个诸葛亮”。

2.实现AdaBoost算法

整篇博文以下表的中数据为例,构建一个强分类器。
例题来源于http://www.csie.edu.tw

序号 1 2 3 4 5 6 7 8 9 10
x 0 1 2 3 4 5 6 7 8 9
y 1 1 1 -1 -1 -1 1 1 1 -1

2.1创建数据集

AdaBoost的训练数据集一般为 T={(x1,y1),(x2,y2),...,(xN,yN)} ,其中 xi n 维特征变量, yi 为其对应的分类。
我们创建数据集的代码如下:

from numpy import *

#创建数据集和标签
def create_dataMat():
    dataMat = mat([[0], [1], [2], [3], [4], [5], [6], [7], [8], [9]]) #x
    labels = [1, 1, 1, -1, -1, -1, 1, 1, 1, -1] #y
    return dataMat, labels

2.2建立弱分类器

常见的机器学习算法都可以建立弱分类器,不过最经常使用的弱分类器是单层决策树。单层决策树又称为决策树桩(decision stump),即层数为1的决策树。

本文使用单层决策树作为弱分类器。每一个训练数据都有一个权值系数,注意不是弱分类器的系数。建立最佳单层决策树的依据就是:每个训练数据在单层决策树中的分类结果乘以自己的权值系数后相加的“和”最小,即分类误差最小化(详见下文)。

建立最佳决策树的方法其实很“笨”,就是遍历所有的决策树,然后选择“和”最小的决策树。其中有一点需要注意,就是决策树的阈值边界要在特征最小值和最大值之外,即需要检测把所有数据划分为一类的决策树。

建立单层决策树的输入为训练数据集,权值系数矢量,具体代码如下:

#使用单层决策树对数据进行分类
def stumpClassify(dataMatrix,dimen,threshVal,threshIneq): 
    retArray = ones((shape(dataMatrix)[0],1))
    if threshIneq == 'lt': #按照大于或小于关系分类
        retArray[dataMatrix[:,dimen] <= threshVal] = -1.0 #所有第dimen维小于等于阈值的分类置为-1
    else:
        retArray[dataMatrix[:,dimen] > threshVal] = -1.0
    return retArray

#建立最佳单层决策树,即弱分类器 
def buildStump(dataMat,labels,D):
    labelMat = mat(labels).T #转置为列向量
    m,n = shape(dataMat)
    numSteps = 10.0
    bestStump = {} #存贮决策树
    bestClasEst = mat(zeros((m,1))) #最佳单层决策树的结果,初始化为全部分类错误
    minError = inf #最小错误,初始化为无穷大
    for i in range(n): #遍历所有的特征
        rangeMin = dataMat[:,i].min()
        rangeMax = dataMat[:,i].max()
        stepSize = (rangeMax-rangeMin)/numSteps #步长
        for j in range(-1,int(numSteps)+1): #将阈值起始与终止设置在该特征取值的范围之外
            for inequal in ['lt', 'gt']: #取less than和greater than
                threshVal = (rangeMin + float(j) * stepSize)
                predictedVals = stumpClassify(dataMat,i,threshVal,inequal) #使用单层决策树分类
                errArr = mat(ones((m,1))) #1表示分类错误,0表示分类正确,初始化为全部错误
                errArr[predictedVals == labelMat] = 0 #矢量比较,分类正确的置为1
                weightedError = D.T*errArr  #乘以系数D
                #print "split: dim %d, thresh %.2f, thresh ineqal: %s, the weighted error is %.3f" % (i, threshVal, inequal, weightedError)
                if weightedError < minError: #如果错误率变小,更新最佳决策树
                    minError = weightedError
                    bestClasEst = predictedVals.copy()
                    bestStump['dim'] = i
                    bestStump['thresh'] = threshVal
                    bestStump['ineq'] = inequal
    return bestStump,minError,bestClasEst

2.3建立强分类器

AdaBoost算法的关键和难点是建立强分类器,而建立强分类器的关键和难点是计算每个训练数据权值系数和每个弱分类器系数。

建立强分类器的具体步骤如下:

(1) 初始化训练数据的权值系数

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

(2) 对 m=1,2,...,M

  • 使用具有权值系数 Dm 的训练数据集创建弱分类器 Gm(x)
  • 计算 Gm(x) 在训练数据集上的分类误差率
    em=i=1NwmiI(Gm(xi)yi)(2)
  • 计算 Gm(x) 的系数
    αm=12log1emem(3)
  • 更新训练数据的权值系数
    Dm+1=(wm+1,1,wm+1,2,...,wm+1,N)(4)

    wm+1,i=wmiZmexp(αmyiGm(xi))(5)

    Zm=i=1Nwm,iexp(αmyiGm(xi))(6)

    (3) 最终的强分类器公式为
    G(x)=sign(m=1MαmGm(x))(7)

这里进行一些定性的说明

  • 训练数据的初始权值系数 w1i 均相等
  • 权值系数的和为1,即 wmi=1 ,这表明 Gm(x) 在加权的训练数据集上的分类误差率是被 Gm(x) 误分类样本的权值之和
  • 弱分类器的 Gm(x) 的系数为 αm ,由式(3)可知,当 em12 时, αm0 ,且 em 越小, αm 越大,可以看出误差率越小的弱分类器在最终分类器中的作用越大
  • 弱分类器系数的和并不为1,即 αm1
  • 训练数据权值系数的变化有以下性质:提高那些被前一轮弱分类器错误分类样本的权值,而降低那些被正确分类样本的权值。这样的话,新一轮的弱分类器就可以尽可能将这些错误分类样本正确分类了

上述算法的具体代码如下:

#使用Adaboost建立强分类器,numIt表示最大迭代次数
def adaBoostTrainDS(dataMat,classLabels,numIt=40):
    weakClassArr = []
    m = shape(dataMat)[0]
    D = mat(ones((m,1))/m) #初始化系数D
    aggClassEst = mat(zeros((m,1)))
    for i in range(numIt):
        bestStump,error,classEst = buildStump(dataMat,classLabels,D) #建立最佳单层决策树
        #print "D:",D.T
        alpha = float(0.5*log((1.0-error)/max(error,1e-16)))#计算alpha, max(error,1e-16)防止下溢出
        bestStump['alpha'] = alpha  
        weakClassArr.append(bestStump) #保存弱分类器
        #print "classEst: ",classEst.T
        expon = multiply(-1*alpha*mat(classLabels).T,classEst) #自然底数的指数,为了更新D
        D = multiply(D,exp(expon)) #为下次迭代更新D
        D = D/D.sum()
        aggClassEst += alpha*classEst #矢量相加
        #print "aggClassEst: ",aggClassEst.T
        aggErrors = multiply(sign(aggClassEst) != mat(classLabels).T,ones((m,1))) #分类正确与错误的结果
        errorRate = aggErrors.sum()/m #分类错误率
        #print "total error: ",errorRate
        if errorRate == 0.0: break #如果分类错误率为0,结束分类
    return weakClassArr

代码中的一些说明:

  • 强分类器是通过迭代的方式训练的,当错误率为0或者达到最大迭代次数的时候,程序结束
  • max(error,1e-16)确保不会发生除0溢出
  • sign()函数返回自变量的符号,1表示正号,-1表示负号

2.4使用强分类器

创建了强分类器之后,就可以使用了,其中的运算大多都为numpy中的矢量运算,简单方便。

使用每个弱分类器对每个待分类数据进行分类,然后将分类结果乘以各自的弱分类器系数后求和,当和大于0时判断为正类;当和小于0时判断为负类。

#使用Adaboost分类器对数据进行分类 
def adaClassify(datToClass, classifierArr):
    dataMatrix = mat(datToClass) #待分类数据转化为矩阵
    m = shape(dataMatrix)[0] #待分类数据的个数
    aggClassEst = mat(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 sign(aggClassEst)

简单的测试一下我们创建的强分类器,正确率为1。

if __name__ == '__main__':
    dataMat, labels = create_dataMat()
    weakClassArr = adaBoostTrainDS(dataMat, labels)
    for i in range(10):
        res = adaClassify([i], weakClassArr)
        print 'data: %d, class: %2d' % (i, res)

运行结果:
手把手实现AdaBoost算法_第1张图片

3.总结

有一种方法是将不同的分类器组合起来使用,这种方法叫做集成方法或者元算法。本文所实现的AdaBoost算法就是典型的集成方法,AdaBoost算法可以把弱学习方法转化为强学习方法。

  • 本文的弱分类器是单层决策树,弱分类器还可以为Logistic回归,SVM等
  • AdaBoost算法的特点就是通过迭代每次学习一个基本分类器。每次迭代中,提高被前一轮分类器错误分类数据的权值,降低被正确分类的权值
  • AdaBoost算法的训练比较耗时
  • 源码在我的GitHub中,MachineLearningAction仓库里面有常见的机器学习算法处理常见数据集的各种实例,希望能够得到你的STAR

你可能感兴趣的:(python,机器学习)