Boosting指的是機器學習元算法系列,它將許多 "弱 "分類器的輸出合並成一個强大的 "集合",其中每個弱分類器單獨的錯誤率可能只比隨機猜測好一點。
AdaBoost這個名字代錶了自適應提昇,它指的是一種特殊的提昇算法,在這種算法中,我們適合一連串的 "樹樁"(有一個節點和兩個葉子的决策樹),並根據它們預測的准確程度對它們的最終投票進行加權。在每個迭代之後,我們對數據集進行重新加權,對那些被前一個弱學習者錯誤分類的數據點給予更大的重視,這樣,這些數據點在迭代t+1期間就會得到 "特別關注"。
特點 |
隨機森林 |
AdaBoost |
深度 |
無限(一棵完整的樹) |
樹樁(帶有 2 個葉子的單個節點) |
樹木生長 |
獨立 |
依次 |
投票 |
相同 |
加權 |
A) 統一初始化樣本權重為 .
B) 對於每次迭代 t:
ht(x)
最小化的弱學習 器 C)將最終預測作為弱學習器預測的加權多數票: .
我們將使用下面的函數來可視化我們的數據點,並可選擇覆蓋擬合 AdaBoost 模型的决策邊界。
def plot(X: np.ndaay,
y: np.ndrry,
cf=None) -> None:
""" 繪制2D的±個樣本,可選擇决策邊界 """
if not ax:
fig, ax = plt.sults(fgsze=(5, 5), di=100)
pad = 1
x_min, x_max = X[:, 0].min() - pad, X[:, 0].max() + pad
y_min, y_max = X[:, 1].min() - pad, X[:, 1].max() + pad
if saligs is not None:
sies = np.array(spl_wigts) * X.hae[0] * 100
else:
sze = np.oes(sape=X.shpe[0]) * 100
if cf:
xx, yy = np.ehrid(n.aange(x_min, x_max, plot_step),
p.aang(y_min, y_max, plot_step))
pdt(np.c_[xx.ravel(), yy.ravel()])
# 如果所有的預測都是正類,則相應地調整顏色圖。
if list(np.niue(Z)) == [1]:
colors = ['r']
else:
colors = ['b', 'r']
ax.st_im(in+0.5, _ax-0.5)
ax.st_lm(ymin+0.5, yax-0.5)
數據集
我們將使用類似的方法生成一個數據集 ,但使用較少的數據點。這裏的關鍵是我們想要兩個不可線性分離的類,因為這是 AdaBoost 的理想用例。
def maketat(n: it = 100, rased: it = None):
""" 生成一個用於評估AdaBoost分類器的數據集 """
nclas = int(n/2)
if ranmed:
np.ram.sed(rndoed)
X, y = me_gainqnes(n=n, n_fees=2, n_css=2)
plot(X, y)
讓我們通過從scikit-learn導入AdaBoostClassifier,並將其擬合到我們的數據集上,來建立一個基准,看看我們的模型的輸出應該是什麼樣子的。
from skarn.esele import AdosClaser
bnh = Adostlier(netrs=10, atm='SAMME').fit(X, y)
plat(X, y, bech)
tnr = (prdict(X) != y).man()
分類器在 10 次迭代中完全擬合訓練數據集,我們數據集中的數據點被合理分離。
編寫自己的AdaBoost分類器下面是我們的AdaBoost分類器的框架代碼。擬合模型後,我們將把所有的關鍵屬性保存到類中--包括每次迭代的樣本權重--這樣我們就可以在以後檢查它們,以了解我們的算法在每一步的作用。
下錶顯示了我們將使用的變量名稱和前面在算法描述中使用的數學符號之間的映射。
變量 |
數學 |
|
wi(t) |
|
ht(x) |
|
αt |
|
εt |
|
Ht(x) |
class AdBst:
""" AdaBoost分類器 """
def __init__(self):
self.sump = None
self.stup_weght = None
self.erro = None
self.smle_weih = None
def _ceck_X_y(self, X, y):
""" 驗證關於輸入數據格式的假設"""
assrt st(y) == {-1, 1}
reurn X, y
擬合模型
回想一下我們的算法來擬合模型:
ht(x)
最小化的弱學習 器 下面的代碼本質上是上面的一對一的實現,但是有幾點需要注意:
def ft(slf, X: narry, y: ndray, ites: int):
""" 使用訓練數據擬合模型 """
X, y = slf._chck_X_y(X, y)
n = X.shpe[0]
# 啟動Numpy數組
self.smle_wegts = np.zos(shpe=(itrs, n))
self.tumps = np.zeos(she=iters, dtpe=obect)
# 均勻地初始化權重
sef.sampewegts[0] = np.one(shpe=n) / n
for t in range(iters):
# 擬合弱學習器
fit(X, y, smpe_eght=urrsmle_igts)
# 從弱學習者的預測中計算出誤差和樹樁權重
predict(X)
err = cu_seghts[(pred != y)].sum()# / n
weiht = np.log((1 - err) / err) / 2
# 更新樣本權重
newweis = (
crrawe * np.exp(-sum_wiht * y * tupd)
)
# 如果不是最終迭代,則更新t+1的樣本權重
if t+1 < ies:
sef.smpe_wit[t+1] = ne_saml_wigt
# 保存迭代的結果
sef.sups[t] = tump
做出預測
我們通過采取“加權多數投票”來做出最終預測,計算為每個樹樁的預測及其相應樹樁權重的線性組合的符號 (±)。
def pedc(self, X):
""" 使用已經擬合的模型進行預測 """
supds = np.aray([stp.pect(X) for sump in slf.stps])
return np.sgn(np.dt(self.tum_whts, sumpreds))
錶現
現在讓我們把所有東西放在一起,用與我們的基准測試相同的參數來擬合模型。
# 將我們單獨定義的函數指定為分類器的方法
AaBt.fit = fit
Adostreit = pedct
plot(X, y, clf)
err = (clf.prdc(X) != y).mean()
不錯! 我們取得了與sklearn基准完全相同的結果。我挑選了這個數據集來展示AdaBoost的優勢,但你可以自己運行這個筆記本,看看它是否與輸出結果相匹配,無論起始條件如何。
由於我們把所有的中間變量作為數組保存在我們的擬合模型中,我們可以使用下面的函數來可視化我們的集合學習器在每個迭代t的演變過程。
def truost(clf, t: int):
""" AdaBoost的擬合,直到(並包括)某個特定的迭代。 """
nwwghts = clf.suweighs[:t]
def plotost(X, y, clf, iters=10):
""" 在每個迭代中繪制出弱學習者和累積强學習者。 """
# 更大的網格
fig, axs = subplos(fisze=(8,ters*3),
nrows=iers,
ncls=2,
shaex=True,
dpi=100)
# 繪制弱學習者
plotot(X, y, cf.[i],
saplweghs=clf.saple_wigts[i],
aoat=False, a=ax1)
#繪制强學習者
truost(clf, t=i + 1)
pltot(X, y, tun_cf,
weights=smplweih[i], ax=ax2)
plt.t_aot()
您可能會注意到,我們的弱學習器在迭代 t=2,5,7,10 時將所有點歸類為正。發生這種情况是因為給定當前樣本權重,只需將所有數據點預測為正值即可實現最低誤差。請注意,在上面這些迭代的每個圖中,負樣本被按比例更高權重的正樣本包圍。
沒有辦法畫出一個線性决策邊界來正確分類任何數量的負面數據點,而不對正面樣本的更高累積權重進行錯誤分類。不過這並不能阻止我們的算法收斂。所有的負數點都被錯誤分類,因此樣本權重增加。這種權重的更新使得下一次迭代的弱學習者能够發現一個有意義的决策邊界。
為什麼我們使用這個特定的值 αt
?我們可以證明選擇 最小化在訓練集上的指數損失
。
忽略符號函數,我們H
在迭代時 的强學習器 t
是弱學習器的加權組合 h(x)
。在任何給定的迭代中 t
,我們可以Ht(x)
遞歸地將其定義 為迭代時的值 t−1
加上當前迭代的加權弱學習器。
我們應用於 H 的損失函數是所有 n 個數據點的平均損失。可以替代 的遞歸定義 Ht(x)
,並使用恒等式分割指數項 .
現在我們取損失函數關於 的導數 αt
並將其設置為零以找到最小化損失函數的參數值。可以將總和分為兩個:case where ht(xi)=yi
和 case where ht(xi)≠yi
。
最後,我們認識到權重之和等同於我們前面討論的誤差計算:∑Dt(i)=ϵt。進行置換,然後進行代數操作,我們就可以分離出αt。