Apa yang dilakukan tf.nn.conv2d di tensorflow?

138

Saya melihat dokumen tensorflow tentang di tf.nn.conv2d sini . Tetapi saya tidak dapat memahami apa yang dilakukannya atau apa yang ingin dicapai. Dikatakan di dokumen,

# 1: Meratakan filter ke matriks 2-D dengan bentuk

[filter_height * filter_width * in_channels, output_channels].

Sekarang apa fungsinya? Apakah itu perkalian bijak atau hanya perkalian matriks biasa? Saya juga tidak dapat memahami dua poin lainnya yang disebutkan dalam dokumen. Saya telah menuliskannya di bawah ini:

# 2: Mengekstrak patch citra dari tensor input untuk membentuk tensor virtual

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

# 3: Untuk setiap tambalan, perbanyak kanan matriks filter dan vektor tambalan gambar.

Akan sangat membantu jika ada yang bisa memberikan contoh, sepotong kode (sangat membantu) mungkin dan menjelaskan apa yang terjadi di sana dan mengapa operasinya seperti ini.

Saya sudah mencoba mengkodekan sebagian kecil dan mencetak bentuk operasi. Tetap saja, saya tidak bisa mengerti.

Saya mencoba sesuatu seperti ini:

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)

Saya memahami potongan-potongan jaringan saraf konvolusional. Saya mempelajarinya di sini . Tetapi implementasi pada tensorflow tidak seperti yang saya harapkan. Jadi itu menimbulkan pertanyaan.

EDIT : Jadi, saya menerapkan kode yang lebih sederhana. Tapi saya tidak tahu apa yang sedang terjadi. Maksud saya bagaimana hasilnya seperti ini. Akan sangat membantu jika ada yang bisa memberi tahu saya proses apa yang menghasilkan keluaran ini.

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)

keluaran

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

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

  [[-0.00354624]
   [ 0.41650501]]]]
Shubhashis
sumber
Sebenarnya cudnn diaktifkan secara default pada GPU in tf.nn.conv2d(), jadi metode yang dimaksud tidak digunakan sama sekali ketika kita menggunakan TF dengan dukungan GPU, kecuali use_cudnn_on_gpu=Falseditentukan secara eksplisit.
gkcn

Jawaban:

65

Konvolusi 2D dihitung dengan cara yang sama seperti cara menghitung konvolusi 1D : Anda menggeser kernel ke atas input, menghitung perkalian berdasarkan elemen, dan menjumlahkannya. Tetapi alih-alih kernel / input Anda menjadi sebuah array, inilah matriksnya.


Pada contoh paling dasar tidak ada padding dan stride = 1. Mari kita asumsikan inputdan kerneladalah: masukkan deskripsi gambar di sini

Ketika Anda menggunakan kernel Anda, Anda akan menerima keluaran berikut:, masukkan deskripsi gambar di siniyang dihitung dengan cara berikut:

  • 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

Fungsi konv2 TF menghitung konvolusi dalam batch dan menggunakan format yang sedikit berbeda. Sebagai masukan, ini [batch, in_height, in_width, in_channels]untuk kernel [filter_height, filter_width, in_channels, out_channels]. Jadi kami perlu menyediakan data dalam format yang benar:

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

Setelah itu konvolusi dihitung dengan:

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)

Dan akan setara dengan yang kami hitung dengan tangan.


Untuk contoh padding / langkah, lihat di sini .

Salvador Dali
sumber
Contoh yang bagus, namun beberapa tautan rusak.
silgon
1
@silgon sayangnya ini karena SO memutuskan untuk tidak mendukung fitur dokumentasi yang mereka buat dan iklankan pada awalnya.
Salvador Dali
160

Ok saya rasa ini tentang cara termudah untuk menjelaskan semuanya.


Contoh Anda adalah 1 gambar, ukuran 2x2, dengan 1 saluran. Anda memiliki 1 filter, dengan ukuran 1x1, dan 1 saluran (ukuran tinggi x lebar x saluran x jumlah filter).

Untuk kasus sederhana ini, gambar saluran 2x2, 1 yang dihasilkan (ukuran 1x2x2x1, jumlah gambar x tinggi x lebar xx saluran) adalah hasil perkalian nilai filter dengan setiap piksel gambar.


Sekarang mari kita coba lebih banyak saluran:

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

Di sini gambar 3x3 dan filter 1x1 masing-masing memiliki 5 saluran. Citra yang dihasilkan akan berukuran 3x3 dengan 1 kanal (ukuran 1x3x3x1), dimana nilai tiap piksel adalah produk titik melintasi kanal filter dengan piksel yang sesuai pada citra masukan.


Sekarang dengan filter 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')

Disini kita mendapatkan gambar 1x1, dengan 1 channel (ukuran 1x1x1x1). Nilainya adalah jumlah dari 9 produk titik 5 elemen. Tetapi Anda bisa menyebutnya sebagai perkalian titik 45 elemen.


Sekarang dengan gambar yang lebih besar

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

Outputnya berupa gambar 1 saluran 3x3 (ukuran 1x3x3x1). Masing-masing nilai ini adalah jumlah dari 9 produk titik 5 elemen.

Setiap keluaran dibuat dengan memusatkan filter pada salah satu dari 9 piksel tengah gambar masukan, sehingga tidak ada filter yang menonjol. The xs bawah mewakili pusat filter untuk setiap keluaran pixel.

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

Sekarang dengan padding "SAMA":

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

