一.簡介
adaboost是一種boosting方法,它的要點包括如下兩方面:
1.模型生成
每一個基分類器會基於上一輪分類器在訓練集上的表現,對樣本做權重調整,使得錯分樣本的權重增加,正確分類的樣本權重降低,所以當前輪的訓練更加關注於上一輪誤分的樣本;
2.模型組合
adaboost是採用的加權投票的方法
簡單來說,adaboost演算法涉及兩種權重的計算:樣本權重、分類器權重,接下來直接講演算法流程
二.演算法流程
輸入:訓練集\(T=\{(x_1,y_1),(x_2,y_2),...,(x_N,y_N)\}\),其中\(x_i\in R^n,y_i\in\{+1,-1\},i=1,2,...,N\)
輸出:最終分類器\(G(x)\)
(1)初始化訓練資料的權重分佈:
(2)對\(m=1,2,...,M:\)
(2.1)使用具有權重分佈\(D_m\)的訓練資料集學習,得到基本分類器:\(G_m(x)\)
(2.2)計算\(G_m(x)\)在訓練集上的分類誤差率:\(e_m=\sum_{i=1}^NP(G_m(x_i)\neq y_i)=\sum_{i=1}^Nw_{mi}I(G_m(x_i)\neq y_i)\)
(2.3)計算\(G_m(x)\)的權重係數:\(\alpha_m=\frac{1}{2}ln\frac{1-e_m}{e_m}\)
(2.4)更新訓練樣本權重:
這裡\(Z_m\)是歸一化因子
(3)基於基分類器,構建最終的分類器:
簡單來說大致流程如下:
三.程式碼實現
import os
os.chdir('../')
from ml_models import utils
from ml_models.tree import CARTClassifier
import copy
import numpy as np
%matplotlib inline
"""
AdaBoost分類器的實現,封裝到ml_models.ensemble
"""
class AdaBoostClassifier(object):
def __init__(self, base_estimator=None, n_estimators=10, learning_rate=1.0):
"""
:param base_estimator: 基分類器,允許異質;異質的情況下使用列表傳入比如[estimator1,estimator2,...,estimator10],這時n_estimators會失效;
同質的情況,單個estimator會被copy成n_estimators份
:param n_estimators: 基分類器迭代數量
:param learning_rate: 學習率,降低後續基分類器的權重,避免過擬合
"""
self.base_estimator = base_estimator
self.n_estimators = n_estimators
self.learning_rate = learning_rate
if self.base_estimator is None:
# 預設使用決策樹樁
self.base_estimator = CARTClassifier(max_depth=2)
# 同質分類器
if type(base_estimator) != list:
estimator = self.base_estimator
self.base_estimator = [copy.deepcopy(estimator) for _ in range(0, self.n_estimators)]
# 異質分類器
else:
self.n_estimators = len(self.base_estimator)
# 記錄estimator權重
self.estimator_weights = []
def fit(self, x, y):
n_sample = x.shape[0]
sample_weights = np.asarray([1.0] * n_sample)
for index in range(0, self.n_estimators):
self.base_estimator[index].fit(x, y, sample_weight=sample_weights)
indicates = (self.base_estimator[index].predict(x) == y).astype(int)
# 計算誤分率
error_rate = np.sum([sample_weights[j] * (1.0 - indicates[j]) for j in range(0, n_sample)]) / n_sample
# 計算權重係數
alpha_rate = 1.0 / 2.0 * np.log((1 - error_rate) / (error_rate + 1e-7))
alpha_rate = min(10.0, alpha_rate)
self.estimator_weights.append(alpha_rate)
# 更新樣本權重
for j in range(0, n_sample):
sample_weights[j] = sample_weights[j] * np.exp(-1.0 * alpha_rate * np.power(-1.0, 1 - indicates[j]))
sample_weights = sample_weights / np.sum(sample_weights) * n_sample
# 更新estimator權重
for i in range(0, self.n_estimators):
self.estimator_weights[i] *= np.power(self.learning_rate, i)
def predict_proba(self, x):
# TODO:並行優化
result = np.sum(
[self.base_estimator[j].predict_proba(x) * self.estimator_weights[j] for j in
range(0, self.n_estimators)],
axis=0)
return result / result.sum(axis=1, keepdims=True)
def predict(self, x):
return np.argmax(self.predict_proba(x), axis=1)
#造偽資料
from sklearn.datasets import make_classification
data, target = make_classification(n_samples=100, n_features=2, n_classes=2, n_informative=1, n_redundant=0,
n_repeated=0, n_clusters_per_class=1, class_sep=.5,random_state=21)
# 同質
classifier = AdaBoostClassifier(base_estimator=CARTClassifier(max_depth=2),n_estimators=10)
classifier.fit(data, target)
utils.plot_decision_function(data, target, classifier)
#異質
from ml_models.linear_model import LogisticRegression
from ml_models.svm import SVC
classifier = AdaBoostClassifier(base_estimator=[LogisticRegression(),SVC(kernel='rbf',C=5.0),CARTClassifier()])
classifier.fit(data, target)
utils.plot_decision_function(data, target, classifier)
# 權重衰減
classifier = AdaBoostClassifier(base_estimator=[LogisticRegression(),SVC(kernel='rbf',C=5.0),CARTClassifier()],learning_rate=0.5)
classifier.fit(data, target)
utils.plot_decision_function(data, target, classifier)
四.問題討論
1.基本要求:弱可學習
注意有個基本要求,那就是\(e_m<0.5\),即分類器至少是弱可學習的,這樣才能保證\(\alpha_m>0\),此時樣本的權重調整(如下公式)才有意義,即正確分類的樣本權重降低,錯誤分類的樣本權重升高:
對於二分類問題,弱可學習其實是很容易保證的,對於\(e_m>0.5\)的情況,只需要對其預測取反,即可得到\(1-e_m<0.5\)的錯誤率
2.基分類器不支援樣本權重怎麼辦?
對於不能支援樣本權重訓練的基分類器,可以通過樣本重取樣來實現
五.訓練誤差分析
這一部分證明訓練誤差會隨著基分類器的數量增加而指數下降,首先丟擲第一個不等式關係:
這裡\(f(x)=\sum_{m=1}^M\alpha_mG_m(x),G(x)=sign(f(x)),Z_m\)與上面的定義一樣,前半部分很好證明:如果\(G(x_i)\neq y_i\),則\(y_if(x_i)<0\),所以\(exp(-y_if(x_i))\geq 1=I(G(x_i)\neq y_i)\),而對於\(G(x_i)= y_i\)的情況,顯然有\(exp(-y_if(x_i))\geq 0=I(G(x_i\neq y_i))\);
接下來證明後半部分,根據之前的推導,有如下的兩點條件需要注意:
所以:
接下來要丟擲第二個關係式,對於二分類問題有如下不等式成立:
這裡:\(\gamma_m=\frac{1}{2}-e_m\),首先證明等式部分,由前面的演算法部分,我們知道\(e_m=\sum_{i=1}^Nw_{mi}I(G_m(x_i)\neq y_i)\),所以:
至於不等式部分,其實對於\(\forall 0\leq x\leq 1\),都有\(e^{-x/2}\geq \sqrt{1-x}\)恆成立(證明從略,直觀理解如下圖),將\(x\)替換為\(4\gamma_m^2\)即可得到上面的不等式,從而關係式2得到證明;
接下來簡單做一個推論:一定能找到一個\(\gamma>0\),對所有\(\gamma_m\geq\gamma\)成立,則有如下關係:
結合關係式1、2、3可以得出:
即adaboost的誤差上界會隨著\(M\)的增加以指數速率下降
import matplotlib.pyplot as plt
x=np.linspace(0,1,10)
plt.plot(x,np.sqrt(1-x),'b')
plt.plot(x,np.exp(-0.5*x),'r')
[<matplotlib.lines.Line2D at 0x21a6b0c1048>]