乐趣区

关于人工智能:PyTorch之BN核心参数详解

PyTorch 之 BN 外围参数详解

原始文档:https://www.yuque.com/lart/ug…

BN 是 CNN 中一种罕用的操作和模块。具体的实现中,其蕴含多个参数。这也导致了在不同的参数组合下,其会体现出不同的成果。

affine

初始化时批改

affine 设为 True 时,BatchNorm 层才会学习参数 gamma 和 beta,否则不蕴含这两个变量,变量名是 weight 和 bias。

.train()

  • 如果affine==True,则对归一化后的 batch 进行仿射变换,即乘以模块外部的 weight(初值是[1., 1., 1., 1.])而后加上模块外部的 bias(初值是[0., 0., 0., 0.]),这两个变量会在反向流传时失去更新。
  • 如果affine==False,则 BatchNorm 中不含有 weight 和 bias 两个变量,什么都都不做。

.eval()

  • 如果affine==True,则对归一化后的 batch 进行喷射变换,即乘以模块外部的 weight 而后加上模块外部的 bias,这两个变量都是网络训练时学习到的。
  • 如果affine==False,则 BatchNorm 中不含有 weight 和 bias 两个变量,什么都不做。

批改实例属性

无影响,仍依照初始化时的设定。

track_running_stats

因为 BN 的前向流传中波及到了该属性,所以实例属性的批改会影响最终的计算过程。

class _NormBase(Module):
    """Common base of _InstanceNorm and _BatchNorm"""
    _version = 2
    __constants__ = ['track_running_stats', 'momentum', 'eps',
                     'num_features', 'affine']
    num_features: int
    eps: float
    momentum: float
    affine: bool
    track_running_stats: bool
    # WARNING: weight and bias purposely not defined here.
    # See https://github.com/pytorch/pytorch/issues/39670

    def __init__(
        self,
        num_features: int,
        eps: float = 1e-5,
        momentum: float = 0.1,
        affine: bool = True,
        track_running_stats: bool = True
    ) -> None:
        super(_NormBase, self).__init__()
        self.num_features = num_features
        self.eps = eps
        self.momentum = momentum
        self.affine = affine
        self.track_running_stats = track_running_stats
        if self.affine:
            self.weight = Parameter(torch.Tensor(num_features))
            self.bias = Parameter(torch.Tensor(num_features))
        else:
            self.register_parameter('weight', None)
            self.register_parameter('bias', None)
        if self.track_running_stats:
            self.register_buffer('running_mean', torch.zeros(num_features))
            self.register_buffer('running_var', torch.ones(num_features))
            self.register_buffer('num_batches_tracked', torch.tensor(0, dtype=torch.long))
        else:
            self.register_parameter('running_mean', None)
            self.register_parameter('running_var', None)
            self.register_parameter('num_batches_tracked', None)
        self.reset_parameters()
    ...

class _BatchNorm(_NormBase):
    ...

    def forward(self, input: Tensor) -> Tensor:
        self._check_input_dim(input)
        if self.momentum is None:
            exponential_average_factor = 0.0
        else:
            exponential_average_factor = self.momentum

        if self.training and self.track_running_stats:
            if self.num_batches_tracked is not None:  # type: ignore
                self.num_batches_tracked = self.num_batches_tracked + 1  # type: ignore
                if self.momentum is None:  # use cumulative moving average
                    exponential_average_factor = 1.0 / float(self.num_batches_tracked)
                else:  # use exponential moving average
                    exponential_average_factor = self.momentum

        r"""
        Decide whether the mini-batch stats should be used for normalization rather than the buffers.
        Mini-batch stats are used in training mode, and in eval mode when buffers are None.

        能够看到这里的 bn_training 管制的是,数据运算应用以后 batch 计算失去的统计量(True)
        """
        if self.training:
            bn_training = True
        else:
            bn_training = (self.running_mean is None) and (self.running_var is None)

        r"""
        Buffers are only updated if they are to be tracked and we are in training mode. Thus they only need to be
        passed when the update should occur (i.e. in training mode when they are tracked), or when buffer stats are
        used for normalization (i.e. in eval mode when buffers are not None).

        这里强调的是统计量 buffer 的应用条件(self.running_mean, self.running_var)
        - training==True and track_running_stats==False, 这些属性被传入 F.batch_norm 中时,均替换为 None
        - training==True and track_running_stats==True, 会应用这些属性中寄存的内容
        - training==False and track_running_stats==True, 会应用这些属性中寄存的内容
        - training==False and track_running_stats==False, 会应用这些属性中寄存的内容
        """
        assert self.running_mean is None or isinstance(self.running_mean, torch.Tensor)
        assert self.running_var is None or isinstance(self.running_var, torch.Tensor)
        return F.batch_norm(
            input,
            # If buffers are not to be tracked, ensure that they won't be updated
            self.running_mean if not self.training or self.track_running_stats else None,
            self.running_var if not self.training or self.track_running_stats else None,
            self.weight, self.bias, bn_training, exponential_average_factor, self.eps)

