Adaboost从原理到实现(Python)

时间:2022-05-07
本文章向大家介绍Adaboost从原理到实现(Python),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

首先来回答AdaBoosting的基本思想

通俗地讲就是综合某些专家的判断,往往要比一个专家单独的判断要好(三个臭皮匠顶过诸葛亮-周志华《机器学习第八章》)。在”强可学习”和”弱可学习”的概念上来说就是我们通过对多个弱可学习的算法进行”组合提升或者说是强化”得到一个性能赶超强可学习算法的算法。

其次回答Boosting的思路

1.找到一个弱分类器,分类器简单,快捷,易操作(如果它本身就很复杂,而且效果还不错,那么进行提升无疑是锦上添花,增加复杂度,甚至上性能并没有得到提升,具体情况具体而论)。

2.迭代寻找N个最优的分类器(最优的分类器,就是说这N个分类器分别是每一轮迭代中分类误差最小的分类器,并且这N个分类器组合之后是分类效果最优的。)。

在迭代求解最优的过程中我们需要不断地修改数据的权重(AdaBoost中是每一轮迭代得到一个分类结果与正确分类作比较,修改那些错误分类数据的权重,减小正确分类数据的权重 ),后一个分类器根据前一个分类器的结果修改权重在进行分类,因此可以看出,迭代的过程中分类器的效果越来越好,所以需要给每个分类器赋予不同的权重。最终我们得到了N个分类器和每个分类器的权重,那么最终的分类器也得到了。

归纳算法流程

输入:训练数据集M*N(M为样本数量,N为特征数量)弱,其中xi表示数据i[数据i为N维],yi表示数据的分类为yi,Y={-1,1}表示xi在某种规则约束下的分类。

输出:最终的分类器G(x)

1. 初始化训练数据的权值分布(每个样本数据权重均等),D1 = (W11,W12,W13,...,W1M), W1i=1/M, ,M表示数据的个数,i=1,2,3…M。 

2. j=1,2,3,…,J(表示迭代的次数/或者最终分类器的个数,取决于是否能够使分类误差为0)。

(a)使用具有权值分布Dj的训练数据集学习,得到基本的分类器:Gj(x);

(b) 计算Gj(x)在训练集上的分类误差率

求的是分错类别的数据的权重值和,表示第i个数据的权重Dj[i];

(c)计算Gj(x)第j个分类器的系数,,ej表示的是分类错误率。

(d)更新训练数据集的权重Dj+1,数据集的权重根据上一次权重进行更新, i=1,2,…M

Z是规范化因子,他表示所有数据权重之和,它使Dj+1成为一个概率分布。

3. 构建基本分类器的线性组合

得到最终的分类器:

Boosting与Bagging对比:

AdaBoost泛化错误率低,易编码,可以应用在大部分分类器上,无参数调整,但是对离群点敏感。

Bagging基于数据随机重抽样的分类器构建方法,原始数据集中重新选择S次得到S个新数据集,将磨沟算法分别作用于这个数据集,最后进行投票,选择投票最多的类别作为分类类别。

Boosting关注那些已有分类器错分的数据来获得新的分类器,Bagging则是根据已训练的分类器的性能来训练的。Boosting分类器权重不相等,权重对应与上一轮迭代成功度Bagging分类器权重相等。

代码实现(由《机器学习实战》改编)

