AdaBoost-Machine Learning In Action学习笔记

元算法(集成方法)是对其他算法进行组合的一种方式。
使用集成方法时会有多种形式:
  • 可以是不同算法的集成,
  • 也可以是同一算法在不同设置下的集成,
  • 还可以是数据集不同部分分配给不同分类器之后的集成。

AdaBoost
优点:泛化错误率低,易编码,可以应用在大部分分类器上,无参数调整。
缺点:离群点敏感。
适用数据类型:数值型和标称型数据。

自举汇聚法bootstrap aggregating),也称为bagging方法:数据集通常被认为是放回取样得到的,比如要得到一个大小为n的新数据集,该数据集中的每个样本都是在原始数据集中随机抽样(即抽样之后又放回)得到的。(分类器权重是相等的)
boosting:与bagging类似的集成分类器方法。不同的分类器是通过串行训练而获得的,每个新分类器都根据已训练出的分类器的性能来进行训练。(分类器权重并不相等,每个权重代表的是其对应分类器在上一轮迭代中的成功度。)
AdaBoost:boosting方法拥有多个版本,AdaBoost是其中一个最流行的版本。

AdaBoost的一般流程
收集数据:可以使用任意方法。
准备数据:依赖于所使用的弱分类器类型,本章使用的是单层决策树,这种分类器可以处理任何数据类型。当然也可以使用任意分类器作为弱分类器,第2章到第6章中的任一分类器都可以充当弱分类器。作为弱分类器,简单分类器的效果更好。(“弱”意味着分类器的性能比随机猜测要略好,但是也不会好太多。)
分析数据:可以使用任意方法。
训练算法:AdaBoost的大部分时间都用在训练上,分类器将多次在同一数据集上训练弱分类器。
测试算法:计算分类的错误率。
使用算法:同SVM一样,AdaBoost预测两个类别中的一个。如果想把它应用到多个类别的场合,那么就要像多类SVM中的做法一样对AdaBoost进行修改。

AdaBoost运行过程:
  1. 训练数据中的每个样本,并赋予其一个权重,这些权重构成了向量D。一开始,这些权重都初始化成相等值。
  2. 首先在训练数据上训练出一个弱分类器并计算该分类器的错误率,然后在同一数据集上再次训练弱分类器。
  3. 在分类器的第二次训练当中,将会重新调整每个样本的权重,其中第一次分对的样本的权重将会降低,而第一次分错的样本的权重将会提高。(为了从所有弱分类器中得到最终的分类结果,AdaBoost为每个分类器都分配了一个权重值(alpha),这些alpha值是基于每个弱分类器的错误率(ε)进行计算的。)
  4. 计算出alpha值之后,可以对权重向量D进行更新,以使得那些正确分类的样本的权重降低而错分样本的权重升高。
  5. 在计算出D之后,AdaBoost又开始进入下一轮迭代。AdaBoost算法会不断地重复训练和调整权重的过程,直到训练错误率为0或者弱分类器的数目达到用户的指定值为止。

以下公式计算依次为错误率,alpha,正确分类的样本权重,错分的样本权重。
                 
                  

单层决策树:仅基于单个特征来做决策,这棵树只有一次分裂过程。

基于单层决策树构建弱分类器伪代码:
将最小错误率minError设为+∞  
对数据集中的每一个特征(第一层循环):  
    对每个步长(第二层循环):  
       对每个不等号(第三层循环):  
          建立一棵单层决策树并利用加权数据集对它进行测试  
          如果错误率低于minError,则将当前单层决策树设为最佳单层决策树  
返回最佳单层决策树

完整AdaBoost算法伪代码:
对每次迭代:
    利用buildStump()函数找到最佳的单层决策树   
    将最佳单层决策树加入到单层决策树数组 
    计算alpha   
    计算新的权重向量D 
    更新累计类别估计值  
    如果错误率等于0.0,则退出循环

示例:在一个难数据集上的AdaBoost应用
收集数据:提供的文本文件。
准备数据:确保类别标签是+1和-1而非1和0。
分析数据:手工检查数据。
训练算法:在数据上,利用adaBoostTrainDS()函数训练出一系列的分类器。
测试算法:我们拥有两个数据集。在不采用随机抽样的方法下,我们就会对AdaBoost和Logistic回归的结果进行完全对等的比较。
使用算法:观察该例子上的错误率。不过,也可以构建一个Web网站,让驯马师输入马的症状然后预测马是否会死去。

过拟合(过学习):测试错误率在达到了一个最小值之后又开始上升了。

