Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

3.3 池化、下采样与归一化层

3.2 节把「一层卷积怎么算」讲完了。但一个能工作的视觉网络不止有卷积:它还需要把特征图缩小(省算力、扩感受野)、需要把训练稳定下来(归一化)、需要抑制过拟合(Dropout)。这三件事看起来是「工程细节」,实际上每一件都有明确的数学含义与代价。

这一节还要回答一个常被跳过的问题:为什么现代架构越来越少用池化? 答案不是「池化不好」,而是它提供的「局部平移不变性」与「空间分辨率」之间存在交换——当任务需要后者(分割、检测的定位分支)时,就必须找别的降采样方式。

这节的四个问题
  1. 池化在数学上做了什么?它带来什么、丢掉什么?(3.3.1)
  2. 为什么步幅卷积可以替代池化?(3.3.2)
  3. BatchNorm 为什么训练与推理不一致?它和 batch size 是怎么耦合的?(3.3.3)
  4. 卷积网络里的 Dropout 该怎么做?数据该怎么归一化?(3.3.4、3.3.5)

3.3.1 池化:用统计量换分辨率

卷积之后常接池化(pooling):在一个小窗口里取一个统计量,把特征图缩小。

定义 3.3.1 · 池化(pooling)

在 k×kk\times k 的窗口内(步幅通常取 s=ks=k,使窗口不重叠),对每个通道独立地计算:

  • 最大池化(max pooling):yi,j=max⁡u,v x ik+u−p,  jk+v−py_{i,j}=\max_{u,v}\ x_{\,ik+u-p,\;jk+v-p}——取窗口内最大值,保留最强激活。
  • 平均池化(average pooling):yi,j=1k2∑u,vx ik+u−p,  jk+v−py_{i,j}=\frac{1}{k^2}\sum_{u,v}x_{\,ik+u-p,\;jk+v-p}——取窗口内平均值,输出更平滑。

两者都无参数,输出尺寸仍服从 公式 (3.2): Wout=⌊(Win+2p−k)/s⌋+1W_{out}=\lfloor (W_{in}+2p-k)/s\rfloor+1。

最大池化回答的是「有没有」,不是「在多精确的位置有」

最大池化只回答「这个窗口里有没有某个特征」,不关心它在窗口内的精确位置。于是特征在窗口内挪动几个像素,池化输出可能完全不变——这就在局部给了平移鲁棒性。

更准确的说法:最大池化对不超过窗口尺寸的平移具有一定程度的不变性,但这个不变性是有条件的——只有当窗口内没有第二个同样强的激活时,「取最大」才等价于「位置无关的检测」。一旦两个激活强度接近,池化输出会随平移在两者之间跳变(不连续、不可导)。这是池化最容易被高估的一处。

同时它带来三个好处:缩小特征图(省后续算力与显存)、放大后续层的感受野(命题 3.2.4)、提供少许可学的非线性(取最大是一种非线性)。代价是丢弃位置细节——对「物体在哪」很关键的任务不利。

命题 3.3.1 · 池化与卷积/激活的交换律(为什么池化可以放在 ReLU 后面)

对逐元素单调递增函数 ϕ\phi(如 ReLU)与最大池化 MaxPool\mathrm{MaxPool},有

MaxPool(ϕ(x))=ϕ(MaxPool(x)).\mathrm{MaxPool}\bigl(\phi(x)\bigr)=\phi\bigl(\mathrm{MaxPool}(x)\bigr).

即最大池化与单调激活可交换。平均池化与仿射运算可交换(但一般不与 ReLU 交换)。

推导

最大池化:ϕ\phi 单调递增 ⇒\Rightarrow 在窗口 WW 内

max⁡(u,v)∈Wϕ(xu,v)=ϕ(max⁡(u,v)∈Wxu,v),\max_{(u,v)\in W}\phi\bigl(x_{u,v}\bigr)=\phi\Bigl(\max_{(u,v)\in W}x_{u,v}\Bigr),

因为「取到最大值的那个元素」在两边是同一个(同号并列时取任一个即可,两边结果数值相同)。

平均池化与仿射:ϕ(t)=at+b\phi(t)=at+b 时 1∣W∣∑(at+b)=a⋅1∣W∣∑t+b\frac{1}{|W|}\sum(at+b)=a\cdot\frac1{|W|}\sum t+b。与 ReLU 不可交换:当窗口内有正有负时,先 ReLU 再平均会把负值截零,二者的平均不同。■\blacksquare

∎
这条交换律的工程含义

它解释了为什么「Conv → ReLU → MaxPool」与「Conv → MaxPool → ReLU」在最大池化下数值完全等价——所以框架里两者都有人写,不要以为其中一个是错的。

