2015-11-19 8 views
20

Sto provando a visualizzare l'output di uno strato convoluzionale in tensorflow utilizzando la funzione tf.image_summary. Lo sto già utilizzando con successo in altri casi (ad esempio visualizzando l'immagine di input), ma ho qualche difficoltà a rimodellare l'output qui correttamente. Ho il seguente strato di conv:Visualizzazione dell'output dello strato convoluzionale in tensorflow

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) 

Così l'output di h_conv1 avrebbe la forma [-1, img_size, img_size, 32]. L'uso di tf.image_summary("first_conv", tf.reshape(h_conv1, [-1, img_size, img_size, 1])) non tiene conto dei 32 diversi kernel, quindi in pratica sto tagliando le diverse mappe delle caratteristiche qui.

Come posso rimodellarli correttamente? O c'è un'altra funzione di aiuto che potrei usare per includere questo output nel sommario?

risposta

23

Non so una funzione di supporto, ma se si desidera vedere tutti i filtri è possibile comprimerli in un'unica immagine con alcuni usi fantasiosi di tf.transpose.

Quindi, se si dispone di un tensore che è images x ix x iy x channels

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

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

Quindi in questo esempio ix = 256, iy=256, channels=32

prima fetta off 1 immagine, e rimuovere la dimensione image

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

Successivamente aggiungere un paio di pixel di padding a zero intorno all'immagine

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

Poi rimodellare in modo che invece di 32 canali avete canali 4x8, permette di chiamarli cy=4 e cx=8.

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

Ora la parte difficile. tf sembra restituire risultati in ordine C, impostazione predefinita di numpy.

L'ordine corrente, se appiattito, sarebbe elencare tutti i canali per il primo pixel (l'iterazione di cx e cy), prima di elencare i canali del secondo pixel (incrementando ix). Attraversare le righe di pixel (ix) prima di passare alla riga successiva (iy).

Vogliamo l'ordine per disporre le immagini in una griglia. Quindi si attraversa una riga di un'immagine (ix), prima di spostarsi lungo la fila di canali (cx), quando si preme la fine della fila di canali si passa alla riga successiva nell'immagine (iy) e quando si esaurire o righe nell'immagine si incrementa alla successiva riga di canali (cy). così:

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

Personalmente preferisco np.einsum per traspone fantasia, per migliorare la leggibilità, ma non è in tfyet.

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

in ogni caso, ora che i pixel sono nel giusto ordine, possiamo tranquillamente appiattirla in un tensore 2d:

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

provare tf.image_summary su questo, si dovrebbe ottenere una griglia di piccole immagini.

Di seguito è riportata un'immagine di ciò che si ottiene dopo aver seguito tutti i passaggi qui.

enter image description here

+1

Grazie per la tua risposta, ero bloccato sulla parte transpose. Ho finito per usare una [versione leggermente diversa] (https://gist.github.com/panmari/4622b78ce21e44e2d69c) visto che sto bene vedendo solo le prime convoluzioni (non ho bisogno di assemblarle tutte in un griglia). La griglia è piuttosto difficile da ispezionare sul tensore. – panmari

+1

Mi sembra che l'ultima fy e fx che hai scritto siano effettivamente cy e cx – jean

+1

Per di più puoi solo passare il tensore 4D a 'tf.image_summary' quindi dovrai rimodellare' V = tf.reshape (V, (1,4 * 256,8 * 256,1)) ' – jean

2

Nel caso in cui qualcuno vorrebbe "saltare" a NumPy e visualizzare "là" Ecco un esempio come visualizzare sia Weights e processing result. Tutte le trasformazioni sono basate sulla risposta prev da 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') 
0

si può tentare immagine attivazione strato convoluzione per ottenere in questo modo:

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

questo ottiene una striscia verticale con tutte le immagini concatenati in verticale.

se li volete imbottita (nel mio caso di attivazioni Relu al pad con linea bianca):

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

io personalmente cercano di piastrelle di ogni 2d-filtro in una sola immagine.

Per fare questo -se io non sono terribilmente sbagliato dato che sono abbastanza nuovo a Dl- ho scoperto che potrebbe essere utile per sfruttare la funzione depth_to_space, visto che richiede un tensore 4d

[batch, height, width, depth]

e genera un'uscita di forma

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

Dove BLOCK_SIZE è il numero di "mattonelle" l'immagine di output in. L'unica limitazione a questo è che la profondità dovrebbe essere il quadrato di block_size, che è un numero intero, altrimenti non può "riempire" correttamente l'immagine risultante. Una possibile soluzione potrebbe essere quella di riempire la profondità del tensore di ingresso fino ad una profondità che è accettata dal metodo, ma io non l'ho provato.