webentwicklung-frage-antwort-db.com.de

Was macht tf.nn.conv2d im Tensorflow?

Ich habe mir die Dokumente von tensorflow über tf.nn.conv2dhier angesehen. Aber ich kann nicht verstehen, was es tut oder was es zu erreichen versucht. Es sagt auf den Dokumenten, 

# 1: Glättet den Filter mit einer Form auf eine 2-D-Matrix 

[filter_height * filter_width * in_channels, output_channels].

Was macht das jetzt? Ist diese elementweise Multiplikation oder nur Matrixmultiplikation? Ich konnte auch die beiden anderen in den Dokumenten genannten Punkte nicht verstehen. Ich habe sie unten geschrieben:

# 2: Extrahiert Bildflecken aus dem Eingangstensor, um einen virtuellen Formfaktor zu bilden 

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

# 3: Für jedes Patch multipliziert die Filtermatrix und der Image-Patch-Vektor mit Rechts.

Es wäre wirklich hilfreich, wenn jemand ein Beispiel geben könnte, vielleicht einen Code (extrem hilfreich) und erklären würde, was dort vor sich geht und warum die Operation so ist.

Ich habe versucht, einen kleinen Teil zu codieren und die Form der Operation auszudrucken. Ich kann es immer noch nicht verstehen. 

Ich habe so etwas ausprobiert:

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)

Ich verstehe Teile der konvolutionellen neuronalen Netze. Ich studierte sie hier . Aber die Implementierung von Tensorflow entspricht nicht meinen Erwartungen. Es stellte sich also die Frage.

EDIT: Also habe ich einen viel einfacheren Code implementiert. Aber ich kann nicht herausfinden, was los ist. Ich meine, wie die Ergebnisse so sind. Es wäre äußerst hilfreich, wenn mir jemand sagen könnte, welcher Prozess zu diesem Ergebnis führt.

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)

ausgabe

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

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

  [[-0.00354624]
   [ 0.41650501]]]]
114
Shubhashis

Die 2D-Faltung wird auf ähnliche Weise berechnet: 1D-Faltung : Sie schieben Ihren Kern über die Eingabe, berechnen die elementweisen Multiplikationen und summieren diese auf. Anstatt dass Ihr Kern/Ihre Eingabe ein Array ist, handelt es sich hier um Matrizen.


Im einfachsten Beispiel gibt es keine Auffüllung und Schritt = 1. Nehmen wir an, Ihre input und kernel sind:  enter image description here

Wenn Sie Ihren Kernel verwenden, erhalten Sie folgende Ausgabe:  enter image description here , das auf folgende Weise berechnet wird:

  • 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

Die Funktion conv2d von TF berechnet Windungen in Batches und verwendet ein etwas anderes Format. Für eine Eingabe ist es [batch, in_height, in_width, in_channels], für den Kernel ist es [filter_height, filter_width, in_channels, out_channels]. Daher müssen wir die Daten im richtigen Format bereitstellen:

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

Anschließend wird die Faltung berechnet mit:

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)

Und wird dem entsprechen, den wir von Hand berechnet haben. 


Für Beispiele mit Polsterung/Strides sehen Sie hier .

42
Salvador Dali

Ok, ich denke, das ist der einfachste Weg, alles zu erklären.


Ihr Beispiel ist 1 Bild, Größe 2x2, mit 1 Kanal. Sie haben 1 Filter mit der Größe 1x1 und 1 Kanal (Größe ist Höhe x Breite x Kanäle x Anzahl der Filter).

Für diesen einfachen Fall ist das resultierende 2 × 2 1-Kanal-Bild (Größe 1 × 2 × 2 × 1, Anzahl der Bilder × Höhe × Breite × x Kanäle) das Ergebnis der Multiplikation des Filterwerts mit jedem Pixel des Bildes.


Probieren wir jetzt mehr Kanäle aus:

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

Hier haben das 3x3-Bild und der 1x1-Filter jeweils 5 Kanäle. Das resultierende Bild ist 3x3 mit 1 Kanal (Größe 1x3x3x1), wobei der Wert jedes Pixels das Punktprodukt über die Kanäle des Filters mit dem entsprechenden Pixel im Eingabebild ist.


Jetzt mit 3x3 Filter

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

Hier erhalten wir ein 1x1-Bild mit 1 Kanal (Größe 1x1x1x1). Der Wert ist die Summe der 9,5-Element-Skalarprodukte. Man könnte es aber auch ein Punktprodukt mit 45 Elementen nennen.


Jetzt mit größerem Bild

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

Die Ausgabe ist ein 3x3 1-Kanal-Bild (Größe 1x3x3x1). Jeder dieser Werte ist eine Summe von 9 5-Element-Punktprodukten.

Jede Ausgabe erfolgt durch Zentrieren des Filters auf eines der 9 mittleren Pixel des Eingabebilds, sodass keiner der Filter hervorsteht. Die folgenden x stellen die Filterzentren für jedes Ausgabepixel dar.

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

Jetzt mit "SAME" -Auffüllung:

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

Dies ergibt ein 5x5-Ausgabebild (Größe 1x5x5x1). Dies erfolgt durch Zentrieren des Filters an jeder Position im Bild.

Alle 5-Element-Punktprodukte, bei denen der Filter über den Bildrand hinausragt, erhalten den Wert Null.

Die Ecken sind also nur Summen von 4, 5-Element-Punktprodukten.


Jetzt mit mehreren Filtern.

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

Dies ergibt immer noch ein 5x5-Ausgabebild, jedoch mit 7 Kanälen (Größe 1x5x5x7). Wobei jeder Kanal von einem der Filter im Set erzeugt wird.


Jetzt mit Schritten 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')

Jetzt hat das Ergebnis noch 7 Kanäle, ist aber nur 3x3 (Größe 1x3x3x7).

Dies liegt daran, dass die Filter nicht an jedem Punkt im Bild zentriert werden, sondern an jedem anderen Punkt im Bild, wobei Schritte (Schritte) der Breite 2 ausgeführt werden. Die folgenden x geben die Filtermitte an für jedes Ausgabepixel auf dem Eingabebild.

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

Und natürlich ist die erste Dimension der Eingabe die Anzahl der Bilder, sodass Sie sie auf einen Stapel von 10 Bildern anwenden können. Beispiel:

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

Dies führt die gleiche Operation für jedes Bild unabhängig aus, wobei ein Stapel von 10 Bildern als Ergebnis erhalten wird (Größe 10x3x3x7).

154
mdaoust

Ich habe versucht, conv2d (für mein Studium) zu implementieren. Nun, ich habe das geschrieben:

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

Hoffe ich habe es richtig gemacht. Auf MNIST geprüft, hatte sehr nahe Ergebnisse (diese Implementierung ist jedoch langsamer). Ich hoffe das hilft dir.

8
Artem Yaschenko

Um nur zu den anderen Antworten hinzuzufügen, sollten Sie an die Parameter in denken 

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

als '5' entsprechend der Anzahl der Kanäle in jedem Filter. Bei jedem Filter handelt es sich um einen 3D-Würfel mit einer Tiefe von 5. Die Filtertiefe muss der Tiefe des Eingabebildes entsprechen. Der letzte Parameter, 7, sollte als Anzahl der Filter im Stapel betrachtet werden. Vergessen Sie einfach, dass dies 4D ist, und stellen Sie sich stattdessen vor, dass Sie einen Satz oder einen Satz von 7 Filtern haben. Sie erstellen 7 Filterwürfel mit Abmessungen (3,3,5).

Es ist viel einfacher, sich im Fourier-Bereich zu visualisieren, da die Faltung zur punktweisen Multiplikation wird. Für ein Eingabebild mit Bemaßungen (100, 100,3) können Sie die Filterabmessungen als umschreiben

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

Um eine der 7 Ausgabemerkmalkarten zu erhalten, führen wir einfach die punktweise Multiplikation des Filterwürfels mit dem Bildwürfel durch. Dann summieren wir die Ergebnisse über die Kanäle/Tiefe (hier 3) und fallen auf 2d (100,100) -Kennfeld. Tun Sie dies mit jedem Filterwürfel, und Sie erhalten 7 2D-Funktionskarten.

8
Val9265

Zusätzlich zu anderen Antworten wird die conv2d-Operation in c ++ (cpu) oder cuda für gpu-Maschinen ausgeführt, die Daten auf bestimmte Weise abflachen und umformen und die Matrixmultiplikation gemmBLAS oder cuBLAS (cuda) verwenden müssen.

0
karaspd