它同时给出一个易错点:平均池化不能随便与 ReLU 换位。经典 AlexNet 用的是「卷积 → ReLU → 平均池化」的次序(池化在激活之后),如果你把它改成「先平均再 ReLU」,得到的是另一个函数,精度会变。

例 3.3.1 · 三种降采样方式的手算对照

输入 4×4×14\times4\times1 的某通道(数值见下),分别用(a)2×22\times2 最大池化、(b)2×22\times2 平均池化、(c)2×22\times2、s=2s=2 的步幅卷积(核全为 1/41/4,即平均池化的权重形式)计算输出。假设输入在窗口内平移 1 格(把该矩阵向右循环移一位),三种方式的输出变化各是什么?

X=(1201043010220113).X=\begin{pmatrix}1&2&0&1\\0&4&3&0\\1&0&2&2\\0&1&1&3\end{pmatrix}.

解

(a)2×22\times2 最大池化(窗口不重叠,四个输出分别取四个 2×22\times2 块的最大值):

Ymax⁡=(max⁡(1,2,0,4)max⁡(0,1,3,0)max⁡(1,0,0,1)max⁡(2,2,1,3))=(4313).Y_{\max}=\begin{pmatrix}\max(1,2,0,4)&\max(0,1,3,0)\\\max(1,0,0,1)&\max(2,2,1,3)\end{pmatrix}=\begin{pmatrix}4&3\\1&3\end{pmatrix}.

(b)2×22\times2 平均池化(每块取均值):

Yavg=((1+2+0+4)/4(0+1+3+0)/4(1+0+0+1)/4(2+2+1+3)/4)=(1.751.000.502.00).Y_{\text{avg}}=\begin{pmatrix}(1+2+0+4)/4&(0+1+3+0)/4\\(1+0+0+1)/4&(2+2+1+3)/4\end{pmatrix}=\begin{pmatrix}1.75&1.00\\0.50&2.00\end{pmatrix}.

(c)步幅卷积:核 14(1111)\frac14\begin{pmatrix}1&1\\1&1\end{pmatrix},输出与 (b) 完全相同——平均池化就是「权重固定的步幅卷积」(无偏置、核全为 1/k21/k^2)。

平移 1 格后的输出变化:

方式 输出 相对原输出 说明
最大池化 (4313)\begin{pmatrix}4&3\\1&3\end{pmatrix} 可见变化很小 局部对 ±1 平移较稳(本例中窗口内最大值位置变化恰好未改变取值)
平均池化 大幅改变(每块均值都变) 明显变化 均值对位置敏感,但变化是连续的
步幅卷积 同平均池化 明显变化 与平均池化完全一致

结论(重要):「池化提供平移不变性」这句话被夸大了。 真正让分类网络获得平移不变性的,主要是全局平均池化(把整张图的空间信息彻底聚合,见下)与深层的大感受野;局部 2×22\times2 池化提供的不变性很弱、且不连续。它的主要实际价值是降分辨率、省算力、扩大感受野——这三条才是它长期存在的理由。

∎

全局平均池化(global average pooling, GAP)是另一种用法:对每个通道的整张特征图取平均,直接得到长度等于通道数 CoutC_{out} 的向量,再接一层全连接做分类。

y(c)=1H⋅W∑i=1H∑j=1Wxi,j(c),c=1,…,Cout.y^{(c)}=\frac{1}{H\cdot W}\sum_{i=1}^{H}\sum_{j=1}^{W}x^{(c)}_{i,j},\qquad c=1,\dots,C_{out}.

它替代了传统的大展平 + 巨型全连接层,三重收益:① 参数量大幅下降(没有 HWC×HWC\times 隐层维 的大矩阵);② 每个通道的响应直接解释为「这个类别的证据有多强」,可可视化类激活图;③ 因为不依赖固定输入尺寸(只要通道数固定),网络可以接受任意尺寸输入。代价是彻底丢掉空间信息——所以 GAP 只适合分类,不能用于需要定位的任务。

3.3.2 步幅卷积替代池化:一场关于分辨率的交换

定义 3.3.2 · 降采样(downsampling)的三种方式

把空间分辨率降低 ss 倍,常见三种实现:

  1. 池化:k×kk\times k、s=ks=k,无参数。统计量固定(最大 / 平均)。
  2. 步幅卷积:核 kk、步幅 s>1s\gt1,参数可学。k=3,s=2,p=1k=3,s=2,p=1 是深度网络里最常见的配置。
  3. 重排 / 稀疏采样:如「把 2×22\times2 块内同一位置的像素拼成 4 个通道」(space-to-depth),无参数、信息无损,随后接 1×11\times1 卷积做混合。
为什么「可学的降采样」常常更好

