ID3算法超详细讲解(附Python完整实现+可视化)
ID3算法(Iterative Dichotomiser 3)是机器学习中经典的决策树分类算法,由Ross Quinlan在1986年提出,是决策树算法的入门基础。它基于信息论中的信息增益选择最优划分特征,通过递归的方式构建决策树,核心逻辑简单直观,可解释性强,是本科生和研究生学习决策树、信息论在机器学习中应用的核心内容,也是后续C4.5、CART等决策树算法的基础。
本文将从通俗原理、核心理论推导、完整算法流程、Python实战实现、优缺点与适用场景五个维度展开,内容通俗易懂,公式做详细拆解,附带可直接运行的代码和可视化效果,适配本科课程学习和研究生入门实践。
一、什么是ID3算法?(通俗理解)
ID3算法的核心是“不断挑选最关键的特征,用特征层层划分数据,直到得到明确的分类结果”,就像我们日常通过线索做判断的过程,用一个侦探断案的例子就能轻松理解:
侦探要判断案件是「事故」还是「犯罪」,有4个线索:天气、地点、目击者数量、时间段。
这个过程就是ID3算法的核心逻辑,最终会形成一棵决策树:
- 非叶节点:代表划分特征(如目击者数量、时间段);
- 分支:代表特征的取值(如0、≥3、夜晚、白天);
- 叶节点:代表最终的分类结果(如事故、犯罪)。
简单来说,ID3算法就是一套基于“信息增益”选择最优特征,递归构建树形分类规则的方法,把复杂的分类问题拆解为层层递进的简单判断,最终从数据中提炼出清晰的分类逻辑。
二、ID3的核心理论基础(信息论)
ID3算法的特征选择、节点划分完全基于信息论的两个核心概念:信息熵和信息增益。用这两个指标量化“数据的混乱程度”和“特征的分类能力”,让计算机能客观选择最优划分特征,而非人工主观判断。
2.1 信息熵(Entropy):衡量数据的“混乱程度”
信息熵是信息论的核心概念,用于衡量一个数据集的不确定性/混乱程度:
- 数据集的样本类别越混乱(各类别样本占比接近),熵越大;
- 数据集的样本完全纯净(所有样本属于同一类别),熵为0;
- 熵的取值越大,说明数据的分类难度越高。
信息熵的计算公式
假设数据集SSS包含nnn个样本,共有ccc个不同的类别C1,C2,…,CcC_1,C_2,…,C_cC1,C2,…,Cc,第iii类样本的数量为∣Si∣|S_i|∣Si∣,总样本数为∣S∣|S|∣S∣,则第iii类样本的概率pi=∣Si∣∣S∣p_i = \\frac{|S_i|}{|S|}pi=∣S∣∣Si∣,数据集SSS的信息熵为:
H(S)=−∑i=1cpilog2piH(S) = -\\sum_{i=1}^{c} p_i \\log_2 p_iH(S)=−i=1∑cpilog2pi
其中:
- H(S)H(S)H(S):数据集SSS的信息熵,单位为比特(bit);
- log2\\log_2log2:以2为底的对数,是信息论中熵的标准计算方式;
- 若pi=0p_i=0pi=0,则pilog2pi=0p_i\\log_2 p_i=0pilog2pi=0(避免对数无意义)。
关键结论
简单举例
二分类数据集SSS有10个样本,6个“事故”、4个“犯罪”,其信息熵为:
H(S)=−(610log2610+410log2410)≈0.97H(S) = -(\\frac{6}{10}\\log_2\\frac{6}{10} + \\frac{4}{10}\\log_2\\frac{4}{10}) \\approx 0.97H(S)=−(106log2106+104log2104)≈0.97
若数据集是5个“事故”、5个“犯罪”,则熵为1,混乱程度更高,分类难度更大。
2.2 条件熵(Conditional Entropy):按特征划分后的平均混乱程度
条件熵表示在给定某一特征AAA的条件下,数据集SSS的平均不确定性,记为H(S∣A)H(S|A)H(S∣A)。简单来说,就是用特征AAA划分数据后,所有子数据集的熵的加权平均值,权重为每个子数据集的样本数占原数据集的比例。
条件熵的计算公式
假设特征AAA有vvv个不同的取值a1,a2,…,ava_1,a_2,…,a_va1,a2,…,av,用特征AAA将数据集SSS划分为vvv个子集S1,S2,…,SvS_1,S_2,…,S_vS1,S2,…,Sv(SjS_jSj表示特征AAA取aja_jaj的样本集合),则条件熵为:
H(S∣A)=∑j=1v∣Sj∣∣S∣H(Sj)H(S|A) = \\sum_{j=1}^{v} \\frac{|S_j|}{|S|} H(S_j)H(S∣A)=j=1∑v∣S∣∣Sj∣H(Sj)
其中:
- ∣Sj∣∣S∣\\frac{|S_j|}{|S|}∣S∣∣Sj∣:子集SjS_jSj的样本数占原数据集SSS的比例(加权系数);
- H(Sj)H(S_j)H(Sj):子集SjS_jSj的信息熵。
物理意义
条件熵H(S∣A)H(S|A)H(S∣A)越小,说明用特征AAA划分数据后,各个子数据集的混乱程度越低,特征AAA的分类能力越强。
2.3 信息增益(Information Gain):特征带来的“混乱度减少量”
信息增益是ID3算法选择最优特征的核心指标,衡量的是引入特征AAA后,数据集SSS的不确定性减少的程度,记为G(S,A)G(S,A)G(S,A)。
信息增益的计算公式
信息增益 = 原数据集的熵 – 按特征AAA划分后的条件熵,即:
G(S,A)=H(S)−H(S∣A)G(S,A) = H(S) – H(S|A)G(S,A)=H(S)−H(S∣A)
关键结论
简单举例
原数据集SSS的熵H(S)=0.97H(S)=0.97H(S)=0.97,用特征“目击者数量”划分后的条件熵H(S∣A)=0.23H(S|A)=0.23H(S∣A)=0.23,则该特征的信息增益:
G(S,A)=0.97−0.23=0.74G(S,A) = 0.97 – 0.23 = 0.74G(S,A)=0.97−0.23=0.74
若用特征“天气”划分后的条件熵H(S∣A)=0.85H(S|A)=0.85H(S∣A)=0.85,则信息增益为0.12,显然“目击者数量”的分类能力更强。
三、ID3算法的完整流程
ID3算法的本质是自顶向下、递归构建决策树的过程,从根节点(整个训练集)开始,每次选择信息增益最大的特征划分数据,生成子节点,再对每个子节点重复该过程,直到满足停止条件,最终形成一棵完整的决策树。
3.1 算法输入与输出
- 输入:训练数据集SSS、特征集合AAA、样本的类别标签集合CCC;
- 输出:一棵用于分类的决策树。
3.2 详细执行步骤
Step 1:计算根节点的信息熵
根据样本的类别标签,计算整个训练数据集SSS的信息熵H(S)H(S)H(S),作为初始的混乱程度。
Step 2:计算每个特征的信息增益
对特征集合AAA中的每一个特征AiA_iAi,依次执行:
Step 3:选择最优划分特征
Step 4:构建当前划分节点
Step 5:递归构建子树
对每个分支对应的子数据集SjS_jSj,递归执行Step 1-Step 4,注意:已使用的特征会从特征集合AAA中移除,不再参与后续划分。
Step 6:触发停止条件,生成叶节点
当某个子数据集满足以下任一条件时,停止递归,将该节点设为叶节点,叶节点的分类结果为该子集中占比最大的类别:
Step 7:输出决策树
当所有节点都完成构建(非叶节点/叶节点),得到一棵完整的决策树,可用于新样本的分类预测。
3.3 决策树的预测逻辑
对于新的待分类样本,从决策树的根节点开始,根据样本的特征取值,沿着对应的分支向下遍历,直到到达叶节点,叶节点的类别即为该样本的预测分类结果。
例如:新样本的“目击者数量=0”,从根节点沿该分支直接到达叶节点,预测结果为“事故”;新样本的“目击者数量=2,时间段=夜晚”,沿分支逐层遍历,最终到达叶节点“犯罪”。
四、ID3算法的Python完整实现(附可视化)
本次实战基于经典的网球运动预测数据集(判断不同天气条件下是否适合打网球),使用Python+sklearn实现ID3算法,包含数据构建、预处理、模型训练、信息增益可视化、决策树可视化、新样本预测全流程,代码可直接复制运行,注释详细,适配Python3.7+、sklearn1.0+。
4.1 实战目标
4.2 完整代码与注释
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 决策树模型(criterion='entropy'即为ID3算法)
from sklearn.tree import DecisionTreeClassifier, plot_tree
# 标签编码器:将离散特征的字符串值转为数值
from sklearn.preprocessing import LabelEncoder
# 模型准确率评估
from sklearn.metrics import accuracy_score
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# ===================== 1. 构建数据集(网球运动预测) =====================
# 特征:Weather(天气)、Temperature(温度)、Humidity(湿度)、Windy(风力)
# 目标值:PlayTennis(是否打网球,Yes/No)
data = {
'Weather': ['Sunny', 'Sunny', 'Overcast', 'Rainy', 'Rainy', 'Rainy',
'Overcast', 'Sunny', 'Sunny', 'Rainy', 'Sunny', 'Overcast',
'Overcast', 'Rainy'],
'Temperature': ['Hot', 'Hot', 'Hot', 'Mild', 'Cool', 'Cool', 'Cool',
'Mild', 'Cool', 'Mild', 'Mild', 'Mild', 'Hot', 'Mild'],
'Humidity': ['High', 'High', 'High', 'High', 'Normal', 'Normal', 'Normal',
'High', 'Normal', 'Normal', 'Normal', 'High', 'Normal', 'High'],
'Windy': ['False', 'True', 'False', 'False', 'False', 'True', 'True',
'False', 'False', 'False', 'True', 'True', 'False', 'True'],
'PlayTennis': ['No', 'No', 'Yes', 'Yes', 'Yes', 'No', 'Yes', 'No',
'Yes', 'Yes', 'Yes', 'Yes', 'Yes', 'No']
}
df = pd.DataFrame(data)
print("原始数据集:")
print(df)
# ===================== 2. 数据预处理:离散特征数值化 =====================
# ID3仅支持离散特征,需将字符串类型的特征/目标值转为数值(0,1,2…)
le = LabelEncoder()
for column in df.columns:
df[column] = le.fit_transform(df[column])
# 分离特征矩阵X和目标值向量y
X = df[['Weather', 'Temperature', 'Humidity', 'Windy']]
y = df['PlayTennis']
feature_names = ['天气', '温度', '湿度', '风力'] # 特征名(可视化用)
class_names = ['不打', '打'] # 类别名(可视化用)
print("\\n数值化后的特征:")
print(X.head())
# ===================== 3. 构建ID3决策树模型 =====================
# sklearn中DecisionTreeClassifier设置criterion='entropy'即为ID3算法
# max_depth=3:限制树的最大深度,防止过拟合
id3_model = DecisionTreeClassifier(
criterion='entropy', # 熵准则,对应ID3的信息增益
max_depth=3, # 预剪枝:最大深度3
random_state=42 # 随机种子,保证结果可复现
)
# 训练模型
id3_model.fit(X, y)
# ===================== 4. 计算并可视化每个特征的信息增益 =====================
# 步骤1:计算原数据集的总熵H(S)
p = np.bincount(y) / len(y) # 计算每个类别的概率
H_S = –np.sum(p * np.log2(p + 1e-9)) # +1e-9避免log2(0)
# 步骤2:遍历每个特征,计算信息增益
info_gain = []
for i in range(X.shape[1]):
feature_vals = X.iloc[:, i]
unique_vals = np.unique(feature_vals)
H_S_A = 0 # 初始化条件熵
# 计算每个特征取值对应的子集的熵,加权得到条件熵
for val in unique_vals:
subset_y = y[feature_vals == val]
p_sub = np.bincount(subset_y) / len(subset_y)
H_sub = –np.sum(p_sub * np.log2(p_sub + 1e-9))
H_S_A += (len(subset_y) / len(y)) * H_sub
# 计算信息增益
ig = H_S – H_S_A
info_gain.append(ig)
# 可视化信息增益(柱状图)
plt.figure(figsize=(8, 6))
plt.bar(feature_names, info_gain, color=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4'], alpha=0.8)
plt.title('各特征的信息增益(ID3特征选择依据)', fontsize=16)
plt.xlabel('特征名称', fontsize=14)
plt.ylabel('信息增益值', fontsize=14)
plt.xticks(fontsize=12)
plt.yticks(fontsize=12)
plt.grid(axis='y', linestyle='–', alpha=0.7)
plt.tight_layout()
plt.show()
# ===================== 5. 可视化ID3决策树结构 =====================
plt.figure(figsize=(12, 8))
plot_tree(
id3_model,
feature_names=feature_names,
class_names=class_names,
filled=True, # 节点填充颜色
rounded=True, # 节点圆角
fontsize=10, # 字体大小
precision=1 # 数值精度
)
plt.title('ID3决策树结构(是否适合打网球)', fontsize=16)
plt.tight_layout()
plt.show()
# ===================== 6. 新样本预测与模型优化 =====================
# 构建测试集:3个新样本的天气特征
test_data = pd.DataFrame({
'Weather': ['Sunny', 'Rainy', 'Overcast'],
'Temperature': ['Hot', 'Mild', 'Cool'],
'Humidity': ['Normal', 'High', 'High'],
'Windy': ['False', 'True', 'False']
})
print("\\n测试集(新样本):")
print(test_data)
# 测试集预处理:特征数值化(与训练集使用相同的编码器逻辑)
for column in test_data.columns:
le_test = LabelEncoder()
# 用训练集的特征值拟合编码器,避免数据泄露
le_test.fit(data[column])
test_data[column] = le_test.transform(test_data[column])
# 模型预测
y_pred = id3_model.predict(test_data)
# 将数值预测结果转回字符串(0→No,1→Yes)
y_pred_str = ['No' if x == 0 else 'Yes' for x in y_pred]
print("\\n新样本预测结果(是否打网球):", y_pred_str)
# 模型优化:减小树的深度(max_depth=2),进一步防止过拟合
id3_model_optimized = DecisionTreeClassifier(
criterion='entropy',
max_depth=2,
random_state=42
)
id3_model_optimized.fit(X, y)
# 优化模型预测
y_pred_opt = id3_model_optimized.predict(test_data)
y_pred_opt_str = ['No' if x == 0 else 'Yes' for x in y_pred_opt]
print("优化后模型预测结果:", y_pred_opt_str)
# 计算训练集准确率(验证模型拟合效果)
y_train_pred = id3_model.predict(X)
accuracy = accuracy_score(y, y_train_pred)
print(f"\\nID3模型训练集准确率:{accuracy * 100:.2f}%")
4.3 运行结果说明
4.4 核心参数说明
sklearn中DecisionTreeClassifier的核心参数(ID3算法相关):
- criterion='entropy':指定特征选择准则为熵,对应ID3算法的信息增益;若设为gini则为CART算法的基尼指数;
- max_depth:决策树的最大深度,预剪枝核心参数,值越小,树越简单,抗过拟合能力越强;
- min_samples_split:节点划分的最小样本数,若节点样本数小于该值,停止划分;
- random_state:随机种子,保证模型训练结果可复现。
五、ID3算法的优缺点
ID3作为决策树的入门算法,核心逻辑简单、可解释性强,但存在明显的局限性,是后续C4.5、CART算法改进的基础。以下优缺点总结适配本科/研究生课程考点和实战选型参考。
5.1 核心优点
5.2 核心缺点
六、ID3算法的适用场景与使用前提
ID3算法的局限性决定了它的适用场景较为有限,更适合作为入门学习和小规模离散数据的快速分类,工业级实战中通常被C4.5、CART及集成算法替代。
6.1 优先选择ID3的场景
6.2 ID3算法的使用前提
使用ID3算法前,数据需满足以下条件,否则需要做复杂的预处理:
6.3 不适合使用ID3的场景
七、ID3的改进方向与后续算法
ID3的诸多缺陷为后续决策树算法的改进提供了方向,C4.5和CART是ID3的经典改进版,也是实战中最常用的决策树算法,三者的核心差异见下表(适配课程考点):
| 特征选择准则 | 信息增益 | 信息增益率(修正多值特征偏置) | 基尼指数/均方误差 |
| 支持数据类型 | 仅离散特征 | 离散+连续特征 | 离散+连续特征 |
| 缺失值处理 | 不支持 | 支持(概率权重+分配) | 部分支持(预处理/内置) |
| 剪枝机制 | 无(需手动预剪枝) | 预剪枝+后剪枝 | 后剪枝(代价复杂度剪枝) |
| 树结构 | 多叉树(按特征取值数) | 多叉树+二叉树(连续特征) | 二叉树(所有特征均二叉) |
| 适用任务 | 仅分类 | 仅分类 | 分类+回归 |
| 核心改进 | – | 解决ID3多值特征偏置问题 | 支持回归,二叉划分更高效 |
核心改进逻辑
八、总结
拓展学习:掌握ID3算法后,可依次学习C4.5算法(信息增益率、连续值/缺失值处理)、CART算法(基尼指数、分类+回归),再过渡到集成学习(Bagging/Boosting),掌握随机森林、XGBoost等工业级算法,形成完整的决策树知识体系。





