核心要点
在 batch 维度上对每个特征通道计算均值 μ 和方差 σ²。
归一化 (x−μ)/sqrt(σ²+eps),再用可学习的 γ、β 做缩放和平移。
训练用当前 batch 统计量,同时以动量更新滑动均值/方差供推理使用。
易错点:eps 防止除零;推理阶段必须用 running 统计量而非 batch 统计量。
标准回答
一、核心回答
可以先说核心:BatchNorm 是对每个特征,在 batch 维度上算均值和方差。
一、训练阶段怎么做?
对于形状 (N, D) 的输入,沿 axis=0 计算每个特征的 μ 和 σ²,然后做 (x−μ)/sqrt(σ²+eps)。eps 是为了防止除零。归一化后还要乘 γ、加 β,让模型能学回合适的尺度和偏移。
二、关键机制
二、推理阶段有什么不同?
训练时除了用当前 batch 的统计量,还要用 momentum 更新 running_mean 和 running_var。推理时不能再依赖当前 batch,尤其 batch_size=1 会非常不稳定,所以要用 running 统计量。
三、面试补充
三、代码里最容易错什么?
第一是归一化方向:这里是 batch 维 axis=0,不是特征维。第二是训练/推理分支要分清。第三是别忘了 eps 和 γ/β。下面给出对 (N, D) 输入的 NumPy 前向实现:
import numpy as np
def batchnorm_forward(x, gamma, beta, running_mean, running_var,
training=True, momentum=0.9, eps=1e-5):
# x: (N, D),沿 batch 维 (axis=0) 归一化每个特征
if training:
mu = x.mean(axis=0) # (D,) 批均值
var = x.var(axis=0) # (D,) 批方差(有偏)
x_hat = (x - mu) / np.sqrt(var + eps)
# 用动量更新滑动统计量,供推理使用
running_mean = momentum * running_mean + (1 - momentum) * mu
running_var = momentum * running_var + (1 - momentum) * var
else:
# 推理:使用训练阶段累积的全局统计量
x_hat = (x - running_mean) / np.sqrt(running_var + eps)
out = gamma * x_hat + beta # 缩放平移
return out, running_mean, running_var
if __name__ == '__main__':
np.random.seed(0)
x = np.random.randn(8, 4) * 5 + 3
D = x.shape[1]
out, rm, rv = batchnorm_forward(
x, np.ones(D), np.zeros(D), np.zeros(D), np.ones(D), training=True)
print(out.mean(axis=0).round(5), out.std(axis=0).round(3)) # ~0, ~1常见误区
⚠️ 常见踩坑
误区一:推理时还用当前 batch 的统计量。 batch_size 很小时会抖动,甚至方差接近 0。
误区二:把归一化维度写错。 BatchNorm 对 (N,D) 通常沿 axis=0 统计每个特征,不是沿特征维做。
🔗 相似问题
同一考点的不同问法,换着练更稳
没找到想看的面试题?把你想看的告诉我们 →
延伸学习
按主题分类的相关资源,便于系统复习