Ini memberikan gambar keluaran 5x5 (ukuran 1x5x5x1). Ini dilakukan dengan memusatkan filter di setiap posisi pada gambar.

Salah satu produk titik 5 elemen yang filternya menonjol melewati tepi gambar akan mendapatkan nilai nol.

Jadi sudut-sudutnya hanyalah penjumlahan dari 4, hasil perkalian 5-elemen titik.


Sekarang dengan banyak filter.

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

Ini masih memberikan gambar keluaran 5x5, tetapi dengan 7 saluran (ukuran 1x5x5x7). Dimana setiap saluran diproduksi oleh salah satu filter di set.


Sekarang dengan langkah 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')

Sekarang hasilnya masih ada 7 channel, tapi hanya 3x3 (ukuran 1x3x3x7).

Ini karena alih-alih memusatkan filter di setiap titik pada gambar, filter dipusatkan di setiap titik lain pada gambar, mengambil langkah (langkah) lebar 2. Di xbawah ini mewakili pusat filter untuk setiap piksel keluaran, di gambar masukan.

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

Dan tentu saja input dimensi pertama adalah jumlah gambar sehingga Anda dapat menerapkannya pada 10 gambar, misalnya:

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

Ini melakukan operasi yang sama, untuk setiap gambar secara terpisah, memberikan tumpukan 10 gambar sebagai hasilnya (ukuran 10x3x3x7)

mdaoust
sumber
@ZijunLost Tidak, dokumen menyatakan bahwa elemen pertama dan terakhir harus 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
Apakah ini implementasi konvolusi berbasis matriks Toeplitz ?
gkcn
Mengenai ini: "Ini masih memberikan gambar keluaran 5x5, tetapi dengan 7 saluran (ukuran 1x5x5x7). Di mana setiap saluran diproduksi oleh salah satu filter di set.", Saya masih kesulitan memahami dari mana 7 saluran itu berasal? apa maksudmu "filter di set"? Terima kasih.
derek
@mdaoust Hai, mengenai contoh kedua Anda di mana the 3x3 image and the 1x1 filter each have 5 channels, saya menemukan hasilnya berbeda dari perkalian titik yang dihitung secara manual.
Tgn Yang
1
@ Derek Saya memiliki pertanyaan yang sama, apakah "output_channel" sama dengan "jumlah filter" ??? jika demikian, mengapa mereka dinamai "output_channel" di dokumen tensorflow?
Wei
12

Hanya untuk menambah jawaban lainnya, Anda harus memikirkan parameter di

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

sebagai '5' sesuai dengan jumlah saluran di setiap filter. Setiap filter adalah kubus 3d, dengan kedalaman 5. Kedalaman filter Anda harus sesuai dengan kedalaman gambar input Anda. Parameter terakhir, 7, harus dianggap sebagai jumlah filter dalam kelompok. Lupakan saja ini adalah 4D, dan sebagai gantinya bayangkan Anda memiliki satu set atau sekumpulan 7 filter. Apa yang Anda lakukan adalah membuat 7 kubus filter dengan dimensi (3,3,5).

Jauh lebih mudah untuk divisualisasikan dalam domain Fourier karena konvolusi menjadi perkalian yang bijaksana. Untuk gambar masukan dimensi (100.100,3) Anda dapat menulis ulang dimensi filter sebagai

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

Untuk mendapatkan salah satu dari 7 peta fitur keluaran, kita cukup melakukan perkalian titik-bijak dari kubus filter dengan kubus gambar, lalu kita menjumlahkan hasilnya di seluruh dimensi saluran / kedalaman (ini 3), menciut menjadi 2d (100.100) peta fitur. Lakukan ini dengan setiap kubus filter, dan Anda mendapatkan 7 peta fitur 2D.

Val9265
sumber
8

Saya mencoba menerapkan konv2 (untuk studi saya). Nah, saya menulis bahwa:

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

Semoga saya melakukannya dengan benar. Diperiksa di MNIST, memiliki hasil yang sangat dekat (tetapi penerapan ini lebih lambat). Saya harap ini membantu Anda.

Artem Yaschenko
sumber
0

Selain jawaban lain, operasi konv2d beroperasi di c ++ (cpu) atau cuda untuk mesin gpu yang perlu meratakan dan membentuk kembali data dengan cara tertentu dan menggunakan perkalian matriks gemmBLAS atau cuBLAS (cuda).

karaspd
sumber
Jadi dalam memori, konvolusi sebenarnya dilakukan sebagai perkalian matriks yang menjelaskan mengapa gambar yang lebih besar tidak perlu berjalan ke dalam waktu komputasi yang lebih besar, tetapi lebih cenderung mengalami kesalahan OOM (kehabisan memori). Dapatkah Anda menjelaskan kepada saya mengapa konvolusi 3D lebih tidak efisien / efisien dalam memori dibandingkan dengan konvolusi 2D? Misalnya melakukan konv. 3D pada [B, H, W, D, C] dibandingkan dengan konv. 2D pada [B * C, H, W, D]. Tentunya, secara komputasi harganya sama?
SomePhysicsStudent
0

Ini melakukan konvulsi melalui gambar ketika Anda mencoba misalnya fungsi klasifikasi gambar yang memiliki semua parameter yang diperlukan untuk melakukan itu.

Bila pada dasarnya Anda dapat memilih dimensi filter. Langkah. Lapisan. Sebelum digunakan perlu undestant konsep konvolusi

Jonathan Lorence
sumber