Tf.nn.conv2d tensorflow'da ne yapar?


135

tf.nn.conv2d Burada tensorflow'un belgelerine bakıyordum . Ama ne yaptığını ya da neyi başarmaya çalıştığını anlayamıyorum. Dokümanlar üzerinde diyor ki,

# 1: Filtreyi şekilli 2 boyutlu bir matrise düzleştirir

[filter_height * filter_width * in_channels, output_channels].

Şimdi ne yapıyor? Bu elemanlar arası çarpma mı yoksa sadece düz matris çarpma mı? Dokümanlarda belirtilen diğer iki noktayı da anlayamadım. Onları aşağıda yazdım:

# 2: Sanal bir şekil tensörü oluşturmak için giriş tensöründen görüntü yamaları çıkarır

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

# 3: Her bir yama için, filtre matrisini ve görüntü yama vektörünü sağa çarpar.

Herkes bir örnek verebilir, (son derece yararlı) bir kod parçası olabilir ve orada neler olup bittiğini ve işlemin neden böyle olduğunu açıklamak gerçekten yararlı olacaktır.

Küçük bir kısmı kodlamayı ve operasyonun şeklini yazdırmayı denedim. Yine de anlayamıyorum.

Ben böyle bir şey denedim:

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)

Evrişimsel sinir ağlarının parçalarını ve parçalarını anlıyorum. Onları burada inceledim . Ama tensorflow uygulaması beklediğim gibi değil. Bu yüzden soruyu gündeme getirdi.

EDIT : Böylece, çok daha basit bir kod uyguladım. Ama neler olduğunu anlayamıyorum. Yani sonuçların nasıl olduğu. Biri bana bu çıktıyı hangi sürecin verdiğini söyleyebilseydi son derece yararlı olurdu.

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)

çıktı

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

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

  [[-0.00354624]
   [ 0.41650501]]]]

Aslında cudnn, GPU'da varsayılan olarak etkindir tf.nn.conv2d(), bu nedenle use_cudnn_on_gpu=Falseaçıkça belirtilmediği sürece GPU desteği ile TF kullandığımızda söz konusu yöntem hiç kullanılmaz .
gkcn

Yanıtlar:


59

2B evrişim, 1B evrişimi hesaplayacak şekilde benzer şekilde hesaplanır : çekirdeğinizi girdinin üzerine kaydırırsınız, eleman bazında çarpımları hesaplar ve toplarsınız. Ancak, çekirdek / girişiniz bir dizi olmak yerine, burada matrislerdir.


En temel örnekte dolgu yoktur ve adım = 1'dir. Diyelim ki inputve kernel: resim açıklamasını buraya girin

Çekirdeğinizi kullandığınızda resim açıklamasını buraya girin, şu şekilde hesaplanan şu çıktıyı alırsınız :

  • 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'nin conv2d işlevi, kıvrımları gruplar halinde hesaplar ve biraz farklı bir format kullanır. Bir girdi [batch, in_height, in_width, in_channels]için çekirdek içindir [filter_height, filter_width, in_channels, out_channels]. Bu nedenle verileri doğru biçimde sağlamalıyız:

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

Daha sonra evrişim şu şekilde hesaplanır:

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)

Ve elle hesapladığımızla eşdeğer olacak.


İçin dolgu / adımlarla örnekler, burada bir göz atın .


Güzel bir örnek, ancak bazı bağlantılar koptu.
silgon

1
@silgon ne yazık ki SO ilk başta oluşturdukları ve reklamını yaptıkları dokümantasyon özelliğini desteklememeye karar verdi.
Salvador Dali

161

Tamam, bu her şeyi açıklamanın en basit yolu hakkında düşünüyorum.


Örneğiniz, 1x2 boyutunda, 1 kanallı 1 resimdir. 1x1 boyutunda ve 1 kanallı 1 filtreniz var (boyut yükseklik x genişlik x kanal x filtre sayısıdır).

Bu basit durum için ortaya çıkan 2x2, 1 kanallı görüntü (boyut 1x2x2x1, görüntü sayısı x yükseklik x genişlik xx kanallar), filtre değerinin görüntünün her pikseli ile çarpılması sonucudur.


Şimdi daha fazla kanal deneyelim:

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

Burada 3x3 görüntü ve 1x1 filtrenin her biri 5 kanala sahiptir. Ortaya çıkan görüntü, 1 kanallı 3x3 (boyut 1x3x3x1) olacaktır; burada her pikselin değeri, giriş görüntüsünde karşılık gelen piksel ile filtrenin kanalları boyunca nokta ürünüdür.


Şimdi 3x3 filtreli

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

Burada 1 kanallı (boyut 1x1x1x1) bir 1x1 görüntü elde ediyoruz. Değer 9, 5 elementli nokta ürünlerinin toplamıdır. Ancak buna 45 elementli nokta ürün diyebilirsiniz.


Şimdi daha büyük bir resimle

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

Çıktı 3x3 1 kanallı bir görüntüdür (boyut 1x3x3x1). Bu değerlerin her biri 9, 5 elementli nokta ürünlerin toplamıdır.

Her çıkış, filtreyi giriş görüntüsünün 9 merkez pikselinden birinde ortalayarak yapılır, böylece filtrenin hiçbiri dışarı çıkmaz. Aşağıdaki xs, her çıkış pikseli için filtre merkezlerini temsil eder.

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

