机器学习算法——决策树

一、什么是决策树

1. 决策树简介

决策树(Decision Tree) 是一种以树形结果展示决策规则和分类结果的模型。作为一种归纳算法模型,它可以从看似无序、杂乱的数据中提取出数据分类的规律,并用其进行新数据分类或预测。

决策树可被用于解决分类和回归问题。例如:通过一个西瓜的多种特征,如其纹理、色泽、触感,等判断其是否是好瓜;或通过一天的天气情况,如是否晴天、风力大小、空气湿度等,判断这一天是否适合进行户外运动等。

2. 决策树的运作方式

作为树形结构,决策树自然拥有树形结构的特征:具有有向边(Dirceted Edge)结点(Node)。有向边连接结点,形成的树状结构即为树。

对于决策树,特别地,结点代表了决策树不同阶段的分类状态。结点分为内部结点(Internal Node)叶子结点(Leaf Node)。叶子结点定义与常规树形结构相同,即无子结点的结点,它明显代表了决策树决策路径的最终决策结果。而内部结点即为树内部的结点,即非叶子结点,它代表了决策的中间过程。从根结点开始,根据每个结点处分类标准的不同,选择不同有向边到达后续的结点,直至到达叶子结点,即获取决策结果。

以先前提到的西瓜分类问题为例,决策树的结构如下图所示:
西瓜分类问题决策树图

3. 决策树的构建方法

可以看出,决策树在每一个内部结点处,通过选取某一特征值,再根据其具体值来进行下一步决策。故构建决策树的过程,即选取最优特征值作为划分属性的过程,即:

Created with Raphaël 2.3.0 开始 生成结点 可用数据为空? 返回空结点Null 结束 结果纯净? 标记结点为该类型叶子结点 选择最优划分属性 生成分支结点 yes no yes no

以上为单条决策路径的构建流程。

其中选择最优划分属性的方法则涉及到具体的决策树构建算法。

二、决策树构建算法

1. 决策树构建算法基本概念

在选择最优属性之前,我们需要指标来衡量属性在结果划分上的优劣性。

因此,我们需要了解决策树构建的基本概念。

1.1 信息量

信息量(Information Content) 是一个用于衡量事件“意外程度”的指标。

若有一事件 x x x,其发生的概率为 P ( x ) P(x) P(x),则 x x x的信息量的定义为:
I ( x ) = − l o g 2 P ( x ) I(x) = -log_2P(x) I(x)=log2P(x)
可以看出事件概率越低,其信息量越大。对应了它衡量事件“意外程度”的特点:事件概率越低,它的发生就越“意外”,越不确定;相对地,其信息量也就越大。

1.2 熵

熵(Entropy) 是一个用于衡量系统不确定性的指标。熵越大,系统越不确定、越混乱。

熵的概念最初起源于热力学,随后被推广至各领域。对于决策树构建,我们采用信息论中熵的定义(也就是信息熵):所有可能事件的信息量的期望值。

即假设一个事件集合 x = { x 1 , x 2 , . . . , x n } x=\{x_1, x_2, ..., x_n\} x={x1,x2,...,xn},其熵的定义为:
E ( x ) = ∑ i = 1 n P ( x i ) ⋅ I ( x i ) E(x) = \sum^n_{i=1}P(x_i)·I(x_i) E(x)=i=1nP(xi)I(xi)
将信息量的定义公式代入,则可得:
E ( x ) = − ∑ i = 1 n P ( x i ) ⋅ l o g 2 P ( x i ) E(x)=-\sum^n_{i=1}P(x_i)·log_2P(x_i) E(x)=i=1nP(xi)log2P(xi)

2. ID3算法

2.1 信息增益

信息增益(Information Gain) 是一种衡量某个特征在对数据集进行划分时带来的信息量减少的指标。它常用于决策树算法中来选择分裂点,以便将数据集划分为更加纯净的子集。后文介绍的ID3算法就是一种利用信息增益来选取划分点的算法。

