【决策树深度探索(五)】智慧之眼:信息增益,如何找到最佳决策问题?



文章目录

智慧之眼:信息增益是什么?

在决策树的构建过程中,每当我们到达一个节点,都需要决定接下来根据哪个特征进行划分。一个好的特征划分应该能够最大程度地减少当前数据集的混乱程度,使得划分后的子数据集变得更"纯净"。

信息增益 ,顾名思义,就是特征对数据集带来的信息量增加的程度 ,或者更直白地说,是由于使用了某个特征进行划分而导致数据集混乱程度(熵)的减少量

核心思想

一个特征的信息增益越大,说明它在划分数据集时,使得数据的纯度提升得越多,混乱程度降低得越显著。因此,决策树在每个节点都会选择信息增益最大的特征来进行划分。

数学公式:

假设数据集 D D D 的熵为 H ( D ) H(D) H(D)。

用特征 A A A 划分数据集 D D D 后,根据特征 A A A 的不同取值 v v v,数据集被分成若干个子集 D v D_v Dv。

那么,特征 A A A 带来的信息增益 G a i n ( D , A ) Gain(D, A) Gain(D,A) 定义为:

G a i n ( D , A ) = H ( D ) − ∑ v ∈ V a l u e s ( A ) ∣ D v ∣ ∣ D ∣ H ( D v ) Gain(D, A) = H(D) - \sum_{v \in Values(A)} \frac{|D_v|}{|D|} H(D_v) Gain(D,A)=H(D)−v∈Values(A)∑∣D∣∣Dv∣H(Dv)

这里:

  • H ( D ) H(D) H(D):是原始数据集 D D D 的熵(未划分前的混乱程度)。
  • V a l u e s ( A ) Values(A) Values(A):是特征 A A A 所有可能的取值集合。
  • D v D_v Dv:是数据集 D D D 中,特征 A A A 取值为 v v v 的样本组成的子集。
  • ∣ D v ∣ |D_v| ∣Dv∣:子集 D v D_v Dv 中样本的数量。
  • ∣ D ∣ |D| ∣D∣:原始数据集 D D D 中样本的总数量。
  • H ( D v ) H(D_v) H(Dv):是子集 D v D_v Dv 的熵(划分后的混乱程度)。
  • ∑ v ∈ V a l u e s ( A ) ∣ D v ∣ ∣ D ∣ H ( D v ) \sum_{v \in Values(A)} \frac{|D_v|}{|D|} H(D_v) ∑v∈Values(A)∣D∣∣Dv∣H(Dv):这部分是划分后所有子集的加权平均熵 。权重 ∣ D v ∣ ∣ D ∣ \frac{|D_v|}{|D|} ∣D∣∣Dv∣ 是每个子集在原始数据集中的比例。

直观理解

信息增益 = (划分前的熵)-(划分后所有子集的加权平均熵)。

这个差值越大,说明这个特征的划分效果越好,它"解决"了更多的不确定性。决策树的目标就是找出那个能最大化这个差值的特征。

特征选择:算法如何驱动决策?

信息增益是ID3(Iterative Dichotomiser 3)算法的核心驱动力。ID3算法就是通过不断计算每个可用特征的信息增益,选择增益最大的那个来构建树。这个过程使得决策树在每一步都能做出"最优化"的决策,快速而有效地将数据集分类。

举个例子,假设我们要决定"今天是否打网球":

原始数据集的熵可能很高(因为Yes和No都有)。

  • 如果按"天气"划分:

    • 晴天组:熵很低(比如很多No)
    • 阴天组:熵很低(比如很多Yes)
    • 下雨组:熵中等
    • 计算出"天气"的信息增益。
  • 如果按"温度"划分:

    • 热组:熵中等
    • 适中组:熵中等
    • 凉爽组:熵中等
    • 计算出"温度"的信息增益。

比较"天气"和"温度"的信息增益,哪个大就选哪个作为根节点的第一层划分。这个过程会递归地进行,直到叶节点足够纯净。

手撕代码:亲手计算信息增益,找出最佳划分特征!

现在,让我们再次回到我们的"是否打网球"数据集,亲手计算每个特征的信息增益,来找出哪个特征是最佳的根节点划分!

我们将使用上一篇中定义的 calculate_entropy 函数。

python 复制代码
import math
import collections

# 示例数据集:Outlook, Temperature, Humidity, Wind, PlayTennis
# 数据格式:[特征1, 特征2, ..., 目标变量]
dataset = [
    ['Sunny', 'Hot', 'High', 'Weak', 'No'],
    ['Sunny', 'Hot', 'High', 'Strong', 'No'],
    ['Overcast', 'Hot', 'High', 'Weak', 'Yes'],
    ['Rain', 'Mild', 'High', 'Weak', 'Yes'],
    ['Rain', 'Cool', 'Normal', 'Weak', 'Yes'],
    ['Rain', 'Cool', 'Normal', 'Strong', 'No'],
    ['Overcast', 'Cool', 'Normal', 'Strong', 'Yes'],
    ['Sunny', 'Mild', 'High', 'Weak', 'No'],
    ['Sunny', 'Cool', 'Normal', 'Weak', 'Yes'],
    ['Rain', 'Mild', 'Normal', 'Weak', 'Yes'],
    ['Sunny', 'Mild', 'Normal', 'Strong', 'Yes'],
    ['Overcast', 'Mild', 'High', 'Strong', 'Yes'],
    ['Overcast', 'Hot', 'Normal', 'Weak', 'Yes'],
    ['Rain', 'Mild', 'High', 'Strong', 'No']
]

feature_names = ['Outlook', 'Temperature', 'Humidity', 'Wind']
target_index = 4 # 目标变量在数据集中的索引

# 引入上一篇中定义的熵计算函数
def calculate_entropy(data, target_idx):
    """
    计算给定数据集的目标变量的熵。
    参数:
        data: 列表的列表,每个子列表代表一个样本。
        target_idx: 目标变量在每个样本中的索引。
    返回:
        熵值 (float)。
    """
    num_samples = len(data)
    if num_samples == 0:
        return 0.0

    label_counts = collections.Counter(sample[target_idx] for sample in data)
    
    entropy = 0.0
    for label in label_counts:
        prob = label_counts[label] / num_samples
        if prob > 0: # 避免 log(0) 的情况
            entropy -= prob * math.log2(prob)
    return entropy

# 1. 计算信息增益的核心函数
def calculate_information_gain(data, feature_idx, target_idx):
    """
    计算给定特征的信息增益。
    参数:
        data: 数据集。
        feature_idx: 要计算信息增益的特征的索引。
        target_idx: 目标变量的索引。
    返回:
        信息增益 (float)。
    """
    initial_entropy = calculate_entropy(data, target_idx) # 计算划分前的熵
    num_samples = len(data)
    
    # 按照特征值分组
    feature_value_groups = collections.defaultdict(list)
    for sample in data:
        feature_value = sample[feature_idx]
        feature_value_groups[feature_value].append(sample)
    
    weighted_avg_entropy = 0.0
    for value, group_data in feature_value_groups.items():
        prob = len(group_data) / num_samples # 子集在总数据集中的比例
        weighted_avg_entropy += prob * calculate_entropy(group_data, target_idx) # 计算加权熵
        
    return initial_entropy - weighted_avg_entropy # 原始熵减去加权平均熵

# --- 找出最佳划分特征! ---
print("🚀 开始计算每个特征的信息增益,寻找最佳决策问题...")

initial_entropy_dataset = calculate_entropy(dataset, target_index)
print(f"原始数据集的熵 H(D): {initial_entropy_dataset:.4f}\n")

best_gain = -1.0
best_feature_name = None

for i, f_name in enumerate(feature_names):
    gain = calculate_information_gain(dataset, i, target_index)
    print(f"特征 '{f_name}' 的信息增益 Gain(D, {f_name}): {gain:.4f}")
    
    if gain > best_gain:
        best_gain = gain
        best_feature_name = f_name

print(f"\n 最佳划分特征是: '{best_feature_name}',其信息增益为: {best_gain:.4f}")
print(" 决策树会选择这个特征作为根节点(或当前节点)的第一个决策问题!")

