为何计算两个numpy向量互信息结果相同?正确计算方法是什么?
问题
我想要计算两个numpy向量之间的互信息,使用sklearn的mutual_info_score函数进行测试:
>>>from sklearn.metrics.cluster import mutual_info_score >>>import numpy as np >>>a, b = np.random.rand(10), np.random.rand(10) >>>mutual_info_score(a, b) 1.6094379124341005 >>>a, b = np.random.rand(10), np.random.rand(10) >>>mutual_info_score(a, b) 1.6094379124341005
可以看到,尽管更新了a和b,却返回相同的值。随后我尝试了另一组示例:
>>>a = np.array([167.52523295, 73.2904335 , 98.61953303, 152.17297007, 211.01341451, 327.72296346, 356.60500081, 43.9371432 , 119.09474284, 125.20180842]) >>>b = np.array([280.9287028 , 131.76304983, 176.0277832 , 188.56630096, 229.09811401, 228.47200012, 617.67000122, 52.7211511 , 125.95361582, 148.55247447]) >>>mutual_info_score(a, b) 2.302585092994046 >>>a = np.array([ 6.71381009, 1.43607653, 3.78729242, -4.75706796, -3.81281173, 3.23440092, 10.84495625, -0.19646145, 4.09724507, -0.13858104]) >>>b = np.array([ 4.25330873, 3.02197642, -3.2833848 , 0.41855662, -3.74693531, 0.7674982 , 11.36459148, 0.64636462, 0.51817262, 1.65318943]) >>>mutual_info_score(a, b) 2.302585092994046
为何会出现这种情况?这些向量数值差异明显,却返回相同结果?更重要的是,如何正确计算两个向量之间的互信息?
原因分析
sklearn.metrics.cluster.mutual_info_score是为离散类别数据设计的工具,比如聚类标签、分类任务的类别,它会将输入中的每个唯一值视为一个独立类别。
当你输入连续型numpy向量时,由于随机生成的浮点数几乎全是唯一值,函数会把每个样本当成单独的类别。此时:
- 每个
a的类别对应唯一的b类别(因为样本是成对的(a_i, b_i)),条件熵H(b|a)=0 - 互信息I(a;b) = H(a) - H(a|b) = H(a)(或等于H(b),因为两者熵值相同)
你的测试案例中:
- 第二组的两个向量都包含10个唯一值,所以H(a)=ln(10)≈2.3025,与返回结果一致
- 第一组的随机向量恰好包含5个唯一值,因此返回ln(5)≈1.6094,两次随机生成的向量类别数相同,结果自然一致
正确计算连续向量互信息的方法
方法1:分箱离散化后计算
先把连续向量划分为若干离散区间,再用mutual_info_score计算:
import numpy as np from sklearn.metrics.cluster import mutual_info_score from sklearn.preprocessing import KBinsDiscretizer # 生成连续向量 a = np.random.rand(100) b = np.random.rand(100) # 分箱离散化,分成10个区间 discretizer = KBinsDiscretizer(n_bins=10, encode='ordinal', strategy='uniform') a_discrete = discretizer.fit_transform(a.reshape(-1, 1)).flatten() b_discrete = discretizer.transform(b.reshape(-1, 1)).flatten() # 计算互信息 mi = mutual_info_score(a_discrete, b_discrete) print(mi)
方法2:使用连续变量专用函数
sklearn.feature_selection.mutual_info_regression可以直接计算连续特征与连续目标的互信息:
import numpy as np from sklearn.feature_selection import mutual_info_regression a = np.random.rand(100).reshape(-1, 1) b = np.random.rand(100) # 计算互信息 mi = mutual_info_regression(a, b)[0] print(mi)
方法3:手动估计概率密度计算
通过核密度估计(KDE)计算联合概率密度和边缘概率密度,再按互信息定义计算:
import numpy as np from scipy.stats import kde def mutual_info_continuous(x, y): # 估计联合概率密度 kde_xy = kde.gaussian_kde([x, y]) # 估计边缘概率密度 kde_x = kde.gaussian_kde(x) kde_y = kde.gaussian_kde(y) # 生成网格点计算密度 x_grid, y_grid = np.meshgrid(np.linspace(x.min(), x.max(), 100), np.linspace(y.min(), y.max(), 100)) positions = np.vstack([x_grid.ravel(), y_grid.ravel()]) p_xy = kde_xy(positions) p_x = kde_x(positions[0]) p_y = kde_y(positions[1]) # 计算互信息,避免log(0) mask = p_xy > 0 mi = np.sum(p_xy[mask] * np.log(p_xy[mask] / (p_x[mask] * p_y[mask]))) # 归一化网格面积 mi *= (x.max() - x.min()) * (y.max() - y.min()) / (100*100) return mi # 测试 a = np.random.rand(100) b = np.random.rand(100) print(mutual_info_continuous(a, b))
内容的提问来源于stack exchange,提问作者Shayan
相关产品推荐
相关产品推荐

