CC's

Back

在之前的一篇文章中我们介绍过关于 BN 和 LN 的一些小细节,在那里提到了这类 Layer 一般会在训练阶段统计输入数据的分布信息,并将该信息使用在推理阶段。随着近期 CV 侧深度学习也从 fine-tune 逐渐走向了直接 freeze backbone,我觉得是时候进一步明确 Norm Layer 在训练阶段和测试阶段的行为细节了。

本文将以 PyTorch 的 BatchNorm 为例。

统计信息是如何更新的#

BatchNorm 可以描述为以下过程:

y=xE(x)Var(x)+ϵγ+βy=\frac{x-E(x)}{\sqrt{\mathrm{Var}(x)+\epsilon}}\gamma+\beta

其中 xx 为输入,γ\gammaβ\beta 是两个可学习参数。

我们来看一看 PyTorch 如何实现的 BatchNorm。BatchNorm1d 类仅仅是检查了输入的维度是否符合要求,_BatchNorm 中描述了逻辑,而 NormBase 则包含所有的可训练参数,我们先检查它。

可以看到,此类注册了几个值得注意的参数:

  • weight
  • bias
  • running_mean
  • running_var

前两个实际上就是 gamma 和 beta。^[https://github.com/pytorch/pytorch/issues/16149]比较有趣的东西是 running_meanrunning_mean,在 _BatchNorm 中描述了这两个参数的作用。

简单来说,BatchNorm 的 E(x)E(x)Var(x)\mathrm{Var}(x) 的来源有三种:

  • 当你特意地关闭更新开关时:来自于当前的 mini-batch。
  • 当你没做什么特别的事情:来自于一个可学习参数,这个参数在所有的 mini-batch 上平滑更新
    • x^(1m)x^+mx\hat x \leftarrow (1-m)\hat x+mx
  • 当你没做什么特别的事情,并且在推理阶段时:来自于训练阶段的可学习参数。

所以 BatchNorm 并不是简单的将数据以当前 batch 做标准化,通常情况会在相对更 global 的均值方差上归一化。

我们是如何锁住 Backbone 的#

一般我们是这么做的:

# set lr as 0
opti=Optimizer([
    {
        "params": "<params you want to freeze>",
        "lr": 0,
    }
])

# or set require_grad, which is more common in practice.
for param in model.parameters():
    param.requires_grad_(False)
python

发现问题了吗?BN 的平滑更新并非梯度优化,所以并没有被锁住。大多数情况这不是人们所期望的。

正确做法是同时将 BN 层切换到 eval 状态:

bn.eval()
python
如何正确地锁住一个 Normalization Layer?
https://astro-pure.js.org/blog/how-to-correctly-freeze-norm-layer
Author Cheng Chen
Published at 2023年7月13日
Comment seems to stuck. Try to refresh?✨