提升方法AdaBoost算法

此算法就是一个学习,然后积累,再学习的方法。

大概流程是首先建立一个弱分类,然后计算其错误率,根据错误的样本数给样本分配权重,然后再根据这个样本权重去计算新的最小分类错误率,以此类推,直到所有分类器权重*样本==label。

算法原理:

D表示是每行数据样本的权重

根据D来计算每个特征值的错误率即 wD=D*Error(error表示划分的数据集错误率),从中选择一个最好的划分(最好的预测结果为G(x),特征列i,特征值,minerror)

然后计算α,表示第k个弱分类器的权重,\alpha = \frac{1}{2}\cdot \log \left ( \frac{1-err}{err} \right )

此函数在(0,1/2)单减,所以表示的是error越大,表示弱分类器权重越小。

再计算样本的权重:

第k+1个弱分类器的样本集权重为w_{k+1}=\frac{w_{k}\cdot \exp \left ( -\alpha _{k}\cdot y_{i}\cdot G(xi) \right )}{\sum w_{ki}\exp \left ( -\alpha _{k}\cdot y_{i}\cdot G(xi) \right )}

当第i个样本分类错误时,y*G(x)<0,然后-α*y*G(x)>0,表示其样本权重增加

所以公式表示的是如果样本分类错误,那么它在下一次分类的时候会给予更多的关注。

最后统计所有弱分类器,采用加权表决法:

f(x)=\sum \alpha _{k}\cdot G_{k}(x)

表示的是弱分类器的权重*其分类结果

最终强分类器为:

G(x)=sign(f(x))=sign(\sum \alpha _{k}\cdot G_{k}(x))

误差分析:

误差界限:

\frac{1}{N}\cdot \sum_{i=1}^{N}I(G(x_{i})\neq y_{i})\leq \frac{1}{N}\sum \exp (-y_{i}f(x_{i}))=\prod Z_{m}   (1)

其中 Z_{m}=\sum_{i=1}^{N}w_{mi}\exp (-\alpha _{m}\cdot y_{i}\cdot G_{m}(x_{i}))

f(x)=\sum_{m=1}^{M}\alpha _{m}G_{m}(x)

G(x)=sign(f(x))=sign(\sum_{m=1}^{M}\alpha _{m}\cdot G_{m}(x))

w_{k+1}=\frac{w_{k}\cdot \exp \left ( -\alpha _{k}\cdot y_{i}\cdot G(xi) \right )}{\sum w_{ki}\exp \left ( -\alpha _{k}\cdot y_{i}\cdot G(xi) \right )}

现在证明(1),第一个<=比较好证明,因为e^{x}-x> 0,所以不等式必定成立。

等式证明:

