机器学习之决策树(数据分析师学习必备)——糖潮丽子的博客

现实生活中,我们会进行各种各样的选择。不论是挑选商品,还是挑选任何东西,都是通过以往经验所得。如果我们把挑选东西背后的逻辑整理成一个结构图,你会发现它实际上就是一个树状图,就像公司人员结构组成那样,这就是我们本篇博客要讲解的内容——决策树。

决策树是一种机器学习的方法。决策树的生成算法有ID3, C4.5和CART等。决策树是一种树形结构,其中每个内部节点表示一个属性上的判断,每个分支代表一个判断结果的输出,最后每个叶节点代表一种分类结果。
在这里插入图片描述

1、主题

本篇博客讲述决策树算法,以及通过算法实现分类与回归任务。
先来了解一下树的概念:
树:是由节点和边两种元素组成的结构。理解树,就需要理解几个关键词:根节点、父节点、子节点和叶子节点。
父节点和子节点是相对的,说白了子节点由父节点根据某一规则分裂而来,然后子节点作为新的父亲节点继续分裂,直至不能分裂为止。而根节点是没有父节点的节点,即初始分裂节点,叶子节点是没有子节点的节点,如下图所示:
在这里插入图片描述
决策树利用如上图所示的树结构进行决策,每一个非叶子节点是一个判断条件,每一个叶子节点是结论。从跟节点开始,经过多次判断得出结论。

2、目标

  • 熟知决策树算法的原理,训练与预测方式。
  • 分类树与回归树的训练准则及各自的度量标准。
  • 三种常用的决策树算法。
  • 使用决策树实现分类与回归任务。

3、信息熵

讲解决策树之前,我们需要知道信息熵的概念。

3.1 概念

信息熵,在1948年由香农提出。用来描述系统信息量的不确定度
不确定性越大,则信息熵越大,反之,信息熵越小。

例如,4只猎豹参与赛跑,每只猎豹的能力都是旗鼓相当,平分秋色。我们很难确定哪只猎豹会获得胜利,因此,这种情况下,不确定性很大,信息熵就大。但是,假设让1只猎豹与3只蜗牛进行赛跑,则猎豹取胜便是毋容置疑的,因此,这种情况下,不确定性很小,信息熵就小。

练习:

