adaboost 机器学习实战

本文深入探讨了Adaboost算法的原理与实现,包括bagging和boosting的概念,Adaboost如何通过集成多个弱分类器提升分类效果,以及具体的算法流程和权重调整策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前介绍了K近临,决策树,贝叶斯,逻辑回归以及SVM。相对于本节将要介绍的,前者属于“强”分类器,而本节的adaboost则属于集中了多个”弱“分类器来进行分类。可以理解成”三个臭皮匠赛过诸葛亮“的思想。

这种将分类器组合的方法称为集成方法或者元算法。这里介绍两类算法,bagging和boosting。

bagging的思路就是把原始数据有放回的随机选择S次后得到S个新数据集的方法。新数据集和原始数据集大小相等。比如原始数据集是m条数据,每个新数据集也是m个。但是由于是有放回的随机选择,所以每个新数据集内的数据会有重复,并且可能会有部分原始数据集的数据没有包含在新数据集中。之后用某个学习算法分别作用于每个数据集,就得到了S个分类器。然后通过这S个分类器分类并进行投票。

boosting是关注分类错误的数据来构建新的分类器。这里我们介绍其中的一种方法,adaboost。

adaboost 在训练过程中,先是对样本赋予一个权重,向量D,开始每个样本的权重都是一样的。先在这个基础之上,训练一个分类器。然后计算该分类器的错误率,并且将样本扔到分类器里,对于分类器分对的元素,降低权重,对于分类错误的,增加权重。然后再用一个新分类器对修改过权重的数据进行分类。之后形成第二个分类器,再把修改过一次的数据扔到第二个分类器里面,正确的降低权重,错误的增加权重,然后再用一个新的分类器(第三个)对第二次修改权重的数据训练……同时对于每个分类器都也有一个权重alpha。

并将错误率定义为:
ε = 未 正 确 分 类 的 样 本 数 目 所 有 样 本 数 目 \varepsilon = \frac{未正确分类的样本数目}{所有样本数目} ε=
分类器权重alpha定义为:
α = 1 2 l n ( 1 − ε ε ) \alpha =\frac{1}{2}ln(\frac{1-\varepsilon}{\varepsilon}) α=21ln(ε1ε)
权重的修改方式:(正确样本)
D i ( t + 1 ) = D i ( t ) e − α S u m ( D ) D_i^{(t+1)} = \frac{D_i^{(t)}e^{-\alpha}}{Sum(D)} Di(t+1)=Sum(D)Di(t)eα
(错误样本)
D i ( t + 1 ) = D i ( t ) e α S u m ( D ) D_i^{(t+1)} = \frac{D_i^{(t)}e^{\alpha}}{Sum(D)} Di(t+1)=Sum(D)Di(t)eα
分类过程可以参考这个图,黑色的条长度代表权重的大小
在这里插入图片描述
对于adaboost的方法,其思想就是,通过构建分类器来实现对“错误数据”的分类。因为我们不难发现,在每次构建分类器,更改数据的权重时,错误的数据往往获得更大的权重,而对于之后的分类器,错误的数据会很大程度的影响到分类的正确率,所以会分类算法会更“照顾”权重大的数据。

那么对于adaboost的里面权重的理解,可以参考如下的文章,以及其他内容。

adaBoost权重的意义
https://blog.youkuaiyun.com/qq_27015119/article/details/80797767

关于AdaBoost的一些再思考
https://www.cnblogs.com/chaosimple/p/4029795.html

单层决策树(decision stump , 也称决策树桩)是一种简单的决策树。前面我们已经介绍了决
策树的工作原理,接下来将构建一个单层决策树,而它仅基于单个特征来做决策。由于这棵树只
有一次分裂过程,因此它实际上就是一个树桩。

from numpy import *
import matplotlib.pyplot as plt



def loadSimpData():
    dataMat =matrix([[1.,2.1], [2.,1.1], [1.3,1.], [1.,1.],[2.,1.]])
    classLable=[1.0,1.0,-1.0,-1.0,1.0]
    return dataMat,classLable


def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):
    retArray = ones((shape(dataMatrix)[0],1))
    if threshIneq == 'lt':
        retArray[dataMatrix[:,dimen]<=threshVal] = -1.0
    else:
        retArray[dataMatrix[:, dimen] > threshVal] = -1.0
    return retArray

