Encontrar el gradiente de un filtro conv de Caffe con respecto a la entrada


Necesito encontrar el gradiente con respecto a la capa de entrada para un solo filtro convolucional en una red neuronal convolucional (CNN) como una forma de visualizar los filtros.
Dada una red entrenada en la interfaz Python de Caffe como la de este ejemplo , ¿cómo puedo entonces encontrar el gradiente de un conv-filter con respecto a los datos en la capa de entrada?

Editar:

Basado en la respuesta de cesans , agregué el siguiente código. Las dimensiones de mi capa de entrada es [8, 8, 7, 96]. Mi primera capa conv, conv1, tiene 11 filtros con un tamaño de 1x5, lo que resulta en las dimensiones [8, 11, 7, 92].

net = solver.net
diffs = net.backward(diffs=['data', 'conv1'])
print diffs.keys() # >> ['conv1', 'data']
print diffs['data'].shape # >> (8, 8, 7, 96)
print diffs['conv1'].shape # >> (8, 11, 7, 92)

Como puede ver en la salida, las dimensiones de los arrays devueltos por net.backward() son iguales a las dimensiones de mis capas en Caffe. Después de algunas pruebas he encontrado que esta salida es los gradientes de la pérdida con respecto a la capa data y la capa conv1 respectivamente.

Sin embargo, mi pregunta era cómo encontrar el gradiente de un solo filtro conv con respecto a los datos en la capa de entrada, que es otra cosa. ¿Cómo puedo lograr esto?

Author: Community, 2015-07-09

2 answers

Caffe net hace malabares con dos "corrientes" de números.
El primero es el "flujo" de datos: imágenes y etiquetas empujadas a través de la red. A medida que estos insumos avanzan a través de la red, se convierten en representación de alto nivel y, finalmente, en vectores de probabilidades de clase (en tareas de clasificación).
La segunda "corriente" contiene los parámetros de las diferentes capas, los pesos de las circunvoluciones, los sesgos, etc. Estos números / pesos se cambian y se aprenden durante la fase de tren de la neto.

A pesar del papel fundamentalmente diferente que juegan estos dos "flujos", caffe utiliza la misma estructura de datos, blob, para almacenarlos y administrarlos.
Sin embargo, para cada capa hay dos diferentes vectores blobs uno para cada flujo.

Aquí hay un ejemplo que espero que aclare:

import caffe
solver = caffe.SGDSolver( PATH_TO_SOLVER_PROTOTXT )
net = solver.net

Si ahora nos fijamos en

net.blobs

Verá un diccionario que almacena un objeto "caffe blob" para cada capa de la red. Cada gota tiene espacio de almacenamiento para tanto los datos como el gradiente

net.blobs['data'].data.shape    # >> (32, 3, 224, 224)
net.blobs['data'].diff.shape    # >> (32, 3, 224, 224)

Y para una capa convolucional:

net.blobs['conv1/7x7_s2'].data.shape    # >> (32, 64, 112, 112)
net.blobs['conv1/7x7_s2'].diff.shape    # >> (32, 64, 112, 112)

net.blobs contiene el primer flujo de datos, su forma coincide con la de las imágenes de entrada hasta el vector de probabilidad de clase resultante.

Por otro lado, se puede ver otro miembro de net

net.layers

Este es un vector caffe que almacena los parámetros de las diferentes capas.
Mirando la primera capa ('data' capa):

len(net.layers[0].blobs)    # >> 0

No hay parámetros que almacenar para una entrada estrato.
Por otro lado, para la primera capa convolucional

len(net.layers[1].blobs)    # >> 2

La red almacena un blob para los pesos del filtro y otro para el sesgo constante. Aquí están

net.layers[1].blobs[0].data.shape  # >> (64, 3, 7, 7)
net.layers[1].blobs[1].data.shape  # >> (64,)

Como puede ver, esta capa realiza 7x7 circunvoluciones en la imagen de entrada de 3 canales y tiene 64 filtros de este tipo.

Ahora, cómo obtener los gradientes? bueno, como usted señaló

diffs = net.backward(diffs=['data','conv1/7x7_s2'])

Devuelve los gradientes de la secuencia data. Podemos verificar esto por

np.all( diffs['data'] == net.blobs['data'].diff )  # >> True
np.all( diffs['conv1/7x7_s2'] == net.blobs['conv1/7x7_s2'].diff )  # >> True

(TL; DR ) Desea los gradientes de los parámetros, estos se almacenan en el net.layers con los parámetros:

net.layers[1].blobs[0].diff.shape # >> (64, 3, 7, 7)
net.layers[1].blobs[1].diff.shape # >> (64,)

Para ayudarlo a mapear entre los nombres de las capas y sus índices en el vector net.layers, puede usar net._layer_names.


Actualización con respecto al uso de gradientes para visualizar respuestas de filtro:
Un gradiente se define normalmente para una función escalar. La pérdida es un escalar, y por lo tanto se puede hablar de un gradiente de peso de píxel / filtro con respecto a la pérdida escalar. Este gradiente es un solo número por peso de píxel/filtro.
Si desea obtener la entrada que resulta con la activación máxima de un nodo interno oculto específico, necesita una red "auxiliar" cuya pérdida es exactamente una medida de la activación del nodo oculto específico que desea visualizar. Una vez que tenga esta red auxiliar, puede comenzar desde una entrada arbitraria y cambiar esta entrada en función de los gradientes de la pérdida auxiliar a la capa de entrada:

update = prev_in + lr * net.blobs['data'].diff
 27
Author: Shai,
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
2015-08-06 09:07:50

Puede obtener los degradados en términos de cualquier capa cuando ejecuta el paso backward(). Simplemente especifique la lista de capas cuando llame a la función. Para mostrar los degradados en términos de la capa de datos:

net.forward()
diffs = net.backward(diffs=['data', 'conv1'])`
data_point = 16
plt.imshow(diffs['data'][data_point].squeeze())

En algunos casos es posible que desee forzar que todas las capas se lleven a cabo hacia atrás, mire el parámetro force_backward del modelo.

Https://github.com/BVLC/caffe/blob/master/src/caffe/proto/caffe.proto

 10
Author: cesans,
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
2015-07-10 20:42:29