非均衡分类问题:在大多数情况下不同类别的分类代价并不相等。
混淆矩阵:帮助更好地了解分类中的错误

分类性能度量指标:错误率、正确率、召回率及ROC曲线
  • 正确率:TP/(TP+FP),给出的是预测为正例的所有样本中的真正正例的比例
  • 召回率:TP/(TP+FN),给出的是预测为正例的真实正例占所有真实正例的比例
很容易构造一个高正确率或高召回率的分类器,但是很难同时保证两者成立。
  • ROC曲线:ROC曲线给出的是当阈值变化时假阳率(FP/(FP+TN))和真阳率(TP/(TP+FN))的变化情况。
1. 由于在不同的阈值下,不同的分类器的表现情况可能各不相同,因此以某种方式将它们组合起来或许会更有意义。如果只是简单地观察分类器的错误率,那么我们就难以得到这种更深入的洞察效果了。
2. 最佳的分类器应该尽可能地处于左上角,这就意味着分类器在假阳率很低的同时获得了很高的真阳率。
  • 曲线下的面积(AUC):对不同的ROC曲线进行比较的一个指标。(AUC给出的是分类器的平均性能值,当然它并不能完全代替对整条曲线的观察。一个完美分类器的AUC为1.0,而随机猜测的AUC则为0.5。)

代价敏感的学习:比如分类代价的计算公式为:TP*(-5)+FN*1+FP*50+TN*0(数值可变,调整代价策略)

