决策树,是有监督学习的一种算法,并且是一种基本的分类和回归的方法,也就是说决策树有两种:分类树和回归树。这里我们主要讨论分类树。

1.一个例子理解决策树的原理:
  • 你是否玩过二十个问题的小游戏,游戏的规则很简单,参与游戏的一方在脑海中想象某一事物,其他参与者向他提问问题,只允许提问20个问题,问题的答案也只能是对或者错。问问题的人通过推断分解,逐步缩小待猜测事物的范围。
  • 决策树的原理与上述的 二十个问题 的游戏类是,都只通过用户输入的一系列数据,然后找出游戏的答案。
2. 决策树相较于K近邻的优势
  • K近邻可以完成很多的分类任务,但是他最大的缺点就是无法给出数据的内在含义,决策树的主要优势救赎在于数据的形式非常容易理解。
3.特点:
  • 优点:计算复杂度不高:输出结果易于理解,对于中间值的缺失不敏感,可以处理不相关特征数据。
  • 缺点:可能会产生过度匹配的问题。
  • 适用数据类型:数值型和标称型。

决策树的构造

在构造决策树时,我们需要解决的第一个为问题就是,当前数据集上那个特征在划分数据分类时起决定性作用,为找到决定性的特征,划分出最好的效果,我们必须评估每一个特征。完成测试之后原始数据集就被分配为几个数据子集,这些数据会分布在第一个决策节点的所有分支上,如果某个分支下的数据属于同一类型,则当前的数据集已经做好了分类。就无需再对数据集进行分类,如果分支下的数据子集不属于同一分类那么就需要重复划分数据子集的功能。划分方法类似。知道所有所有具有相同类型的数据同一数据子集中。

- 决策树的一般流程

    (1)收集数据:可以使用任何方法。(2)准备数据:书构造算法只适用于标称型数据,因此数值型数据必须离散化。(3)分析数据:可以使用任何方法,构造树完成以后,我们检查该图形是否符合预期。(4)训练算法:构造树的数据结构。(5)测试算法:使用经验树计算错误率。(6)使用算法:此步骤可以适用于任何监督学习的算法,而使用决策树可以更好的理解数据的内在含义。

1. 特征选择

特征选择就是决定用那个特征来划分特征空间,其目的在于选取对数据具有分类能力较好的特征,这样可以提高决策树的学习效率。如果利用一个特征进行分类的结果和随机分类的结果没有很大差别,则称这个特征是没有分类特征能力的经验上丢弃这些特征对决策树学习的精度影响不会很大。
那么如何选择最优的特征来划分呢?一般而言随着划分过程的不断进行,我们希望决策树的分支节点所包含的样本尽量属于同一类别,也就是说节点的纯度越来越高。

  • 在现实生活中,我们衡量的往往不是纯度而是不纯度,衡量不纯度的指标有很多,比如熵、信息增益、基尼指数。我们这里主要讨论熵,也叫香农熵。这名字来源于信息论之父 克劳德·香农。
1.1. 香农熵及其计算公式
  • 熵定义为信息的期望值。
  • 假定当前样本集合D中一共有n类样本,第i类样本为xix_ixi​,那么xix_ixi​的信息定义为:
    l(xi)=−log2P(xi)l(x_i) = - log_2P(x_i)l(xi​)=−log2​P(xi​)
    其中p(xi)p(x_i)p(xi​)是选择该分类的概率。
    为了计算信息熵,我们需要计算所有类别所有可能值包含的信息期望值,通过下面的公式可以得到:
    Ent(D)=−∑i=1np(xi)log2p(xi)Ent(D) = - \sum_{i=1}^{n}p(x_i)log_2p(x_i)Ent(D)=−i=1∑n​p(xi​)log2​p(xi​)
    H的值越小,则D的不纯度越低。(熵越小,不纯度就越低)

香农熵的python代码如下:

'''函数功能:计算香农熵参数说明:datsSet:原始数据集(DataFrame格式最后一列为标签列)返回:ent:香农熵的值
'''
def calEnt(dataSet):n = dataSet.shape[0] #数据集总行数iset = dataSet.iloc[:,-1].value_counts() #标签的所有类别(的数目)p = iset/n #每一类标签所占比ent = (-p*np.log2(p)).sum() #计算信息熵(np模块)return ent

