1. 程式人生 > >Adaboost底層程式碼(python)——非常詳細

Adaboost底層程式碼(python)——非常詳細

參考《機器學習實戰》,程式碼可執行

#!/user/bin/python3
# Author: HuangCong
# -*- coding:utf-8 -*-
import numpy as np

#建立簡單資料集
def loadSimpData():
    datMat = np.mat([[1., 2.1],
                     [2., 1.1],
                     [1.3, 1.],
                     [1., 1.],
                     [2., 1.]])
    classLabel =
[1.0, 1.0, -1.0, -1.0, 1.0] return datMat, classLabel #通過閾值比較對資料進行分類——(特徵矩陣、維度、閾值、閾值不等號) def stumpClassify(dataMatrix, dimen, threshVal, threshIneq): retArray = np.ones((dataMatrix.shape[0], 1)) #建立列向量[m, 1]——與標籤列相對應 if threshIneq == 'lt': retArray[dataMatrix[:, dimen] <= threshVal]
= -1 #在該dimen維度的特徵值小於等於閾值時,取-1 else: retArray[dataMatrix[:, dimen] > threshVal] = -1 return retArray #該函式會遍歷stumpClassify()函式所有可能的輸入值,並找到該資料集上的最佳單層樹——根據資料權重向量D來定義 def buildStump(dataArr, classLabels, D): dataMatrix = np.mat(dataArr); labelMat = np.mat(classLabels).T #classLabel向量為[1,n],需要轉置
m, n = dataMatrix.shape #m個樣本,n個特徵 numSteps = 10.0 #用於在特徵的所有可能值上進行遍歷 bestStump = {} #該詞典儲存最佳單層決策樹的相應引數 bestClasEst = np.mat(np.zeros((m, 1))) #儲存最佳估計標籤值,先初始化[m,1]零向量 minError = np.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']: #在大於和小於之間切換不等式 threshVal = (rangeMin + float(j) * stepSize) #確定閾值 predictdVals = stumpClassify(dataMatrix, i, threshVal, inequal) #進行預測 errArr = np.mat(np.ones((m, 1))) #錯誤矩陣,如果predictedVals值不等於labelMat中真正類別值,置為一 errArr[predictdVals == labelMat] = 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 = predictdVals.copy() #儲存預測值 bestStump['dim'] = i #儲存維度 bestStump['thresh'] = threshVal #儲存閾值 bestStump['ineq'] = inequal #儲存不等號 return bestStump, minError, bestClasEst #基於單層決策樹的AdaBoost的訓練過程 (資料集、類別標籤、迭代次數),尾部DS代表(decision stump單層決策樹) def adaBoostTrainDS(dataArr, classLabels, numIt=40): #迭代次數是演算法中唯一需要使用者指定的引數 weakClassArr = [] #聚焦該分類器的所有資訊,最後返回 m = dataArr.shape[0] #樣本數為m D = np.mat(np.ones((m, 1)) / m) #樣本權重初始化,都相等,後續迭代中會增加錯分資料的權重同時,降低正確分類資料的權重 aggClassEst = np.mat(np.zeros((m, 1))) #記錄每個資料點的類別估計累計值 for i in range(numIt): #numIt次迭代 bestStump, error, classEst = buildStump(dataArr, classLabels, D) #上一行返回利用D得到的具有最小錯誤率的單層決策樹,同時返回最小錯誤率和估計的類別向量 print("D:", D.T) #下一行alpha的計算公式可詳見李航藍本,max()函式以防發生除零錯誤 alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16))) bestStump['alpha'] = alpha #繼續存入該字典——包括了分類所需要的所有資訊 weakClassArr.append(bestStump) #儲存資訊到列表中 print("classEst: ", classEst.T) #列印類別估計值 #以下三行用於計算下一次迭代中的新的資料權重向量D,公式可見李航藍本 expon = np.multiply(-1 * alpha * np.mat(classLabels).T, classEst) D = np.multiply(D, np.exp(expon)) D = D/D.sum() #以下四行用於錯誤率累加的計算,通過aggClassEst變數保持一個執行時的類別估計值來實現 aggClassEst += alpha * classEst print("aggClassEst: ", aggClassEst) #由於aggClassEst是浮點數,需要呼叫sign()函式 aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(classLabels).T, np.ones((m, 1))) errorRate = aggErrors.sum() / m print("errorRate: ", errorRate) if errorRate == 0.0: #如果錯誤率為0,停止for迴圈 break return weakClassArr #返回資訊列表 #基於adaboost進行分類——(待分類樣例,多個弱分類器組成的陣列) def adaClassify(dataToClass, classifierArr): dataMatrix = np.mat(dataToClass) #首先轉成numpy矩陣 m = dataMatrix.shape[0] #待分類樣例的個數為m aggClassEst = np.mat(np.zeros((m, 1))) #構建0列向量,與adaBoostTrainDS中含義一樣 for i in range(len(classifierArr)): #遍歷所有的弱分類器 #基於stumpClassify()對每個分類器得到一個類別的估計值 classEst = stumpClassify(dataMatrix, classifierArr[i]['dim'], classifierArr[i]['thresh'], classifierArr[i]['ineq']) aggClassEst += classifierArr[i]['alpha']*classEst print(aggClassEst) return np.sign(aggClassEst) # #定義自適應載入函式(很有用) # def loadDataSet(fileName): # numFeat = len(open(fileName).readline().split('\t')) # dataMat = [] # labelMat = [] # fr = open(fileName) # for line in fr.readlines(): # lineArr = [] # curLine = line.strip().split('\t') # for i in range(numFeat-1): # lineArr.append(float(curLine[i])) # dataMat.append(lineArr) # labelMat.append(float(curLine[-1])) # return dataMat, labelMat if __name__ == "__main__": dataMat, classLabels = loadSimpData() classifierArr = adaBoostTrainDS(dataMat, classLabels, 30) print(adaClassify([0, 0], classifierArr)) #估計資料點[0,0]的類別 print(adaClassify([[5, 5], [0, 0]], classifierArr)) #估計資料點[5,5],[0,0]的類別

以上,祝好!