監督學習集成模型——AdaBoost

一、集成學習與Boosting

集成學習是指將多個弱學習器組合成一個強學習器,這個強學習器能取所有弱學習器之所長,達到相對的最佳性能的一種學習範式。

集成學習主要包括Boosting和Bagging兩種學習框架。Boosting是一種將弱學習器提升爲強學習器的算法,所以也叫提升算法。

以分類問題爲例,給定一個訓練數據集,訓練弱分類器要比訓練強分類器相對容易很多,從第一個弱分類器開始,Boosting通過訓練多個弱分類器,並在訓練過程中不斷改變訓練樣本的概率分佈,使得每次訓練時算法都會更加關注上一個弱分類器的錯誤。通過組合多個這樣的弱分類器,便可以獲得一個接近相對完美的強分類器。boosting方法的核心理念在於博採衆長,正所謂"三個臭皮匠,頂個諸葛亮",這也使得boosting方法要好於大多數單模型算法。

二、AdaBoost模型

AdaBoost基本原理

AdaBoost的全稱爲Adaptive Boosting,可以翻譯爲自適應提升算法。(提升方法是將弱學習算法提升爲強學習算法的統計學習方法。)

AdaBoost是一種通過改變訓練樣本權重來學習多個弱分類器併線性組合成強學習器的Boosting算法。

Boosting要解決兩個關鍵問題:

  • 一是在訓練過程中如何改變訓練樣本的權重或者概率分佈。
  • 二是如何將多個弱分類器組合成一個強分類器。

針對這兩個問題,Adaboost是做法非常樸素,第一個就是提高前一輪被弱分類器分類錯誤的樣本的權重、而降低分類正確的樣本權重;第二則是對多個弱分類器進行線性組合,提高分類效果好的弱分類器權重,減小分類誤差率大的弱分類器權重。

給定訓練數據集\(D={(x_1,y_1),(x_2,y_2),⋯,(x_N,y_N)}\),其中\(x_i∈χ⊆R^n,y_i∈Y={−1,+1}\),AdaBoost訓練算法如下。

  1. 初始化訓練數據樣本的權值分佈,即爲每個訓練樣本分配一個初始權值:$D_1=(w_{11},⋯,w_{1i},⋯w_{1N}), w_{1i}=1/N, i=1, 2,⋯,N $

  2. 對於\(t=1, 2,⋯,T\),分別執行以下步驟。

  • 對包含權值分佈\(D_m\)的訓練數據集進行訓練並得到弱分類器\(G_t(x)\)

  • 計算\(G_t(x)\)在當前加權訓練集上的分類誤差率\(ϵ_t\)

    \(ϵ_t=P(G_t(x_i)≠y_i)=∑_{i=1}^Nw_{ti}I(G_t(x_i)≠y_i)\)

  • 根據分類誤差率\(ϵ_t\)計算當前弱分類器的權重係數\(α_t\)

    \(α_t=1/2 log{(1−ϵ_t)/ϵ_t}\)

  • 調整訓練數據集的權值分佈:

    \(D_{t+1}=(w_{t+1},1,⋯,w_{t+1},i,⋯w_{t+1},N)\)

    \(w_{t+1},i=w_ti/{Z_t} exp(−α_t y_i G_t(x_i))\)

    分類正確的權重下降,分類錯誤權重上升

    其中\(Z_t\)爲歸一化因子,\(Z_t=∑_{i=1}^N w_{ti}exp(−α_t y_i G_t(x_i))\)

  1. 最後構建T個弱分類器的線性組合:

    \(f(x)=∑_{i=1}^T α_t G_t(x)\)

最終的AdaBoost強分類器可以寫爲:
\(G(x)=sign(f(x))=sign(∑_{i=1}^N α_t G_t(x))\)

在弱分類器權重係數計算過程中,當弱分類器的分類誤差率\(ϵ_t≤1/2\)時,\(α_t≥0\),且\(α_t\)隨着\(ϵ_t\)的減小而變大,這也正是弱分類器權重係數計算公式的設計思想,它能夠使得分類誤差率較低的分類器有較大的權重係數。AdaBoost訓練樣本權值分佈可以寫爲:

