tf.nn.conv2d는 tensorflow에서 무엇을합니까?


135

나는 tf.nn.conv2d 여기 에 대한 tensorflow의 문서를보고 있었습니다 . 그러나 나는 그것이 무엇을하는지 또는 달성하려는 것을 이해할 수 없습니다. 문서에 나와 있습니다.

# 1 : 필터를 모양이있는 2 차원 행렬로 평탄화

[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)

나는 컨볼 루션 신경망의 비트와 조각을 이해합니다. 나는 그들을 여기 에서 공부했다 . 그러나 tensorflow의 구현은 내가 기대 한 것이 아닙니다. 그래서 질문을 제기했습니다.

편집 : 그래서 훨씬 간단한 코드를 구현했습니다. 그러나 나는 무슨 일이 일어나고 있는지 알 수 없습니다. 나는 결과가 어떻게 이런지를 의미합니다. 어떤 프로세스 가이 출력을 산출하는지 말해 줄 수 있다면 매우 도움이 될 것입니다.

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]]]]

실제로 cudnn은 GPU에서 기본적으로 활성화되어 tf.nn.conv2d()있으므로 use_cudnn_on_gpu=False명시 적으로 지정 하지 않으면 GPU 지원으로 TF를 사용할 때 문제의 방법이 전혀 사용되지 않습니다 .
gkcn

답변:


59

2D 컨볼 루션은 1D 컨볼 루션을 계산하는 것과 유사한 방식으로 계산됩니다. 커널을 입력 위로 밀어 내고 요소 별 곱셈을 계산하여 요약합니다. 그러나 커널 / 입력이 배열 대신 행렬입니다.


가장 기본적인 예에서는 패딩과 보폭이 없습니다. 의 당신의 가정하자 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)

그리고 우리가 손으로 계산 한 것과 같습니다.


들어 패딩 / 진보와 예, 여기 좀 봐 .


좋은 예, 그러나 일부 링크가 끊어졌습니다.
silgon

1
@silgon 슬프게도 이것은 SO가 처음에 만들고 보급 한 문서 기능을 지원하지 않기로 결정했기 때문입니다.
살바도르 달리

161

좋아, 이것이 모든 것을 설명하는 가장 간단한 방법이라고 생각합니다.


예를 들어 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 개의 채널이 있습니다. 결과 이미지는 1 채널 (크기 1x3x3x1)의 3x3이됩니다. 여기서 각 픽셀의 값은 입력 이미지의 해당 픽셀이있는 필터의 채널에 대한 내적입니다.


이제 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 개 중앙 픽셀 중 하나에 필터를 중앙에 배치하여 이루어집니다. 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)을 갖습니다. 각 채널은 세트의 필터 중 하나에 의해 생성됩니다.


이제 보폭 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)


@ZijunLost 아니요, 문서에는 첫 번째 요소와 마지막 요소가 1이어야합니다.Must have strides[0] = strides[3] = 1. For the most common case of the same horizontal and vertices strides, strides = [1, stride, stride, 1].
JohnAllen

Toeplitz 매트릭스 기반 컨볼 루션 구현입니까?
gkcn

"이것은 여전히 ​​5x5 출력 이미지를 제공하지만 7 채널 (크기 1x5x5x7)입니다. 각 채널이 세트의 필터 중 하나에 의해 생성되는 곳입니다.", 여전히 7 채널의 출처를 이해하는 데 어려움이 있습니까? "세트의 필터"란 무엇입니까? 감사.
derek

@mdaoust 안녕하세요, 두 번째 예에서 the 3x3 image and the 1x1 filter each have 5 channels결과는 수동으로 계산 된 내적과 다릅니다.
Tgn Yang

1
@derek 같은 질문이 있습니다. "output_channel"은 "filters number"와 같은가요 ??? 그렇다면 왜 tensorflow 문서에서 이름이 "output_channel"입니까?
Wei

11

다른 답변에 추가하기 위해 매개 변수를 생각해야합니다.

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

각 필터의 ​​채널 수에 해당하는 '5'. 각 필터는 깊이가 5 인 3D 큐브입니다. 필터 깊이는 입력 이미지의 깊이와 일치해야합니다. 마지막 매개 변수 인 7은 배치의 필터 수로 간주해야합니다. 이것이 4D라는 것을 잊어 버리고 대신 7 개의 필터 세트 또는 배치가 있다고 상상해보십시오. 당신이하는 일은 차원 (3,3,5)의 7 개의 필터 큐브를 만드는 것입니다.

컨벌루션이 점별 곱셈이되기 때문에 푸리에 영역에서 시각화하는 것이 훨씬 쉽습니다. 치수 (100,100,3)의 입력 이미지의 경우 필터 치수를 다음과 같이 다시 작성할 수 있습니다.

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

7 개의 출력 기능 맵 중 하나를 얻기 위해 이미지 큐브와 필터 큐브의 포인트 단위 곱셈을 수행 한 다음 채널 / 깊이 차원 (여기서는 3)에 걸쳐 결과를 합하여 2d로 축소합니다. (100,100) 기능 맵. 각 필터 큐브에서이 작업을 수행하면 7 2D 기능 맵이 제공됩니다.


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에서 확인한 결과가 매우 근접했습니다 (그러나이 구현은 느립니다). 이것이 도움이되기를 바랍니다.


0

다른 답변 외에도 conv2d 작업은 특정 방식으로 데이터를 평탄화하고 재구성하고 gemmBLAS 또는 cuBLAS (cuda) 행렬 곱셈을 사용해야하는 gpu 머신의 c ++ (cpu) 또는 cuda에서 작동합니다.


따라서 메모리에서 컨볼 루션은 실제로 행렬 곱셈으로 수행되므로 더 큰 이미지가 계산 시간이 길어지지 않고 대신 OOM (메모리 부족) 오류가 발생할 가능성이 높은 이유를 설명합니다. 왜 3D 컨볼 루션이 2D 컨볼 루션보다 메모리 비효율적 / 효율적인지 설명 할 수 있습니까? 예를 들어 [B * C, H, W, D]의 2D 전환과 비교하여 [B, H, W, D, C]에서 3D 전환을 수행합니다. 분명히, 그들은 계산 비용이 동일합니까?
SomePhysicsStudent
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.