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)))

結果如下:

在這裏插入圖片描述
參考資料:

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