scikit-learn Adaboost類庫使用小結

劉建平Pinard發表於2016-12-06

    在整合學習之Adaboost演算法原理小結中,我們對Adaboost的演算法原理做了一個總結。這裡我們就從實用的角度對scikit-learn中Adaboost類庫的使用做一個小結,重點對調參的注意事項做一個總結。

1. Adaboost類庫概述

    scikit-learn中Adaboost類庫比較直接,就是AdaBoostClassifier和AdaBoostRegressor兩個,從名字就可以看出AdaBoostClassifier用於分類,AdaBoostRegressor用於迴歸。

    AdaBoostClassifier使用了兩種Adaboost分類演算法的實現,SAMME和SAMME.R。而AdaBoostRegressor則使用了我們原理篇裡講到的Adaboost迴歸演算法的實現,即Adaboost.R2。

    當我們對Adaboost調參時,主要要對兩部分內容進行調參,第一部分是對我們的Adaboost的框架進行調參, 第二部分是對我們選擇的弱分類器進行調參。兩者相輔相成。下面就對Adaboost的兩個類:AdaBoostClassifier和AdaBoostRegressor從這兩部分做一個介紹。

2. AdaBoostClassifier和AdaBoostRegressor框架引數

    我們首先來看看AdaBoostClassifier和AdaBoostRegressor框架引數。兩者大部分框架引數相同,下面我們一起討論這些引數,兩個類如果有不同點我們會指出。

 

    1)base_estimator:AdaBoostClassifier和AdaBoostRegressor都有,即我們的弱分類學習器或者弱迴歸學習器。理論上可以選擇任何一個分類或者回歸學習器,不過需要支援樣本權重。我們常用的一般是CART決策樹或者神經網路MLP。預設是決策樹,即AdaBoostClassifier預設使用CART分類樹DecisionTreeClassifier,而AdaBoostRegressor預設使用CART迴歸樹DecisionTreeRegressor。另外有一個要注意的點是,如果我們選擇的AdaBoostClassifier演算法是SAMME.R,則我們的弱分類學習器還需要支援概率預測,也就是在scikit-learn中弱分類學習器對應的預測方法除了predict還需要有predict_proba。

 

    2)algorithm:這個引數只有AdaBoostClassifier有。主要原因是scikit-learn實現了兩種Adaboost分類演算法,SAMME和SAMME.R。兩者的主要區別是弱學習器權重的度量,SAMME使用了和我們的原理篇裡二元分類Adaboost演算法的擴充套件,即用對樣本集分類效果作為弱學習器權重,而SAMME.R使用了對樣本集分類的預測概率大小來作為弱學習器權重。由於SAMME.R使用了概率度量的連續值,迭代一般比SAMME快,因此AdaBoostClassifier的預設演算法algorithm的值也是SAMME.R。我們一般使用預設的SAMME.R就夠了,但是要注意的是使用了SAMME.R, 則弱分類學習器引數base_estimator必須限制使用支援概率預測的分類器。SAMME演算法則沒有這個限制。

 

    3)loss:這個引數只有AdaBoostRegressor有,Adaboost.R2演算法需要用到。有線性‘linear’, 平方‘square’和指數 ‘exponential’三種選擇, 預設是線性,一般使用線性就足夠了,除非你懷疑這個引數導致擬合程度不好。這個值的意義在原理篇我們也講到了,它對應了我們對第k個弱分類器的中第i個樣本的誤差的處理,即:如果是線性誤差,則$e_{ki}= \frac{|y_i - G_k(x_i)|}{E_k}$;如果是平方誤差,則$e_{ki}= \frac{(y_i - G_k(x_i))^2}{E_k^2}$,如果是指數誤差,則$e_{ki}= 1 - exp(\frac{-y_i + G_k(x_i))}{E_k})$,$E_k$為訓練集上的最大誤差$E_k= max|y_i - G_k(x_i)|\;i=1,2...m$

 

     4) n_estimators: AdaBoostClassifier和AdaBoostRegressor都有,就是我們的弱學習器的最大迭代次數,或者說最大的弱學習器的個數。一般來說n_estimators太小,容易欠擬合,n_estimators太大,又容易過擬合,一般選擇一個適中的數值。預設是50。在實際調參的過程中,我們常常將n_estimators和下面介紹的引數learning_rate一起考慮。

 

    5) learning_rate:  AdaBoostClassifier和AdaBoostRegressor都有,即每個弱學習器的權重縮減係數$\nu$,在原理篇的正則化章節我們也講到了,加上了正則化項,我們的強學習器的迭代公式為$f_{k}(x) = f_{k-1}(x) + \nu\alpha_kG_k(x) $。$\nu$的取值範圍為$0 < \nu \leq 1 $。對於同樣的訓練集擬合效果,較小的$\nu$意味著我們需要更多的弱學習器的迭代次數。通常我們用步長和迭代最大次數一起來決定演算法的擬合效果。所以這兩個引數n_estimators和learning_rate要一起調參。一般來說,可以從一個小一點的$\nu$開始調參,預設是1。

 

