I was looking at the docs of tensorflow about tf.nn.conv2d
here. But I can't understand what it does or what it is trying to achieve. It says on the docs,
#1 : Flattens the filter to a 2-D matrix with shape
[filter_height * filter_width * in_channels, output_channels]
.
Now what does that do? Is that element-wise multiplication or just plain matrix multiplication? I also could not understand the other two points mentioned in the docs. I have written them below :
# 2: Extracts image patches from the the input tensor to form a virtual tensor of shape
[batch, out_height, out_width, filter_height * filter_width * in_channels]
.# 3: For each patch, right-multiplies the filter matrix and the image patch vector.
It would be really helpful if anyone could give an example, a piece of code (extremely helpful) maybe and explain what is going on there and why the operation is like this.
I've tried coding a small portion and printing out the shape of the operation. Still, I can't understand.
I tried something like this:
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)
I understand bits and pieces of convolutional neural networks. I studied them here. But the implementation on tensorflow is not what I expected. So it raised the question.
EDIT: そこで、もっとシンプルなコードを実装しました。でも、何が起こっているのかわかりません。つまり、結果がどうしてこうなるかということです。どのプロセスでこの出力が得られるのかを教えていただけると非常に助かります。
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]]]]
ベストアンサー1
さて、これがすべてを説明する最も簡単な方法だと思います。
例は、サイズが 2x2、チャネルが 1 つの画像です。サイズが 1x1、チャネルが 1 つのフィルターが 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')
ここでは、1 チャネル (サイズ 1x1x1x1) の 1x1 画像を取得します。値は、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 つの中心ピクセルの 1 つにフィルターを中心に配置することによって作成されます。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 要素のドット積の値はいずれも 0 になります。
したがって、コーナーは 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) になります。各チャネルは、セット内のフィルターの 1 つによって生成されます。
ストライド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 のステップ (ストライド) で画像上の 1 つおきのポイントでフィルターを中央に配置するためです。以下のは、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)を生成します。