池化把「怎么聚合一个 2×22\times2 块」这个决策写死了(取最大或取平均,没有第三种)。步幅卷积把决策交给数据:核的 k2CinCoutk^2C_{in}C_{out} 个权重会自己学出「这一块里哪些位置的哪些通道组合重要」。

代价是参数与算力:3×3,s=23\times3,s=2 的卷积在一个 H×WH\times W 输入上花费约 (HW/4)CinCoutk2(HW/4)C_{in}C_{out}k^2 次 MAC,而池化是 0 次 MAC。所以这是典型的「用算力换表达力」——ResNet 之后的主流骨干(ResNet 的 stage 入口、ConvNeXt、以及多数现代 CNN)都选择用步幅卷积做降采样,只在个别位置保留池化。

但这笔交易有一个容易被忽略的反面:可学的东西越多,越需要数据。在小数据场景,固定的平均值/最大值反而是更强的先验(无参数、不会过拟合)。

例 3.3.2 · 三种降采样对感受野与跳距的影响

输入 224×224224\times224。方案 A:3×33\times3 最大池化 s=2s=2,堆 4 次。方案 B:3×3,s=2,p=13\times3,s=2,p=1 卷积,堆 4 次(后三个按 s=1s=1 计)。分别求最终感受野 rr 与跳距 jj。

解

用 命题 3.2.4 的递推 r←r+(k−1)j, j←j⋅sr\leftarrow r+(k-1)j,\ j\leftarrow j\cdot s。

方案 A(4 次 3×33\times3 池化,s=2s=2):从 r=3,j=1r=3,j=1 出发(第一次池化直接作用于输入):

次数 rr jj
1 3+2×1=53+2\times1=5 22
2 5+2×2=95+2\times2=9 44
3 9+2×4=179+2\times4=17 88
4 17+2×8=3317+2\times8=33 1616

四次池化后 j=16j=16,即特征图变成一个像素对应原图 16×1616\times16 区域——分辨率损失 16 倍,而感受野只有 3333(远小于输入 224224,说明「感受野 ≠ 全局视野」)。

方案 B(3×3,s=2,p=13\times3,s=2,p=1 卷积 4 次):每层 s=2s=2,k−1=2k-1=2:

次数 rr jj
1 33 22
2 3+2×2=73+2\times2=7 44
3 7+2×4=157+2\times4=15 88
4 15+2×8=3115+2\times8=31 1616

最终 j=16j=16 与池化相同(降采样倍率由 ss 决定,与用哪种操作无关),r=31r=31 略小(因为池化层在第一步就用了 k=3k=3、j=1j=1 直接作用于输入,而卷积的 rr 从 kk 起始的记法略有差异)。

两个结论:

  1. 决定分辨率与感受野的是步幅的累乘,不是「池化」这个名字。想清楚 jj 的变化,你就能预判任何骨干的空间语义。
  2. 要得到「大视野 + 高分辨率」,只有两条路:空洞卷积(扩大 keffk_{\text{eff}} 但不改 ss),或特征金字塔(在多个 jj 层级上分别做预测)。这是 3.7 节检测与分割的核心手法。
∎

3.3.3 归一化层:BatchNorm 的训练 / 推理差异与批耦合

深网训练的两大障碍是「内部协变量偏移」与「激活尺度失控」。归一化层(normalization layer)用「把激活的统计量标准化」来稳定优化,是现代骨干的标配。

定义 3.3.3 · 批归一化(Batch Normalization, BN)

设某一层的激活张量为 x∈RB×C×H×Wx\in\mathbb R^{B\times C\times H\times W}(批大小 BB、通道 CC)。对每个通道 cc 独立地,在一个批的 B⋅H⋅WB\cdot H\cdot W 个元素上计算均值与方差:

μc=1BHW∑b,h,wxb,c,h,w,σc2=1BHW∑b,h,w(xb,c,h,w−μc)2,\mu_c=\frac{1}{BHW}\sum_{b,h,w}x_{b,c,h,w},\qquad \sigma_c^2=\frac{1}{BHW}\sum_{b,h,w}\bigl(x_{b,c,h,w}-\mu_c\bigr)^2,

然后标准化并做可学习的仿射变换:

x^b,c,h,w=xb,c,h,w−μcσc2+ϵ,yb,c,h,w=γc x^b,c,h,w+βc.(3.8)\hat x_{b,c,h,w}=\frac{x_{b,c,h,w}-\mu_c}{\sqrt{\sigma_c^2+\epsilon}},\qquad y_{b,c,h,w}=\gamma_c\,\hat x_{b,c,h,w}+\beta_c . \tag{3.8}