以书上海洋生物数据为例,我们来构建数据集,并计算香农熵。

NO. no surfacing flippers fisl
1 1 1 yes
2 1 1 yes
3 1 0 no
4 0 1 no
5 0 1 no
#创建数据集
import numpy as np
import pandas as pddef createDataSet():row_data = {'no surfacing':[1,1,1,0,0],'flippers':[1,1,0,1,1],'fish':['yes','yes','no','no','no']}dataSet = pd.DataFrame(row_data)  # 将字典的数据集转化为DataFrame格式return dataSet
dataSet = createDataSet()
dataSet
no surfacing flippers fish
0 1 1 yes
1 1 1 yes
2 1 0 no
3 0 1 no
4 0 1 no
# 计算原始数据集的香农熵
calEnt(dataSet)
0.9709505944546686
1.2. 信息增益

信息增益的计算公式其实就是父节点的信息熵与其下所有子节点总信息熵之差。但是这里要注意的是此时计算子节点的总信息熵不能简单求和,而要求在求和汇总之前进行修正。

假设离散属性a有V个可能取值a1,a2,......,aV{a^1,a^2,......,a^V}a1,a2,......,aV,若使用a对样本数据集D进行划分,则会产生V个分支节点,其中第V个分支节点包含了D中所有在属性a熵取值ava^vav的样本,记为DvD^vDv。我们可以根据信息熵的计算公式计算出DvD^vDv的信息熵,再考虑到不同的分支节点所包含的样本数不同,给分支节点赋予权重∣Dv∣/∣D∣|D^v|/|D|∣Dv∣/∣D∣,这就是所谓的修正。
所以信息增益的计算公式为:
Gain(D,a)=Ent(D)−∑v=1V∣DV∣∣D∣Ent(Dv)Gain(D,a) = Ent(D) - \sum_{v=1}^{V}\frac{|D^V|}{|D|}Ent(D^v)Gain(D,a)=Ent(D)−v=1∑V​∣D∣∣DV∣​Ent(Dv)
那我们手动计算一下海洋生物数据中第0列的信息增益:
\begin{aligned}
Gain(‘no surfacing’) &= Ent(D) - [\frac{3}{5}Ent(D_1)+\frac{2}{5}Ent(D_2)] \
&= calEnt(dataSet) - [\frac{3}{5}(-\frac{2}{3}log_2\frac{2}{3}-\frac{1}{3}log_2\frac{1}{3})+\frac{2}{5}(-\frac{2}{2}log_2\frac{2}{2})] \
&= 0.97-0.55 \
&=0.42
\end{aligned}

a = (3/5)*(-(2/3)*np.log2(2/3)-(1/3)*np.log2(1/3))
calEnt(dataSet) - a
0.4199730940219749

同样的我们可以计算出第一列的信息增益,结果为0.17

2.1数据集最佳切分函数

划分数据集的最大准则是选择最大的信息增益也就是,也就是信息下降最快的方向。

"""函数功能:根据信息增益选择出最佳数据集切分的列参数说明:dataSet:原始数据集返回:axis:数据集最佳切分列的索引
"""
#选择最优的列进行切分
def bestSplit(dataSet):baseEnt = calEnt(dataSet) #计算原始熵bestGain = 0 #初始化信息增益axis = -1 #初始化最佳切分列,标签列for i in range(dataSet.shape[1]-1): #对特征的每一列进行循环levels= dataSet.iloc[:,i].value_counts().index #提取出当前列的所有取值ents = 0 #初始化子节点的信息熵for j in levels: #对当前列的每一个取值进行循环childSet = dataSet[dataSet.iloc[:,i]==j] #某一个子节点的dataframeent = calEnt(childSet) #计算某一个子节点的信息熵ents += (childSet.shape[0]/dataSet.shape[0])*ent #计算当前列的信息熵#print(f'第{i}列的信息熵为{ents}')infoGain = baseEnt-ents #计算当前列的信息增益# print(f'第{i}列的信息增益为{infoGain}')if (infoGain > bestGain):bestGain = infoGain #选择最大信息增益axis = i #最大信息增益所在列的索引___________return axis
bestSplit(dataSet)
0

