決策樹
- 5.1 決策樹模型與學習
- 5.2 特征選擇
-
- 5.2.1 資訊增益
- 5.2.2 資訊增益比
-
- python代碼實作例題:資訊增益與資訊增益比
- 5.3 決策樹的生成
-
- 5.3.1 ID3算法(python實作)
- 5.3.2 C4.5生成算法(python實作)
- 5.4 決策樹的剪枝
- 5.5 CART算法
-
- 5.5.1 CART生成
- 5.5.2 CART剪枝
- 習題5.1(python實作)
- 習題5.2(python實作)
- 習題5.3
- 習題5.4
- 參考
5.1 決策樹模型與學習
分類決策樹模型是一種描述對執行個體進行分類得樹形結構。内部節點表示一個特征或屬性,葉子節點表示一個類。
路徑上内部節點的特征對應着規則的結論,而葉節點的類對應着規則的結論。決策樹的路徑具有一個重要的性質:互斥并且完備,每一個執行個體都被一條路徑或一條規則所覆寫,且隻被一條路徑或一條規則所覆寫。
-
決策樹與條件機率
決策樹還可以表示給定特征條件下類的條件機率分布。下圖中(a)的大正方形表示特征空間,每個小矩形表示一個單元。假設 Y Y Y的取值隻有-1和+1,小矩形中的式子表示單元的類。(b)圖表示特征空間劃分确定時,特征單元給定下的條件機率分布。當某個單元 c c c的條件機率滿足 P ( Y = + 1 ∣ X = c ) > 0.5 P(Y=+1|X=c)>0.5 P(Y=+1∣X=c)>0.5時,落在這個單元的執行個體都被視為+1。
-
決策樹學習
決策樹學習本質上是從訓練資料集中歸納出一組分類規則。決策樹可能不止一個也可能一個都沒有,是以目标是找到與訓練資料沖突較小的決策樹,同時具有很好的泛化能力。
學習過程:
(1)首先構造根節點,将所有訓練資料都放在根節點。
(2)選取一個最優特征,按照這一特征将訓練資料集分割成子集,使得各個子集有一個在目前條件下最好的分類。
(3)若這些子集能夠被基本正确分類,那麼建構葉子節點,并将這些子集分到所對應的葉子節點中去;如果還有子集不能被基本正确分類,那麼就對這些子集選擇新的最優特征,繼續進行分割,建構相應的結點。
(4)遞歸(2)(3),知道所有訓練資料子集被基本正确分類,或者沒有合适的特征。
通過這樣生成的決策樹,雖然對訓練資料有較好的拟合效果,但是對于測試資料可能會發生過拟合現象,是以需要适當的對樹進行剪枝,去掉過于細分的葉子節點。
決策樹的生成對應于模型的局部選擇(目前分割後損失函數最小化為目的),決策樹的剪枝對應于模型的全局選擇(測試集上的損失函數盡可能小)
- 決策樹學習算法:特征選擇、決策樹的生成、決策樹的剪枝
5.2 特征選擇
特征選取的目的:選取對訓練資料具有分類能力的特征,提高決策樹學習的效率。常見的選擇名額為資訊增益與資訊增益比。
5.2.1 資訊增益
-
熵:表示随機變量不确定性的度量,熵越大,随機變量的不确定性越大,資料混亂程度越大,不整齊;且熵僅依賴于X的分布,與X的取值範圍無關。
P ( X = x i ) = p i i = 1 , 2 , . . . . n H ( X ) = − ∑ i = 1 n p i log p i P(X=x_i)=p_i\ \ \ i=1,2,....n\\ H(X)=-\sum\limits_{i=1}^{n}p_i\log p_i P(X=xi)=pi i=1,2,....nH(X)=−i=1∑npilogpi
H ( X ) H(X) H(X)即為随機變量X的熵。
P ( X = x i , Y = y i ) = p i j i = 1 , 2 , . . . , n ; j = 1 , 2 , . . . , m H ( Y ∣ X ) = ∑ i = 1 n p i H ( Y ∣ X = x i ) P(X=x_i,Y=y_i)=p_{ij} \ \ \ i=1,2,...,n;\ j=1,2,...,m\\ H(Y|X)=\sum\limits_{i=1}^np_iH(Y|X=x_i) P(X=xi,Y=yi)=pij i=1,2,...,n; j=1,2,...,mH(Y∣X)=i=1∑npiH(Y∣X=xi)
H ( Y ∣ X ) H(Y|X) H(Y∣X)表示随機變量X給定條件下随機變量Y的條件熵,換句話說,為X給定條件下Y的條件機率分布的熵對X的數學期望。
當熵和條件熵中的機率由資料統計(極大似然估計)得到,則對應稱為經驗熵和經驗條件熵。
-
資訊增益:集合D的經驗熵H(D)與特征A在給定條件下D的經驗條件熵H(D|A)之差,稱為資訊增益g(D,A)
g ( D , A ) = H ( D ) − H ( D ∣ A ) g(D,A)=H(D)-H(D|A) g(D,A)=H(D)−H(D∣A)
該值表示由于特征A使得對資料集D的分類的不确定性減少的程度。資訊增益越大,表示該特征對不确定性減少的程度越大,使得分類更偏向穩定。
5.2.2 資訊增益比
資訊增益作為劃分時,為了盡可能降低不确定性,會出現偏向于選擇取值較多的特征的問題,為了校正該問題,引入了資訊增益比
-
資訊增益比:資訊增益 g ( D , A ) g(D,A) g(D,A)與訓練資料集D關于特征A的值的熵 H A ( D ) H_A(D) HA(D)d的比值為資訊增益比 g R ( D , A ) g_R(D,A) gR(D,A)。
g R ( D , A ) = g ( D , A ) H A ( D ) H A ( D ) = − ∑ i = 1 n ∣ D i ∣ ∣ D ∣ l o g 2 ∣ D i ∣ ∣ D ∣ n 為 A 的 特 征 取 值 個 數 g_R(D,A)=\frac{g(D,A)}{H_A(D)}\\ H_A(D)=-\sum\limits_{i=1}^n\frac{|D_i|}{|D|}log_2\frac{|D_i|}{|D|} \ \ \ n為A的特征取值個數 gR(D,A)=HA(D)g(D,A)HA(D)=−i=1∑n∣D∣∣Di∣log2∣D∣∣Di∣ n為A的特征取值個數
python代碼實作例題:資訊增益與資訊增益比
import numpy as np
import math
#直接更改x,y即可
x = [[1, 0, 0, 1],
[0, 1, 1, 1],
[0, 0, 1, 0]]
y = [0, 0, 1]
feature_num = np.shape(x)[1]
y_len = len(y)
feature = np.array(x)
for i in range(feature_num):
_feature = feature[:, i]
temp_condition_ent = 0
temp_ent_i = 0
HA_D = 0
print(_feature)
for j in set(_feature):
D_i = _feature.tolist().count(j)
temp_ent = 0
for k in set(y):
temp_ent += -(y.count(k) / y_len) * math.log(y.count(k) / y_len)#H(D)
D_ik = 0
for index in range(len(y)):
if y[index] == k and _feature[index] == j:
D_ik = D_ik + 1
if D_ik != 0:
temp_ent_i += -(D_ik / D_i) * math.log(D_ik / D_i)
else:
temp_ent_i = 0
temp_condition_ent += (D_i / y_len) * temp_ent_i#H(D|A)
conditon_ent = temp_ent - temp_condition_ent#g(D|A)
HA_D += -(D_i / y_len) * math.log(D_i / y_len)#H_A(D)
conditon_ent_ratio = conditon_ent / HA_D #g_R(D,A)
print(conditon_ent)
print(conditon_ent_ratio)
5.3 決策樹的生成
5.3.1 ID3算法(python實作)
ID3算法的核心是決策樹的各個節點上應用資訊增益準則選擇特征,遞歸的建構決策樹,直到所有特征資訊的資訊增益均很小或沒有特征選擇位置。
算法5.2 ID3算法
輸入:訓練資料集D, 特征集A的門檻值 ϵ \epsilon ϵ
輸出:決策樹T
- 若D中所有的執行個體屬于同一類 C k C_k Ck,則T為單節點樹,并将類 C k C_k Ck作為該節點的類标記,傳回T;
- 若 A = ∅ A=\varnothing A=∅,則T為單節點樹,并将D中執行個體數最大的類 C k C_k Ck作為該節點的類标記,傳回T;
- 否則,計算A中各特征對D的資訊增益,選擇資訊增益最大的特征 A g A_g Ag;
- 如果 A g A_g Ag的資訊增益小于門檻值 ϵ \epsilon ϵ,則置T為單節點樹,并将D中執行個體數最大的類 C k C_k Ck作為該節點的類标記,傳回T;
- 否則, A g A_g Ag的每一可能值 a i a_i ai,依照, A g = a i A_g=a_i Ag=ai将D分割為若幹非空子集 D i D_i Di,将 D i D_i Di中執行個體數最大的類作為标記,建構子節點,由結點及其子節點構成樹T,傳回T;
- 對第i個子節點,以 D i D_i Di為訓練集,以 A − { A g } A-\{A_g\} A−{Ag}為特征集,遞歸地調用步(1)到(5),得到子樹 T i T_i Ti,傳回 T i T_i Ti。
python實作ID3算法(參考機器學習實戰)
from math import log
# 計算資訊熵
def calcShannonEnt(dataSet):
numEntries = len(dataSet) # 樣本數
labelCounts = {}
for featVec in dataSet: # 周遊每個樣本
currentLabel = featVec[-1] # 目前樣本的類别
if currentLabel not in labelCounts.keys(): # 生成類别字典
labelCounts[currentLabel] = 0
labelCounts[currentLabel] += 1
shannonEnt = 0.0
for key in labelCounts: # 計算資訊熵
prob = float(labelCounts[key]) / numEntries
shannonEnt = shannonEnt - prob * log(prob, 2)
return shannonEnt
# 劃分資料集,axis:按第幾個屬性劃分,value:要傳回的子集對應的屬性值
def splitDataSet(dataSet, axis, value):
retDataSet = []
featVec = []
for featVec in dataSet:
if featVec[axis] == value:
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis + 1:])
retDataSet.append(reducedFeatVec)
return retDataSet
# 選擇資訊增益最大的特征劃分資料
def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[0]) - 1 # 屬性的個數
baseEntropy = calcShannonEnt(dataSet)
bestInfoGain = 0.0
bestFeature = -1
for i in range(numFeatures): # 對每個屬性技術資訊增益
featList = [example[i] for example in dataSet]
uniqueVals = set(featList) # 該屬性的取值集合
newEntropy = 0.0
for value in uniqueVals: # 對每一種取值計算資訊增益
subDataSet = splitDataSet(dataSet, i, value)
prob = len(subDataSet) / float(len(dataSet))
newEntropy += prob * calcShannonEnt(subDataSet)
infoGain = baseEntropy - newEntropy
if (infoGain > bestInfoGain): # 選擇資訊增益最大的屬性
bestInfoGain = infoGain
bestFeature = i
return bestFeature
# 通過排序傳回出現次數最多的類别
def majorityCnt(classList):
classCount = {}
for vote in classList:
if vote not in classCount.keys(): classCount[vote] = 0
classCount[vote] += 1
sortedClassCount = sorted(classCount.iteritems(),
key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
# 遞歸建構決策樹
def createTree(dataSet, labels):
classList = [example[-1] for example in dataSet] # 類别向量
if classList.count(classList[0]) == len(classList): # 如果隻有一個類别,傳回
return classList[0]
if len(dataSet[0]) == 1: # 如果所有特征都被周遊完了,傳回出現次數最多的類别
return majorityCnt(classList)
bestFeat = chooseBestFeatureToSplit(dataSet) # 最優劃分屬性的索引
bestFeatLabel = labels[bestFeat] # 最優劃分屬性的标簽
myTree = {bestFeatLabel: {}}
del (labels[bestFeat]) # 已經選擇的特征不再參與分類
featValues = [example[bestFeat] for example in dataSet]
uniqueValue = set(featValues) # 該屬性所有可能取值,也就是節點的分支
for value in uniqueValue: # 對每個分支,遞歸建構樹
subLabels = labels[:]
myTree[bestFeatLabel][value] = createTree(
splitDataSet(dataSet, bestFeat, value), subLabels)
return myTree
x = [[1, 0, 0, 1],
[0, 1, 1, 1],
[0, 0, 1, 0]]
y = [1, 2, 1]
Trees = createTree(x,y)
print(Trees)
5.3.2 C4.5生成算法(python實作)
與ID3算法不同的是,C4.5算法采用資訊增益比選擇特征。
算法5.3 C4.5算法
輸入:訓練資料集D, 特征集A的門檻值 ϵ \epsilon ϵ
輸出:決策樹T
- 若D中所有的執行個體屬于同一類 C k C_k Ck,則T為單節點樹,并将類 C k C_k Ck作為該節點的類标記,傳回T;
- 若 A = ∅ A=\varnothing A=∅,則T為單節點樹,并将D中執行個體數最大的類 C k C_k Ck作為該節點的類标記,傳回T;
- 否則,計算A中各特征對D的資訊增益比,選擇資訊增益比最大的特征 A g A_g Ag;
- 如果 A g A_g Ag的資訊增益小于門檻值 ϵ \epsilon ϵ,則置T為單節點樹,并将D中執行個體數最大的類 C k C_k Ck作為該節點的類标記,傳回T;
- 否則, A g A_g Ag的每一可能值 a i a_i ai,依照, A g = a i A_g=a_i Ag=ai将D分割為若幹非空子集 D i D_i Di,将 D i D_i Di中執行個體數最大的類作為标記,建構子節點,由結點及其子節點構成樹T,傳回T;
- 對第i個子節點,以 D i D_i Di為訓練集,以 A − { A g } A-\{A_g\} A−{Ag}為特征集,遞歸地調用步(1)到(5),得到子樹 T i T_i Ti,傳回 T i T_i Ti。
python實作C4.5算法
隻需将上述算法中特征選擇函數更改為以資訊增益比最大為原則選擇的函數即可。
# 選擇資訊增益比最大的特征劃分資料
def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[0]) - 1 # 屬性的個數
baseEntropy = calcShannonEnt(dataSet)
bestInfoGainRatio = 0.0
bestFeature = -1
for i in range(numFeatures): # 對每個屬性計算資訊增益比
featList = [example[i] for example in dataSet]
uniqueVals = set(featList) # 該屬性的取值集合
newEntropy = 0.0
HA_D = 0
for value in uniqueVals: # 對每一種取值計算資訊增益比
subDataSet = splitDataSet(dataSet, i, value)
prob = len(subDataSet) / float(len(dataSet))
newEntropy += prob * calcShannonEnt(subDataSet)
infoGain = baseEntropy - newEntropy#資訊增益
infoGainRatio = infoGain/baseEntropy #資訊增益比
if (infoGainRatio > bestInfoGainRatio): # 選擇資訊增益比最大的屬性
bestInfoGainRatio = infoGainRatio
bestFeature = i
return bestFeature
5.4 決策樹的剪枝
決策樹生成算法遞歸地産生決策樹,往往會發生過拟合現象,學習時過多地考慮如何提高對訓練資料地正确分類,進而建構出過于複雜地決策樹。是以,需要對生成的樹進行剪枝。
決策樹剪枝原則:極小化決策樹整體的損失函數:
C α ( T ) = ∑ t = 1 ∣ T ∣ N t H t ( T ) + α ∣ T ∣ C_\alpha(T)=\sum\limits_{t=1}^{|T|}N_tH_t(T)+\alpha|T| Cα(T)=t=1∑∣T∣NtHt(T)+α∣T∣
這種剪枝方法在ISLR書中也稱為:Cost complexity pruning (or weakest link pruning)
采用的損失函數為:
C α ( T ) = ∑ m = 1 ∣ T ∣ ∑ x i ∈ R m ( y i − y R m ^ ) 2 + α ∣ T ∣ C_\alpha(T)=\sum\limits_{m=1}^{|T|}\sum\limits_{x_i\in R_m}(y_i-\hat{y_{Rm}})^2+\alpha|T| Cα(T)=m=1∑∣T∣xi∈Rm∑(yi−yRm^)2+α∣T∣
其中 R m R_m Rm表示葉子節點中的樣本集。
其中 ∣ T ∣ |T| ∣T∣為樹T的葉子節點個數,t為葉子節點,該葉節點含有 N t N_t Nt個樣本點,其中 k k k類的樣本點有 N t k N_{tk} Ntk個, H t ( T ) H_t(T) Ht(T)為葉節點t的經驗上, α > = 0 \alpha>=0 α>=0為參數。其中:
H t ( T ) = − ∑ k K N t k N t log N t k N t H_t(T)=-\sum\limits_k^K\frac{N_{tk}}{N_t}\log\frac{N_{tk}}{N_t} Ht(T)=−k∑KNtNtklogNtNtk
把該式子帶入損失函數中,第一項記作:
C ( T ) = ∑ t = 1 ∣ T ∣ N t H t ( T ) = − ∑ t = 1 ∣ T ∣ ∑ k K N t k N t log N t k N t C_(T)=\sum\limits_{t=1}^{|T|}N_tH_t(T)=-\sum\limits_{t=1}^{|T|}\sum\limits_k^K\frac{N_{tk}}{N_t}\log\frac{N_{tk}}{N_t} C(T)=t=1∑∣T∣NtHt(T)=−t=1∑∣T∣k∑KNtNtklogNtNtk
這時,決策樹的損失函數又可表示為
C α ( T ) = C ( T ) + α ∣ T ∣ C_\alpha(T) = C(T) + \alpha|T| Cα(T)=C(T)+α∣T∣
其中 C ( T ) C(T) C(T)表示模型對訓練資料的預測誤差,即模型與訓練資料的拟合程度,|T|表示葉子節點的個數(即模型的複雜度), α \alpha α作為懲罰系數,負責調節拟合程度與複雜度之間的權衡關系。 α = 0 \alpha=0 α=0即不考慮複雜度, α \alpha α越大,模型的複雜度越低。
算法5.4 樹的剪枝算法
輸入:整個樹,參數 α \alpha α.
輸出:修剪後的子樹 T α T_\alpha Tα.
(1)計算每個節點的經驗熵。
(2)遞歸地從樹的節點向上回縮。
設一組葉節點回縮到父節點之前與之後的整體樹分類為 T B , T A T_B,T_A TB,TA,其對應的損失函數值為 C α ( T B ) , C α ( T A ) C_\alpha(T_B),C_\alpha(T_A) Cα(TB),Cα(TA),如果
C α ( T B ) ≤ C α ( T A ) C_\alpha(T_B) \leq C_\alpha(T_A) Cα(TB)≤Cα(TA)
則進行剪枝,将父節點變為新的葉子節點。
注意:該步驟由于隻考慮兩個樹的損失函數的差,計算可以在局部進行(内部節點是否剪枝隻與以該節點為根節點的子樹有關)。是一種動态規劃的算法。
(3)傳回(2),直到不能繼續為止,得到損失函數最小的子樹 T α T_\alpha Tα。
5.5 CART算法
CART(分類與回歸樹):假設決策樹是二叉樹,内部節點特征的取值為“是”“否”,左分支取值為“是”,右分支取值為“否”。
5.5.1 CART生成
回歸樹:平方誤差最小化準則
分類樹:基尼系數最小化準則,進行特征選擇
-
回歸樹的生成
假設已經将輸入空間劃分為M個單元, R 1 , R 2 . . . R M R_1,R_2...R_M R1,R2...RM,并且在每個單元上有固定的輸出值 c m c_m cm,則模型表示為
f ( x ) = ∑ m = 1 M c m I ( x ∈ R m ) f(x)=\sum\limits_{m=1}^Mc_mI(x \in R_m) f(x)=m=1∑McmI(x∈Rm)
當輸入空間的劃分确定時,可以用平方誤差 ∑ x ∈ R m ( y i − f ( x i ) ) 2 \sum\limits_{x\in R_m}(y_i-f(x_i))^2 x∈Rm∑(yi−f(xi))2來表示回歸樹對于訓練資料的預測誤差,用平方誤差準則求解每個單元上的最優輸出值。已知,單元 R m R_m Rm上的 c m c_m cm的最優值 c m ^ \hat{c_m} cm^是 R m R_m Rm上所有輸入執行個體 x i x_i xi對應的輸出 y i y_i yi的均值:
c m ^ = a v e ( y i ∣ x i ∈ R m ) \hat{c_m} =ave({y_i}|x_i \in R_m) cm^=ave(yi∣xi∈Rm)
輸入空間劃分方法:
假設第j個變量 x ( j ) x^{(j)} x(j)和它取的值s,作為切分變量和切分點,則
R 1 ( j , s ) = { x ∣ x ( j ) ≤ s } a n d R 2 ( j , s ) = { x ∣ x ( j ) > s } R_1(j,s)=\{x|x^{(j)} \leq s \} \ \ and\ \ R_2(j,s)=\{x|x^{(j)} > s \} R1(j,s)={x∣x(j)≤s} and R2(j,s)={x∣x(j)>s}
其中最優切分變量與最優切分點的求解:
min j , s [ min c 1 ∑ x i ∈ R 1 ( j , s ) ( y i − c 1 ) 2 + min c 1 ∑ x i ∈ R 2 ( j , s ) ( y i − c 2 ) 2 ] \min\limits_{j,s}\left [ \min\limits_{c_1}\sum\limits_{x_i \in R_1(j,s)}(y_i-c_1)^2 + \min\limits_{c_1}\sum\limits_{x_i \in R_2(j,s)}(y_i-c_2)^2 \right ] j,smin⎣⎡c1minxi∈R1(j,s)∑(yi−c1)2+c1minxi∈R2(j,s)∑(yi−c2)2⎦⎤
這樣就可以得到切分後區域内的最優輸出值:
c 1 ^ = a v e ( y i ∣ x i ∈ R 1 ( j , s ) ) a n d c 2 ^ = a v e ( y i ∣ x i ∈ R 2 ( j , s ) ) \hat{c_1} =ave({y_i}|x_i \in R_1(j,s)) \ \ and\ \ \hat{c_2} =ave({y_i}|x_i \in R_2(j,s)) c1^=ave(yi∣xi∈R1(j,s)) and c2^=ave(yi∣xi∈R2(j,s))
對每個區域重複上述的劃分過程,直到滿足停止條件。
5.5 最小二乘回歸樹生成算法
輸入:訓練資料集D;
輸出:回歸樹f(x)。
(1)求解最優切分變量j與最優切分點s.(類似決策樹中的特征選擇和劃分資料集)
min j , s [ min c 1 ∑ x i ∈ R 1 ( j , s ) ( y i − c 1 ) 2 + min c 1 ∑ x i ∈ R 2 ( j , s ) ( y i − c 2 ) 2 ] \min\limits_{j,s}\left [ \min\limits_{c_1}\sum\limits_{x_i \in R_1(j,s)}(y_i-c_1)^2 + \min\limits_{c_1}\sum\limits_{x_i \in R_2(j,s)}(y_i-c_2)^2 \right ] j,smin⎣⎡c1minxi∈R1(j,s)∑(yi−c1)2+c1minxi∈R2(j,s)∑(yi−c2)2⎦⎤
(2)劃分區域,決定對應的輸出值
R 1 ( j , s ) = { x ∣ x ( j ) ≤ s } a n d R 2 ( j , s ) = { x ∣ x ( j ) > s } c m ^ = a v e ( y i ∣ x i ∈ R m ) m = 1 , 2 R_1(j,s)=\{x|x^{(j)} \leq s \} \ \ and\ \ R_2(j,s)=\{x|x^{(j)} > s \}\\ \hat{c_m} =ave({y_i}|x_i \in R_m) \ \ m=1,2 R1(j,s)={x∣x(j)≤s} and R2(j,s)={x∣x(j)>s}cm^=ave(yi∣xi∈Rm) m=1,2
(3)繼續對兩子區域調用步驟(1)(2),直至滿足停止條件
(4)将輸入空間劃分為M個區域,生成決策樹:
f ( x ) = ∑ m = 1 M c m I ( x ∈ R m ) f(x)=\sum\limits_{m=1}^Mc_mI(x \in R_m) f(x)=m=1∑McmI(x∈Rm)
-
分類樹的生成
與決策樹不同,分類樹中采用基尼指數選擇最優特征,确定最優二值切分點:
G i n i ( D ) = 1 − ∑ k = 1 K ( ∣ C k ∣ ∣ D ∣ ) 2 G i n i ( D , A ) = ∣ D 1 ∣ ∣ D ∣ G i n i ( D 1 ) + ∣ D 2 ∣ ∣ D ∣ G i n i ( D 2 ) Gini(D)=1-\sum\limits_{k=1}^K(\frac{|C_k|}{|D|})^2 \\ Gini(D,A)=\frac{|D_1|}{|D|}Gini(D_1)+\frac{|D_2|}{|D|}Gini(D_2) Gini(D)=1−k=1∑K(∣D∣∣Ck∣)2Gini(D,A)=∣D∣∣D1∣Gini(D1)+∣D∣∣D2∣Gini(D2)
C k C_k Ck是D中屬于第k類的樣本子集,K是類的個數。D1和D2為經特征值A的某一可能值a劃分的訓練集。
基尼指數數值越大,樣本集合的不确定性越大,樣本越混亂,與熵的特性相似。
算法5.6 CART生成算法
輸入:訓練資料集D,停止計算條件
輸出:CART決策樹。
從根節點開始,遞歸地對每個節點進行以下操作:
(1)設節點的訓練資料集為D,計算現有特征對個資料集的基尼指數。
(2)在所有可能特征A以及他們所有的可能切分點中,選擇基尼系數最小的特征及其切分點作為最優特征和最優切分點,切分該節點稱為兩個子節點,并将訓練集依據特征配置設定到兩個子節點中去。
(3)對兩個子節點遞歸地調用(1)(2),直至滿足停止條件。
(4)生成CART決策樹。
停止條件:結點中的樣本個數小于預定門檻值;樣本集的基尼系數小于一定門檻值;沒有更多特征。
5.5.2 CART剪枝
-
首先從生成算法産生的決策樹 T 0 T_0 T0底端不斷剪枝,直到 T 0 T_0 T0的根節點,形成一個子樹序列 { T 0 , T 1 . . . , T n } \{ T_0,T_1...,T_n\} {T0,T1...,Tn};
對 T 0 T_0 T0的任意内部節點t,以t為單節點樹的損失函數為:
C α ( t ) = C ( t ) + α C_\alpha(t)=C(t)+\alpha Cα(t)=C(t)+α
以t為根節點的子樹 T t T_t Tt的損失函數“
C α ( T t ) = C ( T t ) + α ∣ T t ∣ C_\alpha(T_t)=C(T_t)+\alpha|T_t| Cα(Tt)=C(Tt)+α∣Tt∣
(1)當 α = 0 \alpha=0 α=0及 α \alpha α充分小時,因為CART生成樹的原則,父節點的損失一定比以該父節點為根節點的子樹的損失函數大,是以有不等式
C α ( T t ) < C α ( t ) C_\alpha(T_t)<C_\alpha(t) Cα(Tt)<Cα(t)
(2)當 α \alpha α增大時,在某 α \alpha α時
C α ( T t ) = C α ( t ) α = C ( t ) − C ( T t ) ∣ T t ∣ − 1 C_\alpha(T_t)=C_\alpha(t)\\ \alpha=\frac{C(t)-C(T_t)}{|T_t|-1} Cα(Tt)=Cα(t)α=∣Tt∣−1C(t)−C(Tt)
(3)當 α \alpha α繼續增大時
C α ( T t ) > C α ( t ) C_\alpha(T_t)>C_\alpha(t) Cα(Tt)>Cα(t)
通過以上規律可以發現,當(2)情況發生時, T t T_t Tt與t有相同的損失函數,而t的節點少,是以t比 T t T_t Tt更可取,對 T t T_t Tt進行剪枝。
是以,對 T 0 T_0 T0中的每一内部結點t,計算對應 α = g ( t ) \alpha=g(t) α=g(t):
g ( t ) = C ( t ) − C ( T t ) ∣ T t ∣ − 1 g(t)=\frac{C(t)-C(T_t)}{|T_t|-1} g(t)=∣Tt∣−1C(t)−C(Tt)
表示剪枝後整體損失函數減少的程度。在 T 0 T_0 T0中剪去g(t)最小的 T t T_t Tt
為什麼選取最小得而不是最大g(t)? 假設所有結點中 g ( t 1 ) g(t_1) g(t1)最小, g ( t 2 ) g(t_2) g(t2)最大。若對結點2進行剪枝,此時結點1中 C α 1 ( T 1 ) > C α 1 ( t 1 ) C_{\alpha_1}(T_1)>C_{\alpha_1}(t_1) Cα1(T1)>Cα1(t1),不剪枝就會造成樹的損失變大,以此類推,其他結點也是如此,最終導緻整體累計的損失更大。若對結點1進行剪枝,其餘節點不剪枝的損失要小于剪枝後的損失,是以不應該修剪,這是整體的損失最小,是以應該剪掉g(t)最小的結點
将得到的子樹作為 T 1 T_1 T1,同時将最小的g(t)設為 α 1 \alpha_1 α1,則 T 1 T_1 T1為區間 [ α 1 , α 2 ) \left [ \alpha_1,\alpha_2 \right) [α1,α2)的最優子樹。
Breiman等人證明,将 α \alpha α從小增大, 0 = α 0 < α 1 < . . . < α n < + ∞ 0=\alpha_0<\alpha_1<...<\alpha_n<+\infty 0=α0<α1<...<αn<+∞,産生一系列的區間 [ α i , α i + 1 ) , i = 0 , 1 , . . . n \left [ \alpha_i,\alpha_{i+1} \right),\ \ i=0,1,...n [αi,αi+1), i=0,1,...n;剪枝得到的子樹列對應着區間 α ∈ [ α i , α i + 1 ) , i = 0 , 1 , . . . n \alpha \in \left [ \alpha_i,\alpha_{i+1} \right),\ \ i=0,1,...n α∈[αi,αi+1), i=0,1,...n的最優子樹序列 { T 0 , T 1 . . . , T n } \{ T_0,T_1...,T_n\} {T0,T1...,Tn},序列中的子樹時嵌套的
- 通過交叉驗證法(cross validation)在獨立的驗證資料集上對子樹序列進行測試平方誤差或基尼指數,平方誤差或基尼指數最小的子樹為最優子樹。當最優子樹确定時,對應的 α \alpha α值也确定了。
算法5.7 CART剪枝算法
輸入:CART算法生成的決策樹 T 0 T_0 T0
輸出:最優決策樹 T α T_\alpha Tα
(1)設 k = 0 , T = T 0 k=0,T=T_0 k=0,T=T0;
(2)設 α = + ∞ \alpha=+\infty α=+∞;
(3)自上而下的對内部節點t計算 C ( T t ) , ∣ T t ∣ , C(T_t),|T_t|, C(Tt),∣Tt∣,以及
g ( t ) = C ( t ) − C ( T t ) ∣ T t ∣ − 1 α = min ( α , g ( t ) ) g(t)=\frac{C(t)-C(T_t)}{|T_t|-1}\\ \alpha=\min(\alpha,g(t)) g(t)=∣Tt∣−1C(t)−C(Tt)α=min(α,g(t))
(4)對 g ( t ) = α g(t)=\alpha g(t)=α的内部節點t進行剪枝,并對葉子節點t以多數表決決定其類,得到樹T;
(5)設 k = k + 1 , α k = α , T k = T k=k+1,\alpha_k=\alpha,T_k=T k=k+1,αk=α,Tk=T;
(6)如果 T k T_k Tk不是由根節點及兩個葉子結點構成的樹,則傳回步驟二;否則令 T k = T n T_k=T_n Tk=Tn;
(7)采用交叉驗證法在子樹序列中選取最優子樹 T α T_\alpha Tα.
習題5.1(python實作)
該題代碼與5.3.2中寫的代碼相同,完整代碼如下:
from math import log
import operator
def calcShannonEnt(dataSet):
numEntries = len(dataSet)
labelCounts = {}
for featVec in dataSet:
currentLabel = featVec[-1]
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel] = 0
labelCounts[currentLabel] += 1
shannonEnt = 0.0
for key in labelCounts:
prob = float(labelCounts[key]) / numEntries
shannonEnt -= prob * log(prob, 2)
return shannonEnt
def splitDataSet(dataSet, axis, value):
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value:
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis + 1:])
retDataSet.append(reducedFeatVec)
return retDataSet
def chooseBestFeatureToSplit(dataSet):
numFeatures = len(dataSet[0]) - 1
baseEntropy = calcShannonEnt(dataSet)
bestInfoGainRatio= 0.0
bestFeature = -1
for i in range(numFeatures):
featList = [example[i] for example in dataSet]
uniqueVals = set(featList)
newEntropy = 0.0
for value in uniqueVals:
subDataSet = splitDataSet(dataSet, i, value)
prob = len(subDataSet) / float(len(dataSet))
newEntropy += prob * calcShannonEnt(subDataSet)
infoGainRatio = (baseEntropy - newEntropy) / baseEntropy
if (infoGainRatio > bestInfoGainRatio):
bestInfoGainRatio = infoGainRatio
bestFeature = i
return bestFeature
def majorityCnt(classList):
classCount = {}
for vote in classList:
if vote not in classCount.keys():
classCount[vote] = 0
classCount[vote] += 1
sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
def createTree(dataSet, labels):
classList = [example[-1] for example in dataSet]
if classList.count(classList[0]) == len(classList):
return classList[0]
if len(dataSet[0]) == 1:
return majorityCnt(classList)
bestFeat = chooseBestFeatureToSplit(dataSet)
bestFeatLabel = labels[bestFeat]
myTree = {bestFeatLabel: {}}
del (labels[bestFeat])
featValues = [example[bestFeat] for example in dataSet]
uniqueVals = set(featValues)
for value in uniqueVals:
subLabels = labels[:]
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels)
return myTree
dataSet = [[1, 0, 0, 1, 0],
[1, 0, 0, 2, 0],
[1, 1, 0, 2, 1],
[1, 1, 1, 1, 1],
[1, 0, 0, 1, 0],
[2, 0, 0, 1, 0],
[2, 0, 0, 2, 0],
[2, 1, 1, 2, 1],
[2, 0, 1, 3, 1],
[2, 0, 1, 3, 1],
[3, 0, 1, 3, 1],
[3, 0, 1, 2, 1],
[3, 1, 0, 2, 1],
[3, 1, 0, 3, 1],
[3, 0, 0, 1, 0] ]
labels = ['age', 'job', 'house', 'creadit']
mytree = createTree(dataSet,labels)
print (mytree)
習題5.2(python實作)
import numpy as np
# 計算資料集的平方誤差
def calcMSE(dataSet):
means = np.mean(dataSet)
sums = sum([(i - means) * (i - means) for i in dataSet]) * 1.0
return sums
# 選擇最優的劃分點
def chooseBestFeatureToSplit(dataSet):
nums = len(dataSet) - 1
if nums == 0:
return 0
best = 0
bestMES = 100000
for i in range(nums):
temp = calcMSE(dataSet[:i + 1]) + calcMSE(dataSet[i + 1:])
if temp <= bestMES:
bestMES = temp
best = i
return best
# 建樹過程
def createTree(dataSet, datalabel, left, right):
if right - left == 1:
# return dataSet[left]
return datalabel[left]
if left >= right:
return -1
# 最優劃分函數加上left為原資料集上的最優劃分下标
bestchoose = left + chooseBestFeatureToSplit(dataSet[left:right])
# print bestchoose+1
mytree = {datalabel[bestchoose]: {}}
mytree[datalabel[bestchoose]]['left'] = createTree(dataSet, datalabel, left, bestchoose)
mytree[datalabel[bestchoose]]['right'] = createTree(dataSet, datalabel, bestchoose + 1, right)
return mytree
dataSet = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
datalabel = [4.5, 4.75, 4.91, 5.34, 5.8, 7.05, 7.9, 8.23, 8.7, 9]
mytree = createTree(dataSet,datalabel,0,len(dataSet))
print (mytree)
習題5.3
證明:CART剪枝算法中,當 α \alpha α确定時,存在唯一的最小子樹 T α T_\alpha Tα使損失函數 C α ( T ) C_\alpha(T) Cα(T)最小。
這裡采用反證法,假設由兩個子樹A,B都可以使的損失函數最小,如下圖所示,假設最簡單的情況:
由于CART樹生成的特性,父節點的損失函數一定會比以父節點為根節點的子樹的損失函數大,是以當 α \alpha α确定時,子樹A,B一定是類似的關系。但是損失函數相同時,B更為簡單,是以應該對A進行剪枝,即A不是最小子樹。
參考柴前吾狼
習題5.4
證明:CART剪枝算法中求出的子樹序列 { T 0 , T 1 . . . , T n } \{ T_0,T_1...,T_n\} {T0,T1...,Tn}分别是區間 [ α i , α i + 1 ) , i = 0 , 1 , . . . n \left [ \alpha_i,\alpha_{i+1} \right),\ \ i=0,1,...n [αi,αi+1), i=0,1,...n的最優子樹 T α T_\alpha Tα,其中 0 = α 0 < α 1 < . . . < α n < + ∞ 0=\alpha_0<\alpha_1<...<\alpha_n<+\infty 0=α0<α1<...<αn<+∞。
暫未得以證明,有部落客一定給出部分證明,可供參考。
正在閱讀 Breiman L:《Classification and regression trees》尋求方法。
參考
[1] https://www.jianshu.com/p/da5d7a4d38dd
[2] 李航《統計學習方法》
[3] 《機器學習實戰》
[4] https://blog.csdn.net/familyshizhouna/article/details/72551841
筆者剛剛入門學習機器學習,因為水準有限,李航老師的書對入門不是特别友好,還在生啃階段,如果有錯誤還請之處。