adaboost詳解


集成學習方法

通過組合多個弱基分類器來實現強分類器目的,從而提高分類性能。集成學習是一類算法,並不是指一個算法。集成學習策略有非常多種,包括數據層面、模型層面和算法層面三個方面集成,這方面由於研究非常廣泛,論文非常多,可以去知網下載碩博論文,論文總結非常全面。常用的兩種集成學習方法是:bagging袋裝法,典型代表隨機森林(Random Forests)和boosting提升法,典型代表GBDT(Gradient Boosting Decision Tree)以及最近非常火熱的XGBOOST。

AdaBoost算法原理和優缺點(1995年的算法了)

優點:泛化錯誤率低,易編碼,可以應用在大部分分類器上,無參數調整

缺點:對離群點敏感(這一類都有這個缺點)

首先講一下boosting的流程

先從數據集D中隨機選出一些數據得到數據集D1,通過訓練得到分類器C1。

然后再從D中隨機選出一些數據,用C1來進行測試,會得到D2,此時的D2中會有一些數據是C1分對了的,有一些是C1分錯了的。然后用這些數據進行訓練,會得到C2,。

然后再從D中隨機選出一些數據,用C1、C2一起來測試,丟棄兩個分類器判斷相同的結果,對於某個數據如果兩個分類器的判斷結果不一樣則保留,這些保留下來的數據組成D3,然后對D3進行訓練得到C3分類器。

預測時,將三個分類器同時作用在數據上,對於C1與C2得到的結果如果一樣,就輸出O1,如果不一樣,則輸出C3的結果O3。

以上就是一個簡單的boosting算法

 由於AdaBoost是boosting的改進方法,而且性能比較穩定,故而在實際中一般都直接使用AdaBoost而不會使用原始的boosting。

adaboost流程:

給定一個樣本,例如標簽只有-1和1

初始化樣本權重,所有樣本權重都是一樣的

T為分類器的個數

         首先訓練第一個弱分類器,弱分類器輸出后會得到一個錯誤率,利用錯誤率來得到一個α,錯誤率越高,α越小,因為這個分類器比較垃圾,在最后算結果時都是α乘以分類器。垃圾點的分類器肯定權重就要小一點。

         根據錯誤率將樣本權重更新,對分錯的樣本權重要加大,分對的樣本權重要縮小,經過t輪之后就得到T個弱分類器。

最后將T個弱分類器乘以各自的權重α相加,經過激活函數的處理,得到結果。

構建單層決策樹代碼實現:

# -*-coding:utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt

def loadSimpData():
    """
    創建單層決策樹的數據集
    Parameters:
        無
    Returns:
        dataMat - 數據矩陣
        classLabels - 數據標簽
    """
    datMat = np.matrix([[1., 2.1],    #生成一個(5,2)的矩陣
                        [1.5, 1.6],
                        [1.3, 1.],
                        [1., 1.],
                        [2., 1.]])
    classLabels = [1.0, 1.0, -1.0, -1.0, 1.0]     #生成一個(1,5)的矩陣
    return datMat, classLabels


def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
    """
    單層決策樹分類函數
    Parameters:
        dataMatrix - 數據矩陣
        dimen - 第dimen列,也就是第幾個特征
        threshVal - 閾值
        threshIneq - 標志
    Returns:
        retArray - 分類結果
    """
    retArray = np.ones((np.shape(dataMatrix)[0], 1))  # np.shape(dataMatrix)得到(5,2)[0]為5,所以retArray.shape=(5,1),初始化retArray為1
    if threshIneq == 'lt': #因為每一個閾值有兩種判斷方式,小於閾值為-1或者大於閾值為-1
        retArray[dataMatrix[:, dimen] <= threshVal] = -1.0  # 如果小於閾值,則賦值為-1
    else:
        retArray[dataMatrix[:, dimen] > threshVal] = -1.0  # 如果大於閾值,則賦值為-1
    return retArray


