什么是决策树?
决策树算法实现分类问题可以被理解为不断地进行条件语句判断,最终实现分类,如下图:
进行判断的分支很像树的枝干,被分出来的类别像是枝干上的叶子,所以将这个图称为决策树。所以,用决策树算法解决问题的第一步就是画出决策树。
决策树的结构
在画决策树之前,要先了解决策树的结构是怎样的。
在分类阶段时,从根开始,按照决策树的分类属性逐层往下划分,直到叶节点,获得分类结果。
需要的一些基本概念
画出决策树的第一步即决定哪一个特征适合当作根节点。在这里,我们引入一些概念。
1、熵
在化学中,熵用来衡量一种物质内部的混乱程度,即这种物质的纯度。
在概率论中,设有一事件
X
X
X,则它的熵
H
(
X
)
H(X)
H(X)可以衡量事件
X
X
X发生的不确定性。
熵的公式为:
H
(
X
)
=
−
∑
i
=
1
n
P
(
X
)
l
o
g
2
(
P
(
X
)
)
H(X)=-\sum_{i=1}^{n}P(X)log_{2}(P(X))
H(X)=−i=1∑nP(X)log2(P(X))
举例来说,假设这里有两组数据,A组数据为:1,2,3,4,5,4,6,1,7,8;B组数据为:1,2,1,1,1,1,1,1,1,1。那么我们可以很清楚地判断出A组数据的不纯度比较高,也就是熵值比较高。
那么,怎么用熵的公式来证明的确是A组数据的熵值较高呢?
首先,我们可以求出在A中随机挑出各个数字的概率,那么在A组数据中,挑出各个数据的概率不是0.1就是0.2,而在B组数据中,选到1的概率达到了0.9。将这些概率代到熵的公式中去以后,由于对数函数的性质,概率越小,得到的对数值的绝对值越大,所以A组数据中得到的对数值都比较大,而B组数据中由于有0.9的存在,使得熵远远小于A组的熵。
总而言之,当一组数据中的纯度越高,熵值就越小。
2、Gini系数
G
i
n
i
(
p
)
=
∑
k
=
1
K
p
k
(
1
−
p
k
)
=
1
−
∑
k
=
1
K
p
k
2
Gini(p)=\sum_{k=1}^{K}p_{k}(1-p_{k})=1-\sum_{k=1}^{K}p_{k}^2
Gini(p)=k=1∑Kpk(1−pk)=1−k=1∑Kpk2
从式子可以看出,当概率越小时,得到的Gini系数越大,这和熵值的定义是一样的。
怎样画出决策树?
基本想法
构造数的基本想法是随着树深度的增加,节点的熵迅速地降低。熵降低的速度越快越好,这样我们有望得到一颗高度最矮的决策树。
举例说明
下图为14组历史数据,记录了14天中小明是否出去打球的情况,以及当天的天气(outlook),温度(temperature),湿度(humidity)和是否刮风(windy)。
首先,在没有给定任何天气信息时,根据历史数据,我们只知道新的一天打球的概率是9/14,不打球的概率是5/14。那么,此时的熵为:
接着,我们可以计算出分别以以上四个特征(天气,温度,湿度和是否刮风)为根节点时的最初的熵与经过分支之后的熵的变化值:
对于基于天气的划分,
- 当outlook=sunny时,有2/5的概率打球,3/5的概率不打球,则可以算出熵值为 − 0.4 l o g 2 ( 0.4 ) − 0.6 l o g 2 ( 0.6 ) = 0.971 -0.4log_2(0.4)-0.6log_2(0.6)=0.971 −0.4log2(0.4)−0.6log2(0.6)=0.971;
- 同样地,outlook=overcast时,熵值为0;
- outlook=rainy时,熵值为0.971。
而根据历史数据统计,outlook取值为sunny,overcast,rainy的概率分别为5/14,4/14,5/14,所以当已知变量outlook的值时,信息熵为:
5 / 14 × 0.971 + 4 / 14 × 0 + 5 / 14 × 0.971 = 0.693 ( 类 似 于 全 概 率 事 件 ) 5/14\times0.971+4/14\times 0+5/14\times 0.971=0.693(类似于全概率事件) 5/14×0.971+4/14×0+5/14×0.971=0.693(类似于全概率事件)
这样一来,系统熵就从0.940下降到了0.693,信息增益gain(outlook)=0.940-0.693=0.247。
同样可以计算出gain(temperature)=0.029,gain(humidity)=0.152,gain(windy)=0.048。
gain(outlook)最大,即outlook在第一步使系统的信息熵下降得最快,所以决策树的根节点就取outlook。
以什么标准来确定节点?
在上面的例子中,我们用信息增益gain来确定下一个节点是什么特征,我们一般称这种方法为ID3。
但是这种方法有个缺点,就是当历史数据中有一些特征中存在的属性较多,但每个属性对应的样本个数很少时,ID3很难画出真正的决策树。
比如在历史数据中增加一列特征ID,表示第几天,那么ID这个特征中包含的属性有1,2,……,14,每个属性都只包含一个样本,那么如果要将ID作为根节点来划分的话,根据上例的分析可以得出信息熵已经归零,即它的信息增益一定是最大的。
所以,我们可以使用C4.5方法,用信息增益率为标准划分节点:
信
息
增
益
率
=
g
a
i
n
H
信息增益率=\frac{gain}{H}
信息增益率=Hgain
因为属性很多,所以熵值一定很大,那么其信息增益率就会减小,就不会被选作根节点了。
除了用ID3和C4.5之外,CART方法用Gini系数作为标准也很常用。
CART是英文Classification And Regression Tree的简写,又称为分类回归树。从它的名字我们就可以看出,它是一个很强大的算法,既可以用于分类还可以用于回归。
CART算法使用二元切分法来处理连续型变量。而使用二元切分法则易于对树构建过程进行调整以处理连续型特征。具体的处理方法是:如果特征值大于给定值就走左子树,否则就走右子树。
CART算法有两步:
- 决策树生成:递归地构建二叉决策树的过程,基于训练数据集生成决策树,生成的决策树要尽量大;自上而下从根开始建立节点,在每个节点处要选择一个最好的属性来分裂,使得子节点中的训练集尽量的纯。
- 决策树剪枝:用验证数据集对已生成的树进行剪枝并选择最优子树,这时令损失函数最小作为剪枝的标准。
总结来说:
用什么来评价一棵决策树的好坏?
评价函数:
C
(
T
)
=
∑
t
∈
l
e
a
f
N
t
×
H
(
t
)
C(T)=\sum_{t\in leaf}N_t\times H(t)
C(T)=t∈leaf∑Nt×H(t)
其中,
N
t
N_t
Nt是指第
t
t
t个叶节点上的样本个数,
H
(
t
)
H(t)
H(t)是指第
t
t
t个叶节点的熵(或Gini系数)。评价函数越小,说明得到的决策树越好。
决策树剪枝
如果不剪枝,最后形成的决策树很有可能一个叶节点对应一个样本,这对训练集来说准确率达到了百分之百,但在测试集中效果很差,即过拟合现象。
预剪枝和后剪枝都可以防止过拟合现象。
在预剪枝中,可以规定一个深度限制为3,则决策树中划分出三层节点后就会停止;或者规定一个节点中的样本数少于50个时停止。
在后剪枝中,评价函数被改变为如上图所示,
α
\alpha
α越大,对叶节点数的要求越严格。
python实现
我们使用决策树来对wine dataset中的酒进行分类。
1、导入需要的库
from sklearn import tree
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
import pandas as pd
import graphviz
import matplotlib.pyplot as plt
2、读取数据
# 读取数据
wine = load_wine()
print(wine.data.shape)
print(wine.target)
pd.concat([pd.DataFrame(wine.data),pd.DataFrame(wine.target)],axis=1)
3、划分训练集和测试集
Xtrain, Xtest, Ytrain, Ytest = train_test_split(wine.data,wine.target,test_size=0.3)
print(Xtrain.shape)
print(Xtest.shape)
print(Ytrain.shape)
print(Ytest.shape)
4、建立模型
# 建立模型
clf = tree.DecisionTreeClassifier(criterion="entropy", random_state=30, splitter="random",
max_depth=3, min_samples_leaf=10, min_samples_split=10 )
clf = clf.fit(Xtrain, Ytrain)
score = clf.score(Xtest, Ytest) #返回预测的准确度
print(score)
函数介绍之tree.DecisionTreeClassifier:
输入:
- criterion:划分节点的标准:可以使用"entropy"(熵)或"gini"(基尼系数)。
- random_state:用来设置分枝中的随机模式的参数,默认None,在高维度时随机性会表现更明显,对于低维度的数据 (比如鸢尾花数据集),随机性几乎不会显现。输入任意整数,会一直长出同一棵树,让模型稳定下来。
- splitter:也是用来控制决策树中的随机选项的,有两种输入值,输入”best",决策树在分枝时虽然随机,但是还是会 优先选择更重要的特征进行分枝(重要性可以通过属性feature_importances_查看);输入“random",决策树在分枝时会更加随机,树会因为含有更多的不必要信息而更深更大,并因这些不必要信息而降低对训练集的拟合。这也是防止过拟合的一种方式。当你预测到你的模型会过拟合时,可以用这个以及上一个参数来帮助你降低树建成之后过拟合的可能性。当然,树一旦建成,我们依然是使用剪枝参数来防止过拟合。
- max_depth:限制树的最大深度,超过设定深度的树枝全部剪掉。这是用得最广泛的剪枝参数,在高维度低样本量时非常有效。决策树多生长一层,对样本量的需求会增加一倍,所以限制树深度能够有效地限制过拟合。在集成算法中也非常实用。实际使用时,建议从max_depth=3开始尝试,看看拟合的效果再决定是否增加设定深度。
- min_samples_leaf:一个节点在分枝后的每个子节点都必须包含至少min_samples_leaf个训练样本,否则分枝就不会发生,或者,分枝会朝着满足每个子节点都包含min_samples_leaf个样本的方向去发生,一般搭配max_depth使用,在回归树中有神奇的效果,可以让模型变得更加平滑。这个参数的数量设置得太小会引起过拟合,设置得太大就会阻止模型学习数据。一般来说,建议从min_samples_leaf=5开始使用。如果叶节点中含有的样本量变化很大,建议输入浮点数作为样本量的百分比来使用。同时,这个参数可以保证每个叶子的最小尺寸,可以在回归问题中避免低方差,过拟合的叶子节点出现。对于类别不多的分类问题,min_samples_leaf=1通常就是最佳选择。
- min_samples_split:一个节点必须要包含至少min_samples_split个训练样本,这个节点才允许被分枝,否则分枝就不会发生。
5、画出决策树
feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚','类黄酮','非黄烷类酚类','花青素','颜色强度','色调','od280/od315稀释葡萄酒','脯氨酸']
dot_data = tree.export_graphviz(clf,
feature_names= feature_name,
class_names=["琴酒","雪莉","贝尔摩德"],
filled=True,
rounded=True)
graph = graphviz.Source(dot_data)
graph
6、查看特征重要性
[*zip(feature_name,clf.feature_importances_)]
7、查看决策树应用于训练集和测试集上的得分
score_train = clf.score(Xtrain,Ytrain)
score_test = clf.score(Xtest,Ytest)
print(score_train)
print(score_test)
8、确认最优的剪枝参数
test = []
for i in range(10):
clf = tree.DecisionTreeClassifier(max_depth=i+1, criterion="entropy", random_state=30,splitter="random")
clf = clf.fit(Xtrain, Ytrain)
score = clf.score(Xtest, Ytest)
test.append(score)
plt.plot(range(1,11),test,color="red",label="max_depth")
plt.legend()
plt.show()
python实现CART回归树
from sklearn.tree import DecisionTreeRegressor
from sklearn import linear_model # 用线性回归做对比
ex0 = pd.read_table('ex0.txt',header=None)
ex0.describe()
plt.scatter(ex0.iloc[:,1].values,ex0.iloc[:,-1].values)
#用来训练的数据
x = (ex0.iloc[:, 1].values).reshape(-1,1) # 将行数组转换为列数组
y = (ex0.iloc[:,-1].values).reshape(-1,1)
# 预测
X_test = np.arange(0, 1, 0.01)[:, np.newaxis] # 另一种方法将行数组转换为列数组
print(X_test.shape)
y_1 = model1.predict(X_test)
y_2 = model2.predict(X_test)
y_3 = model3.predict(X_test)
# 可视化结果
plt.figure() # 创建画布
plt.scatter(x, y, s=20, edgecolor="black",c="darkorange", label="data")
plt.plot(X_test, y_1, color="cornflowerblue",label="max_depth=1", linewidth=2)
plt.plot(X_test, y_2, color="yellowgreen", label="max_depth=3", linewidth=2)
plt.plot(X_test, y_3, color='red', label='liner regression', linewidth=2)
plt.xlabel("data")
plt.ylabel("target")
plt.title("Decision Tree Regression")
plt.legend()
plt.show()