tf.nn.conv2d在tensorflow中做了什么?

时间:2016-01-05 18:51:57

标签: neural-network tensorflow

我正在查看有关tf.nn.conv2d here的张量流的文档。但我无法理解它的作用或它想要实现的目标。它在文档中说,

  

#1:将滤镜展平为具有形状

的二维矩阵      

[filter_height * filter_width * in_channels, output_channels]

现在该怎么办?是元素乘法还是纯矩阵乘法?我也无法理解文档中提到的其他两点。我在下面写了这些:

  

#2:从输入张量中提取图像块以形成虚拟张量形状

     

[batch, out_height, out_width, filter_height * filter_width * in_channels]

     

#3:对于每个补丁,右对乘滤波器矩阵和图像补丁矢量。

如果有人可以提供一个例子,一段代码(非常有用),并且解释那里发生的事情以及为什么操作就是这样,那将非常有用。

我尝试过编码一小部分并打印出操作的形状。不过,我无法理解。

我试过这样的事情:

op = tf.shape(tf.nn.conv2d(tf.random_normal([1,10,10,10]), 
              tf.random_normal([2,10,10,10]), 
              strides=[1, 2, 2, 1], padding='SAME'))

with tf.Session() as sess:
    result = sess.run(op)
    print(result)

我理解卷积神经网络的点点滴滴。我研究了它们here。但是,张量流的实现并不是我的预期。所以它提出了这个问题。

修改: 所以,我实现了一个更简单的代码。但我无法弄清楚发生了什么。我的意思是结果是这样的。如果有人能告诉我什么过程产生这个输出,那将是非常有帮助的。

input = tf.Variable(tf.random_normal([1,2,2,1]))
filter = tf.Variable(tf.random_normal([1,1,1,1]))

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='SAME')
init = tf.initialize_all_variables()
with tf.Session() as sess:
    sess.run(init)

    print("input")
    print(input.eval())
    print("filter")
    print(filter.eval())
    print("result")
    result = sess.run(op)
    print(result)

输出

input
[[[[ 1.60314465]
   [-0.55022103]]

  [[ 0.00595062]
   [-0.69889867]]]]
filter
[[[[-0.59594476]]]]
result
[[[[-0.95538563]
   [ 0.32790133]]

  [[-0.00354624]
   [ 0.41650501]]]]

7 个答案:

答案 0 :(得分:154)

好的我认为这是解释这一切的最简单方法。

您的示例是1张图片,尺寸2x2,有1个频道。您有1个过滤器,大小为1x1,1个通道(大小为高x宽x通道x过滤器数)。

对于这种简单的情况,得到的2x2,1通道图像(尺寸1x2x2x1,图像数x高x宽x x通道)是将滤镜值乘以图像的每个像素的结果。

现在让我们尝试更多频道:

input = tf.Variable(tf.random_normal([1,3,3,5]))
filter = tf.Variable(tf.random_normal([1,1,5,1]))

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')

这里3x3图像和1x1滤镜各有5个通道。生成的图像为3x3,有1个通道(大小为1x3x3x1),其中每个像素的值是滤镜通道上的点积与输入图像中的相应像素。

现在使用3x3过滤器

input = tf.Variable(tf.random_normal([1,3,3,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')

这里我们得到1x1图像,1个通道(大小为1x1x1x1)。该值是9,5元素点积的总和。但你可以称之为45元素点产品。

现在有了更大的形象

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='VALID')

输出为3x3 1通道图像(尺寸1x3x3x1)。 这些值中的每一个都是9,5个元素点积的总和。

每个输出都是通过将滤镜置于输入图像的9个中心像素之一上进行的,这样滤镜就不会突出。下面的x代表每个输出像素的滤镜中心。

.....
.xxx.
.xxx.
.xxx.
.....

现在使用“SAME”填充:

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='SAME')

这会产生5x5输出图像(尺寸1x5x5x1)。这是通过将滤镜置于图像上的每个位置来完成的。

过滤器伸出图像边缘的任何5元素点积都会得到零值。

所以角落只是4个5元素点积的总和。

现在有多个过滤器。

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='SAME')

这仍然提供5x5输出图像,但有7个通道(大小为1x5x5x7)。每个通道由集合中的一个过滤器生成。

现在大步前进2,2:

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op = tf.nn.conv2d(input, filter, strides=[1, 2, 2, 1], padding='SAME')

现在结果仍有7个通道,但只有3x3(大小为1x3x3x7)。

这是因为滤镜不是将滤镜置于图像上的每个点上,而是以图像上的每个其他点为中心,采用宽度为2的步长(步幅)。下面的x代表在输入图像上为每个输出像素过滤中心。

x.x.x
.....
x.x.x
.....
x.x.x

当然,输入的第一个维度是图像的数量,因此您可以将其应用于一批10个图像,例如:

input = tf.Variable(tf.random_normal([10,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,7]))

op = tf.nn.conv2d(input, filter, strides=[1, 2, 2, 1], padding='SAME')

对每个图像独立执行相同的操作,结果显示10个图像的堆栈(大小为10x3x3x7)

答案 1 :(得分:44)

2D卷积的计算方法类似于计算1D convolution的方法:在输入上滑动内核,计算逐元素乘法并求它们。但是你的内核/输入不是数组,而是矩阵。

