zoukankan      html  css  js  c++  java
  • Python实现Adaboost

    1.Adaboost概念

    提升方法的思路是综合多个分类器,得到更准确的分类结果。 即“三个臭皮匠顶个诸葛亮”。《统计学习方法》称AdaBoost是提升算法的代表,所谓提升算法,指的是一种常用的统计学习方法,应用广泛且有效。在分类问题中,它通过改变训练样本的权重,学习多个分类器,并将这些分类器进行线性组合,提髙分类的性能。
    AdaBoost算法的基本思想:
    1)多轮训练,多个分类器
    2)每轮训练增加错误分类样本的权值,降低正确分类样本的权值
    3)降低错误率高的分类器的权值,增加正确率高的分类器的权值

    2.Adaboost算法流程

    给定一个二类分类的训练数据集,T={(x1,y1),(x2,y2),(xn,yn)}T={({x_1},y_1),({x_2},y_2),dots({x_n},y_n)},其中χiXRnχ_i∈X⊆ R^n表示实例的特征向量,yiY{+1,1}y_i∈Yin{+1,-1},XX是实例空间,YY是标记集合。AdaBoost利用以下算法,从训练数据中学习一系列弱分类器或基本分类器,并将这些弱分类器线性组合成为一个强分类器。
    AdaBoost算法
    输入:训练数据集T={(x1,y1),(x2,y2),(xn,yn)}T={({x_1},y_1),({x_2},y_2),dots({x_n},y_n)},其中χiXRnχ_i∈X⊆ R^n,yiY{+1,1}y_i∈Yin{+1,-1};弱学习算法;
    输出:最终分类器G(x)G(x)
    (1)初始化训练数据的权值分布

    D1=(W11,...,W1i,...,W1n)D_1=(W_{11},...,W_{1i},...,W_{1n})

    每个w的下标由两部分构成,前一个数表示当前迭代次数,与D的下标保持一致,后一个数表示第几个权值,与位置保持一致。初始情况下,每个权值都是均等的。
    (2)对m=1,2,...Mm=1,2,...M
    (这里的M表示训练的迭代次数,是由用户指定的。每轮迭代产生一个分类器,最终就有M个分类器,当然我们也可以设置一些条件,满足某些条件时跳出迭代,例如,所有样本都分对了,误差为0时,跳出迭代):
    (a)使用具有权值分布的训练数据集学习,得到基本分类器

    Gm(x):X{+1,1}G_m(x):X{ ightarrow} {+1,-1}

    (b)在Gm(x)G_m(x)训练数据集上的分类误差率

    em=iNP(Gm(xi)yi)=iNwmiI(Gm(xi)yi))e_m=sum_i^NP(G_m(x_i){ eq}y_i)=sum_i^Nw_{mi}I(G_m(x_i){ eq}y_i))

    分类误差率这个名字可能产生误解,这里其实是个加权和。
    (c)计算Gm(x)G_m(x)的系数

    αm=12log1ememalpha_m = frac{1}{2}log{frac{1-e_m}{e_m}}

    这里的对数是自然对数。ama_m表示Gm(x)G_m(x)在最终分类器中的重要性。由式αm=12log1ememalpha_m = frac{1}{2}log{frac{1-e_m}{e_m}}可知,当em<=1/2e_m<=1/2时,am>=0a_m>=0,并且ama_m随着eme_m的减小而增大,所以分类误差率越小的基本分类器在最终分类器中的作用越大。

    (d)更新训练数据集的权值分布

    Dm+1=(Wm+1,1,...,Wm+1,i,...,Wm+1,n)D_{m+1}=(W_{m+1,1},...,W_{m+1,i},...,W_{m+1,n})

    wm+1,i=wm,iZmeαmyiGm(xi)w_{m+1, i}=frac{w_{m, i}}{Z_m}e^{-alpha_my_iG_m(x_i)}

    y只有正负一两种取值,所以上式可以写作:

    wm+1,i={wmiZmeam,Gm(xi)=yiwmiZmeam,Gm(xi)yiw_{m+1,i}=egin{cases} frac{w_{mi}}{Z_m}e^{-a_m},G_m(x_i)=y_i \ frac{w_{mi}}{Z_m}e^{a_m},G_m(x_i){ eq}y_i end{cases}

    这里,ZmZ_m是规范化因子

    Zm=i=1Nwmiexp(αmyiGm(xi))Z_m=sum_{i=1}^Nw_{m i}exp({-alpha_my_iG_m(x_i)})

    它使Dm+1D_{m+1}成为一个概率分布。
    由此可知,被基本分类器误分类样本的权值得以扩大,而被正确分类样本的权值却得以缩小。两相比较,误分类样本的权值被放大e2am=1ememe^{2am} = {frac{1-e_m}{e_m}}倍。因此,误分类样本在下一轮学习中起更大的作用。不改变所给的训练数据,而不断改变训练数据权值的分布,使得训练数据在基本分类器的学习中起不同的作用,这是AdaBoost的一个特点。

    (3)构建基本分类器的线性组合:

    f(x)=m=1MamGm(x)f(x)=sum_{m=1}^{M}a_mG_m(x)

    得到最终分类器:

    G(x)=sign(f(x))=sign(m=1MamGm(x))G(x)=sign(f(x))=sign(sum_{m=1}^{M}a_mG_m(x))
    这里需要注意的是,我们得到的是M个f(x)方程,需要将每个方程的结果求和,最好只取结果的正负号,在Adaboost中我们的预测结果与数值是无关的。

    3.完整代码

    代码运行结果:
    在这里插入图片描述

    # -*- coding: utf-8 -*-
    """
     @Time    : 2018/12/04 13:58
     @Author  : hanzi5
     @Email   : hanzi5@yeah.net
     @File    : Adaboost.py
     @Software: PyCharm
    """
    import numpy as np
    #import pandas as pd
    import matplotlib
    import matplotlib.pyplot as plt
    
    matplotlib.rcParams['font.family']='SimHei'  # 用来正常显示中文
    plt.rcParams['axes.unicode_minus']=False     # 用来正常显示负号
    
    def splitDataSet(dataSet, i, value,types='lt'):
        """
    #划分数据集,只进行一次划分的树,将划分后的结果返回
        :param dataSet: 数据
        :param i: 特征的下标
        :param value: 阈值
        :param types: 大于或小于
        :return: 分类结果,注意返回的直接就是1,-1分类结果
        """
        retArray = np.ones((np.shape(dataSet)[0],1))     # 默认类型都为1
        if types=='lt':   # 使用(小于等于value)划分数据,满足条件的将结果值改为-1
            retArray[dataSet[:,i]<=value]= -1.0  
        elif types=='gt': # 使用(大于value)划分数据,满足条件的将结果值改为-1
            retArray[dataSet[:,i]>value]= -1.0  
        return retArray 
    
    def bulidSimpleTree(dataSet,y,D):
        """
    创建一个最简单的树,只进行一次划分,相当于一个树桩
        :param dataSet: 数据特征矩阵
        :param y: 标签向量
        :param D: 训练数据的权重向量
        :return: 最佳决策树,最小的错误率加权和,最优预测结果
        """
        m,n=dataSet.shape                      # 样本行数及列数
        numFeatures = len(dataSet[0]) - 1      # 最后一列为y,计算x特征列数
        numSteps=10                            # 用于计算步长,进行numSteps等分
        minError=np.inf                        # 初始化损失值
        bestTree={}                            # 使用dict存储最优的一系列树
        for i in range(numFeatures):           # 遍历所有x特征列
            # i=0
            rangeMin = dataSet[:, i].min()     # 该xi维的最小值
            rangeMax = dataSet[:, i].max()     # 该xi维的最大值
            stepSize = (rangeMax - rangeMin) / numSteps # 步长
            for j in range(-1,int(numSteps) + 1):    # 循环寻找最优切分点
                # j=-1
                for inequal in ['lt', 'gt']:         # 遍历(lt小于等于)及(gt大于)
                    # inequal=1
                    value = (rangeMin + float(j) * stepSize) # 切分值
                    predictedVals = splitDataSet(dataSet, i, value, inequal)  # 获取切分后的数据
                    errArr = np.mat(np.ones((m, 1)))
                    errArr[predictedVals == y] = 0  # 预测正确的样本对应的错误率为0,否则为1
                    weightedError=D.T*errArr        # 《统计学习方法》李航P138,8.1,计算训练数据上的分类误差率
                    if weightedError < minError:    # 记录最优树桩决策树分类器
                        minError = weightedError    # 计算错误率加权和
                        bestClasEst = predictedVals.copy() # 最好的预测结果
                        bestTree['column'] = i             # 维度x
                        bestTree['splitValue'] = value     # 切分值
                        bestTree['ineq'] = inequal         # 切分方法(lt小于等于)及(gt大于)
        return bestTree, minError, bestClasEst
    
    # 基于单层决策树的adaboost分类器
    def adaboost(dataSet,maxLoop=100):
        """
    基于单层决策树的ada训练
        :param dataSet: 样本x及y
        :param maxLoop: 迭代次数
        :return: 一系列弱分类器及其权重,样本分类结果
        """
        adaboostTree=[]
        m,n=dataSet.shape                     # 样本行数及列数
        y=dataSet[:,-1].reshape((-1,1))       # 将y提取出来,方便进行计算
        D = np.array(np.ones((m,1))/m)        # 将每个样本的权重初始化为均等,有多少条数据就有多少个d
        aggClassEst = np.mat(np.zeros((m,1))) # 每个数据点的类别估计累计值
        for i in range(maxLoop):              # maxLoop超参数,总迭代次数
            bestTree, minError, bestClasEst=bulidSimpleTree(dataSet,y,D)
            alpha=0.5*np.log((1-minError)/(minError+0.00001)) # 《统计学习方法》李航P139,8.2,计算Gm的系数,分母加一个小数避免除数为0
            bestTree['alpha'] = alpha.getA()[0][0]            # 将matrix中的值提取出来,并加入到bestTree中
            adaboostTree.append(bestTree)                     # 将树存入list中存储
            D=D*np.exp(-alpha.getA()[0][0]*y*bestClasEst)     # 更新权重D,《统计学习方法》李航P139,8.3-8.5,计算Gm(x)的系数
            D=D/D.sum()                                       # 归一化权重值,统计学习方法》李航P139,8.5,计算Zm
            # 计算所有分类器的误差,如果为0则终止训练
            aggClassEst += alpha.getA()[0][0]*bestClasEst     # 分类估计累计值,adaboost是线性运行的,需要将每次的树预测结果相加
            aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(y),np.ones((m,1))) # aggClassEst每个元素的符号代表分类结果,如果与y不等则表示错误,统计学习方法》李航P138,8.8
            errorRate = aggErrors.sum()/m                     # 平均分类误差
            print( "total error: ",errorRate)
            if errorRate == 0.0:                              # 平均分类误差等于0的,说明数据已经全部分类正确,跳出循环
                break
        return adaboostTree,aggClassEst
    
    
    def adaClassify(data, adaboostTree):
        """
    对预测数据进行分类
        :param data: 预测样本x及y
        :param adaboostTree: 使用训练数据,训练好的决策树
        :return: 预测样本分类结果
        """
        dataMatrix = np.mat(data)  
        m = np.shape(dataMatrix)[0]
        aggClassEst = np.mat(np.zeros((m, 1)))
        for i in range(len(adaboostTree)):                     # 遍历所有adaboostTree,将估计值累加
            classEst = splitDataSet(dataMatrix, adaboostTree[i]['column'], adaboostTree[i]['splitValue'], adaboostTree[i]['ineq'])  
            aggClassEst += adaboostTree[i]['alpha'] * classEst # 分类估计累计值,adaboost是线性运行的,需要将每次的树预测结果相加
        result = np.sign(aggClassEst)                          # 只取正负号,《统计学习方法》李航P139,8.8
        return result
    
    def plotData(dataSet):
        """
    数据画图
        """
        type1_x1 = []
        type1_x2 = []
        type2_x1 = []
        type2_x2 = []
    
        # 取两类x1及x2值画图
        type1_x1 = dataSet[dataSet[:,-1] == -1][:,:-1][:,0].tolist()
        type1_x2 = dataSet[dataSet[:,-1] == -1][:,:-1][:,1].tolist()
        type2_x1 = dataSet[dataSet[:,-1] == 1][:,:-1][:,0].tolist()
        type2_x2 = dataSet[dataSet[:,-1] == 1][:,:-1][:,1].tolist()
        
        # 画点
        fig = plt.figure()
        ax = fig.add_subplot(111)
        ax.scatter(type1_x1,type1_x2, marker='s', s=90)
        ax.scatter(type2_x1,type2_x2, marker='o', s=50, c='red')
        plt.title('Adaboost训练数据')
        
    if __name__ == '__main__':
        print('
    1、Adaboost,开始')
        dataSet = np.array([
            [ 1. , 2.1, 1 ],
            [ 2. , 1.1, 1 ],
            [ 1.3, 1. , -1],
            [ 1. , 1. , -1],
            [ 2. , 1. , 1 ]])
        #classLabels = [1.0, 1.0, -1.0, -1.0, 1.0]
        
        # 画图
        print('
    2、Adaboost数据画图')
        plotData(dataSet)
        
        print('
    3、计算Adaboost树')
        adaboostTree,aggClassEst=adaboost(dataSet)
        
        # 对数据进行分类
        print('
    4、对[5,5],[0, 0]点,使用Adaboost进行分类:')
        print( adaClassify([[5,5],[0, 0]], adaboostTree))    
    

    参考资料:
    1、《机器学习实战》Peter Harrington著
    2、《机器学习》西瓜书,周志华著
    3、 斯坦福大学公开课 :机器学习课程
    4、机器学习视频,邹博
    5、《统计学习方法》李航
    6、提升方法
    7、分类——组合算法之提升1:AdaBoost提升算法以及Python实现

  • 相关阅读:
    mysql缓存
    复杂映射
    SQL 映射的 XML 文件
    xml配置文件
    从xml中构建sqlSessionFactory
    eclipse使用时jar不在libraries
    去掉不用的工作空间
    javac找不到或无法加载主类 com.sun.tools.javac.Main,
    文本,布局,样式
    (常用)re模块
  • 原文地址:https://www.cnblogs.com/hanzi5/p/10105613.html
Copyright © 2011-2022 走看看