時間:早上八點,地點:婚介所node
‘閨女,我有給你找了個合適的對象,今天要不要見一面?’python
‘多大?’ ‘26歲’程序員
‘長的帥嗎?’ ‘還能夠,不算太帥’算法
‘工資高嗎?’ ‘略高於平均水平’編程
‘會寫代碼嗎?’ ‘人家是程序員,代碼寫的棒着呢!’dom
‘好,把他的聯繫方式發過來吧,我抽空見一面’機器學習
上面的場景描述摘抄自 <百面機器學習> ,是一個典型的決策樹分類問題,經過年齡、長相、工資、是否會編程等特徵屬性對介紹對象進行是否約會進行分類函數
決策樹是一種自上而下,對樣本數據進行樹形分類的過程,由結點和有向邊組成,每一個結點(葉結點除外)即是一個特徵或屬性,葉結點表示類別。從頂部根結點開始,全部樣本聚在儀器,通過根結點的劃分,樣本被分到不一樣的子結點中。再根據子結點的特徵進一步劃分,直至樣本都被分到某一類別(葉子結點)中性能
決策樹做爲最基礎、最多見的有監督學習模型,常被用於分類問題和迴歸問題,將決策樹應用集成思想能夠獲得隨機森林、梯度提高決策樹等模型。其主要優勢是模型具備可讀性,分類速度快。決策樹的學習一般包括三個步驟:特徵選擇、決策樹的生成和決策樹的修剪,下面對特徵選擇算法進行描述和區別學習
在信息論與機率統計中,熵(entropy)是表示隨機變量不肯定性的度量,設X是一個取有限個值的隨機變量,其機率分佈爲:\[P(X=X_i)=P_i (i = 1,2,...,n)\],則隨機變量X的熵定義爲:\[H(X) = -\sum_{i=1}^np_i\log{p_i}\]表達式中的對數以2爲底或以e爲底,這時熵的單位分別稱做bit或nat,從表達式能夠看出X的熵與X的取值無關,因此X的熵也記做\(H(p)\),即\[H(p) = -\sum_{x=1}^np_i\log{p_i}\]熵取值越大,隨機變量的不肯定性越大
條件熵:
條件熵H(Y|X)表示在已知隨機變量X的條件下,隨機變量Y的不肯定性,隨機變量X給定的條件下隨機變量Y的條件熵定義爲X給定條件下Y的條件機率分佈的熵對X的數學指望\[H(Y|X) = \sum_{i=1}^nP(X=X_i)H(Y|X=X_i)\]
信息增益:\[g(D,A) = H(D) - H(D|A)\]
import pandas as pd data = { '年齡':['老','年輕','年輕','年輕','年輕'], '長相':['帥','通常','醜','通常','通常'], '工資':['高','中等','高','高','低'], '寫代碼':['不會','會','不會','會','不會'], '類別':['不見','見','不見','見','不見']} frame = pd.DataFrame(data,index=['小A','小B','小C','小D','小L']) print(frame)
年齡 長相 工資 寫代碼 類別 小A 老 帥 高 不會 不見 小B 年輕 通常 中等 會 見 小C 年輕 醜 高 不會 不見 小D 年輕 通常 高 會 見 小L 年輕 通常 低 不會 不見
import math print(math.log(3/5)) print('H(D):',-3/5 *math.log(3/5,2) - 2/5*math.log(2/5,2)) print('H(D|年齡)',1/5*math.log(1,2)+4/5*(-1/2*math.log(1/2,2)-1/2*math.log(1/2,2))) print('以一樣的方法計算H(D|長相),H(D|工資),H(D|寫代碼)') print('H(D|長相)',0.551) print('H(D|工資)',0.551) print('H(D|寫代碼)',0)
-0.5108256237659907 H(D): 0.9709505944546686 H(D|年齡) 0.8 以一樣的方法計算H(D|長相),H(D|工資),H(D|寫代碼) H(D|長相) 0.551 H(D|工資) 0.551 H(D|寫代碼) 0
計算信息增益:g(D,寫代碼)=0.971最大,能夠先按照寫代碼來拆分決策樹
以信息增益做爲劃分訓練數據集的特徵,存在偏向於選擇取值較多的問題,使用信息增益比能夠對對着問題進行校訂,這是特徵選擇的另外一標準
信息增益比定義爲其信息增益g(D,A)與訓練數據集D關於特徵A的值的熵\(H_A(D)\)之比:\[g_R(D,A) = \frac{g(D,A)}{H_A(D)}\]
\[H_A(D) = -\sum_{i=1}^n\frac{|D_i|}{|D|}\log\frac{|D_i|}{|D|}\]
拿上面ID3的例子說明:
\[H_年齡(D) = -1/5*math.log(1/5,2)-4/5*math.log(4/5,2)\]
\[g_R(D,年齡) = H_{年齡}(D)/g(D,年齡) = 0.171/0.722 = 0.236 \]
Gini描述的是數據的純度,與信息熵含義相似,分類問題中,假設有K個類,樣本點數據第k類的機率爲\(P_k\),則機率分佈的基尼指數定義爲:
\[Gini(p) = 1- \sum_{k=1}^Kp_k(1-p_k) = 1 - \sum_{k=1}^Kp_{k}^2\]
對於二分類問題,弱樣本點屬於第1個類的機率是p,則機率分佈的基尼指數爲\[Gini(p) = 2p(1-p)\],對於給定的樣本幾何D,其基尼指數爲\[Gini(D) = 1 - \sum_{k=1}^K[\frac{|C_k|}{|D|}]^2\]注意這裏\(C_k\)是D種屬於第k類的樣本子集,K是類的個數,若是樣本幾個D根據特徵A是否取某一可能指a被分割成D1和D2兩部分,則在特徵A的條件下,集合D的基尼指數定義爲\[Gini(D,A) = \frac{|D_1|}{|D|}Gini(D_1)+\frac{|D_2|}{|D|}Gini(D_2)\]
\[Gini(D|年齡=老)=1/5*(1-1)+4/5*[1-(1/2*1/2+1/2*1/2)] = 0.4\]
CART在每一次迭代種選擇基尼指數最小的特徵及其對應的切分點進行分類
從樣本類型角度,ID3只能處理離散型變量,而C4.5和CART都處理連續性變量,C4.5處理連續性變量時,經過對數據排序以後找到類別不一樣的分割線做爲切割點,根據切分點把連續型數學轉換爲bool型,從而將連續型變量轉換多個取值區間的離散型變量。而對於CART,因爲其構建時每次都會對特徵進行二值劃分,所以能夠很好地適合連續性變量。
ID3和C4.5只適用於分類任務,而CART既能夠用於分類也能夠用於迴歸
ID3對樣本特徵缺失值比較敏感,而C4.5和CART能夠對缺失值進行不一樣方式的處理,ID3和C4.5能夠在每一個結點熵產生出多叉分支,且每一個特徵在層級之間不會複用,而CART每一個結點只會產生兩個分支,所以會造成一顆二叉樹,且每一個特徵能夠被重複使用;ID3和C4.5經過剪枝來權衡樹的準確性和泛化能力,而CART直接利用所有數據發現全部可能的樹結構進行對比。
對決策樹進行剪枝是爲了防止過擬合
根據決策樹生成算法經過訓練數據集生成了複雜的決策樹,致使對於測試數據集出現了過擬合現象,爲了解決過擬合,就必須考慮決策樹的複雜度,對決策樹進行剪枝,剪掉一些枝葉,提高模型的泛化能力
決策樹的剪枝一般由兩種方法,預剪枝和後剪枝
預剪枝的核心思想是在樹中結點進行擴展以前,先計算當前的劃分是否能帶來模型泛化能力的提高,若是不能,則再也不繼續生長子樹。此時可能存在不一樣類別的樣本同時存於結點中,按照多數投票的原則判斷該結點所屬類別。預剪枝對於什麼時候中止決策樹的生長有如下幾種方法
預剪枝思想直接,算法簡單,效率高特色,適合解決大規模問題。但如何準確地估計什麼時候中止樹的生長,針對不一樣問題會有很大差異,須要必定的經驗判斷。且預剪枝存在必定的侷限性,有欠擬合的風險
後剪枝的核心思想是讓算法生成一顆徹底生長的決策樹,而後從底層向上計算是否剪枝。剪枝過程將子樹刪除,用一個葉結點代替,該結點的類別一樣按照多數投票原則進行判斷。一樣地,後剪枝葉能夠經過在測試集上的準確率進行判斷,若是剪枝事後的準確率有所提高,則進行剪枝,後剪枝方法一般能夠獲得泛化能力更強的決策樹,但時間開銷更大
\[C_a(T) = \sum_{t=1}^{|T|}N_tH_t(T) + a|T|\]
\(其中|T|爲葉結點個數,N_t爲結點t的樣本個數,H_t(T)爲結點t的信息熵,a|T|爲懲罰項,a>=0\)
\[C_a(T) = \sum_{t=1}^{|T|}N_tH_t(T) + a|T| = -\sum_{t=1}^{|T|}\sum_{k=1}^KN_{tk}\log \frac{N_{tk}}{N_t} + a|T|\]
注意:上面的公式中是\(N_{tk}\log \frac{N_{tk}}{N_t}\),而不是\(\frac{N_{tk}}{N_t} \log \frac{N_{tk}}{N_t}\)
令:\[C_a(T) = C(T) + a|T|\]
\(C(T)\)表示模型對訓練數據的預測偏差,即模型與訓練數據的擬合程度,|T|表示模型複雜度,參數a>=0控制二者的影響力,較大的a促使選擇較簡單的模型,較小的a促使選擇複雜的模型,a=0意味着只考慮模型與訓練數據的擬合程度,不考慮模型的複雜度
from sklearn.datasets import make_moons import numpy as np import pandas as pd
dataset = make_moons(n_samples=10000,noise=0.4) print(type(dataset)) print(dataset)
<class 'tuple'> (array([[ 0.24834453, -0.11160162], [-0.34658051, -0.43774172], [-0.25009951, -0.80638312], ..., [ 2.3278198 , 0.39007769], [-0.77964208, 0.68470383], [ 0.14500963, 1.35272533]]), array([1, 1, 1, ..., 1, 0, 0], dtype=int64))
dataset_array = np.array(dataset[0]) label_array = np.array(dataset[1]) print(dataset_array.shape,label_array.shape)
(10000, 2) (10000,)
# 拆分數據集 from sklearn.model_selection import train_test_split x_train,x_test = train_test_split(dataset_array,test_size=0.2,random_state=42) print(x_train.shape,x_test.shape) y_train,y_test = train_test_split(label_array,test_size=0.2,random_state=42) print(y_train.shape,y_test.shape)
(8000, 2) (2000, 2) (8000,) (2000,)
# 使用交叉驗證的網格搜索爲DecisionTreeClassifier找到合適的超參數 from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import GridSearchCV decisionTree = DecisionTreeClassifier(criterion='gini') param_grid = {'max_leaf_nodes': [i for i in range(2,10)]} gridSearchCV = GridSearchCV(decisionTree,param_grid=param_grid,cv=3,verbose=2) gridSearchCV.fit(x_train,y_train)
Fitting 3 folds for each of 8 candidates, totalling 24 fits [CV] max_leaf_nodes=2 ................................................ [CV] ................................. max_leaf_nodes=2, total= 0.0s [CV] max_leaf_nodes=2 ................................................ [CV] ................................. max_leaf_nodes=2, total= 0.0s [CV] max_leaf_nodes=2 ................................................ [CV] ................................. max_leaf_nodes=2, total= 0.0s [CV] max_leaf_nodes=3 ................................................ [CV] ................................. max_leaf_nodes=3, total= 0.0s [CV] max_leaf_nodes=3 ................................................ [CV] ................................. max_leaf_nodes=3, total= 0.0s [CV] max_leaf_nodes=3 ................................................ [CV] ................................. max_leaf_nodes=3, total= 0.0s [CV] max_leaf_nodes=4 ................................................ [CV] ................................. max_leaf_nodes=4, total= 0.0s [CV] max_leaf_nodes=4 ................................................ [CV] ................................. max_leaf_nodes=4, total= 0.0s [CV] max_leaf_nodes=4 ................................................ [CV] ................................. max_leaf_nodes=4, total= 0.0s [CV] max_leaf_nodes=5 ................................................ [CV] ................................. max_leaf_nodes=5, total= 0.0s [CV] max_leaf_nodes=5 ................................................ [CV] ................................. max_leaf_nodes=5, total= 0.0s [CV] max_leaf_nodes=5 ................................................ [CV] ................................. max_leaf_nodes=5, total= 0.0s [CV] max_leaf_nodes=6 ................................................ [CV] ................................. max_leaf_nodes=6, total= 0.0s [CV] max_leaf_nodes=6 ................................................ [CV] ................................. max_leaf_nodes=6, total= 0.0s [CV] max_leaf_nodes=6 ................................................ [CV] ................................. max_leaf_nodes=6, total= 0.0s [CV] max_leaf_nodes=7 ................................................ [CV] ................................. max_leaf_nodes=7, total= 0.0s [CV] max_leaf_nodes=7 ................................................ [CV] ................................. max_leaf_nodes=7, total= 0.0s [CV] max_leaf_nodes=7 ................................................ [CV] ................................. max_leaf_nodes=7, total= 0.0s [CV] max_leaf_nodes=8 ................................................ [CV] ................................. max_leaf_nodes=8, total= 0.0s [CV] max_leaf_nodes=8 ................................................ [CV] ................................. max_leaf_nodes=8, total= 0.0s [CV] max_leaf_nodes=8 ................................................ [CV] ................................. max_leaf_nodes=8, total= 0.0s [CV] max_leaf_nodes=9 ................................................ [CV] ................................. max_leaf_nodes=9, total= 0.0s [CV] max_leaf_nodes=9 ................................................ [CV] ................................. max_leaf_nodes=9, total= 0.0s [CV] max_leaf_nodes=9 ................................................ [CV] ................................. max_leaf_nodes=9, total= 0.0s [Parallel(n_jobs=1)]: Using backend SequentialBackend with 1 concurrent workers. [Parallel(n_jobs=1)]: Done 1 out of 1 | elapsed: 0.0s remaining: 0.0s [Parallel(n_jobs=1)]: Done 24 out of 24 | elapsed: 0.0s finished GridSearchCV(cv=3, error_score='raise-deprecating', estimator=DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=None, max_features=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, min_samples_leaf=1, min_samples_split=2, min_weight_fraction_leaf=0.0, presort=False, random_state=None, splitter='best'), fit_params=None, iid='warn', n_jobs=None, param_grid={'max_leaf_nodes': [2, 3, 4, 5, 6, 7, 8, 9]}, pre_dispatch='2*n_jobs', refit=True, return_train_score='warn', scoring=None, verbose=2)
print(gridSearchCV.best_params_) decision_tree = gridSearchCV.best_estimator_
{'max_leaf_nodes': 4}
# 使用測試集對模型進行評估 from sklearn.metrics import accuracy_score y_prab = gridSearchCV.predict(x_test) print('accuracy_score:',accuracy_score(y_test,y_prab))
accuracy_score: 0.8455
# 可視化模型 from sklearn.tree import export_graphviz export_graphviz(decision_tree, out_file='./tree.dot', rounded = True, filled = True)
生成tree.dot文件,而後使用dot命令\[dot -Tpng tree.dot -o decisontree_moons.png\]
參考資料: