Adaboost底層程式碼(python)——非常詳細
阿新 • • 發佈:2018-12-16
參考《機器學習實戰》,程式碼可執行
#!/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]的類別
以上,祝好!