决策树公式推导
(1)信息熵--用来度量样本集合纯度最常用的一种指标,定义如下:
Ent(D)=−k=1∑∣Y∣pklog2pk(式1)
其中,D={(x1,y1),(x2,y2),⋯,(xm,ym)}表示样本集合,∣y∣表示样本类别总数,Pk表示第k类样本所占的比例,而且满足0≤Pk≤1⋅∑k=1∣y∣=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∣log2D∣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)