我在tf.nn.conv2d
这里看一下tensorflow的文档.但我无法理解它的作用或它想要实现的目标.它在文档上说,
#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)
我理解卷积神经网络的点点滴滴.我在这里研究过它们.但是,张量流的实现并不是我的预期.所以它提出了这个问题.
编辑:所以,我实现了一个更简单的代码.但我无法弄清楚发生了什么.我的意思是结果是这样的.如果有人能告诉我什么过程产生这个输出,那将是非常有帮助的.
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]]]]
mdaoust.. 155
好吧,我认为这是解释这一切的最简单方法.
您的示例是1个图像,大小为2x2,带有1个通道.您有1个过滤器,大小为1x1,1个通道(大小为高x宽x通道x过滤器数).
对于这种简单的情况,得到的2x2,1通道图像(尺寸1x2x2x1,图像数x高x宽xx通道)是将滤波器值乘以图像的每个像素的结果.
现在让我们尝试更多渠道:
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
下面的s表示每个输出像素的滤波器中心.
..... .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个图像,大小为2x2,带有1个通道.您有1个过滤器,大小为1x1,1个通道(大小为高x宽x通道x过滤器数).
对于这种简单的情况,得到的2x2,1通道图像(尺寸1x2x2x1,图像数x高x宽xx通道)是将滤波器值乘以图像的每个像素的结果.
现在让我们尝试更多渠道:
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
下面的s表示每个输出像素的滤波器中心.
..... .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)
2D卷积的计算方法与计算1D卷积的方式类似:在输入上滑动内核,计算元素乘法并将它们相加.但是代替你的内核/输入是一个数组,这里它们是矩阵.
在最基本的示例中,没有填充和stride = 1.让我们假设你的input
和kernel
有:
使用内核时,您将收到以下输出: ,按以下方式计算:
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)
并且将等同于我们手工计算的那个.
有关填充/跨步的示例,请查看此处.
只是为了添加其他答案,你应该考虑参数
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要素贴图.
我试图实施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,结果非常接近(但实施速度较慢).我希望这可以帮助你.