机器学习算法二——决策树(1)(决策树的构造)

决策树的构造

下图构造了一个假想的邮件分类系统:
在这里插入图片描述
前面介绍的k-近邻算法可以完成很多分类任务,但它最大的缺点是无法给出数据的内在含义,决策树的主要优势就在于数据形式非常容易理解
决策树很多任务都是为了数据中所蕴含的知识信息,因此决策树可以使用不熟悉的数据集合,并从中提取出一系列规则,机器学习算法最终将使用这些机器从数据集中创造的规则。
专家系统中经常使用决策树,而且决策树给出结果往往可以匹敌在当前领域具有几十年工作经验的人类专家。

1、决策树的构造
优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据。
缺点:可能会产生过度匹配问题。
适用数据类型:数值型和标称型。
首先我们讨论数学上如何使用信息论划分数据集,然后编写代码将理论应用到具体的数据集上,最后编写代码构建决策树。
第一个问题:当前数据集上哪个特征在划分数据分类时起决定性作用。
因此,必须评估每个特征。
完成测试后,原始数据集就被划分为几个数据子集。划分数据子集和划分原始数据集的方法相同,直到所有具有相同类型的数据均在一个数据子集内。
创建分支的伪代码函数createBranch()如下:

#检测数据集中的每个子项是否属于同一分类
If so return 类标签;
Else
		寻找划分数据集的最好特征
		划分数据集
		创建分支节点
			for 每个划分的子集
				调用函数createBranch并增加返回结果到分支节点中
		return 分支节点

上面的伪代码createBranch是一个递归函数,在倒数第二行直接调用了它自己。

在这里插入图片描述表3-1的数据包含5个海洋动物。现在我们要决定依据第一个特征还是第二个特征划分数据。
我们可以先分析一下表中的数据。如果我们按照第一个特征,可分为两组,其中一组将有2个属于鱼类,一个属于非鱼类;另一组则全部属于非鱼类。如果按照第二个特征分组,则其中一组将有两个属于鱼类,两个属于非鱼类;另一组只有一个非鱼类。

(1)信息增益
划分数据集的大原则:将无序的数据变得有序
方法:使用信息论度量信息。划分数据集前后信息发生的变化称为信息增益,获得信息增益最高的特征就是最好的选择。
在这里插入图片描述
计算给定数据集的香农熵

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

可以创建自己的数据集函数,并执行计算熵:

def createDataSet():
    dataSet = [[1,1,'yes'], [1,1,'yes'], [1,0,'no'], [0,1,'no'], [0,1,'no']]
  #  dataSet[0][-1] = 'maybe'
    labels = ['no surfacing','flippers']
    return dataSet, labels

def main():
    data,labels = createDataSet()
    print(data)
    print(calcShannonEnt(data))

if __name__ == '__main__':
    main()

则可见数据集和熵值如下:

