决策树是最经常使用的数据挖掘算法,他之所以如此流行,一个很重要的原因是使用者基本不用了解机器学习算法,也不用深究他是如何工作的。
3-1就是一个决策树,正方形表示判断模块,椭圆形表示终止模块,从模块引出的箭头称作分支,他可以到达另一个判断模块或则终止模块。我们之前介绍的KNN最大的缺点就是无法得出数据的内在意义,而决策树的主要优势就是在于数据形式非常容易理解。
下面我们将构造决策树算法,能够读取数据集合,构造类似于上图的决策树,决策树很多任务都是为了数据中所蕴含的知识信息,因此决策树可以使用不熟悉的数据集合,并从中提取一系列规则,机器学习算法最终将使用这些机器从数据集中创造的规则。专家系统常用决策树,而且决策树给出的结果往往很不错。
接下来我们将学习从一堆原始数据中构造决策树。首先我们将讨论构造决策树的方法,以及如何编写构造树的python代码,然后学习一些度量算法成功率的方法,最后使用递归分类器,并使用matplotlib绘制决策树图。构造完成决策树之后,我们将输入一些数据,并由决策树来决出结果。
1、决策树的构造
首先我们将在数学上讨论使用信息论来划分数据集,然后编写代码将理论应用到具体的数据集上,最后编写代码构建决策树。
第一个问题,我们要知道当前数据集上哪个特征在分类数据时起到决定性作用。为了找到决定性的特征,划分出最好的结果,我们必须评估每个特征。完成测试之后,原始数据就被划分为几个数据子集。这些数据子集会分布在第一个决策点的所有分支上。如果某个分支下面的数据都属于同一种类型,那么就不需要再对数据进行划分了。如果数据子集内的数据不是同一类型的,就需要重复划分子集。划分子集的方法和划分原始书记的方法相同,直到所有具有相同类型的数据全在一个子集内。
伪代码如下:
If so return 类标签;
Else
寻找划分数据集的最好特征
划分数据集
创建分支点
for 每个划分的子集
调用函数,并增加返回结果到分支节点中去
return 分支节点
我们使用ID3算法划分数据集,该算法用来处理如何划分数据集,何时停止数据集,每次划分我们只选用一个特征属性。
我们必须采用量化的方法来划分这些数据:
信息增益
划分数据集的最大原则是:将无序的数据变得更加有序。我们可以使用多种方法划分数据集,但是每种方法都有各自的优缺点。
在划分数据集之前之后发生的变化成为信息增益,知道如何计算信息增益,我们就可以计算每个特征划分数据集获得的增益,所得信息增益最高的特征就是最好的选择。
集合信息的度量方式称为香浓熵或则熵。
下面是计算信息熵的python程序:计算给定数据集的熵
from math import log def calcShannonEnt(dataSet): #数据的实例总数 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
再利用createDataSet()函数得到鉴定数据集:
def createDataSet():
dataSet = [[1,1,'yes'],
[1,1,'yes'],
[1,0,'no'],
[0,1,'no'],
[0,1,'no']]
labels = ['no surfacing', 'flippers']
return dataSet, labels
我们可以得到运算的结果:
熵越高则混合的数据也就越多,我们可以在数据集中添加更多的分类,观察熵是如何变化的。
这里我们增加第三个名为maybe的分类,测试熵的变化:
计算熵之后,我们就可以按照最大信息增益的方法来划分数据集。
划分数据集:
除了计算信息熵,还要进行划分数据集,度量划分数据集的熵,以便判断当前是否正确的划分了数据集。我们将对每个特征划分数据集的结果计算一次信息熵,以便判断哪个特征是划分数据最好的。
按照给定特征划分数据集的程序:
我们可以得到这样的划分结果。
接下来我们会遍历整个数据集,循环计算香浓熵和splitDataSet()函数,找到最好的特征划分方式。
程序:选择最好的数据集划分方式:
def chooseBestFeatureToSplit(dataSet): ''' 数据满足的要求:1、数据必须是一种由列表元素组成的列表,且所有列表元素相同 2、数据的最后一列必须是当前实例的类别标签 ''' numFeatures = len(dataSet[0]) - 1 #计算数据集的香浓熵,用于与划分后的香浓熵进行比较 baseEntropy = calcShannonEnt(dataSet) bestInfoGain = 0.0 bestFeature = -1 ''' 取出每一个特征的所有特征值,所有的特征值都被取出 然后按照每个特征值进行分类 计算分类后的信息熵 ''' for i in range(numFeatures): ''' 基本格式 列表推导式: variable = [out_exp_res for out_exp in input_list if out_exp == 2] out_exp_res: 列表生成元素表达式,可以是有返回值的函数。 for out_exp in input_list: 迭代input_list将out_exp传入out_exp_res表达式中。 if out_exp == 2: 根据条件过滤哪些值可以。 ''' #遍历数据集中的所有特征 featList = [example[i] for example in dataSet] uniqueVals = set(featList) newEntropy = 0.0 for value in uniqueVals: #取出第i个特征为value的所有数据 subDataSet = splitDataSet(dataSet, i, value) prob = len(subDataSet)/float(len(dataSet)) #周志华《机器学习》P75 4.2公式 计算信息增益 newEntropy += prob*calcShannonEnt(subDataSet) infoGain = baseEntropy - newEntropy if(infoGain > bestInfoGain): bestInfoGain = infoGain bestFeature = i return bestFeature将我们之前的数据集放入其中,得到结果:
也就是说我们按照第一类特征对数据集进行分类最好。我们可以看到,如果按照第一类特征进行分类,那么第一类为1的有两个‘yes’,一个‘no’,第二类为0的有两个‘no’。相比较另外两类特征的分类方法是比较好的一种分类。