# -*- coding:utf-8 -*- 
from numpy import*
class Adaboosting(object):
    def loadSimpData(self):
        datMat = 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 stumpClassify(self, datMat, dimen, threshVal, threshIneq):
        # print "-----data-----"
        # print datMat
        retArr = ones((shape(datMat)[0], 1))  
        if threshIneq == 'lt':
            retArr[datMat[:, dimen] <= threshVal] = -1.0  # 小于阈值的列都为-1
        else:
            retArr[datMat[:, dimen] > threshVal] = -1.0  # 大于阈值的列都为-1
        # print "---------retArr------------"
        # print retArr
        return retArr
    def buildStump(self, dataArr, classLables, D):
        """
        单层决策树生成函数
        """
        dataMatrix = mat(dataArr)
        lableMat = mat(classLables).T
        m, n = shape(dataMatrix)
        numSteps = 10.0  # 步数,影响的是迭代次数,步长
        bestStump = {}  # 存储分类器的信息
        bestClassEst = mat(zeros((m, 1)))  # 最好的分类器
        minError = inf  # 迭代寻找最小错误率
        for i in range(n):
            # 求出每一列数据的最大最小值计算步长
            rangeMin = dataMatrix[:, i].min()
            rangeMax = dataMatrix[:, i].max()
            stepSize = (rangeMax - rangeMin) / numSteps
            # j唯一的作用用步数去生成阈值,从最小值大最大值都与数据比较一边了一遍
            for j in range(-1, int(numSteps) + 1):
                threshVal = rangeMin + float(j) * stepSize  # 阈值
                for inequal in ['lt', 'gt']:
                    predictedVals = self.stumpClassify(
                        dataMatrix, i, threshVal, inequal)
                    errArr = mat(ones((m, 1)))
                    errArr[predictedVals == lableMat] = 0  # 为1的 表示i分错的
                    weightedError = D.T * errArr  # 分错的个数*权重(开始权重=1/M行)
                    # print "split: dim %d, thresh %.2f, thresh ineqal:
#%s,the weighted error is %.3f" % (i, threshVal, inequal, weightedError)
                    if weightedError < minError:  # 寻找最小的加权错误率然后保存当前的信息
                        minError = weightedError
                        bestClassEst = predictedVals.copy()  # 分类结果
                        bestStump['dim'] = i
                        bestStump['thresh'] = threshVal
                        bestStump['ineq'] = inequal
        # print bestStump
        # print minError
        # print bestClassEst  # 类别估计
        return bestStump, minError, bestClassEst
    def adaBoostingDs(self, dataArr, classLables, numIt=40):
        """
        基于单层决策树的AdaBoosting训练过程:
        """
        weakClassArr = []  # 最佳决策树数组
        m = shape(dataArr)[0]
        D = mat(ones((m, 1)) / m)
        aggClassEst = mat(zeros((m, 1)))
        for i in range(numIt):
            bestStump, minError, bestClassEst = self.buildStump(
                dataArr, classLables, D)
            print "bestStump:", bestStump
            print "D:", D.T
            alpha = float(
                0.5 * log((1.0 - minError) / max(minError, 1e-16)))
            bestStump['alpha'] = alpha
            weakClassArr.append(bestStump)
            print "alpha:", alpha
            print "classEst:", bestClassEst.T  # 类别估计
            expon = multiply(-1 * alpha * mat(classLables).T, bestClassEst)
            D = multiply(D, exp(expon))
            D = D / D.sum()
            aggClassEst += alpha * bestClassEst
            print "aggClassEst ;", aggClassEst.T
            # 累加错误率
            aggErrors = multiply(sign(aggClassEst) !=
                                 mat(classLables).T, ones((m, 1)))
            # 错误率平均值
            errorsRate = aggErrors.sum() / m
            print "total error:", errorsRate, "n"
            if errorsRate == 0.0:
                break
        print "weakClassArr:", weakClassArr
        return weakClassArr
    def adClassify(self, datToClass, classifierArr):
        """
        预测分类:
        datToClass:待分类数据
        classifierArr: 训练好的分类器数组
        """
        dataMatrix = mat(datToClass)
        m = shape(dataMatrix)[0]
        aggClassEst = mat(zeros((m, 1)))
        print
        for i in range(len(classifierArr)):  # 有多少个分类器迭代多少次
            # 调用第一个分类器进行分类
            classEst = self.stumpClassify(dataMatrix, classifierArr[i]['dim'],
                                          classifierArr[i]['thresh'],
                                          classifierArr[i]['ineq']
                                          )
            # alpha 表示每个分类器的权重,
            print classEst
            aggClassEst += classifierArr[i]['alpha'] * classEst
            print aggClassEst
        return sign(aggClassEst)
if __name__ == "__main__":
    adaboosting = Adaboosting()
    D = mat(ones((5, 1)) / 5)
    dataMat, lableMat = adaboosting.loadSimpData()
    # 训练分类器
    classifierArr = adaboosting.adaBoostingDs(dataMat, lableMat, 40)
    # 预测数据
    result = adaboosting.adClassify([0, 0], classifierArr)
    print result