处理非均衡问题的数据抽样方法:对分类器的训练数据进行改造
  • 欠抽样:删除样例
  • 过抽样:复制样例
  1. # -*- coding:utf8 -*-
  2. from numpy import *
  3. def loadSimpData():
  4. datMat = matrix([[1., 2.1],
  5. [2., 1.1],
  6. [1.3, 1.],
  7. [1., 1.],
  8. [2., 1.]])
  9. classLabels = [1.0, 1.0, -1.0, -1.0, 1.0]
  10. return datMat, classLabels
  11. # 基于单层决策树构建弱分类器
  12. # 测试是否有某个值小于或者大于我们正在测试的阈值
  13. def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
  14. retArray = ones((shape(dataMatrix)[0], 1))
  15. # 数组过滤,所有在阈值外的数据会分到类别-1,否则为+1
  16. if threshIneq == 'lt':
  17. retArray[dataMatrix[:, dimen] <= threshVal] = -1.0
  18. else:
  19. retArray[dataMatrix[:, dimen] > threshVal] = -1.0
  20. return retArray
  21. # 在一个加权数据集中循环,找到具有最低错误率的单层决策树,D为权重向量
  22. def buildStump(dataArr, classLabels, D):
  23. dataMatrix = mat(dataArr)
  24. labelMat = mat(classLabels).T
  25. m, n = shape(dataMatrix)
  26. numSteps = 10.0 # 用于在特征的所有可能值上进行遍历
  27. bestStump = {} # 存储给定权重向量D时所得到的最佳单层决策树的相关信息
  28. bestClasEst = mat(zeros((m, 1)))
  29. minError = inf # 初始化成正无穷大,之后用于寻找可能的最小错误率
  30. # 第一层for循环在数据集的所有特征上遍历
  31. for i in range(n):
  32. rangeMin = dataMatrix[:, i].min()
  33. rangeMax = dataMatrix[:, i].max()
  34. stepSize = (rangeMax - rangeMin) / numSteps
  35. # 第二层for循环,遍历不同等级的阈值
  36. for j in range(-1, int(numSteps) + 1):
  37. # 最后一个for循环则是在大于和小于之间切换不等式
  38. for inequal in ['lt', 'gt']:
  39. threshVal = (rangeMin + float(j) * stepSize)
  40. predictedVals = stumpClassify(dataMatrix, i, threshVal, inequal)
  41. errArr = mat(ones((m, 1)))
  42. errArr[predictedVals == labelMat] = 0
  43. weightedError = D.T * errArr
  44. # print "split: dim %d, thresh %.2f, thresh ineqal: %s, the weighted error is %.3f" % (i, threshVal, inequal, weightedError)
  45. if weightedError < minError:
  46. minError = weightedError
  47. bestClasEst = predictedVals.copy()
  48. bestStump['dim'] = i
  49. bestStump['thresh'] = threshVal
  50. bestStump['ineq'] = inequal
  51. return bestStump, minError, bestClasEst
  52. # 基于单层决策树的AdaBoost训练过程
  53. def adaBoostTrainDS(dataArr, classLabels, numIt=40):
  54. weakClassArr = []
  55. m = shape(dataArr)[0]
  56. # D是一个概率分布向量,因此其所有的元素之和为1.0
  57. # 后续的迭代中,算法会在增加错分数据的权重的同时,降低正确分类数据的权重
  58. D = mat(ones((m, 1)) / m)
  59. # 每个数据点的类别估计累计值
  60. aggClassEst = mat(zeros((m, 1)))
  61. # 循环运行numIt次或者直到训练错误率为0为止
  62. for i in range(numIt):
  63. bestStump, error, classEst = buildStump(dataArr, classLabels, D)
  64. # print "D:", D.T
  65. # max(error, 1e-16)用于确保在没有错误时不会发生除零溢出,double极限就是1e-16
  66. alpha = float(0.5 * log((1.0 - error) / max(error, 1e-16)))
  67. bestStump['alpha'] = alpha
  68. weakClassArr.append(bestStump)
  69. # print "classEst: ", classEst.T
  70. # 为下一次迭代计算*D*
  71. expon = multiply(-1 * alpha * mat(classLabels).T, classEst)
  72. D = multiply(D, exp(expon))
  73. D = D / D.sum()
  74. # 错误率累加计算
  75. aggClassEst += alpha * classEst
  76. # print "aggClassEst: ", aggClassEst.T
  77. # 调用sign()函数得到二值分类结果
  78. aggErrors = multiply(sign(aggClassEst) != mat(classLabels).T, ones((m, 1)))
  79. errorRate = aggErrors.sum() / m
  80. print "total error: ", errorRate, "\n"
  81. if errorRate == 0.0:
  82. break
  83. return weakClassArr, aggClassEst
  84. def adaClassify(datToClass, classifierArr):
  85. dataMatrix = mat(datToClass)
  86. m = shape(dataMatrix)[0]
  87. aggClassEst = mat(zeros((m, 1)))
  88. # 遍历classifierArr中的所有弱分类器
  89. for i in range(len(classifierArr)):
  90. classEst = stumpClassify(dataMatrix, classifierArr[i]['dim'], classifierArr[i]['thresh'], classifierArr[i]['ineq'])
  91. aggClassEst += classifierArr[i]['alpha'] * classEst
  92. # print aggClassEst
  93. return sign(aggClassEst)
  94. def loadDataSet(fileName):
  95. numFeat = len(open(fileName).readline().split('\t'))
  96. dataMat, labelMat = [], []
  97. fr = open(fileName)
  98. for line in fr.readlines():
  99. lineArr = []
  100. curLine = line.strip().split('\t')
  101. for i in range(numFeat - 1):
  102. lineArr.append(float(curLine[i]))
  103. dataMat.append(lineArr)
  104. labelMat.append(float(curLine[-1]))
  105. return dataMat, labelMat
  106. # ROC曲线的绘制及AUC计算函数
  107. def plotROC(predStrengths, classLabels):
  108. import matplotlib.pyplot as plt
  109. # 绘制光标的位置
  110. cur = (1.0, 1.0)
  111. # 用于计算AUC
  112. ySum = 0.0
  113. numPosClas = sum(array(classLabels) == 1.0)
  114. yStep = 1 / float(numPosClas)
  115. xStep = 1 / float(len(classLabels) - numPosClas)
  116. # 获取排好序的索引
  117. sortedIndicies = predStrengths.argsort()
  118. # 以下3行构建画笔
  119. fig = plt.figure()
  120. fig.clf()
  121. ax = plt.subplot(111)
  122. for index in sortedIndicies.tolist()[0]:
  123. if classLabels[index] == 1.0:
  124. delX = 0
  125. delY = yStep
  126. else:
  127. delX = xStep
  128. delY = 0
  129. ySum += cur[1]
  130. ax.plot([cur[0], cur[0] - delX], [cur[1], cur[1] - delY], c='b')
  131. cur = (cur[0] - delX, cur[1] - delY)
  132. ax.plot([0, 1], [0, 1], 'b--')
  133. plt.xlabel('False Positive Rate')
  134. plt.ylabel('True Positive Rate')
  135. plt.title('ROC curve for AdaBoost Test')
  136. ax.axis([0, 1, 0, 1])
  137. plt.show()
  138. print "the AUC is: ", ySum * xStep





posted @ 2016-05-13 15:16  woaielf  阅读(346)  评论(0编辑  收藏  举报