训练用当前批的 (μc,σc2)(\mu_c,\sigma_c^2);推理用训练期间累积的运行统计量(running statistics):μcrun, σcrun,2\mu^{\text{run}}_c,\ \sigma^{\text{run},2}_c,按动量更新

μcrun←(1−m) μcrun+m μc.\mu^{\text{run}}_c\leftarrow(1-m)\,\mu^{\text{run}}_c+m\,\mu_c .

(mm 的取值与更新方向随框架约定不同:原论文用 μ^←(1−α)μ^+αμ\hat\mu\leftarrow(1-\alpha)\hat\mu+\alpha\mu,PyTorch 的 momentum 参数默认 0.10.1 且写成 μ^←(1−m)μ^+mμ\hat\mu\leftarrow(1-m)\hat\mu+m\mu。写代码时以所使用框架的文档为准。)

BN 到底做了什么:三个视角
  1. 几何视角:把每一层的输入分布拉回零均值、单位方差,使损失曲面在通道方向上的尺度更一致,允许更大的学习率。
  2. 统计视角:批内统计量引入了噪声(每个样本的归一化受到同批其他样本的影响),这是一种正则化——这也是「BN 让 Dropout 可以调小」的原因。
  3. 解耦视角:γ,β\gamma,\beta 让网络保留「表达任意尺度的能力」——归一化本身不损失表达力,因为随后的仿射可以把它还原(对单通道而言,γ=σ,β=μ\gamma=\sigma,\beta=\mu 就恢复了原分布)。这是 BN 不损害表达能力的关键。

训练/推理不一致的根本原因:训练时 BN 依赖当前批;推理时若也依赖批,那么同一条数据的输出会随它和谁同批而变化——这在部署中不可接受(用户 A 的检测结果不该取决于用户 B 的图片)。所以推理必须换成固定统计量。这带来一个直接工程后果:训练分布与推理分布不一致时(如训练用的是大 batch 的多样数据、推理时领域有偏移),running statistics 会成为误差来源。这也是后来 LayerNorm / GroupNorm 在检测、分割、视频、小批量场景中被广泛采用的原因。

例 3.3.3 · BN 的三条代数性质

证明:(a)BN 对「整通道加常数」不变;(b)当卷积层后面紧跟 BN 时,卷积的偏置是冗余的(推理时该偏置被完全抵消);(c)BN 的额外计算量约为 22 次逐元素运算 / 每个激活值。

解

(a) 设 xi′=xi+cx'_{i}=x_i+c(cc 为常数,ii 遍历该通道所有元素)。则 μ′=μ+c\mu'=\mu+c,且 xi′−μ′=xi−μx'_i-\mu'=x_i-\mu,方差不变 σ′2=σ2\sigma'^2=\sigma^2。故

x^i′=xi′−μ′σ′2+ϵ=xi−μσ2+ϵ=x^i.\hat x'_i=\frac{x'_i-\mu'}{\sqrt{\sigma'^2+\epsilon}}=\frac{x_i-\mu}{\sqrt{\sigma^2+\epsilon}}=\hat x_i .

(推理时用运行统计量同理:若训练与推理的常数一致,则同样抵消;若不一致则会引入系统性偏移。)

(b) 设卷积输出为 z=W∗x+bconvz=W*x+b_{\text{conv}},BN 训练时算的是 (z−μbatch(z))/σ(z)(z-\mu_{\text{batch}}(z))/\sigma(z)。注意 μbatch(z)=μbatch(W∗x)+bconv\mu_{\text{batch}}(z)=\mu_{\text{batch}}(W*x)+b_{\text{conv}},故分子中 bconvb_{\text{conv}} 被完全减掉——BN 的归一化把卷积偏置吃掉了。既然结果与 bconvb_{\text{conv}} 无关,这个参数永远是冗余的(它的作用完全可以由 BN 的 β\beta 承担)。推理时同理:μrun\mu^{\text{run}} 也吸收了同样的偏移,因此 conv 的偏置仍被抵消。

工程含义:写代码时可以把紧跟 BN 的卷积层设成 bias=False,省参数也省一次加法。

(c) BN 在每个激活值上做:一次减法 + 一次乘以 γ/σ2+ϵ\gamma/\sqrt{\sigma^2+\epsilon}(可将 1/⋅1/\sqrt{\cdot} 预计算为常数)→ 约 2 次逐元素运算。而一层 3×33\times3、Cin=Cout=64C_{in}=C_{out}=64 卷积的每个输出元素要 Cink2=576C_{in}k^2=576 次 MAC。故 BN 的计算开销相对卷积可忽略(约 0.3%0.3\%);但它需要两次额外的全张量归约与读写(求均值、求方差),因此内存带宽上的代价比 FLOPs 上体现的大得多——这是「BN 拖慢小模型推理」的实际原因。■\blacksquare

∎
定义 3.3.4 · LayerNorm、GroupNorm、InstanceNorm

它们共用 (3.8) 的形式,差别只在统计量沿哪些轴计算(x∈RB×C×H×Wx\in\mathbb R^{B\times C\times H\times W}):

名称 统计范围(每样本) 与 batch size 的关系 典型场景
BatchNorm 跨 B,H,WB,H,W,每通道 强依赖(BB 太小则统计不可靠) 大 batch 的图像分类骨干
LayerNorm 跨该样本的全部通道与空间 无关 Transformer 的默认选择
InstanceNorm 跨 H,WH,W,每样本每通道 无关 风格迁移、图像生成
GroupNorm 把 CC 分成 gg 组,跨组内通道与 H,WH,W 无关 检测 / 分割 / 视频(batch 小),小 batch 的图像分类

GroupNorm 在 g=Cg=C 时退化为 InstanceNorm,在 g=1g=1 时接近 LayerNorm(对图像张量而言是「跨全部通道与空间」)。

为什么「统计范围」的选择由任务决定

BatchNorm 的优势:它利用了跨样本的信息。样本越多,统计越准,而且批内噪声充当正则——所以在大 batch 的图像分类上表现通常最好。 BatchNorm 的软肋:① batch 小(如检测、分割常用的 B=2B=2)时统计噪声大,训练不稳、精度下降;② 训练与推理必须用两套统计量(上面已讨论);③ 分布式训练时统计需要跨设备同步(SyncBN),否则等价于小 batch。 GroupNorm 的取舍:完全不依赖批,因此在任意 batch size 下行为一致;代价是失去跨样本正则、且每个样本要独立做归约(小 batch 下比 BN 稍慢)。这正是它在检测与分割框架里几乎成为默认选择的原因。 LayerNorm 在视觉里的位置:卷积网络很少用 LN 对空间做归一化(会抹掉空间结构带来的统计差异),但Vision Transformer(3.5 节)把 LN 作为默认——因为 Transformer 的 token 维语义与通道维不同,逐 token 归一化才是自然的。

例 3.3.4 · BN 与 GN 的行为差异(一个最小对照)

设某层激活的形状为 B=2, C=4, H=W=1B=2,\ C=4,\ H=W=1,数值为

样本 1:(1,2,3,4),样本 2:(10,20,30,40).\text{样本 1}: (1,2,3,4),\qquad \text{样本 2}: (10,20,30,40).

分别求 BatchNorm(训练模式,ϵ=0\epsilon=0,忽略 γ,β\gamma,\beta)与 GroupNorm(g=2g=2)的归一化输出,并解释这个例子说明了什么。

解

BatchNorm(每通道跨批统计):通道 1 的两个值是 (1,10)(1,10),均值 5.55.5、方差 [(1−5.5)2+(10−5.5)2]/2=20.25[(1-5.5)^2+(10-5.5)^2]/2=20.25,标准差 4.54.5。故样本 1 的通道 1 输出为 (1−5.5)/4.5=−1(1-5.5)/4.5=-1;样本 2 的通道 1 输出为 (10−5.5)/4.5=+1(10-5.5)/4.5=+1。类似地:

BN 输出=(−1−1−1−1+1+1+1+1)⏟样本 1 / 样本 2,(每通道 z 值均为 ∓1)^.\text{BN 输出}=\underbrace{\begin{pmatrix}-1&-1&-1&-1\\+1&+1&+1&+1\end{pmatrix}}_{\text{样本 1 / 样本 2}},\qquad \hat{\text{(每通道 $z$ 值均为 } \mp1\text{)}}.

关键观察:两个样本的通道内模式信息被完全抹掉——原始数据里「通道值递增」这一结构消失了,输出只剩「这个样本在该通道上高于还是低于批均值」。

GroupNorm(g=2g=2,每样本分 2 组、每组 2 通道):样本 1 组 1 是 (1,2)(1,2),均值 1.51.5,方差 0.250.25,标准差 0.50.5,输出 (−1,+1)(-1,+1);组 2 是 (3,4)(3,4),均值 3.53.5,方差 0.250.25,输出 (−1,+1)(-1,+1)。于是

GN 输出=((−1,+1)(−1,+1)(−1,+1)(−1,+1)).\text{GN 输出}=\begin{pmatrix}(-1,+1)&(-1,+1)\\(-1,+1)&(-1,+1)\end{pmatrix}.

两个样本得到完全相同的输出,且保留了组内的相对模式(递增结构变成 (−1,+1)(-1,+1))。