.train()

留神代码中的正文:Buffers are only updated if they are to be tracked and we are in training mode. 即仅当为训练模式且 track_running_stats==True 时会更新这些统计量 buffer。

另外,此时self.training==Truebn_training=True

track_running_stats==True

BatchNorm 层会统计全局均值 running_mean 和方差 running_var,而对 batch 归一化时,仅应用以后 batch 的统计量。

            self.register_buffer('running_mean', torch.zeros(num_features))
            self.register_buffer('running_var', torch.ones(num_features))
            self.register_buffer('num_batches_tracked', torch.tensor(0, dtype=torch.long))

应用 momentum 更新模块外部的 running_mean。

  • 如果 momentum 是 None,那么就是用累计挪动均匀(这里会应用属性 self.num_batches_tracked 来统计曾经通过的 batch 数量),否则就应用指数挪动均匀(应用 momentum 作为系数)。二者的更新公式根本框架是一样的:$x_{new}=(1 – factor) \times x_{cur} + factor \times x_{batch}$
    ,只是具体的 $factor$ 有所不同。

    • $x_{new}$ 代表更新后的 running\_mean 和 running\_var;
    • $x_{cur}$ 示意更新前的 running\_mean 和 running\_var;
    • $x_{batch}\$ 示意以后 batch 的均值和无偏样本方差。
  • 累计挪动均匀的更新中 $factor=1/num\_batches\_tracked$。
  • 指数挪动均匀的更新公式是 $factor=momentum$。
批改实例属性

如果设置 .track_running_stats==False,此时self.num_batches_tracked 不会更新,而且 exponential_average_factor 也不会被从新调整。
而因为:

            self.running_mean if not self.training or self.track_running_stats else None,
            self.running_var if not self.training or self.track_running_stats else None,

且此时 self.training==True,并且self.track_running_stats==False,所以送入F.batch_normself.running_mean&self.running_var两个参数都是 None。
也就是说,此时和间接在初始化中设置 **track_running_stats==False** 是一样的成果。
然而要小心这里的 ~~exponential_average_factor~~ 的变动。不过因为通常咱们初始化 BN 时,仅仅会送入 ~~num_features~~,所以默认会应用~~exponential_average_factor = self.momentum~~ 来结构指数挪动均匀更新运行时统计量。(此时 exponential_average_factor 不会发挥作用)

track_running_stats==False

则 BatchNorm 中不含有 running\_mean 和 running\_var 两个变量,也就是仅仅应用以后 batch 的统计量来归一化 batch。

            self.register_parameter('running_mean', None)
            self.register_parameter('running_var', None)
            self.register_parameter('num_batches_tracked', None)
批改实例属性

如果设置 .track_running_stats==True,此时self.num_batches_tracked 依然不会更新,因为其初始值是 None。
整体来看,这样的批改并没有理论影响。

.eval()

此时self.training==False

            self.running_mean if not self.training or self.track_running_stats else None,
            self.running_var if not self.training or self.track_running_stats else None,

此时送入 F.batch_norm 的两个统计量 buffer 和初始化时的后果是统一的。

track_running_stats==True

            self.register_buffer('running_mean', torch.zeros(num_features))
            self.register_buffer('running_var', torch.ones(num_features))
            self.register_buffer('num_batches_tracked', torch.tensor(0, dtype=torch.long))

此时 bn_training = (self.running_mean is None) and (self.running_var is None) == False。所以应用全局的统计量。
对 batch 进行归一化,公式为 $y=\frac{x-\hat{E}[x]}{\sqrt{\hat{Var}[x]+\epsilon}}$,留神这里的均值和方差是 running\_mean 和 running\_var,在网络训练时统计进去的 全局均值和无偏样本方差

批改实例属性

如果设置 .track_running_stats==False,此时bn_training 不变,仍未 False,所以依然应用全局的统计量。也就是 self.running_mean, self.running_var 中寄存的内容。
整体而言,此时批改属性没有影响。

track_running_stats==False

            self.register_parameter('running_mean', None)
            self.register_parameter('running_var', None)
            self.register_parameter('num_batches_tracked', None)

此时 bn_training = (self.running_mean is None) and (self.running_var is None) == True。所以应用以后 batch 的统计量。
对 batch 进行归一化,公式为 $y=\frac{x-{E}[x]}{\sqrt{{Var}[x]+\epsilon }}$,留神这里的均值和方差是 batch 本人的 mean 和 var,此时 BatchNorm 里不含有 running_mean 和 running_var。
留神此时应用的是无偏样本方差(和训练时不同),因而如果 batch_size=1,会使分母为 0,就报错了。

批改实例属性

如果设置 .track_running_stats==True,此时bn_training 不变,仍为 True,所以依然应用以后 batch 的统计量。也就是疏忽 self.running_mean, self.running_var 中寄存的内容。
此时的行为和未修改时统一。

汇总

图片截图自原始文档。

参考

  • https://www.cnblogs.com/shuimuqingyang/p/14007260.html?ivk_sa=1024320u
退出移动版