https://www.ibm.com/developerworks/cn/analytics/library/machine-learning-hands-on6-adaboost/index.html?ca=drs-
AdaBoost 簡介
前面五篇文章涵蓋了分類、回歸、關聯分析等諸多模型,其中分類模型被介紹得最多。原因是分類在機器學習方向是應用最廣的方向之一。本文將要介紹的是分類模型中的另一種模型,AdaBoost(adaptive boosting),即自適應提升算法。
Boosting 是一類算法的總稱,這類算法的特點是通過訓練若干弱分類器,然后將弱分類器組合成強分類器進行分類。為什么要這樣做呢?因為弱分類器訓練起來很容易,將弱分類器集成起來,往往可以得到很好的效果。俗話說,"三個臭皮匠,頂個諸葛亮",就是這個道理。這類 boosting 算法的特點是各個弱分類器之間是串行訓練的,當前弱分類器的訓練依賴於上一輪弱分類器的訓練結果。各個弱分類器的權重是不同的,效果好的弱分類器的權重大,效果差的弱分類器的權重小。值得注意的是,AdaBoost 不止適用於分類模型,也可以用來訓練回歸模型。這需要將弱分類器替換成回歸模型,並改動損失函數。本文將重點介紹用 AdaBoost 進行分類的算法原理。
AdaBoost 算法有其獨特的優點,那就是可以將不同的分類算法組合起來,形成強分類器。這就可以充分利用不同分類算法的優勢進行建模。也可以將同一算法的不同設置進行組合,這樣訓練的模型比單一設置模型的訓練精度高。
當然,就如每一個算法都有自己的優缺點一樣,AdaBoost 也有自身的缺點。AdaBoost 算法只直接支持二分類,遇到多分類的情況,需要借助 one-versus-rest 的思想來訓練多分類模型。關於 one-verus-rest 的細節可以參考本系列第一篇文章 SVM。
為了讓讀者有一個感性的認識,在文章一開始先舉個 AdaBoost 訓練出來的強分類器的例子,如下所示,強分類器 G(x)中包含三個弱分類器 f(x), g(x) 和 z(x), 其中每個弱分類器的權重分別為0.80, 0.69和0.71。
G(x) = sign( 0.80 * f(x) + 0.69 * g(x) + 0.71 * z(x) )
AdaBoost原理
AdaBoost 的核心就是不斷迭代訓練弱分類器,並計算弱分類器的權重。需要注意的是,弱分類器的訓練依賴於樣本權重。每一輪迭代的樣本權重都不相同,依賴於弱分類器的權重值和上一輪迭代的樣本權重。具體過程如下:
訓練當前迭代最優弱分類器
最優弱分類器是錯誤率最小的那個弱分類器。錯誤率的計算公式是:
其中m = 1,2,..,M,代表第m輪迭代。i代表第i個樣本。w 是樣本權重。I指示函數取值為1或0,當I指示函數括號中的表達式為真時,I 函數結果為1;當I函數括號中的表達式為假時,I 函數結果為0。取錯誤率最低的弱分類器為當前迭代的最優弱分類器。
注意,第一輪迭代計算時樣本權重初始化為總樣本數分之一。
計算最優弱分類器的權重
最優弱分類器的權重只與該弱分類器的錯誤率有關。弱分類器的權重計算公式如下:
可以看出,錯誤率越小,則 alpha 值越大,即該弱分類器的權重越高;反之,錯誤率越大,則 alpha 值越小,則該弱分類器的權重越小。這樣可以使分類精度高的弱分類器起到更大的作用,並削弱精度低的弱分類器的作用。
根據錯誤率更新樣本權重
樣本權重的更新與當前樣本權重和弱分類器的權重有關。樣本權重更新公式如下:
其中m = 1,2,..,M,代表第 m 輪迭代。i代表第i個樣本。w 是樣本權重。alpha 是弱分類器的權重。當樣本被正確分類時,y 和 Gm 取值一致,則新樣本權重變小;當樣本被錯誤分類時,y 和 Gm 取值不一致,則新樣本權重變大。這樣處理,可以使被錯誤分類的樣本權重變大,從而在下一輪迭代中得到重視。
迭代終止條件
不斷重復1,2,3步驟,直到達到終止條件為止。終止條件是強分類器的錯誤率低於最低錯誤率閾值或達到最大迭代次數。
用例子解釋 AdaBoost 原理
本節主要用示例數據詳細說明用上節介紹的 AdaBoost 原理進行分類的過程。本例用到的數據集如表1所示。為方便說明,本文所用弱分類器為形如x<1.5,則y=1,否則y=-1的簡單分類算法。熟悉了 AdaBoost 原理的讀者,可以使用其他分類算法作為弱分類器。如使用本系列上篇文章介紹的 CART 樹中的分類樹作為弱分類器,可訓練出提升分類樹模型。
表 1. 示例數據集
第一輪迭代
1.a 選擇最優弱分類器
第一輪迭代時,樣本權重初始化為(0.167, 0.167, 0.167, 0.167, 0.167, 0.167)。
表1數據集的切分點有0.5, 1.5, 2.5, 3.5, 4.5
若按0.5切分數據,得弱分類器x < 0.5,則 y = 1; x > 0.5, 則 y = -1。此時錯誤率為2 * 0.167 = 0.334
若按1.5切分數據,得弱分類器x < 1.5,則 y = 1; x > 1.5, 則 y = -1。此時錯誤率為1 * 0.167 = 0.167
若按2.5切分數據,得弱分類器x < 2.5,則 y = 1; x > 2.5, 則 y = -1。此時錯誤率為2 * 0.167 = 0.334
若按3.5切分數據,得弱分類器x < 3.5,則 y = 1; x > 3.5, 則 y = -1。此時錯誤率為3 * 0.167 = 0.501
若按4.5切分數據,得弱分類器x < 4.5,則 y = 1; x > 4.5, 則 y = -1。此時錯誤率為2 * 0.167 = 0.334
由於按1.5划分數據時錯誤率最小為0.167,則最優弱分類器為x < 1.5,則 y = 1; x > 1.5, 則 y = -1。
1.b 計算最優弱分類器的權重
alpha = 0.5 * ln((1 – 0.167) / 0.167) = 0.8047
1.c 更新樣本權重
x = 0, 1, 2, 3, 5時,y分類正確,則樣本權重為:
0.167 * exp(-0.8047) = 0.075
x = 4時,y分類錯誤,則樣本權重為:
0.167 * exp(0.8047) = 0.373
新樣本權重總和為0.075 * 5 + 0.373 = 0.748
規范化后,
x = 0, 1, 2, 3, 5時,樣本權重更新為:
0.075 / 0.748 = 0.10
x = 4時, 樣本權重更新為:
0.373 / 0.748 = 0.50
綜上,新的樣本權重為(0.1, 0.1, 0.1, 0.1, 0.5, 0.1)。
此時強分類器為G(x) = 0.8047 * G1(x)。G1(x)為x < 1.5,則 y = 1; x > 1.5, 則 y = -1。則強分類器的錯誤率為1 / 6 = 0.167。
第二輪迭代
2.a 選擇最優弱分類器
若按0.5切分數據,得弱分類器x > 0.5,則 y = 1; x < 0.5, 則 y = -1。此時錯誤率為0.1 * 4 = 0.4
若按1.5切分數據,得弱分類器x < 1.5,則 y = 1; x > 1.5, 則 y = -1。此時錯誤率為1 * 0.5 = 0.5
若按2.5切分數據,得弱分類器x > 2.5,則 y = 1; x < 2.5, 則 y = -1。此時錯誤率為0.1 * 4 = 0.4
若按3.5切分數據,得弱分類器x > 3.5,則 y = 1; x < 3.5, 則 y = -1。此時錯誤率為0.1 * 3 = 0.3
若按4.5切分數據,得弱分類器x < 4.5,則 y = 1; x > 4.5, 則 y = -1。此時錯誤率為2 * 0.1 = 0.2
由於按4.5划分數據時錯誤率最小為0.2,則最優弱分類器為x < 4.5,則 y = 1; x > 4.5, 則 y = -1。
2.b 計算最優弱分類器的權重
alpha = 0.5 * ln((1 –0.2) / 0.2) = 0.6931
2.c 更新樣本權重
x = 0, 1, 5時,y分類正確,則樣本權重為:
0.1 * exp(-0.6931) = 0.05
x = 4 時,y分類正確,則樣本權重為:
0.5 * exp(-0.6931) = 0.25
x = 2,3時,y分類錯誤,則樣本權重為:
0.1 * exp(0.6931) = 0.20
新樣本權重總和為 0.05 * 3 + 0.25 + 0.20 * 2 = 0.8
規范化后,
x = 0, 1, 5時,樣本權重更新為:
0.05 / 0.8 = 0.0625
x = 4時, 樣本權重更新為:
0.25 / 0.8 = 0.3125
x = 2, 3時, 樣本權重更新為:
0.20 / 0.8 = 0.250
綜上,新的樣本權重為(0.0625, 0.0625, 0.250, 0.250, 0.3125, 0.0625)。
此時強分類器為G(x) = 0.8047 * G1(x) + 0.6931 * G2(x)。G1(x)為x < 1.5,則 y = 1; x > 1.5, 則 y = -1。G2(x)為x < 4.5,則 y = 1; x > 4.5, 則 y = -1。按G(x)分類會使x=4分類錯誤,則強分類器的錯誤率為1 / 6 = 0.167。
第三輪迭代
3.a 選擇最優弱分類器
若按0.5切分數據,得弱分類器x < 0.5,則 y = 1; x > 0.5, 則 y = -1。此時錯誤率為0.0625 + 0.3125 = 0.375
若按1.5切分數據,得弱分類器x < 1.5,則 y = 1; x > 1.5, 則 y = -1。此時錯誤率為1 * 0.3125 = 0.3125
若按2.5切分數據,得弱分類器x > 2.5,則 y = 1; x < 2.5, 則 y = -1。此時錯誤率為0.0625 * 2 + 0.250 + 0.0625 = 0.4375
若按3.5切分數據,得弱分類器x > 3.5,則 y = 1; x < 3.5, 則 y = -1。此時錯誤率為0.0625 * 3 = 0.1875
若按4.5切分數據,得弱分類器x < 4.5,則 y = 1; x > 4.5, 則 y = -1。此時錯誤率為2 * 0.25 = 0.5
由於按3.5划分數據時錯誤率最小為0.1875,則最優弱分類器為x > 3.5,則 y = 1; x < 3.5, 則 y = -1。
3.b 計算最優弱分類器的權重
alpha = 0.5 * ln((1 –0.1875) / 0.1875) = 0.7332
3.c 更新樣本權重
x = 2, 3時,y分類正確,則樣本權重為:
0.25 * exp(-0.7332) = 0.1201
x = 4 時,y分類正確,則樣本權重為:
0.3125 * exp(-0.7332) = 0.1501
x = 0, 1, 5時,y分類錯誤,則樣本權重為:
0.0625 * exp(0.7332) = 0.1301
新樣本權重總和為 0.1201 * 2 + 0.1501 + 0.1301 * 3 = 0.7806
規范化后,
x = 2, 3時,樣本權重更新為:
0.1201 / 0.7806 = 0.1539
x = 4時, 樣本權重更新為:
0.1501 / 0.7806 = 0.1923
x = 0, 1, 5時, 樣本權重更新為:
0.1301 / 0.7806 = 0.1667
綜上,新的樣本權重為(0.1667, 0.1667, 0.1539, 0.1539, 0.1923, 0.1667)。
此時強分類器為G(x) = 0.8047 * G1(x) + 0.6931 * G2(x) + 0.7332 * G3(x)。G1(x)為x < 1.5,則 y = 1; x > 1.5, 則 y = -1。G2(x)為x < 4.5,則 y = 1; x > 4.5, 則 y = -1。G3(x)為x > 3.5,則 y = 1; x < 3.5, 則 y = -1。按G(x)分類所有樣本均分類正確,則強分類器的錯誤率為0 / 6 = 0。則停止迭代,最終強分類器為G(x) = 0.8047 * G1(x) + 0.6931 * G2(x) + 0.7332 * G3(x)。
實現步驟: 自己動手實現 AdaBoost
本節將介紹如何用 python 代碼實現 AdaBoost。AdaBoost 的實現是一個迭代的過程,每一輪迭代都會訓練最優基本弱分類器,然后計算弱分類器的權重;再根據錯誤率和上一輪樣本權重更新樣本權重值。如果當前所有弱分類器的線性組合的錯誤率低於最低錯誤率閾值,或者達到最大迭代數,則結束迭代,返回所有弱分類器的線性組合作為最終訓練所得的強分類器。
清單 1. 計算可能的划分點
1
2
3
4
5
6
|
def generateGxList(x):
gxlist = []
for i in range(len(x) - 1):
gx = (x[i] + x[i + 1]) / 2
gxlist.append(gx)
return gxlist
|
清單1介紹了計算x所有可能划分點的過程。本文選取兩個相鄰x的均值作為划分點。
清單 2. 計算弱分類器權重
1
2
3
|
def calcAlpha(minError):
alpha = 1/2 * log((1-minError)/minError)
return alpha
|
清單2用來計算弱分類器的權重。權重計算公式參加上文 AdaBoost 原理部分。可以看出弱分類器的權重只與錯誤率有關,錯誤率越大,則對應的弱分類器的權重越小;錯誤率越小,而對應的弱分類器的權重越大。
清單 3. 計算樣本新權重
1
2
3
4
5
6
7
8
9
10
11
12
13
|
def calcNewWeight(alpha,ygx, weight, gx, y):
newWeight = []
sumWeight = 0
for i in range(len(weight)):
flag = 1
if i <
gx
and y[i] != ygx: flag = -1
if i > gx and y[i] != -ygx: flag = -1
weighti = weight[i]*exp(-alpha*flag)
newWeight.append(weighti)
sumWeight += weighti
newWeight = newWeight / sumWeight
return newWeight
|
清單3介紹了更新樣本權重的方法。該方法只與上一輪樣本權重和弱分類器的權重 alpha 值有關。具體計算公式為weighti = weight[i]*exp(-alpha*flag),可以看出若樣本分類正確,則flag為1,exp(-alpha*flag)越小,則新權重越小;若樣本分類錯誤,則flag為-1,exp(-alpha*flag)越大,則新權重會越大。這會使得上一輪分類錯誤地樣本的權重變大,使其在新一輪訓練中得到重視。
清單 4. 訓練基本弱分類器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
def trainfxi(fx, i, x, y, weight):
minError = inf
bestGx = 0.5
gxlist = generateGxList(x)
bestygx = 1
# 計算基本分類器
for xi in gxlist:
error, ygx = calcErrorNum(xi, x, y, weight)
if error <
minError:
minError
=
error
bestGx
=
xi
bestygx
= ygx
fx[i]['gx'] = bestGx
#計算alpha
alpha
=
calcAlpha
(minError)
fx[i]['alpha'] = alpha
fx[i]['ygx'] = bestygx
#計算新的訓練數據權值
newWeight
=
calcNewWeight
(alpha,bestygx, weight, bestGx, y)
return newWeight
def calcErrorNum(gx, x, y, weight):
#判斷以gx為切分點的兩種方式里,哪種會讓誤差更小
error1
=
0
errorNeg1
=
0
ygx
=
1
for i in range(len(x)):
if i < gx and y[i] != 1: error1 += weight[i]
if i > gx and y[i] != -1: error1 += weight[i]
if i <
gx
and y[i] != -1: errorNeg1 += weight[i]
if i > gx and y[i] != 1: errorNeg1 += weight[i]
if errorNeg1 <
error1:
return errorNeg1, -1 #x>gx,則fgx = 1
return error1, 1 #x <gx, 則fgx = 1
|
清單4用來訓練基本弱分類器。首先,選出錯誤率最低的弱分類器作為當前迭代的基礎分類器,然后調用 calcAlpha 函數計算該弱分類器的權重,接着調用 calcNewWeight 函數更新訓練數據權重。
清單 5. 計算當前弱分類器線性組合的錯誤率
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
def calcFxError(fx, n, x, y):
errorNum = 0
for i in range(len(x)):
fi = 0
for j in range(n):
fxiAlpha = fx[j]['alpha']
fxiGx = fx[j]['gx']
ygx = fx[j]['ygx']
if i < fxiGx: fgx = ygx
else: fgx = -ygx
fi += fxiAlpha * fgx
if sign(fi) != y[i]: errorNum += 1
return errorNum/len(x)
|
清單5用來計算當前所有弱分類器線性組合形成的強分類器的錯誤率。錯誤率為錯分類樣本數除以總樣本數。
清單 6. 訓練強分類器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
def trainAdaBoost(x, y, errorThreshold, maxIterNum):
fx = {}
weight = []
xNum = len(x)
for i in range(xNum):
w = float(1/xNum)
weight.append(w)
for i in range(maxIterNum):
fx[i] = {}
newWeight = trainfxi(fx, i, x, y, weight)
weight = newWeight
fxError = calcFxError(fx, (i+1), x, y)
if fxError < errorThreshold: break
return fx
|
清單6用來訓練強分類器。errorThreshold 為最低錯誤率閾值,maxIterNum 為最大迭代數,用這兩個變量控制迭代是否結束。首先,初始化樣本權重為總樣本數分之一,然后訓練弱分類器,並更新樣本權重。如果強分類器權重小於最低錯誤率閾值 errorThreshold,或者達到最大迭代數,則算法結束。
代碼下載
本文所有 AdaBoost 實現代碼可在文末下載。
本文數據集簡介
圖1.數據集樣例
數據集共有6條數據。分別為x取0時,y為1;x取1時,y取1;x取2時,y取-1;以此類推。其中y是目標變量,取值范圍為1和-1。
應用示例: 應用實現的 AdaBoost 訓練模型
清單 7. 用 AdaBoost 訓練模型
1
2
3
4
5
6
7
8
9
10
11
|
def loadDataSet():
x = [0, 1, 2, 3, 4, 5]
y = [1, 1, -1, -1, 1, -1]
return x, y
if __name__ == '__main__':
x, y = loadDataSet()
errorThreshold = 0.01
maxIterNum = 10
fx = trainAdaBoost(x, y, errorThreshold, maxIterNum)
print(fx)
|
清單7用來訓練模型。首先加載數據集,得到x和y。然后設置最小錯誤閾值和最大迭代數,用來控制模型訓練何時結束。最后調用 trainAdaBoost 函數訓練模型。運行結果如下所示。
運行結果:
{0: {'alpha': 0.80471895621705025, 'gx': 1.5, 'ygx': 1}, 1: {'alpha': 0.69314718055994529, 'gx': 4.5, 'ygx': 1}, 2: {'alpha': 0.73316853439671348, 'gx': 3.5, 'ygx': -1}}
運行結果中共包含3個弱分類器。第一個弱分類器的權重是0.8047,該分類器為x < 1.5,則y取1,否則y取-1。第二個弱分類器的權重是0.6931,該分類器為x < 4.5,則y取1,否則y取-1。第三個弱分類器的權重是0.7332,該分類器為x < 3.5,則y取-1,否則y取1。
總結
本文首先介紹了 AdaBoost 的概念、優點及缺點,接着從訓練弱分類器、計算弱分類器權重、更新樣本權重等入手詳細深入地講解了 AdaBoost 的原理。接着用例子介紹 AdaBoost 的算法過程。然后通過代碼樣例,介紹了自己動手實現 AdaBoost 的思路。最后,利用數據展示了如何應用 AdaBoost 訓練具體模型。有讀者肯定會想問弱分類器的權重計算公式由何而來?其實,該公式並非憑空造出來的,而是經過嚴格推導出來的。它是將指數函數作為損失函數,前向分步算法作為學習算法而一步步推導得到的。上文介紹了用 AdaBoost 思想進行簡單分類的過程,如果把上文中的弱分類器替換成樹中的分類樹,就得到了提升樹模型。其實用 AdaBoost 也可以進行回歸。用 AdaBoost 實現回歸模型需要將弱分類器替換成回歸樹,並將平方誤差作為損失函數。由於篇幅有限在此不展開,有興趣的讀者可以詳細閱讀下方參考資料進行了解。
參考資源
本文用到的參考文獻如下:
- 參考李航著《統計學習方法》,了解弱分類器權重計算過程及用 AdaBoost 進行回歸的原理和過程。
- 參考 Peter Harrington 著《機器學習實戰》,了解 Boosting 的特點。