3. AdaBoostClassifier和AdaBoostRegressor弱學習器引數

    這裡我們再討論下AdaBoostClassifier和AdaBoostRegressor弱學習器引數,由於使用不同的弱學習器,則對應的弱學習器引數各不相同。這裡我們僅僅討論預設的決策樹弱學習器的引數。即CART分類樹DecisionTreeClassifier和CART迴歸樹DecisionTreeRegressor。

    DecisionTreeClassifier和DecisionTreeRegressor的引數基本類似,在scikit-learn決策樹演算法類庫使用小結這篇文章中我們對這兩個類的引數做了詳細的解釋。這裡我們只拿出調引數時需要尤其注意的最重要幾個的引數再拿出來說一遍:

    1) 劃分時考慮的最大特徵數max_features: 可以使用很多種型別的值,預設是"None",意味著劃分時考慮所有的特徵數;如果是"log2"意味著劃分時最多考慮$log_2N$個特徵;如果是"sqrt"或者"auto"意味著劃分時最多考慮$\sqrt{N}$個特徵。如果是整數,代表考慮的特徵絕對數。如果是浮點數,代表考慮特徵百分比,即考慮(百分比xN)取整後的特徵數。其中N為樣本總特徵數。一般來說,如果樣本特徵數不多,比如小於50,我們用預設的"None"就可以了,如果特徵數非常多,我們可以靈活使用剛才描述的其他取值來控制劃分時考慮的最大特徵數,以控制決策樹的生成時間。

    2) 決策樹最大深max_depth: 預設可以不輸入,如果不輸入的話,決策樹在建立子樹的時候不會限制子樹的深度。一般來說,資料少或者特徵少的時候可以不管這個值。如果模型樣本量多,特徵也多的情況下,推薦限制這個最大深度,具體的取值取決於資料的分佈。常用的可以取值10-100之間。

    3) 內部節點再劃分所需最小樣本數min_samples_split: 這個值限制了子樹繼續劃分的條件,如果某節點的樣本數少於min_samples_split,則不會繼續再嘗試選擇最優特徵來進行劃分。 預設是2.如果樣本量不大,不需要管這個值。如果樣本量數量級非常大,則推薦增大這個值。

    4) 葉子節點最少樣本數min_samples_leaf: 這個值限制了葉子節點最少的樣本數,如果某葉子節點數目小於樣本數,則會和兄弟節點一起被剪枝。 預設是1,可以輸入最少的樣本數的整數,或者最少樣本數佔樣本總數的百分比。如果樣本量不大,不需要管這個值。如果樣本量數量級非常大,則推薦增大這個值。

    5)葉子節點最小的樣本權重和min_weight_fraction_leaf:這個值限制了葉子節點所有樣本權重和的最小值,如果小於這個值,則會和兄弟節點一起被剪枝。 預設是0,就是不考慮權重問題。一般來說,如果我們有較多樣本有缺失值,或者分類樹樣本的分佈類別偏差很大,就會引入樣本權重,這時我們就要注意這個值了。

    6) 最大葉子節點數max_leaf_nodes: 通過限制最大葉子節點數,可以防止過擬合,預設是"None”,即不限制最大的葉子節點數。如果加了限制,演算法會建立在最大葉子節點數內最優的決策樹。如果特徵不多,可以不考慮這個值,但是如果特徵分成多的話,可以加以限制,具體的值可以通過交叉驗證得到。

 

