En una CNN, ¿cada filtro nuevo tiene pesos diferentes para cada canal de entrada, o se usan los mismos pesos de cada filtro en los canales de entrada?


28

Entiendo que la capa convolucional de una red neuronal convolucional tiene cuatro dimensiones: input_channels, filter_height, filter_width, number_of_filters. Además, entiendo que cada filtro nuevo se enreda en TODOS los canales de entrada (o mapas de características / activación de la capa anterior).

SIN EMBARGO, el siguiente gráfico de CS231 muestra cada filtro (en rojo) que se aplica a un CANAL ÚNICO, en lugar del mismo filtro que se utiliza en todos los canales. Esto parece indicar que hay un filtro separado para CADA canal (en este caso, supongo que son los tres canales de color de una imagen de entrada, pero lo mismo se aplicaría para todos los canales de entrada).

Esto es confuso: ¿hay un filtro único diferente para cada canal de entrada?

ingrese la descripción de la imagen aquí

Fuente: http://cs231n.github.io/convolutional-networks/

La imagen de arriba parece contradictoria con un extracto de los "Fundamentos del aprendizaje profundo" de O'reilly :

"... los filtros no solo funcionan en un único mapa de entidades. Funcionan en todo el volumen de mapas de entidades que se han generado en una capa particular ... Como resultado, los mapas de entidades deben poder operar sobre volúmenes, no solo áreas "

... También, entiendo que estas imágenes a continuación indican que EL MISMO filtro está enredado en los tres canales de entrada (contradictorio con lo que se muestra en el gráfico CS231 anterior):

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí


Respuestas:


13

En una red neuronal convolucional, ¿hay un filtro único para cada canal de entrada o se utilizan los mismos filtros nuevos en todos los canales de entrada?

El primero De hecho, hay un núcleo separado definido para cada combinación de canal de entrada / canal de salida.

Típicamente para una arquitectura CNN, en un solo filtro como se describe en su number_of_filtersparámetro, hay un núcleo 2D por canal de entrada. Hay input_channels * number_of_filtersconjuntos de pesos, cada uno de los cuales describe un núcleo de convolución. Por lo tanto, los diagramas que muestran un conjunto de pesos por canal de entrada para cada filtro son correctos. El primer diagrama también muestra claramente que los resultados de la aplicación de esos núcleos se combinan al sumarlos y agregar sesgos para cada canal de salida.

Esto también puede verse como el uso de una convolución 3D para cada canal de salida, que tiene la misma profundidad que la entrada. Que es lo que muestra su segundo diagrama, y ​​también lo que muchas bibliotecas harán internamente. Matemáticamente, este es el mismo resultado (siempre que las profundidades coincidan exactamente), aunque el tipo de capa generalmente se etiqueta como "Conv2D" o similar. Del mismo modo, si su tipo de entrada es inherentemente 3D, como voxels o un video, entonces podría usar una capa "Conv3D", pero internamente podría implementarse como una convolución 4D.


Gracias por esta explicación. Parece que cada filtro en realidad tiene varias input_channelsversiones con diferentes pesos . ¿Tiene una fuente "oficial" que confirme este entendimiento?
Ryan Chase

@RyanChase: Sí, eso es correcto. Simplemente le señalaría el curso de Andrew Ng sobre CNN, comenzando aquí con cómo se procesaría la imagen en color: coursera.org/learn/convolutional-neural-networks/lecture/ctQZz/…
Neil Slater el

Me gustaría señalar que, en esa fuente ( cs231n.github.io/convolutional-networks ), los filtros (pesos o kernesl) son volúmenes (es decir, tridimensionales), y tienen la misma tercera dimensión que la entrada. volumen. Además, como se dice (al menos) ahora en esa fuente, los volúmenes se han dividido en la tercera dimensión para visualizar mejor la aplicación del filtro al volumen de entrada. No creo que, en general, "haya un núcleo separado definido para cada combinación de canal de entrada / canal de salida". es correcto.
nbro

Tenga en cuenta que los filtros (o núcleos) son los pesos que deben aprenderse (es decir, no son fijos, pero en realidad son los parámetros de la CNN). Puede ser que sean (es decir, las rebanadas del filtro), al final, iguales en toda la tercera dimensión.
nbro

