Visualización de la salida de la capa convolucional en tensorflow


Estoy tratando de visualizar la salida de una capa convolucional en tensorflow usando la función tf.image_summary. Ya lo estoy usando con éxito en otros casos (por ejemplo, visualizando la imagen de entrada), pero tengo algunas dificultades para remodelar la salida aquí correctamente. Tengo la siguiente capa conv:

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)

Así que la salida de h_conv1 tendría la forma [-1, img_size, img_size, 32]. El solo uso de tf.image_summary("first_conv", tf.reshape(h_conv1, [-1, img_size, img_size, 1])) No tiene en cuenta los 32 núcleos diferentes, así que básicamente estoy rebanando a través de diferentes mapas de características aqui.

¿Cómo puedo remodelarlos correctamente? ¿O hay otra función auxiliar que pueda usar para incluir esta salida en el resumen?

Author: Engineero, 2015-11-19

4 answers

No conozco una función auxiliar, pero si quieres ver todos los filtros puedes empaquetarlos en una imagen con algunos usos sofisticados de tf.transpose.

Así que si tienes un tensor es images x ix x iy x channels

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

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

En este ejemplo ix = 256, iy=256, channels=32

Primero corte 1 imagen, y elimine la dimensión image

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

A continuación, agregue un par de píxeles de relleno cero alrededor de la imagen

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

Entonces remodelar de modo que en lugar de 32 canales tienes canales 4x8, vamos a llamarlos cy=4 y cx=8.

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

Ahora la parte difícil. tf parece devolver los resultados en orden C, el valor predeterminado de numpy.

El orden actual, si se aplana, listaría todos los canales para el primer píxel (iterando sobre cx y cy), antes de listar los canales del segundo píxel (incrementando ix). Ir a través de las filas de píxeles (ix) antes de aumentar a la siguiente fila (iy).

Queremos el orden que establecería la imágenes en una cuadrícula. Así que ir a través de una fila de una imagen (ix), antes de dar un paso a lo largo de la fila de canales (cx), cuando usted golpea el extremo de la fila de canales que paso a la siguiente fila en la imagen (iy) y cuando te quedas o filas en la imagen de incremento a la siguiente fila de canales (cy). así:

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

Personalmente prefiero np.einsum para transposiciones de lujo, para la legibilidad, pero no está en tf yet .

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

De todos modos, ahora que los píxeles están a la derecha orden, podemos aplanar con seguridad en un tensor 2d:

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

Intenta tf.image_summary en eso, deberías obtener una cuadrícula de pequeñas imágenes.

A continuación se muestra una imagen de lo que se obtiene después de seguir todos los pasos aquí.

introduzca la descripción de la imagen aquí

 30
Author: mdaoust,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-11 19:24:33

En caso de que alguien quiera "saltar" a numpy y visualizar "allí", aquí hay un ejemplo de cómo mostrar Weights y processing result. Todas las transformaciones se basan en la respuesta anterior de 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
Author: rgr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-06 23:36:20

Puede intentar obtener la imagen de activación de la capa de convolución de esta manera:

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

Esto obtiene una franja vertical con todas las imágenes concatenadas verticalmente.

Si los quieres acolchados (en mi caso de las activaciones relu para rellenar con línea blanca):

    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
Author: Anton Suslov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-17 05:11:09

Personalmente trato de teja cada filtro 2d en una sola imagen.

Para hacer esto -si no estoy terriblemente equivocado ya que soy bastante nuevo en DL - Descubrí que podría ser útil explotar la función depth_to_space , ya que toma un tensor 4d

[batch, height, width, depth]

Y produce una salida de forma

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

Donde block_size es el número de "tiles" en la imagen de salida. La única limitación a esto es que la profundidad debe ser el cuadrado de block_size, que es un entero, de lo contrario no puede "llenar" la imagen resultante correctamente. Una posible solución podría ser de relleno de la profundidad del tensor de entrada hasta una profundidad que es aceptada por el método, pero yo no he probado esto.

 0
Author: Edoardo Giacomello,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-29 11:34:38