它的定义为:使用某个特征来划分数据集后,目标变量的不确定性(即信息熵)减少的程度。

设有一数据集 D D D,现要根据某一特征 X X X进行划分,则信息增益的计算方式如下:

  1. 计算原始数据集的熵 E ( D ) E(D) E(D)
  2. 根据数据集样本中特征值 X X X的不同,将原始数据集 D D D划分为不同的子集 D 1 , D 2 , . . . , D n D_1, D_2, ..., D_n D1,D2,...,Dn(明显地, n n n为特征值 X X X的取值个数)。
  3. 计算划分后数据集的加权平均熵,即所有子集的熵根据子集样本数进行加权平均得到的结果,记为 E ( D ∣ X ) E(D|X) E(DX)
    E ( D ∣ X ) = ∑ i = 1 n ∣ D i ∣ ∣ D ∣ E ( D i ) E(D|X)=\sum^n_{i=1}\frac{|D_i|}{|D|}E(D_i) E(DX)=i=1nDDiE(Di)
  4. 可得到按 X X X划分之后的信息增益,记为 G a i n ( D , X ) Gain(D, X) Gain(D,X)
    G a i n ( D , X ) = E ( D ) − E ( D ∣ X ) Gain(D, X)=E(D)-E(D|X) Gain(D,X)=E(D)E(DX)

2.2 ID3算法简述

ID3算法 即以信息熵和信息增益为核心构建决策树的算法。它计算每个属性的信息增益,并选取具有最高增益的属性作为给定集合的测试属性。对被选取的测试属性创建一个节点,并以该节点的属性标记,对该属性的每个值创建一个分支据此划分样本。

明显地,ID3算法是一种贪心决策算法。它是所有决策树算法中最简单易行的一种。但它同时还有易过拟合、无法处理连续型数据、易产生无效分裂等缺点。由此,出于改进ID3算法的目的,后续在其基础上还衍生出了C4.5算法等决策树算法。

2.3 信息增益与ID3算法的缺陷

在实际运用中,用户会很容易发现ID3算法具有易过拟合的缺陷。这很大一部分都来源于它的核心标准,也就是信息增益的数学特性。

由上文所提到的信息增益的定义可知:信息增益等于原始信息熵减去划分后的加权平均熵得到的值。假设有一特征,它的取值非常多,这样其每个取值对应的子集数就会变得极小(如极端情况下,每个子集都只包含一个样本),由加权平均熵的定义易得:当一个特征的取值越多、每个取值对应的子集数越少时,加权平均熵的值也就越小,同时信息增益也就越大。 当特征的取值逐渐增多,对应的子集数逐渐减小,直至趋近于0时,加权平均熵的值也就趋近于0,同时信息增益也趋近于最大值。也就是说,一个特征的取值越多,其信息增益也就越大,无论其实际是否重要。

这个特性导致了ID3算法在进行选取时,容易偏向取值多的特征,如物品的编号,人的身份证号或电话号码等。这些特征明显并不是重要的分类特征,但是因其信息增益往往极大,ID3算法往往会偏向这些无关紧要的特征,导致模型过拟合,缺乏泛化能力,易被噪声影响生成无效分支。

3. C4.5算法

3.1 信息增益率

为修正信息增益带来的偏向性,C4.5算法引入了信息增益率(Information Gain Ratio) 作为标准。某个特征的信息增益率被定义为其信息增益与其固有信息(Intrinsic Information)的比值

