在機器學習實戰前幾章學習了不同的分類算法,自然會考慮到是否可以将不同的分類器組合起來進行分類。
在這裡,簡單解釋兩種組合的方法。
1,bagging(基于資料随機重抽樣的分類器建構)
從原始資料集中随機選擇S次,得到S個新資料集,新資料集與原始資料集大小相同(随機意味着可以重複選擇)。再用這S個資料集訓練S個分類器,用着S個分類器進行分類,選擇分類器投票數最多的類作為分類結果。
2,boosting
boosting是一種與bagging很類似的技術。但是boosting是通過集中關注已有分類器錯分的那些資料來擷取新的分類器。即增大被錯分資料的權值,然後再用改變權值後的資料訓練新的分類器。最後基于所有的分類器權重求和,這裡的權值不是資料的權值,而是每個分類器的權值,錯誤率小的分類器權值較大。
boosting方法版本較多,這裡隻讨論最流行的版本AdaBoost。
Adaboost的原理
Adaboost是一個有若幹弱分類器組合成的強分類器。
AdaBoost,是英文”Adaptive Boosting”(自适應增強)的縮寫,由Yoav Freund和Robert Schapire在1995年提出。它的自适應在于:前一個基本分類器分錯的樣本會得到加強,權重後的全體樣本再次被用來訓練下一個基本分類器。
AdaBoost是一種疊代算法,在每一輪中加入一個新的弱分類器,直到達到某個預定的足夠小的錯誤率。每一個訓練樣本都被賦予一個權重,表明它被某個分類器選入訓練集的機率。如果某個樣本點已經被準确地分類,那麼在構造下一個訓練集中,它被選中的機率就被降低;相反,如果某個樣本點沒有被準确地分類,那麼它的權重就得到提高。
在具體實作上,最初令每個樣本的權重都相等,對于第k次疊代操作,我們就根據這些權重來選取樣本點,進而訓練分類器。然後就根據這個分類器,來提高被它分錯的的樣本的權重,并降低被正确分類的樣本權重。然後,權重更新過的樣本集被用于訓練下一個分類器。整個訓練過程如此疊代地進行下去。
(1)AdaBoost算法流程
- 1,首先初始化樣本的權值
- 2,每一個分類器的形式如下: ,
- 3,通過計算a使得誤差率 最低,得到最低誤差。
- 4,此時分類器的權值為: 。em <= 1/2時,am >= 0,且am随着em的減小而增大,意味着分類誤差率越小的基本分類器在最終分類器中的作用越大。
- 5,更新樣本的權值: 使得被基本分類器Gm(x)誤分類樣本的權值增大,而被正确分類樣本的權值減小。就這樣,通過這樣的方式,AdaBoost方法能“聚焦于”那些較難分的樣本上。其中,Zm是規範化因子,使得Dm+1成為一個機率分布: 。
- 6,計算各個分類器的權重值: ,得到最終的類别:
-
如果得到的各分類器的權重值還不滿足要求,誤差還很高,繼續上述的3,4,5,6步。
(2)一個例子
下面,給定下列訓練樣本,請用AdaBoost算法學習一個強分類器。
求解過程:初始化訓練資料的權值分布,令每個權值W1i = 1/N = 0.1,其中,N = 10,i = 1,2, …, 10,然後分别對于m = 1,2,3, …等值進行疊代。
疊代過程1:對于m=1,在權值分布為D1的訓練資料上,門檻值v取2.5時誤差率最低,故基本分類器為:
進而可得G1(x)在訓練資料集上的誤差率e1=P(G1(xi)≠yi) = 0.3
然後計算G1的系數:
接着更新訓練資料的權值分布:
最後得到各個資料的權值分布D2=(0.0715, 0.0715, 0.0715, 0.0715, 0.0715, 0.0715, 0.1666, 0.1666, 0.1666, 0.0715),分類函數f1(x)=0.4236G1(x),故最終得到的分類器sign(f1(x))在訓練資料集上有3個誤分類點。
疊代過程2:對于m=2,在權值分布為D2的訓練資料上,門檻值v取8.5時誤差率最低,故基本分類器為:
G2(x)在訓練資料集上的誤差率e2=P(G2(xi)≠yi) = 0.2143
計算G2的系數:
更新訓練資料的權值分布:
D3=(0.0455, 0.0455, 0.0455, 0.1667, 0.1667, 0.01667, 0.1060, 0.1060, 0.1060, 0.0455)
f2(x)=0.4236G1(x) + 0.6496G2(x)
分類器sign(f2(x))在訓練資料集上有3個誤分類點。
疊代過程3:對于m=3,在權值分布為D3的訓練資料上,門檻值v取5.5時誤差率最低,故基本分類器為:
G3(x)在訓練資料集上的誤差率e3=P(G3(xi)≠yi) = 0.1820
計算G3的系數:
更新訓練資料的權值分布:
D4=(0.125, 0.125, 0.125, 0.102, 0.102, 0.102, 0.065, 0.065, 0.065, 0.125),f3(x)=0.4236G1(x) + 0.6496G2(x)+0.7514G3(x),分類器sign(f3(x))在訓練資料集上有0個誤分類點。
3,Python實作
from numpy import *
def loadSimpData():
datMat = matrix([[ , ],
[ , ],
[ , ],
[ , ],
[ , ]])
classLabels = [, , -, -, ]
return datMat,classLabels
#從本地檔案讀取資料
def loadDataSet(fileName): #general function to parse tab -delimited floats
numFeat = len(open(fileName).readline().split('\t')) #get number of fields
dataMat = []; labelMat = []
fr = open(fileName)
for line in fr.readlines():
lineArr =[]
curLine = line.strip().split('\t')
for i in range(numFeat-):
lineArr.append(float(curLine[i]))
dataMat.append(lineArr)
labelMat.append(float(curLine[-]))
return dataMat,labelMat
#對資料進行分類,threshIneq表示兩種比較模式(>threshVal為1,或<threshVal為1),dimen是用來比較的特征的位置
def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):#just classify the data
retArray = ones((shape(dataMatrix)[],))
if threshIneq == 'lt':
retArray[dataMatrix[:,dimen] <= threshVal] = -
else:
retArray[dataMatrix[:,dimen] > threshVal] = -
return retArray
#建立單層決策樹
def buildStump(dataArr,classLabels,D):
dataMatrix = mat(dataArr); labelMat = mat(classLabels).T
m,n = shape(dataMatrix)
numSteps = ; bestStump = {}; bestClasEst = mat(zeros((m,)))
minError = inf #init error sum, to +infinity
#計算在所有特征中能使得誤差率最小的特征是哪一個,并計算出用來分割的值,即在上文中的a
for i in range(n):#loop over all dimensions
#計算每個特征的最大值與最小值
rangeMin = dataMatrix[:,i].min(); rangeMax = dataMatrix[:,i].max();
#将最大值與最小值之間分為10段
stepSize = (rangeMax-rangeMin)/numSteps
#依次在最大值與最小值之間取值,計算誤差
for j in range(-,int(numSteps)+):
for inequal in ['lt', 'gt']: #go over less than and greater than
threshVal = (rangeMin + float(j) * stepSize)
predictedVals = stumpClassify(dataMatrix,i,threshVal,inequal)
errArr = mat(ones((m,)))
errArr[predictedVals == labelMat] =
weightedError = D.T*errArr #calc total error multiplied by D
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=):
weakClassArr = []
m = shape(dataArr)[]
D = mat(ones((m,))/m) #init D to all equal
aggClassEst = mat(zeros((m,)))
#疊代numIt次
for i in range(numIt):
#計算在目前樣本權值D下的最小誤差
bestStump,error,classEst = buildStump(dataArr,classLabels,D)#build Stump
#print "D:",D.T
#calc alpha, throw in max(error,eps) to account for error=0
alpha = float(*log((-error)/max(error,)))
bestStump['alpha'] = alpha
#store Stump Params in Array
weakClassArr.append(bestStump)
expon = multiply(-*alpha*mat(classLabels).T,classEst) #exponent for D calc, getting messy
#更新D
D = multiply(D,exp(expon))
D = D/D.sum()
#classEst的值為1或-1,alpha*classEst為每一個分類器的權值與對應樣本的類别相乘,下式對所有的分類器進行相加,得到最終的類别,不過還要在用sign函數
aggClassEst += alpha*classEst
#sign(aggClassEst) != mat(classLabels).T不相等的位為1,相等的位為0,與ones((m,1)相乘,即得到誤差個數
aggErrors = multiply(sign(aggClassEst) != mat(classLabels).T,ones((m,)))
errorRate = aggErrors.sum()/m
print "total error: ",errorRate
if errorRate == : break
return weakClassArr,aggClassEst
#classifierArr即為上個方法傳回的weakClassArr,即每個分類器的最小誤差,分類所選取的特征,分割數a等
def adaClassify(datToClass,classifierArr):
dataMatrix = mat(datToClass)#do stuff similar to last aggClassEst in adaBoostTrainDS
m = shape(dataMatrix)[]
aggClassEst = mat(zeros((m,)))
for i in range(len(classifierArr)):
classEst = stumpClassify(dataMatrix,classifierArr[i]['dim'],\
classifierArr[i]['thresh'],\
classifierArr[i]['ineq'])#call stump classify
aggClassEst += classifierArr[i]['alpha']*classEst
print aggClassEst
return sign(aggClassEst)