吴恩达深度学习课程二: 改善深层神经网络 第三周:超参数调整,批量标准化和编程框架(二)batch归一化

此分类用于记录吴恩达深度学习课程的学习笔记。
课程相关信息链接如下:

  1. 原课程视频链接:[双语字幕]吴恩达深度学习deeplearning.ai
  2. github课程资料,含课件与笔记:吴恩达深度学习教学资料
  3. 课程配套练习(中英)与答案:吴恩达深度学习课后习题与答案

本篇为第二课的第三周内容,3.43.7的内容。


本周为第二课的第三周内容,你会发现这周的题目很长,实际上,作为第二课的最后一周内容,这一周是对基础部分的最后补充。
在整个第一课和第二课部分,我们会了解到最基本的全连接神经网络的基本结构和一个完整的模型训练,验证的各个部分。
之后几课就会进行更多的实践和进阶内容介绍,从“通用”走向“特化”。
总的来说这周的难度不高,但也有需要理解的内容,我仍会在相对较难理解的部分增加更多基础和例子,以及最后的“人话版”总结。
本篇的内容关于batch归一化,是本周中相对较难的理解内容,为了保证完整,所以本篇的篇幅也较长,但更多是一些基础,实例和与之前内容的联系。
同样,我也在最后增加了总结部分。

1. 什么是Batch 归一化?

先上概念:

Batch Normalization(简称 BN)是深度学习中非常常用的一种技巧,它能加速模型训练、稳定梯度、减少对初始化的依赖、提高网络收敛速度

简单概括一下它的核心思想: 把对输入的归一化逻辑应用在隐藏层,不让每一层的输入“乱七八糟”,让它们始终保持在一个更稳定、可控的分布中。

我们先从之前学过的“输入归一化”开始讲起。

1.1 对输入的归一化

我们之前已经了解过,对于原始的数据集,我们常发现这样一个问题:
不同维度的特征常常有不同的数值范围,导致训练过程难以协调。

举个例子——假设我们要训练一个分类模型,有这样一些输入特征:

特征数值范围
年龄0–100
收入0–1,000,000
身高150–190

而如果不归一化,模型在训练时梯度会被“收入”这种巨大数值主导,而“年龄、身高”被淹没。
这时,归一化让所有特征摆在一个“公平”的平台上训练,让模型更容易学习。
如果你有些忘了它的具体概念,可以在这里回顾:归一化

1.2 内部协变量偏移(Internal Covariate Shift)

现在我们已经解决了原始数据的量纲差距问题。
你可能会问: “用输入归一化解决了输入的混乱后,那在传播中不会出现任何问题了吗?”

答案当然是否定的,我们在之前的RMSprop权重初始化部分也提到过下面这种现象:
在传播过程中随着训练进行:

  • 前一层的权重不断更新
  • 这一层的输出分布不断改变
  • 下一层看到的“输入分布”也在不断漂移
    这次就来给这种现象专门下一个定义,这种现象被称为:内部协变量偏移(Internal Covariate Shift,ICS)
    即:随着训练进行,每一层的输入分布不断变化。

我们举一个实例:
假设现在有一个两层的神经网络,它的传播是这样的:

输入 → 第 1 层 → a¹ → 第 2 层

很明显,第 2 层把 当作“输入”。
现在,我们简单设定一次训练过程如下:

阶段第 1 层权重状态a¹(第 1 层输出)的分布变化对第 2 层的影响
第一次迭代权重较小均值 ≈ 0 ; 方差 ≈ 1 ; ReLU 截断部分为 0输入分布正常、稳定
训练若干轮后权重变大整体被放大 ; 均值从 0 → 3 ; 方差从 1 → 10 ; 分布向右漂移输入突然变大,“味道变了”
再训练一段时间权重更新向反方向变化a¹ 又变小 ; 均值回到 0 附近 ; 方差变窄 ; 更多值被 ReLU 截成 0输入再次变化,第二层需重新适应

你会发现:前一层一更新,后一层的输入分布就跟着跳。
假如今天均值 0,明天均值 3,后天又掉回 0,那下一层永远在“适应新分布”,就会导致训练不稳定、收敛变慢。
这就是内部协变量偏移现象。
我们再详细展开区分一下。

1.3 输入层的”混乱“和隐藏层的”混乱“

了解了内部协变量偏移后,你就会发现导致输入层混乱和隐藏层混乱的原因并不一样,我们总结如下:

位置混乱的根本原因产生机制典型表现解决方法
输入层(原始数据)量纲差距不同特征本身的单位、范围差异过大,如 0.001 vs 1000梯度被大尺度特征主导,优化方向不准输入归一化(标准化/MinMax)
隐藏层(传播过程中)内部协变量偏移前一层权重每次更新 → 输出分布随训练不断改变输入分布一会儿大、一会儿小,下一层每轮都要重新适应Batch Normalization(BN)

现在,我们希望:隐藏层也像输入数据一样保持稳定的分布,不要每训练一个 batch,分布就偏移。
这就是 Batch Normalization 被提出的原因。

2. Batch 归一化的步骤

既然我们已经知道隐藏层混乱的根源是 内部协变量偏移(ICS),那么解决方式就非常自然了:
既然输入层靠归一化稳定了输入特征分布,那隐藏层是否也能对自己的“输入”——也就是 \(z\)——进行归一化?
这就是BN 的核心思想:对每一层的 \(z\) 做归一化,使分布保持稳定
在一层网络的前向传播中,我们计算:

\[z = W·x + b \]

\[a = f(z) \]

而隐藏层真正的“输入”是 \(z\),而不是原始输入 \(x\)
因此 BN 会在激活函数 \(a\) 之前,对 \(z\) 进行“batch 内归一化”。
流程如下:

z → 归一化后的 ẑ → 激活函数 → a

现在,我相信你有一个小问题:

2.1 为什么对 \(z\) 做归一化而不是激活后的 \(a\) ?

你会发现,要是按照输入层的逻辑,那经过激活函数后的 \(a\) 不才是下一层的输入吗?为什么我们反而选择了更靠前的 \(z\) 来进行归一化?
实际上,激活函数(如 ReLU、sigmoid、tanh)会对分布进行非线性变换:

  • ReLU 截断负值
  • Sigmoid 压缩到 0~1
  • tanh 压缩到 -1~1
    如果在 \(a\) 上归一化,分布可能已经被截断或拉伸,归一化效果不稳定
    而对 \(z\) 归一化,可以在激活函数之前就稳定每一层的输入分布。

这么说不太清晰,我们来看下面这个实例:
假设某一层神经网络的线性输出 \(z\) 和激活输出 \(a\) 如下:
对某个 batch,第一个神经元的 \(z\) 值范围大致为:

样本\(z\)
1-2
20.5
31.5
43

激活函数采用 ReLU,则 \(a = f(z) = \max(0, z)\)

样本\(a\)
10
20.5
31.5
43

现在考虑归一化:

(1) 在 \(a\) 上归一化

  • 均值 \(\mu_a = (0+0.5+1.5+3)/4 = 1.25\)
  • 方差 \(\sigma_a^2 = [(0-1.25)^2 + (0.5-1.25)^2 + (1.5-1.25)^2 + (3-1.25)^2]/4 = 1.4375\)

归一化后:

\[\hat{a}_i = \frac{a_i - \mu_a}{\sqrt{\sigma_a^2 + \epsilon}} \]

结果:

样本\(\hat{a}_i\)
1-1.04
2-0.63
30.21
41.46

好像没什么不对,但是注意:样本 1 的负值对应原本被 ReLU 截断的 0
这样归一化会产生负数,实际上 破坏了 ReLU 的非负性特性,而且分布已经被截断过,归一化效果不稳定。

(2) 在 \(z\) 上归一化

  • 均值 \(\mu_z = (-2+0.5+1.5+3)/4 = 0.75\)
  • 方差 \(\sigma_z^2 = [(-2-0.75)^2 + (0.5-0.75)^2 + (1.5-0.75)^2 + (3-0.75)^2]/4 = 3.1875\)
    归一化后:
样本\(\hat{z}_i\)
1-1.57
2-0.14
30.42
41.28

然后再经过 ReLU 激活:

样本\(a_i = \max(0, \hat{z}_i)\)
10
20
30.42
41.28

这次,归一化是在 未被截断的原始分布 z 上进行,保持了统计特性,激活函数再作用不会破坏归一化效果,从而稳定下一层输入分布。
这就是 BN 选择对 \(z\) 而非 \(a\) 归一化的直观原因。
现在就来看看它的具体步骤。

2.2 步骤一:对当前 batch 的 \(z\) 做均值与方差归一化

对一个 batch 中每一维特征 \(j\),BN 首先计算 均值方差

\[\mu_j = \frac{1}{m} \sum_{i=1}^{m} z_{i,j} \]

其中,\(z_{i,j}\) 就是\(i\) 个样本在第 \(j\) 个神经元的线性输出

