使用集成方法时会有多种形式:
- 可以是不同算法的集成,
- 也可以是同一算法在不同设置下的集成,
- 还可以是数据集不同部分分配给不同分类器之后的集成。
AdaBoost
优点:泛化错误率低,易编码,可以应用在大部分分类器上,无参数调整。
缺点:对
离群点敏感。
适用数据类型:数值型和标称型数据。
自举汇聚法(
bootstrap aggregating),也称为
bagging方法:数据集通常被认为是
放回取样得到的,比如要得到一个大小为n的新数据集,该数据集中的每个样本都是在原始数据集中随机抽样(即抽样之后又放回)得到的。
(分类器权重是相等的)
boosting:与bagging类似的集成分类器方法。不同的分类器是通过串行训练而获得的,每个新分类器都根据已训练出的分类器的性能来进行训练。
(分类器权重并不相等,每个权重代表的是其对应分类器在上一轮迭代中的成功度。)
AdaBoost:boosting方法拥有多个版本,
AdaBoost是
其中一个最流行的版本。
AdaBoost的一般流程
收集数据:可以使用任意方法。
准备数据:依赖于所使用的弱分类器类型,本章使用的是
单层决策树,这种分类器可以处理任何数据类型。当然也可以使用
任意分类器作为弱分类器,第2章到第6章中的任一分类器都可以充当弱分类器。作为弱分类器,
简单分类器的效果更好。(“弱”意味着分类器的性能比随机猜测要略好,但是也不会好太多。)
分析数据:可以使用任意方法。
训练算法:AdaBoost的大部分时间都用在训练上,分类器将多次在同一数据集上训练弱分类器。
测试算法:计算分类的错误率。
使用算法:同SVM一样,AdaBoost预测两个类别中的一个。如果想把它应用到多个类别的场合,那么就要像多类SVM中的做法一样对AdaBoost进行修改。
AdaBoost运行过程:
- 训练数据中的每个样本,并赋予其一个权重,这些权重构成了向量D。一开始,这些权重都初始化成相等值。
- 首先在训练数据上训练出一个弱分类器并计算该分类器的错误率,然后在同一数据集上再次训练弱分类器。
- 在分类器的第二次训练当中,将会重新调整每个样本的权重,其中第一次分对的样本的权重将会降低,而第一次分错的样本的权重将会提高。(为了从所有弱分类器中得到最终的分类结果,AdaBoost为每个分类器都分配了一个权重值(alpha),这些alpha值是基于每个弱分类器的错误率(ε)进行计算的。)
- 计算出alpha值之后,可以对权重向量D进行更新,以使得那些正确分类的样本的权重降低而错分样本的权重升高。
- 在计算出D之后,AdaBoost又开始进入下一轮迭代。AdaBoost算法会不断地重复训练和调整权重的过程,直到训练错误率为0或者弱分类器的数目达到用户的指定值为止。
单层决策树:仅基于单个特征来做决策,这棵树只有一次分裂过程。
基于单层决策树构建弱分类器伪代码:
将最小错误率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))的变化情况。
2. 最佳的分类器应该尽可能地处于
左上角,这就意味着分类器在假阳率很低的同时获得了很高的真阳率。
- 曲线下的面积(AUC):对不同的ROC曲线进行比较的一个指标。(AUC给出的是分类器的平均性能值,当然它并不能完全代替对整条曲线的观察。一个完美分类器的AUC为1.0,而随机猜测的AUC则为0.5。)
代价敏感的学习:比如分类代价的计算公式为:TP*(-5)+FN*1+FP*50+TN*0(数值可变,调整代价策略)
处理非均衡问题的
数据抽样方法:对分类器的训练数据进行改造
- 欠抽样:删除样例
- 过抽样:复制样例
# -*- coding:utf8 -*-
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):
retArray = ones((shape(dataMatrix)[0], 1))
# 数组过滤,所有在阈值外的数据会分到类别-1,否则为+1
if threshIneq == 'lt':
retArray[dataMatrix[:, dimen] <= threshVal] = -1.0
else:
retArray[dataMatrix[:, dimen] > threshVal] = -1.0
return retArray
# 在一个加权数据集中循环,找到具有最低错误率的单层决策树,D为权重向量
def buildStump(dataArr, classLabels, D):
dataMatrix = mat(dataArr)
labelMat = mat(classLabels).T
m, n = shape(dataMatrix)
numSteps = 10.0 # 用于在特征的所有可能值上进行遍历
bestStump = {} # 存储给定权重向量D时所得到的最佳单层决策树的相关信息
bestClasEst = mat(zeros((m, 1)))
minError = inf # 初始化成正无穷大,之后用于寻找可能的最小错误率
# 第一层for循环在数据集的所有特征上遍历
for i in range(n):
rangeMin = dataMatrix[:, i].min()
rangeMax = dataMatrix[:, i].max()
stepSize = (rangeMax - rangeMin) / numSteps
# 第二层for循环,遍历不同等级的阈值
for j in range(-1, int(numSteps) + 1):
# 最后一个for循环则是在大于和小于之间切换不等式
for inequal in ['lt', 'gt']:
threshVal = (rangeMin + float(j) * stepSize)
predictedVals = stumpClassify(dataMatrix, i, threshVal, inequal)
errArr = mat(ones((m, 1)))
errArr[predictedVals == labelMat] = 0
weightedError = 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 = weightedError
bestClasEst = predictedVals.copy()
bestStump['dim'] = i
bestStump['thresh'] = threshVal
bestStump['ineq'] = inequal
return bestStump, minError, bestClasEst
# 基于单层决策树的AdaBoost训练过程
def adaBoostTrainDS(dataArr, classLabels, numIt=40):
weakClassArr = []
m = shape(dataArr)[0]
# D是一个概率分布向量,因此其所有的元素之和为1.0
# 后续的迭代中,算法会在增加错分数据的权重的同时,降低正确分类数据的权重
D = mat(ones((m, 1)) / m)
# 每个数据点的类别估计累计值
aggClassEst = mat(zeros((m, 1)))
# 循环运行numIt次或者直到训练错误率为0为止
for i in range(numIt):
bestStump, error, classEst = buildStump(dataArr, classLabels, D)
# print "D:", D.T
# max(error, 1e-16)用于确保在没有错误时不会发生除零溢出,double极限就是1e-16
alpha = float(0.5 * log((1.0 - error) / max(error, 1e-16)))
bestStump['alpha'] = alpha
weakClassArr.append(bestStump)
# print "classEst: ", classEst.T
# 为下一次迭代计算*D*
expon = multiply(-1 * alpha * mat(classLabels).T, classEst)
D = multiply(D, exp(expon))
D = D / D.sum()
# 错误率累加计算
aggClassEst += alpha * classEst
# print "aggClassEst: ", aggClassEst.T
# 调用sign()函数得到二值分类结果
aggErrors = multiply(sign(aggClassEst) != mat(classLabels).T, ones((m, 1)))
errorRate = aggErrors.sum() / m
print "total error: ", errorRate, "\n"
if errorRate == 0.0:
break
return weakClassArr, aggClassEst
def adaClassify(datToClass, classifierArr):
dataMatrix = mat(datToClass)
m = shape(dataMatrix)[0]
aggClassEst = mat(zeros((m, 1)))
# 遍历classifierArr中的所有弱分类器
for i in range(len(classifierArr)):
classEst = stumpClassify(dataMatrix, classifierArr[i]['dim'], classifierArr[i]['thresh'], classifierArr[i]['ineq'])
aggClassEst += classifierArr[i]['alpha'] * classEst
# print aggClassEst
return sign(aggClassEst)
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
# ROC曲线的绘制及AUC计算函数
def plotROC(predStrengths, classLabels):
import matplotlib.pyplot as plt
# 绘制光标的位置
cur = (1.0, 1.0)
# 用于计算AUC
ySum = 0.0
numPosClas = sum(array(classLabels) == 1.0)
yStep = 1 / float(numPosClas)
xStep = 1 / float(len(classLabels) - numPosClas)
# 获取排好序的索引
sortedIndicies = predStrengths.argsort()
# 以下3行构建画笔
fig = plt.figure()
fig.clf()
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]
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 Test')
ax.axis([0, 1, 0, 1])
plt.show()
print "the AUC is: ", ySum * xStep