4. AdaBoostClassifier實戰

    這裡我們用一個具體的例子來講解AdaBoostClassifier的使用。

    完整程式碼參見我的github: https://github.com/ljpzzz/machinelearning/blob/master/ensemble-learning/adaboost-classifier.ipynb

    首先我們載入需要的類庫:

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import make_gaussian_quantiles

    接著我們生成一些隨機資料來做二元分類,如果對如何產生隨機資料不熟悉,在另一篇文章機器學習演算法的隨機資料生成中有比較詳細的介紹。

# 生成2維正態分佈,生成的資料按分位數分為兩類,500個樣本,2個樣本特徵,協方差係數為2
X1, y1 = make_gaussian_quantiles(cov=2.0,n_samples=500, n_features=2,n_classes=2, random_state=1)
# 生成2維正態分佈,生成的資料按分位數分為兩類,400個樣本,2個樣本特徵均值都為3,協方差係數為2
X2, y2 = make_gaussian_quantiles(mean=(3, 3), cov=1.5,n_samples=400, n_features=2, n_classes=2, random_state=1)
#講兩組資料合成一組資料
X = np.concatenate((X1, X2))
y = np.concatenate((y1, - y2 + 1))

    我們通過視覺化看看我們的分類資料,它有兩個特徵,兩個輸出類別,用顏色區別。

plt.scatter(X[:, 0], X[:, 1], marker='o', c=y)

    輸出為下圖:

    可以看到資料有些混雜,我們現在用基於決策樹的Adaboost來做分類擬合。

bdt = AdaBoostClassifier(DecisionTreeClassifier(max_depth=2, min_samples_split=20, min_samples_leaf=5),
                         algorithm="SAMME",
                         n_estimators=200, learning_rate=0.8)
bdt.fit(X, y)

    這裡我們選擇了SAMME演算法,最多200個弱分類器,步長0.8,在實際運用中你可能需要通過交叉驗證調參而選擇最好的引數。擬合完了後,我們用網格圖來看看它擬合的區域。

x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
                     np.arange(y_min, y_max, 0.02))

Z = bdt.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
cs = plt.contourf(xx, yy, Z, cmap=plt.cm.Paired)
plt.scatter(X[:, 0], X[:, 1], marker='o', c=y)
plt.show()

    輸出的圖如下:

    從圖中可以看出,Adaboost的擬合效果還是不錯的,現在我們看看擬合分數:

print "Score:", bdt.score(X,y)

    輸出為:

Score: 0.913333333333

    也就是說擬合訓練集資料的分數還不錯。當然分數高並不一定好,因為可能過擬合。

    現在我們將最大弱分離器個數從200增加到300。再來看看擬合分數。

bdt = AdaBoostClassifier(DecisionTreeClassifier(max_depth=2, min_samples_split=20, min_samples_leaf=5),
                         algorithm="SAMME",
                         n_estimators=300, learning_rate=0.8)
bdt.fit(X, y)
print "Score:", bdt.score(X,y)

    此時的輸出為:

Score: 0.962222222222

    這印證了我們前面講的,弱分離器個數越多,則擬合程度越好,當然也越容易過擬合。

    現在我們降低步長,將步長從上面的0.8減少到0.5,再來看看擬合分數。

bdt = AdaBoostClassifier(DecisionTreeClassifier(max_depth=2, min_samples_split=20, min_samples_leaf=5),
                         algorithm="SAMME",
                         n_estimators=300, learning_rate=0.5)
bdt.fit(X, y)
print "Score:", bdt.score(X,y)

    此時的輸出為:

Score: 0.894444444444

    可見在同樣的弱分類器的個數情況下,如果減少步長,擬合效果會下降。

    最後我們看看當弱分類器個數為700,步長為0.7時候的情況:

bdt = AdaBoostClassifier(DecisionTreeClassifier(max_depth=2, min_samples_split=20, min_samples_leaf=5),
                         algorithm="SAMME",
                         n_estimators=600, learning_rate=0.7)
bdt.fit(X, y)
print "Score:", bdt.score(X,y)

    此時的輸出為:

Score: 0.961111111111

    此時的擬合分數和我們最初的300弱分類器,0.8步長的擬合程度相當。也就是說,在我們這個例子中,如果步長從0.8降到0.7,則弱分類器個數要從300增加到700才能達到類似的擬合效果。

 

    以上就是scikit-learn Adaboost類庫使用的一個總結,希望可以幫到朋友們。

 

(歡迎轉載,轉載請註明出處。歡迎溝通交流: liujianping-ok@163.com) 

相關文章