我在哪里调用 Keras 中的 BatchNormalization 函数?

Where do I call the BatchNormalization function in Keras?

如果我想在Keras中使用BatchNormalization函数,是否只需要在开始时调用一次?

我阅读了这个文档:http://keras.io/layers/normalization/

我不知道应该在哪里调用它。下面是我尝试使用它的代码:

model = Sequential()
keras.layers.normalization.BatchNormalization(epsilon=1e-06, mode=0, momentum=0.9, weights=None)
model.add(Dense(64, input_dim=14, init='uniform'))
model.add(Activation('tanh'))
model.add(Dropout(0.5))
model.add(Dense(64, init='uniform'))
model.add(Activation('tanh'))
model.add(Dropout(0.5))
model.add(Dense(2, init='uniform'))
model.add(Activation('softmax'))

sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='binary_crossentropy', optimizer=sgd)
model.fit(X_train, y_train, nb_epoch=20, batch_size=16, show_accuracy=True, validation_split=0.2, verbose = 2)

我问是因为如果我 运行 第二行的代码包括批量归一化,如果我 运行 没有第二行的代码我得到类似的输出。所以要么我没有在正确的地方调用函数,要么我猜它并没有太大的不同。

这是另一种类型的图层,因此您应该将其作为图层添加到模型的适当位置

model.add(keras.layers.normalization.BatchNormalization())

请在此处查看示例:https://github.com/fchollet/keras/blob/master/examples/kaggle_otto_nn.py

只是更详细地回答这个问题,正如 Pavel 所说,批量归一化只是另一层,因此您可以直接使用它来创建所需的网络架构。

一般用例是在网络中的线性层和非线性层之间使用 BN,因为它对激活函数的输入进行归一化,因此您位于激活函数的线性部分的中心(例如乙状结肠)。有一个小讨论here

在你上面的例子中,这可能看起来像:


# import BatchNormalization
from keras.layers.normalization import BatchNormalization

# instantiate model
model = Sequential()

# we can think of this chunk as the input layer
model.add(Dense(64, input_dim=14, init='uniform'))
model.add(BatchNormalization())
model.add(Activation('tanh'))
model.add(Dropout(0.5))

# we can think of this chunk as the hidden layer    
model.add(Dense(64, init='uniform'))
model.add(BatchNormalization())
model.add(Activation('tanh'))
model.add(Dropout(0.5))

# we can think of this chunk as the output layer
model.add(Dense(2, init='uniform'))
model.add(BatchNormalization())
model.add(Activation('softmax'))

# setting up the optimization of our weights 
sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='binary_crossentropy', optimizer=sgd)

# running the fitting
model.fit(X_train, y_train, nb_epoch=20, batch_size=16, show_accuracy=True, validation_split=0.2, verbose = 2)

希望这能让事情更清楚一些。

Conv2D 之后是 ReLu 之后是 BatchNormalization 层几乎已经成为一种趋势。所以我编写了一个小函数来一次调用所有这些函数。使模型定义看起来更加清晰易读。

def Conv2DReluBatchNorm(n_filter, w_filter, h_filter, inputs):
    return BatchNormalization()(Activation(activation='relu')(Convolution2D(n_filter, w_filter, h_filter, border_mode='same')(inputs)))

Keras 现在支持 use_bias=False 选项,所以我们可以通过这样写来节省一些计算量

model.add(Dense(64, use_bias=False))
model.add(BatchNormalization(axis=bn_axis))
model.add(Activation('tanh'))

model.add(Convolution2D(64, 3, 3, use_bias=False))
model.add(BatchNormalization(axis=bn_axis))
model.add(Activation('relu'))

该线程具有误导性。尝试对 Lucas Ramadan 的回答发表评论,但我还没有合适的权限,所以我就把它放在这里。

批量归一化在激活函数之后效果最好,here or here 原因是:开发它是为了防止内部协变量偏移。当层的 activations 的分布在整个训练过程中发生显着变化时,就会发生内部协变量偏移。使用批归一化,以便特定层的输入(这些输入实际上是激活函数的结果)的分布不会因每个批次的参数更新而随时间改变(或者至少允许它改变)以有利的方式)。它使用批量统计进行归一化,然后使用批量归一化参数(原始论文中的 gamma 和 beta)"to make sure that the transformation inserted in the network can represent the identity transform"(引用自原始论文)。但关键是我们正在尝试规范化一个层的输入,所以它应该总是紧接在网络中的下一层之前。这是否在激活函数之后取决于所讨论的体系结构。

关于 BN 应该在当前层的非线性之前应用还是在前一层的激活之前应用,这个线程有一些相当大的争论。