def buildStump(dataArr,classLabels,D):
    dataMatrix=mat(dataArr);labelMat = mat(classLabels).T
    m, n = shape(dataMatrix)#m5 n2
    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']:
                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("split:dim %d, thresh %.2f, thresh ineqal: %s, the weighted errror is %.3f" %(i,threshVal,inequal,weightedError))
                if weightedError<minError:	
                    minError = weightedError
                    bestClasEst = predictedVals.copy()
                    bestStump['dim'] = i
                    bestStump['thresh'] = threshVal
                    bestStump['ineq'] = inequal
    print(bestStump,minError,bestClasEst)
    return bestStump,minError,bestClasEst


dataMat,classLabels=loadSimpData()
D =mat(ones((5,1))/5)
buildStump(dataMat,classLabels,D)

我看了很多教程,对第二三层的for循环都是一句带过,没有解释是啥意思。for j in range(-1,int(numSteps)+1):for inequal in ['lt','gt']:

这里首先先说一下步长是啥,同时,我们找到了在x轴上最大和最小的两个点,然后求差并分成numSteps份,然后形成分类边界,这时第二个循环做的事情。第三个循环就是尝试分界线的左右两个部分是正负样本。比如下图中,左边分成橘色,右边分成蓝色。得到的错误率是0.2,然后在尝试左蓝右橘,错误则是0.8。这与上面程序中的内容一致。

split:dim 0, thresh 1.20, thresh ineqal: gt, the weighted errror is 0.600
split:dim 0, thresh 1.30, thresh ineqal: lt, the weighted errror is 0.200
split:dim 0, thresh 1.30, thresh ineqal: gt, the weighted errror is 0.800
split:dim 0, thresh 1.40, thresh ineqal: lt, the weighted errror is 0.200
split:dim 0, thresh 1.40, thresh ineqal: gt, the weighted errror is 0.800

在这里插入图片描述


def adaBoostTrainDS(dataArr,classLabels,numIt=40):
    weakClassArr = []
    m = shape(dataArr)[0]
    D = mat(ones((m,1))/m)
    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']=alpha
        weakClassArr.append(bestStump)
        print("classEst:",classEst.T)
        expon = multiply(-1*alpha*mat(classLabels).T,classEst) # 数组对应元素位置相乘 数据处理的技巧 正确分类是-alpha 错相反
        D = multiply(D,exp(expon))
        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
    return weakClassArr
    
classifierArray = adaBoostTrainDS(dataMat,classLabels,9)
print("finish")

完整的Adaboost算法的实现

def adaBoostTrainDS(dataArr,classLabels,numIt=40):
    weakClassArr = []
    m = shape(dataArr)[0]
    D = mat(ones((m,1))/m)
    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']=alpha
        weakClassArr.append(bestStump)
        print("classEst:",classEst.T)
        expon = multiply(-1*alpha*mat(classLabels).T,classEst) # 数组对应元素位置相乘 数据处理的技巧 正确分类是-alpha 错相反
        D = multiply(D,exp(expon))
        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
    return weakClassArr

dataMat,classLabels=loadSimpData()
D =mat(ones((5,1))/5)
buildStump(dataMat,classLabels,D)
classifierArray = adaBoostTrainDS(dataMat,classLabels,9)
D: [[0.2 0.2 0.2 0.2 0.2]]
classEst: [[-1.  1. -1. -1.  1.]]
aggClassEst: [[-0.69314718  0.69314718 -0.69314718 -0.69314718  0.69314718]]
total error : 0.2
D: [[0.5   0.125 0.125 0.125 0.125]]
classEst: [[ 1.  1. -1. -1. -1.]]
aggClassEst: [[ 0.27980789  1.66610226 -1.66610226 -1.66610226 -0.27980789]]
total error : 0.2
D: [[0.28571429 0.07142857 0.07142857 0.07142857 0.5       ]]
classEst: [[1. 1. 1. 1. 1.]]
aggClassEst: [[ 1.17568763  2.56198199 -0.77022252 -0.77022252  0.61607184]]
total error : 0.0

这段程序就是对整个Adaboost过程的实现,因为实战的书里,没有对D的求解的过程,所以程序中expon和D那段也没有做过多介绍。

当我们想做分类预测的时候,通过如下的代码实现,我们可以看到stumpClassify方法,在上述分类的过程中已经介绍到了。只需要将我们分类好的参数扔到这个方法中即可。

def adaClassify(dataToclass,classifierArr):
    dataMatrix = mat(dataToclass)
    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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值