決策樹(Decision Tree)和隨機森林

1. 決策樹

1.1 概念

        決策樹是一種樹形結構,爲人們提供決策依據,決策樹可以用來回答yes和no問題,它通過樹形結構將各種情況組合都表示出來,每個分支表示一次選擇(選擇yes還是no),直到所有選擇都進行完畢,最終給出正確答案。決策樹是一種貪心算法,它要在給定時間內做出最佳選擇,但 並不關心能否達到全局最優 。

           

 

       決策樹(decision tree)是一個樹結構(可以是二叉樹或非二叉樹)。在實際構造決策樹時,通常要進行剪枝,這是爲了處理由於數據中的噪聲和離羣點導致的過分擬合問題。剪枝有兩種:

先剪枝——在構造過程中,當某個節點滿足剪枝條件,則直接停止此分支的構造。

後剪枝——先構造完成完整的決策樹,再通過某些條件遍歷樹進行剪枝。

1.2 劃分準則

       決策樹學習的關鍵:如何選擇最優劃分屬性

       劃分數據集的大原則是:將無序的數據變得更加有序

       劃分數據集,構建決策樹時將對每個特徵劃分數據集的結果計算一次信息增益/基尼指數/增益率,然後判斷按照哪個特徵劃分數據集是最好的劃分方式。

(1)信息增益

                                    

      信息增益越大,則意味着使用屬性 α 來進行劃分所獲得的"純度提升"越大。信息增益準則對可取值數目較多的屬性有所偏好。

(2)增益率

          

      屬性 α 的可能取值數目越多(即 V 越大),則 IV(α) 的值通常會越大,增益率越小。增益率準則對可取值數目較少的屬性有所偏好。

(3)基尼指數

     基尼值:

                                           

      基尼指數:

                                      

       Gini(D) 反映了從數據集 D 中隨機抽取兩個樣本,其類別標記不一致的概率。 因此, Gini(D) 越小,兩個樣本的類別越一致,則數據集 D 的純度越高。 

1.3 決策樹算法

(1)ID3

       以信息增益爲準則來選擇劃分屬性,用於劃分離散型數據集。

做法:

       每次選取當前最佳的特徵來分割數據,並按照該特徵的所有可能取值來切分。一旦按某特徵切分後,該特徵在之後的算法執行過程中將不會再起作用,所以有觀點認爲這種切分方式過於迅速。        

缺點:  

  • 切分方式過於迅速;  
  •  不能直接處理連續型特徵。只有事先將連續型特徵轉換成離散型,才能使用。這種轉換過程會破壞連續型變量的內在性質。        

        ID3算法無法直接處理數值型數據,儘管我們可以通過量化的方法將數值型數據轉化爲離散型數值,但是如果存在太多的特徵劃分, ID3算法仍然會面臨其他問題。

(2)C4.5

      以增益率爲準則來選擇劃分屬性,核心算法ID3的改進算法。

C4.5比ID3改進的地方:        

(3)CART

      CART決策樹(分類迴歸決策樹):使用"基尼指數" 來選擇劃分屬性。

      CART是十分著名且廣泛記載的樹構建算法,它使用二元切分來處理連續型變量:

      二元切分法:每次把數據集切成兩份

      做法:如果特徵值大於給定值就走左子樹, 否則就走右子樹。        

      優點:易於對樹構建過程進行調整以處理連續型特徵;   二元切分法也節省了樹的構建時間。

1.4 代碼實現

  • ID3選擇屬性用的是子樹的信息增益,即熵的變化值;而C4.5用的是信息增益率。一般來說率就是用來取平衡用的,比如有兩個跑步的人,一個起點是10m/s的人、其1s後爲20m/s;另一個人起速是1m/s、其1s後爲2m/s。如果緊緊算差值那麼兩個差距就很大了,如果使用速度增加率(加速度)來衡量,2個人就是一樣了。在這裏,其克服了用信息增益選擇屬性時偏向選擇取值多的屬性的不足。        
  • 在樹構造過程中進行剪枝。有些節點只掛着幾個元素,對於這種節點,乾脆不考慮最好,不然很容易導致overfitting。      
  • 對非離散數據都能處理,也就是把連續性的數據轉化爲離散的值進行處理。這個其實就是一個個式,看對於連續型的值在哪裏分裂好。        
  • 能夠對不完整數據進行處理。這個重要也重要,其實也沒那麼重要,缺失數據採用一些方法補上去就是了。

     決策樹主要是調用sklearn裏面函數,這個裏面包含了DecisionTreeClassifier,不需要我們自己去實現。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeRegressor

if __name__ == "__main__":
 n = 500
 x = np.random.rand(n) * 8 - 3
 x.sort()
 y = np.cos(x) + np.sin(x) + np.random.randn(n) * 0.4
 x = x.reshape(-1, 1)

 reg = DecisionTreeRegressor(criterion='mse')
 # reg1 = RandomForestRegressor(criterion='mse')
 dt = reg.fit(x, y)
 # dt1 = reg1.fit(x, y)
 x_test = np.linspace(-3, 5, 100).reshape(-1, 1)
 y_hat = dt.predict(x_test)
 plt.figure(facecolor="w")
 plt.plot(x, y, 'ro', label="actual")
 plt.plot(x_test, y_hat, 'k*', label="predict")
 plt.legend(loc="best")
 plt.title(u'Decision Tree', fontsize=17)
 plt.tight_layout()
 plt.grid()
 plt.show()

2. 隨機森林

2.1 Bagging策略

Bagging( bootstrap aggregation)的策略:從樣本集中進行有放回地選出n個樣本;在樣本的所有特徵上,對這n個樣本建立分類器;重複上述兩步m次,獲得m個樣本分類器;最後將測試數據都放在這m個樣本分類器上,最終得到m個分類結果,再從這m個分類結果中決定數據屬於哪一類(多數投票制)。

Bootstrap:一種有放回的抽樣方法。

隨機森林採用了Bagging策略,且在其基礎上進行了一些修改,採用了兩個隨機:

  1. 從訓練樣本集中使用Bootstrap採樣(隨機有放回)選出n個樣本。
  2. 設樣本共有b個特徵,從這b個特徵中只隨機選擇k個特徵來分割樣本,通過計算選擇最優劃分特徵作爲節點來劃分樣本集合來建立決策樹。(與Bagging的不同之處:沒有使用全部的特徵,這樣可以避免一些過擬合的特徵,不再對決策樹進行任何剪枝)
  3. 重複以上兩步m次,可建立m棵決策樹
  4. 這m棵決策樹形成了森林,可通過簡單多數投票法(或其他投票機制)來決定森林的輸出,決定屬於哪一類型。(針對解決迴歸問題,可以採用單棵樹輸出結果總和的平均值)

隨機森林在一定程序上提高了泛化能力,而且可以並行地生成單棵樹。

2.2 代碼示例

使用決策樹和隨機森林進行手寫數字(sklearn中的digits數據)的預測

from sklearn import datasets
from sklearn.model_selection import cross_val_score
import datetime
from sklearn import tree
from sklearn.ensemble import RandomForestClassifier

digits = datasets.load_digits();
X = digits.data     # 特徵矩陣
y = digits.target   # 標籤矩陣
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=1/3., random_state=8) # 分割訓練集和測試集

estimators = {}
# criterion: 分支的標準(gini/entropy)
# 1.決策樹
estimators['tree'] = tree.DecisionTreeClassifier(criterion='gini',random_state=8) 
# 2.隨機森林
# n_estimators: 樹的數量
# bootstrap: 是否隨機有放回
# n_jobs: 可並行運行的數量
estimators['forest'] = RandomForestClassifier(n_estimators=20,criterion='gini',bootstrap=True,n_jobs=2,random_state=8) 

for k in estimators.keys():
 start_time = datetime.datetime.now()
 # print '----%s----' % k
 estimators[k] = estimators[k].fit(X_train, y_train)
 pred = estimators[k].predict(X_test)
 # print pred[:10]
 print("%s Score: %0.2f" % (k, estimators[k].score(X_test, y_test)))
 scores = cross_val_score(estimators[k], X_train, y_train,scoring='accuracy' ,cv=10)
 print("%s Cross Avg. Score: %0.2f (+/- %0.2f)" % (k, scores.mean(), scores.std() * 2))
 end_time = datetime.datetime.now()
 time_spend = end_time - start_time
 print("%s Time: %0.2f" % (k, time_spend.total_seconds()))

未完待續。。。

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