def buildStump(dataArr, classLabels, D):
    """
    找到數據集上最佳的單層決策樹
    Parameters:
        dataArr - 數據矩陣
        classLabels - 數據標簽
        D - 樣本權重
    Returns:
        bestStump - 最佳單層決策樹信息
        minError - 最小誤差
        bestClasEst - 最佳的分類結果
    """
    dataMatrix = np.mat(dataArr);   #dataMatrix.shape=(5,2)
    labelMat = np.mat(classLabels).T   #labelMat.shape = (5,1)
    m, n = np.shape(dataMatrix)      #m=5,n=2
    numSteps = 10.0;
    bestStump = {};
    bestClasEst = np.mat(np.zeros((m, 1)))         #初始化為(5,1),值為0
    minError = float('inf')  # 最小誤差初始化為正無窮大
    for i in range(n):  # 遍歷所有特征
        rangeMin = dataMatrix[:, i].min();   #例如當i=0時,從第0列中取最小值
        rangeMax = dataMatrix[:, i].max()  # 找到特征中最小的值和最大值
        stepSize = (rangeMax - rangeMin) / numSteps  # 計算步長
        for j in range(-1, int(numSteps) + 1):   #對其中一個特征進行閾值划分
            for inequal in ['lt', 'gt']:  # 大於和小於的情況,均遍歷。lt:less than,gt:greater than
                threshVal = (rangeMin + float(j) * stepSize)  # 計算閾值,例如第一輪閾值為0.9=1+(-1)*0.1
                predictedVals = stumpClassify(dataMatrix, i, threshVal, inequal)  # 計算分類結果
                errArr = np.mat(np.ones((m, 1)))  # 初始化誤差矩陣
                errArr[predictedVals == labelMat] = 0  # 分類正確的,賦值為0
                weightedError = D.T * errArr  # 計算誤差,第一輪0.4=0.2*2
                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


if __name__ == '__main__':
    dataArr, classLabels = loadSimpData()
    D = np.mat(np.ones((5, 1)) / 5)
    bestStump, minError, bestClasEst = buildStump(dataArr, classLabels, D)
    print('bestStump:\n', bestStump)
    print('minError:\n', minError)
    print('bestClasEst:\n', bestClasEst)

 下面實例為簡單的adaboost實現過程:

# -*-coding:utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt

def loadSimpData():
    """
    創建單層決策樹的數據集
    Parameters:
        無
    Returns:
        dataMat - 數據矩陣
        classLabels - 數據標簽
    """
    datMat = np.matrix([[1., 2.1],
                        [1.5, 1.6],
                        [1.3, 1.],
                        [1., 1.],
                        [2., 1.]])
    classLabels = [1.0, 1.0, -1.0, -1.0, 1.0]
    return datMat, classLabels


def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
    """
    單層決策樹分類函數
    Parameters:
        dataMatrix - 數據矩陣
        dimen - 第dimen列,也就是第幾個特征
        threshVal - 閾值
        threshIneq - 標志
    Returns:
        retArray - 分類結果
    """
    retArray = np.ones((np.shape(dataMatrix)[0], 1))  # 初始化retArray為1
    if threshIneq == 'lt':
        retArray[dataMatrix[:, dimen] <= threshVal] = -1.0  # 如果小於閾值,則賦值為-1
    else:
        retArray[dataMatrix[:, dimen] > threshVal] = -1.0  # 如果大於閾值,則賦值為-1
    return retArray


