Adaboost-Python代码实践

基於单层决策树的Adaboost实践

Adaboost 计算流程:

  1. 首先给定数据集,基于弱学习器计算分类误差:
    在这里插入图片描述
  2. 计算该学习器的权重:

在这里插入图片描述

  1. 更新权重分布,分类错误的样本会获得更高的权重,从而使得其在下一次迭代时受到更多的关注

在这里插入图片描述

  1. 将所有的基学习器进行累加,合成最终的学习器:

在这里插入图片描述

  • 流程图:
    在这里插入图片描述
  • 基学习器为单层决策树
import numpy as np

def loadData():
    dataMat = np.array([[0,1,3],[0,3,1],[1,2,2],[1,1,3],[1,2,3],[0,1,2],[1,1,2],[1,1,1],[1,3,1],[0,2,1]])
    dataMat = np.reshape(dataMat,(-1,3))
    labelMat = np.array([-1,-1,-1,-1,-1,-1,1,1,-1,-1])
    return dataMat,labelMat

def stumpClassify(dataMat,dimen,threshold,threInfo):
	"""根据阀值进行分类
	Params:
		dataMat: 数据集
		dimen: 维度
		threshold: 阀值
		threInfo: less than or greater than 因为要考虑两种情况(将小于阀值的样本归为1类还是归为-1类)
	return:
		retArr:根据要求的分类结果"""
    m,n = dataMat.shape
    retArr = np.ones((m,1)) #初始化分类
    if threInfo == 'lt': #将小于阀值的样本归为-1类
        retArr[dataMat[:,dimen]<=threshold] = -1.0
    else: #将小于阀值的样本归为1类
        retArr[dataMat[:,dimen]>threshold] = -1.0
    return retArr

def buildStump(dataMat,labelMat,D):
	"""建立单层决策树
	Params:
		dataMat:数据集
		labelMat:标签集
		D:权重分布
	return:
		Minerror:最小误差值
		bestSump:存放阀值、维度、分类方向的字典
		bestClasEst:最小误差的分类"""
    StepNum = 10.0 #有10个样本首先将步数设置为10
    m,n = dataMat.shape
    bestStump = {} #初始化一个字典
    bestClasEst = np.zeros((m,1)) #初试化分类
    Minerror = float('inf') #将Minerror设置为无限大
    for i in range(n): #每个维度都要遍历
        rangeMin = dataMat[:,i].min()
        rangeMax = dataMat[:,i].max()
        StepSize = (rangeMax-rangeMin)/(StepNum) #计算步长
        for j in range(-1,int(StepNum)+1): #一个一个间隔
            for inequal in ['lt','gt']: #计算lt、gt
                threshold = (rangeMin+float(j)*StepSize)
                retArr = stumpClassify(dataMat,i,threshold,inequal) #进行分类
                retArr = np.mat(retArr)
                errArr = np.mat(np.ones((m,1))) #初始化误差
                errArr[retArr == labelMat] = 0 #将正确的变为0
                weightError = D.T * errArr #计算误差值
                if weightError < Minerror: #获取最小误差的信息
                    Minerror = weightError
                    bestClasEst = retArr.copy()
                    bestStump['threshold'] = threshold
                    bestStump['dim']= i
                    bestStump['ineq'] = inequal
    return Minerror,bestStump,bestClasEst
  • adaboost训练器
import numpy as np

def loadData():
    dataMat = np.array([[0,1,3],[0,3,1],[1,2,2],[1,1,3],[1,2,3],[0,1,2],[1,1,2],[1,1,1],[1,3,1],[0,2,1]])
    dataMat = np.reshape(dataMat,(-1,3))
    labelMat = np.array([-1,-1,-1,-1,-1,-1,1,1,-1,-1])
    return dataMat,labelMat

def stumpClassify(dataMat,dimen,threshold,threInfo):
	"""根据阀值进行分类
	Params:
		dataMat: 数据集
		dimen: 维度
		threshold: 阀值
		threInfo: less than or greater than 因为要考虑两种情况(将小于阀值的样本归为1类还是归为-1类)
	return:
		retArr:根据要求的分类结果"""
    m,n = dataMat.shape
    retArr = np.ones((m,1)) #初始化分类
    if threInfo == 'lt': #将小于阀值的样本归为-1类
        retArr[dataMat[:,dimen]<=threshold] = -1.0
    else: #将小于阀值的样本归为1类
        retArr[dataMat[:,dimen]>threshold] = -1.0
    return retArr

def buildStump(dataMat,labelMat,D):
	"""建立单层决策树
	Params:
		dataMat:数据集
		labelMat:标签集
		D:权重分布
	return:
		Minerror:最小误差值
		bestSump:存放阀值、维度、分类方向的字典
		bestClasEst:最小误差的分类"""
    StepNum = 10.0 #有10个样本首先将步数设置为10
    m,n = dataMat.shape
    bestStump = {} #初始化一个字典
    bestClasEst = np.zeros((m,1)) #初试化分类
    Minerror = float('inf') #将Minerror设置为无限大
    for i in range(n): #每个维度都要遍历
        rangeMin = dataMat[:,i].min()
        rangeMax = dataMat[:,i].max()
        StepSize = (rangeMax-rangeMin)/(StepNum) #计算步长
        for j in range(-1,int(StepNum)+1): #一个一个间隔
            for inequal in ['lt','gt']: #计算lt、gt
                threshold = (rangeMin+float(j)*StepSize)
                retArr = stumpClassify(dataMat,i,threshold,inequal) #进行分类
                retArr = np.mat(retArr)
                errArr = np.mat(np.ones((m,1))) #初始化误差
                errArr[retArr == labelMat] = 0 #将正确的变为0
                weightError = D.T * errArr #计算误差值
                if weightError < Minerror: #获取最小误差的信息
                    Minerror = weightError
                    bestClasEst = retArr.copy()
                    bestStump['threshold'] = threshold
                    bestStump['dim']= i
                    bestStump['ineq'] = inequal
    return Minerror,bestStump,bestClasEst

def Adaboost(dataMat,labelMat,Iter_num=40):
    m,n = dataMat.shape
    D = np.mat(np.ones((m,1)))/m #初始化权重分布
    labelMat = np.mat(labelMat).T
    aggClass = np.mat(np.zeros((m,1)))
    weakClass = [] #用来存放弱学习器的列表
    for i in range(Iter_num): #进入迭代
        error,Stump,Class = buildStump(dataMat,labelMat,D) 
        weakClass.append(Stump)
        alpha = float((np.log((1-error)/max(error,1e-16)))/2) #根据公式求alpha
        Stump['alpha'] = alpha
        Class = np.mat(Class)
        expon = - (np.multiply((alpha*labelMat),Class)) 
        D = np.multiply(D , np.exp(expon))/(2*(np.sqrt(error*(1-error)))) #根据公式更新权值
        
        aggClass+= alpha*Class #累加获得最终学习器
        aggF = np.sign(aggClass) #进行分类
        Error = np.zeros(m) 
        Error[np.ravel(aggF) != np.ravel(labelMat)]=1
        ErrorRate = float(Error.sum()/m) #计算误差
        if ErrorRate == 0.0: #如果误差为0即停止计算
            break
    return aggClass,Stump,weakClass

def AdaboostClassifier(dataMat,weakClass):
    dataMat = np.reshape(dataMat,(-1,3))
    m = dataMat.shape[0]
    aggclass = np.zeros((m,1))
    for i in range(len(weakClass)):
        Class = stumpClassify(dataMat,weakClass[i]['dim'],weakClass[i]['threshold'],weakClass[i]['ineq'])
        aggclass += Class*weakClass[i]['alpha']
    aggF = np.sign(aggclass)
    return aggF
if __name__ == '__main__':
    dataMat,labelMat = loadData()
    aggClass,Stump,weakClass = Adaboost(dataMat,labelMat,Iter_num=40)
    Test = np.array([[0,1,1],[1,3,3]])
    aggF = AdaboostClassifier(Test,weakClass)
    print(aggF)

