1. 简介
-
决策树是机器学习中属于监督类算法,有ID3,C4.5,C5.0,CART(Classification and Regression Tree),CHAID(CHi-squared Automatic Interaction Detection),决策树是一种树形结构,每个内部节点表示一种属性判断,每个分支表示一种判断结果,每个叶子节点(终端节点)表示一种分类结果。
-
下图就是一种流程图形式的决策树:
2.决策树的构造
- 决策树的优缺点
决策树优点 | 决策树缺点 |
---|---|
计算复杂度不高,输出结果容易理解,对中间值缺失不敏感,可以处理不相关特征数据 | 可能会产生过度匹配问题 |
- 适用数据类型:数值型和标称型。
标称型:标称型目标变量的结果只在有限目标集中取值,比如真与假(标称型目标变量主要用于分类)
数值型:数值型目标变量则可以从无限的数值集合中取值,如0.555,666.666等 (数值型目标变量主要用于回归分析)
- 下面是构造一个决策树的伪代码:
CreateBranch():
检查数据集中的每个子项是否属于同一分类:
If so return 类标签
Else
寻找划分数据集的最好特征
划分数据集
创建分支节点
for 每个划分的子集
调用函数CreateBranch并增加返回结果到分支节点中
return 分支节点
- 决策树一般流程
决策树的一般流程 |
---|
1.收集数据:可以使用任何方法 |
2.准备数据:树构造算法只适用于标称型数据,因此数值型数据必须离散化 |
3.分析数据:可以使用任何方法,构造树完成之后,我们应该检查图形是否符合预期 |
4.训练算法:构造树的数据结构 |
5.测试算法:使用经验树计算错误率 |
6.使用算法:此步骤可以适用于任何监督学习算法,而使用决策树可以更好地理解数据的内在含义。 |
- ID3算法如何划分数据集:
划分数据的大原则是:将无序的数据变得更加有序。
在划分数据之前之后的信息发生变化成为信息增益,知道如何计算信息增益,我们就可以计算每个特征值划分数据集获得的信息增益,获得信息增益最高的特征就算最好的选择。
下面我们需要知道两个专有名词:信息增益(information gain)和熵(entropy),熵定义为信息的期望值,那么信息的定义为:
l
(
x
i
)
=
−
l
o
g
2
p
(
x
i
)
l(x_i) = -log_2p(x_i)
l(xi)=−log2p(xi)
其中
p
(
x
i
)
p(x_i)
p(xi)是选择该分类的概率。
为了计算熵,我们需要计算所有可能值包含的信息期望值,通过下面的公式得到:
E
n
t
(
D
)
=
−
∑
i
=
1
n
p
(
x
i
)
l
o
g
2
p
(
x
i
)
Ent(D) = -\sum_{i=1}^np(x_i)log_2p(x_i)
Ent(D)=−i=1∑np(xi)log2p(xi)
Ent(D)的值越小,说明其纯度越高;计算信息熵时约定:若 p = 0 , 则 p l o g 2 p = 0 , H 最 小 值 为 0 , 最 大 值 为 l o g 2 n . p=0,则plog_2p=0,H最小值为0,最大值为log_2n. p=0,则plog2p=0,H最小值为0,最大值为log2n.
信息增益定义:
假
定
离
散
属
性
a
有
V
个
可
能
的
取
值
{
a
1
,
a
2
,
.
.
.
,
a
V
}
,
若
使
用
a
来
对
样
本
集
D
进
行
划
分
,
则
会
产
生
V
个
分
支
节
点
,
其
中
第
v
个
分
支
节
点
包
含
了
D
中
所
有
在
属
性
a
上
取
值
为
a
v
的
样
本
,
记
为
D
v
.
我
们
可
根
据
熵
的
公
式
计
算
出
D
v
的
信
息
熵
,
再
考
虑
到
不
同
分
支
结
点
所
包
含
的
样
本
数
不
同
,
给
分
支
结
点
赋
予
权
重
∣
D
v
∣
/
∣
D
∣
,
即
样
本
越
多
的
分
支
结
点
的
影
响
越
大
,
于
是
可
计
算
出
用
属
性
a
对
样
本
集
D
进
行
划
分
所
获
得
的
信
息
增
益
(
i
n
f
o
r
m
a
t
i
o
n
g
a
i
n
)
:
G
a
i
n
(
D
,
a
)
=
E
n
t
(
D
)
−
∑
v
=
1
V
∣
D
v
∣
∣
D
∣
E
n
t
(
D
v
)
.
假定离散属性a有V个可能的取值\{a^1,a^2,...,a^V\},若使用a来对样本集D进行划分,则会产生V个分支节点,\\其中第v个分支节点包含了D中所有在属性a上取值为a^v的样本,记为D^v.我们可根据熵的公式\\计算出D^v的信息熵,再考虑到不同分支结点所包含的样本数不同,给分支结点赋予权重|D^v|/|D|,\\即样本越多的分支结点的影响越大,于是可计算出用属性a对样本集D进行划分所获得的\\信息增益(information gain): \\ Gain(D,a) =Ent(D) - \sum_{v=1}^V\frac{|D^v|}{|D|}Ent(D^v).
假定离散属性a有V个可能的取值{a1,a2,...,aV},若使用a来对样本集D进行划分,则会产生V个分支节点,其中第v个分支节点包含了D中所有在属性a上取值为av的样本,记为Dv.我们可根据熵的公式计算出Dv的信息熵,再考虑到不同分支结点所包含的样本数不同,给分支结点赋予权重∣Dv∣/∣D∣,即样本越多的分支结点的影响越大,于是可计算出用属性a对样本集D进行划分所获得的信息增益(informationgain):Gain(D,a)=Ent(D)−v=1∑V∣D∣∣Dv∣Ent(Dv).
一般而言,信息增益越大,则意味着使用属性a来划分所获得的”纯度提升"越大。即选则属性
a
∗
=
a
r
g
a
∈
A
m
a
x
G
a
i
n
(
D
,
a
)
a_* = arg_{a\in A}max \,Gain(D,a)
a∗=arga∈AmaxGain(D,a)
例子:下面是海洋生物数据
不浮出水面是否可以生存 | 是否有脚蹼 | 属于鱼类 | |
---|---|---|---|
1 | 是 | 是 | 是 |
2 | 是 | 是 | 是 |
3 | 是 | 否 | 否 |
4 | 否 | 是 | 否 |
5 | 否 | 是 | 否 |
上面包括5个海洋动物,特征包括:不浮出水面是否可以生存,是否有脚蹼。分成两类,鱼类和非鱼类,现在我们想要决定依据第一个特征还是第二个特征划分数据
在决策树开始时,显然只有两种结果,属于鱼类或不属于鱼类,|y| = 2.
正
例
p
1
=
2
5
,
反
例
p
2
=
3
5
正例p_1=\frac{2}{5},反例p_2=\frac{3}{5}
正例p1=52,反例p2=53
E
n
t
(
D
)
=
−
∑
k
=
1
2
p
k
l
o
g
2
p
k
=
−
(
2
5
l
o
g
2
2
5
+
3
5
l
o
g
2
3
5
)
=
0.97095
Ent(D) = -\sum_{k=1}^2p_klog_2p_k = -(\frac{2}{5}log_2\frac{2}{5}+\frac{3}{5}log_2\frac{3}{5})=0.97095
Ent(D)=−k=1∑2pklog2pk=−(52log252+53log253)=0.97095
如
果
我
们
以
不
浮
出
水
面
是
否
可
以
生
存
这
个
属
性
来
划
分
,
那
么
可
知
该
属
性
正
例
占
比
3
5
,
反
例
2
5
,
正
例
中
属
于
鱼
类
为
2
3
,
不
属
于
鱼
类
为
1
3
,
反
例
中
属
于
鱼
类
为
0
,
不
属
于
鱼
类
为
1
,
那
么
有
E
n
t
(
D
1
)
=
−
(
2
3
l
o
g
2
2
3
+
1
3
l
o
g
2
1
3
)
=
0.91829
;
E
n
t
(
D
2
)
=
0
;
G
a
i
n
(
D
,
是
否
浮
出
水
面
)
=
E
n
t
(
D
)
−
∑
v
=
1
2
∣
D
v
∣
∣
D
∣
E
n
t
(
D
v
)
=
0.97095
−
(
3
5
∗
0.91829
+
2
5
∗
0
)
=
0.419976
如果我们以不浮出水面是否可以生存这个属性来划分,那么可知该属性正例占比\frac{3}{5},反例\frac{2}{5},\\正例中属于鱼类为\frac{2}{3},\\不属于鱼类为\frac{1}{3},反例中属于鱼类为0,不属于鱼类为1,那么有\\\ Ent(D_1)=-(\frac{2}{3}log_2\frac{2}{3}+\frac{1}{3}log_2\frac{1}{3}) =0.91829 ; \\\ Ent(D_2) = 0;\\\ Gain(D,是否浮出水面) = Ent(D) - \sum_{v=1}^2\frac{|D^v|}{|D|}Ent(D^v)\\\ =0.97095-(\frac{3}{5}*0.91829+\frac{2}{5}*0)\\\ =0.419976
如果我们以不浮出水面是否可以生存这个属性来划分,那么可知该属性正例占比53,反例52,正例中属于鱼类为32,不属于鱼类为31,反例中属于鱼类为0,不属于鱼类为1,那么有 Ent(D1)=−(32log232+31log231)=0.91829; Ent(D2)=0; Gain(D,是否浮出水面)=Ent(D)−v=1∑2∣D∣∣Dv∣Ent(Dv) =0.97095−(53∗0.91829+52∗0) =0.419976
类似的我们可以计算出第二个特征是否有脚蹼的信息增益:
G
a
i
n
(
D
,
是
否
有
脚
蹼
)
=
0.17095
Gain(D,是否有脚蹼) = 0.17095
Gain(D,是否有脚蹼)=0.17095
很明显,第一个特征的信息增益最大,所以第一个分支以第一个属性作为结点,第二个分支结点以第二个属性为结点。
3. 主要实现代码 - python
1.创建数据集
根据上面的列表创建对应的数据如下:
def createDataSet():
"""
创建数据集
:return:数据集
"""
dataSet = [[1, 1, 'yes'],
[1, 1, 'yes'],
[1, 0, 'no'],
[0, 1, 'no'],
[0, 1, 'no']
]
labels = ['no surfacing', 'flippers']
return dataSet, labels
2. 计算熵
def calShannonEnt(dataSet):
"""
计算给定数据集的香农熵 公式: H = -Σ(n,i=1)p(xi)log2p(xi),其中p(xi)是选择该分类的概率
:param dataSet: 数据集
:return: 返回熵
"""
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
3.计算最大信息增益
def chooseBestFeatureToSplit(dataSet):
"""
选择最好的数据集划分方式
:param dataSet:数据集
:return:返回最佳分割位置
"""
#计算属性长度
numFeatures = len(dataSet[0])-1
#未划分前的熵值
baseEntropy = calShannonEnt(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 = spliteDataSet(dataSet, i, value)
prob = len(subDataSet)/float(len(dataSet))
newEntropy += prob * calShannonEnt(subDataSet)
infoGain = baseEntropy - newEntropy
if (infoGain > bestInfoGain):
bestInfoGain = infoGain
bestFeature = i
return bestFeature
4.创建决策树
def creatTree(dataSet,labels):
"""
创建树的函数代码
:param dataSet:数据集合
:param labels:特征值集合
:return:一棵决策树
"""
#获取属性取值集合
classList = [example[-1] for example in dataSet]
print("classList:")
print(classList)
print("labels:")
print(labels)
#当前列表中都属于同一个值,类别完全相同停止继续划分
if classList.count(classList[0]) == len(classList):
print("all items equals:")
print(classList)
return classList[0]
if len(dataSet[0]) == 1:
print("dataSet:")
print(dataSet)
print("dataSet[0]:")
print(dataSet[0])
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] = creatTree(spliteDataSet(dataSet, bestFeat, value), subLabels)
return myTree
5. 根据数据集合创建树并绘制树形图
#创建数据集
mydat, labels = createDataSet()
#复制一份,防止改变该变量
labelscopy = labels.copy()
#创建决策树 实际上是一个字典集合
myTree = creatTree(mydat, labels)
print(myTree)
#导入绘图类 decision_graphic_tree.py
import decision_graphic_tree
#根据创建的决策树进行绘图
decision_graphic_tree.createPlot(myTree)
其中绘制属性图采用了Matplotlib注解工具annotations来实现,具体实现在decision_graphic_tree.py里面。
详细代码请参考本人github。