其中,固有信息 是衡量一个特征本身混乱程度的指标,可以看作一个特征集合本身的熵值。即设一个数据集 D D D,一样本特征 X X X X X X的取值范围为 { x 1 , x 2 , . . . , x n } \{x_1, x_2, ..., x_n\} {x1,x2,...,xn},则对于数据集 D D D X X X的固有信息为:
I n t I ( D , X ) = − ∑ i = 1 n ∣ D i ∣ ∣ D ∣ ⋅ l o g 2 ∣ D i ∣ ∣ D ∣ IntI(D,X) = -\sum^n_{i=1}\frac{|D_i|}{|D|}·log_2\frac{|D_i|}{|D|} IntI(D,X)=i=1nDDilog2DDi
其中 ∣ D i ∣ |D_i| Di为按特征 X X X划分后,其取值为 x i x_i xi的子集数量。

同时可得对于数据集 D D D X X X的信息增益率为:
G a i n R a t i o ( D , X ) = G a i n ( D , X ) I n t I ( D , X ) GainRatio(D,X) = \frac{Gain(D,X)}{IntI(D,X)} GainRatio(D,X)=IntI(D,X)Gain(D,X)

3.2 C4.5算法简述

C4.5算法 在ID3算法的基础上,引入信息增益率作为新的划分标准。对于一个特征,其可能取值越多,其混乱程度越高,相应的,其固有信息值也就越大,同时其信息增益率也就越小。

可以看出,信息增益率对多取值的样本起到了一个“惩罚”的作用。通过固有信息来抑制信息增益偏多取值的特点对决策结果的影响,使得C4.5算法对特征有效性的评估更加公平,其构建的决策树模型发生过拟合的概率也大大降低。

4. CART算法

4.1 基尼指数

基尼指数(Gini Index),也称基尼不纯度(Gini Impurity),它是衡量当前样本类别分布是否均匀的指标。设一数据集 D D D,其基尼指数为:
G i n i ( D ) = 1 − ∑ i = 1 n p i 2 Gini(D)=1-\sum^n_{i=1}p_i^2 Gini(D)=1i=1npi2
其中 n n n是数据集 D D D的标签类别总数, p i p_i pi是第 i i i类标签样本占总样本的比例。

基尼指数值越小,代表样本分布越均匀。

4.2 基尼增益

基尼增益(Gini Gain) 是一个衡量当前样本混乱度的指标,本质是样本加权基尼指数的总和。即对于数据集 D D D,其按照某特征 X X X划分后产生的基尼增益为:
G i n i G a i n ( D , X ) = ∑ i = 1 n ∣ D i ∣ ∣ D ∣ ⋅ G i n i ( D i ) GiniGain(D,X) = \sum^n_{i=1}\frac{|D_i|}{|D|}·Gini(D_i) GiniGain(D,X)=i=1nDDiGini(Di)
其中 D i D_i Di为按照 X X X划分后产生的第 i i i个子集。

基尼增益越大,划分后的数据集越混乱,划分收益就越低。相反地,基尼增益越小,划分后数据集越纯净,划分收益越高。

4.3 CART算法简述

CART算法 的核心思想是利用基尼增益不断作二分决策,不断二分特征空间,直至结果纯净为止。

