0

我正在使用BatchNormalization层,但我无法弄清楚我得到的数值结果。

让我们考虑一下我们将 BatchNormalization 用于计算机视觉。

我们有 4D 张量。

维度是:批量大小图像高度图像宽度通道

如果我理解正确,BatchNormalization 会做的是:

  1. 训练时:
    • 对于每个批次,计算平均MU和标准偏差SIGMA。这是按通道完成的,并且跨越批次的所有图像的所有行和所有列。
    • 在所有批次中保持MU(例如)和SIGMA(例如SIĜMA )的指数移动平均值
    • 使用SIĜMA归一化像素:normalized_pixel = ((input_pixel - MÛ) / sqrt(SIĜMA))
    • 如果SIĜMA在训练期间的某一时刻变为空,则将超参数epsilon添加到SIĜMA以防止除以零: normalized_pixel = ((input_pixel - MÛ) / sqrt(SIĜMA + epsilon))
    • 使用缩放参数GAMMA和偏移参数BETA重新缩放归一化像素:output_pixel = ((GAMMA x normalized_pixel) + BETA)
    • GAMMABETA是可训练的参数,它们在训练期间进行了优化
  2. 在推理时:
    • SIĜMA现在是固定参数,就像GAMMABETA
    • 相同的计算适用

现在,我的问题来了……

首先,我只对推理时发生的事情感兴趣。我不关心训练,我认为SIĜMAGAMMABETA是固定参数。

我写了一段 python 来测试(1, 3, 4, 1)张量上的 BatchNormalization。由于只有一个通道,因此 MÛSIĜMAGAMMABETA各只有 1 个元素。

我选择了MÛ = 0.0SIĜMA = 1.0GAMMA = 1.0BETA = 0.0,所以 BatchNormalization 没有效果。

这是代码:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import numpy
import keras
import math

input_batch                         =   numpy.array(
                                            [[
                                                [[ 1.0], [ 2.0], [ 3.0], [ 4.0]],
                                                [[ 5.0], [ 6.0], [ 7.0], [ 8.0]],
                                                [[ 9.0], [10.0], [11.0], [12.0]]
                                            ]],
                                            dtype=numpy.float32
                                        )


MU                                  = 0.0
SIGMA                               = 1.0
GAMMA                               = 1.0
BETA                                = 0.0

input_layer                         =   keras.layers.Input(
                                            shape = (
                                                        None,
                                                        None,
                                                        1
                                                    )
                                        )

BatchNormalization_layer            =   keras.layers.BatchNormalization(
                                            axis=-1,
                                            #epsilon=0.0,
                                            center=True,
                                            scale=True
                                        )(
                                            input_layer
                                        )

model                               =   keras.models.Model(
                                            inputs  = [input_layer],
                                            outputs = [BatchNormalization_layer]
                                        )

model.layers[1].set_weights(
    (
        numpy.array([GAMMA], dtype=numpy.float32),
        numpy.array([BETA],  dtype=numpy.float32),
        numpy.array([MU],    dtype=numpy.float32),
        numpy.array([SIGMA], dtype=numpy.float32),
    )
)

print model.predict(input_batch)

print ((((input_batch - MU) / math.sqrt(SIGMA)) * GAMMA) + BETA)

当我((((input_batch - MU) / math.sqrt(SIGMA)) * GAMMA) + BETA)使用 numpy 明确编写计算时,我得到了预期的结果。

但是,当我使用 keras.layers.BatchNormalization 层执行计算时,我得到了类似的结果,只是存在某种舍入误差或不精确:

Using TensorFlow backend.
[[[[ 0.9995004]
   [ 1.9990008]
   [ 2.9985013]
   [ 3.9980016]]

  [[ 4.997502 ]
   [ 5.9970026]
   [ 6.996503 ]
   [ 7.996003 ]]

  [[ 8.995503 ]
   [ 9.995004 ]
   [10.994504 ]
   [11.994005 ]]]]
[[[[ 1.]
   [ 2.]
   [ 3.]
   [ 4.]]

  [[ 5.]
   [ 6.]
   [ 7.]
   [ 8.]]

  [[ 9.]
   [10.]
   [11.]
   [12.]]]]

当我使用 MU*、SIGMA*、GAMMA 和 BETA 的值时,输出会受到预期的影响,所以我相信我正确地向层提供了参数。

我还尝试将图层的超参数 epsilon 设置为 0.0。它会稍微改变结果,但不能解决问题。

Using TensorFlow backend.
[[[[ 0.999995 ]
   [ 1.99999  ]
   [ 2.999985 ]
   [ 3.99998  ]]

  [[ 4.999975 ]
   [ 5.99997  ]
   [ 6.9999647]
   [ 7.99996  ]]

  [[ 8.999955 ]
   [ 9.99995  ]
   [10.999945 ]
   [11.99994  ]]]]
[[[[ 1.]
   [ 2.]
   [ 3.]
   [ 4.]]

  [[ 5.]
   [ 6.]
   [ 7.]
   [ 8.]]

  [[ 9.]
   [10.]
   [11.]
   [12.]]]]

有人可以解释发生了什么吗?

谢谢,

朱利安

4

1 回答 1

1

我研究了 tensorflow 代码(被 keras 称为后端)。在 batch_normalization 的代码中,我读到:

  # Set a minimum epsilon to 1.001e-5, which is a requirement by CUDNN to
  # prevent exception (see cudnn.h).
  min_epsilon = 1.001e-5
  epsilon = epsilon if epsilon > min_epsilon else min_epsilon

解释为什么在 keras 中设置epsilon = 0.0不起作用。

在我的脚本中考虑 epsilon 时,我得到了正确的结果......

((((input_batch - MU) / math.sqrt(SIGMA + EPSILON)) * GAMMA) + BETA)
Using TensorFlow backend.
[[[[ 0.99503714]
   [ 1.9900743 ]
   [ 2.9851115 ]
   [ 3.9801486 ]]

  [[ 4.975186  ]
   [ 5.970223  ]
   [ 6.96526   ]
   [ 7.960297  ]]

  [[ 8.955335  ]
   [ 9.950372  ]
   [10.945409  ]
   [11.940446  ]]]]
[[[[ 0.99503714]
   [ 1.9900743 ]
   [ 2.9851115 ]
   [ 3.9801486 ]]

  [[ 4.975186  ]
   [ 5.970223  ]
   [ 6.96526   ]
   [ 7.960297  ]]

  [[ 8.955335  ]
   [ 9.950372  ]
   [10.945409  ]
   [11.940446  ]]]]
于 2019-11-29T01:07:36.263 回答