wake-up-neo.com

Visualisierung der Ausgabe der Faltungsschicht im Tensorflow

Ich versuche, die Ausgabe einer Faltungsschicht im Tensorflow mit der Funktion tf.image_summary Zu visualisieren. Ich verwende es bereits erfolgreich in anderen Fällen (z. B. Visualisierung des Eingabebilds), habe jedoch einige Probleme, die Ausgabe hier korrekt umzugestalten. Ich habe die folgende Conv-Schicht:

img_size = 256
x_image = tf.reshape(x, [-1,img_size, img_size,1], "sketch_image")

W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])

h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)

Die Ausgabe von h_conv1 Hätte also die Form [-1, img_size, img_size, 32]. Nur die Verwendung von tf.image_summary("first_conv", tf.reshape(h_conv1, [-1, img_size, img_size, 1])) berücksichtigt nicht die 32 verschiedenen Kernel, daher gehe ich hier im Grunde genommen verschiedene Feature-Maps durch.

Wie kann ich sie richtig umformen? Oder gibt es eine andere Hilfsfunktion, mit der ich diese Ausgabe in die Zusammenfassung aufnehmen kann?

34
panmari

Ich kenne keine Hilfefunktion, aber wenn Sie alle Filter sehen möchten, können Sie sie mit einigen ausgefallenen Verwendungen von tf.transpose In ein Bild packen.

Wenn Sie also einen Tensor haben, der images x ix x iy x channels ist

>>> V = tf.Variable()
>>> print V.get_shape()

TensorShape([Dimension(-1), Dimension(256), Dimension(256), Dimension(32)])

Also in diesem Beispiel ix = 256, iy=256, channels=32

schneiden Sie zuerst 1 Bild ab und entfernen Sie die Dimension image

V = tf.slice(V,(0,0,0,0),(1,-1,-1,-1)) #V[0,...]
V = tf.reshape(V,(iy,ix,channels))

Fügen Sie als Nächstes ein paar Pixel mit Nullabstand um das Bild hinzu

ix += 4
iy += 4
V = tf.image.resize_image_with_crop_or_pad(image, iy, ix)

Verändern Sie dann die Form, sodass Sie anstelle von 32 Kanälen 4x8-Kanäle haben, und nennen Sie sie cy=4 Und cx=8.

V = tf.reshape(V,(iy,ix,cy,cx)) 

Nun der knifflige Teil. tf scheint Ergebnisse in der C-Reihenfolge zurückzugeben, der Standardeinstellung von numpy.

Wenn die aktuelle Reihenfolge abgeflacht ist, werden alle Kanäle für das erste Pixel (mit Iteration über cx und cy) aufgelistet, bevor die Kanäle des zweiten Pixels aufgelistet werden (inkrementieren von ix ). Durchlaufen Sie die Pixelzeilen (ix), bevor Sie zur nächsten Zeile (iy) übergehen.

Wir wollen die Reihenfolge, in der die Bilder in einem Raster angeordnet werden. Sie gehen also über eine Bildreihe (ix), bevor Sie die Kanalreihe (cx) durchlaufen, und wenn Sie das Ende der Kanalreihe erreichen, gelangen Sie zur nächsten Reihe im Bild (iy) und, wenn Sie oder Reihen im Bild ausgehen, erhöhen Sie zur nächsten Reihe der Kanäle (cy). so:

V = tf.transpose(V,(2,0,3,1)) #cy,iy,cx,ix

Persönlich bevorzuge ich np.einsum Für ausgefallene Transponierungen, aus Gründen der Lesbarkeit, aber es ist nicht in tfnoch .

newtensor = np.einsum('yxYX->YyXx',oldtensor)

nun, da die Pixel in der richtigen Reihenfolge sind, können wir sie sicher zu einem 2d-Tensor reduzieren:

# image_summary needs 4d input
V = tf.reshape(V,(1,cy*iy,cx*ix,1))

probieren Sie tf.image_summary aus, Sie sollten ein Raster mit kleinen Bildern erhalten.

Unten ist ein Bild von dem, was man bekommt, nachdem man alle Schritte hier befolgt hat.

enter image description here

34
mdaoust

Für den Fall, dass jemand "springen" und "dort" visualisieren möchte, ist hier ein Beispiel, wie sowohl Weights als auch processing result Angezeigt werden. Alle Transformationen basieren auf der vorherigen Antwort von mdaoust.

# to visualize 1st conv layer Weights
vv1 = sess.run(W_conv1)

# to visualize 1st conv layer output
vv2 = sess.run(h_conv1,feed_dict = {img_ph:x, keep_prob: 1.0})
vv2 = vv2[0,:,:,:]   # in case of bunch out - slice first img