虽然没有正确答案,但是Batch Normalization的作者说 它应该在当前层的非线性之前立即应用。 原因(引用自原始论文)-

"We add the BN transform immediately before the nonlinearity, by normalizing x = Wu+b. We could have also normalized the layer inputs u, but since u is likely the output of another nonlinearity, the shape of its distribution is likely to change during training, and constraining its first and second moments would not eliminate the covariate shift. In contrast, Wu + b is more likely to have a symmetric, non-sparse distribution, that is “more Gaussian” (Hyv¨arinen & Oja, 2000); normalizing it is likely to produce activations with a stable distribution."

批归一化用于通过调整激活的均值和缩放比例来归一化输入层和隐藏层。由于深度神经网络中附加层的这种归一化效果,网络可以使用更高的学习率而不会消失或爆炸梯度。此外,batch normalization 对网络进行正则化,使其更容易泛化,因此无需使用 dropout 来减轻过度拟合。

在使用 Keras 中的 Dense() 或 Conv2D() 计算线性函数之后,我们使用 BatchNormalization() 计算层中的线性函数,然后我们将非线性添加到层中使用激活().

from keras.layers.normalization import BatchNormalization
model = Sequential()
model.add(Dense(64, input_dim=14, init='uniform'))
model.add(BatchNormalization(epsilon=1e-06, mode=0, momentum=0.9, weights=None))
model.add(Activation('tanh'))
model.add(Dropout(0.5))
model.add(Dense(64, init='uniform'))
model.add(BatchNormalization(epsilon=1e-06, mode=0, momentum=0.9, weights=None))
model.add(Activation('tanh'))
model.add(Dropout(0.5))
model.add(Dense(2, init='uniform'))
model.add(BatchNormalization(epsilon=1e-06, mode=0, momentum=0.9, weights=None))
model.add(Activation('softmax'))

sgd = SGD(lr=0.1, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='binary_crossentropy', optimizer=sgd)
model.fit(X_train, y_train, nb_epoch=20, batch_size=16, show_accuracy=True, 
validation_split=0.2, verbose = 2)

如何应用批量归一化?

假设我们将a[l-1]输入到第l层。我们还有层 l 的权重 W[l] 和偏置单元 b[l]。令 a[l] 为层 l 计算的激活向量(即添加非线性后),z[l] 为添加非线性前的向量

  1. 使用a[l-1]和W[l]我们可以计算层l
  2. 的z[l]
  3. 通常在前馈传播中我们会像这样z[l]+b[l]在这个阶段给z[l]添加偏置单元,但是在Batch Normalization中这一步添加了b[l]不需要,也没有使用 b[l] 参数。
  4. 计算z[l]均值并从每个元素中减去它
  5. 使用标准偏差除以 (z[l] - 平均值)。称之为 Z_temp[l]
  6. 现在定义新的参数 γ 和 β 来改变隐藏层的尺度如下:

    z_norm[l] = γ.Z_temp[l] + β

在此代码摘录中,Dense() 获取 a[l-1],使用 W[l] 并计算 z[l]。然后立即的BatchNormalization()会执行上面的步骤,得到z_norm[l]。然后立即 Activation() 将计算 tanh(z_norm[l]) 以给出 a[l] 即

a[l] = tanh(z_norm[l])

在非线性激活之前或之后调用batch normalization的争论再添一条:

除了原始论文在激活之前使用批归一化之外,Bengio 的书 Deep Learning, section 8.7.1 给出了一些推理,说明为什么在激活之后(或直接在输入到下一层之前)应用批归一化可能会导致一些问题:

It is natural to wonder whether we should apply batch normalization to the input X, or to the transformed value XW+b. Ioffe and Szegedy (2015) recommend the latter. More specifically, XW+b should be replaced by a normalized version of XW. The bias term should be omitted because it becomes redundant with the β parameter applied by the batch normalization reparameterization. The input to a layer is usually the output of a nonlinear activation function such as the rectified linear function in a previous layer. The statistics of the input are thus more non-Gaussian and less amenable to standardization by linear operations.

换句话说,如果我们使用 relu 激活,所有负值都映射为零。这可能会导致平均值已经非常接近于零,但剩余数据的分布将严重偏向右侧。尝试将该数据标准化为漂亮的钟形曲线可能不会产生最佳结果。对于 relu 系列之外的激活,这可能不是什么大问题。

请记住,有报告称模型在激活后使用批量归一化会获得更好的结果,而其他模型在激活前使用批量归一化会获得最佳结果。最好使用这两种配置来测试您的模型,如果激活后的批量归一化显着降低了验证损失,请改用该配置。