数学补课 · 方差可加,标准差不可加:√n 是从哪来的
上一篇拿 σ_z = √n · σ_W · σ_x 定了 W1 该乘多少,但公式本身是借来就用的。
这一篇把它从期望的定义推出来,顺带回答一个更值钱的问题:
为什么诊断神经网络盯的是方差,而不是均值或最大值?
期望:一切的起点
期望就是平均值,记作 E[X] 或 μ。
X = [2, 4, 4, 4, 5, 5, 7, 9]E[X] = (2+4+4+4+5+5+7+9) / 8 = 5E[X²] 是「先每个数平方,再取平均」—— 顺序不能调,E[X²] ≠ (E[X])²。
这两个量的差正好是方差,下面就会看到。
A、B 是什么
下面公式里的 A、B、X 都是随机变量 —— 值不确定、但知道它服从什么分布的量。
它们是占位符,就像代数里的 x、y。在这个场景里它们具体代的是:
zⱼ = x₁w₁ + x₂w₂ + … + x₃₀w₃₀
| 符号 | 代什么 | 一次采样的值 |
|---|---|---|
X | 单个权重 wᵢ,或单个输入 xᵢ | 比如 −0.722 |
A | 和式里的第 1 项 x₁w₁ | 比如 −0.330 |
B | 第 2 项 x₂w₂ | 比如 −0.230 |
于是三条规则各自在回答一个具体问题:
| 规则 | 回答什么 |
|---|---|
Var(cX) | 把权重乘 0.2,方差变多少? |
Var(AB) | 单项 xᵢwᵢ 的方差是多少? |
Var(A+B) | 30 项加起来之后方差是多少? |
用字母而不用具体符号,是为了强调「只要独立且均值为 0,谁都行」。
写成 x₁w₁ 反而会让人以为只对这一项成立。
「随机变量」不意味着反复生成
这里容易搞混:随机变量像掷骰子 —— 掷之前不知道是几,但知道每面 1/6。
但这不是说训练时要反复生成 W。
W1 = torch.randn((30, 200)) # 只执行一次,6000 个数W1.std() # ≈ 1.0,直接就是 σ_WW1 只生成一次,之后一直用它、被梯度更新 —— 这就是正常的训练流程。
概率论用得上,是因为那一次生成里就已经有 6000 个来自同一分布的样本。
前向之后 z 有 32×200 = 6400 个值,天然就是「不同样本 × 不同神经元」的随机组合。
公式预测的就是这 6400 个数的散布程度:
z.std() # 4.916 ← 公式算的就是它一次生成、一次前向,公式就对上了。
σ_W 是分布参数,不是某个权重
| 是什么 | 例子 | |
|---|---|---|
w₁ | 一次采样的结果 | −0.722 |
σ_W | 它所来自的分布的标准差 | 1.0 |
单个确定的数没有方差 —— −0.722 就是 −0.722。而 σ_W 描述的是「这类数典型有多大」。
所以 σ_W 不带下标:30 个权重来自同一分布,共享同一个 σ_W。
这也是为什么后面求和号里的项没有下标。
两条性质后面反复要用:
E[A + B] = E[A] + E[B] 总是成立E[AB] = E[A] · E[B] 仅当 A、B 独立第二条的前提很重要,后面所有推导都靠它。
方差:散得多开
两组数,平均值都是 100:
A 组: 99, 100, 101, 100, 100 ← 挤在一起B 组: 40, 160, 100, 20, 180 ← 散得很开平均值一样,但明显不是一回事。方差就是量化这个差别的。
Var(X) = E[(X − μ)²]三步:每个数减平均值 → 平方 → 取平均。用 A 组算(μ = 100):
| 数 | 偏离 | 偏离² |
|---|---|---|
| 99 | −1 | 1 |
| 100 | 0 | 0 |
| 101 | +1 | 1 |
| 100 | 0 | 0 |
| 100 | 0 | 0 |
Var = (1+0+1+0+0) / 5 = 0.4 σ = √0.4 = 0.632为什么要平方
因为直接把偏离量相加会正负抵消:
E[X − μ] = E[X] − μ = 0 永远是 0平方让所有偏离变正数。取绝对值也行,但不可导,也没有下面那些好性质。
一个好用的等价形式
Var(X) = E[X²] − (E[X])²推导:E[(X−μ)²] = E[X² − 2μX + μ²] = E[X²] − 2μ² + μ² = E[X²] − μ²。
关键在于 μ 是个确定的数(平均值)而不是随机变量,所以能自由进出期望符号:
E[2μX] = 2μ·E[X] = 2μ²,E[μ²] = μ²。
当
μ = 0时,Var(X) = E[X²]。randn生成的数均值就是 0,后面的推导因此大幅简化 —— 这不是巧合,而是初始化故意选均值 0 的原因之一。
用 A 组对一遍:
E[X²] = (99² + 100² + 101² + 100² + 100²) / 5 = 10000.4(E[X])² = 100² = 10000Var = 10000.4 − 10000 = 0.4 ✅ 与定义式一致「先平方后平均」总是 ≥「先平均后平方」,所以方差永远非负;相等只在所有数都相同时成立。
标准差:开方回到同单位
σ = √Var(X)方差的单位是「原单位的平方」,没法直接跟数据比。标准差和原数据同单位,能直接读。
推导用方差,报告用标准差。 因为只有方差可加(下面第四节),标准差不可加。 这是两者分工的根本原因。
三条运算规则
规则 1:乘常数
Var(cX) = c² · Var(X) σ_cX = |c| · σ_X推导(设 μ = 0):E[(cX)²] = c²E[X²]。
这就是 W1 = randn(...) * 0.2 为什么能把标准差正好变成 0.2 —— 常数直接乘在标准差上,
不用试。上一篇那个「手调 0.2」不是试出来的,是算出来的。
规则 2:相加(需独立)
Var(A + B) = Var(A) + Var(B)推导(均值都为 0):
E[(A+B)²] = E[A²] + 2·E[AB] + E[B²] ↑ 独立时 = E[A]E[B] = 0 × 0 = 0交叉项消失是全部关键。 反例:A 跟自己完全相关时
Var(A + A) = Var(2A) = 4·Var(A) ≠ 2·Var(A)实测 Var(a+a) = 3.999,不是 2。
交叉项为什么会消失
E[AB] = 0 需要两个条件同时成立,各管一步:
| 步骤 | 用到的条件 | 得到 |
|---|---|---|
| ① 拆开乘积 | 独立 | E[AB] = E[A]E[B] |
| ② 两个因子都为 0 | 均值 0 | 0 × 0 = 0 |
直观上,E[AB] 就是协方差,衡量「A 和 B 有没有同涨同跌的倾向」。
独立时四种符号组合等概率,正负抵消:
| A | B | AB 的符号 | 概率 |
|---|---|---|---|
| + | + | + | 1/4 |
| + | − | − | 1/4 |
| − | + | − | 1/4 |
| − | − | + | 1/4 |
E[A] = 0 但 E[A²] ≠ 0,不矛盾
容易混的一点:既然 E[A] = 0,为什么展开式里的 E[A²] 不也是 0?
因为 A 有正有负,但 A² 永远非负。
| 量 | 值 | 为什么 |
|---|---|---|
E[A] | 0 | 正负抵消 |
E[AB] | 0 | 独立,四种符号等概率抵消 |
E[A²] | σ² > 0 | 平方后全正,无法抵消 |
一次方能抵消,平方不能。 而这正是方差存在的原因 —— 如果
E[A²]也是 0, 那方差就是 0,整个推导也就没意义了。展开式里消失的只有中间那项, 两边的平方项恰恰就是方差本身。
规则 3:相乘(需独立 + 均值 0)
Var(AB) = σ_A² · σ_B²推导:
Var(AB) = E[A²B²] − (E[AB])² = E[A²]E[B²] − (E[A]E[B])²均值为 0 时第二项为 0,而 E[A²] = σ_A²,所以结果是 σ_A²σ_B²。
均值不为 0 时公式复杂得多:Var(AB) = σ_A²σ_B² + σ_A²μ_B² + σ_B²μ_A²。
取 μ_A = 2、μ_B = 3、σ = 1 实测:理论 1 + 9 + 4 = 14,实测 13.991 ✅
三条规则一起验
a, b = torch.randn(2_000_000), torch.randn(2_000_000)
Var(a) = 0.9997 理论 1Var(3a) = 8.9969 理论 9 ← 规则 1Var(a+b) = 1.9985 理论 2 ← 规则 2Var(ab) = 1.0007 理论 1 ← 规则 3Var(a+a) = 3.9986 ← 不独立,规则 2 失效方差可加,标准差不可加
这是整套推导的枢纽。
| n 项相加后 | |
|---|---|
| 方差 | n σ²(线性) |
| 标准差 | √n σ(开方) |
正负会抵消,所以 30 个随机数相加的总幅度不是 30 倍而是 5.5 倍。
随机游走是同一回事:抛 100 次硬币,最后离原点大约 √100 = 10 步。
所以推导必须在方差空间做,最后再开方回到标准差。
上一篇那句「√n 来自方差相加、标准差开方」,全部内容就在这一节。
回到神经网络
一个神经元:zⱼ = Σᵢ xᵢwᵢⱼ,i 从 1 到 n。
第 1 步 —— 单项(规则 3)
Var(xᵢwᵢⱼ) = σ_x² σ_W²第 2 步 —— n 项相加(规则 2)
Var(zⱼ) = n · σ_x² σ_W²这一步里有两个等号,性质完全不同:
①
Var(Σ…) = ΣVar(…)—— Var 穿过求和号,是规则 2 反复用了 29 次,必须独立才成立 ②ΣVar(xᵢwᵢ) = Σσ_x²σ_W²—— 纯代数替换,把符号换成第 1 步的结果。 右边没有下标i,因为 30 项同分布、方差全相同最后「同一个常数加 n 遍 = 乘 n」。等号 ① 是全部推导里唯一有实质内容的一步。
验证等号 ①:
x = torch.randn(100000, 30) * 0.911W = torch.randn(30)terms = x * W # (100000, 30) 每一项
print("左边 Var(∑):", terms.sum(1).var().item())print("右边 ∑Var :", terms.var(0).sum().item()) # 两者几乎相同 ✅
# 不独立的反例a = torch.randn(100000)st = torch.stack([a, a], dim=1) # 两列完全相同print("左边:", st.sum(1).var().item()) # ≈ 4print("右边:", st.var(0).sum().item()) # ≈ 2 ❌ 不相等第 3 步 —— 开方
σ_z = √n · σ_W · σ_x实测对账:σ_x = 0.909,理论 √30 × 1 × 0.911 = 4.99,实测 4.958 ✅
两个假设
| 假设 | 在哪里用的 | 真的成立吗 |
|---|---|---|
xᵢ 与 wᵢⱼ 独立 | 规则 2、3 | 初始化时 ✅(W 是新抽的随机数) |
| 均值为 0 | 规则 3 | randn ✅ |
训练开始后两个假设都会逐渐失效:
W被梯度推动后不再独立于数据,均值也不再是 0。 所以这套公式只管第 0 步,管不了第 1000 步 —— 这正是 BatchNorm 存在的理由。
正态分布与预测饱和
大量独立的小因素叠加,结果趋向正态分布(中心极限定理)。
zⱼ 是 30 项相加,所以它必然近似正态 —— 不管 x 和 W 原本是什么分布。
| 范围 | 理论 | 实测(200 万样本) |
|---|---|---|
| ±1σ | 68.27% | 68.22% |
| ±2σ | 95.45% | 95.42% |
| ±3σ | 99.73% | 99.73% |
这条性质直接换来一个能力:知道 σ_z 就能算出多少神经元会死,不用跑模型。
预测 54.8%,上一篇实测 62.3%,同一量级。差值有两个来源:
|z| > 3 和 |h| > 0.99 不是同一条线,以及单次采样的波动。
反过来用更有意思:想让饱和低于 5%,就是在要求 3 至少有 2 个 σ 那么远,
也就是 σ_z ≲ 1.5。上一篇手调出来的 0.2 给出 σ_z ≈ 1.0 —— 落在这个区间里。
为什么诊断盯方差
| 指标 | 能不能用 | 为什么 |
|---|---|---|
| 均值 | ❌ | 初始化时永远是 0,没有区分度 |
| 最大值 | ❌ | 极值噪声大,一个异常值就带偏 |
| 饱和比例 | ⚠️ | 只对 tanh / sigmoid 有意义,ReLU 用不了 |
| 方差 / 标准差 | ✅ | 可逐层传递、有解析公式、不依赖激活函数 |
三个不可替代的性质:
① 可预测 —— 有解析公式,不用跑模型就能算出下一层会怎样。上面那张图就是它。
② 可逐层累乘 —— 每层的比值乘起来就是总效应。每层 1.5,十层后 57 倍(爆炸); 每层 0.5,十层后 0.001(消失)。这是上一篇「逐层 σ 比值 ≈ 1」那个指标的全部依据。
③ 反向也适用 —— 梯度反向传递遵循同一套规则,因为 ∂L/∂x = ∂L/∂z · Wᵀ 也是矩阵乘。
一个指标同时监控两个方向。
方差是唯一能把「初始化选多少」和「网络能堆多深」连起来的量。
回头看上一篇那几个指标,其实都是方差的不同面孔:
| 指标 | 本质上在看 |
|---|---|
| 初始 loss = ln|V| | logits 的方差够不够小 |
| 饱和 < 5% | z 的方差合不合适 |
| 逐层 σ 比值 ≈ 1 | 方差有没有逐层漂移 |
| grad/data ≈ 1e-3 | 梯度方差与参数方差的比 |
两个实用细节
n 还是 n−1
| 除以 | 叫什么 | 什么时候用 |
|---|---|---|
| n | 总体方差 | 手上就是全部数据 |
| n−1 | 样本方差(无偏) | 用样本估计总体 |
t.std() # 默认 correction=1,除 n−1t.std(correction=0) # 除 n元素成千上万时两者差别可忽略,不必纠结。
「矩阵的方差」是什么
W1.shape # (30, 200)W1.std() # 把 6000 个元素当一堆数算,≈ 1.0W1[:, 0].std() # 第 0 列,也 ≈ 1.0W1[0, :].std() # 第 0 行,也 ≈ 1.0因为 randn 是逐元素独立生成的,怎么切都一样。
「矩阵的标准差」是个偷懒的说法,真正的意思是**「每个元素所服从的分布的标准差」**。
自己验证
import torcha, b = torch.randn(1_000_000), torch.randn(1_000_000)
# 三条规则print((3*a).var(), ' 理论 9')print((a+b).var(), ' 理论 2')print((a*b).var(), ' 理论 1')print((a+a).var(), ' ← 不独立,是 4 不是 2')
# √n 增长for n in [1, 4, 30, 200]: s = torch.randn(200000, n).sum(1) print(f'n={n:<4} std={s.std():.3f} √n={n**0.5:.3f}')
# 神经网络场景x = torch.randn(5000, 30) * 0.911W = torch.randn(30, 200)print(f'σ_z 实测 {(x@W).std():.3f} 理论 {(30**0.5)*0.911:.3f}')
# 68/95/99.7u = torch.randn(1_000_000)for k in [1, 2, 3]: print(f'±{k}σ 内 {100*(u.abs()<k).float().mean():.2f}%')这一篇没有引入任何新机制,只是把上一篇借来就用的那个公式还清:
√n 来自「方差可加、标准差不可加」,而方差可加又来自「独立时交叉项消失」。
一路追到底,全部内容就是 E[AB] = E[A]E[B] 那一个条件。
也正因为它靠这个条件,训练一开始它就开始失效 —— W 被梯度推动之后不再独立于数据。
下一段讲 BatchNorm,它的思路是与其算准,不如强制:每层输出直接减均值除标准差,
不管前面发生了什么。