以下哪个选项的信息熵最大? (C
A 明天会下雨。
B 足球是圆的。
C 下一期双色球的一等奖的中奖号码是红球:1,2,3,4,5,6,蓝球:7。
D 下周二某支股票会上涨。

3.2 计算方式

假设随机变量具有M个值,分别为: V1,V2 ,… ,Vm 。并且各个值出现的概率如下:
在这里插入图片描述
则变量的信息期望值(信息熵)为:
在这里插入图片描述
信息熵公式:
在这里插入图片描述
例如:
一个班级男生了60人,女生有40人。那么男生的概率是0.6,女生的概率是0.4。
信息熵 H(X)= - (0.6log2(0.6) + 0.4log2(0.4)) 。

3.3 概率分布与信息熵

对于信息熵,我们可以用概率分布来衡量,对于随机变量X,其分布越均衡,则不确定性越多,信息熵越大。其分布越不均衡,则不确定性越小,信息熵越小。

import numpy as np
import matplotlib.pyplot as plt

plt.rcParams["font.family"] = "SimHei"
plt.rcParams["axes.unicode_minus"] = False
plt.rcParams["font.size"] = 12

# 假设随机变量X可以取两个值,一个值的概率为p,则另外一个值的概率为1 - p。
p = np.linspace(0.01, 0.99, 100)
# 计算在不同概率分布下的信息熵。
h = -p * np.log2(p) - (1 - p) * np.log2(1 - p)
plt.plot(p, h)
plt.xlabel("p1取值")
plt.ylabel("信息熵")
plt.title("概率-信息熵对比")
plt.show()

结果:
在这里插入图片描述
从以上图中我们可以看出当p1取值越来越大时,信息熵就越大,分布就越来越均衡,也就是不确定性越大。p1取值为0.5时,信息熵最大,p1和p2概率都是0.5,这时候分布均等,不确定性也是最大的。

如果我们把概率分布转换到数据集上。信息熵体现的就是数据的不纯度,即样本类别的均衡程度
样本类别越均衡,不纯度越高,信息熵越大。反之,样本类别越不均衡,不纯度越低,信息熵越小。

我们可以进行如下的表示:
信息熵 =》 概率(比例)均衡度 =》 不纯度

练习:

不通过计算,以下哪个数据集的信息熵最大? (C
A 箱子中有9个红球,1个白球。
B 班级中有30个男生,25个女生。
C饮料店成交30单,其中10单苹果味,10单哈密瓜味,10单葡萄味。
D 路口经过55辆车,其中50辆本地车,5辆外地车。

4、决策树训练与预测

4.1 决策树概念

决策树 是一种树形结构通过特征的不同来将样本数据划分到不同的分支(子树)中,最终,每个样本一定会划分到一个叶子节点中。 我们可以将每个特征视为一个问题(提问),特征值的不同,就视为样本给出的不同答案,然后,我们就可以根据一系列问题(特征),将样本划分到不同的叶子节点中。决策树是一种非参数监督学习方法,可以用于分类与回归

树形结构适合一对多的情形。
例如:家族族谱,公司组织机构等都是树形结构。

4.2 训练决策树

例如,给定如下的数据集:
在这里插入图片描述
我们就可以将三个特征作为三个问题,依次来“询问”数据集中的每个样本,经过每个样本依次“作答”之后,就可以将样本划分到不同的分支中,这样,决策树就训练完成。其实,决策树的训练,就是根据训练集去构建一颗决策树。结果如下图所示。
在这里插入图片描述

4.3 预测原理

当在训练集上构建决策树后,我们就可以对未知样本进行预测。
预测的过程为根据未知样本的特征,逐步进行分支选择(回答问题),直到叶子节点为止。
那么,我们就可以使用该叶子节点中的已知样本来预测该未知样本。
可是,我们预测的依据是什么呢?
我们可以想象,假设对与两个人进行性格测试,两个人回答一些相同的选择题,如果两个人的选项完全一致,则说明两个人的性格存在很大的相似性。
同样,决策树是根据特征值来划分样本的(这类似于回答问题)。
如果样本经过层层划分之后,分到了同一个叶子节点中,则表明这些样本应该也是非常
相似的。

因此,我们就可以使用在同一个叶子节点中的已知样本,去预测未知样本的标签了。

预测的方式为:

  • 对于分类树,使用叶子节点中,类别数量最多的类别,作为未知样本的类别。
  • 对于回归树,使用叶子节点中,所有样本的均值,作为未知样本的类别。

例如,样本11预测的类别为:能。

5、分类决策树

5.1 训练的疑问

从刚才的介绍可知,决策树可以看做由若干个节点构成,其中,每个节点包含一定数量的样本(根节点包含所有样本数据)。
决策树的训练过程就是根据特征值来分割样本数据。

然而,从刚才训练决策树的方式上,我们需要解决如下的问题:

  • 我们是否可以先根据“拥有房产”或“婚姻情况”特征进行划分?当选择特征划分样本时,顺序是否是任意的?
  • 对于连续变量类型的特征,年收入为什么选择以97.5来进行划分,而不是其他值?
    在这里插入图片描述

5.2 信息增益

信息增益在决策树算法中是用来选择特征的指标,信息增益越大,则这个特征的选择性越好。
信息增益(IG-Information gain)定义如下:
在这里插入图片描述
信息增益就是父节点的不纯度减去所有子节点不纯度(加权)。
我们要优先选择信息增益大的节点当做父节点。

练习:

假设你是总经理助理,目前有10份文件,其中4个与M公司有关,6个与N公司有关。我们需要对文件 进行整理,目前X与Y两个盒子,我们会选择( A )。
A 4个与M公司相关的文件放入X,6个与N公司相关的文件放入Y。
B 3个与M公司相关的文件放入X,另外1个与6个与N公司相关的文件放入Y。
C 1个与M公司相关的文件放入X,另外3个与6个与N公司相关的文件放入Y。
D 所有文件放入X或Y中,另外一个盒子空闲。

就像我们整理文件一样,在选择特征分裂样本时,我们应该让子节点的不纯度尽可能的低,这样就可以更快的完成训练(更少的分割次数),同时,在预测未知样本时,也会具有更高的准确度。

因此,在我们选择特征进行划分时,特征的顺序不是任意的,而是应该选择在分割样本集后,能够使得所有子节点不纯度最低(加权)的特征。由于父节点的不纯度是不变的,因此,能够让所有子节点不纯度最小的特征,实际上也就是能够所得信息增益最大的特征。 而这,也正是训练分类决策树时,选择特征顺序的依据。

5.3 训练规则

训练分类决策树的具体规则如下:

  1. 将每一个特征看成是一种分裂可能。特征可以分为离散型与连续性
  • 离散型特征,每一个类别可以划分为一个子节点(多叉树),或者属于类别A与不属于类别A(二叉树)。
  • 对于连续性特征,可以划分为大于等于A与小于A。
  1. 从根节点开始,选择可获得最大信息增益的特征进行分裂(实现信息增益最大化)。
  2. 对子节点继续选择能够获得最大信息增益的特征进行分裂,直到满足如下条件之一,停止分裂。
  • 所有叶子节点中的样本属于同一个类别。
  • 树达到指定的最大深度(max_depth),每次分裂视为一层。
  • 叶子节点包含的样本数量小于指定的最小分裂样本数量(min_samples_split)。
  • 如果分裂后,叶子节点包含的样本数量小于指定的叶子最小样本数量 (min_samples_leaf)。

5.4 分类决策树示例

以不纯度衡量使用信息熵为例,父节点的信息熵为:
在这里插入图片描述
如果以特征“拥有房产”作为分裂特征,则:
在这里插入图片描述
在这里插入图片描述

因此,特征“拥有房产”的信息增益为:
在这里插入图片描述
对于收入来说,为连续型变量,我们这里将收入的取值进行排序,然后选择“否”与“是”的分界点(75与85之间,95与100之间),取平均值进行分割:
在这里插入图片描述
在这里插入图片描述
从上面的结果中可知,相比于收入=80来说,收入=97.5可以获得更大的信息增益。
这也就是为什么我们一开始选择年收入(信息增益大的)作为父节点,作为分裂条件。

6、不纯度度量标准

不纯度可以采用如下方式度量:

  • 信息熵(Entropy)
  • 基尼系数(Gini Index)
  • 错误率(classification error)

6.1 信息熵

在这里插入图片描述

  • m:节点中含有样本的类别数量。
  • p(i | D):节点中,属于类别的样本占节点中样本总数的比例(概率)。

6.2 基尼系数

在这里插入图片描述

6.3 错误率

在这里插入图片描述

无论哪种度量标准,都有一个特性:
如果样本以相同的比例分布于不同的类别时,度量值最大,不纯度最高。如果所有的样本都属于同一个类别,则度量值为0,不纯度最低。

# 定义函数,返回基尼系数。
def gini(p):
    return 1 - p ** 2 - (1 - p) ** 2

# 定义函数,返回信息熵。
def entropy(p):
    return -p * np.log2(p) - (1 - p) * np.log2(1 - p)

# 定义函数,返回错误率。
def error(p):
    return 1 - np.max([p, 1 - p], axis=0)

# 定义概率的取值范围。
p = np.linspace(0.01, 0.99, 200)
# 计算信息熵。
en = entropy(p)
# 计算缩放的信息熵。
en2 = en * 0.5
# 计算错误率。
err = error(p)
# 计算基尼系数。
g = gini(p)
fig = plt.figure()
for i, lab, ls, c, in zip([en, en2, g, err], ["信息熵", "信息熵(缩放)", "基尼系数", "错误率"],
    ["-", ":", "--", "-."], ["r", "g", "b", "y"]):
    plt.plot(p, i, label=lab, linestyle=ls, lw=2, color=c)
    plt.legend(loc="right", bbox_to_anchor=(1.55, 0.8))
    plt.axhline(y=0.5, linewidth=1, color='k', linestyle="--")
    plt.axhline(y=1.0, linewidth=1, color='k', linestyle="--")
    plt.ylim([0, 1.1])
    plt.xlabel("概率1取值")
    plt.ylabel("纯度系数")
plt.show()

结果:
在这里插入图片描述
可以看到信息熵、信息熵(缩放)、基尼系数、错误率他们都会随着不纯度的增大而增大,随着不纯度的减小而减小。

7、决策树算法

决策树主要包含以下三种算法:

  • ID3
  • C4.5
  • CART(Classification And Regression Tree)

7.1 ID3

ID3(Iterative Dichotomiser3-迭代二分法)算法是非常经典的决策树算法,该算法描述如下:

  • 使用多叉树结构
  • 使用信息熵作为不纯度度量标准,选择信息增益最大的特征分割数据

ID3算法简单,训练较快。但该算法具有一些局限,如下:

  • 不支持连续特征
  • 不支持缺失值
  • 仅支持分类,不支持回归
  • 在选择特征时,会倾向于选择类别多的特征

7.2 C4.5

C4.5算法是在ID3算法上改进而来,该算法描述如下:

  • 使用多叉树结构
  • 仅支持分类,不支持回归

不过,C4.5在ID3算法上,进行了一些优化,包括:

  • 支持对缺失值的处理
  • 支持将连续值进行离散化处理
  • 使用信息熵作为不纯度度量标准,但选择信息增益率(而不是信息增益)最大的特征分裂节点。

信息增益率的定义方式为:
在这里插入图片描述

  • IH(f) :根据特征 f 的不同类别值比例(概率),计算得到的信息熵。

之所以从信息增益改为信息增益率,是因为在ID3算法中,倾向于选择类别多的特征,因此,经过这样的调整,在C4.5中就可以得到缓解。因为类别多的特征在计算信息熵时,往往会比类别少的特征信息熵 IH(f) 大。这样,就可以在分母上进行一定的惩罚。

def entropy(p):
    return np.sum(-p * np.log2(p))

# 计算在不同类别数量时,信息熵的对比。每个元素代表一个类别所占的比例。
a1 = np.array([0.4, 0.6])
a2 = np.array([0.3, 0.3, 0.2, 0.2])
a3 = np.array([0.1] * 10)
print(entropy(a1))
print(entropy(a2))
print(entropy(a3))

结果:
在这里插入图片描述
从结果中可以看出,类别比较多的话信息熵也会比较大。

7.3 CART

CART(Classification And Regression Tree),分类与回归树。该算法描述如下:

  • 使用二叉树结构
  • 支持连续值与缺失值处理
  • 既支持分类,也支持回归
    - 使用基尼系数作为不纯度度量标准,选择基尼增益最大的特征分裂节点(分类)
    - 使用MSE或MAE最小的特征分类节点(回归)

8、回归决策树

当使用回归决策树时,与分类决策树会有所不同。
回归任务的标签( 值)是连续的,故之前以分类为基础的不纯度度量标准(信息熵,基尼系数与错误率)都不适用于回归树,因此,在回归树中,自然也就没有信息增益,信息增益率或基尼增益等概念了。可以说,分类决策树选择特征的方式,完全不适用于回归决策树。

对于回归决策树,会使用叶子节点的均值来预测未知样本。
同时,回归决策树使用MSE或MAE作为评估指标,用来选择特征。也就是说,回归决策树在选择特征上,每次选择能够使得MSE或MAE最小的特征,用来分裂节点。

9、程序(代码)实现

在scikit-learn中,使用优化的CART算法来实现决策树。

9.1 分类

scikit-learn中,提供DecisionTreeClassifier类,用来实现决策树分类

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

X, y = load_iris(return_X_y=True)
# 为了后续的可视化方便,这里选择两个特征。
X = X[:, :2]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25,
random_state=0)
# criterion:不纯度度量标准,默认为gini。
# gini:基尼系数 entropy:信息熵
# splitter:选择分裂节点的方式。默认为best。
# best:在最好的位置分裂节点。 random:在随机的位置分裂节点。
# max_depth:树的最大深度,默认为None(不限制深度)。
# min_samples_split:分裂节点的最小样本数,默认为2。
# min_samples_leaf:分裂节点后,叶子节点最少的样本数量,默认为1。
# max_features:分裂节点时,考虑的最大特征数量,默认为None(考虑所有特征)。
# random_state:随机种子。
tree = DecisionTreeClassifier()
tree.fit(X_train, y_train)
print(tree.score(X_train, y_train))
print(tree.score(X_test, y_test))

决策树参数:
criterion:不纯度度量标准,默认为gini。
gini:基尼系数 entropy:信息熵
splitter:选择分裂节点的方式。默认为best。
best:在最好的位置分裂节点。 random:在随机的位置分裂节点。
max_depth:树的最大深度,默认为None(不限制深度)。
min_samples_split:分裂节点的最小样本数,默认为2。
min_samples_leaf:分裂节点后,叶子节点最少的样本数量,默认为1。
max_features:分裂节点时,考虑的最大特征数量,默认为None(考虑所有特征)。
random_state:随机种子。

结果:
在这里插入图片描述
结果中,训练集高于测试集,原因就在于过拟合。
我们发现,模型存在严重的过拟合倾向,原因在于,如果没有指定树的深度,则默认会训练一颗完全生长的决策树(不限深度),这会容易导致模型复杂化,从而过分依赖于训练集数据的特性,造成过拟合。

我们可以从不同深度树的决策边界,来证实这一点。

from matplotlib.colors import ListedColormap
def plot_decision_boundary(model, X, y):
    color = ["r", "g", "b"]
    marker = ["o", "v", "x"]
    class_label = np.unique(y)
    cmap = ListedColormap(color[: len(class_label)])
    x1_min, x2_min = np.min(X, axis=0)
    x1_max, x2_max = np.max(X, axis=0)
    x1 = np.arange(x1_min - 1, x1_max + 1, 0.02)
    x2 = np.arange(x2_min - 1, x2_max + 1, 0.02)
    X1, X2 = np.meshgrid(x1, x2)
    Z = model.predict(np.c_[X1.ravel(), X2.ravel()])
    Z = Z.reshape(X1.shape)
    plt.contourf(X1, X2, Z, cmap=cmap, alpha=0.5)
    for i, class_ in enumerate(class_label):
        plt.scatter(x=X[y == class_, 0], y=X[y == class_, 1],c=cmap.colors[i], label=class_, marker=marker[i])
    plt.legend()
    
plt.figure(figsize=(15, 10))
for index, depth in enumerate([1, 4, 7, 12], start=1):
    plt.subplot(2, 2, index)
    plt.title(f"最大深度:{depth}")
    tree = DecisionTreeClassifier(random_state=0, max_depth=depth)
    tree.fit(X_train, y_train)
    plot_decision_boundary(tree, X_test, y_test)

结果:
在这里插入图片描述
最大深度为1时,只能分成两个类别,因为只能建立一个二叉树,没有办法变出更多的分类。
最大深度为4的时候,三个类别,可以有4个问题,比深度为1时要复杂。
当最大深度越来越大时,决策边界越来越复杂,模型也就越复杂。

对于决策树来说,最大深度对模型有着较重要的影响,如果最大深度很小,意味着仅进行少数的切分,容易欠拟合,但是,如果最大深度很大,则意味着可能进行较多次切分,容易过拟合。

# 定义列表,用来存储在不同深度下,模型的分值。
train_score = []
test_score = []
for depth in range(1, 13):
    tree = DecisionTreeClassifier(random_state=0, max_depth=depth)
    tree.fit(X_train, y_train)
    train_score.append(tree.score(X_train, y_train))
    test_score.append(tree.score(X_test, y_test))
    
plt.plot(train_score, marker="o", c="red", label="训练集")
plt.plot(test_score, marker="o", c="green", label="测试集")
plt.legend()

结果:
在这里插入图片描述
从运行结果中,我们可知,随着最大深度的增加,训练集的表现越来越好,但是测试集的表现,是先增加后减少,这说明,在树深度较小时,模型是欠拟合的,因此,增加树深度,能够提升预测效果,但随着深度的增加,模型越来越依赖于训练集,这反而降低预测效果,造成过拟合。

9.2 回归

scikit-learn中,提供DecisionTreeRegressor类,用来实现决策树回归。

from sklearn.datasets import load_boston
from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import train_test_split

X, y = load_boston(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25,
random_state=0)

# 回归决策树的参数,可以参考分类决策树的参数。
tree = DecisionTreeRegressor(max_depth=3)
tree.fit(X_train, y_train)
print(tree.score(X_train, y_train))
print(tree.score(X_test, y_test))

结果:
在这里插入图片描述
在这里插入图片描述

10、总结

我们学习了以下内容:

  • 决策树的三种算法
    分别是:ID3、C4.5、CART

  • 分类树节点分裂准则。
    ①将每一个特征看成是一种分裂可能。特征可以分为离散型与连续性。
    ②从根节点开始,选择可获得最大信息增益的特征进行分裂(实现信息增益最大化)。
    ③对子节点继续选择能够获得最大信息增益的特征进行分裂,直到满足如下条件之一,停止分裂。

  • 回归树节点分裂准则。
    对于回归决策树,会使用叶子节点的均值来预测未知样本。
    同时,回归决策树使用MSE或MAE作为评估指标,用来选择特征。也就是说,回归决策树在选择特征上,每次选择能够使得MSE或MAE最小的特征,用来分裂节点。

  • 深度对决策树的影响。
    对于决策树来说,最大深度对模型有着较重要的影响,如果最大深度很小,意味着仅进行少数的切分,容易欠拟合,但是,如果最大深度很大,则意味着可能进行较多次切分,容易过拟合。

在这里插入图片描述
颜值高的关注/点赞 鼓励下呗!

学习链接:
https://www.cnblogs.com/yonghao/p/5061873.html
https://www.cnblogs.com/molieren/articles/10664954.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章