为什么 Keras BatchNorm 产生的输出与 PyTorch 不同?

cal*_*_ye 1 deep-learning keras tensorflow pytorch batch-normalization

火炬\xef\xbc\x9a\'1.9.0+cu111\'

\n

Tensorflow-gpu\xef\xbc\x9a\'2.5.0\'

\n

我遇到一个奇怪的事情,当使用tensorflow 2.5的Batch Normal层和Pytorch 1.9的BatchNorm2d层计算相同的Tensor时,结果有很大不同(TensorFlow接近1,Pytorch接近0)。我一开始以为是动量和epsilon的区别,但是把它们改成相同后,结果是一样的。

\n
from torch import nn\nimport torch\nx = torch.ones((20, 100, 35, 45))\na = nn.Sequential(\n            # nn.Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), padding=0, bias=True),\n            nn.BatchNorm2d(100)\n        )\nb = a(x)\n\nimport tensorflow as tf\nimport tensorflow.keras as keras\nfrom tensorflow.keras.layers import *\nx = tf.ones((20, 35, 45, 100))\na = keras.models.Sequential([\n            # Conv2D(128, (1, 1), (1, 1), padding=\'same\', use_bias=True),\n            BatchNormalization()\n        ])\nb = a(x)\n
Run Code Online (Sandbox Code Playgroud)\n

TensorFlow 的结果

\n

Pytorch的结果

\n

Zab*_*azi 5

批量归一化在训练和推理中的工作方式不同,

在训练期间(即使用fit()或使用参数调用层/模型时training=True),层使用当前批次输入的平均值和标准差标准化其输出。也就是说,对于每个被归一化的通道,该层返回

gamma * (batch - mean(batch)) / sqrt(var(batch) + epsilon) + beta
Run Code Online (Sandbox Code Playgroud)

在哪里:

  • epsilon是一个小常量(可配置为构造函数参数的一部分)
  • gamma是一个学习的缩放因子(初始化为 1),可以通过将 scale=False 传递给构造函数来禁用它。
  • beta是一个学习的偏移因子(初始化为 0),可以通过将 center=False 传递给构造函数来禁用它。

在推理过程中(即,当使用evaluate()predict()或使用参数调用层/模型时training=False(这是默认值),该层使用在训练期间看到的批次的平均值和标准差的移动平均值来规范化其输出。即说,它返回

gamma * (batch - self.moving_mean) / sqrt(self.moving_var + epsilon) + beta.
Run Code Online (Sandbox Code Playgroud)

self.moving_meanself.moving_var是不可训练的变量,每次在训练模式下调用层时都会更新,如下所示:

gamma * (batch - mean(batch)) / sqrt(var(batch) + epsilon) + beta
Run Code Online (Sandbox Code Playgroud)

参考: https: //www.tensorflow.org/api_docs/python/tf/keras/layers/BatchNormalization

如果你在模式下运行pytorch batchnorm eval,你会得到接近的结果(其余的差异来自不同的内部实现、参数选择等),

from torch import nn
import torch
x = torch.ones((1, 2, 2, 2))
a = nn.Sequential(
            # nn.Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), padding=0, bias=True),
            nn.BatchNorm2d(2)
        )
a.eval()
b = a(x)
print(b)
import tensorflow as tf
import tensorflow.keras as keras
from tensorflow.keras.layers import *
x = tf.ones((1, 2, 2, 2))
a = keras.models.Sequential([
            # Conv2D(128, (1, 1), (1, 1), padding='same', use_bias=True),
            BatchNormalization()
        ])
b = a(x)
print(b)
Run Code Online (Sandbox Code Playgroud)

out:

tensor([[[[1.0000, 1.0000],
          [1.0000, 1.0000]],

         [[1.0000, 1.0000],
          [1.0000, 1.0000]]]], grad_fn=<NativeBatchNormBackward>)
tf.Tensor(
[[[[0.9995004 0.9995004]
   [0.9995004 0.9995004]]

  [[0.9995004 0.9995004]
   [0.9995004 0.9995004]]]], shape=(1, 2, 2, 2), dtype=float32)
Run Code Online (Sandbox Code Playgroud)