>>>[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
0.9709505944546686

将 dataSet[0][-1] = 'maybe’去掉注释,即在数据集中添加更多的分类,则混合的数据越多,熵越高:

>>>[[1, 1, 'maybe'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
1.3709505944546687

(2)划分数据集
以下代码的功能是:按照给定特征划分数据集。
使用了三个输入参数:待划分的数据集、划分数据集的特征、特征的返回值。
可以这样理解这段代码:当我们按照某个特征划分数据集时,就需要将所有符合要求的元素抽取出来。

#按照给定特征划分数据集
def splitDataSet(dataSet, axis, value):#待划分的数据集、划分数据集的特征、特征的返回值
    retDataSet = [] #声明一个新列表对象
    for featVec in dataSet:
        if featVec[axis] == value: #将符合特征的数据抽取出来
            reducedFeatVec = featVec[:axis] #从头开始取,到axis-1元素
            reducedFeatVec.extend(featVec[axis+1:])#axis+1开始取直到结束
            retDataSet.append(reducedFeatVec) 
    return retDataSet #符合特征的、去掉了axis的数据

执行结果:

print(data)
print(splitDataSet(data,1,1)) #第一个值为1的数据
>>>[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
[[1, 'yes'], [1, 'yes'], [0, 'no'], [0, 'no']]

(a) list.append(object)方法
是指在列表末尾增加一个数据项

students = ['Cleese','Palin','Jones']
    students_new = ['Idle','Gavin']
    students.append(students_new)
    print(students)
>>>['Cleese', 'Palin', 'Jones', ['Idle', 'Gavin']]

使用append时,将students_new看作一个对象,作为一个整体添加到students中。

(b) list.extend(sequence)方法
是指在列表末尾增加一个数据集合

 students = ['Cleese','Palin','Jones']
    students_new = ['Idle','Gavin']
    students.extend(students_new)
    print(students)
>>>['Cleese', 'Palin', 'Jones', 'Idle', 'Gavin']

使用extend时,是把一个序列seq的内容添加到列表,将students_new看作一个序列,将此序列放在students序列之后。

© list.insert()方法
是指在某个特定位置前面增加一个数据项

students = ['Cleese','Palin','Jones']
    students_new = ['Idle','Gavin']
    students.insert(2,students_new)
    print(students)
>>>['Cleese', 'Palin', ['Idle', 'Gavin'], 'Jones']

使用insert时,也是将对象作为整体插入的,插入在列表下标为2的对象之前。

以下这段代码遍历整个数据集,循环计算香农熵和splitDataSet()函数,找到最好的特征划分方式。

#选择最好的数据集划分方式
def chooseBestFeatureToSplit(dataSet):
    numFeatures = len(dataSet[0]) - 1 #数据特征的个数,最后一列是类别标签
    baseEntropy = calcShannonEnt(dataSet)#计算整个数据集的原始香农熵
    bestInfoGain = 0.0
    bestFeature = -1
    for i in range(numFeatures): #遍历数据集中的所有特征
        featList = [example[i] for example in dataSet] #获取dataSet的第i个所有特征
        uniqueVals = set(featList)#创建set集合,元素不可重复
        newEntropy = 0.0
        for value in uniqueVals:
            subDataSet = splitDataSet(dataSet, i, value) #按给定特征划分数据集
            prob = len(subDataSet)/float(len(dataSet)) #计算子集的概率
            newEntropy += prob * calcShannonEnt(subDataSet) #计算信息熵
        infoGain = baseEntropy - newEntropy #信息增益
        if (infoGain > bestInfoGain): #寻找最大增益
            bestInfoGain = infoGain
            bestFeature = i
    return bestFeature  #返回信息增益最大时的特征索引值

运行结果:

>>>0

说明第1个特征是最好的用于划分数据集的特征。

(d)set()函数
描述:set()函数创建一个无序不重复元素集,可进行关系测试,删除重复数据,还可计算交集、差集、并集等。
语法:class set([iterable])
参数说明:iterable-------可迭代对象
返回值:返回新的集合对象
实例:
在这里插入图片描述
以上是从数据集构造决策树算法所需要的子功能模块,其工作原理如下:
得到原始数据集,然后基于最好的属性值划分数据集,由于特征值可能多于两个,因此可能存在大于两个分支的数据集划分。第一次划分之后,数据将被向下传递到树分支的下一个节点,在这个节点上,我们可以再次划分数据。
因此,我们可以采用递归的原则处理数据集。

(3)递归构建决策树
递归结束的条件是:程序遍历完所有划分数据集的属性,或者每个分支下的所有实例都具有相同的分类。
如果所有实例具有相同的分类,则得到一个叶子节点或者终止块。
在这里插入图片描述
如果数据集已经处理了所有属性,但是类标签依然不是唯一的,此时我们需要决定如何定义该叶子节点,在这种情况下,我们通常会采用多数表决的方法决定该叶子节点的分类。代码如下:

#采用多数表决的方法决定该叶子节点的分类
def majorityCnt(classList):
    classCount={}
    for vote in classList:
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True)
    return sortedClassCount[0][0] #返回出现次数最多的分类名称

创建树的函数代码如下:

#创建树
def createTree(dataSet,labels):
    classList = [example[-1] for example in dataSet] #数据集最后一列的变量列表,即类别
    if classList.count(classList[0]) == len(classList):#classList中第一个元素的个数与classList长度相同,表示类别相同,则停止划分
        return classList[0]
    if len(dataSet[0]) == 1:#一个数据集中只有一个类别时,停止划分
        return majorityCnt(classList) #返回出现次数最多的分类名称
    bestFeat = chooseBestFeatureToSplit(dataSet) #选择最好的数据集划分方式,返回特征索引值
    bestFeatLabel = labels[bestFeat] #信息增益最大时的划分特征
    myTree = {bestFeatLabel:{}} #字典变量myTree存储了树的划分特征
    del(labels[bestFeat]) #从标签中删除已经划分好的特征
    featValues = [example[bestFeat] for example in dataSet] #得到该属性的样例的所有数据值
    uniqueVals = set(featValues)
    for value in uniqueVals: #在每个数据集划分上递归调用函数createTree()
        subLabels = labels[:] #复制了类标签存储到新变量列表subLabels中
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)#按照最佳分割点位置上的值进行分类,有几个值就又建几棵树。递归
    return myTree
data,labels = createDataSet() print(createTree(data,labels))
>>>{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

变量myTree包含了很多代表树结构信息的嵌套字典。
从左边开始,第一个关键字no surfacing是第一个划分数据集的特征名称,该关键字的值是no surfacing节点的子节点。这些值可能是类标签,也可能是另一个数据字典。
如果值是类标签,则该子节点是叶子节点;如果值是另一个数据字典,则子节点是一个判断节点,这种格式结构不断重复就构成了整棵树。

可以看到,虽然我们已经从数据集中创建了树,但是字典的表示形式非常不易于理解,而且直接绘制图形也比较困难。下节学习使用Matplotlib库创建树形图!呼~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
决策树算法是一种广泛应用于分类和回归的机器学习算法,它基于树形结构对样本进行分类或预测。决策树算法的主要思想是通过一系列的判断来对样本进行分类或预测。在决策树中,每个节点表示一个属性或特征,每个分支代表该属性或特征的一个取值,而每个叶子节点代表一个分类或预测结果。 决策树算法的训练过程主要包括以下步骤: 1. 特征选择:根据某种指标(如信息增益或基尼系数)选择最优的特征作为当前节点的分裂属性。 2. 决策树生成:根据选择的特征将数据集分成若干个子集,并递归地生成决策树。 3. 剪枝:通过剪枝操作来提高决策树的泛化性能。 决策树算法的优点包括易于理解和解释、计算复杂度较低、对缺失值不敏感等。但是,决策树算法也存在一些缺点,如容易出现过拟合、对离散数据敏感等。 下面是一个决策树算法的案例:假设我们要根据一个人的年龄、性别、教育程度和职业预测其收入水平(高于或低于50K)。首先,我们需要将这些特征进行编码,将其转换为数值型数据。然后,我们可以使用决策树算法对这些数据进行训练,并生成一个决策树模型。最后,我们可以使用该模型对新的数据进行分类或预测。例如,根据一个人的年龄、性别、教育程度和职业,我们可以使用决策树模型预测该人的收入水平。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值