1. 一个被教材“易证”带过、实际却暗藏权重陷阱的恒等式
早年讲机器学习里的LDA(线性判别分析)时,我习惯直接在白板上写下这个式子:
[ \text{Total Variance}=\text{Within-Class Variance}+\text{Between-Class Variance} ]
然后给一句话总结:“每个样本与总体均值的偏差,可以拆成它与所属类均值的偏差,加上类均值与总体均值的偏差,交叉项求和为零,证毕。”
直到有个学生课后追着我问:“老师,交叉项为什么一定为零?如果我把类间方差里的权重 n_k 改成别的权重,比如等权重 1/K,式子还成立吗?你上课写的那个‘总方差=类内方差+类间方差’到底是在除以 N 还是除以 N-1 的前提下成立的?”
这三个问题,一个比一个尖锐,也把我不小心省略的细节全部逼了出来。确实,教材里这块基本都是“易证”“显然”,但真正推一遍就会发现:这个恒等式不是简单的代数展开,它藏着加权机制、自由度约定、以及“平方和分解”与“方差分解”之间的微妙差别。
如果你只在算法里调用现成的 LDA 或 Otsu 阈值,可能一辈子不需要手推它;但如果你想理解这些算法的判别依据,或者自己去实现一版聚类评价指标,这个证明迟早要补上。这篇文章我打算把它从头到尾拆开,包括那些容易踩的坑。
2. 证明前必须锁死的符号约定:平方和分解才是核心
2.1 从“偏差平方和”开始,而不是从“方差”开始
设我们有一共 N 个样本,被划分成 K 个类。第 k 类有 n_k 个样本,满足:
[ \sum_{k=1}^{K} n_k = N ]
把第 k 类里的第 i 个样本记为 x_{ki}。定义三类均值:
- 第 k 类类均值:(\mu_k = \frac{1}{n_k}\sum_{i=1}^{n_k} x_{ki})
- 总体均值:(\mu = \frac{1}{N}\sum_{k=1}^{K}\sum_{i=1}^{n_k} x_{ki})
然后定义三个偏差平方和:
[ TSS=\sum_{k=1}^{K}\sum_{i=1}^{n_k}(x_{ki}-\mu)^2 ]
[ WSS=\sum_{k=1}^{K}\sum_{i=1}^{n_k}(x_{ki}-\mu_k)^2 ]
[ BSS=\sum_{k=1}^{K}n_k(\mu_k-\mu)^2 ]
这三个量通常被叫做总平方和(Total Sum of Squares)、组内平方和(Within Sum of Squares)、组间平方和(Between Sum of Squares)。教科书上说的“总方差等于类内方差加类间方差”,严格来说,第一层要证明的是:
[ TSS = WSS + BSS ]
这个式子才是整个关系的根。方差、均方差、二乘误差那一堆概念,都是在这个平方和等式基础上,再除以不同分母得到的。
2.2 为什么先证明平方和版本?
因为“方差”在不同场景下有不同口径,而“平方和”没有歧义。同一个平方和等式,除以 N 可以被解释成“总体方差分解”,除以 N-1 则对应“样本方差分解”,但此时自由度问题会让“类内方差+类间方差=总方差”这句话变得不再严格成立。
后面我会细讲这个坑。现在先把平方和版本的证明做扎实。
2.3 一组具体的数字,便于后面核对
空对空推导容易飘。我先摆一组小数据,后面每一步都可以拿它验证。
A 类:{1, 2, 4},B 类:{7, 8, 9}。这里 N=6,K=2,n_1=n_2=3。
- A 类均值:(\mu_1 = (1+2+4)/3 = 7/3 \approx 2.333)
- B 类均值:(\mu_2 = (7+8+9)/3 = 8)
- 总体均值:(\mu = (1+2+4+7+8+9)/6 = 31/6 \approx 5.167)
算一下三个平方和:
A 类内偏差平方和:
[ (1-\frac{7}{3})^2+(2-\frac{7}{3})^2+(4-\frac{7}{3})^2 =\frac{16}{9}+\frac{1}{9}+\frac{25}{9} =\frac{42}{9} =\frac{14}{3} ]
B 类内偏差平方和:
[ (7-8)^2+(8-8)^2+(9-8)^2=1+0+1=2 ]
所以 (WSS=\frac{14}{3}+2=\frac{20}{3}\approx6.667)。
组间平方和:
[ BSS=3(\frac{7}{3}-\frac{31}{6})^2+3(8-\frac{31}{6})^2 =3(-\frac{17}{6})^2+3(\frac{17}{6})^2 =3\times\frac{289}{36}+3\times\frac{289}{36} =\frac{289}{6}\approx48.167 ]
总平方和:
[ TSS=(1-\frac{31}{6})^2+(2-\frac{31}{6})^2+(4-\frac{31}{6})^2+(7-\frac{31}{6})^2+(8-\frac{31}{6})^2+(9-\frac{31}{6})^2 ]
算出来约等于 54.833,也就是 (20/3 + 289/6 = 329/6)。正好 (WSS+BSS = 20/3 + 289/6 = 40/6 + 289/6 = 329/6 = TSS)。
数字验证通过,接下来看通用证明。
3. 核心推导:把每个偏差拆成两块,再让交叉项归零
3.1 所有证明的关键一步:偏差分解
对于任意一个样本 (x_{ki}),我们做一个恒等变形:
[ x_{ki}-\mu = (x_{ki}-\mu_k)+(\mu_k-\mu) ]
这是一个纯粹的代数操作,在任何情况下都成立。第一项描述这个样本偏离“自己所属类均值”的程度,第二项描述“这个类的均值”偏离“总体均值”的程度。一个样本的离群程度,就这样被拆成了“局部偏差”和“类别偏差”两部分。
接下来对平方展开:
[ (x_{ki}-\mu)^2=(x_{ki}-\mu_k)^2+2(x_{ki}-\mu_k)(\mu_k-\mu)+(\mu_k-\mu)^2 ]
对 k 和 i 求和,得到:
[ TSS=\sum_{k=1}^{K}\sum_{i=1}^{n_k}(x_{ki}-\mu_k)^2 +2\sum_{k=1}^{K}\sum_{i=1}^{n_k}(x_{ki}-\mu_k)(\mu_k-\mu) +\sum_{k=1}^{K}\sum_{i=1}^{n_k}(\mu_k-\mu)^2 ]
第一项就是 WSS,第三项因为是同一个类内的常数求和,所以等于:
[ \sum_{k=1}^{K}n_k(\mu_k-\mu)^2=BSS ]
所以证明的重心落在了中间这一项上。
3.2 交叉项为什么是零:关键在于类均值的定义
中间项,我们先把对 i 的求和拿到前面:
[ 2\sum_{k=1}^{K}(\mu_k-\mu)\sum_{i=1}^{n_k}(x_{ki}-\mu_k) ]
现在盯住里面的内层求和:
[ \sum_{i=1}^{n_k}(x_{ki}-\mu_k) ]
这个式子等于多少?把它展开:
[ \sum_{i=1}^{n_k}x_{ki}-\sum_{i=1}^{n_k}\mu_k =\sum_{i=1}^{n_k}x_{ki}-n_k\mu_k ]
而 (\mu_k) 的定义是 ( \frac{1}{n_k}\sum_{i=1}^{n_k}x_{ki}),所以 (n_k\mu_k=\sum_{i=1}^{n_k}x_{ki})。于是:
[ \sum_{i=1}^{n_k}x_{ki}-n_k\mu_k=0 ]
也就是说,任何一类样本对自身类均值的偏差之和,天然为零。这不是巧合,而是“均值”的本质属性:均值是让偏差和为零的那个点。
我们也可以换一种说法:第 k 类的样本均值 (\mu_k),是第 k 类样本平方偏差和的最小值点。这个性质在后面的几何解释里还会再次出现。
3.3 完整写出证明
现在把交叉项为零代回去:
[ TSS=\sum_{k=1}^{K}\sum_{i=1}^{n_k}(x_{ki}-\mu_k)^2+0+\sum_{k=1}^{K}n_k(\mu_k-\mu)^2 ]
即:
[ TSS=WSS+BSS ]
证明完毕。从平方和到方差:如果定义总体方差为 (TSS/N),定义“类内方差”为各组内方差的样本量加权平均,即:
[ \sigma_W^2=\frac{WSS}{N}=\frac{1}{N}\sum_{k=1}^{K}\sum_{i=1}^{n_k}(x_{ki}-\mu_k)^2 ]
定义“类间方差”为:
[ \sigma_B^2=\frac{BSS}{N}=\frac{1}{N}\sum_{k=1}^{K}n_k(\mu_k-\mu)^2 ]
那么自然有:
[ \sigma_{Total}^2=\frac{TSS}{N}=\sigma_W^2+\sigma_B^2 ]
这个形式,就是教材里最常见的那句话。注意,这里的“类内方差”前面没有任何额外的类数归一化,它就是所有样本的组内偏差平方和平均到了每个样本头上;而“类间方差”也是带 (n_k) 权重、再平均到每个样本头上的加权均值的离散程度。
3.4 学生追问的那个问题:把权重改成 1/K 还成立吗?
这是最容易理解错的地方。假如我们不按 (n_k) 加权,而是把组间平方和定义成:
[ BSS_{unweighted}=\sum_{k=1}^{K}(\mu_k-\mu)^2 ]
也就是每个类别均值到总体均值距离的简单求和,那还能保证 TSS=WSS+BSS 吗?答案是不能,除非每一类样本量刚好相等。
我用上面的数据做个反例。还是 A、B 两组,总体均值 31/6。
- 加权 BSS 是 (3(7/3-31/6)^2+3(8-31/6)^2=289/6)
- 不加权 BSS 是 ((7/3-31/6)^2+(8-31/6)^2=2\times 289/36=289/18)
两者差了三倍,而 WSS 不变,依然是 20/3。显然:
[ \frac{20}{3}+\frac{289}{18}=\frac{120}{18}+\frac{289}{18}=\frac{409}{18}\approx22.72 ]
远小于 TSS=329/6≈54.83。所以“类间方差”的权重绝不是拍脑袋定的,它是从平方和分解的代数结构里自然逼出来的:每个样本贡献一个单位权重,组间距离必须乘以该组样本数,才能与组内平方和的尺度对齐。
4. 为什么交叉项必然为零:从代数到几何直觉
4.1 残差与拟合值的正交
刚才的证明在代数上已经完整了,但它有一个缺点:算完就完了,缺少一种“就该如此”的直觉。这里我给出一个从回归视角看问题的角度。
每个样本可以看作一个观测值,我们把所属类别的均值 (\mu_k) 视为对 (x_{ki}) 的一个预测。这样一来:
- (x_{ki}-\mu_k) 是残差;
- (\mu_k-\mu) 是预测值相对总体均值的高出部分;
- 总偏差 (x_{ki}-\mu=\text{残差}+\text{预测高出部分})。
上面证明的交叉项为零,本质上是说:这个“预测高出部分”与“残差”在所有样本上的内积为零。这在统计学里叫残差与拟合值正交,或者叫均值是最小二乘估计的自然推论。因为每一类内部的预测值是一个常数,残差在该类内的和为零,那么残差与任何“仅仅随类变化、在类内恒定”的向量内积,都必然为零。
一个直观类比是:把 I 型误差和第二类错误(可解释的类别差异)看成一组互相垂直的向量。总平方和就是直角三角形的斜边,类内平方和与类间平方和是两条直角边。毕达哥拉斯定理当然不会因为“类之间差距大”就失效,它本来就是勾股定理在平方和空间里的投影版本。
4.2 二维平面里的物理图景
假设我们把所有样本按照“类别”涂成两堆点,在数轴上排列。每个点离总体均值的距离平方,构成总平方和。现在我们把这条数轴上的总体均值当成一个坐标原点,所有点的位置由两类信息决定:
- 各堆点自己的质心离总体质心有多远;
- 各堆内部的点离自己的质心有多远。
总平方和等于两部分平方和的叠加。这跟物理学里“转动惯量可以用质心项 + 相对质心项表达”的结构几乎一模一样。你不可能通过把点往左右推开而不改变“点到总体中心”的总平方和——推开后组间平方和上升的代价,正是组内平方和不变的情况下,总平方和必然等量上升。
这一点也解释了一个常见直觉误区:有人会以为“类间方差大”就意味着“组内方差小”。从分解式看,总方差固定时,两者的确此消彼长;但现实数据里总方差并不固定,类间方差变大、类内方差也跟着变大的情况比比皆是,毕竟总体方差本身也在变。
4.3 为什么必须是“组内平方和最小”,而不是“组内方差自由定义”
为了把证明链条延伸到方差,我还想强调一个自由度的坑。
在很多编程实现里,我们倾向于用无偏估计的方差,即除以 (n_k-1) 来估计某个类的类内方差。这种估计类内方差的公式是:
[ s_k^2=\frac{1}{n_k-1}\sum_{i=1}^{n_k}(x_{ki}-\mu_k)^2 ]
这时候如果直接把这个 (s_k^2) 按照样本量加权平均,再与某个类间方差相加,试图等于总方差,一般对不上。因为无偏方差把自由度差异揉进去了。真正的“总方差分解”链条是建立在“平方和除以 N”的总体口径下,不是建立在“第 k 类除以 n_k-1”的抽样口径下。
具体说,如果你按下面的方式定义:
[ \frac{1}{N}\sum (x-\mu)^2 = \frac{1}{N}\sum n_k s_k^2 + \text{组间项} ]
即使最后一项用 BSS/N,等式也不成立,除非你用 (n_k\sigma_k^2) 而不是 ((n_k-1)s_k^2) 去回填。这是非常常见的代码级 bug 来源,后面专门再写一节排查思路。
5. 这个恒等式在 LDA、Otsu 阈值和聚类评估里的真实用途
5.1 LDA/Fisher 判别:为什么“类间方差/类内方差”有判别力
线性判别分析的目标,是寻找一个线性投影方向,让投影后不同类别尽可能分开。Fisher 准则写作:
[ J=\frac{\text{类间方差}}{\text{类内方差}} ]
利用本文的恒等式,在总方差固定时,最大化 (J) 等价于最小化类内方差,也等价于最大化类间方差。很多人初学时觉得这是三个不同的目标,实际上在总方差不变的约束下,它们是同一个优化问题的三个侧面。
我在工程里见过一种错误做法:拿到数据先标准化,然后就拿原始特征的类内方差去比较大小,忽略了总方差也可能在变化。比如类别 A 的特征范围本身比类别 B 大很多,标准化之后类内方差的变化被扭曲,Fisher 准则的解释力会明显下降。
5.2 Otsu 阈值法:最大类间方差与最小类内方差的一体两面
图像处理里的 Otsu 二值化算法,本质上是在找一个灰度阈值 (t),把像素分成前景和背景两类。对这个阈值下的类间方差:
[ \sigma_B^2(t)=p_1(t)(\mu_1(t)-\mu)^2+p_2(t)(\mu_2(t)-\mu)^2 ]
进行最大化。因为对于一幅给定图像,总方差 (TSS/N) 是不变的(它不随阈值变化),而:
[ \sigma_{Total}^2 = \sigma_W^2(t)+\sigma_B^2(t) ]
所以最大化 (\sigma_B^2(t)) 完全等价于最小化 (\sigma_W^2(t))。这也是为什么 Otsu 算法能用一个公式同时概括“最大化类间距离”和“最小化类内聚集”两种直观说法。
实际实现时,类间方差可以用累积直方图和一阶累积量在线性时间内算出来,不需要对每个阈值都重新遍历所有像素。这正是利用了 (\sigma_B^2) 只依赖每个类的样本占比和类均值的特性,而这两个统计量都可以通过前缀和快速更新。
5.3 聚类评估里的“解释方差占比”
聚类任务中,轮廓系数之外还有一个非常常用的指标叫“解释方差占比”(explained variance),定义是:
[ \frac{BSS}{TSS}=1-\frac{WSS}{TSS} ]
它衡量的是:聚类出的类别结构能够解释总方差的百分比。根据本文证明的恒等式,这个指标既可以解释为“类别差异占总差异的比例”,也可以解释为“组内散度被降低到只剩多少”。KMeans 算法里的 inertia(组内平方和)之所以被拿来对比不同 K 值,本质上就是因为 TSS 固定时,WSS 越小,解释方差占比越高。
但这里有个必须警惕的陷阱:KMeans 用欧氏距离,所以它最小化的 WSS 和本文定义一致。如果你改用曼哈顿距离或余弦距离,那组内“方差”与总“方差”不再满足平方和分解关系,直接套用这个指标会得到误导性结果。类似的坑发生在对稀疏高维文本向量做聚类时,很多人还拿“解释方差占比”比较模型,但此时距离度量已经违背了平方和分解的前提。
5.4 实战排查:当“总方差=类内方差+类间方差”对不上时从哪查
如果你在自己实现聚类评估或 LDA 相关代码时发现两边对不上,按下面的顺序排查通常能快速定位。
| 排查步骤 | 检查点 | 出错后果 |
|---|---|---|
| 1 | 是否把均值算成了向量均值而非标量均值 | 交叉项不再为零,原本的正交性被破坏 |
| 2 | 类间平方和中的权重是否用的是 n_k 而非 1 | 权重不匹配,TSS 被低估或高估 |
| 3 | 组内平方和计算时是否误用了无偏方差的 (n_k - 1) | 两边自由度口径不同,数值对不上 |
| 4 | 分母用的是 N 还是 N-1 | 方差版的恒等式在 N-1 口径下不再保持简单相加形式 |
| 5 | 是否把多列特征的总方差分成了逐列求和 | 如果协方差矩阵有非对角项,必须先做整体迹分解 |
最后再提一个我自己的排查经验:当你手动核验方差分解时,先把两边都换成平方和,用“TSS 是否等于 WSS 加 BSS”来验证。这比直接比较方差表达更干净,因为它绕开了分母和自由度问题。确认平方和层面没问题之后,再去定义自己想要的方差口径。颠倒这个顺序,容易在自由度问题上绕很久。
我在实际实现中,每次处理涉及方差分解的反馈逻辑,都会先在拿到的数据上跑一个最小的手工验证——就像这篇文章开头那组 {1,2,4} 和 {7,8,9} ——确认公式完全对得上再往下写业务代码。实践证明,这一步省下来的排错时间,远大于那几行临时验证代码的成本。