def vis_conv(v,ix,iy,ch,cy,cx, p = 0) :
    v = np.reshape(v,(iy,ix,ch))
    ix += 2
    iy += 2
    npad = ((1,1), (1,1), (0,0))
    v = np.pad(v, pad_width=npad, mode='constant', constant_values=p)
    v = np.reshape(v,(iy,ix,cy,cx)) 
    v = np.transpose(v,(2,0,3,1)) #cy,iy,cx,ix
    v = np.reshape(v,(cy*iy,cx*ix))
    return v

# W_conv1 - weights
ix = 5  # data size
iy = 5
ch = 32   
cy = 4   # grid from channels:  32 = 4x8
cx = 8
v  = vis_conv(vv1,ix,iy,ch,cy,cx)
plt.figure(figsize = (8,8))
plt.imshow(v,cmap="Greys_r",interpolation='nearest')

#  h_conv1 - processed image
ix = 30  # data size
iy = 30
v  = vis_conv(vv2,ix,iy,ch,cy,cx)
plt.figure(figsize = (8,8))
plt.imshow(v,cmap="Greys_r",interpolation='nearest')
2
rgr

sie können versuchen, das Aktivierungsbild der Faltungsebene auf folgende Weise zu erhalten:

    h_conv1_features = tf.unpack(h_conv1, axis=3)
    h_conv1_imgs = tf.expand_dims(tf.concat(1, h_conv1_features_padded), -1)

dies ergibt einen vertikalen Streifen mit allen Bildern, die vertikal verkettet sind.

wenn Sie möchten, dass sie gepolstert werden (bei Relu-Aktivierungen mit weißer Linie auffüllen):

    h_conv1_features = tf.unpack(h_conv1, axis=3)
    h_conv1_max = tf.reduce_max(h_conv1)
    h_conv1_features_padded = map(lambda t: tf.pad(t-h_conv1_max, [[0,0],[0,1],[0,0]])+h_conv1_max, h_conv1_features)
    h_conv1_imgs = tf.expand_dims(tf.concat(1, h_conv1_features_padded), -1)
1
Anton Suslov

Ich persönlich versuche, jeden 2d-Filter in einem Bild zusammenzufassen.

Dafür -wenn ich mich nicht furchtbar irre, da ich für DL noch recht neu bin- Ich fand heraus, dass es hilfreich sein könnte, die Funktion depth_to_space zu nutzen, da sie einen 4d-Tensor benötigt

[batch, height, width, depth]

und erzeugt eine Ausgabe von Form

[batch, height*block_size, width*block_size, depth/(block_size*block_size)]

Dabei ist block_size die Anzahl der "Kacheln" im Ausgabebild. Die einzige Einschränkung besteht darin, dass die Tiefe das Quadrat von block_size sein sollte, das eine Ganzzahl ist, sonst kann es das resultierende Bild nicht richtig "füllen". Eine mögliche Lösung könnte darin bestehen, die Tiefe des Eingangstensors auf eine Tiefe aufzufüllen, die von der Methode akzeptiert wird, aber ich habe dies noch nicht versucht.

1
EdoardoG

Ein anderer Weg, den ich für sehr einfach halte, ist die Verwendung von get_operation_by_name Funktion. Es fiel mir schwer, die Ebenen mit anderen Methoden zu visualisieren, aber das half mir.

#first, find out the operations, many of those are micro-operations such as add etc.
graph = tf.get_default_graph()
graph.get_operations()

#choose relevant operations
op_name = '...' 
op = graph.get_operation_by_name(op_name)
out = sess.run([op.outputs[0]], feed_dict={x: img_batch, is_training: False}) 
#img_batch is a single image whose dimensions are (1,n,n,1). 

# out is the output of the layer, do whatever you want with the output
#in my case, I wanted to see the output of a convolution layer
out2 = np.array(out)
print(out2.shape)

# determine, row, col, and fig size etc.
for each_depth in range(out2.shape[4]):
    fig.add_subplot(rows, cols, each_depth+1)
    plt.imshow(out2[0,0,:,:,each_depth], cmap='gray')

Zum Beispiel unten ist die Eingabe (farbige Katze) und Ausgabe der zweiten Conv-Ebene in meinem Modell. below

Beachten Sie, dass mir bekannt ist, dass diese Frage alt ist und es mit Keras einfachere Methoden gibt. Für Benutzer, die ein altes Modell von anderen Benutzern (z. B. mir) verwenden, kann dies jedoch hilfreich sein.

0
smttsp