欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

AdaBoost算法源码分析

发布时间:2024/9/20 编程问答 33 豆豆
生活随笔 收集整理的这篇文章主要介绍了 AdaBoost算法源码分析 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

基本的理论知识可以参考李航的统计学习和西瓜书,在这里简单介绍:

  • bagging:基于数据随机抽样的分类器,更先进的bagging方法有随机森林等。
  • boosting:是一种与bagging类似的技术,但boosting是不同的分类器通过串行训练获得的,每个新分类器都是根据已训练出的分类器的性能进行训练。boosting是通过集中关注被已有分类器错分的那些数据来获得新的分类器。

    AdaBoost算法的流程如图:

其运行过程下:训练数据中的每个样本,并赋予其一个权重,这些权重构成了向量乃。一开始,这些权重都初始化成相等值。首先在训练数据上训练出一个弱分类器并计算该分类器的错误率,然后在同一数据集上再次训练弱分类器。在分类器的第二次训练当中,将会重新调整每个样本的权重,其中第一次分对的样本的权重将会降低,而第一次分错的样本的权重将会提高。为了从所有弱分类器中得到最终的分类结果,AdaBoost为每个分类器都分配了一个权重值alpha,这些alpha值是基于每个弱分类器的错误率进行计算的。

这里只是简单说了下,下面是adaboost的代码实现:

1. 基于单层决策树的AdaBoost算法

# -*- coding: utf-8 -*- from numpy import *def loadSimpData():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(dataMatrix,dimen,threshVal,threshIneq): # dimen:那一列的特征,threshVal:阈值retArray = ones((shape(dataMatrix)[0],1))if threshIneq == 'lt': retArray[dataMatrix[:,dimen] <= threshVal] = -1.0 # <=阈值的相应的下标的值会被赋予-1else:retArray[dataMatrix[:,dimen] > threshVal] = -1.0 # >阈值的相应的下标的值会被赋予-1return retArray # 返回的是该特征列的分类的列向量# 遍历所有的可能输入值,并找到数据集上最佳的单层决策树 def buildStump(dataArr,classLabels,D): # D是权重向量,在更新过程中保持不变dataMatrix = mat(dataArr); labelMat = mat(classLabels).T # 确保转化为矩阵格式m,n = shape(dataMatrix) # m个样本,n个特征# bestStump字典用于存储给定权重向量D时所得到的最佳单层决策树,numSteps用于在特征的所有可能值上进行遍历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): # 遍历[-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]for inequal in ['lt', 'gt']: # 在大于和小于之间切换threshVal = (rangeMin + float(j) * stepSize) # 阈值大小predictedVals = stumpClassify(dataMatrix,i,threshVal,inequal) # 调用stumpClassify来分类errArr = mat(ones((m,1)))errArr[predictedVals == labelMat] = 0 # 正确分类的被赋予0weightedError = D.T*errArr # 计算总的误差,即总的分类错的样本的权重的和print "split: dim %d, thresh %.2f, thresh ineqal: %s, the weighted error is %.3f" % (i, threshVal, inequal, weightedError)if weightedError < minError:minError = weightedErrorbestClasEst = predictedVals.copy() # numpy的copy()是深拷贝bestStump['dim'] = ibestStump['thresh'] = threshValbestStump['ineq'] = inequal #print 'bestStump:',bestStumpprint 'finish'# 返回的:对应最小分类误差的{特征,阈值,和正负切换},既是单层决策树,最小误差率,该特征列的估计分类的列向量return bestStump,minError,bestClasEst # 基于单层决策树的AdaBoost训练过程 def adaBoostTrainDS(dataArr,classLabels,numIt=40): # 输入参数:数据集,类别,迭代次数(唯一需要用户指定的)weakClassArr = [] m = shape(dataArr)[0] # m代表多少个样本数D = mat(ones((m,1))/m) # 初始化所有样本的权重D为1/m,为了满足D是一个概率分布向量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))) # 每个分类器对应的更新权重值,max(error,1e-16)确保没有错误时不会发生下溢bestStump['alpha'] = alpha weakClassArr.append(bestStump) # 存储新树的参数print "classEst: ",classEst.T # 输出最小误差分类的相应特征的列向量# 对应的样本权重更新 expon = multiply(-1*alpha*mat(classLabels).T,classEst) # classEst:分类器的分类结果D = multiply(D,exp(expon)) D = D/D.sum() # D.sum():规范化因子#计算所有分类器的训练误差,如果为0,则退出循环aggClassEst += alpha*classEst # 每个数据点的类别估计累计值print "aggClassEst: ",aggClassEst.TaggErrors = multiply(sign(aggClassEst) != mat(classLabels).T,ones((m,1))) # errorRate = aggErrors.sum()/mprint "total error: ",errorRate,'\n'if errorRate == 0.0: breakreturn weakClassArr,aggClassEst# 主函数 datMat,classLabels=loadSimpData() #D=mat(ones((5,1))/5) #print buildStump(datMat,classLabels,D) classifierArray=adaBoostTrainDS(datMat,classLabels,9)

运行结果:

