一、AdaBoost元算法簡介
元算法(也叫集成算法)是對其他算法進行組合的一種方式。Boosting是通過集中關注被已有分類器錯分的那些數據來獲得新的分類器,並且Boosting分類器中的權重並不相等,每個權重代表的是其對應分類器在上一輪迭代中的成功度。Boosting有多個版本,博客討論其中最流行的版本AdaBoosting。
二、訓練算法:基於錯誤提升分類器的性能
AdaBoosting(adaptive boosting)的運行過程如下:
1、訓練數據集中的每個樣本,並賦予其中一個權重,這些權重構成一個向量,一開始,這些權重都初始化成相等的值。
2、首先在訓練數據集訓練處一個弱分類器,並計算該分類器的錯誤率
3、然後在同一數據集上再次訓練弱分類器。在第二次訓練分類器中,將會重新調整每個樣本的權重,其中第一次分類正確樣本的權重會降低,第一次分類錯誤的樣本的權重會提高。
爲了從所有弱分類器中得到最終的分類結果,AdaBoosting爲每個分類器都分配了一個權重值alpha,這些權重alpha的值都是基於每個弱分類器的錯誤率進行計算的。其中錯誤率的定義爲:,而alpha的計算公式爲:
AdaBoost的流程圖如下所示:
計算出alphas的值之後就可以對權重向量進行更新,以使得那些正確分類的樣本的權重降低,而那些錯分的樣本的權重升高。D的計算方法如下:
如果某個樣本被正確分類,那麼該樣本的權重更改爲:
如果某個樣本被錯分,那麼該樣本的權重更改爲:
在計算出D之後,AdaBoost又開始進行下一輪迭代。AdaBoost算法會不斷地重複訓練和調整權重過程,知道訓練出錯誤率爲0或者弱分類器的數目達到用戶指定的值爲止。
三、AdaBoost訓練過程代碼
"""
機器學習-AdaBoost算法
姓名:pcb
日期:2019.1.2
"""
from numpy import *
import matplotlib.pyplot as plt
#創建一個簡單的數據集
def loadSimpData():
datMat=matrix([[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=ones((shape(dataMatrix)[0],1)) #將數組中的元素全部設爲1
if threshIneq=='lt':
retArray[dataMatrix[:,dimen]<=threshVal]=-1.0 #將所有不滿足不等式要求的的元素設置爲-1
else:
retArray[dataMatrix[:,dimen]>threshVal]=-1.0
return retArray
"""
程序的僞代碼:
將最小的錯誤率minError設爲+無窮大
對數據集中的每一個特徵(第一層循環):
對每個步長(第二層循環):
對每個不等號(第三層循環):
建立一棵單層決策樹,並利用加權數據集對它進行測試
如果錯誤率低於minError,則將當前單層決策樹設爲最佳單層決策樹
返回最佳單層決策樹
"""
#將遍歷stumpClassify函數中所有可能的輸入值,找到數據集上最佳的單層決策樹
def buildStump(dataArr,classLabels,D):
dataMatrix=mat(dataArr);labelMat=mat(classLabels).T
m,n=shape(dataMatrix)
numSteps=10.0 #用於在特徵所有可能值上遍歷
bestStump={} #空字典,用於存儲給定權重的向量D時所得到最佳單層
bestClasEst=mat(zeros((m,1))) #
minError=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']: #最後一個循環在在大於和小於之間切換不等式
thresVal=(rangeMin+float(j)*stepSize)
predictedVal=stumpClassify(dataMatrix,i,thresVal,inequal)
errArr=mat(ones((m,1))) #構建一個列向量,判斷
errArr[predictedVal==labelMat]=0
weightedError=D.T*errArr
#print('split:dim %d,thresh %.2f,thresh ineqal:%s,the weighted error is %.3f'
#%(i,thresVal,inequal,weightedError))
if weightedError<minError:
minError=weightedError
bestClasEst=predictedVal.copy()
bestStump['dim']=i
bestStump['thresh']=thresVal
bestStump['ineq']=inequal
return bestStump,minError,bestClasEst
#基於單層決策樹的AdaBoost訓練過程
def adaBoostTrianDS(dataArr,classLabels,numIt=40):
weakClassArr=[]
m=shape(dataArr)[0]
D=mat(ones((m,1))/m) #D是一個概率分佈向量,所有元素之和是1.0
aggClassEst=mat(zeros((m,1))) #記錄每個數據點的類別估計累計值
for i in range(numIt):
bestStump,error,classEst=buildStump(dataArr,classLabels,D) #返回則是利用D而得到的具有最小錯誤率的單層決策樹
#print("D:",D.T)
alpha=float(0.5*log((1.0-error)/max(error,1e-16)))
bestStump['alpha']=alpha
weakClassArr.append(bestStump)
#print("classEst:",classEst.T)
expon=multiply(-1*alpha*mat(classLabels).T,classEst)
D=multiply(D,exp(expon))
D=D/D.sum()
aggClassEst+=alpha*classEst
#print("aggClassEst:",aggClassEst.T)
aggErrors=multiply(sign(aggClassEst)!=mat(classLabels).T,ones((m,1)))
errorRate=aggErrors.sum()/m
print("total error:",errorRate,"\n")
if errorRate==0.0:
break
return weakClassArr,aggClassEst
#基於adaBoost分類算法的測試函數
def adaclassify(datToClass,classifierArr):
dataMatrix=mat(datToClass)
m=shape(dataMatrix)[0]
aggClassEst=mat(zeros((m,1)))
for i in range(len(classifierArr)):
classEst=stumpClassify(dataMatrix,classifierArr[i]['dim'],classifierArr[i]['thresh'],classifierArr[i]['ineq'])
aggClassEst+=classifierArr[i]['alpha']*classEst
print(aggClassEst)
return 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
#1.ROC(接收者操作特徵,receiver operating characteristic)曲線的繪製
#2.AUC(曲線下的面積,Area Unser the Curve,AUC)計算函數
#3.分類器需要提供每個樣例被判爲陽性或者陰性的可信程度值
def plotROC(predStrengths,classLabels):
cur=(1.0,1.0) #構建一個浮點數的二維元組,該元組保留的是繪製光標的位置
ySum=0.0 #用於計算AUC的值
numPosClas=sum(array(classLabels)==1.0) #通過數組過濾方式計算正例的數目,並將該值賦給numPosClas
yStep=1/float(numPosClas) #確定座標軸上的步長
xStep=1/float(len(classLabels)-numPosClas)
sortedIndicies=predStrengths.argsort() #對分類器預測的強度進行排序(從小到大)得到排序的索引,
#構建畫筆
fig=plt.figure()
fig.clf()
ax=plt.subplot(111)
for index in sortedIndicies.tolist()[0]: #調用tolist,產生一個表進行迭代循環
if classLabels[index]==1.0: #每得到一個標籤爲1.0的類,則沿着y軸的方向下降一個步長,即不斷降低真陽率
delX=0
delY=yStep
else:
delX=xStep #對於其他類別則需要在x軸上倒退一個步長
delY=0
ySum+=cur[1] #所有的高度和(ySum)隨着x軸的每次移動而增加,計算總的AUC=ySum*xStep
#一旦決定了在X軸或者Y軸方向進行移動,則可以在當前點和新點之間畫一條線段,然後跟新當前點
ax.plot([cur[0],cur[0]-delX],[cur[1],cur[1]-delY],c='r')
cur=(cur[0]-delX,cur[1]-delY)
ax.plot([0,1],[0,1],'b--')
plt.xlabel('False Positive Rate')
plt.ylabel('True positive Rate')
ax.axis([0,1,0,1])
plt.show()
print('the Area Under the curve is :',ySum*xStep)
def main():
# #1.-----------測試算法------------------------------------------
# D=mat(ones((5,1))/5)
# datMat,classLabels=loadSimpData()
# #buildStump(datMat,classLabels,D)
# classifierArr=adaBoostTrianDS(datMat,classLabels,9)
# classifierResult=adaclassify([[0,0],[5,5]],classifierArr)
# print(classifierResult)
# #2.----------在大的數據集上使用AdaBoost算法-----------------------
# dataArr,labelArr=loadDataSet('horseColicTraining2.txt')
# classifierArry,aggClassEst=adaBoostTrianDS(dataArr,labelArr,50)
# testArr,testLabelArr=loadDataSet('horseColicTest2.txt')
# prediction10=adaclassify(testArr,classifierArry)
# errArr=mat(ones((67,1)))
# errCount=errArr[prediction10!=mat(testLabelArr).T].sum()
# print(errCount/67)
#3.繪製ROC曲線以及AUC計算函數
dataArr, labelArr = loadDataSet('horseColicTraining2.txt')
classifierArry,aggClassEst=adaBoostTrianDS(dataArr,labelArr,10)
plotROC(aggClassEst.T,labelArr)
if __name__=='__main__':
main()
在一個複雜數據集上應用AdaBoost分類結果:
基於AdaBoost馬疝病檢測系統的ROC曲線
打包代碼: