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==True
。bn_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_norm
的self.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