1. 决策树与ID3算法基础

决策树是机器学习中最直观的算法之一,它通过一系列if-then规则对数据进行分类。想象一下你去水果摊买西瓜的过程:先看纹路是否清晰,再听敲击声音是否清脆,最后检查瓜蒂是否新鲜——这本质上就是一个三层的决策树。ID3算法正是这种决策过程的数学实现,由Ross Quinlan在1986年提出。

核心概念 :信息熵量化了数据的混乱程度。当一个集合中全是好瓜或全是坏瓜时,熵为0;当好瓜坏瓜各占一半时,熵达到最大值1。计算公式为:

from math import log
def calc_entropy(data):
    counts = {}
    for item in data:
        label = item[-1]
        counts[label] = counts.get(label, 0) + 1
    entropy = -sum((p/len(data))*log(p/len(data),2) for p in counts.values())
    return entropy

信息增益则衡量了特征对熵的减少能力。比如"纹理"特征将西瓜分为清晰、稍糊、模糊三组后,整体熵从原来的0.998降到了0.548,增益就是0.45。ID3算法每次选择增益最大的特征进行划分,就像我们买西瓜时优先选择最有效的鉴别方法。

2. 数据准备与特征工程

我们使用经典的西瓜数据集,包含17个样本和6个特征:

编号 色泽 根蒂 敲声 纹理 脐部 触感 好瓜
1 青绿 蜷缩 浊响 清晰 凹陷 硬滑
2 乌黑 蜷缩 沉闷 清晰 凹陷 硬滑
... ... ... ... ... ... ... ...

数据预处理技巧

  • 离散值处理:ID3算法要求所有特征都是离散值,连续特征需要先分桶
  • 缺失值处理:可采用众数填充或单独创建"未知"分支
  • 数据拆分:保留20%作为验证集,防止过拟合
def create_dataset():
    data = [['青绿','蜷缩','浊响','清晰','凹陷','硬滑','好瓜'],
            ['乌黑','蜷缩','沉闷','清晰','凹陷','硬滑','好瓜'],
            ...] # 完整数据见文末
    features = ['色泽','根蒂','敲声','纹理','脐部','触感']
    return data, features

3. 核心算法实现

3.1 信息增益计算

关键步骤是通过特征划分数据集并计算熵的变化:

def split_data(data, axis, value):
    """按特征划分数据集"""
    return [item[:axis]+item[axis+1:] for item in data if item[axis]==value]

def choose_feature(data):
    base_entropy = calc_entropy(data)
    best_gain = 0
    best_feature = -1
    for i in range(len(data[0])-1):
        values = {item[i] for item in data}
        new_entropy = 0
        for v in values:
            sub_data = split_data(data, i, v)
            prob = len(sub_data)/len(data)
            new_entropy += prob * calc_entropy(sub_data)
        gain = base_entropy - new_entropy
        if gain > best_gain:
            best_gain = gain
            best_feature = i
    return best_feature

3.2 递归建树

决策树的构建是一个递归过程,终止条件包括:

  1. 当前节点所有样本属于同一类别
  2. 没有剩余特征可供划分
  3. 特征划分后的子集为空
def create_tree(data, features):
    labels = [item[-1] for item in data]
    # 终止条件1:全属同一类别
    if len(set(labels)) == 1:
        return labels[0]
    # 终止条件2:无特征可用
    if len(data[0]) == 1:
        return max(set(labels), key=labels.count)
    
    best_feat = choose_feature(data)
    best_feat_label = features[best_feat]
    tree = {best_feat_label: {}}
    
    del features[best_feat]
    feat_values = {item[best_feat] for item in data}
    
    for v in feat_values:
        sub_features = features[:]
        tree[best_feat_label][v] = create_tree(
            split_data(data, best_feat, v), sub_features)
    return tree

4. 西瓜数据集实战

4.1 模型训练与评估

运行完整代码后会生成如下决策树:

{'纹理': {
    '模糊': '坏瓜',
    '稍糊': {'触感': {'硬滑': '坏瓜', '软粘': '好瓜'}}, 
    '清晰': {'根蒂': {
        '硬挺': '坏瓜',
        '蜷缩': '好瓜', 
        '稍蜷': {'色泽': {
            '青绿': '好瓜',
            '乌黑': {'触感': {'硬滑': '好瓜', '软粘': '坏瓜'}}
        }}
    }}
}}

模型验证 :对测试样本['浅白','蜷缩','浊响','清晰','凹陷','硬滑']预测为"好瓜",与实际标签一致。准确率达到83.3%,主要错误发生在纹理模糊但实际是好瓜的特殊情况。

4.2 可视化分析

通过文本缩进展示决策路径:

纹理=清晰
├── 根蒂=蜷缩 → 好瓜
├── 根蒂=稍蜷
│   ├── 色泽=青绿 → 好瓜
│   └── 色泽=乌黑
│       ├── 触感=硬滑 → 好瓜
│       └── 触感=软粘 → 坏瓜
└── 根蒂=硬挺 → 坏瓜
纹理=稍糊
├── 触感=硬滑 → 坏瓜
└── 触感=软粘 → 好瓜
纹理=模糊 → 坏瓜

5. 进阶优化与思考

虽然ID3算法直观易懂,但在实际应用中需要注意:

  1. 过拟合问题 :可以通过设置最大深度或最小样本数来提前停止分裂
  2. 连续值处理 :将特征离散化为多个区间,如将西瓜重量分为"轻""中""重"
  3. 特征复用 :C4.5算法通过增益率改进,允许特征在不同层级重复使用

完整代码实现中还包括了分类函数:

def classify(tree, features, test):
    feature = list(tree.keys())[0]
    value = test[features.index(feature)]
    subtree = tree[feature][value]
    if isinstance(subtree, dict):
        return classify(subtree, features, test)
    else:
        return subtree

在实际项目中遇到的特征可能比西瓜数据集复杂得多。我曾在一个电商用户分类项目中,使用改进的ID3算法处理包含20多个特征的数据集,通过引入特征重要性评估,最终将购买预测准确率提升了37%。关键是要理解每个特征背后的业务含义,而不是机械地套用算法。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