集成学习-AdaBoost实现 机器学习实战

AdaBoost实现

前两篇文章针对AdaBoost的伪代码实现步骤进行了讨论,也对关键步骤的更新方法进行了推导,脑海里已经基本有了整体框架,有了基本框架,代码的含义就清晰易懂了,下面看看AdaBoost是如何串行生成一系列基学习器的.

导入数据

from numpy import *
import matplotlib.pyplot as plt

def loadSimpData():#二维数据点,二分类
    datMat = [[ 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

几个简单的二维数据点,对应的标签值属于二分类{-1,1}

数据可视化

def show_dataSet(dataMat,classLabels):
    for i in range(len(dataMat)):
        if classLabels[i] == 1.0:
            plt.scatter(dataMat[i][0],dataMat[i][1],c='r',marker = 'o')
        else:
            plt.scatter(dataMat[i][0],dataMat[i][1],c='b',marker = 'o')
    plt.show()

简单画图看一下几个数据点的分布情况:

                           

根据维度与阈值调整标签

def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):#just classify the data
    retArray = ones((shape(dataMatrix)[0],1))#自定义一个5x1的全部为1的列表作为初始标签
    if threshIneq == 'lt':#根据维度和阈值,调整标签值
        retArray[dataMatrix[:,dimen] < threshVal] = -1.0
    else:
        retArray[dataMatrix[:,dimen] > threshVal] = -1.0
    return retArray

retArray[dataMatrix[:,dimen] <= threshVal] = -1.0 改为了 retArray[dataMatrix[:,dimen] < threshVal] = -1.0与书中不同,这样修改是为了使界限更加清晰,应为有等号时分割界限会和样本点重合,可视化效果不是太好.

选择最优划分方式

def buildStump(dataArr,classLabels,D):
    dataMatrix = mat(dataArr); labelMat = mat(classLabels).T#转换数据为矩阵形式
    m,n = shape(dataMatrix)#提取维度
    numSteps = 10.0; bestStump = {}; bestClasEst = mat(zeros((m,1)))#初始化最优划分选择
    minError = 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']: #lt,gt为不同的标签适应
                threshVal = (rangeMin + float(j) * stepSize)
                predictedVals = stumpClassify(dataMatrix,i,threshVal,inequal)#根据阈值修改标签
                errArr = mat(ones((m,1)))
                errArr[predictedVals == labelMat] = 0
                weightedError = D.T*errArr  #计算错误率
                print ("划分维度: %d, 阈值: %.2f, 标签方法: %s, 错误率 %.3f" % (i, threshVal, inequal, weightedError))
                if weightedError < minError:#添加最低错误率时对应的维度,阈值,划分方式
                    minError = weightedError
                    bestClasEst = predictedVals.copy()
                    bestStump['划分维度:'] = i
                    bestStump['阈值:'] = threshVal
                    bestStump['标签方法:'] = inequal
                    # print('bestStump is ',bestStump)
    return bestStump,minError,bestClasEst

这一步是针对单个学习器而言,通过最大值与最小值构造阈值变化步长,针对不同的维度选择,阈值选择,标签划分方式,分别计算对应划分方式的预测错误率,选择当前错误率最低的划分方式,依次放到beststum中,用于之后构建最佳分类边界.

AdaBoost串行实现

def adaBoostTrainDS(dataArr,classLabels,numIt=40):
    weakClassArr = []
    m = shape(dataArr)[0]
    D = mat(ones((m,1))/m)   #初始化权值,1/样本数
    aggClassEst = mat(zeros((m,1)))
    for i in range(numIt):
        bestStump,error,classEst = buildStump(dataArr,classLabels,D)#寻找本次最优划分方式
        print ("D:",D.T)
        alpha = float(0.5*log((1.0-error)/max(error,1e-16)))#学习器重要性度量α计算
        bestStump['学习器权数α:'] = alpha  
        weakClassArr.append(bestStump)                  #添加最优划分
        print ("classEst: ",classEst.T)
        expon = multiply(-1*alpha*mat(classLabels).T,classEst) #规范化分布计算,分母zm
        D = multiply(D,exp(expon))                              
        D = D/D.sum()#更新权数D
        #根据预测分类与权数,累加样本预测值
        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
    return weakClassArr,aggClassEst

这一步主要参考伪代码很容易理解,参考之前的推导,这里思路很清晰,1)初始化权值分布 -> 2)迭代生成基学习器,更新α,权值D,计算总体错误率  ->  3)直到总体错误率为0时,停止迭代过程,每一步更新的代码对照之前的公式也很容易读懂,至此,Adaboost的生成过程就结束了.