通过上面的计算我们知道,第0列的信息增益为0.42,第1列的信息为0.17,0.42>0.17,所以我们应选择第0列进行切分数据集。

3.1按照给定列划分数据集

通过最佳切分函数返回最佳切分列的索引,我们就可以根据这个做因,构建一个暗战给定列切分数据集的函数

"""函数功能:按照给定列划分数据集参数说明:dataSet:原始数据集axis:指定列的索引values:指定的属性值返回:redataSet:按照指定索引和属性值切分后的数据集
"""
def mySplit(dataSet,axis,value):col = dataSet.columns[axis]   # 提取出特征的属性名redataSet = dataSet.loc[dataSet[col]==value,:].drop(col,axis=1)# 删除第0列的特征  选取第 axis列 属性值value的样本return redataSet

验证函数,以axis=0,value=1为例

mySplit(dataSet,axis = 0, value = 1)
flippers fish
0 1 yes
1 1 yes
2 0 no
3.1. 递归构建决策树

以上都是构造决策树算法所需要的子功能模块,其工作原理如下:得到原始数据集,然后基于最好的属性值划分数据集,由于特征值可能多余两个,因此可能存在大于两个分支的数据集划分。第一次划分之后数据集被向下传递到树的分支的下一个结点,在这个结点上,我们可以再次划分数据。因此我们可以采用递归的原则处理数据集。

决策树生成算法递归的产生决策树,直到不能继续下去为止。这样产生的树往往对训练数据的分类很准确,但对未知的测试数据的分类却没有那么准确。即出现过拟合现象。过拟合现象产生的原因在于在学习时过多的考虑如何提高对训练数据的正确分类,从而构建了过于复杂的决策树。解决这个问题的办法是考虑决策树的复杂度对已生成的决策树进行简化,也就是常常说的剪枝处理。剪枝处理多用于回归树。

3.2. ID3算法

构建决策树的算法有很多,如ID3、C4.5和CART,基于《机器学习实战》这本书,我们选择ID3算法。

ID3算法的核心是在决策树各个节点上对应信息增益准则选择特征,递归的构建决策树。具体方法是:从根节点开始,对节点计算所有可能的特征的信息增益,选则信息增益最大的特征作为节点的特征,由该特征的不同取值建立子节点;再对子节点递归的调用以上方法,构建决策树;直到所有特征的信息增益均很小或没有特征可以选择未知。最后得到一个决策树。

递归结束的条件是:程序遍历完所有的特征列,或则每个分支下的所有实例都具有相同的分类。如果所有实例都具有相同的分类,则得到一个叶节点。任何到达叶节点的数据必然属于叶节点的分类,即叶节点里面不许是标签。

3.3 编写代码构建决策树
"""函数功能:基于最大信息增益切分数据集,递归构建决策树参数说明:dataSet:原始数据集(最后一列是标签)返回:myTree:字典形式的树"""
def createTree(dataSet):featlist = list(dataSet.columns) #提取出数据集所有的列(属性名)classlist = dataSet.iloc[:,-1].value_counts() #获取最后一列类标签#判断最多标签数目是否等于数据集行数,或者数据集是否只有一列if classlist[0]==dataSet.shape[0] or dataSet.shape[1] == 1:return classlist.index[0] #如果是,返回类标签(纯度最高标签列都一样/只有标签列)///递归结束的条件axis = bestSplit(dataSet) #确定出当前最佳切分列的索引bestfeat = featlist[axis] #获取该索引对应的特征   根节点的名字myTree = {bestfeat:{}} #采用字典嵌套的方式存储树信息del featlist[axis] #删除当前特征valuelist = set(dataSet.iloc[:,axis]) #提取最佳切分列所有属性值(zet去重复值)for value in valuelist: #对每一个属性值递归建树myTree[bestfeat][value] = createTree(mySplit(dataSet,axis,value))#dataSet是切分后的结果return myTree
myTree = createTree(dataSet)
myTree
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}

树的存储

构造决策树是很耗费时间的任务,即使处理很小的数据集,也要花费几秒的时间,如果数据集很大就会耗费很多的计算时间。因此为了节省时间,建好树后立马将其保存,后续使用直接调用即可。

我们这里使用的是numpy里的savea()函数,他乐意将字典形式的数据保存为.npy文件,调用的时候直接向hi用load()函数即可。