@nbro: Sí, puede implementar una convolución 2D a través de múltiples cortes 2D como una sola convolución 3D con la profundidad del núcleo igual al número de canales. Matemáticamente esto es idéntico a mi descripción. También puede verlo como una red de alimentación directa truncada y totalmente conectada con pesos compartidos (muchos de los cuales son cero). Esta respuesta se centra en cuál es la vista de los filtros 2D, porque el OP pregunta sobre cómo están organizados los filtros 2D. De hecho, pueden organizarse en un núcleo 3D más grande, pero todavía se aplican como núcleos 2D utilizando el "truco" de que la convolución 3D es equivalente.
Neil Slater

12

La siguiente imagen que usó en su pregunta describe con mucha precisión lo que está sucediendo. Recuerde que cada elemento del filtro 3D (cubo gris) está formado por un valor ( 3x3x3=27valores) diferente. Por lo tanto, se pueden concatenar tres filtros 2D de tamaño diferentes 3x3para formar este filtro de tamaño 3D3x3x3 .

convnet2D

El 3x3x3fragmento RGB de la imagen se multiplica por elementos por un filtro 3D (se muestra en gris). En este caso, el filtro tiene 3x3x3=27pesos. Cuando estos pesos se multiplican por elementos y luego se suman, se obtiene un valor.


Entonces, ¿hay un filtro separado para cada canal de entrada?

, hay tantos filtros 2D como número de canales de entrada en la imagen. Sin embargo , es útil si cree que para las matrices de entrada con más de un canal, solo hay un filtro 3D (como se muestra en la imagen de arriba).


Entonces, ¿por qué esto se llama convolución 2D (si el filtro es 3D y la matriz de entrada es 3D)?

Esta es una convolución 2D porque las zancadas del filtro son solo a lo largo de las dimensiones de altura y anchura ( NO profundidad) y, por lo tanto, la salida producida por esta convolución también es una matriz 2D. El número de direcciones de movimiento del filtro determina las dimensiones de la convolución.

Nota: Si desarrolla su comprensión visualizando un solo filtro 3D en lugar de múltiples filtros 2D (uno para cada capa), tendrá un tiempo fácil para comprender arquitecturas avanzadas de CNN como Resnet, InceptionV3, etc.


Esta es una buena explicación, pero más específicamente, la pregunta que estoy tratando de entender es si los filtros que operan en cada canal de entrada son copias de los mismos pesos o pesos completamente diferentes. Esto en realidad no se muestra en la imagen y, de hecho, para mí esa imagen sugiere que son los mismos pesos aplicados a cada canal (ya que tienen el mismo color) ... Según la respuesta de @neil slater, suena como cada El filtro en realidad tiene varias input_channelsversiones con diferentes pesos. Si esto también lo comprende, ¿hay una fuente "oficial" que lo confirme?
Ryan Chase

Sí, de hecho, eso también lo entiendo. Para mí, eso estaba claro cuando intenté pensar que ese cubo gris estaría compuesto por 27 valores de peso diferentes. Esto significa que hay 3 filtros 2D diferentes en lugar del mismo filtro 2D aplicado a cada capa de entrada.
Mohsin Bukhari

No pude encontrar ninguna fuente oficial para confirmar esto. Sin embargo, cuando estaba tratando de entender este mismo concepto, creé una entrada ficticia y un filtro de peso en Tensorflow y observé la salida. Estaba contento con eso. Si encuentro alguna explicación oficial . Editaré mi respuesta arriba.
Mohsin Bukhari

Si sigue la ruta de Tensorflow. Puede imprimir su filtro de peso después de mostrar a su capa CNN ficticia una muestra de entrada.
Mohsin Bukhari

@Moshsin Bukhari Definitivamente intentaré explorar los filtros dentro de TensorFlow. ¿Estarías dispuesto a compartir tu código de cómo hiciste para explorar lo que está contenido en los filtros? ¿Puede imprimir los valores del filtro en cada paso de la red, por ejemplo?
Ryan Chase

3

Estoy siguiendo las respuestas anteriores con un ejemplo concreto con la esperanza de aclarar aún más cómo funciona la convolución con respecto a los canales de entrada y salida y los pesos, respectivamente:

Deje que el ejemplo sea el siguiente (wrt a 1 capa convolucional):

  • el tensor de entrada es 9x9x5, es decir, 5 canales de entrada, entonces input_channels=5
  • el tamaño del filtro / núcleo es 4x4 y el paso es 1
  • el tensor de salida es 6x6x56, es decir, 56 canales de salida, entonces output_channels=56
  • el tipo de relleno es "VÁLIDO" (es decir, sin relleno)