def buildStump(dataArr, classLabels, D):
    """
    找到數據集上最佳的單層決策樹
    Parameters:
        dataArr - 數據矩陣
        classLabels - 數據標簽
        D - 樣本權重
    Returns:
        bestStump - 最佳單層決策樹信息
        minError - 最小誤差
        bestClasEst - 最佳的分類結果
    """
    dataMatrix = np.mat(dataArr);
    labelMat = np.mat(classLabels).T
    m, n = np.shape(dataMatrix)
    numSteps = 10.0;
    bestStump = {};
    bestClasEst = np.mat(np.zeros((m, 1)))
    minError = float('inf')  # 最小誤差初始化為正無窮大
    for i in range(n):  # 遍歷所有特征
        rangeMin = dataMatrix[:, i].min();
        rangeMax = dataMatrix[:, i].max()  # 找到特征中最小的值和最大值
        stepSize = (rangeMax - rangeMin) / numSteps  # 計算步長
        for j in range(-1, int(numSteps) + 1):
            for inequal in ['lt', 'gt']:  # 大於和小於的情況,均遍歷。lt:less than,gt:greater than
                threshVal = (rangeMin + float(j) * stepSize)  # 計算閾值
                predictedVals = stumpClassify(dataMatrix, i, threshVal, inequal)  # 計算分類結果
                errArr = np.mat(np.ones((m, 1)))  # 初始化誤差矩陣
                errArr[predictedVals == labelMat] = 0  # 分類正確的,賦值為0
                weightedError = D.T * errArr  # 計算誤差
                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


def adaBoostTrainDS(dataArr, classLabels, numIt=40):
    weakClassArr = []
    m = np.shape(dataArr)[0]
    D = np.mat(np.ones((m, 1)) / m)  # 初始化權重
    aggClassEst = np.mat(np.zeros((m, 1)))
    for i in range(numIt):  #迭代40次,也就是生成40個弱分類器,可以自己設置次數,但一般不超過50次
        bestStump, error, classEst = buildStump(dataArr, classLabels, D)  # 構建單層決策樹
        print("D:",D.T)
        alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16)))  # 計算弱學習算法權重alpha,使error不等於0,因為分母不能為0
        bestStump['alpha'] = alpha  # 存儲弱學習算法權重 bestStump是一個字典類型
        weakClassArr.append(bestStump)  # 存儲單層決策樹也就是弱分類器
        print("classEst: ", classEst.T) #打印分類誤差
        expon = np.multiply(-1 * alpha * np.mat(classLabels).T, classEst)  # 計算e的指數項
        D = np.multiply(D, np.exp(expon))
        D = D / D.sum()  # 根據樣本權重公式,更新樣本權重, 權重的歸一化,分類錯誤的樣本權重會加大,分類正確的樣本權重會減小
        # 計算AdaBoost誤差,當誤差為0的時候,退出循環
        aggClassEst += alpha * classEst  #計算類別估計累計值,這里包括已經訓練好的每一個弱分類器
        print("aggClassEst: ", aggClassEst.T)   #第一輪aggClassEst:  [[-0.69314718  0.69314718 -0.69314718 -0.69314718  0.69314718]]
        aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(classLabels).T, np.ones((m, 1)))  # 計算誤差 np.sign()函數時將大於0的數置為1,小於零的數置為-1
                             #也就是說np.sign(aggClassEst) = [-1,1,-1,-1,1].T,與np.mat = [1,1,-1,-1,1].T相比較,不相等的置為1
                             #np.sign(aggClassEst) != np.mat(classLabels).T得到的結果為[1,0,0,0,0].T
                             #aggErrors = [1,0,0,0,0].T
        errorRate = aggErrors.sum() / m   # sum函數會將所有元素加起來得到1,所以errorRate = 0.2,集成分類器分類錯誤率,如果錯誤率為0,則整個集成算法停止,訓練完成
        print("total error: ", errorRate)
        if errorRate == 0.0: break  # 誤差為0,退出循環
    return weakClassArr, aggClassEst


if __name__ == '__main__':
    dataArr, classLabels = loadSimpData()
    weakClassArr, aggClassEst = adaBoostTrainDS(dataArr, classLabels)
    print(weakClassArr)
    print(aggClassEst)

 此分類器訓練三個弱分類器即可使得誤差為零,如圖所示:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM