AdaBoost算法的理論--(1)

歷史上,Kearns和Valiant首先提出了“強可學習(strongly learnable)”和(弱可學習(weakly learnable)”的概念。指出:在概率近似正確(probably approximately
correct,PAC)學習的框架中,一個概念(一個類),如果存在一個多項式的學習
算法能夠學習它,並且正確率很高,那麼就稱這個概念是強可學習的;一個概念,
如果存在一個多項式的學習算法能夠學習它,學習的正確率僅比隨機猜測略好,那麼就稱這個概念是弱可學習的。非常有趣的是Schapire後來證明強可學習與弱可學習是等價的,也就是說,在PAC學習的框架下,一個概念是強可學習的充分
必要條件是這個概念是弱可學習的。

當做重要決定時,大家可能都會考慮吸取多個專家而不只是一個人的意見。這就元算法(meta-algorithm)背後的思路。元算法對其他算法進行組合的一種方式。接下來我們將集中關注一個稱AdaBoost的最流行的元算法。由於某些人認爲AdaBoost是最好的監督學習的方法,所以該方法是機器學習工具箱中最強有力的工具之一。

我們自然可以將不同的分類器組合起來,而這種組合結果則被稱爲集成方法(ensemble method)或者元算法(meta-algorithm)。使用集成方法時會有多種形式:

  • 不同算法集成
  • 同一算法在不同設置下的集成
  • 數據集不同部分分配給不同分器之後的集成

對於集成方法有兩個問題:
(1)在每一輪如何改變訓練數據的權重或概率分佈;
(2)如何將弱分類器組合成一個強分類器。

1.bagging:基於數據隨機重抽樣的分類器構建方法

自舉匯聚法(bootstrap aggregating),也稱爲bagging方法,是在從原始數據集選擇S次後得到S個新數據集的一種技術。新數據集和原數據集的大小相等。每個數據集都是通過在原始數據集中隨機選擇一個樣本來進行替換而得到的。這裏的替換就意味着可以多次地選擇同一樣本。這一性質就允許新數據集中可以有重複值,而原始數據集的某些值在新集合出現。

在S個數據集建好之後,將某個學習算法分別作用於每個數據集就得到了S個分 器。當我們要對新數據進行分類時,就可以應用這S個分類器進行分類。此同時,選擇分類器投票結果中最多的類別作爲最後的分類結果。
當然,還有一些更先進bagging方法,比如隨機森林(random forest)有關這些方法的一個很討論材參見網頁:
http://www.stat.berkeley.edu/~breiman/RandomForests/cc_home.htm。

2.boosting

提升方法是一種常用的統計學習方法,應用十分廣泛且有效。在分類問題中,它通過改變訓練樣本的權重,學習多個分類器,並將這些分類器進行線性組合,提高分類的性能。提升算法基於這樣一種思路:對於一個複雜任務來說,將多個專家的判斷進行適當的綜合所得出的判斷,要比其中任何一個專家獨斷的判斷好。實際上,就是“三個臭皮匠頂個諸葛亮”的道理。

歷史上,Kearns和Valiant首先提出了“強可學習(Strongly learnable)”和“弱可學習(Weekly learnable)”的概念。支出:在概率近似正確(probably approximately correct,PAC)學習框架中,
(1)一個概念(一個分類),如果存在一個多項式的學習算法能夠學習它,並且正確率很好,那麼就稱這個概念是強可學習的;
(2)一個概念(一個分類),如果存在一個多項式的學習算法能夠學習它,但學習的正確率僅比隨機猜測略好,那麼就稱這個概念是弱可學習的。
非常有趣的是Schapire後來證明強可學習與弱可學習是等價的,也就是說,在PAC學習框架下,一個概念是強可學習的充要條件是這個概念是弱可學習的。

這樣一來,問題便成爲,在學習中,如果已經發現了“弱學習算法”,那麼能否將它提升(boost)爲“強學習算法”。大家知道,發現弱學習算法通常要比發現強學習算法容易得多。那麼如何具體實施提升,便成爲開發提升方法時所要解決的問題。關於提升方法的研究很多,有很多算法被提出,最具代表性的是AdaBoost算法(Adaboost algorithm)。

對與分類問題而言,給定一個訓練樣本集,求比較粗糙的分類規則(弱分類器)要比求精確的分類規則(強分類器)容易得多。提升方法就是從弱學習算法出發,反覆學習,得到一系列分類器,然後組合這些分類器,構成一個強分類器。

這樣,對於提升算法來說,有兩個問題需要回答:一是在每一輪如何改變訓練數據的權值分佈;二是如何將弱分類器組合成一個強分類器。

Boosting算法要涉及到兩個部分,加法模型和前向分步算法。
(1) 加法模型就是說強分類器由一系列弱分類器線性相加而成。一般組合形式如下:
在這裏插入圖片描述
其中h(x,θm)h(x,\theta_m)是一個個的弱分類器,θm\theta_m是弱分類器學習到的最優參數;αm\alpha_m就是弱分類在強分類器中所佔的比重;P是所有θm\theta_mαm\alpha_m的組合。這些弱分類器線性相加組成強分類器。

(2) 前向分佈就是說在訓練過程中,下一輪迭代產生的分類器是在上一輪的基礎上訓練得來的。也就是可以協成這樣的形式:
在這裏插入圖片描述

由於採用的損失函數不同,Boosting算法也因此有了不同的類型,AdaBoost就是損失函數爲指數損失的Boosting算法。

boosting是一種與bagging很類似的技術。不論是在boosting還是bagging當中,所使用的多個分類器的類型都是一致的。但是在前者當中,不同的分類器是通過串行訓練而獲得的,每個新分器都根據已訓練出的分類器的性能來進行訓練。boosting是通過集中關注被已有分類器錯分的那些數據來獲得新的分類器。
由於boosting分類的結果是基於所有分類器的加權求和結果的,因此boosting與bagging不太一樣。bagging中的分類器權重是相等的,而boosting中的分類器權重並不相等,每個權重代表的是其對應分類器在上一輪迭代中的成功度。
boosting方法擁有多個版本,本章將只關注其中一個最流行的版本AdaBoost。

3.AdaBoost算法

AdaBoost是adaptive boosting(自適應boosting)的縮寫,其運行過程如下:
(1)訓練數據中的每個樣本,並賦予其一個權重,這些權重構成了向量D。一開始,這些權重都初始化成相等值。
(2)在訓練數據上訓練出一個弱分類器並計算該分類器的錯誤率,然後在同一數據集上再次訓練弱分類器。在分類器的第二次訓練當中,將會重新調整每個樣本的權重,其中第一次分對的樣本的權重將會降低,而第一次分錯的樣本的權重將會提高。
(3)爲了從所有弱分類器中得到最終的分類結果,AdaBoost爲每個分類器都分配了一個權重值alpha,這些alpha值是基於每個弱分類器的錯誤率進行計算的。其中,錯誤 定義爲:
ϵ=\epsilon=\frac{未正確分類的樣本數}{所有樣本數目}

alpha的計算公式如下:
α=12ln(1ϵϵ)\alpha=\frac12ln(\frac{1-\epsilon}{\epsilon})

Adaboost算法本身是通過改變數據分佈來實現的,它根據每次訓練集之中每個樣本的分類是否正確,以及上次的總體分類的準確率,來確定每個樣本的權值。將修改過權值的新數據集送給下層分類器進行訓練,最後將每次得到的分類器最後融合起來,作爲最後的決策分類器。

AdaBoost的算法流程:
左邊是數據集,其中直方圖的不同寬度表示每個樣例上的不同權重。在經過一個分類器之後,加權的預測結果會通過三角形中的alpha值進行加權。每個三角形 輸出的加權結果在圓形中求和,從而得到最終的輸出結果。
在這裏插入圖片描述

計算出alpha值之後,可以對權重向量D進行更新,以使得那些正確分類的樣本的權重降低而錯分樣本的權重升高。D的計算方法如下。
如果某個樣本被正確分類,那麼該樣本的權重更改爲
在這裏插入圖片描述
而如果某個樣本被錯分,那麼該樣本的權重更改爲:
在這裏插入圖片描述
在計算出D之後,AdaBoost又開始進入下一輪迭代。AdaBoost算法會不斷地重複訓練和調整權重的過程,直到訓練錯誤率爲0或者弱分類器的數目達到用戶的指定值爲止。

Adaboost算法思想

  1. 提高那些被前一輪弱分類器錯誤分類的樣本的權值,降低那些被正確分類的樣本的權值;
  2. 採用加權多數表決的方法。具體的,加大分類誤差率小的弱分類器的權值,使其在表決中起較大的作用;減小分類誤差率大的弱分類器的權值,使其在表決中起較小的作用。

Adaboost算法流程
(1)輸入:訓練數據集T={(x1,y1),(x2,y2),(x3,y3),...,(xn,yn)}T=\{(x_1,y_1),(x_2,y_2),(x_3,y_3),...,(x_n,y_n) \},其中,xiRnorXx_i \in R^n orXyiY={1,+1}y_i \in Y=\{-1,+1\}

(2)輸出:最終分類器Gm(x)G_m(x)
(3)初始化:假定第一次訓練時,樣本均勻分佈權值一樣。
在這裏插入圖片描述

其中,w1i=1/n,i=1,2,...,nw_{1i}=1/n,i=1,2,...,n

(4)循環:m=1,2,3…M,

(a)使用具有權值分佈DmD_m的訓練數據集學習,得到基本分類器GmG_m(選取讓誤差率最低的閾值來設計基本分類器):
Gm(x):{1,+1}G_m(x):\rightarrow \{-1,+1\}

(b)計算Gm(x)G_m(x)在訓練集上的分類誤差率eme_m,即分類錯誤的概率。
在這裏插入圖片描述
I(Gm(xi))yiI(G_m(x_i) )\neq y_i:當Gm(xi)G_m(x_i)yiy_i相等時,函數取值爲0;當與不相等時,取值爲1。
由上述式子可知,Gm(x)G_m(x)在訓練數據集上的誤差率eme_m就是被Gm(x)G_m(x)誤分類樣本的權值之和。

( c ) 計算Gm(x)G_m(x)的係數αm\alpha_mαm\alpha_m表示Gm(x)G_m(x)在最終分類器中的重要程度:
在這裏插入圖片描述

【注】顯然em1/2e_m \leq1/2時,αm0\alpha_m \geq 0,且αm\alpha_m隨着eme_m的減小而增大,意味着分類誤差率越小的基本分類器在最終分類器中的作用越大
此時分類器爲:
fm(x)=αmGm(x)f_m(x)=\alpha_mG_m(x)

(d)更新訓練數據集的權值分佈,用於下一輪迭代。
在這裏插入圖片描述
在這裏插入圖片描述
其中ZmZ_m是規範化因子,使得Dm+1D_{m+1}成爲一個概率分佈。

在這裏插入圖片描述

(5)循環結束條件:

eme_m小於某個閾值(一般是0.5),或是達到最大迭代次數。

AdaBoost 方法中使用的分類器可能很弱(比如出現很大錯誤率),但只要它的分類效果比隨機好一點(比如兩類問題分類錯誤率略小於0.5),就能夠改善最終得到的模型。

(6)組合分類器:
在這裏插入圖片描述

(7)最終分類器:
在這裏插入圖片描述

4.AdaBoost例子

假定給出下列訓練樣本。
序號 i 1 2 3 4 5 6 7 8 9 10
數據 x 0 1 2 3 4 5 6 7 8 9
類別標籤 y 1 1 1 -1 -1 -1 1 1 1 -1

4.1.初始化

初始化:w1i=1/n=0.1w_{1i}=1/n=0.1,n=10(樣本個數)
序號 i 1 2 3 4 5 6 7 8 9 10
數據 x 0 1 2 3 4 5 6 7 8 9
類別標籤 y 1 1 1 -1 -1 -1 1 1 1 -1
初始權值 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1
閾值猜測:觀察數據,可以發現數據分爲兩類:-1和1,其中數據“0,1,2”對應“1”類,數據“3,4,5”對應“-1”類,數據“6,7,8”對應“1”類,數據“9”對應“"1”類。拋開單個的數據“9”,可以找到對應的數據分界點(即可能的閾值),比如:2.5、5.5、8.5(一般0.5的往上加,也可以是其他數)。然後計算每個點的誤差率,選最小的那個作爲閾值。

但在實際操作中,可以每個數據點都可以作爲閾值,然後計算誤差率,保留誤差率最小的那個值。若誤差率有大於0.5的就取反(分類換一下,若大於取1,取反後就小於取1),再計算誤差率。

4.2.迭代過程1:m=1

閾值:變量的取值範圍。閾值的確定標準:誤差率小的閾值。

(1)確定閾值的取值及誤差率

  • 當閾值取2.5時,誤差率爲0.3。即x<2.5 時取1,x>2.5 時取 -1,則數據6、7、8分錯,誤差率爲0.3(簡單理解:10個裏面3個錯的,真正誤差率計算看下面的表格 )
    當閾值取5.5時,誤差率最低爲0.4。即 x<5.5 時取1,x>5.5 時取 -1,則數據3、4、5、6、7、8分錯,錯誤率爲0.6>0.5,故反過來,令x>5.5 取1,x<5.5 時取 -1,則數據0、1、2、9分錯,誤差率爲0.4。
  • 當閾值取8.5時,誤差率爲0.3。即 x<8.5 時取1,x>8.5 時取 -1,則數據3、4、5分錯,錯誤率爲0.3

由上面可知,閾值取2.5 或8.5時,誤差率一樣,所以可以任選一個作爲基本分類器。這裏選2.5爲例。
在這裏插入圖片描述

計算誤差率:
序號 i 1 2 3 4 5 6 7 8 9 10
數據 x 0 1 2 3 4 5 6 7 8 9
類別標籤 y 1 1 1 -1 -1 -1 1 1 1 -1
分類器結果 1 1 1 -1 -1 -1 -1 -1 -1 -1
分類結果 對 對 對 對 對 對 錯 錯 錯 對

從上可得G1(x)G_1(x)在訓練數據集上的誤差率(被分錯類的樣本的權值之和):
在這裏插入圖片描述
(2)計算的係數:
在這裏插入圖片描述
這個代表G1(x)G_1(x)在最終的分類函數中所佔的比重約爲0.42365

(3)分類函數
在這裏插入圖片描述

(4)更新權值分佈:
3個G1(x)G_1(x)爲1,其他爲-1,
在這裏插入圖片描述
序號 i 1 2 3 4 5 6 7 8 9 10
數據 x 0 1 2 3 4 5 6 7 8 9
類別標籤 y 1 1 1 -1 -1 -1 1 1 1 -1
初始權值1 w1i 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1
更新權值2 w2i 0.07143 0.07143 0.07143 0.07143 0.07143 0.07143 0.16666 0.16666 0.16666 0.07143

由上面可以看出,因爲數據“6,7,8”被分錯了,所以它們的權值由初始的0.1增大到了0.16666;其他的數據由於被分對了,所以權值由0.1減小到0.07143。

4.1.3.迭代過程2:m=2

(1)確定閾值的取值及誤差率

  • 當閾值取2.5時,誤差率爲0.49998。即 x<2.5 時取 1,x>2.5 時取 -1,則數據6、7、8分錯,誤差率爲0.16666*3(取過,不列入考慮範圍)
  • 當閾值取5.5時,誤差率最低爲0.28572。即 x<5.5 時取1,x>5.5 時取 -1,則數據3、4、5、6、7、8分錯,錯誤率爲0.071433+0.166663=0.71427>0.5,故反過來,令 x>5.5 取 1,x<5.5 時取 -1,則數據0、1、2、9分錯,誤差率爲0.07143*4=0.28572
  • 當閾值取8.5時,誤差率爲0.21429。即 x<8.5 時取1,x>8.5 時取 -1,則數據3、4、5分錯,錯誤率爲0.07143*3=0.21429
    由上面可知,閾值取8.5時,誤差率最小,所以:
    在這裏插入圖片描述

計算誤差率:

序號 i 1 2 3 4 5 6 7 8 9 10
數據 x 0 1 2 3 4 5 6 7 8 9
類別標籤 y 1 1 1 -1 -1 -1 1 1 1 -1
權值分佈  w2i 0.07143 0.07143 0.07143 0.07143 0.07143 0.07143 0.16666 0.16666 0.16666 0.07143
分類器結果 G2(x) 1 1 1 1 1 1 1 1 1 -1
分類結果 對 對 對 錯 錯 錯 對 對 對 對

從上可得G2(x)G_2(x)在訓練數據集上的誤差率(被分錯類的樣本的權值之和):
在這裏插入圖片描述
(2)計算G2(x)G_2(x)的係數:
在這裏插入圖片描述
這個α2\alpha_2代表G2(x)G_2(x)在最終的分類函數中所佔的比重約爲0.649263

(3)分類函數
在這裏插入圖片描述

(4)更新權值分佈:

在這裏插入圖片描述

序號 i 1 2 3 4 5 6 7 8 9 10
數據 x 0 1 2 3 4 5 6 7 8 9
類別標籤 y 1 1 1 -1 -1 -1 1 1 1 -1
初始權值1 w1i 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1
權值2 w
2i 0.07143 0.07143 0.07143 0.07143 0.07143 0.07143 0.16666 0.16666 0.16666 0.07143
更新權值3 w3i 0.04546 0.04546 0.04546 0.16667 0.16667 0.16667 0.10606 0.10606 0.10606 0.04546

4.1.4.迭代過程3:m=3

(1)確定閾值的取值及誤差率

  • 當閾值取2.5時,誤差率爲0.31818。即 x<2.5 時取 1,x>2.5 時取 -1,則數據6、7、8分錯,誤差率爲0.10606*3=0.31818(取過,不列入考慮範圍)
  • 當閾值取5.5時,誤差率最低爲0.18184。即 x<5.5 時取1,x>5.5 時取 -1,則數據3、4、5、6、7、8分錯,錯誤率爲0.166673+0.106063=0.81819>0.5,故反過來,令x>5.5 取 1,x<5.5 時取 -1,則數據0、1、2、9分錯,誤差率爲0.04546*4=0.18184
  • 當閾值取8.5時,誤差率爲0.13638。即 x<8.5 時取1,x>8.5 時取 -1,則數據3、4、5分錯,錯誤率爲0.04546*3=0.13638(取過,不列入考慮範圍)
    由上面可知,閾值取8.5時,誤差率最小,但8.5取過了,所以取5.5:
    在這裏插入圖片描述

計算誤差率:

序號 i 1 2 3 4 5 6 7 8 9 10
數據 x 0 1 2 3 4 5 6 7 8 9
類別標籤 y 1 1 1 -1 -1 -1 1 1 1 -1
權值分佈  w3i 0.04546 0.04546 0.04546 0.16667 0.16667 0.16667 0.10606 0.10606 0.10606 0.04546
分類器結果 G3(x) -1 -1 -1 -1 -1 -1 1 1 1 1
分類結果 錯 錯 錯 對 對 對 對 對 對 錯

從上可得G3(x)G_3(x)在訓練數據集上的誤差率(被分錯類的樣本的權值之和):
在這裏插入圖片描述

(2)計算G3(x)G_3(x)的係數:
在這裏插入圖片描述
這個α3\alpha_3代表G3(x)G_3(x)在最終的分類函數中所佔的比重約爲0.75197。

(3)分類函數
在這裏插入圖片描述

(4)更新權值分佈:

在這裏插入圖片描述

序號 i 1 2 3 4 5 6 7 8 9 10
數據 x 0 1 2 3 4 5 6 7 8 9
類別標籤 y 1 1 1 -1 -1 -1 1 1 1 -1
初始權值1 w1i 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1
權值2 w2i 0.07143 0.07143 0.07143 0.07143 0.07143 0.07143 0.16666 0.16666 0.16666 0.07143
權值3 w3i 0.04546 0.04546 0.04546 0.16667 0.16667 0.16667 0.10606 0.10606 0.10606 0.04546
更新權值4 w4i 0.12500 0.12500 0.12500 0.10185 0.10185 0.10185 0.06481 0.06481 0.06481 0.12500

4.1.5.迭代過程4:m=4

此時觀察數據,每次迭代被分錯的數據都已經重新分配過權值,按其他參考資料來說,此時的誤差率爲0,所以迭代可以到此結束。

最終分類器:
在這裏插入圖片描述

6.代碼

單層決策樹(decision stump , 也稱決策樹樁)是一種簡單的決策樹。前面我們已經介紹決策樹的工作原理,接下來將構建一個單層決策樹,而它僅基於單個特徵來做決策。由於這棵樹只有一次分裂過程,因此它實際上就是一個樹樁。

完整AdaBoost算法的實現

6.1.簡單數據

# coding: utf-8
# Author: shelley
# 2020/5/1311:42
# -*- coding: utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt


def loadsimpData():
    """
    創建單層決策樹的數據集
    :return:
    dataMat - 數據矩陣
    classLabels - 數據標籤
    """
    datMat = np.matrix([[1., 2.1],
                        [1.5, 1.6],
                        [1.3, 1.],
                        [1., 1.],
                        [2., 1.]])
    classLabels = [1.0, 1.0, -1.0, -1.0, 1.0]
    return datMat, classLabels


def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
    """
    單層決策樹分類函數
    :param dataMatrix: 數據矩陣
    :param dimen: 第dimen列,也就是第幾個特徵
    :param threshVal: 閾值
    :param threshIneq: 標誌
    :return: 分類結果
    """
    # 初始化retArray爲全1列向量
    retArray = np.ones((np.shape(dataMatrix)[0], 1))
    if threshIneq == 'lt':
        # 如果小於閾值則賦值爲-1
        retArray[dataMatrix[:, dimen] <= threshVal] = -1.0
    else:
        # 如果大於閾值則賦值爲-1
        retArray[dataMatrix[:, dimen] > threshVal] = -1.0
    return retArray


def buildStump(dataArr, classLabels, D):
    """
    找到數據集上最佳的單層決策樹
    :param dataArr: 數據矩陣
    :param classLabels: 數據標籤
    :param D: 樣本權重,每個樣本權重相等 1/n
    :return:
    bestStump - 最佳單層決策樹信息
    minError - 最小誤差
    bestClassEst - 最佳的分類結果
    """
    # 輸入數據轉爲矩陣(5, 2)
    dataMatrix = np.mat(dataArr)
    # 將標籤矩陣進行轉置(5, 1)
    labelMat = np.mat(classLabels).T
    # m=5, n=2
    m, n = np.shape(dataMatrix)
    numSteps = 10.0
    bestStump = {}
    # (5, 1)全零列矩陣
    bestClasEst = np.mat(np.zeros((m, 1)))
    # 最小誤差初始化爲正無窮大inf
    minError = float('inf')
    # 遍歷所有特徵
    for i in range(n):  # 遍歷每個特徵
        # 找到(每列)特徵中的最小值和最大值
        rangeMin = dataMatrix[:, i].min()
        rangeMax = dataMatrix[:, i].max()
        # 計算特徵的步長
        stepSize = (rangeMax - rangeMin) / numSteps
        for j in range(-1, int(numSteps) + 1):
            # 大於和小於的情況均遍歷,lt:Less than  gt:greater than
            for inequal in ['lt', 'gt']:
                # 計算特徵的閾值
                threshVal = (rangeMin + float(j) * stepSize)
                # 計算分類結果
                predictedVals = stumpClassify(dataMatrix, i, threshVal, inequal)
                # 初始化誤差矩陣
                errArr = np.mat(np.ones((m, 1)))
                # 分類正確的,賦值爲0
                errArr[predictedVals == labelMat] = 0
                # 計算誤差
                weightedError = D.T * errArr
                print("分裂: 維度:%d, 閾值:%.2f, 閾值不等類別: %s, 權重錯誤是:%.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


def adaBoostTrainDS(dataArr, classLabels, numIt=40):
    """
    使用AdaBoost進行優化
    :param dataArr: 數據矩陣
    :param classLabels: 數據標籤
    :param numIt: 最大迭代次數
    :return:
    weakClassArr - 存儲單層決策樹的list
    aggClassEsc - 訓練的label
    """
    weakClassArr = []
    # 獲取數據集的行數
    m = np.shape(dataArr)[0]
    # 樣本權重,每個樣本權重相等,即1/m
    D = np.mat(np.ones((m, 1)) / m)
    # 初始化爲全零列
    aggClassEst = np.mat(np.zeros((m, 1)))
    # 迭代
    for i in range(numIt):
        # 構建單層決策樹
        bestStump, error, classEst = buildStump(dataArr, classLabels, D)
        # print("D:", D.T)
        # 計算弱學習算法權重alpha,使error不等於0,因爲分母不能爲0
        alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16)))
        # 存儲弱學習算法權重
        bestStump['alpha'] = alpha
        # 存儲單層決策樹
        weakClassArr.append(bestStump)
        # 打印最佳分類結果
        # print("classEst: ", classEst.T)
        # 計算e的指數項
        expon = np.multiply(-1 * alpha * np.mat(classLabels).T, classEst)
        # 計算遞推公式的分子
        D = np.multiply(D, np.exp(expon))
        # 根據樣本權重公式,更新樣本權重
        D = D / D.sum()
        # 計算AdaBoost誤差,當誤差爲0的時候,退出循環
        # 以下爲錯誤率累計計算
        aggClassEst += alpha * classEst
        # print("aggClassEst: ", aggClassEst.T)
        # 計算誤差
        aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(classLabels).T, np.ones((m, 1)))
        errorRate = aggErrors.sum() / m
        # print("total error:", errorRate)
        if errorRate == 0.0:
            # 誤差爲0退出循環
            break
    return weakClassArr, aggClassEst

def adaClassify(datToClass, classifierArr):
    """
    AdaBoost分類函數
    :param datToClass: 待分類樣例
    :param classifierArr: 訓練好的分類器
    :return: 分類結果
    """
    dataMatrix = np.mat(datToClass)
    m = np.shape(dataMatrix)[0]
    aggClassEst = np.mat(np.zeros((m, 1)))
    for i in range(len(classifierArr)):
        # 遍歷所有分類器進行分類
        classEst = stumpClassify(dataMatrix, classifierArr[i]['dim'], classifierArr[i]['thresh'],
                                 classifierArr[i]['ineq'])
        aggClassEst += classifierArr[i]['alpha'] * classEst
        print(aggClassEst)
    return np.sign(aggClassEst)


def showDataSet(dataMat, labelMat):
    """
    數據可視化
    :param dataMat: 數據矩陣
    :param labelMat: 數據標籤
    :return:
    """
    # 正樣本
    data_plus = []
    # 負樣本
    data_minus = []
    for i in range(len(dataMat)):
        if labelMat[i] > 0:
            data_plus.append(dataMat[i])
        else:
            data_minus.append(dataMat[i])
    # 轉換爲numpy矩陣
    data_plus_np = np.array(data_plus)
    # 轉換爲numpy矩陣
    data_minus_np = np.array(data_minus)
    # 正樣本散點圖(scatter)
    # transpose轉置
    plt.scatter(np.transpose(data_plus_np)[0], np.transpose(data_plus_np)[1])
    # 負樣本散點圖(scatter)
    plt.scatter(np.transpose(data_minus_np)[0], np.transpose(data_minus_np)[1])
    # 顯示
    plt.show()


if __name__ == '__main__':
    dataArr, classLabels = loadsimpData()
    weakClassArr, aggClassEst = adaBoostTrainDS(dataArr, classLabels)
    print(adaClassify([[0, 0], [5, 5]], weakClassArr))
    # print('weakClassArr:\n', weakClassArr)
    # print('aggClassEst:\n', aggClassEst)
    # showDataSet(dataArr, classLabels)

6.2.sklearn

# coding: utf-8
# Author: shelley
# 2020/5/1311:43
# -*- coding: utf-8 -*-


import numpy as np
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier


def loadDataSet(fileName):
    """
    加載文件
    :param fileName: 文件名
    :return:
    dataMat - 數據矩陣
    labelMat - 數據標籤
    """
    # 特徵個數
    numFeat = len((open(fileName).readline().split('\t')))
    dataMat = []
    labelMat = []
    fr = open(fileName)
    for line in fr.readlines():
        lineArr = []
        curLine = line.strip().split('\t')
        for i in range(numFeat - 1):
            lineArr.append(float(curLine[i]))
        dataMat.append(lineArr)
        labelMat.append(float(curLine[-1]))
    return dataMat, labelMat


from sklearn.svm import SVC

if __name__ == '__main__':
    dataArr, classLabels = loadDataSet('horseColicTraining2.txt')
    testArr, testLabelArr = loadDataSet('horseColicTest2.txt')
    # bdt = AdaBoostClassifier(DecisionTreeClassifier(max_depth=2), algorithm="SAMME", n_estimators=10)
    bdt = AdaBoostClassifier(SVC(C=200.0, kernel='rbf'), algorithm="SAMME", n_estimators=10)
    bdt.fit(dataArr, classLabels)
    predictions = bdt.predict(dataArr)
    errArr = np.mat(np.zeros((len(dataArr), 1)))
    for i in range(len(classLabels)):
        if classLabels[i] != predictions[i]:
            errArr[i] = 1
    print('訓練集的錯誤率:%.3f%%' % float(errArr.sum() / len(dataArr) * 100))
    predictions = bdt.predict(testArr)
    errArr = np.mat(np.zeros((len(testArr), 1)))
    for i in range(len(testLabelArr)):
        if testLabelArr[i] != predictions[i]:
            errArr[i] = 1
    print('測試集的錯誤率:%.3f%%' % float(errArr.sum() / len(testArr) * 100))

# 訓練集的錯誤率:0.334%
# 測試集的錯誤率:28.358%

7.代碼和數據

鏈接:https://pan.baidu.com/s/1QbeG8g_9JhVwJRhERViTMg
提取碼:80su

github:
https://github.com/shelleyHLX/machine-learning

8.參考

機器學習實戰
一些博客,如有侵權,請聯繫。
https://www.cnblogs.com/liuq/p/9927580.html

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