\[\sigma_j^2 = \frac{1}{m} \sum_{i=1}^{m} (z_{i,j} - \mu_j)^2 \]

这些公式我们都不陌生,现在我们进行归一化:

\[\hat{z}_{i,j} = \frac{z_{i,j} - \mu_j}{\sqrt{\sigma_j^2 + \epsilon}} \]

同样这里的 \(\epsilon\) 是一个很小的数,用于防止分母为 0。

  • 经过这一步,每一层输入 \(z\) 的分布被强制变为 均值 0,方差 1
  • 下一层就不必再每天适应漂移的输入分布

可是这时又会出现一些新的问题:
我们把每层输出都变成均值 0、方差 1 了,那原来大的还是大、小的还是小的这种差别不就没了?
比如我房价模型前一层算出来的结果本来是几百万,现在突然全变成 0.x、1.x 这种小数字——这不把信息压扁了吗?
BN 的标准化就像把所有输入“洗白”一遍,让它们都变成整整齐齐的统一格式。
可麻烦就在这:有些重要的特征确实需要保留原本的大数值尺度。
为了不让网络被限制住,设计者在 BN 之后又贴心地加了两个调节旋钮:

  • 一个让它可以重新把输出“放大或缩小”(γ)
  • 一个让它可以把分布“往上或往下挪动”(β)

这样网络就能在“被清洗干净”后重新找回它想要的表达方式。
这就是下一步——γ 和 β 的由来。

2.3 步骤二:引入可学习的缩放和平移(γ, β)

你可能会想:BN 把每一层的输出都强制归一化,难道不是把数值压扁了吗?比如预测房价,原本几百万的输出可能被缩到 -1~1。

为了避免这种情况,BN 引入了 γ(scale)β(shift) 这两个“调节阀”:

  • γ(scale):负责把归一化后的值按需要放大或缩小
    • 默认值 1,初始不放大也不缩小
  • β(shift):负责把均值移动到合适的位置
    • 默认值 0,初始不平移
      公式如下:

\[z_{i,j}^\text{BN} = \gamma_j \hat{z}_{i,j} + \beta_j \]

这里 \(\hat{z}_{i,j}\) 是归一化后、零均值单位方差的值,γ、β 会把它调整到网络需要的尺度。
要强调一点,我们可以手动设置 γ 和 β 的初始值。
但在实际运行中,每一层的γ 和 β 会和权重,偏置一样在反向传播中更新,通过梯度下降不断调整,让网络学会最合适的缩放和偏移。
最终:

  • γ 学会放大或缩小归一化后的 z,使输出幅度适合网络
  • β 学会平移分布,使输出均值符合任务需求

好像有些模糊,我们通过一个实例来演示一下。

2.4 加入 BN 后的一次传播实例

为了更直观地理解 BN 的实际作用,我们用一个例子来展示一次完整的前向传播。
假设现在有一层神经网络,它的线性输出 \(z = Wx + b\) 对某个 batch 的 4 个样本分别是:

样本\(z\)
1-2
20
32
46

下面我们逐步演示加入BN后的一次传播中每一环节。

(1) 计算 batch 内的均值与方差

先算均值:

\[\mu = (-2 + 0 + 2 + 6)/4 = 1.5 \]

再算方差:

\[\sigma^2 = \frac{(-2-1.5)^2 + (0-1.5)^2 + (2-1.5)^2 + (6-1.5)^2}{4} \]

计算过程如下:

样本\(z_i\)\(z_i - \mu\)\((z_i - \mu)^2\)
1-2-3.512.25
20-1.52.25
320.50.25
464.520.25

方差:

\[\sigma^2 = \frac{12.25+2.25+0.25+20.25}{4}=8.25 \]

(1)对 z 做归一化

再看一下归一化公式:

\[\hat{z}_i = \frac{z_i - \mu}{\sqrt{\sigma^2 + \epsilon}} \]

为了演示,我们暂时忽略 \(\epsilon\),一来它只是为了防止除0而存在,二来它的量级过小,几乎不会有影响。

\[\sqrt{8.25} \approx 2.872 \]

于是:

样本归一化后 \(\hat{z}_i\)
1-3.5 / 2.872 ≈ -1.22
2-1.5 / 2.872 ≈ -0.52
30.5 / 2.872 ≈ 0.17
44.5 / 2.872 ≈ 1.57

到这里,已经符合 BN 的目标:

  • 均值 = 0
  • 方差 = 1
    但如此一来,不同神经元之间的差异是否被压扁了?
    这就是 γ 和 β 的作用要登场了。