对于每个决策结点,CART算法会遍历所有可能的二元分割点,分别计算其基尼增益,并选取基尼增益最小者作为本次二元分割点,将样本分割成两部分。这一过程无论特征是否是二值还是多值,是离散型还是连续型:对于多值离散型特征,CART算法会遍历所有的分割可能,并选取基尼增益最小的结果作为最终结果,对样本进行分割(如西瓜分类问题,特征“纹理”有“清晰”、“稍糊”和“模糊”三种可能的取值。CART算法会不断尝试将该特征二分,如分割成 { { “清晰” } , { “稍糊” , “模糊” } } \{\{“清晰”\}, \{“稍糊”, “模糊”\}\} {{清晰},{稍糊,模糊}}的形式,或 { { “清晰” , “稍糊” } , { “模糊 " } } \{\{“清晰”, “稍糊”\},\{“模糊"\}\} {{清晰,稍糊},{模糊"}}的形式,选取其中基尼增益最小的结果作为最终结果);而对于连续性特征,则先将结果进行排序,然后遍历所有的相邻值中间点来选择划分阈值。若分割出的子集仍不纯净,则继续以相同的方式对其二分,直至结果全部纯净。过程中未完全二分的特征可能进一步参与后续的分割,即CART算法支持特征复用。

因每一次决策都是二分决策,故CART算法构建的决策树只可能是二叉树。这一特点使其计算效率和建树效率变得极高。

CART算法也因此比ID3算法和C4.5算法更适合回归预测任务。但在进行回归预测时,分割标准就不再是基尼指数或基尼增益,而是数据的平方误差或均方误差。

5. 决策树剪枝

无论使用什么算法,决策树都有可能会出现过拟合、过度分裂、树高过高等现象,导致决策树的决策效率降低,甚至发生偏差和错误。在这种前提下,决策树的构建就需要人为设置标准进行限制和干预,也就是决策树剪枝(Pruning)

5.1 预剪枝

预剪枝(Pre-pruning) 是在决策树构建过程中,通过设置​​早期停止条件​​提前终止树的生长,防止模型因过度拟合训练数据而失去泛化能力。其核心思想是“边构建边评估”,仅在分裂能显著提升泛化性能时继续分支。

其​​常见方法有:​​

  • 设置阈值:信息增益或基尼指数阈值​​若当前节点的分裂指标(如信息增益、基尼指数下降值)低于预设阈值,则停止分裂。

  • ​​样本数量限制​​:当节点样本数小于设定值(如10)时,不再分裂。

  • 树深度限制​:​设置最大深度(如3层),防止树过深。

  • ​​验证集性能监控​​:若分裂后验证集精度未提升,则停止生长。

优点:

  • 计算效率高,适合大规模数据。
  • 显著减少训练和推理时间。

缺点:

  • 具有欠拟合风险​​,可能因过早停止分裂而忽略潜在重要模式。
  • 依赖参数调优​​,阈值设置需经验或交叉验证。

5.2 后剪枝

后剪枝(Post-pruning) 在决策树完全生成后,通过​​自底向上合并冗余子树​​来简化模型。其核心是“先充分学习,再优化结构”,利用验证集或统计指标评估剪枝后的性能提升。

​​其常见方法有:

  • ​​代价复杂度剪枝(CCP) ​​
    引入复杂度惩罚系数α,平衡误差与树规模:
    C α ( T ) = C ( T ) + α ∣ T ∣ C_α(T)=C(T)+α|T| Cα(T)=C(T)+αT
    其中 C ( T ) C(T) C(T) 为误差, ∣ T ∣ |T| T为叶节点数。

  • 错误率降低剪枝(REP) ​​
    通过独立验证集评估剪枝前后的错误率,仅保留提升泛化的分支。

  • 悲观剪枝(PEP) ​​
    使用统计学修正因子(如0.5)调整误差估计,避免因样本偏差误剪。

​​优点​​:

  • 保留更多有效分支,泛化能力通常优于预剪枝。
  • 无需依赖预设参数,通过数据驱动优化。

​​缺点​​:

  • 计算开销大​​:需先构建完整树,再多次回溯评估。
  • 实现复杂​​:需处理剪枝路径选择和交叉验证。

对于前文所述的三种算法,预剪枝适配所有三种算法,而后剪枝不适配ID3算法,但可以适配C4.5和CART算法。

三、实际应用

1. 问题引入

有一家银行为用户办理贷款服务。对于不同的用户,银行可以选择是否要为其贷款。现给定部分用户的信息,包括他们年龄段、工作情况、信贷情况等,要求构建决策树帮助银行判断是否要为其办理贷款。

数据集如下:
数据集
测试集如下:
测试集
实际使用时已将数据数值化,映射到对应整数上。

2. 基础代码实现

读取数据:

import numpy as np

def load_data(filename):
    data = np.loadtxt(filename, delimiter=',', dtype=int)
    X = data[:, :4]
    y = data[:, 4]
    return X, y

熵计算函数:

import numpy as np

def entropy(y):
    _, counts = np.unique(y, return_counts=True)
    probs = counts / len(y)
    return -np.sum(probs * np.log2(probs + 1e-10))

条件熵计算函数:

import numpy as np

def conditional_entropy(X_col, y):
    values, counts = np.unique(X_col, return_counts=True)
    cond_ent = 0.0
    for v, c in zip(values, counts):
        mask = (X_col == v)
        cond_ent += (c / len(X_col)) * entropy(y[mask])
    return cond_ent

决策树结点定义:

class TreeNode:

    def __init__(self, feature_idx=None, label=None):
        self.feature_idx = feature_idx
        self.children = {}
        self.label = label

3. 构建决策树算法实现

信息增益计算函数:

import numpy as np

def id3_info_gain(X, y, feature_idx):
    base_ent = entropy(y)
    cond_ent = conditional_entropy(X[:, feature_idx], y)
    return base_ent - cond_ent

信息增益率计算函数:

import numpy as np

def c45_split_info(X_col):
    _, counts = np.unique(X_col, return_counts=True)
    probs = counts / len(X_col)
    return -np.sum(probs * np.log2(probs + 1e-10))


def c45_info_gain_ratio(X, y, feature_idx):
    gain = id3_info_gain(X, y, feature_idx)
    split_info = c45_split_info(X[:, feature_idx])
    return gain / split_info if split_info != 0 else 0

递归决策树构建函数:

import numpy as np

def build_tree(X, y, features, algorithm='id3'):
    if len(np.unique(y)) == 1:
        return TreeNode(label=y[0])

    if len(features) == 0:
        majority = np.argmax(np.bincount(y))
        return TreeNode(label=majority)

    gains = []
    for idx in features:
        if algorithm == 'id3':
            gains.append(id3_info_gain(X, y, idx))
        elif algorithm == 'c45':
            gains.append(c45_info_gain_ratio(X, y, idx))
    best_idx = features[np.argmax(gains)]

    node = TreeNode(feature_idx=best_idx)
    remaining_features = [f for f in features if f != best_idx]

    values = np.unique(X[:, best_idx])
    for val in values:
        mask = (X[:, best_idx] == val)
        if np.sum(mask) == 0:
            majority = np.argmax(np.bincount(y))
            node.children[val] = TreeNode(label=majority)
        else:
            node.children[val] = build_tree(X[mask], y[mask], remaining_features, algorithm)

    return node

4. 决策树可视化

from matplotlib import pyplot as plt

def plot_tree(node, feature_names, class_names, x=0.5, y=0.9, dx=0.2, dy=0.1, ax=None):
	plt.rcParams['font.sans-serif'] = ['SimHei']

    if ax is None:
        fig, ax = plt.subplots(figsize=(12, 8))
        ax.set_axis_off()

    if node.label is not None:
        ax.text(x, y, f'Class: {class_names[node.label]}',
                ha='center', va='center',
                bbox=dict(boxstyle="round", fc="lightblue"))
        return
    else: 
        ax.text(x, y, feature_names[node.feature_idx],
                ha='center', va='center',
                bbox=dict(boxstyle="sawtooth", fc="orange"))

    n_children = len(node.children)
    x_offsets = np.linspace(x - dx * (n_children / 2), x + dx * (n_children / 2), n_children)

    for i, (val, child) in enumerate(node.children.items()):
        x_child = x_offsets[i]
        y_child = y - dy

        ax.plot([x, x_child], [y, y_child], 'k-', lw=1)

        ax.text((x + x_child) / 2, (y + y_child) / 2 - 0.02, str(val),
                ha='center', va='top', color='green')

        plot_tree(child, feature_names, class_names,
                  x_child, y_child, dx * 0.8, dy, ax)

5. 运行结果

ID3决策树
C4.5决策树

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值