split: dim 0, thresh 0.90, thresh ineqal: lt, the weighted error is 0.400 split: dim 0, thresh 0.90, thresh ineqal: gt, the weighted error is 0.600 split: dim 0, thresh 1.00, thresh ineqal: lt, the weighted error is 0.400 . . . split: dim 1, thresh 2.10, thresh ineqal: lt, the weighted error is 0.600 split: dim 1, thresh 2.10, thresh ineqal: gt, the weighted error is 0.400 finish 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 . . . split: dim 1, thresh 2.10, thresh ineqal: gt, the weighted error is 0.143 finish 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

接下来我们观察一下中间的运行结果。数据的类别标签为[1.01.0,1.0,1.0,1.0]。在第一轮迭代中,D中的所有值都相等。于是,只有第一个数据点被错分了。因此在第二轮迭代中,D向量给第一个数据点0.5的权重。这就可以通过变量aggClassEst的符号来了解总的类别。第二次迭代之后,我们就会发现第一个数据点已经正确分类了,但此时最后一个数据点却是错分了。D向量中的最后一个元素变成0.5,而D向量中的其他值都变得非常小。最后,第三次迭代之后aggClassEst所有值的符号和真实类别标签都完全吻合,那么训练错误率为0,程序就此退出.

numpy的一些使用:

代码中:retArray[dataMatrix[:,dimen] <= threshVal] = -1.0

例子:

>>> from numpy import * >>> datMat = matrix([[ 1. , 2.1], ... [ 2. , 1.1], ... [ 1.3, 1. ], ... [ 1. , 1. ], ... [ 2. , 1. ]]) >>> datMat matrix([[ 1. , 2.1],[ 2. , 1.1],[ 1.3, 1. ],[ 1. , 1. ],[ 2. , 1. ]]) >>> datMat[:,1]<=2 matrix([[False],[ True],[ True],[ True],[ True]], dtype=bool) >>> retArray =ones((shape(datMat)[0],1)) >>> retArray array([[ 1.],[ 1.],[ 1.],[ 1.],[ 1.]]) >>> retArray[datMat[:,1]<=2]=5 >>> retArray array([[ 1.],[ 5.],[ 5.],[ 5.],[ 5.]]) >>>

代码:

aggErrors = multiply(sign(aggClassEst) != mat(classLabels).T,ones((m,1)))

例子:

In [8]: a=mat([1,2,1])In [10]: a.T Out[10]: matrix([[1],[2],[1]])In [11]: sign(a.T) Out[11]: matrix([[1],[1],[1]])In [13]: b=mat([1,-1,-1]).TIn [14]: b Out[14]: matrix([[ 1],[-1],[-1]])In [15]: sign(a.T)!=b Out[15]: matrix([[False],[ True],[ True]], dtype=bool)In [16]: c=sign(a.T)!=bIn [17]: c Out[17]: matrix([[False],[ True],[ True]], dtype=bool)In [18]: multiply(c,ones((3,1))) Out[18]: matrix([[ 0.],[ 1.],[ 1.]])In [19]:

其中更新值的几个代码是:
(1)

alpha = float(0.5*log((1.0-error)/max(error,1e-16)))

对应的是:αm=12log1emem
其中:em=p(Gm(xi)yi)=Ni=1wmiI(Gm(xi)yi)
此更新的是分类器的权值

(2)

expon = multiply(-1*alpha*mat(classLabels).T,classEst) D = multiply(D,exp(expon)) D = D/D.sum()

此处是更新训练集的权值分布:
Dm+1=(wm+1,1,...wm+1,i,...wm+1,N)

wm+1,i=wmiZmexp(αmyiGm(xi))
其中Zm是规范化因子

Zm=Ni=1wmiexp(αmyiGm(xi))

2. AdaBoost的应用

测试基于AdaBoost的应用:

# 基于adaboost的分类 def adaClassify(datToClass,classifierArr): # 输入参数:待分类样例,多个弱分类器组成的数组dataMatrix = mat(datToClass) # 把样例转换成numpy矩阵,m = shape(dataMatrix)[0]aggClassEst = mat(zeros((m,1)))print 'classifierArr:', '\n',classifierArr# 遍历所有的弱分类器,然后进行加权和for i in range(len(classifierArr)): classEst = stumpClassify(dataMatrix,classifierArr[i]['dim'],\classifierArr[i]['thresh'],\classifierArr[i]['ineq'])print 'classEst',classEstaggClassEst += classifierArr[i]['alpha']*classEst # 加权和print 'aggClassEst:',aggClassEstreturn sign(aggClassEst)# 主函数 datMat,classLabels=loadSimpData() #D=mat(ones((5,1))/5) #print buildStump(datMat,classLabels,D) classifierArray,aggClassEst=adaBoostTrainDS(datMat,classLabels,30) # 此处和机器学习实战上的不同 classify=adaClassify([0,0],classifierArray) print 'adaClassify:',classify

只需要在最上面的代码中加入adaClassify函数,并且把主函数改成上面这种形式就可以运行了,注意此处在机器学习实战中原书中有错误:

classifierArray=adaBoostTrainDS(datMat,classLabels,30)