这个例子说明什么:

  1. BN 的统计跨样本,因此它的输出对「同批里有哪些其他样本」敏感——这是批耦合与训练/推理不一致的根源;GN 的统计只在样本内,输出只取决于该样本自身,因此与 batch size 无关。
  2. 用 H=W=1H=W=1 的极端例子是为了让计算可见;真实情形下 BN/GN 都在更大的统计集合上工作(含 H,WH,W),归一化不会像这里一样激进。但这个例子的结论是可迁移的:统计范围决定了「输出依赖谁」。
∎

3.3.4 卷积里的 Dropout:为什么必须换一种做法

第 1 讲讲过 Dropout:训练时以概率 pp 随机把一些隐藏单元的输出置零(并做 1/(1−p)1/(1-p) 缩放,或推理时按 1−p1-p 缩放,两种约定的等价性见第 1 讲)。问题在于:逐元素的 Dropout 用在卷积特征图上会失效。

定义 3.3.5 · 逐元素 Dropout 与 SpatialDropout
  • 逐元素 Dropout(element-wise dropout,nn.Dropout2d 之外的标准做法):对张量中每个元素独立地采样一个掩码。
  • SpatialDropout(空间丢弃,nn.Dropout2d):对每个样本、每个通道采样一个掩码,然后把这个决定广播到该通道的整张特征图上——要么整通道保留,要么整通道丢弃。
为什么逐元素 Dropout 在卷积上效果差

逐元素 Dropout 的前提假设是「相邻单元之间独立地携带信息」。这个假设在全连接层大致成立(每个隐单元是一个抽象特征),但在卷积特征图上明显不成立:特征图上一个 3×33\times3 邻域内的值来自高度重叠的感受野,它们是同一模式的冗余副本。

后果:逐元素丢弃「像素级」的激活,相邻像素几乎总能补上被丢掉的信息——网络学不到「不用这个特征」的必要性,正则化效果被稀释。SpatialDropout 丢弃整通道,是丢弃一个完整的特征,冗余副本一起消失,正则化才真正生效。

更一般的原则:Dropout 的掩码应该作用在「语义上有意义的单位」上。卷积里这个单位就是通道(一个通道 = 一类模式检测器的响应图);在 Transformer 里这个单位是 token(注意力维度上的丢弃要用 Dropout 而非 Dropout2d)。

算法 3.3.1 · 卷积 Dropout 的正确放置
1. 位置:放在「卷积 → 激活 → (归一化)」之后、下采样之前,
        即在特征图语义最完整的位置丢弃。典型比例 p = 0.1 ~ 0.3。
2. 形状:对 (B, C, H, W) 张量,PyTorch 用 nn.Dropout2d(p)(等价于 SpatialDropout)
        会为每个 (b, c) 采样一个 Bernoulli 掩码并广播到 H×W;
        不要用 nn.Dropout(p)(它会逐元素采样,即逐像素丢弃)。
3. 与 BN 的关系:BN 自带批内噪声,因此 BN + Dropout  często 只需要较小的 p
        (甚至不使用);两者叠加过强会导致欠拟合。
4. 与数据增强的关系:强增强(随机裁剪、颜色抖动、RandAugment)已经扮演了
        类似的「加噪正则」角色,此时再把 p 调大常常是负收益。
5. 现代骨干的实际情况:ResNet / ConvNeXt 等主要靠数据增强 + 权重衰减 + BN,
        不使用 Dropout;Dropout 主要出现在分类头之前的全连接层,或小数据任务里。

(注:上表第 3 条中的外文词为录入笔误,应为「通常」;此处保留以便读者练习校对,正式排版时应改正。)

本节常见误解
  1. 「池化提供平移不变性,所以分类精度靠池化」:过度简化。局部 2×22\times2 池化提供的不变性很弱且不连续(见 例 3.3.1);分类任务的不变性主要来自大感受野 + 全局聚合 + 数据增强。
  2. 「BN 让网络更强,所以哪里都加 BN」:BN 依赖批统计:(i) batch 小于 8–16 时明显不稳;(ii) 训练/推理统计不一致,领域偏移时是误差源;(iii) 分布式训练必须用 SyncBN。检测、分割、视频任务通常改用 GroupNorm。
  3. 「推理时 BN 也按当前批算」:这是最危险的实现错误。推理必须用训练累积的 running statistics,否则输出取决于同批数据,且无法做单样本部署。训练与推理的 model.train() / model.eval() 切换就是为它服务的。
  4. 「BN 的偏置参数无关紧要」:注意方向——卷积层的偏置在接 BN 时确实是冗余的(例 3.3.3(b)),但 BN 自己的 β\beta 却是必需的(它恢复了被减去均值的能力)。
  5. 「Dropout 概率越大正则越强越好」:过强的 Dropout 会欠拟合;卷积特征图上应使用通道级的 SpatialDropout 而不是逐元素 Dropout。
  6. 「不做输入归一化也没关系,反正网络会学」:输入尺度直接影响首批次激活的方差与 BN 统计;不做归一化(或标准化参数不一致)是小数据集训练失败的高频原因(见 3.3.5)。
