提升方法(Adaboost算法实现)
一、提升方法
提升方法基于这样一种思想:对于一个复杂任务来说,将来多个专家的判断进行适当的综合所得出的判断,要比其中任何一个专家单独的判断好,实际就是“三个臭皮匠顶个诸葛亮”的道理。(统计学习方法/李航)
对于提升方法来说其实就是在解决两个问题的过程:
Q1、如何改变训练数据权值或概率分布?
Q2、如何将弱分类器组合成一个强分类器?
对于这两个问题的回答要根据使用的具体算法才能回答。因为,本文采用的是Adaboost算法,所以就结合该算法回答这两个问题吧。
A1、Adaboost的做法是,提高那些被前一轮分类器错误分类样本的权值,而降低那些被正确分类样本的权值。这样一来,那些没有得到正确分类的数据,由于其权值得加大而受到后一轮的弱分类器的更大关注。于是,分类问题被一系列的弱分类器“分而治之”。
A2、对于弱分类器的组合,Adaboost采取加权多数表决的方法。具体地,加大分类误差率小的弱分类的权值,使其在表决中起较大的作用,减小分类误差率大的弱分类器的权值,使其在表决中起较小的作用。
二、Adaboost算法理论
(1)初始化训练数据的权值分布
(1.1)
先初始化一下分类样本数据的权值,初始值设置为平均值。N为特征值的总个数。因为,还有的限制要求。
(2)对m = 1,2,....,M
(a)使用具有全值分布Dm的训练数据集学习,使得基本分类器
(1.2)
G(x)是弱分类器,不同的分类器具有不同的G(x)形式.
(b)计算Gm(x)在训练数据集上的分类误差率
(1.3)
(c)计算Gm(x)的系数
(1.4)
其中,系数对应着弱分类器的权值。
(d)更新训练数据集的权值分布
(1.5)
其中,Zm称为规范因子。
(3)构建基本分类器的线性组合
(1.6)
其中G(x)是最终的分类器。
三、Adaboost算法代码实现
整个实现的伪代码如下:
对于每次迭代:
利用buildStump()函数找到最佳的单层决策树
将最佳单层决策树加入到单层决策树数组
计算alpha
计算新的权重向量D
更新累计类别估计值
如果错误率等于0.0,则退出循环
当看《Machine Learning in action》时候,发现其中有一个处理Dm更容易理解的公式,不过理论还是一样的。
D的计算方法如下:
如果某个样本被正确分类,那么该样本的权重更改为:
(1.7)
如果某个样本被错分,那么样本的权重更改为:
(1.8)
这里的e表示是2.171826....我记得是吧。哈哈 ~这里是二分类问题,所以可以这样处理。而(1.5)的公式是无论是二分类还是多分类问题都可以使用的公式。
提别提示:
因为,本文才采取单层决策树实现算法的。所以,其中的分类函数根据单层决策树的特点写的。采用不同的弱函数会有不同的分类函数,一定要灵活运用!
(1)基于单层决策函数的Adaboost分类函数
#特别说明:
# 根据不同的弱分类器,则分类函数也会有不同的写法!
#@param dataMatrix: 表示输入的数据;type:mat
#@param dimen: 表示第几个特征值
#@param threshVal: 要分类的数值大小(阀值)
#@param threshIneq: 判断是大于号还是小于号
def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
retArray = ones((shape(dataMatrix)[0], 1) #初始化
if threshIneq == 'lt': #判断是大于号还是小于号
retArray[dataMatrix[:, dimen] <= threshVal] = -1.0 #这是mat数值类型的特殊功能,不是返回bool型。而是满足条件的数据
else:
retArray[dataMatrix[:, dimen] > threshVal] = -1.0
return retArray
(2)单程决策树生成函数
#@param dataArr: 输入数据集
#@param classLabels: 输入数据集的类标签
#@param D: 样本权值
#@return: 建立成功的决策树
def buildStump(dataArr, classLabels, D):
dataMatrix = mat(dataArr); labelMat = mat(classLabels).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(); rangMax = dataMatrix[: ,i].max()
stepSize = (rangMax - rangeMin) / numSteps #从前一个索引到下一个索引的跨度
for j in range(-1, int(numSteps) + 1):
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
if weightedError < minError:
minError = weightedError
bestClasEst = predictedVals.copy() #最优决策树对应的类标签
bestStump['dim'] = i
bestStump['thresh'] = threshVal
bestStump['ineq'] = inequal
return bestStump, minError, bestClasEst
(3)基于单层决策树的Adaboost训练过程
#@param numIt:程序最大循环次数
#return:弱分类器数组
def adaBoostTrainDS(dataArr, classLabels, numIt = 40):
weakClassArr = [] #弱基函数存储数组
m = shape(dataArr)[0]
D = mat(ones((m, 1))) #样本权值
aggClassEst = mat(zeros((m, 1)))
for i in range(numIt):
bestStump, error, classEst = buildStump(dataArr, classLabels, D) #classEst就是G(x)
print "D: ", D.T
alpha = float(0.5 * log((1.0-error) / max(error, 1e-16)) #防止除0!公式(1.4)
bestStump['alpha'] = alpha
weakClassArr.append(bestStump)
print "classEst: ", classEst
expon = multiply(-1*alpha*mat(classLabels).T, classEst) #exp(*)的求解
D = multiply(D, exp(expon)) #(1.7)(1.8)重新计算D
D = D / D.sum()
aggClassEst += alpha * classEst #f(x) (1.6)
print "aggClassEst: ", aggClassEst
aggErrors = multiply(sign(aggClassEst) != mat(classLabels).T, ones((m, 1))) #em (1.3)
errorRate = aggErrors.sum() / m
print "total error: ", errorRate, "\n"
if errorRate == 0.0: break;
return weakClassArr
(4)AdaBoost分类函数
#@param dataToClass: 一个或多个待分类样例
#@param clasifierArr:多个弱分类器组成的数组
def adaClassify(dataToClass, classifierArr):
dataMatrix - mat(dataToClass)
m = shape(dataMatrix)[0]
aggClassEst = mat(zeros((m, 1))) #G(x)
for i in range(len(classifierArr)):
classEst = stumpClassify(dataMatrix, classifierArr[i]['dim'], classifierArr[i]['thresh'],classifierArr[i]['ineq']) #Gm(x)
aggClassEst += classifierArr[i]['alpha'] * classEst #f(x) ,G(x)
print aggClassEst
return sign(aggClassEst)