Python机器学习Adaboost算法

Adaboost算法简介

AdaBoost是典型的Boosting算法,属于Boosting家族的一员。Boosting算法是将“弱学习算法“提升为“强学习算法”的过程,主要思想是“三个臭皮匠顶个诸葛亮”。一般来说,找到弱学习算法要相对容易一些,然后通过反复学习得到一系列弱分类器,组合这些弱分类器得到一个强分类器。Boosting算法要涉及到两个部分,加法模型和前向分步算法。加法模型就是说强分类器由一系列弱分类器线性相加而成。

Adaboost原理推导

AdaBoost,是英文”Adaptive Boosting”(自适应增强)的缩写,由Yoav Freund和Robert Schapire在1995年提出。它的自适应在于:前一个基本分类器分错的样本会得到加强,加权后的全体样本再次被用来训练下一个基本分类器。同时,在每一轮中加入一个新的弱分类器,直到达到某个预定的足够小的错误率或达到预先指定的最大迭代次数。
AdaBoost的工作原理如图:
在这里插入图片描述

(一)初始化训练数据的权值分布

如果有N个样本,则每一个训练样本最开始时都被赋予相同的权值: 1 / N 1/N 。(初始化的时权值相等)
假设数据集为D,各个样本权值为 w 1 i w_{1i} ,
D 1 = ( w 11 + w 12 + + w 1 N ) , w 1 i = 1 / N D_1=(w_{11}+w_{12}+\ldots+w_{1N}),w_{1i}=1/N
其中i= 1,2,3,4,5……N,1表示第一次迭代(也可以说第0次,算初始化)

(二)进行多轮迭代,训练弱分类器

训练过程中,如果某个样本点已经被准确地分类,那么在构造下一个训练集中,它的权值就被降低;相反,如果某个样本点没有被准确地分类,那么它的权值就得到提高。然后,权值更新过的样本集被用于训练下一个分类器,整个训练过程如此迭代地进行下去。
首先,计算 G m ( x ) G_m(x) 在训练数据集上的分类误差率:

G m ( x ) : f ( 1 , 1 ) l a b e l 1 1 G_m(x):f(1,-1)(二分类,对应label是1与-1)
e m = P ( G m ( x ) y i ) = i = 1 N w m i I ( G m ( x ) y i ) e_m=P(G_m(x)\neq y_i)=\sum_{i=1}^N w_{mi}\cdot I(G_m(x)\neq y_i)
上述公式就是计算分类后的误差率 e m e_m =错分概率⋅权值

a l p h a = 1 2 log 1 e m e m alpha=\frac 1 2\log\frac{1-e_m}{e_m}
然后计算alpha的值,由上述式子可知, e m < = 1 / 2 e_m <= 1/2 时, a l p h a > = 0 alpha >= 0 ,且 a l p h a alpha 随着 e m e_m 的减小而增大,意味着分类误差率越小的基本分类器在最终分类器中的作用越大。然后根据 a l p h a alpha 来更新权值:
D m + 1 = ( w m + 1 , 1 + w m + 1 , 2 + + w m + 1 , N ) , w 1 i = 1 / N D_{m+1}=(w_{m+1,1}+w_{m+1,2}+\ldots+w_{m+1,N}),w_{1i}=1/N
w m + 1 , i = w m i Z m e x p ( a l p h a y i G m ( x i ) ) , i = 1 , 2 N w_{m+1,i}=\frac{w_{mi}}{Z_m}exp(-alpha\cdot y_i\cdot G_m(x_i)),i=1,2\ldots N
Z m = i = 1 N w m , i e x p ( a l p h a y i G m ( x i ) ) Z_m=\sum_{i=1}^Nw_{m,i}exp(-alpha\cdot y_i\cdot G_m(x_i))
y i y_i 为标签(-1,1), Z m Z_m 为规范化因子, y i y_i 为标签(-1,1), Z m Z_m 为规范化因子,使得 D m + 1 D_{m+1} 成为一个概率分布。上面的公式使得被基本分类器 G m ( x ) G_m(x) 误分类样本的权值增大,而被正确分类样本的权值减小。就这样,通过这样的方式,AdaBoost方法能“重点关注”或“聚焦于”那些较难分的样本上。

(三)将各个训练得到的弱分类器组合成强分类器

f ( x ) = m = 1 m a m G m ( x ) f(x)=\sum_{m=1}^ma_mG_m(x)
从而得到最终分类器:
G ( x ) = s i g n ( f ( x ) ) = s i g n [ m = 1 m a m G m ( x ) ] G(x)=sign(f(x))=sign[\sum_{m=1}^ma_mG_m(x)]

Adaboost代码实现

首先我们需要创建一个单层决策数分类器:

from numpy import *
#单侧决策树分类器
def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):
	"""
	次函数的功能就是:将dataMatrix的第dimen个维度的数据与threshVal进行比较,对	dataMatrix进行分类,
	将大于threshVal的分成一类,
	将小于threshVal的 分成另外一类。
	:param dataMatrix: 数据集
	:param dimen:     分类依据的维度
	:param threshVal: :分类依据的阈值
	:param threshIneq: 分类的方法:it:将大于阈值的置为1,小于阈值的置为-1  ge:将小于阈值的置为1,大于阈值的置为-1
	"""
	retArray=ones((shape(dataMatrix)[0],1))#用来保存分类结果的矩阵

	if threshIneq=='It':
    	retArray[dataMatrix[:,dimen]<=threshVal]=-1.0#如果维度dimen中数据小于threshVal,就将retArray的对应值置为-1
	else:
    	retArray[dataMatrix[:,dimen]>threshVal]=-1.0
	return retArray  #返回的是标签的预测值


