关于互信息的一些注记
此注记将说明:互信息(Mutual Information)如何作为特征选择指标,及互信息在此用途中的隐蔽缺陷,以及使用 normalized mutual information作为更合理指标的必要性,附及均值的重要意义,等等。
感谢 Google,维基,StackOverflow,Github ,有了它们,程序员的工作总能站在其他人的肩膀上进行,为什么我会写下这篇关于互信息的注记,恰恰也是因为,在我尝试搜索之后,这些东西似乎并不能从 Google 或 StackOverflow 上直接查到,尽量详细地写下这些注记,或能有助于以后通过搜索引擎找到这篇文章的人,以滴水报沧海罢了。

在各种教科书和文档中,互信息(Mutual Information)以其优雅对称的数学表达形式,一直被推荐为一个经典的特征选择方法。但实践中(因为效果不尽如意)互信息被其他指标代替的机会更高,互信息优雅的数学表达+信息论的内核为什么并不是最佳的特征选择方法?熵是那么美丽和有力的一个公式,无处不在,无往不利。互信息脱胎于之,但却没能统治特征选择这一小领域,相对于熵独一无二的地位,在特征选择时至少有半打以上的不同指标在机器学习工程师的工具箱里常备。
我们来看一下计算互信息的实际例子。在 sklearn.metrics 里有现成的互信息计算函数: mutual_info_score, 具体的使用代码如下:

可以看一下这两组数据,这是互信息有效发挥功能的简明例子,mutual_info_score这个函数接受输入一组信号量和一族标签量,当我们要用互信息来鉴别某个特征是否有效的时候,正是希望该特征的输出信号和标签有较好的相关关系。当信号和标签对应更好的时候,互信息较大,反之类似。如上面的例子所示,互信息看起来表现不错,指标和标签是否有效对应确实反映到了互信息上,到此为止,都很正常。
接下来我们看一个互信息“不合理地“失效例子:

互信息看起来失效了。两组不同的分布,为什么会算出相同的互信息呢? 是 sklearn 的源码实现有 bug 吗?不是,我记得当时我的第一反应是 sklearn 出 bug了,但在 check 源码 又 check 了数学公式后,意识到这次真不是代码的锅,是数学定义一开始就误导了我们。
读到此处可以停下来想一下互信息指标看似失效的原因(或者说条件)是什么?
让我们换一个角度来看互信息。互信息事实上可以被重新描述为两个变量的熵之和减去它们联合的熵。

这个公式的形式直接暗示着集合的文氏图,如下:

从这个角度,我们重新看一下互信息失效的细节:

注意上面实现的entropy 函数以自然对数为基础运算,得到的值和以2为底的不同,这样得到的信息量单位或许可以称为自然比特(相对于基于以2为底的对数算出信息量的单位是2进制比特),我们在这里用自然比特,是因为 sklearn 的 mutual_info_score 的实现里用的也是自然对数,为了比较 I(A;B) 和 H(A)+H(B)-H(A,B), 需要保持单位一致。
在之前的例子中,信号序列signals 和 两个不同的标签序列labels_1, labels_2 有相同的互信息,拆解到公式里,就意味着 :
H(signals) + H(labels_1) - H(signals, lables_1) = H(signals) + H(labels_2) - H(signals, lables_2)
消去相同的H(signals),有: H(labels_1) - H(signals, lables_1) = H(labels_2) - H(signals, lables_2) 可以确认是这样:

更复杂的 labels_2 的熵确实变大了。但联合熵也同步变大,求差之后对消,于是交集部分的互信息毫无差异。用图来看会更明确。

