python数据分析之决策树实践

1、信息熵
  • 信息熵,用来描述系统信息量的不确定度,不确定性越大,则信息熵越大,反之,信息熵越小。
1.1 信息熵公式

假设随机变量X具有M个值,分别为: V1,V2 ,… ,Vm ,并且各个值出现的概率(各概率值的和为1)如下:

在这里插入图片描述

则变量X的信息熵为:

在这里插入图片描述

1.2 概率分布与信息熵
  • 对于信息熵,可以用概率分布来衡量:对于随机变量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("概率") 
plt.ylabel("信息熵") 
plt.title("概率-信息熵对比") 
plt.show()

在这里插入图片描述

从上图可以看出,概率p越大时,分布越均衡,其信息熵也越来越大,并在概率为0.5时达到最大值,此时两个分类的概率相等,分布也最均衡。

2、决策树
2.1 决策树概念
  • 决策树是一种树形结构,通过特征的不同来将样本数据划分到不同的分支(子树)中,最终,每个样本一定会划分到一个叶子节点中。
  • 我们可以将每个特征视为一个问题(提问),特征值的不同,就视为样本给出的不同答案,然后,我们就可以根据一系列问题(特征),将样本划分到不同的叶子节点中。
  • 决策树是一种非参数监督学习方法,可以用于分类与回归。
2.2 决策树预测原理
  • 当对训练集进行训练,构建决策树后,我们就可以对未知样本进行预测。
  • 预测的过程为:根据未知样本的特征,逐步进行分支选择(回答问题),直到叶子节点为止。
    然后我们就可以使用该叶子节点中的已知样本来预测该未知样本。
  • 对于分类树,使用叶子节点中类别数量最多的类别,作为未知样本的类别。
  • 对于回归树,使用叶子节点中所有样本的均值,作为未知样本的类别。
3、分类决策树
3.1 信息增益
  • 信息增益在决策树算法中是用来选择特征的指标,信息增益越大,则这个特征的选择性越好。
    信息增益(IG-Information gain)定义如下:

在这里插入图片描述

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

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

(1)将每一个特征看成是一种分裂可能,特征可以分为离散型与连续性。

  • 对于离散型特征,每一个类别可以划分为一个子节点(多叉树),或者属于类别A与不属于类别A(二叉树)。
  • 对于连续性特征,可以划分为大于等于A与小于A。

(2)从根节点开始,选择可获得最大信息增益的特征进行分裂(实现信息增益最大化)。

(3)对子节点继续选择能够获得最大信息增益的特征进行分裂,直到满足如下条件之一,停止分裂:

  • 所有叶子节点中的样本属于同一个类别。
  • 树达到指定的最大深度(max_depth),每次分裂视为一层。
  • 叶子节点包含的样本数量小于指定的最小分裂样本数量(min_samples_split)。
  • 如果分裂后,叶子节点包含的样本数量小于指定的叶子最小样本数量 (min_samples_leaf)。
3.3 分类决策树示例

例如,给定如下的数据集,预测样本11的结果:

序号拥有房产(x1)婚姻状态(x2)年收入(x3)能否偿还债务(Y)
1单身125
2已婚100
3单身100
4已婚110
5离婚60
6离婚95不能
7单身85不能
8已婚75
9单身90不能
10离婚220
11已婚94

我们可以将三个特征作为三个问题,依次来“询问”数据集中的每个样本,经过每个样本依次“作答”之后,就可以将样本划分到不同的分支中,这样,决策树就训练完成(决策树的训练,就是根据训练集去构建一颗决策树)。

以不纯度衡量使用信息熵为例,父节点的信息熵为:

在这里插入图片描述

如果以特征“拥有房产”作为分裂特征,则:

在这里插入图片描述

因此,特征“拥有房产”的信息增益为:

在这里插入图片描述

同样的方法可求出:

在这里插入图片描述

对于收入来说,为连续型变量,我们这里将收入的取值进行排序,然后选择“否”与“是”的分界点(75与85之间,95与100之间),取平均值进行分割:

在这里插入图片描述

同样的方法可求出:

在这里插入图片描述

从上面的结果中可知,相比于房产、婚姻、年收入=80来说,年收入=97.5可以获得更大的信息增益。于是可以选择年收入作为父节点,训练结果如下图所示:

在这里插入图片描述

则对于样本11,很容易就可以得出预测的类别结果为:能。

4、不纯度度量标准
  • 信息熵(Entropy)

在这里插入图片描述

  • 基尼系数(Gini Index)

在这里插入图片描述

  • 错误率(classification error)

在这里插入图片描述

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

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

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

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

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

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

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

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

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

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

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

信息增益率的定义方式为:

在这里插入图片描述

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

5.3 CART

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

  • 使用二叉树结构

  • 支持连续值与缺失值处理

  • 既支持分类,也支持回归。

  • 分类决策:使用基尼系数作为不纯度度量标准,选择基尼增益最大的特征分裂节点。

  • 回归决策:使用MSE或MAE最小的特征分类节点。

6、回归决策树
  • 回归任务的标签(y 值)是连续的,所以之前以分类为基础的不纯度度量标准(信息熵,基尼系数与错误率)都不适用于回归树。
  • 在回归树中,也没有信息增益,信息增益率或基尼增益等概念。可以说,分类决策树选择特征的方式,完全不适用于回归决策树。
  • 对于回归决策树,会使用叶子节点的均值来预测未知样本。
  • 回归决策树使用MSE或MAE作为评估指标,用来选择特征。也就是说,回归决策树在选择特征上,每次选择能够使得MSE或MAE最小的特征,用来分裂节点。
7、决策树实践
  • 在scikit-learn中,使用优化的CART算法来实现决策树。
7.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)

tree = DecisionTreeClassifier()
"""
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.fit(X_train, y_train)
print(tree.score(X_train, y_train))
print(tree.score(X_test, y_test))
----------------------------------------------
0.9375
0.6578947368421053

从以上结果看出,训练集高于测试集,原因就在于过拟合。因为如果没有指定树的深度,则默认会训练一颗完全生长的决策树(不限深度),这会容易导致模型复杂化,从而过分依赖于训练集数据的特性,造成过拟合。

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

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)

在这里插入图片描述

从以上结果可以看出:当最大深度越来越大时,决策边界越来越复杂,模型也就越复杂。

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

# 定义列表,用来存储在不同深度下,模型的分值。 
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()

在这里插入图片描述

从以上结果可以看出:随着最大深度的增加,训练集的表现越来越好,但是测试集的表现,是先增加后减少,这说明,在树深度较小时,模型是欠拟合的,因此,增加树深度,能够提升预测效果,但随着深度的增加,模型越来越依赖于训练集,这反而降低预测效果,造成过拟合。

7.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))
-------------------------------------
0.8290972700366354
0.6354364289453209
  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值