元算法(meta-algorithm)是对其他算法进行组合的一种方式
AdaBoost优点:泛化错误率低,易编码,可以用用在大部分分类器上,无参数调整
缺点:对离群点敏感
适用数据类型:数值型和标称型数据
基于数据集多重抽样的分类器
前面已经介绍了五种不同的分类算法,它们各有优缺点。自然可以将不同的分类器组合起来,而这种组合结果则被称为集成方法(ensemble method)或者元算法(meta-algorithm)。使用集成方法时会有多种形式:可以是不同算法的集成,也可以是同一算法在不同设置下的集成,还可以是数据集不同部分分配给不同分类器之后的集成。
bagging:基于数据随机重抽样的分类器构建方法
自举汇聚发法(bootstrap aggregating),也称为bagging方法,是在从原始数据集选择S次后得到S个新数据集的一种技术。新数据集和原数据集的大小相等。每个数据集都是通过在原始数据集中随机选择一个样本来进行替换而得到的。这里的替换就意味着可以多次地选择同一样本。这一性质就允许新数据集中可以有重复的值,而原始数据集的某些值在新集合中则不再出现。
在S个数据集建好之后,将某个学习算法分别作用于每个数据集就得到了S个分类器。当要对新数据进行分类时,就可以应用这S个分类器进行分类。与此同时,选择分类器投票结果中最多的类别作为最后的分类结果。
还有一些更先进的bagging方法,比如随机森林(random forest)。
boosting
boosting是一种与bagging很类似的技术。不论是在boosting还是bagging当中,所使用的多个分类器的类型都是一致的。但是在前者当中,不同的分类器是通过串行训练而获得的,每个新分类器都根据已训练出的分类器的性能来进行训练。boosting是通过集中关注被已有分类器错分的那些数据来获得新的分类器。
由于boosting分类的结果是基于所有分类器的加权求和结果的,因此boosting和bagging不太一样。bagging中的分类器权重是相等的,而boosting中的分类器权重并不相等,每个权重代表的是其对应分类器在上一轮迭代中的成功度。
boosting方法拥有多个版本,本节只关注其中的Adaboost。
AdaBoost的一般流程:
1. 收集数据:可以使用任意方法
2. 准备数据:依赖于所使用的的弱分类器类型,本节使用的是单层决策树,这种分类器可以处理任何数据类型。当然也可以使用任意分类器作为弱分类器,之前提到的任一分类器都可以充当弱分类器。作为弱分类器,简单分类器的效果更好。
3. 分析数据:可以使用任意方法
4. 训练算法:AdaBoost的大部分时间都用在训练上,分类器将多次在同一数据集上训练弱分类器
5. 测试算法:计算分类的错误率
6. 使用算法:同SVM一样,AdaBoost预测两个类别中的一个。如果想把它应用到多个类别的场合,那么就要像多类SVM中的做法一样对AdaBoost进行修改。
- 训练算法:基于错误提升分类器的性能
AdaBoost是adaptive boosting(自适应boosting)的缩写,其运行过程如下:训练数据中的每个样本,并赋予其一个权重,这些权重构成了向量D。一开始,这些权重都初始化成相等值。首先在训练数据上训练出一个弱分类器并计算该分类器的错误率,然后在同一数据集上再次训练弱分类器。在分类器的第二次训练当中,将会重新调整每个样本的权重,其中第一次分对的样本的权重将会降低,而第一次分错的样本的权重将会提高。为了从所有弱分类器中得到最终的分类结果,AdaBoost为每个分类器都分配了一个权重值alpha,这些alpha值是基于每个弱分类器的错误率进行计算的。其中,错误率
而alpha的计算公式如下:
计算出
如果某个样本被正确分类,那么该样本的权重更改为:
而如果某个样本被错分,那么该样本的权重更改为:
在计算出D之后,AdaBoost又开始进入下一轮迭代。AdaBoost算法会不断地重复训练和调整权重的过程,直到训练错误率为0或者弱分类器的数目达到用户的指定值为止。
- 基于单层决策树构建弱分类器
单层决策树(decision stump, 也称决策树桩)
是一种简单的决策树,它仅基于单个特征来做决策。由于这棵树只有一次分裂过程,因此它实际上就是一个树桩。
# 数据集
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
datMat, classLabels = loadSimpData()
![accdd383660b3f1b51e60579cc13aa17.png](https://i-blog.csdnimg.cn/blog_migrate/b44173d06dae21e6a16de61975543531.jpeg)
# 单层决策树生成函数
# 伪代码
"""
将最小错误率minError设为+♾
对数据集中的每一个特征(第一层循环):
对每个步长(第二层循环):
对每个不等号(第三层循环):
建立一棵单层决策树并利用加权数据集对它进行测试
如果错误率低于minError,则将当前单层决策树设为最佳单层决策树
返回最佳单层决策树
"""
# stumpClassify通过阈值比较对数据进行分类,所有在阈值一边的数据会分到类别-1,而在另外一边的数据分到类别+1
def stumpClassify(dataMatrix, dimen, threshVal, threshIneq): # just classify the data
retArray = ones((shape(dataMatrix)[0], 1))
if threshIneq == 'lt':
retArray[dataMatrix[:, dimen] <= threshVal] = -1.0
else:
retArray[dataMatrix[:, dimen] > threshVal] = -1.0
return retArray
# buildStump将会遍历stumpClassify()函数所有可能输入值,并找到数据集上最佳的单层决策树
# 这里的"最佳"是基于数据的权重向量D来定义的
def buildStump(dataArr, classLabels, D):
dataMatrix = mat(dataArr)
labelMat = mat(classLabels).T
m, n = shape(dataMatrix)
# 用于在特征的所有可能值上进行遍历
numSteps = 10.0
# 用于存储给定权重向量D时所得到的最佳单层决策树的相关信息
bestStump = {}
bestClasEst = mat(zeros((m, 1)))
# init error sum, to +infinity
# 初始化为正无穷大,用于寻找可能的最小错误率
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):
# go over less than and greater than
# 第三层循环
for inequal in ['lt', 'gt']:
threshVal = (rangeMin + float(j) * stepSize)
# call stump classify with i, j, lessThan
predictedVals = stumpClassify(dataMatrix, i, threshVal, inequal)
errArr = mat(ones((m, 1)))
errArr[predictedVals == labelMat] = 0
# 计算加权错误率
# calc total error multiplied by D
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
D=mat(ones((5,1))/5)
buildStump(datMat, classLabels, D)
"""
({'dim': 0, 'thresh': 1.3, 'ineq': 'lt'}, matrix([[0.2]]), array([[-1.],
[ 1.],
[-1.],
[-1.],
[ 1.]]))
"""
- 完整AdaBoost算法的实现
# 基于单层决策树的AdaBoost训练过程
# 伪代码
"""
对每次迭代:
利用buildStump()函数找到最佳的单层决策树
将最佳单层决策树加入到单层决策树数组
计算alpha
计算新的权重向量D
更新累积类别估计值
如果错误率等于0.0,则退出循环
"""
# AdaBoost算法的输入参数包括数据集、类别标签以及迭代次数numInt
# 其中numInt是在整个AdaBoost算法中唯一需要用户指定的参数
# 函数名称尾部的DS代表的就是单层决策树,它是AdaBoost中最流行的弱分类器,当然并非唯一可用的弱分类器
# adaBoostTrainDS是建立于单层决策树至上的,但是我们也可以很容易对此进行修改以引入其他基分类器
# 向量D包含了每个数据点的权重,一开始,这些权重都赋予了相等的值,在后续迭代中,AdaBoost算法会在增加错分数据的权重同时,降低正确分类数据的权重
# D是一个概率分布向量,因此其所有的元素之和为1.0
# 为了满足此要求,一开始所有元素都会被初始化成1/m
# 同时程序还会建立另一个列向量aggClassEst,记录每个数据点的类别估计累计值
def adaBoostTrainDS(dataArr, classLabels, numIt=40):
weakClassArr = []
m = shape(dataArr)[0]
# init D to all equal
D = mat(ones((m, 1)) / m)
aggClassEst = mat(zeros((m, 1)))
# 循环运行numInt次或者直到训练错误率为0为止
# 循环中的第一件事就是利用buildStump()函数建立一个单层决策树
# 该函数的输入为权重向量D,返回的则是利用D而得到的具有最小错误率的单层决策树,同时返回的还有最小的错误率以及估计的类别向量
for i in range(numIt):
# build Stump
bestStump, error, classEst = buildStump(dataArr, classLabels, D)
print("D:",D.T)
# calc alpha, throw in max(error,eps) to account for error=0
# 计算alpha值,该值会告诉总分类器本次单层决策树输出结果的权重
# 其中的语句max(error,1e-16)用于确保在没有错误时不会发生除零溢出
alpha = float(0.5 * log((1.0 - error) / max(error, 1e-16)))
# 将alpha值加入到bestStump字典中,该字典又添加到列表中,该字典包括了分类所需要的所有信息
bestStump['alpha'] = alpha
# store Stump Params in Array
weakClassArr.append(bestStump)
print("classEst: ",classEst.T)
# exponent for D calc, getting messy
# 为下一次迭代计算新权重向量D
expon = multiply(-1 * alpha * mat(classLabels).T, classEst)
# Calc New D for next iteration
D = multiply(D, exp(expon))
D = D / D.sum()
# calc training error of all classifiers, if this is 0 quit for loop early (use break)
# 错误率累加计算
aggClassEst += alpha * classEst
print("aggClassEst: ",aggClassEst.T)
aggErrors = multiply(sign(aggClassEst) != mat(classLabels).T, ones((m, 1)))
errorRate = aggErrors.sum() / m
print("total error: ", errorRate)
if errorRate == 0.0:
break
return weakClassArr, aggClassEst
classifierArray = adaBoostTrainDS(datMat, classLabels, 9)
"""
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
D: [[0.5 0.125 0.125 0.125 0.125]]
classEst: [[ 1. 1. -1. -1. -1.]]
aggClassEst: [[ 0.27980789 1.66610226 -1.66610226 -1.66610226 -0.27980789]]
total error: 0.2
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
"""
classifierArray
"""
([{'dim': 0, 'thresh': 1.3, 'ineq': 'lt', 'alpha': 0.6931471805599453},
{'dim': 1, 'thresh': 1.0, 'ineq': 'lt', 'alpha': 0.9729550745276565},
{'dim': 0, 'thresh': 0.9, 'ineq': 'lt', 'alpha': 0.8958797346140273}],
matrix([[ 1.17568763],
[ 2.56198199],
[-0.77022252],
[-0.77022252],
[ 0.61607184]]))
"""
- 测试算法:基于AdaBoost的分类
def adaClassify(datToClass, classifierArr):
# do stuff similar to last aggClassEst in adaBoostTrainDS
dataMatrix = mat(datToClass)
m = shape(dataMatrix)[0]
aggClassEst = mat(zeros((m, 1)))
# call stump classify
for i in range(len(classifierArr)):
classEst = stumpClassify(dataMatrix, classifierArr[0][i]['dim'], classifierArr[0][i]['thresh'],classifierArr[0][i]['ineq'])
aggClassEst += classifierArr[0][i]['alpha'] * classEst
print(aggClassEst)
return sign(aggClassEst)
datArr, labelArr = loadSimpData()
classifierArr = adaBoostTrainDS(datArr, labelArr, 30)
adaClassify([[0,0], [5,5]], classifierArr)
"""
[[-0.69314718]
[ 0.69314718]]
[[-1.66610226]
[ 1.66610226]]
matrix([[-1.],
[ 1.]])
"""
示例:在一个难数据集上应用AdaBoost
在Logistic回归预测中马疝病数据集上应用AdaBoost分类器
- 收集数据:提供的文本文件
- 准备数据:确保类别标签是+1和-1而非1和0
# general function to parse tab -delimited floats
def loadDataSet(fileName):
# get number of fields
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
datArr, labelArr = loadDataSet('horseColicTraining2.txt')
- 分析数据:手工检查数据
- 训练算法:在数据上,利用adaBoostTrainDS()函数训练出一系列的分类器
classifierArray = adaBoostTrainDS(datArr,labelArr,10)
- 测试算法:有两个数据集,在不采用随机抽样的方法下,对AdaBoost和Logistic回归的结果进行完全对等的比较
testArr,testLabelArr = loadDataSet('horseColicTest2.txt')
prediction10 = adaClassify(testArr, classifierArray)
- 使用算法:观察该例子上的错误率
errArr = mat(ones((67, 1)))
errArr[prediction10 != mat(testLabelArr).T].sum()
# 18