Respuestas:
Quiero explicar con foto de C3D .
En pocas palabras, ¡la dirección convolucional y la forma de salida son importantes!
↑↑↑↑↑ Convoluciones 1D - Básico ↑↑↑↑↑
import tensorflow as tf
import numpy as np
sess = tf.Session()
ones_1d = np.ones(5)
weight_1d = np.ones(3)
strides_1d = 1
in_1d = tf.constant(ones_1d, dtype=tf.float32)
filter_1d = tf.constant(weight_1d, dtype=tf.float32)
in_width = int(in_1d.shape[0])
filter_width = int(filter_1d.shape[0])
input_1d = tf.reshape(in_1d, [1, in_width, 1])
kernel_1d = tf.reshape(filter_1d, [filter_width, 1, 1])
output_1d = tf.squeeze(tf.nn.conv1d(input_1d, kernel_1d, strides_1d, padding='SAME'))
print sess.run(output_1d)
↑↑↑↑↑ Convoluciones 2D - Básico ↑↑↑↑↑
ones_2d = np.ones((5,5))
weight_2d = np.ones((3,3))
strides_2d = [1, 1, 1, 1]
in_2d = tf.constant(ones_2d, dtype=tf.float32)
filter_2d = tf.constant(weight_2d, dtype=tf.float32)
in_width = int(in_2d.shape[0])
in_height = int(in_2d.shape[1])
filter_width = int(filter_2d.shape[0])
filter_height = int(filter_2d.shape[1])
input_2d = tf.reshape(in_2d, [1, in_height, in_width, 1])
kernel_2d = tf.reshape(filter_2d, [filter_height, filter_width, 1, 1])
output_2d = tf.squeeze(tf.nn.conv2d(input_2d, kernel_2d, strides=strides_2d, padding='SAME'))
print sess.run(output_2d)
↑↑↑↑↑ Convoluciones 3D - Básico ↑↑↑↑↑
ones_3d = np.ones((5,5,5))
weight_3d = np.ones((3,3,3))
strides_3d = [1, 1, 1, 1, 1]
in_3d = tf.constant(ones_3d, dtype=tf.float32)
filter_3d = tf.constant(weight_3d, dtype=tf.float32)
in_width = int(in_3d.shape[0])
in_height = int(in_3d.shape[1])
in_depth = int(in_3d.shape[2])
filter_width = int(filter_3d.shape[0])
filter_height = int(filter_3d.shape[1])
filter_depth = int(filter_3d.shape[2])
input_3d = tf.reshape(in_3d, [1, in_depth, in_height, in_width, 1])
kernel_3d = tf.reshape(filter_3d, [filter_depth, filter_height, filter_width, 1, 1])
output_3d = tf.squeeze(tf.nn.conv3d(input_3d, kernel_3d, strides=strides_3d, padding='SAME'))
print sess.run(output_3d)
↑↑↑↑↑ Convoluciones 2D con entrada 3D - LeNet, VGG, ..., ↑↑↑↑↑
in_channels = 32 # 3 for RGB, 32, 64, 128, ...
ones_3d = np.ones((5,5,in_channels)) # input is 3d, in_channels = 32
# filter must have 3d-shpae with in_channels
weight_3d = np.ones((3,3,in_channels))
strides_2d = [1, 1, 1, 1]
in_3d = tf.constant(ones_3d, dtype=tf.float32)
filter_3d = tf.constant(weight_3d, dtype=tf.float32)
in_width = int(in_3d.shape[0])
in_height = int(in_3d.shape[1])
filter_width = int(filter_3d.shape[0])
filter_height = int(filter_3d.shape[1])
input_3d = tf.reshape(in_3d, [1, in_height, in_width, in_channels])
kernel_3d = tf.reshape(filter_3d, [filter_height, filter_width, in_channels, 1])
output_2d = tf.squeeze(tf.nn.conv2d(input_3d, kernel_3d, strides=strides_2d, padding='SAME'))
print sess.run(output_2d)
in_channels = 32 # 3 for RGB, 32, 64, 128, ...
out_channels = 64 # 128, 256, ...
ones_3d = np.ones((5,5,in_channels)) # input is 3d, in_channels = 32
# filter must have 3d-shpae x number of filters = 4D
weight_4d = np.ones((3,3,in_channels, out_channels))
strides_2d = [1, 1, 1, 1]
in_3d = tf.constant(ones_3d, dtype=tf.float32)
filter_4d = tf.constant(weight_4d, dtype=tf.float32)
in_width = int(in_3d.shape[0])
in_height = int(in_3d.shape[1])
filter_width = int(filter_4d.shape[0])
filter_height = int(filter_4d.shape[1])
input_3d = tf.reshape(in_3d, [1, in_height, in_width, in_channels])
kernel_4d = tf.reshape(filter_4d, [filter_height, filter_width, in_channels, out_channels])
#output stacked shape is 3D = 2D x N matrix
output_3d = tf.nn.conv2d(input_3d, kernel_4d, strides=strides_2d, padding='SAME')
print sess.run(output_3d)
↑↑↑↑↑ Bonificación 1x1 conv en CNN - GoogLeNet, ..., ↑↑↑↑↑
in_channels = 32 # 3 for RGB, 32, 64, 128, ...
out_channels = 64 # 128, 256, ...
ones_3d = np.ones((1,1,in_channels)) # input is 3d, in_channels = 32
# filter must have 3d-shpae x number of filters = 4D
weight_4d = np.ones((3,3,in_channels, out_channels))
strides_2d = [1, 1, 1, 1]
in_3d = tf.constant(ones_3d, dtype=tf.float32)
filter_4d = tf.constant(weight_4d, dtype=tf.float32)
in_width = int(in_3d.shape[0])
in_height = int(in_3d.shape[1])
filter_width = int(filter_4d.shape[0])
filter_height = int(filter_4d.shape[1])
input_3d = tf.reshape(in_3d, [1, in_height, in_width, in_channels])
kernel_4d = tf.reshape(filter_4d, [filter_height, filter_width, in_channels, out_channels])
#output stacked shape is 3D = 2D x N matrix
output_3d = tf.nn.conv2d(input_3d, kernel_4d, strides=strides_2d, padding='SAME')
print sess.run(output_3d)
- Enlace original: LINK
- Autor: Martin Görner
- Twitter: @martin_gorner
- Google +: plus.google.com/+MartinGorne
↑↑↑↑↑ Convoluciones 1D con entrada 1D ↑↑↑↑↑
↑↑↑↑↑ Convoluciones 1D con entrada 2D ↑↑↑↑↑
in_channels = 32 # 3, 32, 64, 128, ...
out_channels = 64 # 3, 32, 64, 128, ...
ones_4d = np.ones((5,5,5,in_channels))
weight_5d = np.ones((3,3,3,in_channels,out_channels))
strides_3d = [1, 1, 1, 1, 1]
in_4d = tf.constant(ones_4d, dtype=tf.float32)
filter_5d = tf.constant(weight_5d, dtype=tf.float32)
in_width = int(in_4d.shape[0])
in_height = int(in_4d.shape[1])
in_depth = int(in_4d.shape[2])
filter_width = int(filter_5d.shape[0])
filter_height = int(filter_5d.shape[1])
filter_depth = int(filter_5d.shape[2])
input_4d = tf.reshape(in_4d, [1, in_depth, in_height, in_width, in_channels])
kernel_5d = tf.reshape(filter_5d, [filter_depth, filter_height, filter_width, in_channels, out_channels])
output_4d = tf.nn.conv3d(input_4d, kernel_5d, strides=strides_3d, padding='SAME')
print sess.run(output_4d)
sess.close()
1
, luego → para fila 1+stride
. La convolución en sí es invariante al desplazamiento, entonces, ¿por qué es importante la dirección de la convolución?
Siguiendo la respuesta de @runhani, estoy agregando algunos detalles más para que la explicación sea un poco más clara e intentaré explicar esto un poco más (y, por supuesto, con ejemplos de TF1 y TF2).
Uno de los principales bits adicionales que incluyo son:
tf.Variable
Así es como puede hacer una convolución 1D usando TF 1 y TF 2.
Y para ser específico, mis datos tienen las siguientes formas,
[batch size, width, in channels]
(p 1, 5, 1
. Ej. )[width, in channels, out channels]
(p 5, 1, 4
. Ej. )[batch size, width, out_channels]
(p 1, 5, 4
. Ej. )import tensorflow as tf
import numpy as np
inp = tf.placeholder(shape=[None, 5, 1], dtype=tf.float32)
kernel = tf.Variable(tf.initializers.glorot_uniform()([5, 1, 4]), dtype=tf.float32)
out = tf.nn.conv1d(inp, kernel, stride=1, padding='SAME')
with tf.Session() as sess:
tf.global_variables_initializer().run()
print(sess.run(out, feed_dict={inp: np.array([[[0],[1],[2],[3],[4]],[[5],[4],[3],[2],[1]]])}))
import tensorflow as tf
import numpy as np
inp = np.array([[[0],[1],[2],[3],[4]],[[5],[4],[3],[2],[1]]]).astype(np.float32)
kernel = tf.Variable(tf.initializers.glorot_uniform()([5, 1, 4]), dtype=tf.float32)
out = tf.nn.conv1d(inp, kernel, stride=1, padding='SAME')
print(out)
Es mucho menos trabajo con TF2 ya que TF2 no lo necesita Session
y, variable_initializer
por ejemplo.
Entonces, entendamos qué está haciendo esto usando un ejemplo de suavizado de señal. A la izquierda tienes el original y a la derecha tienes la salida de un Convolution 1D que tiene 3 canales de salida.
Los canales múltiples son básicamente representaciones de características múltiples de una entrada. En este ejemplo, tiene tres representaciones obtenidas por tres filtros diferentes. El primer canal es el filtro de suavizado de igual ponderación. El segundo es un filtro que pondera el medio del filtro más que los límites. El filtro final hace lo opuesto al segundo. Para que pueda ver cómo estos diferentes filtros producen diferentes efectos.
La convolución 1D se ha utilizado con éxito para la tarea de clasificación de frases .
Desactivado a convolución 2D. Si eres una persona de aprendizaje profundo, las posibilidades de que no te hayas encontrado con la convolución 2D son ... bueno, aproximadamente cero. Se utiliza en CNN para clasificación de imágenes, detección de objetos, etc., así como en problemas de PNL que involucran imágenes (por ejemplo, generación de subtítulos de imágenes).
Probemos un ejemplo, obtuve un kernel de convolución con los siguientes filtros aquí,
Y para ser específico, mis datos tienen las siguientes formas,
[batch_size, height, width, 1]
(p 1, 340, 371, 1
. Ej. )[height, width, in channels, out channels]
(p 3, 3, 1, 3
. Ej. )[batch_size, height, width, out_channels]
(p 1, 340, 371, 3
. Ej. )import tensorflow as tf
import numpy as np
from PIL import Image
im = np.array(Image.open(<some image>).convert('L'))#/255.0
kernel_init = np.array(
[
[[[-1, 1.0/9, 0]],[[-1, 1.0/9, -1]],[[-1, 1.0/9, 0]]],
[[[-1, 1.0/9, -1]],[[8, 1.0/9,5]],[[-1, 1.0/9,-1]]],
[[[-1, 1.0/9,0]],[[-1, 1.0/9,-1]],[[-1, 1.0/9, 0]]]
])
inp = tf.placeholder(shape=[None, image_height, image_width, 1], dtype=tf.float32)
kernel = tf.Variable(kernel_init, dtype=tf.float32)
out = tf.nn.conv2d(inp, kernel, strides=[1,1,1,1], padding='SAME')
with tf.Session() as sess:
tf.global_variables_initializer().run()
res = sess.run(out, feed_dict={inp: np.expand_dims(np.expand_dims(im,0),-1)})
import tensorflow as tf
import numpy as np
from PIL import Image
im = np.array(Image.open(<some image>).convert('L'))#/255.0
x = np.expand_dims(np.expand_dims(im,0),-1)
kernel_init = np.array(
[
[[[-1, 1.0/9, 0]],[[-1, 1.0/9, -1]],[[-1, 1.0/9, 0]]],
[[[-1, 1.0/9, -1]],[[8, 1.0/9,5]],[[-1, 1.0/9,-1]]],
[[[-1, 1.0/9,0]],[[-1, 1.0/9,-1]],[[-1, 1.0/9, 0]]]
])
kernel = tf.Variable(kernel_init, dtype=tf.float32)
out = tf.nn.conv2d(x, kernel, strides=[1,1,1,1], padding='SAME')
Aquí puede ver el resultado producido por el código anterior. La primera imagen es la original y, en el sentido de las agujas del reloj, tiene salidas del primer filtro, segundo filtro y tercer filtro.
En el contexto de la convolución 2D, es mucho más fácil comprender lo que significan estos canales múltiples. Digamos que está haciendo reconocimiento facial. Puede pensar en (esta es una simplificación muy poco realista pero transmite el punto) cada filtro representa un ojo, boca, nariz, etc. De modo que cada mapa de características sería una representación binaria de si esa característica está en la imagen que proporcionó . No creo que deba enfatizar que para un modelo de reconocimiento facial esas son características muy valiosas. Más información en este artículo .
Esta es una ilustración de lo que estoy tratando de articular.
La convolución 2D es muy frecuente en el ámbito del aprendizaje profundo.
Las CNN (redes neuronales de convolución) utilizan la operación de convolución 2D para casi todas las tareas de visión por computadora (por ejemplo, clasificación de imágenes, detección de objetos, clasificación de video).
Ahora se vuelve cada vez más difícil ilustrar lo que sucede a medida que aumenta el número de dimensiones. Pero con una buena comprensión de cómo funciona la convolución 1D y 2D, es muy sencillo generalizar esa comprensión a la convolución 3D. Así que aquí va.
Y para ser específico, mis datos tienen las siguientes formas,
[batch size, height, width, depth, in channels]
(p 1, 200, 200, 200, 1
. Ej. )[height, width, depth, in channels, out channels]
(p 5, 5, 5, 1, 3
. Ej. )[batch size, width, height, width, depth, out_channels]
(p 1, 200, 200, 2000, 3
. Ej. )import tensorflow as tf
import numpy as np
tf.reset_default_graph()
inp = tf.placeholder(shape=[None, 200, 200, 200, 1], dtype=tf.float32)
kernel = tf.Variable(tf.initializers.glorot_uniform()([5,5,5,1,3]), dtype=tf.float32)
out = tf.nn.conv3d(inp, kernel, strides=[1,1,1,1,1], padding='SAME')
with tf.Session() as sess:
tf.global_variables_initializer().run()
res = sess.run(out, feed_dict={inp: np.random.normal(size=(1,200,200,200,1))})
import tensorflow as tf
import numpy as np
x = np.random.normal(size=(1,200,200,200,1))
kernel = tf.Variable(tf.initializers.glorot_uniform()([5,5,5,1,3]), dtype=tf.float32)
out = tf.nn.conv3d(x, kernel, strides=[1,1,1,1,1], padding='SAME')
La convolución 3D se ha utilizado al desarrollar aplicaciones de aprendizaje automático que involucran datos LIDAR (Detección y rango de luz) que son de naturaleza tridimensional.
Muy bien, estás casi allí. Así que aguanta. Veamos qué es la zancada y el acolchado. Son bastante intuitivos si se piensa en ellos.
Si cruzas un pasillo, llegarás más rápido en menos pasos. Pero también significa que observó un entorno menor que si caminara por la habitación. ¡Refuercemos ahora nuestra comprensión con una bonita imagen también! Entendamos esto a través de la convolución 2D.
Cuando usa, tf.nn.conv2d
por ejemplo, debe configurarlo como un vector de 4 elementos. No hay razón para sentirse intimidado por esto. Solo contiene los pasos en el siguiente orden.
Convolución 2D - [batch stride, height stride, width stride, channel stride]
. Aquí, paso por lotes y paso de canal que acaba de establecer en uno (he estado implementando modelos de aprendizaje profundo durante 5 años y nunca tuve que configurarlos en nada excepto uno). Así que eso te deja solo con 2 zancadas para establecer.
Convolución 3D - [batch stride, height stride, width stride, depth stride, channel stride]
. Aquí solo te preocupas por los pasos de altura / ancho / profundidad.
Ahora, observa que no importa cuán pequeño sea su paso (es decir, 1), hay una reducción de dimensión inevitable que ocurre durante la convolución (por ejemplo, el ancho es 3 después de convolucionar una imagen de 4 unidades de ancho). Esto no es deseable, especialmente cuando se construyen redes neuronales de convolución profunda. Aquí es donde el acolchado viene al rescate. Hay dos tipos de relleno más utilizados.
SAME
y VALID
A continuación puede ver la diferencia.
Palabra final : si tiene mucha curiosidad, es posible que se esté preguntando. Acabamos de lanzar una bomba sobre la reducción de dimensión completamente automática y ahora hablamos de tener diferentes pasos. Pero lo mejor de la zancada es que controlas cuándo y cómo se reducen las dimensiones.
En resumen, en 1D CNN, el kernel se mueve en una dirección. Los datos de entrada y salida de 1D CNN son bidimensionales. Se utiliza principalmente en datos de series temporales.
En CNN 2D, el kernel se mueve en 2 direcciones. Los datos de entrada y salida de CNN 2D son tridimensionales. Se utiliza principalmente en datos de imagen.
En 3D CNN, el kernel se mueve en 3 direcciones. Los datos de entrada y salida de 3D CNN son de 4 dimensiones. Se utiliza principalmente en datos de imágenes 3D (resonancia magnética, tomografías computarizadas).
Puede encontrar más detalles aquí: https://medium.com/@xzz201920/conv1d-conv2d-and-conv3d-8a59182c4d6