分类边界可视化

def show_classifier_region(classfierarray):
    for i in range(len(dataMat)):
        if classLabels[i] == 1.0:
            plt.scatter(dataMat[i][0],dataMat[i][1],c='r',marker = 'o')
        else:
            plt.scatter(dataMat[i][0],dataMat[i][1],c='b',marker = 'o')
    x = arange(0.5,2.3,0.01)
    y = arange(0.5,2.3,0.01)

    for i in range(3):

        if int(classfierarray[i]['划分维度:']) == 0:
            for j in y:
                plt.scatter(classfierarray[i]['阈值:'],j,c='k',alpha = 0.5,s=1)
        elif int(classfierarray[i]['划分维度:']) == 1:
            for j in x:
                plt.scatter(j,classfierarray[i]['阈值:'],c='k',alpha = 0.5,s=1)
    plt.show()

针对每次最优划分方式,在图中画出对应的分类边界.

[{'划分维度:': 0, '阈值:': 1.3999999999999999, '标签方法:': 'lt', '学习器权数α:': 0.6931471805599453},
{'划分维度:': 0, '阈值:': 0.90000000000000002, '标签方法:': 'lt', '学习器权数α:': 0.5493061443340548},
{'划分维度:': 1, '阈值:': 1.1100000000000001, '标签方法:': 'lt', '学习器权数α:': 0.8047189562170503}]

由于数据点比较少,所以只需要三个基学习器,就可以达到100%精度,上面三条记录就是每个学习器的划分参数

                            

主函数

if __name__ == "__main__":
    dataMat,classLabels = loadSimpData()
    D = mat(ones((5,1))/5)
    print(buildStump(dataMat,classLabels,D))
    weakClassArr,aggClassEst = adaBoostTrainDS(dataMat,classLabels,9)
    print([i for i in weakClassArr])
    show_dataSet(dataMat,classLabels)
    show_classifier_region(weakClassArr)

将上述函数依次添加到AdaBoost.py中,运行主函数即可得到上述结果,这里只显示了一部分,可以看到最终分类器的total error为0.0,从而达到停止条件,如果只想出结果,不需要这些步骤,可以相应的去掉print语句即可.

根据最优选择图解AdaBoost

1)第一条划分界限

{'划分维度:': 0, '阈值:': 1.3999999999999999, '标签方法:': 'lt', '学习器权数α:': 0.6931471805599453}

                            

维度为0,对应x轴,阈值为1.3999,所以分类边界为x = 1.3999,标签方法为lt,即小于阈值的数据判定为-1,大与阈值的判定为+1,如图所示.

2)第二条划分界限

{'划分维度:': 0, '阈值:': 0.90000000000000002, '标签方法:': 'lt', '学习器权数α:': 0.5493061443340548}

                            

维度为0,对应x轴,阈值为0.9,所以分类边界为x = 0.9,标签方法仍为lt,即小于阈值的数据判定为-1,大与阈值的判定为+1,如图所示.这个时候0.9-1.39之间的数据点分类还不正确,因此还需继续迭代.

3)第三条划分界限

{'划分维度:': 1, '阈值:': 1.1100000000000001, '标签方法:': 'lt', '学习器权数α:': 0.8047189562170503}

                            

维度更改为1,对应y轴,阈值为1.11,所以分类边界为y=1.11,标签方法仍为lt,即小于阈值的数据判定为-1,大与阈值的判定为+1,所以对应边界上面部分判断为正,下面判断为负,对于最右边的两个红点,根据投票法,两正一负为正,左上角的红点,两正一负为正,左下的两个蓝点,两负一正为负,从而全部样本预测正确,结束学习器生成过程,完成迭代.

4)总分类边界

                            

根据三条划分线以及对应的标签规则,我们得到了最终的分类边界,如果存在更多的数据点,则可能需要生成更多的基学习器,而分类边界也对应更多的直线.

总结:

针对更复杂的数据点,AdaBoost将生成更多的学习器,分类边界也更加复杂,但是总体的泛化错误率会随之降低,AdaBoost的大致过程就是这样了,总的来说,集成学习就是人多力量大,虽然一个学习器的效果不佳,但是大家的结果组合起来考虑,结果往往有所不同,不过在建模同时,也应该考虑模型是否存在过拟合的情形,像本文的例子,只有五个样例点,建模预测时很容易出现泛化错误率太高的情况。接下来将介绍集成学习中另一个主要成员,Bagging,它和Boosting不同,Bagging可以同时并行生成多个学习器,最终汇总结果.

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BIT_666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值