今天學習的機器學習算法不是一個單獨的算法,我們稱之爲元算法或集成算法(Ensemble)。其實就是對其他算法進行組合的一種方式。俗話說的好:“三個臭皮匠,賽過諸葛亮”。集成算法有多種形式:對同一數據集,使用多個算法,通過投票或者平均等方法獲得最後的預測模型;同一算法在不同設置下的集成;同一算法在多個不同實例下的集成。本文着重講解最後一種集成算法。
bagging
如果訓練集有n個樣本,我們隨機抽取S次,每次有放回的獲取m個樣本,用某個單獨的算法對S個數據集(每個數據集有m個樣本)進行訓練,這樣就可以獲得S個分類器。最後通過投票箱來獲取最後的結果(少數服從多數的原則)。這就是bagging方法的核心思想,如圖所示。
bagging中有個常用的方法,叫隨機森林(random forest),該算法基於決策樹,不僅對數據隨機化,也對特徵隨機化。
數據的隨機化:應用bootstrap方法有放回地隨機抽取k個新的自助樣本集。
特徵隨機化:n個特徵,每棵樹隨機選擇m個特徵劃分數據集。
每棵樹無限生長,最後依舊通過投票箱來獲取最後的結果。
boosting
boosting方法在模型選擇方面和bagging一樣:選擇單個機器學習算法。但boosting方法是先在原數據集中訓練一個分類器,然後將前一個分類器沒能完美分類的數據重新賦權重(weight),用新的權重數據再訓練出一個分類器,以此循環,最終的分類結果由加權投票決定。所以:boosting是串行算法(必須依賴上一個分類器),而bagging是並行算法(可以同時進行);boosting的分類器權重不同,bagging相同(下文中詳細講解)。
boosting也有很多版本,本文只講解AdaBoost(自適應boosting)方法的原理和代碼實踐。如圖所示,爲AdaBoost方法的原理示意圖。
首先,訓練樣本賦權重,構成向量D(初始值相等,如100個數據,那每個數據權重爲1/100)。
在該數據上訓練一個弱分類器並計算錯誤率和該分類器的權重值(alpha)。
基於該alpha值重新計算權重(分錯的樣本權重變大,分對的權重變小)。
循環2,3步,但完成給定的迭代次數或者錯誤閾值時,停止循環。
最終的分類結果由加權投票決定。
alpha和D的計算見下圖(來源於機器學習實戰):
AdaBoost方法實踐
數據來源
數據通過代碼創建:
from numpy import *
def loadSimpData():
dataArr = array([[1., 2.1], [2., 1.1], [1.3, 1.], [1., 1.], [2., 1.]])
labelArr = [1.0, 1.0, -1.0, -1.0, 1.0]
return dataArr, labelArr
弱決策樹
該數據有兩個特徵,我們只用一個特徵進行分類(弱分類器),然後選擇精度最高的分類器。
def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
retArray = ones((shape(dataMatrix)[0],1))
if threshIneq == 'lt':
retArray[dataMatrix[:,dimen] <= threshVal] = -1.0
else:
retArray[dataMatrix[:,dimen] > threshVal] = -1.0
return retArray
def buildStump(dataArr, labelArr, D):
dataMat = mat(dataArr)
labelMat = mat(labelArr).T
m, n = shape(dataMat)
numSteps = 10.0
bestStump = {}
bestClasEst = mat(zeros((m, 1)))
minError = inf
for i in range(n):
rangeMin = dataMat[:, i].min()
rangeMax = dataMat[:, i].max()
stepSize = (rangeMax-rangeMin)/numSteps
for j in range(-1, int(numSteps)+1):
for inequal in ['lt', 'gt']:
threshVal = (rangeMin + float(j) * stepSize)
predictedVals = stumpClassify(dataMat, i, threshVal, inequal)
# print predictedVals
errArr = mat(ones((m, 1)))
errArr[predictedVals == 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 = predictedVals.copy()
bestStump['dim'] = i
bestStump['thresh'] = threshVal
bestStump['ineq'] = inequal
return bestStump, minError, bestClasEst
AdaBoost算法
該函數用於構造多棵樹,並保存每棵樹的信息。
def adaBoostTrainDS(dataArr,classLabels, numIt=40):
weakClassArr = []
m = shape(dataArr)[0]
D = mat(ones((m,1))/m)
aggClassEst = mat(zeros((m,1)))
for i in range(numIt):
bestStump,error,classEst = buildStump(dataArr, classLabels, 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:break
return weakClassArr
算法優缺點
優點:精度高
缺點:容易過擬合