结果如下:
这两个样本的分类为-1类。
在这里插入图片描述

SK-learn实现:

  • SK-learn的AdaBoostClassifier有五个参数
    在这里插入图片描述

  • **base_estimator:**可选参数,默认为DecisionTreeClassifier。理论上可以选择任何一个分类或者回归学习器,不过需要支持样本权重。我们常用的一般是CART决策树或者神经网络MLP。默认是决策树,即AdaBoostClassifier默认使用CART分类树DecisionTreeClassifier,而AdaBoostRegressor默认使用CART回归树DecisionTreeRegressor。另外有一个要注意的点是,如果我们选择的AdaBoostClassifier算法是SAMME.R,则我们的弱分类学习器还需要支持概率预测,也就是在scikit-learn中弱分类学习器对应的预测方法除了predict还需要有predict_proba。

  • **algorithm:**可选参数,默认为SAMME.R。scikit-learn实现了两种Adaboost分类算法,SAMME和SAMME.R。两者的主要区别是弱学习器权重的度量,SAMME使用对样本集分类效果作为弱学习器权重,而SAMME.R使用了对样本集分类的预测概率大小来作为弱学习器权重。由于SAMME.R使用了概率度量的连续值,迭代一般比SAMME快,因此AdaBoostClassifier的默认算法algorithm的值也是SAMME.R。我们一般使用默认的SAMME.R就够了,但是要注意的是使用了SAMME.R, 则弱分类学习器参数base_estimator必须限制使用支持概率预测的分类器。SAMME算法则没有这个限制。

  • **n_estimators:**整数型,可选参数,默认为50。弱学习器的最大迭代次数,或者说最大的弱学习器的个数。一般来说n_estimators太小,容易欠拟合,n_estimators太大,又容易过拟合,一般选择一个适中的数值。默认是50。在实际调参的过程中,我们常常将n_estimators和下面介绍的参数learning_rate一起考虑。

  • **learning_rate:**浮点型,可选参数,默认为1.0。每个弱学习器的权重缩减系数,取值范围为0到1,对于同样的训练集拟合效果,较小的v意味着我们需要更多的弱学习器的迭代次数。通常我们用步长和迭代最大次数一起来决定算法的拟合效果。所以这两个参数n_estimators和learning_rate要一起调参。一般来说,可以从一个小一点的v开始调参,默认是1。

  • **random_state:**整数型,可选参数,默认为None。如果RandomState的实例,random_state是随机数生成器; 如果None,则随机数生成器是由np.random使用的RandomState实例。

  • 代码实战:

import os 
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
os.chdir('E:/Data/机器学习数据集/Machine-Learning-master/AdaBoost/')
def LoadDataSet(file_name):
    dataMat = []
    labelMat = []
    with open(file_name) as f:
        for each in f.readlines():
            line = []
            clean = each.strip().split('\t')
            num_Feature = len(clean)
            for i in range(num_Feature-1):
                line.append(float(clean[i]))
            dataMat.append(line)
            labelMat.append(float(clean[-1]))
    return dataMat,labelMat

if __name__ == '__main__':
    Test_set,Test_label = LoadDataSet('horseColicTest2.txt')
    Train_set,Train_label = LoadDataSet('horseColicTraining2.txt')
    bdt = AdaBoostClassifier(DecisionTreeClassifier(max_depth=2),algorithm='SAMME',n_estimators=10)
    bdt.fit(Train_set,Train_label)
    prediction =bdt.predict(Train_set)
    error = np.mat(np.ones((len(Train_set),1)))
    print('训练集错误率为:{}'.format(float(error[prediction!=Train_label].sum()/len(Train_set)*100)))
    
    prediction =bdt.predict(Test_set)
    error = np.mat(np.ones((len(Test_set),1)))
    print('测试集错误率为:{}'.format(float(error[prediction!=Test_label].sum()/len(Test_set)*100)))

结果如下:

在这里插入图片描述
参考资料:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章