决策树

决策树

决策树学习采用的是自顶向下的递归方法,其基本思想是以信息熵为度量构造一颗熵值下降最快的树,到叶子节点处,熵值为0

具有非常好的可解释性、分类速度快的优点,是一种有监督学习

最早提及决策树思想的是Quinlan在1986年提出的ID3算法和1993年提出的C4.5算法,以及Breiman等人在1984年提出的CART算法

工作原理

一般的,一颗决策树包含一个根结点、若干个内部节点和若干个叶节点

构造

构造就是生成一棵完整的决策树。简单来说,构造的过程就是选择什么属性作为节点的过程

叶结点对应于决策结果, 其他每个结点则对应于一个属性测试,每个结点包含的样本集合根据属性测试的结果被划分到子结点中;

根结点包含样本全集,从根结点到每个叶结点的路径对应了一个判定测试序列. 决策树学习的目的是为了产生一棵泛化能力强, 即处理未见示例能力强的决策树,其基本流程遵循简单且直观的分而治之策略

显然, 决策树的生成是一个递归过程. 在决策树基本算法中, 有三种情形会导致递归返回:

  1. 当前结点包含的样本全属于同一类别, 无需划分
  2. 当前属性集为空, 或是所有样本在所有属性上取值相同, 无法划分
  3. 当前结点包含的样本集合为空, 不能划分

划分选择

决策树学习的关键是如何选择最优划分属性

随着划分过程不断进行, 我们希望决策树的分支结点所包含的样本尽可能属于同一类别, 即结点的“纯度”越来越高

信息熵

信息熵在信息论中代表随机变量不确定度的度量

熵越大,数据的不确定性越高,纯度越低

熵越小,数据的不确定性越低,纯度越高

假定当前样本集合 中第 类样本所占的比例为 的信息嫡定义为

信息增益

假定离散属性 个可能的取值 若使用 来对样本集 进行划分, 则会产生 个分支结点, 其中第 个分支结点包含了 中所有在 属性 上取值为 的样本, 记为 我们可根据式 (4.1) 计算出 的信息嫡, 再考虑到不同的分支结点所包含的样本数不同, 给分支结点赋予权重 即样本数越多的分支结点的影响越大, 于是可计算出用属性 对样本集 进行 划分所获得的“信息增益”

一般而言, 信息增益越大, 则意味着使用属性 来进行划分所获得的“纯度提升”越大. 因此, 我们可用信息增益来进行决策树的划分属性选择, 著名的 ID3 决策树学习算法就是以信息增益为准则来选择划分属性

ID3 算法的优点是方法简单,缺点是对噪声敏感。训练数据如果有少量错误,可能会产生决策树分类错误

信息增益率

C4.5 在 IID3 的基础上,用信息增益率代替了信息增益,解决了噪声敏感的问题,并且可以对构造树进行剪枝、处理连续数值以及数值缺失等情况,但是由于 C4.5 需要对数据集进行多次扫描,算法效率相对较低

基尼指数

基尼指数是经典决策树CART用于分类问题时选择最优特征的指标

假设有个类,样本点属于第类的概率为,则概率分布的基尼指数定义为

在信息增益、增益率、基尼指数之外, 人们还设计了许多其他的准则用于决策树划分选择

然而有实验研究表明这些准则虽然对决策树的尺寸有较大影响, 但对泛化性能的影响很有限

剪枝

决策树的缺点包括对未知的测试数据未必有好的分类、泛化能力,即可能发生过拟合现象,此时可采用剪枝或随机森林

剪枝是决策树学习算法对付“过拟合”的主要手段

在决策树学习中, 为了尽可能正确分类训练样本, 结点划分过程将不断重复, 有时会造成决 策树分支过多, 这时就可能因训练样本学得太好了, 以致于把训练集自身 的一些特点当作所有数据都具有的一般性质而导致过拟合

ID3 决策树代码

参考 Machine Learning in Action by Peter Harrington

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# coding = utf-8

from math import log
import numpy as np
from collections import Counter

class DecisionTree:
"""ID3 DecisionTree

"""

def __init__(self):
self.decisionTree = None
self._X = None
self._y = None

# 计算信息熵
def calcShannonEnt(self,y):
lablesCounter = Counter(y)
shannonEnt = 0.0
for num in lablesCounter.values():
p = num / len(y)
shannonEnt += -p * log(p,2)
return shannonEnt

def fit(self, X, y):
self._X = X
self._y = y
self.decisionTree = self.createTree(self._X,self._y)
return self

def splitDataset(self,X,y,d,value):
features = X[X[:,d]==value]
labels = y[X[:,d]==value]
return np.concatenate((features[:,:d],features[:,d+1:]),axis=1), labels

def chooseBestFeatureToSplit(self,X,y):
numFeatures = X.shape[1]
baseEntropy = self.calcShannonEnt(y)
bestInfoGain, bestFeature = 0.0, -1

for i in range(numFeatures):
# 创建唯一的分类标签列表
uniqueVals = np.unique(X[:,i])
newEntropy =0.0
# 计算每种划分方式的信息熵
for value in uniqueVals:
_x, _y = self.splitDataset(X,y,i,value)
prob = len(_x)/len(X)
newEntropy += prob * self.calcShannonEnt(_y)
infoGain = baseEntropy - newEntropy
if infoGain>bestInfoGain:
bestInfoGain = infoGain
bestFeature = i
return bestFeature

def majorityCnt(self,y):
lablesCounter = Counter(y)
return lablesCounter.most_common(1)[0]

def createTree(self,X,y):
# 类别完全相同则停止继续划分
if y[y == y[0]].size == y.size :
return y[0]
# 遍历完所有特征时返回出现次数最多的类别
if X.shape[1] == 0:
return self.majorityCnt(y)
bestFeat = self.chooseBestFeatureToSplit(X,y)
decisionTree = {bestFeat: {}}
for value in np.unique(X[:,bestFeat]):
decisionTree[bestFeat][value] = self.createTree(*self.splitDataset(X,y,bestFeat, value))
return decisionTree

if __name__ == '__main__':

dataSet = np.array([[1, 1, 'yes'],
[1, 1, 'yes'],
[1, 0, 'no'],
[0, 1, 'no'],
[0, 1, 'no']])
labels = ['no surfacing', 'flippers']
dt = DecisionTree()
X = dataSet[:, :2]
X = X.astype(np.int)
y = dataSet[:,-1]
dt.fit(X,y)
print(dt.decisionTree)

参考

机器学习之-常见决策树算法(ID3、C4.5、CART)

决策树

决策树(Decision Tree):通俗易懂之介绍

机器学习-周志华

Machine Learning in Action by Peter Harrington