Tensorflow:tf.nn.separable_conv2d 是做什么的?

Tensorflow: What does tf.nn.separable_conv2d do?

我不太确定 tf.nn.separable_conv2d 的具体作用。似乎pointwise_filter是生成下一层的一个像素时不同特征的缩放因子。但我不确定我的解释是否正确。这种方法有什么参考吗?有什么好处?

tf.nn.separable_conv2d 生成与 tf.nn.conv2d 相同的形状。我假设我可以用 tf.nn.separable_conv2d 替换 tf.nn.conv2d。但是使用 tf.nn.separable_conv2d 时的结果似乎很糟糕。网络很早就停止了学习。对于 MNIST 数据集,准确率只是随机猜测 ~ 10%。

我想当我将 pointwise_filter 值设置为全部 1.0 并使其不可训练时,我会得到与 tf.nn.conv2d 相同的结果。但不是真的......仍然有 ~10% 的准确率。

但是当tf.nn.conv2d使用相同的超参数时,准确率可以达到99%。为什么?

此外,它需要 channel_multiplier * in_channels < out_channels。为什么?这里channel_multiplier的作用是什么?

谢谢。

编辑:

我以前使用 channel_multiplier 作为 1.0。也许这是一个糟糕的选择。我把它改成2.0后,精度就好多了。但是channel_multiplier的作用是什么?为什么 1.0 不是一个好的值?

tf.nn.separable_conv2d() 实现了 slide 26 and onwards of this talk 中描述的所谓 'separable convolution'。

这个想法是,不是在图像的所有通道上联合卷积,而是在每个通道上 运行 深度为 channel_multiplier 的单独 2D 卷积。 in_channels * channel_multiplier 中间通道连接在一起,并使用 1x1 卷积映射到 out_channels

这通常是降低卷积网络中早期卷积的参数复杂性的有效方法,并且可以显着加快训练速度。 channel_multiplier 控制复杂度,对于 RGB 输入通常为 4 到 8。对于灰度输入,使用它意义不大。

回答问题的最后一部分:

Also, it requires channel_multiplier * in_channels < out_channels. Why?

不知道当初为什么要加这个约束,现在TF的master分支已经去掉了,应该会到1.3版本。想法可能与 "If you are reducing the reducing the number of channels in the pointwise step, you might have as well picked a smaller channel multiplier and saved on computation" 类似。我想这个推理是有缺陷的,因为逐点步骤可以组合来自不同 depthwise_filters 的值,或者可能是因为人们可能想稍微减少维度,而不是减少一个完整因子。

在对多个输入通道执行的常规 2D 卷积中,过滤器与输入一样深,让我们可以自由混合通道以生成输出中的每个元素。 Depthwise convolutions 不这样做——每个通道都是分开的——因此得名 depthwise。这是一张图表,可帮助解释其工作原理[1]:

如果您查看官方文档,您会发现:

output[b, i, j, k] = sum_{di, dj, q, r} input[b, strides[1] * i + di, strides[2] * j + dj, q] * depthwise_filter[di, dj, q, r] * pointwise_filter[0, 0, q * channel_multiplier + r, k]

以及tensorflow中的示例代码进行测试:

import tensorflow as tf
import numpy as np

width = 8
height = 8
batch_size = 100
filter_height = 3
filter_width = 3
in_channels = 3
channel_multiplier = 1
out_channels = 3


input_tensor = tf.get_variable(shape=(batch_size, height, width, in_channels), name="input")
depthwise_filter = tf.get_variable(shape=(filter_height, filter_width, in_channels, channel_multiplier), name="deptwise_filter")
pointwise_filter = tf.get_variable(shape=[1, 1, channel_multiplier * in_channels, out_channels], name="pointwise_filter")

output = tf.nn.separable_conv2d(
    input_tensor,
    depthwise_filter,
    pointwise_filter,
    strides=[1,1,1,1],
    padding='SAME',
)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())

    output_value = sess.run(output, feed_dict={input_tensor: np.random.rand(batch_size, width, height, in_channels),
                                               depthwise_filter: np.random.rand(filter_height, filter_width, in_channels, channel_multiplier),
                                               pointwise_filter: np.random.rand(1, 1, channel_multiplier * in_channels, out_channels)})
    print(np.shape(output_value))

信用:

[1] https://eli.thegreenplace.net/2018/depthwise-separable-convolutions-for-machine-learning/

[2]https://www.tensorflow.org/api_docs/python/tf/nn/separable_conv2d