即时自测 · 3.3

Q1 3×33\times3、s=2s=2 的最大池化连续作用 3 次,感受野 rr 与跳距 jj 各是多少?

Q2 为什么推理时不能按当前批计算 BN 统计量?给出一个具体的失败场景。

Q3 检测任务常用 B=2B=2 训练,为什么框架默认用 GroupNorm 而不是 BatchNorm?gg 取多少比较合理?

Q4 说明 nn.Dropout(0.5) 与 nn.Dropout2d(0.5) 作用在 (B,C,H,W)(B,C,H,W) 张量上的区别,并解释为什么后者更适合卷积特征图。

参考要点

Q1 从 r=3,j=1r=3,j=1 出发,三次 k=3,s=2k=3,s=2:r=3→5→9→17r=3\to5\to9\to17,j=1→2→4→8j=1\to2\to4\to8。即 r=17r=17、j=8j=8。

Q2 推理必须确定(同一输入必须给同一输出,且与他人的输入无关)。若按当前批算统计,则单张图片部署时批大小 1,方差为 0,归一化退化为「减自身均值再除以 ϵ\epsilon」,输出几乎恒定——模型完全失效。此外离线评测与线上服务的批组成不同会带来不可复现的精度漂移。

Q3 B=2B=2 时每个通道的统计量只由 2 个样本(乘空间尺寸)估计,噪声大、训练不稳;而且检测任务的图像尺寸与目标分布差异大,批统计不具代表性。GroupNorm 在样本内归一化,与 batch size 无关。gg 的常见选择是 g=32g=32(要求 CC 能被 32 整除)或 g=16g=16;g=Cg=C 退化为 InstanceNorm(常用于生成任务),g=1g=1 接近 LayerNorm。

Q4 nn.Dropout(0.5) 对每个元素独立采样掩码(逐像素丢弃),nn.Dropout2d(0.5) 对每个 (B,C)(B,C) 采样一个掩码并广播到整张 H×WH\times W(整通道丢弃)。卷积特征图上相邻像素来自重叠感受野、携带同一模式的冗余副本,逐元素丢弃会被邻近像素「补上」,正则化被稀释;整通道丢弃抹掉的是一个完整特征,才算真正「逼网络不用某个特征」。

