tensorflow
Matemáticas detrás de la convolución 2D con ejemplos avanzados en TF
Buscar..
Introducción
La convolución 2D se calcula de una manera similar a la que se calcularía la convolución 1D : desliza el núcleo sobre la entrada, calcula las multiplicaciones de elementos y las suma. Pero en lugar de que su núcleo / entrada sea una matriz, aquí están las matrices.
Sin relleno, zancadas = 1
Este es el ejemplo más básico, con los cálculos más fáciles. Supongamos que su input
y el kernel
son:
Cuando tu kernel recibas la siguiente salida: , que se calcula de la siguiente manera:
- 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
La función conv2d de TF calcula las convoluciones en lotes y utiliza un formato ligeramente diferente. Para una entrada es [batch, in_height, in_width, in_channels]
para el núcleo es [filter_height, filter_width, in_channels, out_channels]
. Así que necesitamos proporcionar los datos en el formato correcto:
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')
Posteriormente se calcula la convolución con:
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)
Y será equivalente al que calculamos a mano.
Un poco de relleno, zancadas = 1
El relleno es solo un nombre elegante de narración: rodee su matriz de entrada con alguna constante. En la mayoría de los casos, la constante es cero y esta es la razón por la que las personas lo llaman "cero relleno". Entonces, si desea usar un relleno de 1 en nuestra entrada original (verifique el primer ejemplo con padding=0, strides=1
), la matriz se verá así:
Para calcular los valores de la convolución se hace el mismo deslizamiento. Tenga en cuenta que, en nuestro caso, no es necesario volver a calcular muchos valores en el medio (serán los mismos que en el ejemplo anterior. Tampoco mostraré todos los cálculos aquí, porque la idea es sencilla. El resultado es:
dónde
- 5 = 0 * 1 + 0 * 0 + 0 * 1 + 0 * 2 + 4 * 1 + 3 * 0 + 0 * 0 + 0 * 1 + 1 * 1
- ...
- 6 = 4 * 1 + 1 * 0 + 0 * 1 + 0 * 2 + 2 * 1 + 0 * 0 + 0 * 0 + 0 * 0 + 0 * 1
TF no admite un relleno arbitrario en la función conv2d , por lo que si necesita un relleno no compatible, utilice tf.pad () . Afortunadamente para nuestra entrada, el relleno 'SAME' será igual a padding = 1. Por lo tanto, no tenemos que cambiar casi nada en nuestro ejemplo anterior:
res = tf.squeeze(tf.nn.conv2d(image, kernel, [1, 1, 1, 1], "SAME"))
# 'SAME' makes sure that our output has the same size as input and
# uses appropriate padding. In our case it is 1.
with tf.Session() as sess:
print sess.run(res)
Puede verificar que la respuesta sea la misma que la calculada a mano.
Relleno y zancadas (el caso más general)
Ahora aplicaremos una convolución de pasos a nuestro ejemplo rellenado descrito anteriormente y calcularemos la convolución donde p = 1, s = 2
Anteriormente, cuando utilizábamos strides = 1
, nuestra ventana deslizante se movía 1 posición, con strides = s
se mueve por posiciones s
(necesitas calcular s^2
elementos menos. Pero en nuestro caso podemos tomar un atajo y no realizar ninguna acción). cálculos. Como ya calculamos los valores para s = 1
, en nuestro caso solo podemos tomar cada segundo elemento.
Entonces si la solución es caso de s = 1
fue
en caso de s = 2
será:
Verifique las posiciones de los valores 14, 2, 12, 6 en la matriz anterior. El único cambio que debemos realizar en nuestro código es cambiar los pasos de 1 a 2 para las dimensiones de ancho y alto (2-en, 3-en).
res = tf.squeeze(tf.nn.conv2d(image, kernel, [1, 2, 2, 1], "SAME"))
with tf.Session() as sess:
print sess.run(res)
Por cierto, no hay nada que nos impida utilizar diferentes zancadas para diferentes dimensiones.