(3)用 γ、β 调整回网络想要的尺度

再次更新BN的输出公式 :

\[z_i^\text{BN} = \gamma \hat{z}_i + \beta \]

现在假设当前这一层的初始参数为:

  • \(\gamma = 2\)
  • \(\beta = 1\)

再次强调,我们我们可以手动设置 γ 和 β 的初始值,这里是为了展示效果。
但在实际训练中,我们通常会用默认的1和0,因为它们会在训练中自动更新成更合适的数值,我们很难提前探知这个“合适的值”,这也是深度学习模型解释性弱的体现之一。

继续计算:

样本\(\hat{z}_i\)\(z^\text{BN}_i = 2\hat{z}_i + 1\)
1-1.222×(-1.22)+1 = -1.44
2-0.522×(-0.52)+1 = -0.04
30.172×0.17 + 1 = 1.34
41.572×1.57 + 1 = 4.14

现在你就会看到:

  • BN 让分布稳定
  • γ=2 把幅度又放大回去了
  • β=1 把整体分布抬高了

网络依然可以学习到它想要的 z 大小,信息没有丢失。

(4) 通过激活函数(例:ReLU)

激活函数:

\[a_i = \max(0, z_i^\text{BN}) \]

得到最终输出:

样本\(z_i^\text{BN}\)激活后 \(a_i\)
1-1.440
2-0.040
31.341.34
44.144.14

这便是一个完整流程,把BN加入了线性组合后,通过激活函数之间,来实现批次间的归一化。

2.5 γ 与 β ,权重与偏置

再补充一个小问题,我们刚刚说通过γ 和 β,网络依然可以学习到它想要的 z 大小,信息没有丢失。
可是权重和偏置不也能实现这样的效果吗?一个是“幅度”,一个是“平移”,在下一层的线性组合部分不也能进行控制吗?
所以,真的有必要在反向传播中增加两个需要更新的新参数吗?

答案当然是有必要,我们先说结论:γ 与 β是为了解决权重调整和归一化调整的冲突性。
现在开始展开:
假设没有γ和β, BN 每次都会把上一层输出 \(z\)强制洗白成:

  • 均值 = 0
  • 方差 = 1
    无论 W 和 b 怎么把 \(z\) 拉大、压小、平移、拉偏,
    只要经过 BN,这些尺度和偏移统统都会压扁。
    因此:
    想只通过 W 和 b 表达“幅度”和“偏移”是不可能的,因为 BN 下一秒就洗掉;
    只有 γ β 能在 BN 之后重新加回来,不会被覆盖。

继续展开:如果网络没有 γ β,它为了恢复想要的幅度/偏移,只能疯狂调 W 和 b:

  • W 变大 → BN 归一化冲掉
  • W 再变更大 → BN 再冲掉
  • …无限循环……
    这会导致:
  • 训练更慢、更乱
  • 参数量级越变越大却完全没意义

这就是两个参数存在的意义:
BN 会把每层输出强行标准化(均值 0、方差 1),所以想表达“幅度”和“偏移”的权利不能放在 W 和 b 上(会被洗掉),必须交给 γ 和 β,它们是 BN 后唯一真正能保留这种表达能力的参数。

我们还是用做菜打个比方:
W,b 决定特征的组合结构———“做什么菜”;
BN 归一化————“菜量,调味料量统一”;
γ,β ————“统一大份还是小份,重口味还是清口味” ;
在一次次迭代中,γ,β 就会训练成让菜最美味的规格。

简单来说,结构归 W,b,量级归 γ,β,各司其职,不冲突也不重复。

3. BN 的作用和使用中的注意事项

3.1 BN 的作用

(1)加速训练

BN 会把每层的输入都“洗白”成 均值 0、方差 1
这会让网络的每一层都在比较“干净、统一”的数值范围里工作:

  • 梯度不会忽大忽小
  • 不容易梯度爆炸 / 消失
  • 学习率可以设得更大胆
  • 收敛更快
    一句话: BN = 把数据洗干净 → 每层都更好训练

(2)一定程度的正则化效果

BN 的 mini-batch 统计量每次都来自当前 batch,因此带有轻微随机性:

  • 每个 batch 的均值和方差略有不同
  • 相当于给网络输出加了一点噪声
    这种噪声会让网络不再过度依赖某个固定模式,从而降低过拟合风险