# 树的存储
np.save('mytree.npy',myTree)# 树的读取
read_myTree = np.load('myTree.npy').item()
read_myTree
{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}
使用决策树执行分类
"""函数说明:对一个测试实例进行分类参数说明:inputTree:已经生成的决策树lables:存储选择的最有特征标签testVec:测试集数据列表,特征顺序对应原始数据集返回:classLable:分类结果
"""def classify(inputTree,labels, testVec):firstStr = next(iter(inputTree)) #获取决策树第一个节点   // iter生成迭代器对象;next返回迭代器下一个项目secondDict = inputTree[firstStr] #下一个字典    根节点下的字典featIndex = labels.index(firstStr) #第一个节点所在列的索引(测试集与训练集索引一致)for key in secondDict.keys():if testVec[featIndex] == key:if type(secondDict[key]) == dict :    # 判断是否为字典类型classLabel = classify(secondDict[key], labels, testVec)else:classLabel = secondDict[key]return classLabel

对测试集所有数据进行分类,并计算准确率

"""函数功能:对测试集进行预测,并返回预测后的结果参数说明:train:训练集teat:测试机返回:test:预测好分类的测试机
"""
def acc_classify(train,test):inputTree = createTree(train) #根据测试集生成一棵树   //前面的生成树的算法 //loadlabels = list(train.columns) #数据集所有的列名称      //result = []for i in range(test.shape[0]): #对测试集中每一条数据进行循环testVec = test.iloc[i,:-1] #测试集中的一个实例classLabel = classify(inputTree,labels,testVec) #预测该实例的分类result.append(classLabel) #将分类结果追加到result列表中test['predict']=result #将预测结果追加到测试集最后一列acc = (test.iloc[:,-1]==test.iloc[:,-2]).mean() #计算准确率print(f'模型预测准确率为{acc}')return test

测试函数

train = dataSet     # 训练集
test = dataSet.iloc[:3,:]    # 测试集
acc_classify(train,test)
模型预测准确率为1.0f:\python3\lib\site-packages\ipykernel_launcher.py:17: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value insteadSee the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
no surfacing flippers fish predict
0 1 1 yes yes
1 1 1 yes yes
2 1 0 no no

#导入相应的包
from sklearn import tree
from sklearn.tree import DecisionTreeClassifier
import graphviz
#特征
Xtrain = dataSet.iloc[:,:-1]
#标签
Ytrain = dataSet.iloc[:,-1]
labels = Ytrain.unique().tolist()
Ytrain = Ytrain.apply(lambda x: labels.index(x)) #将本文转换为数字
#绘制树模型
clf = DecisionTreeClassifier()
clf = clf.fit(Xtrain, Ytrain)
tree.export_graphviz(clf)
dot_data = tree.export_graphviz(clf, out_file=None)
graphviz.Source(dot_data)#给图形增加标签和颜色
dot_data = tree.export_graphviz(clf, out_file=None,feature_names=['no surfacing', 'flippers'],class_names=['fish', 'not fish'],filled=True, rounded=True,special_characters=True)
graphviz.Source(dot_data)

#利用render方法生成图形
graph = graphviz.Source(dot_data)
graph.render("fish")
'fish.pdf'

决策树(Decision Tree)相关推荐

  1. Machine Learning | (7) Scikit-learn的分类器算法-决策树(Decision Tree)

    Machine Learning | 机器学习简介 Machine Learning | (1) Scikit-learn与特征工程 Machine Learning | (2) sklearn数据集 ...

  2. 算法杂货铺——分类算法之决策树(Decision tree)

    算法杂货铺--分类算法之决策树(Decision tree) 2010-09-19 16:30 by T2噬菌体, 88978 阅读, 29 评论, 收藏, 编辑 3.1.摘要 在前面两篇文章中,分别 ...

  3. 数据分类:决策树Decision Tree

    背景 决策树(decision tree)是一种基本的分类和回归(后面补充一个回归的例子?)方法,它呈现的是一种树形结构,可以认为是if-then规则的集合.其其主要优点是模型具有很好的可读性,且分类 ...

  4. 决策树分类python代码_分类算法-决策树 Decision Tree

    决策树(Decision Tree)是一个非参数的监督式学习方法,决策树又称为判定树,是运用于分类的一种树结构,其中的每个内部节点代表对某一属性的一次测试,每条边代表一个测试结果,叶节点代表某个类或类 ...

  5. 决策树Decision Tree+ID3+C4.5算法实战

    决策树Decision Tree 决策树的三种算法: 举个栗子: 熵entropy的概念: 信息熵越大,不确定性越大.信息熵越小,不确定性越小. 其实就是排列组合之中的概率,概率相乘得到其中一个组合, ...

  6. 机器学习算法实践:决策树 (Decision Tree)(转载)

    前言 最近打算系统学习下机器学习的基础算法,避免眼高手低,决定把常用的机器学习基础算法都实现一遍以便加深印象.本文为这系列博客的第一篇,关于决策树(Decision Tree)的算法实现,文中我将对决 ...

  7. 第六章.决策树(Decision Tree)—CART算法

    第六章.决策树(Decision Tree) 6.2 CART算法 CART决策树的生成就是递归地构建二叉决策树的过程.CART用基尼(Gini)系数最小化准则来进行特征选择,生成二叉树. 1.Gin ...

  8. 分类Classification:决策树Decision Tree

    目录 分类的定义 决策树Decision Tree 混乱衡量指标Gini index 决策树的特点 分类的定义 分类:建立一个学习函数(分类模型)将每个属性集合(x1,x2,...xn)对应到一组已定 ...

  9. Python数据挖掘入门与实践 第三章 用决策树预测获胜球队(一)pandas的数据预处理与决策树(Decision tree)

    作为一个NBA球迷,看到这一章还是挺激动的. 不过内容有点难,研究了半天... 要是赌球的,用这章的预测+凯利公式,是不是就能提升赢钱概率了? 数据预处理 回归书本内容,既然要分析,首先需要有数据: ...

  10. 决策树Decision Tree 和随机森林RandomForest基本概念(一)

    文章目录 一.决策树介绍 1.1 什么是决策树 1.2 决策树种类 1.3 决策树学习过程 1.4 Entropy(熵) 1.5 information gain(信息增益) 1.6 信息论 1.8 ...

最新文章

  1. Xamarin环境搭建
  2. 用云函数快速实现图片爬虫
  3. 输入法之核心词典构建
  4. 蓝图设计对SAP项目实施的重要性
  5. [源码]解析 SynchronousQueue 上界,下界.. 数据保存和数据传递. 堵塞队列. 有无频繁await?...
  6. hive sql插入一行数据_Hive查询某一重复字段记录第一条数据
  7. ITK:从Seed开始迭代图像
  8. 面试题 17.13. 恢复空格
  9. php 安装测试程序,PHPUnit安装及使用示例
  10. 高效 Java Web 开发框架 JessMA v3.2.1 正式发布
  11. CentOS 6.2编译安装Nginx1.2.0+MySQL5.5.25+PHP5.3.13
  12. Javaworkers团队第五周项目总结
  13. weblogic下载及安装
  14. tp5 验证码 验证不正确 (跨域问题)
  15. Mac 制作U盘操作系统并清空Mac全部数据后重装系统
  16. python迷宫地图代码_[内附完整源码和文档] 基于python实现的迷宫游戏
  17. matlab哪些教材好,新手入门,恳请推荐一本matlab好教材
  18. IDEA:Push rejected 解决方式
  19. 前世回眸,今生结缘,滚滚红尘,谁人可依
  20. 项目(百万并发网络通信架构)10.2---recv()函数的极限测试

热门文章

  1. 飞翔的红蜻蜓(浙理体育)——生成跑步数据并上传
  2. 计算机excel必备知识,2017职称计算机考试EXCEL知识点:创建图表
  3. php微信红包雨效果,微信红包雨特效口令大全 微信红包雨特效口令有哪些
  4. 2015年,我的创业记忆片段
  5. Rails 用 RJS 简单有效的实现页面局部刷新
  6. 基于MindSpore复现Deeplabv3—语义分割
  7. win7防火墙端口开放
  8. 2021-2027全球与中国β-雌二醇 (CAS 50-28-2)市场现状及未来发展趋势
  9. 【论文笔记】AVA: A Video Dataset of Spatio-temporally Localized Atomic Visual Actions
  10. Remix-IDE安装开发环境与使用文档(Windows环境)