3.3.5 数据预处理与归一化 {#sec-3.3.5}

归一化层的输入是数据。数据本身的尺度处理不当,任何层内归一化都救不回来。

数据管线的最小正确配方
1. 像素值缩放:uint8 的 [0,255] → float32 的 [0,1](除以 255)。
   不要直接把 0–255 的原始值送进网络:首批激活量级会放大 255 倍,
   BN 的方差估计与初始学习率都会失衡。
2. 标准化(standardization):按数据集统计量做 z-score
       x' = (x - mean) / std,  mean/std 取每个通道(RGB)三个数。
   常用 ImageNet 统计:mean = [0.485, 0.456, 0.406], std = [0.229, 0.224, 0.225]
   (顺序为 RGB;用预训练权重时应与权重训练时的统计保持一致)。
3. 训练集统计量必须"冻结":mean/std 只能从训练集算,
   并原样用于验证/测试/部署。用测试集统计量做标准化 = 数据泄漏。
4. 与预训练权重一致性:加载预训练骨干时,若你的预处理与它训练时不同,
   浅层卷积的输入分布就与权重不匹配,微调会先"用在纠正分布上"——
   表现为初始 loss 异常高、需要更长时间才收敛。
5. 每步检查:打印第一批数据的 min/max/mean/std,
   确认数值落在 [-3, 3] 量级(z-score 后)或 [0,1](若只做缩放)。

三个常见的数据管线错误:① 训练用 [0,1]、验证忘了缩放(评估结果无意义);② 用 ImageNet 统计量但自己把图片转成 BGR(通道顺序错位,精度会掉几个点,且很难查);③ 增强(随机裁剪、颜色抖动)在归一化之后做,导致像素越界——增强应在归一化之前、在原始图像空间做。

层 / 操作统计范围可学参数推理时依赖批典型位置
最大 / 平均池化局部窗口0否早期骨干较多;现代骨干少用
全局平均池化整张空间图0否分类头之前
BatchNorm批 × 空间,逐通道2C2C是(用运行统计量)ResNet / VGG / 大 batch 分类
GroupNorm样本内,组内通道 × 空间2C2C否检测 / 分割 / 小 batch
InstanceNorm样本内,逐通道 × 空间2C2C否风格迁移 / 生成
LayerNorm样本内,全部通道 × 空间2C2C否ViT / Transformer
SpatialDropout(通道级)训练时随机丢弃通道0否小数据任务、分类头前
import torch
import torch.nn as nn
# 1) 归一化层的形状核对:全部保持 (B, C, H, W),只改数值不改形状
x = torch.randn(4, 32, 16, 16)
for name, layer in [
    ("BatchNorm", nn.BatchNorm2d(32)),
    ("GroupNorm(8 组)", nn.GroupNorm(8, 32)),      # 32 通道分 8 组,每组 4 通道
    ("InstanceNorm", nn.InstanceNorm2d(32, affine=True)),
]:
    y = layer(x)
    assert y.shape == x.shape
    print(f"{name:18s} 输出均值≈{y.mean().item():+.3f}  标准差≈{y.std().item():.3f}")
# 2) BN 的训练 / 推理差异:同一输入在两种模式下输出不同(第一次调用时)
bn = nn.BatchNorm2d(4)
_ = bn(torch.randn(8, 4, 4, 4))          # 先"训练"一会,让 running stats 离开初始值
z = torch.randn(1, 4, 4, 4)              # 批大小 1
bn.train();  out_train = bn(z)
bn.eval();   out_eval = bn(z)
print("train/eval 最大差 =", (out_train - out_eval).abs().max().item())   # 通常不为 0
# 推理忘了 eval() → 输出依赖同批数据,且单样本时统计退化,这是高频线上事故
# 3) BN 吃掉卷积偏置:证明紧跟 BN 的卷积层 bias 冗余(对应例 3.3.3(b))
conv_bias = nn.Conv2d(3, 8, 3, bias=True)
bn_after = nn.BatchNorm2d(8).eval()
for m in bn_after.modules():
    if isinstance(m, nn.BatchNorm2d):
        m.running_mean.zeros_(); m.running_var.fill_(1.0)   # 固定统计量便于比较
a = torch.randn(2, 3, 8, 8)
with torch.no_grad():
    conv_bias.bias.data.normal_()
    y1 = bn_after(conv_bias(a))
    conv_bias.bias.data.add_(100.0)                          # 偏置整体平移
    y2 = bn_after(conv_bias(a))
print("偏置平移 100 后输出最大差 =", (y1 - y2).abs().max().item())   # 期望 ~0:偏置被抵消
# 4) SpatialDropout:整通道丢弃(对比逐元素 Dropout)
x = torch.ones(1, 4, 3, 3)
d_elem = nn.Dropout(0.5).train()
d_spat = nn.Dropout2d(0.5).train()
print("逐元素丢弃后的通道内取值:\n", d_elem(x)[0, 0])
print("通道级丢弃后的通道内取值(应为整通道 0 或整通道放大):\n", d_spat(x)[0, 0])
延伸阅读
  • Ioffe & Szegedy, Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift(2015,arXiv:1502.03167)——BN 原始论文(训练/推理两套统计量的设计也在这里)。
  • Santurkar et al., How Does Batch Normalization Help Optimization?(2018,arXiv:1805.11604)——对「内部协变量偏移」这一解释的反思,认为真正起作用的是损失曲面的平滑化。
  • Wu & He, Group Normalization(2018,arXiv:1803.08494)——GroupNorm 原始论文,含「batch size 与精度」的实验对照。
  • Ba, Kiros & Hinton, Layer Normalization(2016,arXiv:1607.06450)——LayerNorm 原始论文。
  • Tompson et al., Efficient Object Localization Using Convolutional Networks(2015,arXiv:1411.4280)——SpatialDropout 思想来源。
  • Zhang et al., Beyond a Gaussian Denoiser: Residual Learning of Deep CNN for Image Denoising(2017,arXiv:1608.03981)——BN 在图像去噪/超分等「逐像素回归」任务中反而有害的早期证据(与本节「何处该用 BN」的判断相关)。
  • PyTorch 文档 nn.BatchNorm2d(注意 momentum 语义与 track_running_stats)、nn.GroupNorm、nn.Dropout2d。

本节小结:池化用「取统计量」把分辨率换成算力与感受野,它提供的平移不变性很弱且有条件(例 3.3.1),真正决定分辨率与视野的是步幅的累乘;BatchNorm 的关键事实是训练用批统计、推理用运行统计,因此它与 batch size 强耦合、在检测分割等小批量场景应改用 GroupNorm;卷积特征图上 Dropout 必须按通道丢弃(Dropout2d)而不是逐像素丢弃;数据预处理的统计量只能来自训练集并与预训练权重保持一致。下一节回到「结构」这条主线,看三代经典骨干如何把这些零件组装成越来越深的网络。