我查看了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_fn
,nn.conv2d
的输出将被normalizer_fn
的额外层覆盖。此时,**normalizer_params
作为关键字参数的可迭代对象传递给给定的normalizer_fn
。你可以在这里找到batch_norm
的默认参数,因此传递一个包含你希望更改的参数的字典给normalizer_params应该可以解决问题,像这样:
normalizer_params = {"epsilon" : 0.314592, "center" : False}
希望这对你有帮助!