\[w_{t+1, i}=\left\{\begin{array}{ll} \frac{w_{t i}}{Z_{t}} e^{-\alpha_{t}}, & G_{t}\left(x_{i}\right)=y_{i} \\ \frac{w_{t i}}{Z_{t}} e^{\alpha_{t}}, & G_{t}\left(x_{i}\right) \neq y_{i} \end{array}\right.\]

當樣本被弱分類器正確分類時,對應樣本的權重變小;當樣本被弱分類器錯誤分類時,對應樣本的權重變大。相比之外,錯誤分類樣本的權重擴大了\(e^{2α_t}\)倍,這就使得在下一輪訓練中,算法將更加關注這些誤分類的樣本。

視頻講解:

簡博士

https://www.bilibili.com/video/BV18g41197rC

https://www.bilibili.com/video/BV1pF411F7CY/

前向分步算法

從機器學習三要素(模型、策略、算法)的角度來看,AdaBoost可以看作爲以加性模型爲模型、指數函數爲損失函數和前向分步爲算法的分類學習算法。

所謂加性模型(additive model),就是由多個基模型求和的形式構造起來的。加性模型可以表示爲:

\(f(x)=∑_{t=1}^T α_t b(x;γ_t)\)

其中\(b(x;γ_t)\)爲基模型,\(γ_t\)爲基模型參數,\(α_t\)爲基模型係數,可知f(x)是由T個基模型求和的加性模型。

給定訓練數據集和損失函數的條件下,加性模型的目標函數爲如下最小化損失函數:

\(\min _{\alpha_{t}, \gamma_{t}} \sum_{i=1}^{N} L\left(y_{i}, \sum_{t=1}^{T} \alpha_{t} b\left(x_{i} ; \gamma_{t}\right)\right)\)

針對上式這樣一個較爲複雜的優化問題,可以採用前向分步算法進行求解。其基本思路如下:針對加性模型的特點,從前往後每次只優化一個基模型的參數,每一步優化疊加之後便可逐步逼近目標函數。每一步優化的表達式如下式所示:

\(\min _{α,γ} ∑_{i=1}^N L(y_i,αb(x_i;γ))\)

給定訓練數據集\(D={(x_1,y_1),(x_2,y_2),⋯,(x_N,y_N)}\),其中\(x_i∈χ⊆R^n,y_i∈Y=\{−1,+1\}\),前向分步算法求解過程如下。

初始化模型\(f_0(x)=0\)

對於t=1, 2,⋯,T,分別執行以下操作。

\(α_t\)\(γ_t\)爲優化參數,最小化目標損失函數:

$(α_t,γ_t)=argmin_{α,γ}∑_{i=1}^N L(y_i,f_{t−1}(x_i)+αb(x_i;γ)) $

更新加性模型:

\(f_t(x)=f_{t−1}(x)+α_tb(x;γ_t)\)

可得到最後的加性模型爲:

\(f(x)=f_T(x)=∑_{t=1}^Tα_tb(x;γ_t)\)

從前向分步算法的角度來理解AdaBoost,可以將AdaBoost看作前向分步算法的特例,這時加性模型是以分類器爲基模型、以指數函數爲損失函數的最優化問題。假設經過t−1次前向分步迭代後已經得到\(f_{t−1}(x)\),第t次迭代可以得到第t個基模型的權重係數\(α_t\)、第t個基模型\(G_t(x)\)和t輪迭代後的加性模型\(f_t(x)\)。優化目標是使

\(f_t(x)\)在給定訓練數據集D上的指數損失最小化,有:

\(\left(\alpha_{t}, G_{t}(x)\right)=\underset{\alpha, G}{\operatorname{argmin}} \sum_{i=1}^{N} \exp \left(-y_{i}\left(f_{t-1}\left(x_{i}\right)+\alpha G\left(x_{i}\right)\right)\right)\)

求解上式的最小化指數損失即可得到AdaBoost的優化參數。

三、AdaBoost算法實現

先定義一個基分類器

### 定義決策樹樁類
### 作爲Adaboost弱分類器
class DecisionStump():
    def __init__(self):
        # 基於劃分閾值決定樣本分類爲1還是-1
        self.label = 1
        # 特徵索引
        self.feature_index = None
        # 特徵劃分閾值
        self.threshold = None
        # 指示分類準確率的值
        self.alpha = None

定義AdaBoost算法類

### 定義AdaBoost算法類
class Adaboost:
    # 弱分類器個數
    def __init__(self, n_estimators=5):
        self.n_estimators = n_estimators
        
    # Adaboost擬合算法
    def fit(self, X, y):
        m, n = X.shape
        # (1) 初始化權重分佈爲均勻分佈 1/N
        w = np.full(m, (1/m))
        # 處初始化基分類器列表
        self.estimators = []
        # (2) for m in (1,2,...,M)
        for _ in range(self.n_estimators):
            # (2.a) 訓練一個弱分類器:決策樹樁
            estimator = DecisionStump()
            # 設定一個最小化誤差
            min_error = float('inf')
            # 遍歷數據集特徵,根據最小分類誤差率選擇最優劃分特徵
            for i in range(n):
                # 獲取特徵值
                values = np.expand_dims(X[:, i], axis=1)
                # 特徵取值去重
                unique_values = np.unique(values)
                # 嘗試將每一個特徵值作爲分類閾值
                for threshold in unique_values:
                    p = 1
                    # 初始化所有預測值爲1
                    pred = np.ones(np.shape(y))
                    # 小於分類閾值的預測值爲-1
                    pred[X[:, i] < threshold] = -1
                    # 2.b 計算誤差率
                    error = sum(w[y != pred])
                    
                    # 如果分類誤差大於0.5,則進行正負預測翻轉
                    # 例如 error = 0.6 => (1 - error) = 0.4
                    if error > 0.5:
                        error = 1 - error
                        p = -1

                    # 一旦獲得最小誤差則保存相關參數配置
                    if error < min_error:
                        estimator.label = p
                        estimator.threshold = threshold
                        estimator.feature_index = i
                        min_error = error
                        
            # 2.c 計算基分類器的權重
            estimator.alpha = 0.5 * np.log((1.0 - min_error) / (min_error + 1e-9))
            # 初始化所有預測值爲1
            preds = np.ones(np.shape(y))
            # 獲取所有小於閾值的負類索引
            negative_idx = (estimator.label * X[:, estimator.feature_index] < estimator.label * estimator.threshold)
            # 將負類設爲 '-1'
            preds[negative_idx] = -1
            # 2.d 更新樣本權重
            w *= np.exp(-estimator.alpha * y * preds)
            w /= np.sum(w)

            # 保存該弱分類器
            self.estimators.append(estimator)
    
    # 定義預測函數
    def predict(self, X):
        m = len(X)
        y_pred = np.zeros((m, 1))
        # 計算每個弱分類器的預測值
        for estimator in self.estimators:
            # 初始化所有預測值爲1
            predictions = np.ones(np.shape(y_pred))
            # 獲取所有小於閾值的負類索引
            negative_idx = (estimator.label * X[:, estimator.feature_index] < estimator.label * estimator.threshold)
            # 將負類設爲 '-1'
            predictions[negative_idx] = -1
            # 2.e 對每個弱分類器的預測結果進行加權
            y_pred += estimator.alpha * predictions

        # 返回最終預測結果
        y_pred = np.sign(y_pred).flatten()
        return y_pred

數據測試

from sklearn.model_selection import train_test_split
# 導入sklearn模擬二分類數據生成模塊
from sklearn.datasets._samples_generator import make_blobs
# 生成模擬二分類數據集
X, y =  make_blobs(n_samples=150, n_features=2, centers=2,
  cluster_std=1.2, random_state=40)
# 將標籤轉換爲1/-1
y_ = y.copy()
y_[y_==0] = -1
y_ = y_.astype(float)
# 訓練/測試數據集劃分
X_train, X_test, y_train, y_test = train_test_split(X, y_,
 test_size=0.3, random_state=43)
# 設置顏色參數
colors = {0:'r', 1:'g'}
# 繪製二分類數據集的散點圖
plt.scatter(X[:,0], X[:,1], marker='o', c=pd.Series(y).map(colors))
plt.show();

自定義Adaboost模型測試

# 導入sklearn準確率計算函數
from sklearn.metrics import accuracy_score
# 創建Adaboost模型實例
clf = Adaboost(n_estimators=5)
# 模型擬合
clf.fit(X_train, y_train)
# 模型預測
y_pred = clf.predict(X_test)
# 計算模型預測準確率
accuracy = accuracy_score(y_test, y_pred)
print("Accuracy of AdaBoost by numpy:", accuracy)

Accuracy of AdaBoost by numpy: 0.9777777777777777

導入sklearn adaboost分類器測試

from sklearn.ensemble import AdaBoostClassifier
# 創建Adaboost模型實例
clf_ = AdaBoostClassifier(n_estimators=5, random_state=0)
# 模型擬合
clf_.fit(X_train, y_train)
# 模型預測
y_pred_ = clf_.predict(X_test)
# 計算模型預測準確率
accuracy = accuracy_score(y_test, y_pred_)
print("Accuracy of AdaBoost by sklearn:", accuracy)

Accuracy of AdaBoost by sklearn: 0.9777777777777777

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章