with tutorial train neural network example datacamp convolutional cnn tensorflow conv-neural-network

tutorial - tensorflow neural network



¿Qué hace la operación `conv2d_transpose()` de TensorFlow? (3)

Aquí hay otro punto de vista desde la perspectiva de los "gradientes", es decir, por qué la documentación de TensorFlow dice que conv2d_transpose() es "en realidad la transposición ( gradiente ) de conv2d en lugar de una deconvolución real". Para obtener más detalles sobre el cálculo real realizado en conv2d_transpose , recomendaría este artículo a partir de la página 19.

Cuatro funciones relacionadas

En tf.nn , hay 4 funciones estrechamente relacionadas y bastante confusas para la convolución 2d:

  • tf.nn.conv2d
  • tf.nn.conv2d_backprop_filter
  • tf.nn.conv2d_backprop_input
  • tf.nn.conv2d_transpose

Resumen de una oración: todos son solo 2d convoluciones . Sus diferencias tf.nn.conv2d en el orden de los argumentos de entrada, rotación de entrada o transposición, zancadas (incluido el tamaño fraccionario de zancada), rellenos, etc. Con tf.nn.conv2d en la mano, uno puede implementar las otras 3 operaciones transformando entradas y cambiando el argumentos conv2d .

Configuración de problemas

  • Cálculos hacia adelante y hacia atrás:

# forward out = conv2d(x, w) # backward, given d_out => find d_x? => find d_w?

En el cálculo hacia adelante, calculamos la convolución de la imagen de entrada x con el filtro w , y el resultado está out . En el cálculo hacia atrás, supongamos que tenemos d_out , que es el gradiente wrt out . Nuestro objetivo es encontrar d_x y d_w , que son el gradiente wrt x y w respectivamente.

Para facilitar la discusión, suponemos que:

  • Todo el tamaño del paso es 1
  • Todos in_channels y out_channels son 1
  • Utilice el relleno VALID
  • Número impar de tamaño de filtro, esto evita algún problema de forma asimétrica

Respuesta corta

Conceptualmente, con las suposiciones anteriores, tenemos las siguientes relaciones:

out = conv2d(x, w, padding=''VALID'') d_x = conv2d(d_out, rot180(w), padding=''FULL'') d_w = conv2d(x, d_out, padding=''VALID'')

Donde rot180 es una matriz 2d girada 180 grados (una rot180 izquierda-derecha y una flip descendente), FULL significa "aplicar filtro siempre que se solape parcialmente con la entrada" (vea theano documentos ). Observa que esto solo es válido con las suposiciones anteriores , sin embargo, uno puede cambiar los argumentos conv2d para generalizarlo.

Los puntos clave:

  • El gradiente de entrada d_x es la convolución del gradiente de salida d_out y el peso w , con algunas modificaciones.
  • El gradiente de peso d_w es la convolución de la entrada x el gradiente de salida d_out , con algunas modificaciones.

Respuesta larga

Ahora, demos un ejemplo de código de trabajo real de cómo usar las 4 funciones anteriores para calcular d_x y d_w dado d_out . Esto muestra cómo conv2d , conv2d_backprop_filter , conv2d_backprop_input y conv2d_transpose están relacionados entre sí. Encuentra los scripts completos aquí .

d_x de 4 maneras diferentes:

# Method 1: TF''s autodiff d_x = tf.gradients(f, x)[0] # Method 2: manually using conv2d d_x_manual = tf.nn.conv2d(input=tf_pad_to_full_conv2d(d_out, w_size), filter=tf_rot180(w), strides=strides, padding=''VALID'') # Method 3: conv2d_backprop_input d_x_backprop_input = tf.nn.conv2d_backprop_input(input_sizes=x_shape, filter=w, out_backprop=d_out, strides=strides, padding=''VALID'') # Method 4: conv2d_transpose d_x_transpose = tf.nn.conv2d_transpose(value=d_out, filter=w, output_shape=x_shape, strides=strides, padding=''VALID'')

d_w de 3 maneras diferentes:

# Method 1: TF''s autodiff d_w = tf.gradients(f, w)[0] # Method 2: manually using conv2d d_w_manual = tf_NHWC_to_HWIO(tf.nn.conv2d(input=x, filter=tf_NHWC_to_HWIO(d_out), strides=strides, padding=''VALID'')) # Method 3: conv2d_backprop_filter d_w_backprop_filter = tf.nn.conv2d_backprop_filter(input=x, filter_sizes=w_shape, out_backprop=d_out, strides=strides, padding=''VALID'')

Consulte las secuencias de comandos completas para la implementación de tf_rot180 , tf_pad_to_full_conv2d , tf_NHWC_to_HWIO . En los scripts, verificamos que los valores finales de salida de los diferentes métodos sean los mismos; una implementación numpy también está disponible.

La documentación para la operación conv2d_transpose() no explica claramente lo que hace:

La transposición de conv2d.

Esta operación a veces se denomina "deconvolución" después de las redes deconvolucionales , pero en realidad es la transposición (gradiente) de conv2d en lugar de una deconvolución real.

Revisé el documento al que apunta el doctor, pero no ayudó.

¿Qué hace esta operación y cuáles son ejemplos de por qué querría usarla?


Esta es la mejor explicación que he visto en línea sobre cómo funciona la transposición de convolución here .

Daré mi propia descripción breve. Aplica convolución con un paso fraccional. En otras palabras, espaciar los valores de entrada (con ceros) para aplicar el filtro sobre una región que es potencialmente menor que el tamaño del filtro.

En cuanto al por qué uno querría usarlo. Se puede utilizar como una especie de muestreo ascendente con pesos aprendidos en oposición a la interpolación bilineal o alguna otra forma fija de muestreo ascendente.


conv2d_transpose () simplemente transpone los pesos y los voltea 180 grados. Luego aplica el estándar conv2d (). "Transponer" significa prácticamente que cambia el orden de las "columnas" en el tensor de pesos. Por favor, consulte el ejemplo a continuación.

Aquí hay un ejemplo que usa convolutions con stride = 1 y padding = ''SAME''. Es un caso simple pero el mismo razonamiento podría aplicarse a los otros casos.

Digamos que tenemos:

  • Entrada: imagen MNIST de 28x28x1, forma = [28,28,1]
  • Capa convolucional: 32 filtros de 7x7, forma de pesos = [7, 7, 1, 32], nombre = W_conv1

Si realizamos la convolución de la entrada, entonces las activaciones de la voluntad tendrán forma: [1,28,28,32].

activations = sess.run(h_conv1,feed_dict={x:np.reshape(image,[1,784])})

Dónde:

W_conv1 = weight_variable([7, 7, 1, 32]) b_conv1 = bias_variable([32]) h_conv1 = conv2d(x, W_conv1, strides=[1, 1, 1, 1], padding=''SAME'') + b_conv1

Para obtener la "deconvolución" o "convolución transpuesta", podemos usar conv2d_transpose () en las activaciones de convolución de esta manera:

deconv = conv2d_transpose(activations,W_conv1, output_shape=[1,28,28,1],padding=''SAME'')

O usando conv2d () necesitamos transponer y voltear los pesos:

transposed_weights = tf.transpose(W_conv1, perm=[0, 1, 3, 2])

Aquí cambiamos el orden de las "columnas" de [0,1,2,3] a [0,1,3,2]. Así que a partir de [7, 7, 1, 32] obtendremos un tensor con forma = [7,7,32,1]. Luego volteamos los pesos:

for i in range(n_filters): # Flip the weights by 180 degrees transposed_and_flipped_weights[:,:,i,0] = sess.run(tf.reverse(transposed_weights[:,:,i,0], axis=[0, 1]))

Entonces podemos calcular la convolución con conv2d () como:

strides = [1,1,1,1] deconv = conv2d(activations,transposed_and_flipped_weights,strides=strides,padding=''SAME'')

Y obtendremos el mismo resultado que antes. También se puede obtener el mismo resultado con conv2d_backprop_input () usando:

deconv = conv2d_backprop_input([1,28,28,1],W_conv1,activations, strides=strides, padding=''SAME'')

Los resultados se muestran aquí:

Prueba de conv2d (), conv2d_tranposed () y conv2d_backprop_input ()

Podemos ver que los resultados son los mismos. Para verlo de una mejor manera, por favor revisa mi código en:

https://github.com/simo23/conv2d_transpose

Aquí replico la salida de la función conv2d_transpose () usando el estándar conv2d ().