Notamos eso:

  • Como la entrada tiene 5 canales, la dimensión del filtro se convierte en 4x4x5, es decir, hay 5 filtros 2D únicos y separados de tamaño 4x4 (es decir, cada uno tiene 16 pesos); Para convolucionar sobre la entrada de tamaño 9x9x5, el filtro se convierte en 3D y debe ser de tamaño 4x4x5
  • por lo tanto: para cada canal de entrada, existe un filtro 2D distinto con 16 pesos diferentes cada uno. En otras palabras, el número de filtros 2D coincide con el número de canales de entrada.
  • Como hay 56 canales de salida, debe haber 56 filtros tridimensionales W0, W1, ..., W55 de tamaño 4x4x5 (cf. en el gráfico CS231 hay 2 filtros tridimensionales W0, W1 para dar cuenta de la salida 2 canales), donde la tercera dimensión del tamaño 5 representa el enlace a los 5 canales de entrada (véase en el gráfico CS231 cada filtro 3D W0, W1 tiene la tercera dimensión 3, que coincide con los 3 canales de entrada)
  • por lo tanto: la cantidad de filtros 3D es igual a la cantidad de canales de salida

Esa capa convolucional contiene así:

56 filtros tridimensionales de tamaño 4x4x5 (= 80 pesos diferentes cada uno) para dar cuenta de los 56 canales de salida donde cada uno tiene un valor para la 3ª dimensión de 5 para que coincida con los 5 canales de entrada. En total hay

number_of_filters=input_channel*output_channels=5*56=280

Filtros 2D de tamaño 4x4 (es decir, 280x16 pesos diferentes en total).


0

Solo hay restricciones en 2D. ¿Por qué?

Imagina una capa completamente conectada.

Sería terriblemente enorme, cada neurona se conectaría a quizás neuronas de entrada de 1000x1000x3. Pero sabemos que el procesamiento de píxeles cercanos tiene sentido, por lo tanto, nos limitamos a un pequeño vecindario 2D, por lo que cada neurona está conectada a solo 3x3 cerca de las neuronas en 2D. No sabemos tal cosa sobre los canales, por lo que nos conectamos a todos los canales.

Aún así, habría demasiados pesos. Pero debido a la invariancia de la traducción, un filtro que funcione bien en un área probablemente sea útil en un área diferente. Entonces usamos el mismo conjunto de pesos en 2D. Una vez más, no existe tal invariancia de traducción entre canales, por lo que no existe tal restricción allí.


0

Consulte la sección "Conectividad local" en http://cs231n.github.io/convolutional-networks/ y diapositiva 7-18.

El hiperparámetro de "campo receptivo" del filtro se define solo por la altura y el ancho, ya que la profundidad se fija por la profundidad de la capa anterior.

TENGA EN CUENTA que "La extensión de la conectividad a lo largo del eje de profundidad es siempre igual a la PROFUNDIDAD del volumen de entrada" -o- PROFUNDIDAD del mapa de activación (en el caso de capas posteriores).

Intuitivamente, esto debe ser debido al hecho de que los datos de los canales de imagen están intercalados, no planos. De esta forma, la aplicación del filtro se puede lograr simplemente mediante la multiplicación de vectores de columna.

Tenga en cuenta que Convolutional Network aprende todos los parámetros de filtro (incluida la dimensión de profundidad) y son "h w input_layer_depth + 1 (sesgo)".


0

Recomiendo el capítulo 2.2.1 de mi tesis de maestría como respuesta. Para agregar a las respuestas restantes:

Keras es tu amigo para entender lo que sucede:

from keras.models import Sequential
from keras.layers import Conv2D

model = Sequential()
model.add(Conv2D(32, input_shape=(28, 28, 3),
          kernel_size=(5, 5),
          padding='same',
          use_bias=False))
model.add(Conv2D(17, (3, 3), padding='same', use_bias=False))
model.add(Conv2D(13, (3, 3), padding='same', use_bias=False))
model.add(Conv2D(7, (3, 3), padding='same', use_bias=False))
model.compile(loss='categorical_crossentropy', optimizer='adam')

print(model.summary())

