svm那一章看的太吃力…先缓缓,回头再收拾它(
简介
基于同一分类器多个不同实例的两种计算方法:bagging与boosting
Bagging:从原始数据集选择S次后得到S个新数据集的一种技术,将某个学习算法串行作用于每个数据集就得到了S个分类器。对新数据进行分类时,用这S个分类器进行分类,并选择分类器投票结果中最多的类别作为最后的分类结果。
Boosting:关注被已有分类器错分的数据来获取新的分类器。分类结果为基于所有分类器的结果进行加权求和。
Adaboost是boosting方法中最为流行的一个版本。
AdaBoost优缺点:
优点:泛化错误率低,易编码,可以应用在大部分分类器上,无参数调整。
缺点:对离群点敏感。
适用数据类型:数值型和标称型数据。
例子中使用的分类器是单层决策树。
单层决策树:仅基于单个特征做分类,只有一次分裂过程(树桩)
数学基础
分类器错误率定义:
分类器每次迭代后的权重alpha计算:
分类器每次迭代后样本权重的更新:
如果某个样本被正确分类,那么该样本的权重更改为:
而如果某个样本被错分,那么该样本的权重更改为:
Adaboost算法流程图:
代码学习
首先先把作为分类器的单层决策树弄起来。
单层决策树伪代码:
将最小错误率minError设为+∞
对数据集中的每一个特征(第一层循环):
对每个步长(第二层循环):
对每个不等号(第三层循环):
建立一棵单层决策树并利用加权数据集对它进行测试
如果错误率低于minError,则将当前单层决策树设为最佳单层决策树
返回最佳单层决策树
单层决策树生成函数:
#分类函数,通过数组过滤实现
#传入的参数分别为:dataMatrix数据矩阵,dimen特征类型(维度),threshVal用于作为分类标准的阈值,由(特征值最小值+步长)获得,threshIneq不等号
def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):
retArray = ones((shape(dataMatrix)[0],1)) #用于记录dimen特征类型的分类结果
if threshIneq == 'lt': #若不等号为<
retArray[dataMatrix[:,dimen] <= threshVal] = -1.0 #将特征值小于threshVal的置为-1
else: #若不等号为>
retArray[dataMatrix[:,dimen] > threshVal] = -1.0 #将特征值大于threshVal的置为-1
return retArray
#遍历所有能传给stumpClassify()的数据组合,在其寻找最佳的单层决策树
#传入参数:dataArr数据样本,classLabels数据样本分类结果数组,D为初始权重向量
def buildStump(dataArr,classLabels,D):
#数组转为numpy矩阵
dataMatrix = mat(dataArr); labelMat = mat(classLabels).T #T转置
m,n = shape(dataMatrix)
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): #对每个步长循环,遍历特征所有可能值
for inequal in ['lt', 'gt']: #对每个不等号进行循环
threshVal = (rangeMin + float(j) * stepSize)
#以threshVal作为标准进行分类
predictedVals = stumpClassify(dataMatrix,i,threshVal,inequal)
errArr = mat(ones((m,1))) #记录错误情况的列向量
errArr[predictedVals == labelMat] = 0 #分类结果正确的记为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 #更新最好的作为分类标准的threshVal值
bestStump['ineq'] = inequal #更新最好的用于分类的不等号
return bestStump,minError,bestClasEst #返回带有最好单层决策树的字典、错误率,分类结果(类别估计值)
有了单层决策树后,我们就可以实现一个完整的adaboost算法了。
完整adaboost算法伪代码:
对每次迭代:
利用buildStump()函数找到最佳的单层决策树
将最佳单层决策树加入到单层决策树数组
计算alpha
计算新的权重向量D
更新累计类别估计值
如果错误率等于0.0,则退出循环
基于单层决策树的AdaBoost训练过程,DS代表单层决策树:
#输入参数:dataArr数据样本数组,classLabels样本分类结果数组,numIt迭代次数
def adaBoostTrainDS(dataArr,classLabels,numIt=40):
weakClassArr = [] #弱分类器的分类结果,是一个二维数组
m = shape(dataArr)[0]
D = mat(ones((m,1))/m) #初始权重向量矩阵
aggClassEst = mat(zeros((m,1))) #记录特征类别估计累计值
for i in range(numIt):
#获取最好的单层决策树(树桩)字典,最好的错误率,最好的分类结果
bestStump,error,classEst = buildStump(dataArr,classLabels,D)
#根据分类错误率计算分类器权重alpha
alpha = float(0.5*log((1.0-error)/max(error,1e-16)))
bestStump['alpha'] = alpha #字典中加入权重
weakClassArr.append(bestStump) #记录最好的分类结果
#(以下三行)更新样本权重D
expon = multiply(-1*alpha*mat(classLabels).T,classEst)
D = multiply(D,exp(expon))
D = D/D.sum()
aggClassEst += alpha*classEst #累计值
#记录每个特征错误情况的数组aggErrors,sign(n)获取二值分类结果,若n大于0返回1.0,若小于0返回-1.0,若等于0返回0.0
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
拥有了复数的分类器与对应的alpha值,我们就可以进行分类操作了。
AdaBoost分类函数:
#输入参数:datToClass待分类数据,classifierArr多个弱分类器组成的数组
def adaClassify(datToClass,classifierArr):
dataMatrix = mat(datToClass)
m = shape(dataMatrix)[0]
aggClassEst = mat(zeros((m,1)))
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)
总结
通常情况下,AdaBoost会得到一个稳定的测试错误率,而不会随着分类器数目增多而提高。但有时候也会发生过拟合现象。
“本章以单层决策树作为弱学习器构建了AdaBoost分类器。实际上,AdaBoost函数可以应用于任意分类器,只要该分类器能够处理加权数据即可。AdaBoost算法十分强大,它能够快速处理其他分类器很难处理的数据集。”