\frac{1}{N}\cdot \sum \exp (-y_{i}f(x_{i}))=\frac{1}{N}\sum \exp (-\sum_{m=1}^{M}\alpha _{m}y_{i}G_{m}(x_{i}))

                                        = \sum w_{1i}\prod_{m=1}^{M}\exp (-\alpha _{m}y_{i}G_{m}(x_{i}))

                                        =Z_{1}\sum w_{2i}\prod_{m=2}^{M}\exp (-\alpha _{m}y_{i}G_{m}(x_{i}))

                                        =Z_{1}Z_{2}\sum w_{3i}\prod_{m=2}^{M}\exp (-\alpha _{m}y_{i}G_{m}(x_{i}))

                                        =.....=Z_{1}Z_{2}Z_{3}.....Z_{M-1}\sum w_{M}\exp (-\alpha _{M}y_{i}G_{m}(x_{i})

                                        =\prod Z_{m}

 第一个等号,直接代入f(x) 

第二个等号,因为一开始权重为1/N,所以w1=1/N,然后两个求和函数合并成累乘。

因为w_{k+1}=\frac{w_{k}\cdot \exp \left ( -\alpha _{k}\cdot y_{i}\cdot G(xi) \right )}{\sum w_{ki}\exp \left ( -\alpha _{k}\cdot y_{i}\cdot G(xi) \right )}

所以w_{k+1}*Z_{m}=w_{k}\cdot \exp \left ( -\alpha _{k}\cdot y_{i}\cdot G(xi) \right )代入原式

现在说明为什么使用这个公式\alpha = \frac{1}{2}\cdot \log \left ( \frac{1-err}{err} \right )

为了让模型误差尽可能小,就要选取合适的参数使得误差上界足够小,而上面公式证明了上界与Z_{m}的累乘有关,

Z_{m}=\sum_{i=1}^{N}w_{mi}\exp (-\alpha _{m}y_{i}G(x_{i}))

=\sum_{y_{i}=G_{m}(x_{i})}^{} w_{mi}e^{-\alpha _{m}}+\sum_{y_{i}\neq G_{m}(x_{i})}^{}w_{mi}e^{\alpha _{m}}

=e^{-\alpha _{m}}\cdot (1-e_{m})+e^{\alpha _{m}}\cdot (e_{m})  其中   \sum w_{mi}=e_{m}是因为前者表示的是误差样本的权重,也就间接代表分类误差率。

最后对上式的\alpha _{m}求偏导,并令导数为0,即可得到

e^{2\alpha _{m}}=(1-e_{m})/e_{m}\rightarrow \alpha _{m}=\frac{1}{2}log\frac{1-e_{m}}{e_{m}}

下面对于二分类问题,进一步探讨训练误差界

训练误差界:

\prod_{m=1}^{M}Z_{m}=\prod_{m=1}^{M}\left [ 2\sqrt{e_{m}(1-e_{m})} \right ]=\prod_{m=1}^{M}\sqrt{(1-4\gamma _{m}^{2})}\leq \exp (-2\sum_{m=1}^{M}\gamma _{m}^{2})

其中\gamma _{m}=\frac{1}{2}-e_{m}

因为有不等式1-x\leq e^{-x} ,取x= 4\gamma _{m}^{2} 可得

1-4\gamma _{m}^{2}\leq e^{-4\gamma _{m}^{2}}\Rightarrow \sqrt{1-4\gamma _{m}^{2}}\leq e^{-2\gamma _{m}^{2}}\Rightarrow \prod \sqrt{1-4\gamma _{m}^{2}}\leq \prod e^{-4\gamma _{m}^{2}}= e^{-2\sum \gamma _{m}^{2}}

结合上面定理可得推论:如果所有的\gamma _{m}有下界\gamma,则

\frac{1}{N}\sum_{i=1}^{N}I(G(x_{i})\neq y_{i})\leq e^{-2M\gamma ^{2}}

M表示的弱分类器的个数,上式说明误差是随M变多呈指数下降的

至此,adaboost证明过程推导结束。

from __future__ import print_function
from numpy import *

def loadSimpData():
    '''
    测试数据
    
    Returns:
        dataArr   feature对应的数据集
        labelArr  feature对应的分类标签
    '''
    dataArr=array([[1.,2.1],[2.,1.1],[1.3,1.],[1.,1.],[2.,1.]])
    labelArr=[1.0,1.0,-1.0,1.0]
    return dataArr,labelArr

# general function to parse tab -delimited floats
def loadDataSet(fileName):
    # get number of fields
    numFeat=len(open(fileName).readline().split('\t'))
    dataArr=[]
    labelArr=[]
    fr=open(fileName)
    for line in fr.readlines():
        lineArr=[]
        curLine=line.strip().split('\t')
        for i in range(numFeat-1):
            lineArr.append(float(curLine[i]))
        dataArr.append(lineArr)
        labelArr.append(float(curLine[-1]))
    return dataArr,labelArr

def stumpClassify(dataMat,dimen,threshVal,threshIneq):
    '''
    stumpClassify(将数据集,按照feature列的value进行二分法切分比较来赋值分类)
    
    Args:
        dataMat    Matrix数据集
        dimen      特征列
        threshVal  特征列要比较的值
    Returns:
        retArray 结果集
    '''
    # 默认都是1
    retArray=ones((shape(dataMat)[0],1))
    # dataMat[:,dimen] 表示数据集中第dimen列的所有值
    # threshIneq=='lt' 表示修改左边的值,gt表示修改右边的值
    # print '-----', threshIneq,dataMat[:,dimen],threshVal
    if threshIneq=='lt':
        retArray[dataMat[:,dimen]<=threshVal]=-1.0
    else:
        retArray[dataMat[:,dimen]>threshVal]=-1.0
    return retArray

def buildStump(dataArr,labelArr,D):
    '''
    buildStump(得到决策树的模型)
    
    Args:
        dataArr   特征标签集合
        labelArr  分类标签集合
        D         最初的样本的所有特征权重集合
    Returns:
        bestStump    最优的分类器模型
        minError     错误率
        bestClasEst  训练后的结果集
    '''
    # 转换数据
    dataMat=mat(dataArr)
    labelMat=mat(labelArr).T
    # m行 n列
    m,n=shape(dataMat)
    
    # 初始化数据
    numSteps=10.0
    bestStump={}
    bestClasEst=mat(zeros((m,1)))
    # 初始化的最小误差为无穷大
    minError=inf
    
    # 循环所有的feature列,将列切分成
    for i in range(n):
        rangeMin=dataMat[:,i].min()
        rangeMax=dataMat[:,i].max()
        # print ('rangeMin=%s'%(rangeMin,rangeMax))
        # 计算每一份的元素个数
        stepSize=(rangeMax-rangeMin)/numSteps
        # 例如: 4=(10-1)/2 那么 1-4(-1次) 1(0次)  1+1*4(1次) 1+2*4(2次)
        # 所以: 循环 -1/0/1/2
        for j in range(-1,int(numSteps)+1):
            # go over less than and greater than
            for inequal in ['lt','gt']:
                # 如果是-1,那么得到rangeMin-stepSize,如果是numSteps,那么得到rangeMax
                threshVal=(rangeMin+float(j)*stepSize)
                # 对单层决策树进行简单分类,得到预测的分类值
                predictedVals=stumpClassify(dataMat,i,threshVal,inequal)
                # print predictedVals
                errArr=mat(ones((m,1)))
                # 正确为0 ,错误为1
                errArr[predictedVals==labelMat]=0
                # 计算 平均每个特征的概率0,2*错误概率的总和为多少,就知道错误率多高
                # 例如: 一个都没错,那么错误率=0.2*0=0,5个都错,那么错误率=0.2*5=1。只错3个,错误率为0.2*3=0.6
                weightedError=D.T*errArr   # D为样本权重,样本权重*错误率表示最终错误率
                '''
                dim         表示feature列
                threshVal   表示树的分界值
                inequal     表示计算树左右颠倒的错误率的情况
                weightedError 表示整体结果的错误率
                bestClasEst 预测的最优结果
                '''
                # print ("split:dim %d,thresh %.2f,thresh ineqal:%s,the weighted error is %.3f"%(i,threshVal,inequal,weightedError))
                if weightedError

 

# #  我们要将5个点进行分类
# dataArr,labelArr=loadSimpData
# print('dataArr',dataArr,'labelArr',labelArr)

# # D表示最初值,对1进行均分为5份,平均每一个初始的概率都为0.2
# # D的目的是为了计算错误概率:  weightedError = D.T*errArr
    # D = mat(ones((5, 1))/5)
    # print 'D=', D.T

    # # bestStump, minError, bestClasEst = buildStump(dataArr, labelArr, D)
    # # print 'bestStump=', bestStump
    # # print 'minError=', minError
    # # print 'bestClasEst=', bestClasEst.T

    # # 分类器: weakClassArr
    # # 历史累计的分类结果集
    # weakClassArr, aggClassEst = adaBoostTrainDS(dataArr, labelArr, 9)
    # print '\nweakClassArr=', weakClassArr, '\naggClassEst=', aggClassEst.T

    # """
    # 发现:
    # 分类的权重值: 最大的值,为alpha的加和,最小值为-最大值
    # 特征的权重值: 如果一个值误判的几率越小,那么D的特征权重越少
    # """

    # # 测试数据的分类结果, 观测: aggClassEst分类的最终权重
    # print adaClassify([0, 0], weakClassArr).T
    # print adaClassify([[5, 5], [0, 0]], weakClassArr).T

    # 马疝病数据集
    # 训练集合
dataArr,labelArr=loadDataSet("7.AdaBoost/horseColicTraining2.txt")  
weakClassArr,aggClassEst=adaBoostTrainDS(dataArr,labelArr,40)
print(weakClassArr,'\n-------\n',aggClassEst)
# 计算ROC下面的AUC的面积大小
plotROC(aggClassEst.T,labelArr)
# 测试集合
dataArrTest,labelArrTest=loadDataSet("7.AdaBoost/horseColicTraining2.txt")
m=shape(dataArrTest)[0]
predicting10=adaClassify(dataArrTest,weakClassArr)
errArr=mat(ones((m,1)))
# 测试: 计算总样本数,错误样本数,错误率
print(m,errArr[predicting10!=mat(labelArrTest).T].sum(),errArr[predicting10!=mat(labelArrTest).T].sum()/m)

使用sklearn实现adaboost 

from __future__ import print_function

import matplotlib.pyplot as plt
import numpy as np
from sklearn import metrics
from sklearn.ensemble import AdaBoostRegressor
from sklearn.tree import DecisionTreeRegressor

print(__doc__)

rng=np.random.RandomState(1)   #随机数生成器   rng.normal(0, 0.1, X.shape[0]生成一个值在0,0.1之间的X形状的数组
x=np.linspace(0,6,100)[:,np.newaxis]
y=np.sin(x).ravel()+np.sin(6*x).ravel()+rng.normal(0,0.1,x.shape[0])
# dataArr, labelArr = loadDataSet("data/7.AdaBoost/horseColicTraining2.txt")

# Fit regression model
regr_1=DecisionTreeRegressor(max_depth=4)
regr_2=AdaBoostRegressor(DecisionTreeRegressor(max_depth=4),n_estimators=300,random_state=rng)

regr_1.fit(x,y)
regr_2.fit(x,y)

#Predict
y_1=regr_1.predict(x)
y_2=regr_2.predict(x)

# plot the results
plt.figure()
plt.scatter(x,y,c="k",label="training samples")
plt.plot(x,y_1,c="g",label="n_estimators=1",linewidth=2)
plt.plot(x,y_2,c="r",label="n_estimators=300",linewidth=2)
plt.xlabel("data")
plt.ylabel("target")
plt.title("Boosted Decision Tree Regression")
plt.legend()
plt.show()

print('y---',type(y[0]),len(y),y[:4])
print('y_1---',type(y_1[0]),len(y_1),y_1[:4])
print('y_2---', type(y_2[0]), len(y_2), y_2[:4])

# 适合2分类
y_true=np.array([0,0,1,1])
y_scores=np.array([0.1,0.4,0.35,0.8])
print('y_scores---',type(y_scores[0]),len(y_scores),y_scores)
print(metrics.roc_auc_score(y_true,y_scores))
from numpy import *
a=mat([[1,2],[1,1]])
b=mat([[1,1],[1,1]])
c=mat(ones((2,2)))
c[a==b]=0
print(c)
x = linspace(0, 6, 10)[:, newaxis]   #linespace(0,6,10)在0,6之间产生一个均匀分布的数组,[:,newaxis]表示给数组升一个维度
print(x)
print(x.shape)
print(x.shape[0])
print(sin(x).ravel())   # ravel表示把数组拉成一维

提升方法AdaBoost算法_第1张图片

总结:

弱分类器可以是多层决策树,内部原理应该是先分第一层,然后遍历所有特征值,分第二层,然后再遍历未使用的特征值,分第三层。。。。以此类推,最后有多少个分支就有多少种情况,计算哪种分支最优

你可能感兴趣的:(AI,算法,人工智能)