NMI 的定义与数学原理:归一化互信息公式推导

信息论里的评估指标,平时做聚类用得最多的就是 NMI 了。我刚开始接触时,说实话也被那一堆 log 搞晕过。但后来我发现,只要把公式拆开,理解起来其实不复杂。

今天我们就来啃这块硬骨头。

NMI,全称 Normalized Mutual Information,归一化互信息。它衡量的是两个分布之间的“共享信息量”。简单说就是:你有多了解我的聚类结果?

为什么要归一化?

互信息 MI 本身有个致命缺陷——它没有上界。数据量一大,MI 的值就跟着膨胀。你很难判断 MI=0.5 到底算好还是差。归一化后,把值压在 [0,1] 之间,0 表示完全独立,1 表示完全一致,这样就好对比了。

一句话总结: NMI 解决的是 MI 的“无量纲化”问题。

数学推导:从熵到互信息

咱们一步一步来。

1. 要理解互信息,先理解熵

熵 H(X) 表示一个系统的不确定性。比如一个硬币扔下去,结果是正还是反?你猜对的概率只有一半,这就是高熵。如果硬币两面都是正面,那不确定性为零,熵就是 0。

熵的公式:

H(X) = - Σ p(x) · log p(x)

这里的 x 是聚类标签,p(x) 是标签出现的概率。说白了,就是所有可能情况的信息量加权求和。

2. 条件熵:知道了 Y,X 还剩下多少不确定性

H(X|Y) = - Σ p(y) Σ p(x|y) · log p(x|y)

什么意思?比如我已经知道了你的聚类结果 Y,再去猜真实标签 X,我的不确定性就会降低。这个降低的量,就是互信息。

3. 互信息公式

互信息可以看作“熵的减少量”:

MI(X;Y) = H(X) - H(X|Y)

或者写成对称形式:

MI(X;Y) = Σ Σ p(x,y) · log( p(x,y) / (p(x) · p(y)) )

这里的 p(x,y) 是联合概率分布。记住这个公式,后面都要用它。

符号 含义
H(X) X 的熵,表示不确定性
H(X|Y) 已知 Y 后 X 的剩余不确定性
MI(X;Y) X 和 Y 共享的信息量

NMI 的归一化方式

互信息归一化有好几种办法,最常见的是用熵的算术平均或几何平均做分母。我个人习惯用算术平均,也就是:

NMI(X;Y) = 2 · MI(X;Y) / ( H(X) + H(Y) )

还有这种写法:

NMI(X;Y) = MI(X;Y) / sqrt( H(X) · H(Y) )

前者叫 ARI 那种归一化风格吗?不,它叫“平均熵归一化”。后者是“几何平均归一化”。两种在 [0,1] 范围内差异不大,但算起来速度不一样。

我建议: 如果你用 Python 的 sklearn,它默认用算术平均 NMI。这挺好,因为当 H(X) 和 H(Y) 相差很大时,算术平均更稳定。

完整 NMI 公式

NMI(X;Y) = 
    [ 2 · Σ Σ p(x,y) · log( p(x,y) / (p(x) · p(y)) ) ]
    / 
    [ - Σ p(x) · log p(x) - Σ p(y) · log p(y) ]

注意看:分子就是互信息的原始值,分母是两个熵的和。这样写出来就比较直观了。

含义解读:NMI 到底在衡量什么?

你把 NMI 理解为“预测与真实标签之间的信息重叠率”。比如聚类结果和真实类别完全一致,那 NMI=1。如果随机猜,NMI≈0。你想想看——这比准确率公平多了,因为它不关心标签编号,只关心分布对齐。这就是为什么聚类评估里 NMI 这么受欢迎。

  • NMI=0:两个分布独立,没有任何共享信息
  • 0 < NMI < 1:部分共享,值越大越好
  • NMI=1:两个分布完全一致

嗯,这里要注意一点。NMI 不是完美指标。我遇到过这样的坑:当两个聚类结果都严重偏向某一类(比如全都归到一个类),NMI 反而会造假——变得很高。实际上模型根本没学到东西。这只是数据不平衡带来的假象。

避坑指南

我曾经踩过的一个坑: 有一次做社区检测的评估,NMI 得分 0.87,我差点就要庆祝了。后来发现真相——我的聚类只把 5 个社区合并成了 1 个超大社区,另一个是极小的残类,熵很低,导致分母小,算出来的 NMI 虚高。那之后,我再也不单独看 NMI,肯定会搭配 ARI 一起看。

另外,NMI 值对标签编码方式不敏感。这是它的优点——你把类别编号任意重排,结果不变。当然,这也是缺点,因为丢失了“标签顺序”信息。不过聚类评估本身就不在意这个。

图解 NMI 的核心逻辑

归一化互信息 NMI 核心逻辑 聚类标签 X 真实类别 Y 互信息 MI(X;Y) ΣΣ p·log(p/(p_x·p_y)) H(X) 熵 -Σ p_x log p_x H(Y) 熵 -Σ p_y log p_y 归一化 NMI = 2·MI / (H(X)+H(Y)) 输出 NMI ∈ [0, 1]

上面这张图展示了 NMI 的计算流程。你从左侧的聚类标签 X 和右侧的真实类别 Y 出发,分别计算熵,同时算出它们之间的互信息。最后把互信息用两个熵的平均值归一化,就得到了 [0,1] 之间的 NMI。整个过程其实就是一个“信息对齐”的度量。

一个简单的计算示例

假设有 6 个样本。我手算一下,你就清楚了。

样本 真实标签 Y 聚类标签 X
1 A 1
2 A 1
3 B 1
4 B 2
5 C 2
6 C 2

先算熵。

Y 的分布:A 有2个,B有2个,C有2个。所以 p(A)=1/3,p(B)=1/3,p(C)=1/3。

H(Y) = - 3 × (1/3) × log(1/3) 
     = 1.0986 (用自然对数时)

X 的分布:标签1有3个,标签2有3个。p(1)=1/2,p(2)=1/2。

H(X) = - 2 × (1/2) × log(1/2) = 0.6931

联合分布 p(x,y):

  • p(A,1)=2/6=1/3
  • p(B,1)=1/6
  • p(B,2)=1/6
  • p(C,2)=2/6=1/3
  • 其他组合为0,log里碰到0我通常忽略,不影响结果
MI = (1/3)·log((1/3)/((1/3)·(1/2))) 
     + (1/6)·log((1/6)/((1/3)·(1/2))) 
     + (1/6)·log((1/6)/((1/3)·(1/2))) 
     + (1/3)·log((1/3)/((1/3)·(1/2)))

   = (1/3)·log(2) + (1/6)·log(1) + (1/6)·log(1) + (1/3)·log(2)
   = (2/3)·log(2)
   ≈ 0.4621

最终 NMI:

NMI = 2 × 0.4621 / (1.0986 + 0.6931) 
    = 0.9242 / 1.7917 
    ≈ 0.5158

经验之谈: 手算一遍后,你会发现 NMI 对分布均匀性很敏感。上例中聚类标签把 A、C 都分对了,但 B 被切散,所以 NMI 只有 0.51。如果全部分对,NMI=1。嗯,这跟你的直觉其实是一致的。

小结一下

NMI 的数学推导不复杂,但想用好它,你得理解熵的意义——不确定性。而互信息就是“消除的不确定性”。归一化后它就变成了一个公平的标尺。我个人习惯在聚类评估报告里总是把 NMI、ARI、同质性和完整性放一起看,各有侧重,不能只看一个。