#选择最佳的单侧决策树分类器
def buildStump(dataArr,classLabels,D):
	dataMatrix=mat(dataArr)  #数据集
	labelMat=mat(classLabels).T  #标签

	m,n=shape(dataMatrix) #m为样本数据数量,n为样本数据维度。
	numSteps=10.0 #阈值迭代的步数
	bestStump={}#保存最佳分类器
	bestClasEst=mat(zeros((m,1)))#储存数据的预测值
	minError=inf

	for i in range(n):#对数据集中的每一个特征循环
    	rangeMin=dataMatrix[:,i].min()#取得第i个维上的最小值
    	rangeMax=dataMatrix[:,i].max()#取得第i个维度上的最大值

    	stepSize=(rangeMax-rangeMin)/numSteps

    	for j in range(-1,int(numSteps)+1):#对每个步长循环
        	for inequal in ['It','gt']:
            	threshVal=(rangeMin+float(j)*stepSize) #分类的阈值
            	predictedVals=stumpClassify(dataMatrix,i,threshVal,inequal)#对第i个维度上的数据进行阈值分类。
            	errArr=mat(ones((m,1))) #错误矩阵
            	errArr[predictedVals==labelMat]=0#对于m个数据预测正确值0,错误置为1

            	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

上面有两个函数,这两个函数的功能就是构建一个最佳的决策树分类器。为下面的AdaBoost分类器打下基础。

接下来我们来测试一下上面的两个函数:

创建数据集:

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

函数返回5个数据集。

dataMat,classLabels=loadSimpData()
D=mat(ones((5,1))/5)
stump,error,clasest= buildStump(dataMat,classLabels,D)	
print("最佳分类器stump:",stump)
print("错误率error:",error)
print("预测值clasest:",clasest)

运行结果:

最佳分类器stump: {'dim': 0, 'thresh': 1.3, 'ineq': 'It'}
错误率error: [[0.2]]
预测值clasest: [[-1.]
				[ 1.]
				[-1.]
				[-1.]
				[ 1.]]

可以发现我们训练出来的单层决策树分类器为stump,错误率为0.2,我们的真实值为[1.0, 1.0, -1.0, -1.0, 1.0],预测值的第一个数据错误。

下面我们将会才会AdaBoost来增强这个分类器:

每次迭代:

  1. 利用 buildStump()函数找到最佳单层决策树。

  2. 将最佳单层决策树加入到单层决策树数组。

  3. 计算alpha

  4. 计算新的权重向量D

  5. 更新累计类别估计值

  6. 如果错误率等于0.0 退出循环

    def adaBoostTrainDS(dataArr,classLabels,numIt=40):
    	weakClassArr=[]#保存基本分类器
    	m=shape(dataArr)[0]#m为数据的样本数
    	D=mat(ones((m,1))/m)#样本的权值矩阵
    	aggClassEst=mat(zeros((m,1)))
    
    	for i in range(numIt):#迭代numIt次
    		bestStump,error,classEst=buildStump(dataArr,classLabels,D)#找到最佳决策树
    		print('D',D.T)
    		alpha=float(0.5*log((1.0-error)/max(error,1e-16)))#根据错误率求出alpha
    		print('alpha:',alpha)
    
    		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)
    		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,aggClassEst #返回分类器列表和最终的预测值矩阵
    

这个函数的功能就是叠加多个单层分类器来实现决策。
验证代码为:

dataMat,classLabels=loadSimpData()
weakClassArr,aggClassEst=adaBoostTrainDS(dataMat,classLabels,9)
print('分类器:',weakClassArr,'\n','预测值:',sign(aggClassEst))      #预测值aggClassEst大于0返回+1,小于0返回-1

运行结果:

D [[0.2 0.2 0.2 0.2 0.2]]
alpha: 0.6931471805599453
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]]
alpha: 0.9729550745276565
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       ]]
alpha: 0.8958797346140273
classEst: [[1. 1. 1. 1. 1.]]
aggClassEst: [[ 1.17568763]
 [ 2.56198199]
 [-0.77022252]
 [-0.77022252]
 [ 0.61607184]]
total error 0.0
分类器: [{'dim': 0, 'thresh': 1.3, 'ineq': 'It', 'alpha': 0.6931471805599453}, {'dim': 1, 'thresh': 1.0, 'ineq': 'It', 'alpha': 0.9729550745276565}, {'dim': 0, 'thresh': 0.9, 'ineq': 'It', 'alpha': 0.8958797346140273}] 
预测值: [[ 1.]
[ 1.]
[-1.]
[-1.]
[ 1.]]

由结果可知,最终错误率为0,而预测值与样本标志真实值相同!

发布了23 篇原创文章 · 获赞 0 · 访问量 626

猜你喜欢

转载自blog.csdn.net/qq_43824915/article/details/103438906