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

  前言

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

  算法进行总结并附上自己相关的实现代码。所有算法代码以及用于相应模型的训练的数据都会放到GitHub上(https://github.com/PytLab/MLBox).

    本文中我将一步步通过MLiA的隐形眼镜处方数集构建决策树并使用Graphviz将决策树可视化。

  决策树学习

    决策树学习是根据数据的属性采用树状结构建立的一种决策模型,可以用此模型解决分类和回归问题。常见的算法包括 CART(Classification And Regression Tree), ID3, C4.5等。我们往往根据数据集来构建一棵决策树,

  他的一个重要任务就是为了数据中所蕴含的知识信息,并提取出一系列的规则,这些规则也就是树结构的创建过程就是机器学习的过程。

  决策树的结构

    以下面一个简单的用于是否买电脑预测的决策树为例子,树中的内部节点表示某个属性,节点引出的分支表示此属性的所有可能的值,叶子节点表示最终的判断结果也就是类型。

    

    借助可视化工具例如Graphviz,matplotlib的注解等等都可以讲我们创建的决策树模型可视化并直接被人理解,这是贝叶斯神经网络等算法没有的特性。

    决策树算法

    决策树算法主要是指决策树进行创建中进行树分裂(划分数据集)的时候选取最优特征的算法,他的主要目的就是要选取一个特征能够将分开的数据集尽量的规整,也就是尽可能的纯. 最大的原则就是: 将无序的数据变得更加有序

    这里总结下三个常用的方法:

    1、信息增益(information gain)

    2、增益比率(gain ratio)

    3、基尼不纯度(Gini impurity)

  信息增益 (Information gain)

    这里涉及到了信息论中的一些概念:某个事件的信息量,信息熵,信息增益等, 关于事件信息的通俗解释可以看知乎上的一个回答

    1、某个事件 i 的信息量: 这个事件发生的概率的负对数

    2、信息熵就是平均而言一个事件发生得到的信息量大小,也就是信息量的期望值

    任何一个序列都可以获取这个序列的信息熵,也就是将此序列分类后统计每个类型的概率,再用上述公式计算,使用Python实现如下:

def get_shanno_entropy(self, values):

    ‘‘‘ 根据给定列表中的值计算其Shanno Entropy

    ‘‘‘

    uniq_vals = set(values)

    val_nums = {key: values.count(key) for key in uniq_vals}

    probs = [v/len(values) for k, v in val_nums.items()]

    entropy = sum([-prob*log2(prob) for prob in probs])

    return entropy

  信息增益

    我们将一组数据集进行划分后,数据的信息熵会发生改变,我们可以通过使用信息熵的计算公式分别计算被划分的子数据集的信息熵并计算他们的平均值(期望值)来作为分割后的数据集的信息熵。新的信息熵的相比未划

  分数据的信息熵的减小值便是信息增益了. 这里我在最初就理解错了,于是写出的代码并不能创建正确的决策树。

    假设我们将数据集D划分成kk 份D1,D2,…,Dk,则划分后的信息熵为:

    信息增益便是两个信息熵的差值

    在这里我主要使用信息增益来进行属性选择,具体的实现代码如下:

def choose_best_split_feature(self, dataset, classes):

    ‘‘‘ 根据信息增益确定最好的划分数据的特征

    :param dataset: 待划分的数据集

    :param classes: 数据集对应的类型

    :return: 划分数据的增益最大的属性索引

    ‘‘‘

    base_entropy = self.get_shanno_entropy(classes)

    feat_num = len(dataset[0])

    entropy_gains = []

    for i in range(feat_num):

        splited_dict = self.split_dataset(dataset, classes, i)

        new_entropy = sum([

            len(sub_classes)/len(classes)*self.get_shanno_entropy(sub_classes)

            for _, (_, sub_classes) in splited_dict.items()

        ])

        entropy_gains.append(base_entropy - new_entropy)

    return entropy_gains.index(max(entropy_gains))

  增益比率

    增益比率是信息增益方法的一种扩展,是为了克服信息增益带来的弱泛化的缺陷。因为按照信息增益选择,总是会倾向于选择分支多的属性,这样会是的每个子集的信息熵最小。例如给每个数据添加一个

  第一无二的id值特征,则按照这个id值进行分类是获得信息增益最大的,这样每个子集中的信息熵都为0,但是这样的分类便没有任何意义,没有任何泛化能力,类似过拟合。

    因此我们可以通过引入一个分裂信息来找到一个更合适的衡量数据划分的标准,即增益比率。

    分裂信息的公式表示为:

    

    当然SplitInfo有可能趋近于0,这个时候增益比率就会变得非常大而不可信,因此有时还需在分母上添加一个平滑函数,具体的可以参考参考部分列出的文章

  基尼不纯度(Gini impurity)

    基尼不纯度的定义:

    

    其中m 表示数据集D 中类别的个数, pi 表示某种类型出现的概率。可见当只有一种类型的时候基尼不纯度的值为0,此时不纯度最低。

    针对划分成k个子数据集的数据集的基尼不纯度可以通过如下式子计算:

    

     由此我们可以根据不纯度的变化来选取最有的树分裂属性

    

  树分裂

    有了选取最佳分裂属性的算法,下面我们就需要根据选择的属性来将树进一步的分裂。所谓树分裂只不过是根据选择的属性将数据集划分,然后在总划分出来的数据集中再次调用选取属性的

  方法选取子数据集的中属性。实现的最好方式就是递归了.

    关于用什么数据结构来表示决策树,在Python中可以使用字典很方便的表示决策树的嵌套,一个树的根节点便是属性,属性对应的值又是一个新的字典,其中key为属性的可能值,value为新的子树。

    下面是我使用Python实现的根据数据集创建决策树:

    

def create_tree(self, dataset, classes, feat_names):

    ‘‘‘ 根据当前数据集递归创建决策树

    :param dataset: 数据集

    :param feat_names: 数据集中数据相应的特征名称

    :param classes: 数据集中数据相应的类型

    :param tree: 以字典形式返回决策树

    ‘‘‘

    # 如果数据集中只有一种类型停止树分裂

    if len(set(classes)) == 1:

        return classes[0]

    # 如果遍历完所有特征,返回比例最多的类型

    if len(feat_names) == 0:

        return get_majority(classes)

    # 分裂创建新的子树

    tree = {}

    best_feat_idx = self.choose_best_split_feature(dataset, classes)

    feature = feat_names[best_feat_idx]

    tree[feature] = {}

    # 创建用于递归创建子树的子数据集

    sub_feat_names = feat_names[:]

    sub_feat_names.pop(best_feat_idx)

    splited_dict = self.split_dataset(dataset, classes, best_feat_idx)

    for feat_val, (sub_dataset, sub_classes) in splited_dict.items():

        tree[feature][feat_val] = self.create_tree(sub_dataset,

                                                   sub_classes,

                                                   sub_feat_names)

    self.tree = tree

    self.feat_names = feat_names

    return tree

    树分裂的终止条件有两个

    一个是遍历完所有的属性

  • 可以看到,在进行树分裂的时候,我们的数据集中的数据向量的长度是不断缩短的,当缩短到0时,说明数据集已经将所有的属性用尽,便也分裂不下去了, 这时我们选取最终子数据集中的众数作为最终的分类结果放到叶子节点上.
  • 另一个是新划分的数据集中只有一个类型。

    若某个节点所指向的数据集都是同一种类型,那自然没有必要在分裂下去了即使属性还没有遍历完.

构建一棵决策树

这我用了一下MLiA书上附带的隐形眼镜的数据来生成一棵决策树,数据中包含了患者眼部状况以及医生推荐的隐形眼镜类型.

首先先导入数据并将数据特征同类型分开作为训练数据用于生成决策树

from trees import DecisionTreeClassifier

lense_labels = [‘age‘, ‘prescript‘, ‘astigmatic‘, ‘tearRate‘]

X = []

Y = []

with open(‘lenses.txt‘, ‘r‘) as f:

for line in f:

comps = line.strip().split(‘\t‘)

X.append(comps[: -1])

Y.append(comps[-1])

生成决策树:

clf = DecisionTreeClassifier()

clf.create_tree(X, Y, lense_labels)

查看生成的决策树:

In [2]: clf.tree

Out[2]:

{‘tearRate‘: {‘normal‘: {‘astigmatic‘: {‘no‘: {‘age‘: {‘pre‘: ‘soft‘,

‘presbyopic‘: {‘prescript‘: {‘hyper‘: ‘soft‘, ‘myope‘: ‘no lenses‘}},

‘young‘: ‘soft‘}},

‘yes‘: {‘prescript‘: {‘hyper‘: {‘age‘: {‘pre‘: ‘no lenses‘,

‘presbyopic‘: ‘no lenses‘,

‘young‘: ‘hard‘}},

‘myope‘: ‘hard‘}}}},

‘reduced‘: ‘no lenses‘}}

可视化决策树

直接通过嵌套字典表示决策树对人来说不好理解,我们需要借助可视化工具可视化树结构,这里我将使用Graphviz来可视化树结构。为此实现了讲字典表示的树生成Graphviz Dot文件内容的函数,大致思想就是递归获取整棵树的所有节点和连接节点的边然后将这些节点和边生成Dot格式的字符串写入文件中并绘图。

递归获取树的节点和边,其中使用了uuid给每个节点添加了id属性以便将相同属性的节点区分开.

def get_nodes_edges(self, tree=None, root_node=None):

‘‘‘ 返回树中所有节点和边

‘‘‘

Node = namedtuple(‘Node‘, [‘id‘, ‘label‘])

Edge = namedtuple(‘Edge‘, [‘start‘, ‘end‘, ‘label‘])

if tree is None:

tree = self.tree

if type(tree) is not dict:

return [], []

nodes, edges = [], []

if root_node is None:

label = list(tree.keys())[0]

root_node = Node._make([uuid.uuid4(), label])

nodes.append(root_node)

for edge_label, sub_tree in tree[root_node.label].items():

node_label = list(sub_tree.keys())[0] if type(sub_tree) is dict else sub_tree

sub_node = Node._make([uuid.uuid4(), node_label])

nodes.append(sub_node)

edge = Edge._make([root_node, sub_node, edge_label])

edges.append(edge)

sub_nodes, sub_edges = self.get_nodes_edges(sub_tree, root_node=sub_node)

nodes.extend(sub_nodes)

edges.extend(sub_edges)

return nodes, edges

生成dot文件内容

def dotify(self, tree=None):

‘‘‘ 获取树的Graphviz Dot文件的内容

‘‘‘

if tree is None:

tree = self.tree

content = ‘digraph decision_tree {\n‘

nodes, edges = self.get_nodes_edges(tree)

for node in nodes:

content += ‘    "{}" [label="{}"];\n‘.format(node.id, node.label)

for edge in edges:

start, label, end = edge.start, edge.label, edge.end

content += ‘    "{}" -> "{}" [label="{}"];\n‘.format(start.id, end.id, label)

content += ‘}‘

return content

隐形眼镜数据生成Dot文件内容如下:

digraph decision_tree {

"959b4c0c-1821-446d-94a1-c619c2decfcd" [label="call"];

"18665160-b058-437f-9b2e-05df2eb55661" [label="to"];

"2eb9860d-d241-45ca-85e6-cbd80fe2ebf7" [label="your"];

"bcbcc17c-9e2a-4bd4-a039-6e51fde5f8fd" [label="areyouunique"];

"ca091fc7-8a4e-4970-9ec3-485a4628ad29" [label="02073162414"];

"aac20872-1aac-499d-b2b5-caf0ef56eff3" [label="ham"];

"18aa8685-a6e8-4d76-bad5-ccea922bb14d" [label="spam"];

"3f7f30b1-4dbb-4459-9f25-358ad3c6d50b" [label="spam"];

"44d1f972-cd97-4636-b6e6-a389bf560656" [label="spam"];

"7f3c8562-69b5-47a9-8ee4-898bd4b6b506" [label="i"];

"a6f22325-8841-4a81-bc04-4e7485117aa1" [label="spam"];

"c181fe42-fd3c-48db-968a-502f8dd462a4" [label="ldn"];

"51b9477a-0326-4774-8622-24d1d869a283" [label="ham"];

"16f6aecd-c675-4291-867c-6c64d27eb3fc" [label="spam"];

"adb05303-813a-4fe0-bf98-c319eb70be48" [label="spam"];

"959b4c0c-1821-446d-94a1-c619c2decfcd" -> "18665160-b058-437f-9b2e-05df2eb55661" [label="0"];

"18665160-b058-437f-9b2e-05df2eb55661" -> "2eb9860d-d241-45ca-85e6-cbd80fe2ebf7" [label="0"];

"2eb9860d-d241-45ca-85e6-cbd80fe2ebf7" -> "bcbcc17c-9e2a-4bd4-a039-6e51fde5f8fd" [label="0"];

"bcbcc17c-9e2a-4bd4-a039-6e51fde5f8fd" -> "ca091fc7-8a4e-4970-9ec3-485a4628ad29" [label="0"];

"ca091fc7-8a4e-4970-9ec3-485a4628ad29" -> "aac20872-1aac-499d-b2b5-caf0ef56eff3" [label="0"];

"ca091fc7-8a4e-4970-9ec3-485a4628ad29" -> "18aa8685-a6e8-4d76-bad5-ccea922bb14d" [label="1"];

"bcbcc17c-9e2a-4bd4-a039-6e51fde5f8fd" -> "3f7f30b1-4dbb-4459-9f25-358ad3c6d50b" [label="1"];

"2eb9860d-d241-45ca-85e6-cbd80fe2ebf7" -> "44d1f972-cd97-4636-b6e6-a389bf560656" [label="1"];

"18665160-b058-437f-9b2e-05df2eb55661" -> "7f3c8562-69b5-47a9-8ee4-898bd4b6b506" [label="1"];

"7f3c8562-69b5-47a9-8ee4-898bd4b6b506" -> "a6f22325-8841-4a81-bc04-4e7485117aa1" [label="0"];

"7f3c8562-69b5-47a9-8ee4-898bd4b6b506" -> "c181fe42-fd3c-48db-968a-502f8dd462a4" [label="1"];

"c181fe42-fd3c-48db-968a-502f8dd462a4" -> "51b9477a-0326-4774-8622-24d1d869a283" [label="0"];

"c181fe42-fd3c-48db-968a-502f8dd462a4" -> "16f6aecd-c675-4291-867c-6c64d27eb3fc" [label="1"];

"959b4c0c-1821-446d-94a1-c619c2decfcd" -> "adb05303-813a-4fe0-bf98-c319eb70be48" [label="1"];

}

这样我们便可以使用Graphviz将决策树绘制出来

with open(‘lenses.dot‘, ‘w‘) as f:

dot = clf.tree.dotify()

f.write(dot)

dot -Tgif lenses.dot -o lenses.gif

效果如下:

使用生成的决策树进行分类

对未知数据进行预测,主要是根据树中的节点递归的找到叶子节点即可。z这里可以通过为递归进行优化,代码实现如下:

def classify(self, data_vect, feat_names=None, tree=None):

‘‘‘ 根据构建的决策树对数据进行分类

‘‘‘

if tree is None:

tree = self.tree

if feat_names is None:

feat_names = self.feat_names

# Recursive base case.

if type(tree) is not dict:

return tree

feature = list(tree.keys())[0]

value = data_vect[feat_names.index(feature)]

sub_tree = tree[feature][value]

return self.classify(feat_names, data_vect, sub_tree)

决策树的存储

通过字典表示决策树,这样我们可以通过内置的pickle或者json模块将其存储到硬盘上,同时也可以从硬盘中读取树结构,这样在数据集很大的时候可以节省构建决策树的时间.

def dump_tree(self, filename, tree=None):

‘‘‘ 存储决策树

‘‘‘

if tree is None:

tree = self.tree

with open(filename, ‘w‘) as f:

pickle.dump(tree, f)

def load_tree(self, filename):

‘‘‘ 加载树结构

‘‘‘

with open(filename, ‘r‘) as f:

tree = pickle.load(f)

self.tree = tree

return tree

总结

本文一步步实现了决策树的实现, 其中使用了ID3算法确定最佳划分属性,并通过Graphviz可视化了构建的决策树。本文相关的代码链接: https://github.com/PytLab/MLBox/tree/master/decision_tree

参考:

    • 《Machine Learning in Action》
    • 数据挖掘系列(6)决策树分类算法
时间: 2024-08-25 04:36:50

机器学习算法实践:决策树 (Decision Tree)(转载)的相关文章

转载:算法杂货铺——分类算法之决策树(Decision tree)

作者:张洋 算法杂货铺——分类算法之决策树(Decision tree) 2010-09-19 16:30 by T2噬菌体, 44346 阅读, 29 评论, 收藏, 编辑 3.1.摘要 在前面两篇文章中,分别介绍和讨论了朴素贝叶斯分类与贝叶斯网络两种分类算法.这两种算法都以贝叶斯定理为基础,可以对分类及决策问题进行概率推断.在这一篇文章中,将讨论另一种被广泛使用的分类算法——决策树(decision tree).相比贝叶斯算法,决策树的优势在于构造过程不需要任何领域知识或参数设置,因此在实际

机器学习(二)之决策树(Decision Tree)

Contents 理论基础 熵 信息增益 算法实现 Python 模型的保存与读取 总结 理论基础 决策树(Decision Tree, DT):决策树是一种基本的分类与回归方法.由于模型呈树形结构,可以看做是if-then规则的集合,具有一定的可读性,可视化效果好. 决策树的建立包括3个步骤:特征选择.决策树生成和决策树的修剪. 模型的建立实际上就是通过某种方式,递归地选择最优的特征,并通过数据的划分,将无序的数据变得有序. 因此,在构造决策树时,第一个需要解决的问题就是如何确定出哪个特征在划

【机器学习算法-python实现】决策树-Decision tree(1) 信息熵划分数据集

(转载请注明出处:http://blog.csdn.net/buptgshengod) 1.背景 决策书算法是一种逼近离散数值的分类算法,思路比較简单,并且准确率较高.国际权威的学术组织,数据挖掘国际会议ICDM (the IEEE International Conference on Data Mining)在2006年12月评选出了数据挖掘领域的十大经典算法中,C4.5算法排名第一.C4.5算法是机器学习算法中的一种分类决策树算法,其核心算法是ID3算法. 算法的主要思想就是将数据集依照特

【机器学习算法-python实现】决策树-Decision tree(2) 决策树的实现

(转载请注明出处:http://blog.csdn.net/buptgshengod) 1.背景 接着上一节说,没看到请先看一下上一节关于数据集的划分数据集划分.如今我们得到了每一个特征值得信息熵增益,我们依照信息熵增益的从大到校的顺序,安排排列为二叉树的节点.数据集和二叉树的图见下. (二叉树的图是用python的matplotlib库画出来的) 数据集: 决策树: 2.代码实现部分 由于上一节,我们通过chooseBestFeatureToSplit函数已经能够确定当前数据集中的信息熵最大的

机器学习入门 - 1. 介绍与决策树(decision tree)

机器学习(Machine Learning) 介绍与决策树(Decision Tree) 机器学习入门系列 是 个人学习过程中的一些记录与心得.其主要以要点形式呈现,简洁明了. 1.什么是机器学习? 一个比较概括的理解是: 根据现有的数据,预测未来 2.核心思想 : Generalization 可以理解为,归纳.概括.就像是人的学习一样,找出一件事物与与一件事物的联系 3.归纳性的机器学习(Inductive machine learning) 其核心思想是使用训练数据,并从其中摸索出一套适用

机器学习算法之决策树

机器学习算法之决策树 什么是决策树 决策树(Decision Tree)是一种简单但是广泛使用的分类器.通过训练数据构建决策树,可以高效的对未知的数据进行分类.决策数有两大优点:1)决策树模型可以读性好,具有描述性,有助于人工分析:2)效率高,决策树只需要一次构建,反复使用,每一次预测的最大计算次数不超过决策树的深度. 决策树是一个树结构(可以是二叉树或者非二叉树),非叶节点表示一个特征属性上的测试,每个分支代表在某个值域上的输出,每个叶节点存放一个类别. 测试就是按照从根节点往下走,直到叶节点

数据挖掘-决策树 Decision tree

数据挖掘-决策树 Decision tree 目录 数据挖掘-决策树 Decision tree 1. 决策树概述 1.1 决策树介绍 1.1.1 决策树定义 1.1.2 本质 1.1.3 决策树的组成 1.1.4 决策树的分类 1.1.5 决策过程 1.2 决策树的优化 1.2.1 过拟合 1.3.1 剪枝 2. 理论基础 2.1 香农理论 2.1.1 信息量 2.1.2 平均信息量/信息熵 2.1.3 条件熵 2.1.4 信息增益(Information gain) 2.1.5 信息增益率

机器学习-决策树 Decision Tree

咱们正式进入了机器学习的模型的部分,虽然现在最火的的机器学习方面的库是Tensorflow, 但是这里还是先简单介绍一下另一个数据处理方面很火的库叫做sklearn.其实咱们在前面已经介绍了一点点sklearn,主要是在categorical data encoding那一块.其实sklearn在数据建模方面也是非常666的.一般常用的模型都可以用sklearn来做的.既然它都这么牛逼了,咱们为啥还要学TensorFlow呢?其实主要的原因有两个,一是因为Google在流量方面的强势推广,导致绝

【转载】决策树Decision Tree学习

本文转自:http://www.cnblogs.com/v-July-v/archive/2012/05/17/2539023.html 最近在研究规则引擎,需要学习决策树.决策表等算法.发现篇好文对我这个初学者很有指导价值,先转再细品. 1. 什么是决策树 咱们直接切入正题.所谓决策树,顾名思义,是一种树,一种依托于策略抉择而建立起来的树. 机器学习中,决策树是一个预测模型:他代表的是对象属性与对象值之间的一种映射关系.树中每个节点表示某个对象,而每个分叉路径则代表的某个可能的属性值,而每个叶