如何在TensorFlow中为卷积层正确创建批量归一化层?

我查看了TensorFlow中官方的批量归一化层(BN),但它并没有真正解释如何将其用于卷积层。有人知道如何做到这一点吗?特别重要的是,它应该对每个特征图应用并学习相同的参数(而不是对每个激活值)。换句话说,它应该对每个滤波器应用并学习BN。

在一个特定的示例中,假设我想对MNIST(本质上是2D数据)进行conv2d并应用BN。因此,可以这样做:

W_conv1 = weight_variable([5, 5, 1, 32]) # 5x5的滤波器,共32个滤波器x_image = tf.reshape(x, [-1,28,28,1]) # MNIST图像conv = tf.nn.conv2d(x_image, W_conv1, strides=[1, 1, 1, 1], padding='VALID') #[?,24,24,1]z = conv # [?,24,24,32]z = BN(z) # [?,24,24,32],本质上只有32个不同的缩放和偏移参数需要学习,每个滤波器应用一次a = tf.nn.relu(z) # [?,24,24,32]

其中z = BN(z)对每个滤波器创建的每个特征应用BN。用伪代码表示:

x_patch = x[h:h+5,w:w+h,1] # 用于卷积的补丁z[h,w,f] = x_patch * W[:,:,f] = tf.matmul(x_patch, W[:,:,f]) # 卷积的实际矩阵乘法

我们为其应用了一个正确的批量归一化层(用伪代码省略了重要细节):

z[h,w,f] = BN(z[h,w,f]) = scale[f] * (z[h,w,f]  - mu / sigma) + shift[f]

即,对于每个滤波器f应用BN。


回答:

重要提示:我在这里提供的链接会影响tf.contrib.layers.batch_norm模块,而不是常用的tf.nn(参见下面的评论和帖子)

我没有测试过,但TensorFlow期望你使用的方式似乎在convolution2d文档字符串中有所记录:

def convolution2d(inputs,              num_outputs,              kernel_size,              stride=1,              padding='SAME',              activation_fn=nn.relu,              normalizer_fn=None,              normalizer_params=None,              weights_initializer=initializers.xavier_initializer(),              weights_regularizer=None,              biases_initializer=init_ops.zeros_initializer,              biases_regularizer=None,              reuse=None,              variables_collections=None,              outputs_collections=None,              trainable=True,              scope=None):  """添加一个2D卷积层,后跟一个可选的批量归一化层。  `convolution2d`创建一个名为`weights`的变量,表示卷积核,它与`inputs`卷积以产生一个激活值的`Tensor`。如果提供了`normalizer_fn`(如`batch_norm`),则会应用它。否则,如果`normalizer_fn`为None且提供了`biases_initializer`,则会创建一个`biases`变量并将其添加到激活值中。

根据这个建议,你应该在你的conv2d方法调用中添加normalizer_fn='batch_norm'作为参数。

关于特征图与激活值的问题,我的猜测是TensorFlow会在构建图时将归一化层作为卷积层的上一个“节点”添加,并且这两个层都会修改相同的权重变量(在你的例子中,是W_conv1对象)。我不会将归一化层的任务描述为“学习”,但我不太确定我是否理解了你的观点(如果你进一步说明,也许我可以提供更多的帮助)

编辑:仔细查看函数的主体确认了我的猜测,并解释了normalized_params参数的使用方式。从第354行读取:

outputs = nn.conv2d(inputs, weights, [1, stride_h, stride_w, 1],padding=padding)if normalizer_fn:  normalizer_params = normalizer_params or {}  outputs = normalizer_fn(outputs, **normalizer_params)else:  ...etc...

我们可以看到,保存每层输出结果的outputs变量是顺序覆盖的。因此,如果在构建图时给出了normalizer_fnnn.conv2d的输出将被normalizer_fn的额外层覆盖。此时,**normalizer_params作为关键字参数的可迭代对象传递给给定的normalizer_fn。你可以在这里找到batch_norm的默认参数,因此传递一个包含你希望更改的参数的字典给normalizer_params应该可以解决问题,像这样:

normalizer_params = {"epsilon" : 0.314592, "center" : False}

希望这对你有帮助!

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注