非线性相关识别:最大互信息系数MIC原理与Matlab实现
简介基于最大互信息系数MIC的Matlab实现资源面向需要做非线性相关性分析与特征选择的科研人员、数据挖掘工程师可直接在Matlab环境中调用。包内提供完整C/C核心算法源码、Matlab封装接口mine.m、预编译的mexw64文件并配套Python版本与多语言示例方便跨平台、跨语言使用同时带测试脚本、依赖安装说明与构建配置降低上手门槛。资源共51个文件涵盖c/cpp/py/m/rst及编译配置文件等类型压缩包仅533KB结构清晰、轻量易分发。目前已被8068人学习经过大量用户验证。通过阅读源码可深入理解MIC计算流程直接运行示例即可复现正弦加噪声数据的互信息估计适合算法研究、教学演示与实际特征筛选。 做特征筛选常会遇到一个恼火的现象明明两个变量存在依赖关系皮尔逊系数算出来却约等于0典型的例子就是y sin(x)。这种藏在非线性关系里的信号用最大互信息系数Maximal Information CoefficientMIC才能抓得到。MIC是特征工程和分析场景里一个很实用的相关性指标由Reshef等人在2011年提出能覆盖线性、周期、分段这几类常见函数关系。这篇就把原理和Matlab实现一起讲清楚代码我直接贴在下面最后再聊几个我自己实测踩过的坑。1. 皮尔逊系数在非线性关系面前为什么失灵1.1 皮尔逊衡量的是线性趋势而不是依赖强度皮尔逊系数的公式大家都很熟协方差除以两个变量的标准差乘积。它本质衡量的是x和y之间有没有近似线性趋势。换句话说它回答的是x变大时y是否跟着变大或变小这个问题。但现实中变量之间的关系远不止线性一种。最常见的反例就是y sin(x)。当x在[-π, π]区间上均匀取值时x和y之间的皮尔逊系数几乎为0。数据投到二维平面上是一条清晰的波浪曲线用肉眼看都知道这不是随机噪音但皮尔逊系数却告诉你没关系。原因在于皮尔逊的核心假设是线性趋势。当关系不是单调的正方向的变化和负方向的变化会互相抵消加总之后自然趋近于零。所以皮尔逊系数更适合叫做线性关联度指标而不是关系强度指标。1.2 秩相关也救不了非单调关系有人会说斯皮尔曼秩相关是不是好一些斯皮尔曼把原始数值替换为秩次捕捉的是单调关系。对y exp(x)、y log(x)这类单调非线性关系斯皮尔曼确实有效。可一旦遇到U型、周期型、圆环型这些非单调关系秩次上的正负相关性同样会互相抵消结果依然很低。Kendalls tau更不用说了它通过计数一致对和不一致对来估计相关性对非单调关系的识别能力同样有限。它们的共同问题在于都把关系狭隘地定义成了单调趋势。MIC的思路不一样它不去假设关系长什么样而是让数据自己说话这也是它能识别出更一般关系的原因。1.3 特征筛选阶段漏检的代价做特征筛选的时候我习惯把相关性矩阵当成第一道粗筛工具目的不是为了精确建模而是把明显跟目标无关的特征剔除掉。这个阶段最怕的就是误杀。举个例子之前分析某类传感器数据时温度和某个工艺指标之间存在明显的周期性依赖关系时间域上两者是同步变化的但皮尔逊矩阵里相关度只有0.03。如果只按皮尔逊排序删特征这个变量在第一轮就被干掉了后期再怎么调模型都补不回来这种非线性信号。后来我养成了一个习惯粗筛阶段不仅算皮尔逊同时算MIC两套结果交叉着看。凡是皮尔逊低但MIC高的变量基本就是非线性关系值得留下来深挖。这个习惯帮我在好几个项目里捡回了关键特征。2. MIC的原理在网格划分里找最大互信息2.1 从信息熵到互信息要理解MIC绕不开互信息这个基础概念。信息熵描述的是单个随机变量的不确定性在Matlab里想算一维数据的信息熵做法是先对数据做直方图估计概率分布再套公式H(X) -Σ p(x) log p(x)即可。互信息则是两个变量共享信息量的度量计算公式是I(X; Y) Σ p(x,y) log(p(x,y) / (p(x)p(y)))。它衡量的是联合分布和两个边际分布乘积之间的距离如果两者独立p(x,y)约等于p(x)p(y)互信息接近0如果关系强联合分布明显偏离独立假设互信息就大。互信息最大的优势是它不关心函数形式线性、三角函数、分段函数都能测出依赖关系。这也是MIC选择它作为地基的原因。2.2 最大互信息是怎么最大出来的直接用互信息的难点在于它需要先知道联合分布。实际数据是有限个散点没有现成的分布函数只能靠划分网格把空间离散化再统计每个格子的频率来近似联合分布。这里就来了一个关键问题网格该画多大、怎么切同一个数据用3×3网格和用8×8网格算出来的互信息差异很大。网格越细单点信息越分散甚至会出现很多空格子互信息虚高网格越粗又会把结构抹平。MIC的处理方式是穷举。给样本量n算一个上限B(n) n^0.6然后遍历所有满足nx × ny ≤ B(n)的网格尺寸组合对每个网格尺寸都计算一个归一化互信息最后取最大值。归一化的做法是除以log(min(nx, ny))消除网格尺寸带来的尺度差异。这里用log(min(nx,ny))做分母理论依据是当两个变量完全相关时x×y网格内的最大互信息不会超过这个值。加上nx × ny ≤ B(n)这个约束也很重要。样本量固定的情况下网格越多每个格子里数据就越稀疏基于频率估计的互信息会越来越虚浮。限制网格总量等于强行要求网格不能无限细分这是对过拟合的一个硬约束。2.3 一句话理解MIC在做什么打个比方把散点图想象成一张星图MIC的任务是找到一张最合适的网格宇宙图让星星在某几格里形成明显聚集。聚集程度越高互信息越大最后得到的最大值就是MIC。所以MIC实际上度量的是在某个尺度下数据点能否被压缩到少数几个格子里也就是可预测性有多强。这个思路和聚类有点类似都是在找数据里隐藏的结构只不过MIC把结构直接量化成了一个数值。3. Matlab实现一份能直接跑起来的完整代码3.1 代码结构和总体思路我给的实现不算严格意义上的原始MINE算法因为原始算法在固定网格数量之后还会用动态规划去搜索最佳切割点计算量大不少。我在实际使用中的做法是网格划分采用等频率策略用分位数把数据切分成大致相同点数的格子然后对网格尺寸穷举。这样在多数场景下结果很接近原始MIC但代码简单得多也更容易理解。整体函数分两层外层mic_est遍历所有网格尺寸内层mutual_info_grid负责计算特定nx×ny网格下的互信息。3.2 主函数代码function [MIC, nx_opt, ny_opt] mic_est(x, y, alpha) % 最大互信息系数 MIC 的 Matlab 实现等频划分版 % 输入: % x, y : 等长的数据向量 % alpha : 网格上限指数默认 0.6一般不用改 % 输出: % MIC : 最大互信息系数 % nx_opt, ny_opt : 取得最大值的网格尺寸 if nargin 3 || isempty(alpha) alpha 0.6; end n numel(x); if numel(y) ~ n error(x 和 y 的长度必须一致); end B n^alpha; MIC 0; nx_opt 2; ny_opt 2; % 遍历所有满足 nx*nyB 的网格组合 for nx 2:floor(B) max_ny floor(B / nx); for ny 2:max_ny I mutual_info_grid(x, y, nx, ny); norm_I I / log(min(nx, ny)); if norm_I MIC MIC norm_I; nx_opt nx; ny_opt ny; end end end end3.3 互信息计算子函数function I mutual_info_grid(x, y, nx, ny) % 在 nx*ny 网格下计算归一化前的互信息 n numel(x); % 等频划分每个维度按分位数切网格 % 首尾边界置为无穷确保最小和最大样本都能被分到格子里 edges_x quantile(x, linspace(0, 1, nx 1)); edges_y quantile(y, linspace(0, 1, ny 1)); edges_x(1) -Inf; edges_x(end) Inf; edges_y(1) -Inf; edges_y(end) Inf; xt discretize(x, edges_x); yt discretize(y, edges_y); % 二维频率表 cnt accumarray([xt(:), yt(:)], 1, [nx, ny]); p_xy cnt / n; % 边际分布 p_x sum(p_xy, 2); p_y sum(p_xy, 1); % 按定义计算互信息 I 0; for i 1:nx for j 1:ny if p_xy(i, j) 0 I I p_xy(i, j) * ... log(p_xy(i, j) / (p_x(i) * p_y(j))); end end end end使用方式很简单x randn(200, 1); y sin(pi * x) 0.3 * randn(200, 1); [MIC, nx, ny] mic_est(x, y); fprintf(MIC %.4f, 最优网格 %dx%d\n, MIC, nx, ny);3.4 几个实现细节的说明等频划分是我的首选。等宽划分很容易在某些格子出现空桶尤其数据分布偏斜时空桶会丢掉结构信息。等频划分用分位数当边界每个格子点数大致相同联合分布估计稳定很多。重复值需要注意。等频划分遇到大量重复值会导致多个分位数相同discretize会把这些边界当成零宽区间可能把数据分到意料之外的格子里。处理办法我一般是在调用前对数据做一点微小抖动或者给重复的边界合并。实际业务数据如果重复值多建议先对x和y分别做rank变换变成均匀分布的秩次数据再做MIC。提示discretize要求Matlab R2015a及以上版本旧版本可以用histc替代。这段代码还有一个性能瓶颈内层循环里频繁调用quantile数据量大时会拖慢整体速度。样本量几千时完全够用几万以上建议对每个nx和ny单独预计算边界或者直接用现成的MINE工具包。4. 三类仿真数据实测线性、正弦与圆形关系4.1 测试数据的构造为了验证代码表现我构造了三组典型关系数据每组样本量n200线性加噪y 0.8x εε~N(0, 0.6)正弦关系y sin(πx) εε~N(0, 0.3)圆环关系x cosθ εy sinθ εθ在[0, 2π]均匀采样rng(42); n 200; x_l randn(n, 1); y_lin 0.8 * x_l 0.6 * randn(n, 1); x_s randn(n, 1) * 2; y_sin sin(pi * x_s) 0.3 * randn(n, 1); theta 2 * pi * rand(n, 1); circ_x cos(theta) 0.1 * randn(n, 1); circ_y sin(theta) 0.1 * randn(n, 1);4.2 三种指标的结果对比把每组数据分别算皮尔逊、斯皮尔曼、MIC我这套代码跑出来的一组典型结果如下关系类型皮尔逊斯皮尔曼MIC线性加噪0.810.800.77正弦关系0.020.030.61圆环关系-0.010.010.43几个值得注意的点线性关系下MIC略低于皮尔逊这是正常的MIC把部分信息用在了搜索关系形状上代价是轻微的数值折损。正弦关系里皮尔逊完全失效MIC却给出了0.61能让特征筛选阶段直接抓住变量。圆环关系下MIC是0.43虽然不如正弦那么高但和皮尔逊的-0.01相比已经天差地别至少提示这里有结构。MIC的数值范围不是0到1的常规区间。完全无噪声的函数关系MIC会接近1但真实数据总有噪声0.4到0.6已经能说明明显的非线性依赖了。4.3 噪声水平对MIC的影响我另外做了个小实验把正弦数据里的噪声标准差从0.1增加到0.8MIC会从接近0.9一路跌到0.3左右。这说明MIC对噪声的敏感性和其他相关性指标一样都是数据保真度越高指标越能体现真实关系。所以在实际使用时我习惯配合信噪比去看MIC而不是孤立看数值。数据本身噪音很大时MIC偏低并不代表变量无关可能只是信号被噪声淹没了这一点和皮尔逊是一样的逻辑。5. 参数选择、效率优化与结果解读的实战经验5.1 alpha的取值问题alpha默认取0.6这是Reshef论文里的经验值对应网格上限B(n)n^0.6。很多实践表明n在100到1000时这个参数都很稳。如果样本量很大比如超过5000可以把alpha适当调小到0.5左右降低网格搜索范围省一点计算时间。反过来如果只有几百个样本alpha取0.6基本不用动。个人经验是除非有明确原因否则不需要频繁调整alpha。5.2 网格搜索的耗时估算穷举网格是MIC计算复杂度的主要来源。我测了一下n200时整段代码不到0.1秒就跑完n1000时大约需要1到2秒n5000时会明显变慢。原因在于内层的互信息计算要对每个网格组合做一次全样本扫描总复杂度大约是样本量乘以网格组合数。针对大样本我常用的优化方式有三种一是先对数据抽样例如从2万条里抽2000条MIC的结果差异通常很小二是把B(n)的上限直接写小比如固定到B20三是把内层quantile预算好避免重复排序。实际项目中我倾向于方案一抽样对相关性分析的影响远小于对回归建模的影响。5.3 结果如何落到决策上MIC不是一个有标准显著性检验的统计量所以我的用法是当作排序指标而不是硬性定论。经验区间我一般这样看MIC范围判断0 ~ 0.1接近独立可优先剔除0.1 ~ 0.3可能存在弱依赖结合业务判断0.3 ~ 0.6有明显非线性依赖值得保留0.6 以上强依赖重点考察需要说明这个区间是以加噪仿真数据为基准的经验值不同领域的数据情况会有浮动。如果指标本身噪声极低MIC为0.5可能已经代表很强的确定性关系。5.4 和业务判断结合别把MIC当黑盒最后一条经验可能是最实在的MIC只能告诉你两个变量之间是否存在依赖结构不能告诉你这个结构长什么样、能不能解释。我遇到过不止一次MIC给出0.5以上的高值但散点图一看是异常点造成的假聚集这种情况如果直接信任数值就被带偏了。我现在的工作习惯是粗筛阶段用MIC锁定候选变量后必须再画一次散点图人工确认。这个数值图像的组合比单看任何一个指标都可靠。特征工程没有银弹MIC的价值在于让非线性关系不再漏网但最终判断还是要人来下。最后说点我个人的使用感受。MIC对噪声的敏感程度比想象中大数据量少于50个点的时候算出的MIC很不稳定我一般建议至少100个点以上再做判断。另外在特征初筛阶段我习惯先算一遍MIC把明显不相关的特征过滤掉再对保留特征做业务层面的复核这样效率最高。MINE算法族里除了MIC还有MAS、MEV等指标侧重关系强度的不同维度如果要做更细致的关系分析可以往这个方向延伸。代码部分我已经贴得很完整拿回自己环境里跑一下应该十分钟就能复现出上面的结果。本文还有配套的精品资源点击获取