标签: 机器学习 Python
主要参考资料:
当一个分类器正确率不是那么的高时,我们称其为“弱分类器”,或者说该分类器的学习方法为“弱学习方法”。与之对应的,存在“强分类器”和“强学习方法”。强学习方法的正确率很高。
非常幸运,数学上有证明,强学习方法和弱学习方法是等价的,即一个方法是强学习方法的充要条件是该方法为弱学习方法。因此只要我们找到了一个弱学习方法,就可以把它转化为强学习方法。AdaBoost算法(Adaptive Boosting)就是把弱学习方法转化为强学习方法的典型算法,其英文直译为“自适应增强”,基础的AdaBoost算法用于处理二分类问题。
AdaBoost算法的大部分时间都花在训练上,分类器将多次在同一数据集上训练若干弱分类器。每个弱分类器都有一个系数,分类的时候,将弱分类器的分类结果乘以对应的系数然后求和,最后根据“和”决定强分类器的分类结果。
《统计学习方法》中有一句话对于AdaBoost算法的原理总结的很到位——“三个臭皮匠顶个诸葛亮”。
整篇博文以下表的中数据为例,构建一个强分类器。
例题来源于http://www.csie.edu.tw
序号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
x | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
y | 1 | 1 | 1 | -1 | -1 | -1 | 1 | 1 | 1 | -1 |
AdaBoost的训练数据集一般为 T={(x1,y1),(x2,y2),...,(xN,yN)} ,其中 xi 为 n 维特征变量, yi 为其对应的分类。
我们创建数据集的代码如下:
from numpy import *
#创建数据集和标签
def create_dataMat():
dataMat = mat([[0], [1], [2], [3], [4], [5], [6], [7], [8], [9]]) #x
labels = [1, 1, 1, -1, -1, -1, 1, 1, 1, -1] #y
return dataMat, labels
常见的机器学习算法都可以建立弱分类器,不过最经常使用的弱分类器是单层决策树。单层决策树又称为决策树桩(decision stump),即层数为1的决策树。
本文使用单层决策树作为弱分类器。每一个训练数据都有一个权值系数,注意不是弱分类器的系数。建立最佳单层决策树的依据就是:每个训练数据在单层决策树中的分类结果乘以自己的权值系数后相加的“和”最小,即分类误差最小化(详见下文)。
建立最佳决策树的方法其实很“笨”,就是遍历所有的决策树,然后选择“和”最小的决策树。其中有一点需要注意,就是决策树的阈值边界要在特征最小值和最大值之外,即需要检测把所有数据划分为一类的决策树。
建立单层决策树的输入为训练数据集,权值系数矢量,具体代码如下:
#使用单层决策树对数据进行分类
def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):
retArray = ones((shape(dataMatrix)[0],1))
if threshIneq == 'lt': #按照大于或小于关系分类
retArray[dataMatrix[:,dimen] <= threshVal] = -1.0 #所有第dimen维小于等于阈值的分类置为-1
else:
retArray[dataMatrix[:,dimen] > threshVal] = -1.0
return retArray
#建立最佳单层决策树,即弱分类器
def buildStump(dataMat,labels,D):
labelMat = mat(labels).T #转置为列向量
m,n = shape(dataMat)
numSteps = 10.0
bestStump = {} #存贮决策树
bestClasEst = mat(zeros((m,1))) #最佳单层决策树的结果,初始化为全部分类错误
minError = inf #最小错误,初始化为无穷大
for i in range(n): #遍历所有的特征
rangeMin = dataMat[:,i].min()
rangeMax = dataMat[:,i].max()
stepSize = (rangeMax-rangeMin)/numSteps #步长
for j in range(-1,int(numSteps)+1): #将阈值起始与终止设置在该特征取值的范围之外
for inequal in ['lt', 'gt']: #取less than和greater than
threshVal = (rangeMin + float(j) * stepSize)
predictedVals = stumpClassify(dataMat,i,threshVal,inequal) #使用单层决策树分类
errArr = mat(ones((m,1))) #1表示分类错误,0表示分类正确,初始化为全部错误
errArr[predictedVals == labelMat] = 0 #矢量比较,分类正确的置为1
weightedError = D.T*errArr #乘以系数D
#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算法的关键和难点是建立强分类器,而建立强分类器的关键和难点是计算每个训练数据权值系数和每个弱分类器系数。
建立强分类器的具体步骤如下:
(1) 初始化训练数据的权值系数
这里进行一些定性的说明
上述算法的具体代码如下:
#使用Adaboost建立强分类器,numIt表示最大迭代次数
def adaBoostTrainDS(dataMat,classLabels,numIt=40):
weakClassArr = []
m = shape(dataMat)[0]
D = mat(ones((m,1))/m) #初始化系数D
aggClassEst = mat(zeros((m,1)))
for i in range(numIt):
bestStump,error,classEst = buildStump(dataMat,classLabels,D) #建立最佳单层决策树
#print "D:",D.T
alpha = float(0.5*log((1.0-error)/max(error,1e-16)))#计算alpha, max(error,1e-16)防止下溢出
bestStump['alpha'] = alpha
weakClassArr.append(bestStump) #保存弱分类器
#print "classEst: ",classEst.T
expon = multiply(-1*alpha*mat(classLabels).T,classEst) #自然底数的指数,为了更新D
D = multiply(D,exp(expon)) #为下次迭代更新D
D = D/D.sum()
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 #如果分类错误率为0,结束分类
return weakClassArr
代码中的一些说明:
max(error,1e-16)
确保不会发生除0溢出sign()
函数返回自变量的符号,1
表示正号,-1
表示负号创建了强分类器之后,就可以使用了,其中的运算大多都为numpy中的矢量运算,简单方便。
使用每个弱分类器对每个待分类数据进行分类,然后将分类结果乘以各自的弱分类器系数后求和,当和大于0时判断为正类;当和小于0时判断为负类。
#使用Adaboost分类器对数据进行分类
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)
简单的测试一下我们创建的强分类器,正确率为1。
if __name__ == '__main__':
dataMat, labels = create_dataMat()
weakClassArr = adaBoostTrainDS(dataMat, labels)
for i in range(10):
res = adaClassify([i], weakClassArr)
print 'data: %d, class: %2d' % (i, res)
有一种方法是将不同的分类器组合起来使用,这种方法叫做集成方法或者元算法。本文所实现的AdaBoost算法就是典型的集成方法,AdaBoost算法可以把弱学习方法转化为强学习方法。
MachineLearningAction
仓库里面有常见的机器学习算法处理常见数据集的各种实例,希望能够得到你的STAR