Şimdi "SAME" dolgu ile:

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

Bu 5x5 çıktı görüntüsü verir (boyut 1x5x5x1). Bu, filtrenin görüntüdeki her bir konumda ortalanmasıyla yapılır.

Filtrenin görüntünün kenarını geçtiği 5 elementli nokta ürünlerinden herhangi biri sıfır değerini alır.

Köşeler sadece 4, 5 elemanlı nokta ürünlerin toplamıdır.


Şimdi birden fazla filtreyle.

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

Bu hala 5x5 çıkış görüntüsü verir, ancak 7 kanallıdır (boyut 1x5x5x7). Her kanalın setteki filtrelerden biri tarafından üretildiği yer.


Şimdi adım 2,2 ile:

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

Şimdi sonuç hala 7 kanala sahip, ancak sadece 3x3 (boyut 1x3x3x7).

Bunun nedeni, filtreleri görüntünün her noktasında ortalamak yerine, filtrelerin, genişlik 2 adımlarını (adımlarını) alarak görüntünün diğer her noktasında ortalanmasıdır. xAşağıda, her bir çıkış pikseli için filtre merkezini, giriş görüntüsü.

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

Ve elbette, girdinin ilk boyutu görüntü sayısıdır, böylece 10 görüntüden oluşan bir toplu iş üzerine uygulayabilirsiniz, örneğin:

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

Bu, her resim için bağımsız olarak aynı işlemi gerçekleştirir ve sonuç olarak 10 resim yığını verir (boyut 10x3x3x7)


@ZijunLost Hayır, dokümanlar ilk ve son öğenin 1 olması gerektiğini Must have strides[0] = strides[3] = 1. For the most common case of the same horizontal and vertices strides, strides = [1, stride, stride, 1].
belirtir

Bu Toeplitz matris tabanlı evrişim uygulaması mı?
gkcn

Bununla ilgili olarak: "Bu hala 5x5 çıkış görüntüsü veriyor, ancak 7 kanallı (1x5x5x7 boyutunda). Her kanalın setteki filtrelerden biri tarafından üretildiği yerlerde." "setteki filtreler" ne demek? Teşekkürler.
derek

@mdaoust Merhaba, ikinci örneğinizle ilgili olarak the 3x3 image and the 1x1 filter each have 5 channels, sonucun manuel olarak hesaplanan nokta ürününden farklı olduğunu düşünüyorum.
Tgn Yang

1
@derek Aynı sorum var, "output_channel" "filtre sayısı" ile aynı mı ??? öyleyse neden tensorflow belgelerinde "output_channel" olarak adlandırılıyor?
Wei

11

Sadece diğer cevaplara eklemek için,

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

her filtredeki kanal sayısına karşılık gelen '5' olarak gösterilir. Her filtre, derinliği 5 olan 3d bir küptür. Filtre derinliğiniz, giriş resminizin derinliğine uygun olmalıdır. Son parametre olan 7, partideki filtre sayısı olarak düşünülmelidir. Bunun 4D olduğunu unutun ve bunun yerine 7 filtreli bir kümeniz veya partiniz olduğunu hayal edin. Yaptığınız, boyutları (3,3,5) olan 7 filtre küpü oluşturmaktır.

Evrişim noktasal çarpma olduğunda Fourier alanında görselleştirmek çok daha kolaydır. Boyutların (100,100,3) giriş görüntüsü için filtre boyutlarını şu şekilde yeniden yazabilirsiniz:

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

7 çıkış özellik haritasından birini elde etmek için, filtre küpünün görüntü küpüyle noktasal olarak çoğaltılmasını gerçekleştiririz, ardından sonuçları 2d'ye daraltarak kanallar / derinlik boyutu (burada 3'tür) boyunca toplarız. (100.100) özellik haritası. Bunu her filtre küpü ile yapın ve 7 2B özellik haritası elde edin.


8

Conv2d (çalışmam için) uygulamaya çalıştım. Bunu ben yazdım:

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

Umarım düzgün yaptım. MNIST üzerinde kontrol, çok yakın sonuçlar vardı (ama bu uygulama daha yavaş). Umarım bu sana yardımcı olur.


0

Diğer yanıtlara ek olarak, conv2d işlemi, verileri belirli bir şekilde düzleştirip yeniden şekillendirmeyi ve gemmBLAS veya cuBLAS (cuda) matris çarpımını kullanmasını gerektiren gpu makineleri için c ++ (cpu) veya cuda'da çalışmaktadır.


Bu nedenle bellekte, evrişim aslında daha büyük görüntülerin neden daha büyük hesaplama süresine neden çalışmadığını, bunun yerine OOM (bellek yetersiz) hatasına neden olma olasılığını açıklayan bir matris çarpımı olarak gerçekleştirilmektedir. 3D konvolüsyonun neden 2D konvolüsyona kıyasla daha fazla bellek verimsiz / verimli olduğunu açıklayabilir misiniz? Örneğin, [B * C, H, W, D] 'de 2D dönş. İle karşılaştırıldığında [B, H, W, D, C]' de 3D dönş. Elbette, hesaplama maliyeti aynı mıdır?
SomePhysicsStudent
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.