決策樹公式推導
(1)信息熵--用來度量樣本集合純度最常用的一種指標,定義如下:
Ent(D)=−k=1∑∣Y∣pklog2pk(式1)
其中,D={(x1,y1),(x2,y2),⋯,(xm,ym)}表示樣本集合,∣y∣表示樣本類別總數,Pk表示第k類樣本所佔的比例,而且滿足0≤Pk≤1⋅∑k=1∣y∣pk=1。上式值越小,則純度越高。(樣本儘可能屬於同一類別,則“純度”更高)
[證]:證明0≤Ent(D)≤log2∣Y∣:
已知集合D的信息熵的定義爲
Ent(D)=−k=1∑∣Y∣pklog2pk(式2)
其中,∣Y∣表示樣本類別總數,pk表示第k類樣本所佔的比例,且0≤pk≤1,∑k=1npk=1。如若令∣Y∣=n,pk=xk,那麼信息熵Ent(D)就可以看作一個n元實值函數,也即
Ent(D)=f(x1,...,xn)=−k=1∑nxklog2xk(式3)
其中,0≤xk≤1,∑k=1nxk=1,下面考慮求該多元函數的最值。
最大值:
如果不考慮約束0≤xk≤1,僅考慮∑k=1nxk=1的話,對f(x1,...,xn)求最大值等價於如下最小化問題
min s.t. k=1∑nxklog2xkk=1∑nxk=1(式4)
其中,y=xlogx函數圖像如下:
這樣一來,在0≤xk≤1時,此問題爲凸優化問題,而對於凸優化問題來說,滿足KKT條件的點即爲最優解。由於此最小化問題僅含等式約束,那麼能令其拉格朗日函數的一階偏導數等於0的點即爲滿足KKT條件的點。根據拉格朗日乘子法可知,該優化問題的拉格朗日函數如下:
L(x1,...,xn,λ)=k=1∑nxklog2xk+λ(k=1∑nxk−1)(式5)
其中,λ爲拉格朗日乘子。對L(x1,...,xn,λ)分別關於x1,...,xn,λ求一階偏導數,並令偏導數等於0可得到下式:
∂x1∂L(x1,...,xn,λ)對x2求偏導數有:∂x2∂L(x1,...,xn,λ)⋮對xn求偏導數有:∂xn∂L(x1,...,xn,λ)對λ求偏導數有:∂λ∂L(x1,...,xn,λ)=∂x1∂[k=1∑nxklog2xk+λ(k=1∑nxk−1)]=0=log2x1+x1⋅x1ln21+λ=0=log2x1+ln21+λ=0⇒λ=−log2x1−ln21=∂x2∂[k=1∑nxklog2xk+λ(k=1∑nxk−1)]=0⇒λ=−log2x2−ln21=∂xn∂[k=1∑nxklog2xk+λ(k=1∑nxk−1)]=0⇒λ=−log2xn−ln21=∂λ∂[k=1∑nxklog2xk+λ(k=1∑nxk−1)]=0⇒k=1∑nxk=1實際上就等於約束條件。(式6)
整理一下可得:
⎩⎪⎨⎪⎧λ=−log2x1−ln21=−log2x2−ln21=...=−log2xn−ln21k=1∑nxk=1(式7)
由以上兩個方程可以解得:
x1=x2=...=xn=n1(式8)
又因爲xk還需滿足約束0≤xk≤1,這樣就得到0≤n1≤1,所以x1=x2=...=xn=n1是滿足所有約束的最優解,也即爲當前最小化問題的最小值點,同時也是f(x1,...,xn)的最大值點。將x1=x2=...=xn=n1代入f(x1,...,xn)中可得:
f(n1,...,n1)=−k=1∑nn1log2n1=−n⋅n1log2n1=log2n(式9)
所以f(x1,...,xn)在滿足約束0≤xk≤1,∑k=1nxk=1時的最大值爲log2n。
最小值:
如果不考慮約束∑k=1nxk=1,僅考慮0≤xk≤1,f(x1,...,xn)可以看做是n個互不相關的一元函數的加和,即:
f(x1,...,xn)=k=1∑ng(xk)(式10)
其中,g(xk)=−xklog2xk,0≤xk≤1。那麼當g(x1),g(x2),...,g(xn)分別取到其最小值時,f(x1,...,xn)也就取到了最小值。所以接下來考慮分別求g(x1),g(x2),...,g(xn)各自的最小值,由於g(x1),g(x2),...,g(xn)的定義域和函數表達式均相同,所以只需求出g(x1)的最小值也就求出了g(x2),...,g(xn)的最小值。下面考慮求g(x1)的最小值,首先對g(x1)關於x1求一階和二階導數:
g′(x1)=dx1d(−x1log2x1)=−log2x1−x1⋅x1ln21=−log2x1−ln21(式11)
g′′(x1)=dx1d(g′(x1))=dx1d(−log2x1−ln21)=−x1ln21(式12)
發現,當0≤xk≤1時g′′(x1)=−x1ln21恆小於0,所以g(x1)是一個在其定義域範圍內開口向下的凹函數,那麼其最小值必然在邊界取,於是分別取x1=0和x1=1,代入g(x1)可得:
g(0)=−0log20=0(式13)
g(1)=−1log21=0(式14)
所以,g(x1)的最小值爲0,同理可得g(x2),...,g(xn)的最小值也爲0,那麼f(x1,...,xn)的最小值此時也爲0。
但是,此時是不考慮約束∑k=1nxk=1,僅考慮0≤xk≤1時取到的最小值,若考慮約束∑k=1nxk=1的話,那麼f(x1,...,xn)的最小值一定大於等於0。如果令某個xk=1,那麼根據約束∑k=1nxk=1可知x1=x2=...=xk−1=xk+1=...=xn=0,將其代入f(x1,...,xn)可得:
f(0,0,...,0,1,0,...,0)=−0log20−0log20...−0log20−1log21−0log20...−0log20=0(式15)
所以xk=1,x1=x2=...=xk−1=xk+1=...=xn=0一定是f(x1,...,xn)在滿足約束∑k=1nxk=1和0≤xk≤1的條件下的最小值點,其最小值爲0。
綜上可知,當f(x1,...,xn)取到最大值時:x1=x2=...=xn=n1,此時樣本集合純度最低;當f(x1,...,xn)取到最小值時:xk=1,x1=x2=...=xk−1=xk+1=...=xn=0,此時樣本集合純度最高。
(2)條件熵–在已知樣本屬性a的取值情況下,度量樣本集合純度的一種指標:
H(D∣a)=v=1∑V∣D∣∣Dv∣Ent(Dv)(式16)
其中,a表示樣本的某個屬性,假定屬性a有V個可能的取值{a1,a2,⋯,aV},樣本集合D中在屬性a上取值爲av的樣本記爲Dv,Ent(Dv)爲樣本集合Dv的信息熵。H(D∣a)越小,純度越高。
1. ID3 決策樹
ID3決策樹----以信息增益爲準則來選擇劃分屬性的決策樹。信息增益定義如下:
Gain(D,a)=Ent(D)−v=1∑V∣D∣∣Dv∣Ent(Dv)=Ent(D)−H(D∣a)(式17)
選擇信息增益值最大的屬性作爲劃分屬性。因爲信息增益越大,則意味着使用該屬性來進行劃分所獲得的“純度提升”越大。
將(式17)進一步展開寫爲如下形式:
Gain(D,a)=Ent(D)−v=1∑V∣D∣∣Dv∣Ent(Dv)=Ent(D)−v=1∑V∣D∣∣Dv∣⎝⎛−k=1∑∣Y∣pklog2pk⎠⎞=Ent(D)−v=1∑V∣D∣∣Dv∣⎝⎛−k=1∑∣Y∣∣Dv∣∣Dkv∣log2∣Dv∣∣Dkv∣⎠⎞(式18)
其中,Dkv爲樣本集合D中在屬性a上取值爲av且類別爲k的樣本。
⟹以信息增益爲劃分準則的ID3決策樹對可取值數目較多的屬性有所偏好。
2. C4.5決策樹
C4.5決策樹----以信息增益率爲準則來選擇劃分屬性的決策樹。信息增益率定義爲:
Gain-ratio(D,a)=IV(a)Gain(D,a)其中,IV(a)=−v=1∑V∣D∣∣Dv∣log2∣D∣∣Dv∣(19)
增益率準則,對可取值數目較少的屬性有所偏好,因此C4.5算法並不是直接選擇增益率最大的劃分屬性,而是使用了一個啓發式;先從候選劃分屬性中找出信息增益高於平均水平的屬性,再從中選擇增益率最高的。
3. CART決策樹
CART決策樹----以基尼指數爲準則來選擇劃分屬性的決策樹。基尼值定義爲:
Gini(D)=k=1∑∣Y∣k′=k∑pk′pk=k=1∑∣Y∣pkk′=k∑pk′=k=1∑∣Y∣pk(1−pk)=1−k=1∑∣Y∣pk2(式20)
基尼指數:
Gini-index(D,a)=v=1∑V∣D∣∣Dv∣Gini(Dv)(式21)
基尼值和基尼指數越小,樣本集合純度越高。
CART決策樹分類算法:
1.根據基尼指數公式找出基尼指數最小的屬性a∗
2.計算屬性a∗的所有可能取值的基尼值Gini(Dv),並選擇基尼值最小的取值a∗v作爲劃分點。將集合D劃分爲D1和D2兩個集合(節點),其中D1集合的樣本爲a∗=a∗v的樣本,D2集合爲a∗=a∗v的樣本。
3.對集合D1,D2重複步驟1和2,知道滿足條件。
4. 決策樹的構建
author by xiaoyao 這裏我使用酒的數據集來演示一下決策樹的構建。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn import tree, datasets
from sklearn.model_selection import train_test_split
wine = datasets.load_wine()
X = wine.data[:,:2]
y = wine.target
X_train, X_test, y_train, y_test = train_test_split(X, y)
import warnings
warnings.filterwarnings("ignore")
clf = tree.DecisionTreeClassifier(max_depth=1)
clf.fit(X_train, y_train)
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=1,
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')
cmap_light = ListedColormap(["#FFAAAA", "#AAFFAA", "#AAAAFF"])
cmap_bold = ListedColormap(["#FF0000", "#00FF00", "#0000FF"])
x_min, x_max = X_train[:,0].min() - 1, X_train[:,0].max() + 1
y_min, y_max = X_train[:,1].min() - 1, X_train[:,1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, .02),np.arange(y_min, y_max, .02))
z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
z = z.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx, yy, z, cmap=cmap_light)
plt.scatter(X[:,0], X[:,1], c=y, cmap=cmap_bold, edgecolor="k",s=20)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
plt.title("Classifier:(max_depth = 1)")
plt.show()
最大深度爲1時,分類器的表現不很好,下面加大深度
clf2 = tree.DecisionTreeClassifier(max_depth=3)
clf2.fit(X_train, y_train)
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=3,
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')
cmap_light = ListedColormap(["#FFAAAA", "#AAFFAA", "#AAAAFF"])
cmap_bold = ListedColormap(["#FF0000", "#00FF00", "#0000FF"])
x_min, x_max = X_train[:,0].min() - 1, X_train[:,0].max() + 1
y_min, y_max = X_train[:,1].min() - 1, X_train[:,1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, .02),np.arange(y_min, y_max, .02))
z = clf2.predict(np.c_[xx.ravel(), yy.ravel()])
z = z.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx, yy, z, cmap=cmap_light)
plt.scatter(X[:,0], X[:,1], c=y, cmap=cmap_bold, edgecolor="k",s=20)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
plt.title("Classifier:(max_depth = 3)")
plt.show()
此時,分類器就可以進行3個分類的識別,而且大部分的數據點都進入了正切的分類。接下來進一步調整深度。
clf3 = tree.DecisionTreeClassifier(max_depth=5)
clf3.fit(X_train, y_train)
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=5,
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')
cmap_light = ListedColormap(["#FFAAAA", "#AAFFAA", "#AAAAFF"])
cmap_bold = ListedColormap(["#FF0000", "#00FF00", "#0000FF"])
x_min, x_max = X_train[:,0].min() - 1, X_train[:,0].max() + 1
y_min, y_max = X_train[:,1].min() - 1, X_train[:,1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, .02),np.arange(y_min, y_max, .02))
z = clf3.predict(np.c_[xx.ravel(), yy.ravel()])
z = z.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx, yy, z, cmap=cmap_light)
plt.scatter(X[:,0], X[:,1], c=y, cmap=cmap_bold, edgecolor="k",s=20)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
plt.title("Classifier:(max_depth = 3)")
plt.show()
發現,性能進一步提升了。接下來我使用graphviz這個library來展示這個過程。
安裝方式:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple graphviz
%pwd
'D:\\python code\\8messy'
import graphviz
from sklearn.tree import export_graphviz
export_graphviz(clf2, out_file="./wine.dot", class_names=wine.target_names,
feature_names = wine.feature_names[:2], impurity=False,filled=True)
with open('./wine.dot') as f:
dot_graph = f.read()
graphviz.Source(dot_graph)