17 丨决策树(上):要不要去打篮球?决策树来告诉你
决策树的工作原理决策树基本上就是把我们以前的经验总结出来。我给你准备了一个打篮球的训练集。如果我们要出门打篮球,一般会根据“天气”、“温度”、“湿度”、“刮风”这几个条件来判断,最后得到结果:去打篮球?还是不去?
上面这个图就是一棵典型的决策树。我们在做决策树的时候,会经历两个阶段:构造和剪枝。构造什么是构造呢?
构造就是生成一棵完整的决策树。简单来说,构造的过程就是选择什么属性作为节点的过程,那么在构造过程中,会存在三种节点:根节点:就是树的最顶端,最开始的那个节点。在上图中,“天气”就是一个根节点;内部节点:就是树中间的那些节点,比如说“温度”、“湿度”、“刮风”;叶节点:就是树最底部的节点,也就是决策结果。节点之间存在父子关系。比如根节点会有子节点,子节点会有子子节点,但是到了叶节点就停止了,叶节点不存在子节点。那么在构造过程中,你要解决三个重要的问题:选择哪个属性作为根节点;选择哪些属性作为子节点;什么时候停止并得到目标状态,即叶节点。
剪枝 决策树构造出来之后是不是就万事大吉了呢?也不尽然,我们可能还需要对决策树进行剪枝。剪枝就是给决策树瘦身,这一步想实现的目标就是,不需要太多的判断,同样可以得到不错的结果。之所以这么做,是为了防止“过拟合”(Overfitting)现象的发生。“过拟合”这个概念你一定要理解,它指的就是模型的训练结果“太好了”,以至于在实际应用的过程中,会存在“死板”的情况,导致分类错误。欠拟合,和过拟合就好比是下面这张图中的第一个和第三个情况一样,训练的结果“太好“,反而在实际应用过程中会导致分类错误。
造成过拟合的原因之一就是因为训练集中样本量较小。如果决策树选择的属性过多,构造出来的决策树一定能够“完美”地把训练集中的样本分类,但是这样就会把训练集中一些数据的特点当成所有数据的特点,但这个特点不一定是全部数据的特点,这就使得这个决策树在真实的数据分类中出现错误,也就是模型的“泛化能力”差。泛化能力指的分类器是通过训练集抽象出来的分类能力,你也可以理解是举一反三的能力。如果我们太依赖于训练集的数据,那么得到的决策树容错率就会比较低,泛化能力差。因为训练集只是全部数据的抽样,并不能体现全部数据的特点。既然要对决策树进行剪枝,具体有哪些方法呢?一般来说,剪枝可以分为“预剪枝”(Pre-Pruning)和“后剪枝”(Post-Pruning)。预剪枝是在决策树构造时就进行剪枝。方法是在构造的过程中对节点进行评估,如果对某个节点进行划分,在验证集中不能带来准确性的提升,那么对这个节点进行划分就没有意义,这时就会把当前节点作为叶节点,不对其进行划分。后剪枝就是在生成决策树之后再进行剪枝,通常会从决策树的叶节点开始,逐层向上对每个节点进行评估。如果剪掉这个节点子树,与保留该节点子树在分类准确性上差别不大,或者剪掉该节点子树,能在验证集中带来准确性的提升,那么就可以把该节点子树进行剪枝。方法是:用这个节点子树的叶子节点来替代该节点,类标记为这个节点子树中最频繁的那个类。
如何判断要不要去打篮球?我给你准备了打篮球的数据集,训练数据如下:
显然将哪个属性(天气、温度、湿度、刮风)作为根节点是个关键问题,在这里我们先介绍两个指标:纯度和信息熵。
特别喜欢这里形容的纯度和信息熵,我们自己生活的形式可以用纯度来表示,如果一天只做一件事,那么肯定纯度是很高的,但是一个人不肯定一天只做一件事,而事情做的越多纯度就越低,信息熵恰好相反,我们做的事情越多,越复杂,那么信息熵就越高,信息的不确定程度就越强。 显而易见,纯度越高越好,信息熵越低越好,但我们的生活肯定是面临高信息熵和低纯度的状态。 我们每天需要起床,洗漱,吃饭,上班,写代码,交流需求,有时候摸摸鱼,再找人聊聊天,或者是看看短视频,电影……给我的选择让我们去做的东西太多了。 所以我们应该如何降低自己的信息熵,提高纯度呢? 固定的时间做固定的事情。这个是最好的状态和表现。 早上固定六点起床,晚上固定11点睡觉,中间时间固定聊天,写作业,写代码,问需求,吃饭……将一切都固定好,我觉得这是一个降低信息熵的一个非常好的方法
在信息论中,随机离散事件出现的概率存在着不确定性。为了衡量这种信息的不确定性,信息学之父香农引入了信息熵的概念,并给出了计算信息熵的数学公式:
我们在构造决策树的时候,会基于纯度来构建。而经典的 “不纯度”的指标有三种,分别是信息增益(ID3 算法)、信息增益率(C4.5 算法)以及基尼指数(Cart 算法)。我们先看下 ID3 算法。ID3 算法计算的是信息增益,信息增益指的就是划分可以带来纯度的提高,信息熵的下降。它的计算公式,是父亲节点的信息熵减去所有子节点的信息熵。在计算的过程中,我们会计算每个子节点的归一化信息熵,即按照每个子节点在父节点中出现的概率,来计算这些子节点的信息熵。所以信息增益的公式可以表示为:
于是我们通过 ID3 算法得到了一棵决策树。ID3 的算法规则相对简单,可解释性强。同样也存在缺陷,比如我们会发现 ID3 算法倾向于选择取值比较多的属性。这样,如果我们把“编号”作为一个属性(一般情况下不会这么做,这里只是举个例子),那么“编号”将会被选为最优属性 。但实际上“编号”是无关属性的,它对“打篮球”的分类并没有太大作用。所以 ID3 有一个缺陷就是,有些属性可能对分类任务没有太大作用,但是他们仍然可能会被选为最优属性。这种缺陷不是每次都会发生,只是存在一定的概率。在大部分情况下,ID3 都能生成不错的决策树分类。针对可能发生的缺陷,后人提出了新的算法进行改进。
在 ID3 算法上进行改进的 C4.5 算法那么 C4.5 都在哪些方面改进了 ID3 呢?1. 采用信息增益率因为 ID3 在计算的时候,倾向于选择取值多的属性。为了避免这个问题,C4.5 采用信息增益率的方式来选择属性。信息增益率 = 信息增益 / 属性熵,具体的计算公式这里省略。当属性有很多值的时候,相当于被划分成了许多份,虽然信息增益变大了,但是对于 C4.5 来说,属性熵也会变大,所以整体的信息增益率并不大。2. 采用悲观剪枝ID3 构造决策树的时候,容易产生过拟合的情况。在 C4.5 中,会在决策树构造之后采用悲观剪枝(PEP),这样可以提升决策树的泛化能力。悲观剪枝是后剪枝技术中的一种,通过递归估算每个内部节点的分类错误率,比较剪枝前后这个节点的分类错误率来决定是否对其进行剪枝。这种剪枝方法不再需要一个单独的测试数据集。3. 离散化处理连续属性C4.5 可以处理连续属性的情况,对连续的属性进行离散化的处理。比如打篮球存在的“湿度”属性,不按照“高、中”划分,而是按照湿度值进行计算,那么湿度取什么值都有可能。该怎么选择这个阈值呢,C4.5 选择具有最高信息增益的划分所对应的阈值。4. 处理缺失值针对数据集不完整的情况,C4.5 也可以进行处理。假如我们得到的是如下的数据,你会发现这个数据中存在两点问题。第一个问题是,数据集中存在数值缺失的情况,如何进行属性选择?第二个问题是,假设已经做了属性划分,但是样本在这个属性上有缺失值,该如何对样本进行划分?
我们不考虑缺失的数值,可以得到温度 D={2-,3+,4+,5-,6+,7-}。温度 = 高:D1={2-,3+,4+} ;温度 = 中:D2={6+,7-};温度 = 低:D3={5-} 。这里 + 号代表打篮球,- 号代表不打篮球。比如 ID=2 时,决策是不打篮球,我们可以记录为 2-。针对将属性选择为温度的信息增为:Gain(D′, 温度)=Ent(D′)-0.792=1.0-0.792=0.208属性熵 =1.459, 信息增益率 Gain_ratio(D′, 温度)=0.208/1.459=0.1426。D′的样本个数为 6,而 D 的样本个数为 7,所以所占权重比例为 6/7,所以 Gain(D′,温度) 所占权重比例为 6/7,所以:Gain_ratio(D, 温度)=6/7*0.1426=0.122。这样即使在温度属性的数值有缺失的情况下,我们依然可以计算信息增益,并对属性进行选择。Cart 算法在这里不做介绍,我会在下一讲给你讲解这个算法。现在我们总结下 ID3 和 C4.5 算法。首先 ID3 算法的优点是方法简单,缺点是对噪声敏感。训练数据如果有少量错误,可能会产生决策树分类错误。C4.5 在 ID3 的基础上,用信息增益率代替了信息增益,解决了噪声敏感的问题,并且可以对构造树进行剪枝、处理连续数值以及数值缺失等情况,但是由于 C4.5 需要对数据集进行多次扫描,算法效率相对较低。
总结前面我们讲了两种决策树分类算法 ID3 和 C4.5,了解了它们的数学原理。你可能会问,公式这么多,在实际使用中该怎么办呢?实际上,我们可以使用一些数据挖掘工具使用它们,比如 Python 的 sklearn,或者是 Weka(一个免费的数据挖掘工作平台),它们已经集成了这两种算法。只是我们在了解了这两种算法之后,才能更加清楚这两种算法的优缺点。我们总结下,这次都讲到了哪些知识点呢?首先我们采用决策树分类,需要了解它的原理,包括它的构造原理、剪枝原理。另外在信息度量上,我们需要了解信息度量中的纯度和信息熵的概念。在决策树的构造中,一个决策树包括根节点、子节点、叶子节点。在属性选择的标准上,度量方法包括了信息增益和信息增益率。在算法上,我讲解了两种算法:ID3 和 C4.5,其中 ID3 是基础的决策树算法,C4.5 在它的基础上进行了改进,也是目前决策树中应用广泛的算法。然后在了解这些概念和原理后,强烈推荐你使用工具,具体工具的使用我会在后面进行介绍。
最后我们留一道思考题吧。请你用下面的例子来模拟下决策树的流程,假设好苹果的数据如下,请用 ID3 算法来给出好苹果的决策树。
`
import pandas as pd
from sklearn import tree
import graphviz
df=pd.DataFrame([[1,1,1],[1,0,1],[0,1,0],[0,0,0]]).rename(columns={0:"红?",1:"大?",2:"好?"})
print(df)
X=df[["红?","大?",]]
y=df["好?"]
id3 = tree.DecisionTreeClassifier(criterion='entropy')
id3=id3.fit(X,y)
print(id3)
labels=['hong','da']
dot_data=tree.export_graphviz(
id3,feature_names=labels,
class_names=['hao','huai'],filled=True,rounded=True)
graph=graphviz.Source(dot_data)
print(graph)
`