# 进一步分析 "Outlook" 特征的划分情况
print("\n深入分析最佳特征 'Outlook' 的划分效果:")
outlook_groups = collections.defaultdict(list)
for sample in dataset:
    outlook_groups[sample[0]].append(sample) # 0 是 Outlook 的索引

for value, group_data in outlook_groups.items():
    group_labels = [s[target_index] for s in group_data]
    group_entropy = calculate_entropy(group_data, target_index)
    print(f"  Outlook = '{value}': 样本数={len(group_data)}, 标签={collections.Counter(group_labels)}, 熵={group_entropy:.4f}")

print("\n你看到了吗?通过 'Outlook' 划分后,数据集的混乱程度(熵)显著降低了!")
print("这就是信息增益的魔力!")
代码解读:
  1. calculate_entropy 函数

    • 这部分代码直接复用了上一篇中实现的熵计算函数,它是信息增益计算的基础。
  2. calculate_information_gain(data, feature_idx, target_idx) 函数

    • 这是本篇的核心。它首先计算了整个数据集(未划分前)的初始熵 initial_entropy
    • 接着,它遍历给定特征 feature_idx 的所有可能取值,将数据集划分为多个子集 (feature_value_groups)。
    • 对于每个子集,它计算其熵,并乘以该子集在总数据集中的比例,得到加权平均熵 weighted_avg_entropy
    • 最后,用 initial_entropy 减去 weighted_avg_entropy,就得到了该特征的信息增益
  3. 找出最佳划分特征

    • 我们遍历了 feature_names 中的每一个特征,调用 calculate_information_gain 计算它们各自的信息增益。
    • 通过比较这些增益值,我们找到了信息增益最大的特征,它就是决策树在第一层(根节点)会选择的"最佳决策问题"!在本例中,通常会是Outlook
  4. 深入分析

    • 代码还进一步展示了,当使用Outlook特征进行划分后,各个子集(如SunnyOvercastRain)的样本组成和熵值。你会发现,Overcast组的熵直接降为0(因为它里面全是'Yes'),而SunnyRain组的熵也显著低于原始数据集的熵,这正是信息增益的直观体现!

通过这个实践,你是不是对信息增益这个决策树的"智慧之眼"有了更深刻、更直观的理解?你不仅掌握了它的数学原理,更亲手计算并验证了它的有效性!

结语与展望

在未来的"深度探索"系列中,我们将继续前进,探索更多高级主题:

  • 信息增益率与Gini不纯度:除了ID3,还有C4.5和CART等算法,它们是如何选择最佳划分特征的?
  • 连续型特征的处理:如何为数值型数据找到最佳的划分点?
  • 决策树的剪枝:如何让树变得更"健壮",避免过拟合?
  • 回归决策树:如何用决策树来预测连续值?

机器学习的道路充满了奇妙的数学和算法之美。保持这份好奇心,我们下篇再见!如果你有任何疑问或想分享你的见解,欢迎在评论区留言哦!


相关推荐
好奇龙猫2 小时前
【大学院-筆記試験練習:线性代数和数据结构(16)】
数据结构·线性代数·决策树
倔强的石头1062 小时前
假设空间与版本空间 —— 机器学习是 “猜规律” 的过程
人工智能·机器学习
劈星斩月2 小时前
机器学习(Machine Learning)系列
深度学习·神经网络·机器学习
智者知已应修善业2 小时前
【输出方形点阵】2024-11-1
c语言·c++·经验分享·笔记·算法
近津薪荼2 小时前
优选算法——双指针专题2(模拟)
c++·学习·算法
乌萨奇也要立志学C++2 小时前
【洛谷】DFS 新手必学的4 道DFS经典题 手把手教你剪枝与回溯
算法·深度优先
翱翔的苍鹰2 小时前
多Agent智能体系统设计思路
java·python·深度学习·神经网络·机器学习·tensorflow
sali-tec2 小时前
C# 基于OpenCv的视觉工作流-章15-多边形逼近
图像处理·人工智能·opencv·算法·计算机视觉
TonyLee0172 小时前
LLM与传统机器学习
人工智能·机器学习