意味着什么呢,I(A,B)和I(A,C)的互信息量虽然相当,但H(B)<H(C), 原有的指标H(A) 对标签集 C 而言,表达能力不足了,但也不是完全无效,依然有一定信息,从绝对信息量来说,还可以保持相当。比如[1,1,0,0,0] 作为指标区分不了[a,b,c,d,e] ,只能分开[a,b] 和 [c,d,e] ,和分开上一组标签中的[a] 和 [s] 是一样的。互信息虽然反映了指标(对于标签)的绝对表达能力(通过信息量来度量),但没有反映出指标集相对于标签集整体表达能力的真实减弱。此处我为了暴露互信息的缺陷,故意选择的labels_2 相对于labels_1 是一个更细化的标签集,花一些功夫可以证明,标签或指标的任何细化都无法被互信息基于绝对信息量的度量方式描述出来,一个可以记住的tip是:细化标签(或信号,它们是对称的)不会改变互信息的绝对值。
总结:重点是互信息提供的是指标和标签之间的绝对信息量,而这在实践中随指标集和标签集的自身规模变化而变化,并不真实反映指标集相对于标签集整体的表达能力,要在实际数据中使用互信息挑选指标就很容易进坑了,难怪这个指标表现得时好时坏,并不稳定。
那怎么让互信息能精确反映出这些变化呢?答案也很显然了,需要针对 H(A) 和 H(B) 的整体大小标准化,以消除H(A)和 H(B) 大小的影响,这就是标准化互信息(Normalized Mutual Information, 或 NMI)的思路。比如当 I(A,B) 只是H(A)或H(B)中很小的一部分,标准化后得到的低比例就能反映出指标表达能力的缺陷。
从上面的图示可以直观看出,当 H(A) = H(B) 时 (A,B两个圆重合),I(A,B) = H(A) = H(B) = H(A,B), 此时 I(A,B)最大,I(A,B) 最大时也就是占满 A或 B的时候,因为对称性,必须同时占满。 这可以召唤起我们对均值不等式在a等于 b 时取极值的回忆。这事实上揭示了normalized mutual information 的公式:

是的,这两个都是有效的 NMI 的公式,并且都在[0, 1]闭区间内取值,0代表两组变量完全无关,1代表完全相关。两个公式只是采用算术平均和几何平均的差异。说到均值,均值在统计中,简直是“被嫌弃的均值”,因为它无法反应分布整体情况且容易被极值影响,在描述统计量时避免用均值改用分位数已是常识。但在数学上,均值的重要意义和均值不等式密切相关,当遇到需要相等时取极值的情况,此处就往往隐藏了均值的身影。
下面来验算一下我们的公式,看看是否有效。

一例胜千言,由上可见,NMI 确实是一个比 MI 鉴别能力更强的指标,对之前MI 无法区分的情况也给出了有效度量,我们这里的计算采用几何平均的版本,和 sklearn 保持了一致,还可以用 sklearn 对NMI的现成实现来验算一下:

之前说到过,熵有单位,并基于所用的对数底的不同而不同,以2为底时,一个公平的硬币(两面概率相等)的熵正好是1,代表用1个2进制比特正好可以描述硬币的信息量(正或反),但用自然对数为底并不影响我们对信息量的度量,只是比特的单位变了(于是值有所不同)。从互信息的公式 I(A,B) = H(A)+H(B)-H(A,B) 可以看出互信息的单位也是比特。但normalized 后的标准化互信息(NMI)其实是没有单位的一个比率(分子和分母的单位消掉了,和物理公式一样),因为没有单位,所以感兴趣的读者可以验算一下,不管 entropy 实现时用什么为底,最终的 NMI 值是一样的。
补充一下,sklearn.metrics 中虽然就有 normalized_mutual_info_score 的现成函数,但实际工作场景中如果数据量大的话,往往自己从 H(A), H(B) 和 H(A,B) 计算会更有效率,这个效率不是因为 sklearn 的实现低效,而是你为了满足其输入参数,构造一个个指标序列和标签序列是开销非常大的对象构建过程,建议尽量避免。
最后,在遇到那些建议使用互信息来度量变量相关性却对标准化互信息不置一词的书或文章时,要小心其余的部分,明显这样的作者并没有充分研究过他所谈论的对象。
© 本文版权归 NullPointer 所有,任何形式转载请联系作者。
© 了解版权计划