da

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 28, 28, 32)        2400      
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 28, 28, 17)        4896      
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 28, 28, 13)        1989      
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 28, 28, 7)         819       
=================================================================
Total params: 10,104

Intenta formular tus opciones. ¿Qué significaría eso para los parámetros si otra cosa fuera el caso?

2400=32(35 55 5)

Este enfoque también lo ayuda con otros tipos de capas, no solo capas convolucionales.

Tenga en cuenta también que puede implementar diferentes soluciones, que pueden tener otros números de parámetros.


0

Solo para dejar dos detalles absolutamente claros:

nortenorte3×3norte2norte

norte2norte3×3×

La forma más fácil de convencerse de esto es imaginar lo que sucede en otros escenarios y ver que la computación se degenera, es decir, si no intercala y recombina los resultados, entonces las diferentes salidas en realidad no harían nada. Tendría el mismo efecto que una salida única con pesos combinados.


0

Para cualquiera que intente comprender cómo se calculan las convoluciones, aquí hay un fragmento de código útil en Pytorch:

batch_size = 1
height = 3 
width = 3
conv1_in_channels = 2
conv1_out_channels = 2
conv2_out_channels = 2
kernel_size = 2
# (N, C_in, H, W) is shape of all tensors. (batch_size, channels, height, width)
input = torch.Tensor(np.arange(0, batch_size*height*width*in_channels).reshape(batch_size, in_channels, height, width))
conv1 = nn.Conv2d(in_channels, conv1_out_channels, kernel_size, bias=False) # no bias to make calculations easier
# set the weights of the convolutions to make the convolutions easier to follow
nn.init.constant_(conv1.weight[0][0], 0.25)
nn.init.constant_(conv1.weight[0][1], 0.5)
nn.init.constant_(conv1.weight[1][0], 1) 
nn.init.constant_(conv1.weight[1][1], 2) 
out1 = conv1(input) # compute the convolution

conv2 = nn.Conv2d(conv1_out_channels, conv2_out_channels, kernel_size, bias=False)
nn.init.constant_(conv2.weight[0][0], 0.25)
nn.init.constant_(conv2.weight[0][1], 0.5)
nn.init.constant_(conv2.weight[1][0], 1) 
nn.init.constant_(conv2.weight[1][1], 2) 
out2 = conv2(out1) # compute the convolution

for tensor, name in zip([input, conv1.weight, out1, conv2.weight, out2], ['input', 'conv1', 'out1', 'conv2', 'out2']):
    print('{}: {}'.format(name, tensor))
    print('{} shape: {}'.format(name, tensor.shape))

Ejecutar esto da el siguiente resultado:

input: tensor([[[[ 0.,  1.,  2.],
          [ 3.,  4.,  5.],
          [ 6.,  7.,  8.]],

         [[ 9., 10., 11.],
          [12., 13., 14.],
          [15., 16., 17.]]]])
input shape: torch.Size([1, 2, 3, 3])
conv1: Parameter containing:
tensor([[[[0.2500, 0.2500],
          [0.2500, 0.2500]],

         [[0.5000, 0.5000],
          [0.5000, 0.5000]]],


        [[[1.0000, 1.0000],
          [1.0000, 1.0000]],

         [[2.0000, 2.0000],
          [2.0000, 2.0000]]]], requires_grad=True)
conv1 shape: torch.Size([2, 2, 2, 2])
out1: tensor([[[[ 24.,  27.],
          [ 33.,  36.]],

         [[ 96., 108.],
          [132., 144.]]]], grad_fn=<MkldnnConvolutionBackward>)
out1 shape: torch.Size([1, 2, 2, 2])
conv2: Parameter containing:
tensor([[[[0.2500, 0.2500],
          [0.2500, 0.2500]],

         [[0.5000, 0.5000],
          [0.5000, 0.5000]]],


        [[[1.0000, 1.0000],
          [1.0000, 1.0000]],

         [[2.0000, 2.0000],
          [2.0000, 2.0000]]]], requires_grad=True)
conv2 shape: torch.Size([2, 2, 2, 2])
out2: tensor([[[[ 270.]],

         [[1080.]]]], grad_fn=<MkldnnConvolutionBackward>)
out2 shape: torch.Size([1, 2, 1, 1])

Observe cómo cada canal de la convolución suma sobre todas las salidas de canales anteriores.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.