使用決策樹算法,咱們從樹根開始,基於可得到最大信息增益(information gain,IG)的特徵來對數據進行劃分,咱們將在下一節詳細介紹信息增益的概念。
經過迭代處理,在每一個子節點上重複此劃分過程,直到葉子節點。這意味着在每個節點處,全部的樣本都屬於同一類別。
在實際應用中,這可能會致使生成一棵深度很大且擁有衆多節點的樹,這樣容易產生過擬合問題,由此,咱們通常經過對樹進行「剪枝」來限定樹的最大深度。
最大化信息增益——獲知儘量準確的結果
爲了在可得到最大信息增益的特徵處進行節點劃分,須要定義一個可經過樹學習算法進行優化的目標函數。
在此,目標函數可以在每次劃分時實現對信息增益的最大化。node
信息增益只不過是父節點的不純度與全部子節點不純度總和之差——子節點的不純度越低,信息增益越大。
然而,出於簡化和縮小組合搜索空間的考慮,大多數(包括scikit-learn)庫中實現的都是二叉決策樹。
這意味着每一個父節點被劃分爲兩個子節點:Dleft和Dright。
就目前來講,二叉決策樹中經常使用的三個不純度衡量標準或劃分標準分別是:
基尼係數(Gini index,IG)、熵(entropy,IH),以及誤分類率(classification error,IE)。
熵:
若是某一節點中全部的樣本都屬於同一類別,則其熵爲0,當樣本以相同的比例分屬於不一樣的類時,熵的值最大。
對二類別分類來講,當p(i=1|t)=1或p(i=0|t)=0時,熵爲0,若是類別均勻分佈,即p(i=1|t)=0.5且p(i=1|t)=0.5時,熵爲1。
在決策樹中熵的準則就是使得互信息最大化。
基尼係數
能夠理解爲下降誤分類可能性的標準
與熵相似,當全部類別是等比例分佈時,基尼係數的值最大
# 在實踐中,基尼係數與熵一般會生成很是相似的結果,並不值得花費大量時間使用不純度標準評估樹的好壞,而一般嘗試使用不一樣的剪枝算法。
誤分類率:
這是一個對於剪枝方法頗有用的準則,可是不建議用於決策樹的構建過程,由於它對節點中各種別樣本數量的變更不敏感。
使用誤分類率獲得的信息增益都是相同,在使用基尼係數時,與劃分A(IGG=0.125)相比,更傾向於使用B(IGG=0.16)的劃分,
由於這樣子節點處的類別純度相對更高
熵的純度系統最高
那麼你到底應該使用基尼不純度仍是信息熵呢?其實,大多數狀況下,它們並無什麼大的不一樣,產生的樹都很類似。
基尼不純度的計算速度略微快一些,因此它是個不錯的默認選擇。
它們的不一樣在於,基尼不純度傾向於從樹枝中分裂出最多見的類別,而信息熵則傾向於生產更平衡的樹。算法
決策樹能夠經過將特徵空間進行矩形劃分的方式來構建複雜的決策邊界。然而,必須注意:深度越大的決策樹,決策邊界也就越複雜,於是很容易產生過擬合現象。
scikit-learn一個吸引人的功能就是,它容許將訓練後獲得的決策樹導出爲.dot格式的文件,這使得咱們可使用GraphViz程序進行可視化處理。
經過觀察GraphViz建立的圖像,能夠很好地回溯決策樹在訓練數據集上對各節點進行劃分的過程。
from sklearn.tree import export_graphviz
export_graphviz(
tree_clf,
out_file=image_path("iris_tree.dot"),
feature_names=iris.feature_names[2:],
class_names=iris.target_names,
rounded=True,
filled=True
)
dot -Tpng iris_tree.dot -o iris_tree.png
決策樹的衆多特性之一就是, 它不須要太多的數據預處理, 尤爲是不須要進行特徵的縮放或者歸一化。
估計分類機率:
決策樹還能夠估計某個實例屬於特定類 k 的機率:首先遍歷樹來查找此實例的葉節點,而後它返回此節點中類 k 的訓練實例的比例。
CART 訓練算法:
Scikit-Learn 用分裂迴歸樹(Classification And Regression Tree,簡稱 CART)算法訓練決策樹(也叫「增加樹」)。
這種算法思想真的很是簡單:首先使用單個特徵 k 和閾值 (例如,「花瓣長度 ≤2.45cm 」)將訓練集分紅兩個子集。
它如何選擇 k 和 呢?它尋找到可以產生最純粹的子集一對 ,而後經過子集大小加權計算。
當它成功的將訓練集分紅兩部分以後, 它將會繼續使用相同的遞歸式邏輯繼續的分割子集,而後是子集的子集。
當達到預約的最大深度以後將會中止分裂(由 max_depth 超參數決定),或者是它找不到能夠繼續下降不純度的分裂方法的時候。
幾個其餘超參數(以後介紹)控制了其餘的中止生長條件。
CART 算法是一種貪婪算法:它貪婪地搜索最高級別的最佳分割方式,而後在每一個深度重複該過程。
它不檢查分割是否可以在幾個級別中的所有分割可能中找到最佳方法。貪婪算法一般會產生一個至關好的解決方法,但它不保證這是全局中的最佳解決方案。
而不幸的是,尋找最優樹是一個已知的NP徹底問題:[1]須要的時間是O(exp(m)),因此即便是很小的訓練集,也至關棘手。
這就是爲何咱們必須接受一個「至關不錯」的解。bootstrap
計算複雜度:
進行預測須要從根到葉遍歷決策樹。一般來講,決策樹大體平衡,所以遍歷決策樹須要經歷大約O(log2(m))個節點。(注:log2是以2爲底的對數。
等於log2(m)=log(m)/log(2)。)而每一個節點只須要檢查一個特徵值,因此整體預測複雜度也只是O(log2(m)),與特徵數量無關。
如此,即使是處理大型數據集,預測也很快。多線程
可是,訓練時在每個節點,算法都須要在全部樣本上比較全部特徵(若是設置了max_features會少一些)。這致使訓練的複雜度爲O(n×m log(m))。
對於小型訓練集(幾千個實例之內),Scikit-Learn能夠經過對數據預處理(設置presort=True)來加快訓練,
可是對於較大訓練集而言,可能會減慢訓練的速度。
正則化超參數:
決策樹幾乎不對訓練數據作任何假設(於此相反的是線性迴歸等模型,這類模型一般會假設數據是符合線性關係的)。
若是不添加約束,樹結構模型一般將根據訓練數據調整本身,使自身可以很好的擬合數據,而這種狀況下大多數會致使模型過擬合。
這一類的模型一般會被稱爲非參數模型,這不是由於它沒有任何參數(一般也有不少),而是由於在訓練以前沒有肯定參數的具體數量,
因此模型結構能夠根據數據的特性自由生長。
與此相反的是,像線性迴歸這樣的參數模型有事先設定好的參數數量,因此自由度是受限的,這就減小了過擬合的風險(可是增長了欠擬合的風險)。
DecisionTreeClassifier 類還有一些其餘的參數用於限制樹模型的形狀:
min_samples_split (節點在被分裂以前必須具備的最小樣本數)
min_samples_leaf (葉節點必須具備的最小樣本數)
min_weight_fraction_leaf (和 min_samples_leaf 相同,但表示爲加權總數的一小部分實例)
max_leaf_nodes (葉節點的最大數量)
max_features (在每一個節點被評估是否分裂的時候,具備的最大特徵數量)。
增長 min_* hyperparameters 或者減小 max_* hyperparameters 會使模型正則化。dom
一些其餘算法的工做原理是在沒有任何約束條件下訓練決策樹模型,讓模型自由生長,而後再對不須要的節點進行剪枝。
當一個節點的所有子節點都是葉節點時,若是它對純度的提高不具備統計學意義,咱們就認爲這個分支是沒必要要的。
標準的假設檢驗,例如卡方檢測,一般會被用於評估一個機率值 -- 即改進是否純粹是偶然性的結果(也叫原假設)。
若是 p 值比給定的閾值更高(一般設定爲 5%,也就是 95% 置信度,經過超參數設置),那麼節點就被認爲是非必要的,它的子節點會被刪除。
這種剪枝方式將會一直進行,直到全部的非必要節點都被刪光。機器學習
迴歸:
決策樹也可以執行迴歸任務,讓咱們使用 Scikit-Learn 的 DecisionTreeRegressor 類構建一個迴歸樹,
讓咱們用 max_depth = 2 在具備噪聲的二次項數據集上進行訓練。
from sklearn.tree import DecisionTreeRegressor
tree_reg = DecisionTreeRegressor(max_depth=2)
tree_reg.fit(X, y)
這棵樹看起來很是相似於你以前創建的分類樹,它的主要區別在於,它不是預測每一個節點中的樣本所屬的分類,而是預測一個具體的數值。
CART 算法的工做方式與以前處理分類模型基本同樣,不一樣之處在於,如今再也不以最小化不純度的方式分割訓練集,而是試圖以最小化 MSE 的方式分割訓練集。ide
和處理分類任務時同樣,決策樹在處理迴歸問題的時候也容易過擬合。
不穩定性:
決策樹很喜歡設定正交化的決策邊界,(全部邊界都是和某一個軸相垂直的),這使得它對訓練數據集的旋轉很敏感。
解決這個難題的一種方式是使用 PCA 主成分分析(第八章),這樣一般能使訓練結果變得更好一些。
更加通俗的講,決策時的主要問題是它對訓練數據的微小變化很是敏感。
經過隨機森林將弱分類器集成爲強分類器:
因爲具有好的分類能力、可擴展性、易用性等特色,隨機森林(random forest)過去十年間在機器學習應用領域得到了普遍的關注。
直觀上,隨機森林能夠視爲多棵決策樹的集成。
集成學習的基本理念就是將弱分類器集成爲魯棒性更強的模型,即一個能力更強的分類器,集成後具有更好的泛化偏差,不易產生過擬合現象。
隨機森林算法能夠歸納爲四個簡單的步驟:
1)使用bootstrap抽樣方法隨機選擇n個樣本用於訓練(從訓練集中隨機可重複地選擇n個樣本)。
2)使用第1)步選定的樣本構造一棵決策樹。節點劃分規則以下:
(1)不重複地隨機選擇d個特徵;
(2)根據目標函數的要求,如最大化信息增益,使用選定的特徵對節點進行劃分。
3)重複上述過程1~2000次。
4)彙總每棵決策樹的類標進行多數投票(majority vote)。
雖然隨機森林沒有決策樹那樣良好的可解釋性,但其顯著的優點在於沒必要擔憂超參值的選擇。
咱們一般不須要對隨機森林進行剪枝,由於相對於單棵決策樹來講,集成模型對噪聲的魯棒性更好。
在實踐中,咱們真正須要關心的參數是爲構建隨機森林所需的決策樹數量(步驟3))。
一般狀況下,決策樹的數量越多,隨機森林總體的分類表現就越好,但這同時也相應地增長了計算成本。函數
儘管在實踐中不常見,可是隨機森林中可優化的其餘超參分別是:bootstrap抽樣的數量(步驟1))以及在節點劃分中使用的特徵數量(步驟(2))
經過選擇bootstrap抽樣中樣本數量n,咱們能夠控制隨機森林的誤差與方差權衡的問題。
若是n的值較大,就下降了隨機性,由此更可能致使隨機森林的過擬合。反之,咱們能夠基於模型的性能,經過選擇較小的n值來下降過擬合。
包括scikit-learn中RandomForestClassifier在內的大多數對隨機森林的實現中,bootstrap抽樣的數量通常與原始訓練集中樣本的數量相同,
由於這樣在折中誤差與方差方面通常會有一個好的均衡結果。
而對於在每次節點劃分中用到的特徵數量m,咱們選擇一個比訓練集中特徵總量小的值。
在scikit-learn及其餘程序實現中經常使用的默認值通常是開方,其中m是訓練集中特徵的總量。
forest = RandomForestClassifier(criterion='entropy',n_estimators=10,random_state=1,n_jobs=2)
或:
bag_clf = BaggingClassifier(DecisionTreeClassifier(splitter="random", max_leaf_nodes=16),n_estimators=500, max_samples=1.0, bootstrap=True,
n_jobs=-1)工具
集成學習——組合不一樣的模型:
構建一組分類器的集合,使得總體分類效果優於其中任意一個單獨的分類器。
集成學習:
集成方法(ensemble method)的目標是:將不一樣的分類器組合成爲一個元分類器,與包含於其中的單個分類器相比,元分類器具備更好的泛化性能。
例如:假設咱們收集到了10位專家的預測結果,集成方法容許咱們以必定策略將這10位專家的預測進行組合,與每位專家單獨的預測相比,它具備更好的準確性和魯棒性。
多數投票(majority voting)原則:
將大多數分類器預測的結果做爲最終類標,也就是說,將得票率超過50%的結果做爲類標。
嚴格來講,多數投票僅用於二類別分類情形。不過,很容易將多數投票原則推廣到多類別分類,也稱做簡單多數票法(plurality voting)。
基於訓練集,咱們首先訓練m個不一樣的成員分類器(C1,…,Cm)。在多數投票原則下,可集成不一樣的分類算法,如決策樹、支持向量機、邏輯斯諦迴歸等。
此外,咱們也可使用相同的成員分類算法擬合不一樣的訓練子集。這種方法典型的例子就是隨機森林算法,它組合了不一樣的決策樹分類器。
想要經過簡單的多數投票原則對類標進行預測,咱們要彙總全部分類器Cj的預測類標,並選出得票率最高的類標:
爲了說明集成方法的效果爲什麼好於單個成員分類器,咱們借用下組合學中的概念。
假定每一個分類器都是獨立的,且出錯率之間是不相關的。基於這些假設,咱們能夠將成員分類器集成後的出錯機率簡單地表示爲二項分佈的機率密度函數
在知足全部假設的條件下,集成後的出錯率(0.034)遠遠小於單個分類器的出錯率(0.25)。
請注意,在此演示示例中,當集成分類中分類器個數n爲偶數時,若預測結果爲五五分,咱們則將其以錯誤對待,不過僅有一半的可能會出現這種狀況。
爲了比較成員分類器在不一樣出錯率的狀況下與集成分類器出錯率的差別,咱們用Python實現其機率密度函數:
def ensemble_error(n_classifier,error):
k_start = math.ceil(n_classifier/2.0)
probs = [comb(n_classifier,k)*error**k *(1-error)**(n_classifier-k)
for k in range(k_start,n_classifier+1)]
return sum(probs)
畫圖可知,當成員分類器出錯率低於隨機猜想時(ε<0.5),集成分類器的出錯率要低於單個分類器。
硬投票分類器只是統計每一個分類器的投票,而後挑選出得票最多的類別。
若是全部的分類器都可以預測類別的機率(例如他們有一個 predict_proba() 方法),那麼你就可讓 sklearn 以最高的類機率來預測這個類,
平均在全部的分類器上。這種方式叫作軟投票。他常常比硬投票表現的更好,由於它給予高自信的投票更大的權重。
你能夠經過把 voting="hard" 設置爲 voting="soft" 來保證分類器能夠預測類別機率。然而這不是 SVC類的分類器默認的選項,
因此你須要把它的 probability hyperparameter 設置爲 True (這會使 SVC 使用交叉驗證去預測類別機率,其下降了訓練速度,
但會添加 predict_proba() 方法)。
from sklearn.ensemble import VotingClassifier
voting_clf = VotingClassifier(estimators=[('lr', log_clf), ('rf', rnd_clf),('svc', svm_clf)],voting='hard')性能
實現一個簡單的多數投票分類器:
基於多數投票原則,使用Python實現一個簡單的集成分類器
集成算法容許咱們使用單獨的權重對不一樣分類算法進行組合。咱們的目標是構建一個更增強大的元分類器,以在特定的數據集上平衡單個分類器的弱點。
爲了使用Python代碼實現加權多數投票,可使用NumPy中的argmax和bincount函數:
np.argmax(np.bincount([0,0,1],weights=[0.2,0.2,0.6]))
經過predict_proba方法,scikit-learn中的分類器能夠返回樣本屬於預測類標的機率。
若是集成分類器事先獲得良好的修正,那麼在多數投票中使用預測類別的機率來替代類標會很是有用。
爲實現基於類別預測機率的加權多數投票,咱們能夠再次使用NumPy中的numPy.average和np.argmax方法:
ex = np.array([[0.9,0.1],[0.8,0.2],[0.4,0.6]])
p = np.average(ex,axis=0,weights=[0.2,0.2,0.6])
print(np.argmax(p))
實現一個MajorityVotingClassifier,以10折交叉驗證做爲評估標準,MajorityVotingClassifier的性能與單個成員分類器相比有着質的提升。
評估與調優集成分類器:
在本節,咱們將在測試數據上計算MajorityVoteClassifier類的ROC曲線,以驗證其在未知數據上的泛化性能。
請記住,訓練數據並未應用於模型選擇,使用它們的惟一目的就是對分類系統的泛化性能作出無誤差的估計。
for clf,label,clr,ls in zip(all_clf,clf_labels,colors,linestyles):
y_pred = clf.fit(X_train,y_train).predict_proba(X_test)[:,1] # 測出結果
fpr, tpr, thresholds = roc_curve(y_true=y_test,y_score=y_pred)
roc_auc = auc(x=fpr,y=tpr)
plt.plot(fpr, tpr,color=clr,linestyle=ls,label="%s (auc=%0.2f)" %(label,roc_auc))
由於只使用了分類樣本中的兩個特徵,集成分類器的決策區域究竟是什麼樣子可能會引發咱們的興趣。
因爲邏輯斯諦迴歸和k-近鄰流水線會自動對數據進行預處理,所以沒必要事先對訓練特徵進行標準化。
不過出於可視化考慮,也就是在相同的度量標準上實現決策區域,咱們在此對訓練集作了標準化處理。
也如咱們所預期的那樣,集成分類器的決策區域看起來像是各成員分類器決策區域的混合。
在學習集成分類的成員分類器調優以前,咱們調用一下get_param方法,以便對如何訪問GridSearch對象內的單個參數有個基本的認識:
獲得get_params方法的返回值後,咱們如今知道怎樣去訪問成員分類器的屬性了。如"decisiontreeclassifier__max_depth"
出於演示的目的,先經過網格搜索來調整邏輯斯諦迴歸分類器的正則化係數C以及決策樹的深度。
正如咱們所見,當選擇的正則化強度較小時(C=100.0),咱們可以獲得最佳的交叉驗證結果,而決策樹的深度彷佛對性能沒有任何影響,
這意味着使用單層決策樹足以對數據進行劃分。
請注意,在模型評估時,不止一次使用測試集並不是一個好的作法,本節不打算討論超參調優後的集成分類器泛化能力的評估。
將繼續學習另一種集成方法:bagging。
咱們在本節實現的多數投票方法有時也稱爲堆疊(stacking)。
不過,堆疊算法更典型地應用於組合邏輯斯諦迴歸模型,以各獨立分類器的輸出做爲輸入,經過對這些輸入結果的繼承來預測最終的類標
bagging——經過bootstrap樣本構建集成分類器:
# bagging是有放回的隨機抽樣,pasting是不放回的隨機抽樣,而boosting是循環訓練預測器,每一次都對其前序作出一些改正。
bagging是一種與上一節實現的MajorityVoteClassifier關係緊密的集成學習技術
不過,此算法沒有使用相同的訓練集擬合集成分類器中的單個成員分類器。對每個分類器都使用相同的訓練算法,可是在不一樣的訓練集上去訓練它們。
因爲原始訓練集使用了boostrap抽樣(有放回的隨機抽樣),這也就是bagging被稱爲boostrap aggregating的緣由。
在每一輪的bagging循環中,它們都被可放回隨機抽樣。每一個bootstrap抽樣都被用於分類器Cj的訓練,這就是一棵典型的未剪枝的決策樹:
bagging還與咱們在第3章中曾經介紹過的隨機森林緊密相關。實際上,隨機森林是bagging的一個特例,它使用了隨機的特徵子集去擬合單棵決策。
在實戰中,分類任務會更加複雜,數據集維度會更高,使用單棵決策樹很容易產生過擬合,這時bagging算法就可顯示出其優點了。
最後,咱們需注意bagging算法是下降模型方差的一種有效方法。
然而,bagging在下降模型誤差方面的做用不大,這也是咱們選擇未剪枝決策樹等低誤差分類器做爲集成算法成員分類器的緣由。
聚合函數一般對分類是統計模式(例如硬投票分類器)或者對迴歸是平均。
一般狀況下,集成的結果是有一個類似的誤差,可是對比與在原始訓練集上的單一分類器來說有更小的方差。
在sklearn中的 Bagging 和 Pasting:
sklearn 爲 Bagging 和 Pasting 提供了一個簡單的API: BaggingClassifier 類(或者對於迴歸能夠是 BaggingRegressor 。
接下來的代碼訓練了一個 500 個決策樹分類器的集成,每個都是在數據集上有放回採樣 100 個訓練實例下進行訓練
(這是 Bagging 的例子,若是你想嘗試Pasting,就設置 bootstrap=False )。
n_jobs 參數告訴 sklearn 用於訓練和預測所須要 CPU核的數量。(-1 表明着 sklearn 會使用全部空閒核):
from sklearn.ensemble import BaggingClassifier
rom sklearn.tree import DecisionTreeClassifier
bag_clf = BaggingClassifier(DecisionTreeClassifier(), n_estimators=500,
max_samples=100, bootstrap=True, n_jobs=-1)
bag_clf.fit(X_train, y_train)
y_pred = bag_clf.predict(X_test)
Bootstrap 在每一個預測器被訓練的子集中引入了更多的分集,因此 Bagging 結束時的誤差比Pasting 更高,但這也意味着預測因子最終變得不相關,
從而減小了集合的方差。整體而言,Bagging 一般會致使更好的模型,這就解釋了爲何它一般是首選的。
Out-of-Bag 評價:
對於 Bagging 來講,一些實例可能被一些分類器重複採樣,但其餘的有可能不會被採樣。
BaggingClassifier爲默認採樣。BaggingClassifier 默認是有放回的採樣 m 個實例( bootstrap=True ),其中 m 是訓練集的大小,
這意味着平均下來只有63%的訓練實例被每一個分類器採樣,剩下的37%個沒有被採樣的訓練實例就叫作 Out-of-Bag 實例。
注意對於每個的分類器它們的 37% 不是相同的。
由於在訓練中分類器歷來沒有看到過 oob 實例,因此它能夠在這些實例上進行評估,而不須要單獨的驗證集或交叉驗證。
你能夠拿出每個分類器的 oob 來評估集成自己。
在 sklearn 中,你能夠在訓練後須要建立一個 BaggingClassifier 來自動評估時設置 oob_score=True 來自動評估。接下來的代碼展現了這個操做。
評估結果經過變量 oob_score_ 來顯示:
bag_clf = BaggingClassifier(DecisionTreeClassifier(), n_estimators=500,bootstrap=True, n_jobs=-1, oob_score=True)
bag_clf.fit(X_train, y_train)
bag_clf.oob_score_ # 0.93066666666666664,再與下面測試集上的結果比較一下
根據這個 obb 評估, BaggingClassifier 能夠再測試集上達到93.1%的準確率,讓咱們修改一下:
from sklearn.metrics import accuracy_score
y_pred = bag_clf.predict(X_test)
accuracy_score(y_test, y_pred) # 0.93600000000000005
咱們在測試集上獲得了 93.6% 的準確率,足夠接近了!
對於每一個訓練實例 oob 決策函數也可經過 oob_decision_function_ 變量來展現。在這種狀況下(當基決策器有 predict_proba() 時)
決策函數會對每一個訓練實例返回類別機率。例如,oob 評估預測第二個訓練實例有 60.6% 的機率屬於正類(39.4% 屬於負類):
bag_clf.oob_decision_function_
array([[ 0., 1.], [ 0.60588235, 0.39411765],[ 1., 0. ],... [ 1. , 0. ],[ 0., 1.],[ 0.48958333, 0.51041667]])
隨機貼片與隨機子空間
BaggingClassifier 也支持採樣特徵。它被兩個超參數 max_features 和 bootstrap_features 控制。
他們的工做方式和 max_samples 和 bootstrap 同樣,但這是對於特徵採樣而不是實例採樣。所以,每個分類器都會被在隨機的輸入特徵內進行訓練。
當你在處理高維度輸入下(例如圖片)此方法尤爲有效。對訓練實例和特徵的採樣被叫作隨機貼片。
保留了全部的訓練實例(例如 bootstrap=False 和 max_samples=1.0 ),可是對特徵採樣( bootstrap_features=True 而且/或者
max_features 小於 1.0)叫作隨機子空間。
採樣特徵致使更多的預測多樣性,用高誤差換低方差。
隨機森林:
一般用bagging(有時也多是pasting)方法訓練
極端隨機樹
如前所述,隨機森林裏單棵樹的生長過程當中,每一個節點在分裂時僅考慮到了一個隨機子集所包含的特徵。
若是咱們對每一個特徵使用隨機閾值,而不是搜索得出的最佳閾值(如常規決策樹),則可能讓決策樹生長得更加隨機。
這種極端隨機的決策樹組成的森林,被稱爲極端隨機樹集成[4](或簡稱Extra-Trees)。
一樣,它也是以更高的誤差換取了更低的方差。
極端隨機樹訓練起來比常規隨機森林要快不少,由於在每一個節點上找到每一個特徵的最佳閾值是決策樹生長中最耗時的任務之一。
使用Scikit-Learn的ExtraTreesClassifier能夠建立一個極端隨機樹分類器。它的API與RandomForestClassifier相同。
同理,ExtraTreesRegressor與RandomForestRegressor的API也相同。
一般來講,很難預先知道一個RandomForestClassifier是否會比一個ExtraTreesClassifier更好或是更差。
惟一的方法是兩種都嘗試一遍,而後使用交叉驗證(還須要使用網格搜索調整超參數)進行比較。
提高法:
提高法(Boosting,最初被稱爲假設提高)是指能夠將幾個弱學習器結合成一個強學習器的任意集成方法。
大多數提高法的整體思路是循環訓練預測器,每一次都對其前序作出一些改正。
在boosting中,集成分類器包含多個很是簡單的成員分類器,這些成員分類器性能僅稍好於隨機猜想,常被稱做弱學習機。
典型的弱學習機例子就是單層決策樹。boosting主要針對難以區分的訓練樣本,也就是說,弱學習機經過在錯誤分類樣本上的學習來提升集成分類的性能。
與bagging不一樣,在boosting的初始化階段,算法使用無放回抽樣從訓練樣本中隨機抽取一個子集。
原始的boosting過程可總結爲以下四個步驟:
1)從訓練集D中以無放回抽樣方式隨機抽取一個訓練子集d1,用於弱學習機C1的訓練。
2)從訓練集中以無放回抽樣方式隨機抽取第2個訓練子集d2,並將C1中誤分類樣本的50%加入到訓練集中,訓練獲得弱學習機C2。
3)從訓練集D中抽取C1和C2分類結果不一致的樣本生成訓練樣本集d3,以此訓練第3個弱學習機C3。
4)經過多數投票組合三個弱學習機C一、C2和C3。
與bagging模型相比,boosting可同時下降誤差和方差。在實踐中,boosting算法(如AdaBoost)也存在明顯的高方差問題,也就是說,對訓練數據有過擬合傾向。
AdaBoost:
在本節對集成方法的介紹中,咱們將重點討論boosting算法中一個經常使用例子:AdaBoost(Adaptive Boosting的簡稱)。
AdaBoost與這裏討論的原始boosting過程不一樣,它使用整個訓練集來訓練弱學習機,其中訓練樣本在每次迭代中都會從新被賦予一個權重,
在上一弱學習機錯誤的基礎上進行學習進而構建一個更增強大的分類器。
新預測器對其前序進行糾正的辦法之一,就是更多地關注前序擬合不足的訓練實例。
從而使新的預測器不斷地愈來愈專一於難纏的問題,這就是AdaBoost使用的技術。
過程:
1.首先須要訓練一個基礎分類器(好比決策樹),用它對訓練集進行預測。
2.而後計算該預測器的加權偏差率rj,計算該預測器權重αj,更新實例權重,對錯誤分類的訓練實例增長其相對權重w
# 實例權重w用來衡量決策樹更新權重w時,該實例對權重w的更新系數大小,影響每一個實例的貢獻(因此只能單個訓練?兩個for循環)
# 乘以一個 實例權重 w 矩陣? 相似於批量訓練的 預測結果最後平均出一個值來更新權重 w,平均的時候先乘以一個實例權重矩陣便可。
預測器的準確率越高,其權重就越高。若是它只是隨機猜想,則其權重接近於零。
可是,若是大部分狀況下它都是錯的(也就是準確率比隨機猜想還低),那麼它的權重爲負。
3.使用這個最新的權重對第二個分類器進行訓練,而後再次對訓練集進行預測,繼續更新權重,並不斷循環向前。
4.當到達所需數量的預測器,或獲得完美的預測器時,算法中止。
5.預測時簡單地計算全部預測器的預測結果,並使用預測器權重αj對它們進行加權。
算法步驟:
1.以等值方式爲權重向量w賦值,總和爲1
2.在m輪的boosting操做中,對第j輪作以下操做
訓練一個加權的弱學習機
預測樣本類標
計算權重錯誤率,正確爲1,錯誤爲0
計算相關係數αj
更新權重,計算正確會被下降權重,錯誤會增長
歸一化權重,總爲1
3.完成最終的預測。
學習率越低,波動會越小。
AdaBoost這種依序循環的學習技術跟梯度降低有一些殊途同歸之處,差異只在於——再也不是調整單個預測器的參數使成本函數最小化,
而是不斷在集成中加入預測器,使模型愈來愈好。
一旦所有預測器訓練完成,集成總體作出預測時就跟bagging或pasting方法同樣了,除非預測器有不一樣的權重,由於它們總的準確率是基於加權後的訓練集。
序列學習技術的一個重要的缺點就是:它不能被並行化(只能按步驟),由於每一個分類器只能在以前的分類器已經被訓練和評價後再進行訓練。
所以,它不像Bagging和Pasting同樣。
sklearn 一般使用 Adaboost 的多分類版本 SAMME(這就表明了 分段加建模使用多類指數損失函數)。
若是隻有兩類別,那麼 SAMME 是與 Adaboost 相同的。若是分類器能夠預測類別機率(例如若是它們有 predict_proba() ),
若是 sklearn 可使用 SAMME 叫作 SAMME.R 的變量(R 表明「REAL」),這種依賴於類別機率的一般比依賴於分類器的更好。
from sklearn.ensemble import AdaBoostClassifier
tree = DecisionTreeClassifier(criterion="entropy",max_depth=1)
ada = AdaBoostClassifier(base_estimator=tree,n_estimators=500,learning_rate=0.1,random_state=0)
能夠發現,與單層決策樹相比,AdaBoost分類器可以些許提升分類性能,而且與上一節中訓練的bagging分類器的準確率接近。
不過請注意:重複使用測試集進行模型選擇不是一個好的方法。集成分類器的泛化性能可能會被過分優化。
若是你的AdaBoost集成過分擬合訓練集,你能夠試試減小估算器數量,或是提升基礎估算器的正則化程度。
對集成技術作一個總結:毋庸置疑,與單獨分類器相比,集成學習提升了計算複雜度。
但在實踐中,咱們需仔細權衡是否願意爲適度提升預測性能而付出更多的計算成本。
梯度提高:
另外一個很是著名的提高算法是梯度提高。與 Adaboost 同樣,梯度提高也是經過向集成中逐步增長分類器運行的,每個分類器都修正以前的分類結果。
然而,它並不像 Adaboost 那樣每一次迭代都更改實例的權重,而是讓新的預測器針對前一個預測器的殘差進行擬合。
咱們來看一個簡單的迴歸示例,使用決策樹做爲基礎預測器(梯度提高固然也適用於迴歸任務),這被稱爲梯度樹提高或者是梯度提高迴歸樹(GBRT)。
tree_reg1 = DecisionTreeRegressor(max_depth=2)
tree_reg1.fit(X, y)
如今在第一個分類器的殘差上訓練第二個分類器:
y2 = y - tree_reg1.predict(X) # 預測結果誤差
tree_reg2 = DecisionTreeRegressor(max_depth=2)
tree_reg2.fit(X, y2)
隨後在第二個分類器的殘差上訓練第三個分類器:
y3 = y2 - tree_reg1.predict(X)
tree_reg3 = DecisionTreeRegressor(max_depth=2)
tree_reg3.fit(X, y3)
如今咱們有了一個包含三個迴歸器的集成。它能夠經過集成全部樹的預測來在一個新的實例上進行預測。
y_pred = sum(tree.predict(X_new) for tree in (tree_reg1, tree_reg2, tree_reg3)) # 求和
可使用 sklean 中的 GradientBoostingRegressor 來訓練 GBRT 集成。
與 RandomForestClassifier 類似,它也有超參數去控制決策樹的生長(例如 max_depth , min_samples_leaf 等等),
也有超參數去控制集成訓練,例如基分類器的數量( n_estimators )。
接下來的代碼建立了與以前相同的集成:
from sklearn.ensemble import GradientBoostingRegressor
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=3, learning_rate=1.0)
gbrt.fit(X, y)
超參數learning_rate對每棵樹的貢獻進行縮放。若是你將其設置爲低值,好比0.1,則須要更多的樹來擬合訓練集,可是預測的泛化效果一般更好。
這是一種被稱爲收縮的正則化技術。
要找到樹的最佳數量,可使用早期中止法(參見第4章)。簡單的實現方法就是使用staged_predict()方法:
它在訓練的每一個階段(一棵樹時,兩棵樹時,等等)都對集成的預測返回一個迭代器(用於測量每一個訓練階段的驗證偏差,從而找到樹的最優數量)。
如下代碼訓練了一個擁有120棵樹的GBRT集成,而後測量每一個訓練階段的驗證偏差,從而找到樹的最優數量,最後使用最優樹數從新訓練了一個GBRT集成:
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=120)
gbrt.fit(X_train, y_train)
errors = [mean_squared_error(y_val, y_pred) for y_pred in gbrt.staged_predict(X_test)] # 預測器的數量
bst_n_estimators = np.argmin(errors)
gbrt_best = GradientBoostingRegressor(max_depth=2,n_estimators=bst_n_estimators)
實際上,要實現早期中止法,不必定須要先訓練大量的樹,而後再回頭找最優的數字,還能夠真的提早中止訓練。
設置warm_start=True,當fit()方法被調用時,Scikit-Learn會保留現有的樹,從而容許增量訓練。
如下代碼會在驗證偏差連續5次迭代未改善時,直接中止訓練:
gbrt = GradientBoostingRegressor(max_depth=2, warm_start=True)
for n_estimators in range(1, 120):
gbrt.n_estimators = n_estimators # 調整數量,繼續fit
gbrt.fit(X_train, y_train) #
y_pred = gbrt.predict(X_val)
val_error = mean_squared_error(y_val, y_pred)
if val_error < min_val_error:
min_val_error = val_error
error_going_up = 0
else:
error_going_up += 1
if error_going_up == 5:
break # early stopping
GradientBoostingRegressor類還能夠支持超參數subsample,指定用於訓練每棵樹的實例的比例。
例如,若是subsample=0.25,則每棵樹用25%的隨機選擇的實例進行訓練。如今你能夠猜到,這也是用更高的誤差換取了更低的方差
# 模型下降了過擬合,可能會欠擬合,方差減少,誤差增大(過擬合至少訓練集表現好,模式複雜,多個樣本集來講誤差較小)
同時在至關大的程度上加速了訓練過程。這種技術被稱爲隨機梯度提高。
梯度提高也可使用其餘成本函數,經過超參數loss來控制(有關更多詳細信息,請參閱Scikit-Learn的文檔)。
XGBoost
XGBoost是2014年2月誕生的專一於梯度提高算法的機器學習函數庫,此函數庫因其優良的學習效果以及高效的訓練速度而得到普遍的關注。
XGBoost實現的是一種通用的Tree Boosting算法,此算法的一個表明爲梯度提高決策樹(Gradient Boosting Decision Tree, GBDT),
又名MART(Multiple Additive Regression Tree)。
GBDT的核心在於後面的樹擬合的是前面預測值的殘差,這樣能夠一步步逼近真值。然而,之因此擬合殘差能夠逼近到真值,是由於使用了平方損失做爲損失函數。
若是換成是其餘損失函數,使用殘差將再也不可以保證逼近真值。
XGBoost的方法是,將損失函數作泰勒展開到第二階,使用前兩階做爲改進的殘差。
能夠證實,傳統GBDT使用的殘差是泰勒展開到一階的結果,所以,GBDT是XGBoost的一個特例。
支持列抽樣。
在決策樹中,模型複雜度體如今樹的深度上。XGBoost使用了一種替代指標,即葉子節點的個數。
此外,與許多其餘機器學習模型同樣,XGBoost也加入了L2正則項,來平滑各葉子節點的預測值。
速度快的緣由:
1.連續型特徵的處理,只枚舉若干個分位點,例如將全部樣本根據此特徵進行排序,而後均分10份,兩份之間斷開的數值即爲分位點,
枚舉全部9個分位點後,使用下降損失最多的那個做爲分叉點。
2. 利用數據稀疏性
數據稀疏有三個緣由:缺失數據;某些特徵自己就含有大量的0;對離散特徵作了one-hot處理。
每次分叉時,都指定一條默認分支,若是樣本的這個特徵爲0,就走這個默認分支。
3.數據的預排序和分塊存儲
分叉的時候爲了判斷分叉點,須要對每一個特徵進行排序。這個步驟是要重複屢次的,
所以XGBoost在訓練以前預先對數據進行每一列作了排序,並按列存儲到內存中。
4.減小讀寫相關,提升Cache命中率
5.數據量大時,提升硬盤吞吐率
示例:
import xgboost as xgb
xlf = xgb.XGBClassifier() # 是xgboost的sklearn包,可使用sklearn的utils如Grid Search等,利用函數參數設置模型參數
xlf.fit(X_train, y_train) # 內部調用原始xgboost的xgb.train(),原始利用param列表設置模型參數
preds = xlf.predict(X_test)
LightGBM: 目前已有的 GBDT 工具基本都是基於預排序的方法(pre-sorted)的決策樹算法(如 xgboost), 而LightGBM是基於 Histogram 的決策樹算法,帶深度限制的Leaf-wise的葉子生長策略,直方圖作差加速,直接支持類別特徵(Categorical Feature), Cache命中率優化,基於直方圖的稀疏特徵優化 1.Histogram 算法 直方圖算法的基本思想是先把連續的浮點特徵值離散化成k個整數,同時構造一個寬度爲k的直方圖。 在遍歷數據的時候,根據離散化後的值做爲索引在直方圖中累積統計量,當遍歷一次數據後,直方圖累積了須要的統計量, 而後根據直方圖的離散值,遍歷尋找最優的分割點。 減小了內存,直方圖算法不只不須要額外存儲預排序的結果,並且能夠只保存特徵離散化後的值。 在計算上的代價也大幅下降,預排序算法每遍歷一個特徵值就須要計算一次分裂的增益,而直方圖算法只須要計算k次 固然,Histogram 算法並非完美的。因爲特徵被離散化後,找到的並非很精確的分割點,因此會對結果產生影響。 但在不一樣的數據集上的結果代表,離散化的分割點對最終的精度影響並非很大,甚至有時候會更好一點。 2.帶深度限制的 Leaf-wise 的葉子生長策略 它拋棄了大多數 GBDT 工具使用的按層生長 (level-wise) 的決策樹生長策略,而使用了帶有深度限制的按葉子生長 (leaf-wise) 算法。 Level-wise 過一次數據能夠同時分裂同一層的葉子,容易進行多線程優化,也好控制模型複雜度,不容易過擬合。 但實際上 Level-wise 是一種低效的算法,由於它不加區分的對待同一層的葉子,帶來了不少不必的開銷,由於實際上不少葉子的分裂增益較低, 不必進行搜索和分裂。 Leaf-wise 則是一種更爲高效的策略,每次從當前全部葉子中,找到分裂增益最大的一個葉子,而後分裂,如此循環。 所以同 Level-wise 相比,在分裂次數相同的狀況下,Leaf-wise 能夠下降更多的偏差,獲得更好的精度。 Leaf-wise 的缺點是可能會長出比較深的決策樹,產生過擬合。 所以 LightGBM 在 Leaf-wise 之上增長了一個最大深度的限制,在保證高效率的同時防止過擬合。 3.直方圖加速 一個葉子的直方圖能夠由它的父親節點的直方圖與它兄弟的直方圖作差獲得。 一般構造直方圖,須要遍歷該葉子上的全部數據,但直方圖作差僅需遍歷直方圖的k個桶。 利用這個方法,LightGBM 能夠在構造一個葉子的直方圖後,能夠用很是微小的代價獲得它兄弟葉子的直方圖,在速度上能夠提高一倍。 4.直接支持類別特徵 實際上大多數機器學習工具都沒法直接支持類別特徵,通常須要把類別特徵,轉化到多維的0/1 特徵,下降了空間和時間的效率。 而類別特徵的使用是在實踐中很經常使用的。基於這個考慮,LightGBM 優化了對類別特徵的支持,能夠直接輸入類別特徵,不須要額外的0/1 展開。 並在決策樹算法上增長了類別特徵的決策規則。在 Expo 數據集上的實驗,相比0/1 展開的方法,訓練速度能夠加速 8 倍,而且精度一致。 據咱們所知,LightGBM 是第一個直接支持類別特徵的 GBDT 工具。 超參: 1.核心參數 - boosting_type,默認gbdt,可選rf,dart,goss gbdt, 傳統的梯度提高決策樹 rf, Random Forest (隨機森林) dart, Dropouts meet Multiple Additive Regression Trees goss, Gradient-based One-Side Sampling (基於梯度的單側採樣) - learning_rate 默認設置成0.1 - n_estimators - num_leaves 調整 num_leaves 的取值時, 應該讓其小於 2^(max_depth) - n_jobs - device cpu, gpu 2.控制參數: - max_depth - min_child_samples 默認20,一個葉子上數據的最小數量. 能夠用來處理過擬合.對於大數據來講幾百或幾千 - min_child_weight,0.001,一個葉子上的最小 hessian 和. 相似於 min_data_in_leaf, 能夠用來處理過擬合. - colsample_bytree 1.0,加速訓練,處理過擬合 - subsample 1.0,將在不進行重採樣的狀況下隨機選擇部分數據 - subsample_freq 0 bagging 的頻率, 0 意味着禁用 bagging. k 意味着每 k 次迭代執行bagging - early_stopping_round 若是一個驗證集的度量在 early_stopping_round 循環中沒有提高, 將中止訓練 - reg_alpha L1 正則 - reg_lambda L2 正則 - min_split_gain 0,執行切分的最小增益 - subsample_for_bin:用來構建直方圖的數據的數量,在設置更大的數據時, 會提供更好的培訓效果, 但會增長數據加載時間。若是數據很是稀疏, 則將其設置爲更大的值 3.IO參數 - max_bin 默認255,值越大,準確率越高,可能會過擬合 - save_binary 若是設置爲 true LightGBM 則將數據集(包括驗證數據)保存到二進制文件中。 能夠加快數據加載速度。 - ignore_column ignore_column=0,1,2 - categorical_feature categorical_feature=0,1,2 示例: import lightgbm as lgb lf = lgb.LGBMClassifier() # 相似,封裝了sklearn lf.fit()