在最基本的示例中,没有填充和stride = 1。我们假设您的inputkernel是: enter image description here

当您使用内核时,您将收到以下输出:enter image description here,其计算方式如下:

  • 14 = 4 * 1 + 3 * 0 + 1 * 1 + 2 * 2 + 1 * 1 + 0 * 0 + 1 * 0 + 2 * 0 + 4 * 1
  • 6 = 3 * 1 + 1 * 0 + 0 * 1 + 1 * 2 + 0 * 1 + 1 * 0 + 2 * 0 + 4 * 0 + 1 * 1
  • 6 = 2 * 1 + 1 * 0 + 0 * 1 + 1 * 2 + 2 * 1 + 4 * 0 + 3 * 0 + 1 * 0 + 0 * 1
  • 12 = 1 * 1 + 0 * 0 + 1 * 1 + 2 * 2 + 4 * 1 + 1 * 0 + 1 * 0 + 0 * 0 + 2 * 1

TF' conv2d功能分批计算卷积并使用略有不同的格式。对于输入,内核为[batch, in_height, in_width, in_channels] [filter_height, filter_width, in_channels, out_channels]。所以我们需要以正确的格式提供数据:

import tensorflow as tf
k = tf.constant([
    [1, 0, 1],
    [2, 1, 0],
    [0, 0, 1]
], dtype=tf.float32, name='k')
i = tf.constant([
    [4, 3, 1, 0],
    [2, 1, 0, 1],
    [1, 2, 4, 1],
    [3, 1, 0, 2]
], dtype=tf.float32, name='i')
kernel = tf.reshape(k, [3, 3, 1, 1], name='kernel')
image  = tf.reshape(i, [1, 4, 4, 1], name='image')

然后用以下计算卷积:

res = tf.squeeze(tf.nn.conv2d(image, kernel, [1, 1, 1, 1], "VALID"))
# VALID means no padding
with tf.Session() as sess:
   print sess.run(res)

并且相当于我们手工计算的那个。

examples with padding/strides, take a look here

答案 2 :(得分:8)

只需添加其他答案,您应该考虑

中的参数
filter = tf.Variable(tf.random_normal([3,3,5,7]))

为'5',对应于每个滤波器中的通道数。每个滤镜都是一个3d立方体,深度为5.滤镜深度必须与输入图像的深度相对应。最后一个参数7应该被认为是批次中的过滤器数量。只是忘记这是4D,而是想象你有一组或一批7个过滤器。你要做的是创建7个尺寸为(3,3,5)的滤镜立方体。

在傅里叶域中可视化要容易得多,因为卷积成为逐点乘法。对于尺寸(100,100,3)的输入图像,您可以将过滤器尺寸重写为

filter = tf.Variable(tf.random_normal([100,100,3,7]))

为了获得7个输出特征图中的一个,我们简单地执行滤镜立方体与图像立方体的逐点乘法,然后我们将通道/深度维度(这里是3)的结果相加,折叠到2d(100,100)特征图。对每个滤镜立方体执行此操作,您将获得7个2D要素贴图。

答案 3 :(得分:8)

我试图实施conv2d(我的学习)。好吧,我写道:

def conv(ix, w):
   # filter shape: [filter_height, filter_width, in_channels, out_channels]
   # flatten filters
   filter_height = int(w.shape[0])
   filter_width = int(w.shape[1])
   in_channels = int(w.shape[2])
   out_channels = int(w.shape[3])
   ix_height = int(ix.shape[1])
   ix_width = int(ix.shape[2])
   ix_channels = int(ix.shape[3])
   filter_shape = [filter_height, filter_width, in_channels, out_channels]
   flat_w = tf.reshape(w, [filter_height * filter_width * in_channels, out_channels])
   patches = tf.extract_image_patches(
       ix,
       ksizes=[1, filter_height, filter_width, 1],
       strides=[1, 1, 1, 1],
       rates=[1, 1, 1, 1],
       padding='SAME'
   )
   patches_reshaped = tf.reshape(patches, [-1, ix_height, ix_width, filter_height * filter_width * ix_channels])
   feature_maps = []
   for i in range(out_channels):
       feature_map = tf.reduce_sum(tf.multiply(flat_w[:, i], patches_reshaped), axis=3, keep_dims=True)
       feature_maps.append(feature_map)
   features = tf.concat(feature_maps, axis=3)
   return features

希望我做得好。检查MNIST,结果非常接近(但实施速度较慢)。我希望这会对你有所帮助。

答案 4 :(得分:0)

除了其他答案之外,conv2d操作在c ++(cpu)或cuda中运行,需要以某种方式展平和重塑数据并使用gemmBLAS或cuBLAS(cuda)矩阵乘法。

答案 5 :(得分:0)

当您尝试例如图像分类thuis函数具有执行此操作所需的所有参数时,它会在整个图片中进行抽搐。

基本上您可以选择过滤器尺寸。大步前进。填充。在使用它之前,需要不去理解卷积的概念

答案 6 :(得分:0)

这个解释补充:

Keras Conv2d own filters

我对 keras.conv2d 中的 filter 参数有一些疑问,因为当我了解到我应该设置自己的过滤器设计时。但是这个参数告诉我们要测试多少个过滤器,keras 本身会尝试找到最好的过滤器权重。