由于adaBoostTrainDS返回的是两个数组,但这里仅有一个对象接受返回变量,所以在调用classifierArray时出现报错:

...File "C:/Users/LiLong/Desktop/adaboost_learning/adaboost.py", line 85, in adaClassifyclassEst = stumpClassify(dataMatrix,classifierArr[i]['dim'],\TypeError: list indices must be integers, not str

改成我代码里的形式就可以了。

正常的运行结果:

... split: dim 1, thresh 2.10, thresh ineqal: lt, the weighted error is 0.857 split: dim 1, thresh 2.10, thresh ineqal: gt, the weighted error is 0.143 finish 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 classifierArr: [{'dim': 0, 'ineq': 'lt', 'thresh': 1.3, 'alpha': 0.6931471805599453}, {'dim': 1, 'ineq': 'lt', 'thresh': 1.0, 'alpha': 0.9729550745276565}, {'dim': 0, 'ineq': 'lt', 'thresh': 0.90000000000000002, 'alpha': 0.8958797346140273}] classEst [[-1.]] aggClassEst: [[-0.69314718]] classEst [[-1.]] aggClassEst: [[-1.66610226]] classEst [[-1.]] aggClassEst: [[-2.56198199]] adaClassify: [[-1.]]

可以发现,随着迭代的进行,数据点[00]的分类结果越来越强。

3. 在难数据上应用adaboost算法

# 自适应数据加载函数 def loadDataSet(fileName): numFeat = len(open(fileName).readline().split('\t')) # 得到特征的数目 dataMat = []; labelMat = []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]))dataMat.append(lineArr)labelMat.append(float(curLine[-1])) # 最后一个特征默认为类别标签return dataMat,labelMat

加载函数改为上面的形式,并且把主函数改为下面的即可:

# 难数据上应用adaboost算法 datArr,labelArr=loadDataSet('horseColicTraining2.txt') classifierArr,aggClassEst=adaBoostTrainDS(datArr,labelArr,10) testArr,testlabelArr=loadDataSet('horseColicTest2.txt') prediction10=adaClassify(testArr,classifierArr) errArr=mat(ones((67,1))) numerr=errArr[prediction10!=mat(testlabelArr).T].sum() print 'numerr:',numerr print 'error rate:',float(numerr)/67

运行结果:

... classEst [[-1.][ 1.][-1.]..., [-1.][-1.][ 1.]] aggClassEst: [[ 0.95108899][ 1.20719077][ 0.18915694]..., [ 0.80958618][ 0.54030781][ 0.5273375 ]] numerr: 16.0 error rate: 0.238805970149

本代码将在马疝病数据集上应用adaboost分类器。
将弱分类器的数目设定为1到10000之间的几个不同数字,并运行上述过程。这时,得到的结果就会如表所示:

测试错误率一栏,就会发现测试错误率在达到了一个最小值之后又开始上升了。这类现象称之为过拟合。有文献声称,对于表现好的数据集,adaboost的测试错误率就会达到一个稳定值,并不会随着分类器的增多而上升。

4. 分类性能度量:ROC曲线

在上述1的代码基础之上加入下面的plotROC()函数,并把主函数改为如下形式:

# ROC曲线的绘制和AUC计算函数 def plotROC(predStrengths, classLabels): # predStrengths代表分类器的预测强度,classLabels使用过的类别标签import matplotlib.pyplot as plt # 导入画图函数cur = (1.0,1.0) # 浮点数二元组,保留绘制光标的位置ySum = 0.0 # ysum用于计算AUC的值numPosClas = sum(array(classLabels)==1.0) # 数组过滤的方式计算正例的数目yStep = 1/float(numPosClas); xStep = 1/float(len(classLabels)-numPosClas)sortedIndicies = predStrengths.argsort() # 元素升序排列,得到对应的元素的索引值排序fig = plt.figure() # 绘图fig.clf() # clear:清除原有变量 clc:清楚命令窗口的内容ax = plt.subplot(111)# 循环遍历所有值,索引值是按照元素值从小到大排列的for index in sortedIndicies.tolist()[0]:if classLabels[index] == 1.0:delX = 0; delY = yStep;else:delX = xStep; delY = 0;ySum += cur[1]#draw line from cur to (cur[0]-delX,cur[1]-delY)ax.plot([cur[0],cur[0]-delX],[cur[1],cur[1]-delY], c='b')cur = (cur[0]-delX,cur[1]-delY)ax.plot([0,1],[0,1],'b--')plt.xlabel('False positive rate'); plt.ylabel('True positive rate')plt.title('ROC curve for AdaBoost horse colic detection system')ax.axis([0,1,0,1])plt.show()print "the Area Under the Curve is: ",ySum*xStep# 主函数 datArr,labelArr=loadDataSet('horseColicTraining2.txt') classifierArr,aggClassEst=adaBoostTrainDS(datArr,labelArr,10) plotROC(aggClassEst.T, labelArr) # 传入的参数:aggClassEst代表adaboost的集成累加 labelArr:真实类别值

argsort()函数的使用

运行结果:

总结

以上是生活随笔为你收集整理的AdaBoost算法源码分析的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。