好像不太清晰,再来看一个示例:
假设你训练一个图片分类网络,每次输入 batch 大小为 32 张图片:

  1. BN 会计算 32 张图的平均值和方差,归一化每个特征
  2. 下一层看到的特征略有变化,因为 batch 内的统计量不同, 比如上一批 \(\mu_\text{batch}=120\),下一批 \(\mu_\text{batch}=118\)同一个像素归一化后的值会变化。差值虽小,但对网络来说是一种轻微的“扰动”。
  3. 因此,网络不会只记住某个固定数值,而是学会适应“小幅波动”
  4. 训练过程中,相当于每次都给网络做了微小扰动 → 起到正则化效果

注意:BN 不是 dropout 或 weight decay 的替代,它只是顺带有一点正则化效果。

3.2 注意事项:训练和测试中的 BN

不同于我们之前介绍的其他方法,BN 在训练和测试阶段的行为略有不同。

(1)训练阶段

我们已经知道, 在训练中,BN会使用当前 batch 的均值和方差来进行归一化。
但实际上,BN 还会同时维护一个通过EMA更新 全局均值(moving mean)和全局方差(moving variance)

\[\mu_\text{moving} = \alpha \cdot \mu_\text{moving} + (1-\alpha) \cdot \mu_\text{batch} \]

\[ \sigma^2_\text{moving} = \alpha \cdot \sigma^2_\text{moving} + (1-\alpha) \cdot \sigma^2_\text{batch} \]

这里 \(\alpha\) 一般接近 0.9–0.99。
为什么这两个量之前都没有出现过?他们又是干什么的?
因为这两个全局统计量用于测试阶段的归一化。

(2)测试阶段

测试时,一般 一次只输入一个样本,或者 batch 太小,无法可靠统计均值方差,这就会导致BN反而可能降低最终的测试指标。
于是就出现了一个问题:测试时样本可能和训练 batch 分布不同,就像我们之前说的小批次带来波动性,这时在测试中应用BN反而阻碍拟合,但我们希望保持稳定输出。
因此 BN 不再使用当前 batch 统计量,而是使用训练阶段维护的移动平均,这里就用到了我们在训练阶段维护的全局均值和全局方差 :

\[\hat{z}_i = \frac{z_i - \mu_\text{moving}}{\sqrt{\sigma^2_\text{moving} + \epsilon}} \]

\[z_i^\text{BN} = \gamma \hat{z}_i + \beta \]

就像直接把训练数据的模板套到测试中,不再用测试的数据进行归一化。这样,使用全局统计量可以确保预测一致性和稳定性。
总的来说,就是训练阶段用 batch 统计量保证稳定和轻微正则化,测试阶段用全局均值方差保证预测一致性。

4.“人话版”总结

概念原理比喻
输入归一化对原始特征做标准化(均值0,方差1或缩放到0~1),防止不同特征尺度差异过大影响训练给不同食材“统一切成小块”,保证每样菜量可控,不抢味道
内部协变量偏移 (ICS)随着前一层权重更新,每层输入分布不断漂移,导致训练不稳定菜做一半调味料改变,下一步厨师得重新适应味道,效率变慢
Batch Normalization (BN)对每层线性输出 (z = Wx+b) 在 batch 内做归一化,保持均值0、方差1,减小 ICS 影响厨师在每次下锅前,把食材量和调味量统一标准化,保证每道菜味道稳定
归一化前选择 (z) 而非 (a)激活函数会非线性截断或压缩分布,归一化在 (z) 上更稳定把食材原料量统一,而不是煮熟后的菜,煮熟后可能大小不一
γ(scale)和 β(shift)BN 后可学习的缩放和平移参数,恢复输出幅度和均值“统一菜量后,再决定大份小份、重口味还是清口味”
训练阶段 BN使用当前 batch 的均值和方差归一化,同时更新全局移动平均统计量厨师尝试每锅菜略有不同,但记录总体平均味道
测试阶段 BN使用训练阶段的全局均值和方差归一化,不用当前 batch 统计量,保证输出稳定按照训练时记录的标准味道做菜,不因单锅差异影响味道
作用1. 加速训练(梯度稳定,可用更大学习率)2. 提供轻微正则化(batch 内统计波动)把厨房每个步骤标准化,菜做得快又稳;偶尔小变动让厨师不死板
注意事项BN 不是 Dropout 替代,γ/β 必须单独更新,训练与测试策略不同厨师要同时管理食材量标准化和调味偏好,训练时可微调,测试时按固定标准
作者:哥布林学者原文地址:https://www.cnblogs.com/Goblinscholar/p/19232083

%s 个评论

要回复文章请先登录注册