人工智能之数学基础 信息论
第三章 实用工具
前言
在机器学习和数据科学中,如何从成百上千个特征中选出最有用的子集? 信息论提供了强大而优雅的工具:互信息(Mutual Information) 和 信息增益(Information Gain)。它们不仅能衡量特征与目标变量的相关性,还能用于无监督/有监督特征选择、决策树分裂、聚类评估等任务。
本文将系统讲解:
- 互信息(MI)的数学定义与直觉
- 信息增益(IG)与互信息的关系
- 连续变量的互信息估计方法
- 在特征选择中的实际应用
- 配套 Python 代码实现(从零构建 +
sklearn对比 + 可视化 + 实战案例)
一、核心概念
1. 互信息(Mutual Information, MI)
定义(离散变量)
等价形式
- 直觉: 与 共享的信息量
- 性质:
- (独立)
- 对称:
- 不要求线性关系(可捕捉非线性依赖)
✅ 示例:若知道天气(X)能减少对是否带伞(Y)的不确定性,则
2. 信息增益(Information Gain, IG)
在决策树(如 ID3、C4.5)中,信息增益 = 互信息:
- 解释:使用特征 对样本分组后,标签 的不确定性减少了多少
- 决策树分裂准则:选择使 IG 最大的特征
🌟 关键洞见:信息增益不是新概念,它就是互信息在分类任务中的别名!
3. 归一化互信息(Normalized Mutual Information, NMI)
为便于比较不同数据集,常将 MI 归一化:
- 范围:[0, 1]
- 1 表示完全相关,0 表示独立
二、连续变量的互信息估计
真实数据中,特征常为连续值(如年龄、收入),而 MI 定义基于概率质量函数(PMF)。需进行估计:
常用方法:
| 方法 | 描述 | 优缺点 |
|---|---|---|
| 离散化(Binning) | 将连续值分箱转为离散 | 简单,但结果依赖分箱策略 |
| k 近邻(KNN) | 基于 Kraskov 等人方法 | 更准确,适用于高维 |
| 核密度估计(KDE) | 估计联合/边缘密度 | 计算复杂,带宽敏感 |
💡 实践中:
sklearn使用 k 近邻法(mutual_info_regression/mutual_info_classif)
三、在特征选择中的应用
1. 过滤式特征选择(Filter Method)
- 步骤:
- 对每个特征 ,计算
- 按 MI 降序排序
- 选择 top-k 特征
- 优点:快速、与模型无关、可并行
- 缺点:忽略特征间交互(如 XOR 问题)
2. 与其它方法对比
| 方法 | 是否考虑特征交互 | 是否依赖模型 | 速度 |
|---|---|---|---|
| 互信息 | ❌(单变量) | ❌ | ⚡ 快 |
| 递归特征消除(RFE) | ✅ | ✅ | 慢 |
| L1 正则化 | ✅(隐式) | ✅ | 中 |
✅ 最佳实践:先用 MI 快速筛选,再用包装法精细优化
四、Python 代码实现
1. 导入库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from collections import Counter
from sklearn.feature_selection import mutual_info_classif, mutual_info_regression
from sklearn.datasets import make_classification, load_boston
from sklearn.preprocessing import KBinsDiscretizer
plt.rcParams['font.sans-serif'] = ['SimHei']
sns.set(style="whitegrid")
2. 从零实现离散互信息
def estimate_pmf(data):
"""估计离散数据的 PMF"""
counts = Counter(data)
total = len(data)
return {k: v / total for k, v in counts.items()}
def shannon_entropy(pmf, base=2):
"""计算香农熵"""
probs = np.array(list(pmf.values()))
probs = probs[probs > 0]
return -np.sum(probs * np.log(probs)) / np.log(base)
def joint_pmf(x, y):
"""估计联合 PMF"""
assert len(x) == len(y)
joint = Counter(zip(x, y))
total = len(x)
return {k: v / total for k, v in joint.items()}
def mutual_information_discrete(x, y, base=2):
"""计算离散变量的互信息 I(X;Y)"""
# 边缘分布
px = estimate_pmf(x)
py = estimate_pmf(y)
# 联合分布
pxy = joint_pmf(x, y)
mi = 0.0
for (xi, yi), p_xy in pxy.items():
p_x = px[xi]
p_y = py[yi]
if p_x > 0 and p_y > 0:
mi += p_xy * np.log(p_xy / (p_x * p_y))
return mi / np.log(base)
3. 测试离散 MI:模拟分类数据
# 构造强相关特征
np.random.seed(0)
n = 1000
y = np.random.choice([0, 1], size=n)
# X1 与 Y 高度相关
x1 = np.where(y == 1,
np.random.choice([0,1], p=[0.1, 0.9]),
np.random.choice([0,1], p=[0.9, 0.1]))
# X2 与 Y 独立
x2 = np.random.choice([0,1], size=n)
mi1 = mutual_information_discrete(x1, y)
mi2 = mutual_information_discrete(x2, y)
print(f"I(X1; Y) = {mi1:.4f}") # ≈ 0.32
print(f"I(X2; Y) = {mi2:.4f}") # ≈ 0.00
# 可视化
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
for i, (x, title) in enumerate([(x1, "X1 (相关)"), (x2, "X2 (独立)")]):
df = pd.DataFrame({'X': x, 'Y': y})
crosstab = pd.crosstab(df['X'], df['Y'], normalize='columns')
sns.heatmap(crosstab, annot=True, ax=axes[i], cmap="Blues")
axes[i].set_title(title)
plt.tight_layout()
plt.show()
4. 连续特征的互信息:离散化 vs sklearn
# 加载波士顿房价数据(回归任务)
# 注意:load_boston 已弃用,此处仅作示例;实际可用 fetch_california_housing
from sklearn.datasets import fetch_california_housing
data = fetch_california_housing()
X, y = data.data[:1000], data.target[:1000] # 子采样加速
feature_names = data.feature_names
# 方法1:sklearn 内置(k近邻)
mi_sklearn = mutual_info_regression(X, y, random_state=0)
# 方法2:手动离散化 + 自实现
def mi_via_binning(X_col, y, n_bins=10):
# 对连续特征和目标分别分箱
est = KBinsDiscretizer(n_bins=n_bins, encode='ordinal', strategy='uniform')
x_binned = est.fit_transform(X_col.reshape(-1, 1)).flatten()
y_binned = est.fit_transform(y.reshape(-1, 1)).flatten()
return mutual_information_discrete(x_binned.astype(int), y_binned.astype(int))
mi_manual = np.array([mi_via_binning(X[:, i], y) for i in range(X.shape[1])])
# 比较
mi_df = pd.DataFrame({
'Feature': feature_names,
'Sklearn (KNN)': mi_sklearn,
'Manual (Binning)': mi_manual
}).sort_values('Sklearn (KNN)', ascending=False)
print(mi_df.head())
🔍 观察:两种方法排序大致一致,但绝对值不同(因估计方法不同)
5. 特征选择实战:使用 MI 筛选 top-k 特征
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
# 生成高维分类数据
X, y = make_classification(
n_samples=1000,
n_features=20,
n_informative=5, # 只有5个特征有用
n_redundant=5,
n_clusters_per_class=1,
random_state=42
)
# 划分数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
# 计算每个特征的 MI
mi_scores = mutual_info_classif(X_train, y_train, random_state=0)
# 选择 top-5 特征
top_k = 5
selected_features = np.argsort(mi_scores)[-top_k:]
# 训练模型
rf_full = RandomForestClassifier(random_state=0).fit(X_train, y_train)
rf_mi = RandomForestClassifier(random_state=0).fit(X_train[:, selected_features], y_train)
acc_full = accuracy_score(y_test, rf_full.predict(X_test))
acc_mi = accuracy_score(y_test, rf_mi.predict(X_test[:, selected_features]))
print(f"全特征准确率: {acc_full:.4f}")
print(f"MI 选5特征准确率: {acc_mi:.4f}")
print(f"选中的特征索引: {selected_features}")
# 可视化 MI 分数
plt.figure(figsize=(10, 5))
indices = np.arange(len(mi_scores))
plt.bar(indices, mi_scores, color='skyblue')
plt.xlabel('特征索引'); plt.ylabel('互信息分数')
plt.title('各特征与目标的互信息')
plt.axhline(y=np.sort(mi_scores)[-top_k], color='r', linestyle='--', label=f'前{top_k}阈值')
plt.legend()
plt.show()
💡 结果:通常 MI 选择的特征能达到接近全特征的性能,且训练更快
6. 决策树中的信息增益(ID3 核心)
def information_gain(y, splits):
"""
计算按某特征分割后的信息增益
y: 原始标签
splits: 分割后的子集列表(如 [left_y, right_y])
"""
def entropy(labels):
pmf = estimate_pmf(labels)
return shannon_entropy(pmf)
total_n = len(y)
original_entropy = entropy(y)
weighted_entropy = 0.0
for split in splits:
if len(split) == 0:
continue
weight = len(split) / total_n
weighted_entropy += weight * entropy(split)
return original_entropy - weighted_entropy
# 示例:完美分割
y = [0,0,0,0,1,1,1,1]
split_left = [0,0,0,0]
split_right = [1,1,1,1]
ig = information_gain(y, [split_left, split_right])
print(f"信息增益(完美分割): {ig:.4f}") # 应等于 H(Y) ≈ 1.0
# 随机分割
np.random.shuffle(y)
split1 = y[:4]
split2 = y[4:]
ig_random = information_gain(y, [split1, split2])
print(f"信息增益(随机分割): {ig_random:.4f}") # 接近 0
7. 互信息 vs 相关系数(捕捉非线性关系)
# 构造非线性关系数据
np.random.seed(0)
x = np.linspace(-2, 2, 500)
y_nonlinear = x**2 + np.random.normal(0, 0.2, size=x.shape)
y_linear = 2*x + np.random.normal(0, 0.5, size=x.shape)
# 计算 Pearson 相关系数
corr_nonlinear = np.corrcoef(x, y_nonlinear)[0,1]
corr_linear = np.corrcoef(x, y_linear)[0,1]
# 计算互信息(先离散化)
x_binned = KBinsDiscretizer(n_bins=20).fit_transform(x.reshape(-1,1)).flatten()
y_nl_binned = KBinsDiscretizer(n_bins=20).fit_transform(y_nonlinear.reshape(-1,1)).flatten()
y_l_binned = KBinsDiscretizer(n_bins=20).fit_transform(y_linear.reshape(-1,1)).flatten()
mi_nonlinear = mutual_information_discrete(x_binned.astype(int), y_nl_binned.astype(int))
mi_linear = mutual_information_discrete(x_binned.astype(int), y_l_binned.astype(int))
# 绘图
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
axes[0].scatter(x, y_nonlinear, alpha=0.6)
axes[0].set_title(f"非线性关系\nPearson={corr_nonlinear:.2f}, MI={mi_nonlinear:.2f}")
axes[1].scatter(x, y_linear, alpha=0.6)
axes[1].set_title(f"线性关系\nPearson={corr_linear:.2f}, MI={mi_linear:.2f}")
plt.tight_layout()
plt.show()
📌 结果:
- 非线性关系:Pearson ≈ 0(检测不到),但 MI 显著 > 0
- 线性关系:两者都高
五、总结与最佳实践
| 工具 | 公式 | 适用场景 | 优势 |
|---|---|---|---|
| 互信息(MI) | 特征选择、相关性分析 | 捕捉任意依赖(线性/非线性) | |
| 信息增益(IG) | 决策树分裂 | 直接优化分类不确定性 | |
| 归一化 MI | 聚类评估、跨数据集比较 | 无量纲,范围 [0,1] |
✅ 最佳实践建议:
- 分类任务:使用
mutual_info_classif - 回归任务:使用
mutual_info_regression - 高维数据:先用 MI 快速筛选 top-20% 特征
- 非线性关系:MI 比 Pearson/Spearman 更可靠
- 避免过拟合:MI 对噪声敏感,可结合交叉验证
💡 终极洞见: 互信息是“通用相关性度量”——它不假设任何函数形式,只问:“知道 能减少多少关于 的不确定性?” 这正是特征选择的本质。
后续
python过渡项目部分代码已经上传至gitee,后续会逐步更新。
资料关注
公众号:咚咚王 gitee:gitee.com/wy185850518…

《Python编程:从入门到实践》 《利用Python进行数据分析》 《算法导论中文第三版》 《概率论与数理统计(第四版) (盛骤) 》 《程序员的数学》 《线性代数应该这样学第3版》 《微积分和数学分析引论》 《(西瓜书)周志华-机器学习》 《TensorFlow机器学习实战指南》 《Sklearn与TensorFlow机器学习实用指南》 《模式识别(第四版)》 《深度学习 deep learning》伊恩·古德费洛著 花书 《Python深度学习第二版(中文版)【纯文本】 (登封大数据 (Francois Choliet)) (Z-Library)》 《深入浅出神经网络与深度学习+(迈克尔·尼尔森(Michael+Nielsen)》 《自然语言处理综论 第2版》 《Natural-Language-Processing-with-PyTorch》 《计算机视觉-算法与应用(中文版)》 《Learning OpenCV 4》 《AIGC:智能创作时代》杜雨+&+张孜铭 《AIGC原理与实践:零基础学大语言模型、扩散模型和多模态模型》 《从零构建大语言模型(中文版)》 《实战AI大模型》 《AI 3.0》