Keras, ¿Cómo obtener la salida de cada capa?

155

He entrenado un modelo de clasificación binaria con CNN, y aquí está mi código

model = Sequential()
model.add(Convolution2D(nb_filters, kernel_size[0], kernel_size[1],
                        border_mode='valid',
                        input_shape=input_shape))
model.add(Activation('relu'))
model.add(Convolution2D(nb_filters, kernel_size[0], kernel_size[1]))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=pool_size))
# (16, 16, 32)
model.add(Convolution2D(nb_filters*2, kernel_size[0], kernel_size[1]))
model.add(Activation('relu'))
model.add(Convolution2D(nb_filters*2, kernel_size[0], kernel_size[1]))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=pool_size))
# (8, 8, 64) = (2048)
model.add(Flatten())
model.add(Dense(1024))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(2))  # define a binary classification problem
model.add(Activation('softmax'))

model.compile(loss='categorical_crossentropy',
              optimizer='adadelta',
              metrics=['accuracy'])
model.fit(x_train, y_train,
          batch_size=batch_size,
          nb_epoch=nb_epoch,
          verbose=1,
          validation_data=(x_test, y_test))

Y aquí, quiero obtener la salida de cada capa al igual que TensorFlow, ¿cómo puedo hacer eso?

GoingMyWay
fuente

Respuestas:

182

Puede obtener fácilmente los resultados de cualquier capa utilizando: model.layers[index].output

Para todas las capas, use esto:

from keras import backend as K

inp = model.input                                           # input placeholder
outputs = [layer.output for layer in model.layers]          # all layer outputs
functors = [K.function([inp, K.learning_phase()], [out]) for out in outputs]    # evaluation functions

# Testing
test = np.random.random(input_shape)[np.newaxis,...]
layer_outs = [func([test, 1.]) for func in functors]
print layer_outs

Nota: para simular el uso de la deserción learning_phasecomo 1.de lo layer_outscontrario, use0.

Editar: (basado en comentarios)

K.function crea funciones de tensor de flujo de teano / tensor que luego se utilizan para obtener la salida del gráfico simbólico dada la entrada.

Ahora K.learning_phase()se requiere como entrada ya que muchas capas de Keras como Dropout / Batchnomalization dependen de él para cambiar el comportamiento durante el entrenamiento y el tiempo de prueba.

Entonces, si elimina la capa de abandono en su código, simplemente puede usar:

from keras import backend as K

inp = model.input                                           # input placeholder
outputs = [layer.output for layer in model.layers]          # all layer outputs
functors = [K.function([inp], [out]) for out in outputs]    # evaluation functions

# Testing
test = np.random.random(input_shape)[np.newaxis,...]
layer_outs = [func([test]) for func in functors]
print layer_outs

Edición 2: más optimizado

Me acabo de dar cuenta de que la respuesta anterior no está tan optimizada, ya que para cada evaluación de la función, los datos se transferirán CPU-> GPU y también los cálculos de tensor deben hacerse para las capas inferiores una y otra vez.

En cambio, esta es una manera mucho mejor, ya que no necesita múltiples funciones, sino una sola función que le proporciona la lista de todas las salidas:

from keras import backend as K

inp = model.input                                           # input placeholder
outputs = [layer.output for layer in model.layers]          # all layer outputs
functor = K.function([inp, K.learning_phase()], outputs )   # evaluation function

# Testing
test = np.random.random(input_shape)[np.newaxis,...]
layer_outs = functor([test, 1.])
print layer_outs
indraforyou
fuente
2
señor, su respuesta es buena, ¿qué K.function([inp]+ [K.learning_phase()], [out])significa en su código?
GoingMyWay
Excelente respuesta, np.random.random(input_shape)[np.newaxis,...]también se puede escribir comonp.random.random(input_shape)[np.newaxis,:]
Tom
¿Qué es la función K? ¿Cómo pasó a GPU (MPI?)? ¿Qué hay detrás de la escena? ¿Cómo se habla con CUDA? donde esta el codigo fuente
Stav Bodik
3
@StavBodik Model construye la función de predicción usando K.function aquí , y predict la usa en el bucle de predicción aquí . Predecir bucles sobre el tamaño del lote (si no está configurado por defecto en 32), pero eso es para mitigar las restricciones en la memoria de la GPU. Así que no estoy seguro de por qué estás observando model.predictes más rápido.
indraforyou
1
Estoy obteniendo esto: InvalidArgumentError: S_input_39: 0 se alimenta y se recupera. ... alguien con ideas?
mathtick
138

Desde https://keras.io/getting-started/faq/#how-can-i-obtain-the-output-of-an-intermediate-layer

Una forma simple es crear un nuevo modelo que genere las capas que le interesan:

from keras.models import Model

model = ...  # include here your original model

layer_name = 'my_layer'
intermediate_layer_model = Model(inputs=model.input,
                                 outputs=model.get_layer(layer_name).output)
intermediate_output = intermediate_layer_model.predict(data)

Alternativamente, puede construir una función Keras que devolverá la salida de una determinada capa dada una determinada entrada, por ejemplo:

from keras import backend as K

# with a Sequential model
get_3rd_layer_output = K.function([model.layers[0].input],
                                  [model.layers[3].output])
layer_output = get_3rd_layer_output([x])[0]
cielo azul
fuente
si pudiera te daría dos ^, de esta manera es mucho más conveniente cuando tienes un montón de entradas.
Dan Erez
Está bastante claro en su código anterior, pero solo para verificar mi comprensión: después de crear un modelo a partir de un modelo existente (suponiendo que ya esté entrenado), no es necesario llamar a set_weights en el nuevo modelo. ¿Es eso correcto?
JZ
¿Cuál es la diferencia entre layer_output = get_3rd_layer_output([X, 0])[0]y? layer_output = get_3rd_layer_output([X, 1])[0]Los documentos mencionan el modo de tren y el modo de prueba
Jason
lo siento, ¿puedes explicarme qué hace exactamente este modelo? ¿Tienes que entrenarlo también? No puedo imaginarme ningún diagrama. ¿Agrega la capa de entrada de otro modelo, luego agrega una capa intermedia aleatoria de ese otro modelo como salida y le envía entradas? ¿Por qué hacer esto en lugar de alimentar el modelo original y obtener acceso directo a cualquier capa intermedia? ¿Por qué crear este modelo extra extraño? ¿Y no afectará la salida? ¿no tratará de aprender o requerir entrenamiento, o la capa trae sus propios pesos pre entrenados del modelo original?
PedroD
19

Basado en todas las buenas respuestas de este hilo, escribí una biblioteca para obtener el resultado de cada capa. Resume toda la complejidad y ha sido diseñado para ser lo más fácil de usar posible:

https://github.com/philipperemy/keract

Maneja casi todos los casos de borde

¡Espero eso ayude!

Philippe Remy
fuente
8

Lo siguiente me parece muy simple:

model.layers[idx].output

Arriba hay un objeto tensor, por lo que puede modificarlo mediante operaciones que se pueden aplicar a un objeto tensor.

Por ejemplo, para obtener la forma model.layers[idx].output.get_shape()

idx es el índice de la capa y puedes encontrarlo en model.summary()

diablo en los detalles
fuente
1
¿Qué hay de malo con esta respuesta? ¿Por qué no es esto votado como la mejor respuesta?
Black Jack 21 de
1
Devuelve un objeto tensor, no un marco de datos. Es raro trabajar con los objetos.
HashRocketSyntax
7

Escribí esta función para mí (en Jupyter) y se inspiró en la respuesta de indraforyou . Trazará todas las salidas de capa automáticamente. Sus imágenes deben tener una forma (x, y, 1) donde 1 representa 1 canal. Simplemente llame a plot_layer_outputs (...) para trazar.

%matplotlib inline
import matplotlib.pyplot as plt
from keras import backend as K

def get_layer_outputs():
    test_image = YOUR IMAGE GOES HERE!!!
    outputs    = [layer.output for layer in model.layers]          # all layer outputs
    comp_graph = [K.function([model.input]+ [K.learning_phase()], [output]) for output in outputs]  # evaluation functions

    # Testing
    layer_outputs_list = [op([test_image, 1.]) for op in comp_graph]
    layer_outputs = []

    for layer_output in layer_outputs_list:
        print(layer_output[0][0].shape, end='\n-------------------\n')
        layer_outputs.append(layer_output[0][0])

    return layer_outputs

def plot_layer_outputs(layer_number):    
    layer_outputs = get_layer_outputs()

    x_max = layer_outputs[layer_number].shape[0]
    y_max = layer_outputs[layer_number].shape[1]
    n     = layer_outputs[layer_number].shape[2]

    L = []
    for i in range(n):
        L.append(np.zeros((x_max, y_max)))

    for i in range(n):
        for x in range(x_max):
            for y in range(y_max):
                L[i][x][y] = layer_outputs[layer_number][x][y][i]


    for img in L:
        plt.figure()
        plt.imshow(img, interpolation='nearest')
Milagrosos
fuente
¿Qué pasa si el modelo tiene varias entradas? ¿Cómo especificar las entradas?
Antonio Sesto
En esta línea: layer_outputs_list = [op ([test_image, 1.]). ¿1. necesita ser 0? Parece que 1 representa entrenamiento y 0 representa prueba? No es es?
Kongsea
Esto no está funcionando para mí. He usado una imagen en color y me está dando un error: InvalidArgumentError: input_2: 0 se alimenta y se recupera.
Vaibhav K
5

De: https://github.com/philipperemy/keras-visualize-activations/blob/master/read_activations.py

import keras.backend as K

def get_activations(model, model_inputs, print_shape_only=False, layer_name=None):
    print('----- activations -----')
    activations = []
    inp = model.input

    model_multi_inputs_cond = True
    if not isinstance(inp, list):
        # only one input! let's wrap it in a list.
        inp = [inp]
        model_multi_inputs_cond = False

    outputs = [layer.output for layer in model.layers if
               layer.name == layer_name or layer_name is None]  # all layer outputs

    funcs = [K.function(inp + [K.learning_phase()], [out]) for out in outputs]  # evaluation functions

    if model_multi_inputs_cond:
        list_inputs = []
        list_inputs.extend(model_inputs)
        list_inputs.append(0.)
    else:
        list_inputs = [model_inputs, 0.]

    # Learning phase. 0 = Test mode (no dropout or batch normalization)
    # layer_outputs = [func([model_inputs, 0.])[0] for func in funcs]
    layer_outputs = [func(list_inputs)[0] for func in funcs]
    for layer_activations in layer_outputs:
        activations.append(layer_activations)
        if print_shape_only:
            print(layer_activations.shape)
        else:
            print(layer_activations)
    return activations
cannin
fuente
El enlace está en desuso.
Saeed
5

Quería agregar esto como un comentario (pero no tengo el representante lo suficientemente alto) a la respuesta de @ indraforyou para corregir el problema mencionado en el comentario de @ mathtick. Para evitar la InvalidArgumentError: input_X:Y is both fed and fetched.excepción, simplemente reemplace la línea outputs = [layer.output for layer in model.layers]con outputs = [layer.output for layer in model.layers][1:], es decir

Adaptando el ejemplo de trabajo mínimo de indraforyou:

from keras import backend as K 
inp = model.input                                           # input placeholder
outputs = [layer.output for layer in model.layers][1:]        # all layer outputs except first (input) layer
functor = K.function([inp, K.learning_phase()], outputs )   # evaluation function

# Testing
test = np.random.random(input_shape)[np.newaxis,...]
layer_outs = functor([test, 1.])
print layer_outs

ps mis intentos de probar cosas como outputs = [layer.output for layer in model.layers[1:]]no funcionaron.

KamKam
fuente
1
Eso no es exactamente correcto. Esto es solo si la capa de entrada es la primera definida.
Mpizos Dimitris
Gracias, esto funcionó para mí y solo quiero comprobar si entiendo por qué, según el comentario de Mpizos: mi modelo tiene solo 3 capas (incrustaciones de palabras - BiLSTM - CRF), así que supongo que tuve que excluir la capa [0] ya que es solo incrustaciones y no debería tener una activación, ¿verdad?
KMunro
@MpizosDimitris sí, eso es correcto, pero en el ejemplo proporcionado por @indraforyou (que estaba modificando), este fue el caso. @KMunro si estoy entendiendo correctamente, entonces la razón por la que no te importa tu salida de la primera capa es porque es simplemente la salida de la palabra incrustación, que es solo la palabra incrustada en forma de tensor (que es solo el entrada a la parte de "red" de su kerasmodelo). Su capa de incrustaciones de palabras es equivalente a la capa de entrada en el ejemplo proporcionado aquí.
KamKam
3

Asumiendo que tienes:

1- Keras pre-entrenado model.

2- Entrada xcomo imagen o conjunto de imágenes. La resolución de la imagen debe ser compatible con la dimensión de la capa de entrada. Por ejemplo, 80 * 80 * 3 para imágenes de 3 canales (RGB).

3- El nombre de la salida layerpara obtener la activación. Por ejemplo, la capa "flatten_2". Esto debe incluirse en la layer_namesvariable, representa el nombre de las capas de lo dado model.

4- batch_sizees un argumento opcional.

Entonces puede usar fácilmente la get_activationfunción para obtener la activación de la salida layerpara una entrada determinada xy pre-entrenado model:

import six
import numpy as np
import keras.backend as k
from numpy import float32
def get_activations(x, model, layer, batch_size=128):
"""
Return the output of the specified layer for input `x`. `layer` is specified by layer index (between 0 and
`nb_layers - 1`) or by name. The number of layers can be determined by counting the results returned by
calling `layer_names`.
:param x: Input for computing the activations.
:type x: `np.ndarray`. Example: x.shape = (80, 80, 3)
:param model: pre-trained Keras model. Including weights.
:type model: keras.engine.sequential.Sequential. Example: model.input_shape = (None, 80, 80, 3)
:param layer: Layer for computing the activations
:type layer: `int` or `str`. Example: layer = 'flatten_2'
:param batch_size: Size of batches.
:type batch_size: `int`
:return: The output of `layer`, where the first dimension is the batch size corresponding to `x`.
:rtype: `np.ndarray`. Example: activations.shape = (1, 2000)
"""

    layer_names = [layer.name for layer in model.layers]
    if isinstance(layer, six.string_types):
        if layer not in layer_names:
            raise ValueError('Layer name %s is not part of the graph.' % layer)
        layer_name = layer
    elif isinstance(layer, int):
        if layer < 0 or layer >= len(layer_names):
            raise ValueError('Layer index %d is outside of range (0 to %d included).'
                             % (layer, len(layer_names) - 1))
        layer_name = layer_names[layer]
    else:
        raise TypeError('Layer must be of type `str` or `int`.')

    layer_output = model.get_layer(layer_name).output
    layer_input = model.input
    output_func = k.function([layer_input], [layer_output])

    # Apply preprocessing
    if x.shape == k.int_shape(model.input)[1:]:
        x_preproc = np.expand_dims(x, 0)
    else:
        x_preproc = x
    assert len(x_preproc.shape) == 4

    # Determine shape of expected output and prepare array
    output_shape = output_func([x_preproc[0][None, ...]])[0].shape
    activations = np.zeros((x_preproc.shape[0],) + output_shape[1:], dtype=float32)

    # Get activations with batching
    for batch_index in range(int(np.ceil(x_preproc.shape[0] / float(batch_size)))):
        begin, end = batch_index * batch_size, min((batch_index + 1) * batch_size, x_preproc.shape[0])
        activations[begin:end] = output_func([x_preproc[begin:end]])[0]

    return activations
imanzabet
fuente
2

En caso de que tenga uno de los siguientes casos:

  • error: InvalidArgumentError: input_X:Y is both fed and fetched
  • caso de entradas múltiples

Debe hacer los siguientes cambios:

  • agregar filtro para las capas de entrada en outputsvariable
  • cambio menor en el functorsbucle

Ejemplo mínimo:

from keras.engine.input_layer import InputLayer
inp = model.input
outputs = [layer.output for layer in model.layers if not isinstance(layer, InputLayer)]
functors = [K.function(inp + [K.learning_phase()], [x]) for x in outputs]
layer_outputs = [fun([x1, x2, xn, 1]) for fun in functors]
Mpizos Dimitris
fuente
¿Qué se entiende por [x1, x2, xn, 1]? Mi x1 no está definido y me gustaría entender lo que está definiendo allí.
HashRocketSyntax
@HashRocketSyntax x1y x2son las entradas del modelo. Como se dijo, en caso de que tenga 2 entradas en su modelo.
Mpizos Dimitris
1

Bueno, otras respuestas son muy completas, pero hay una forma muy básica de "ver", no de "obtener" las formas.

Solo haz un model.summary(). Imprimirá todas las capas y sus formas de salida. Los valores "Ninguno" indicarán dimensiones variables, y la primera dimensión será el tamaño del lote.

Daniel Möller
fuente
Se trata de la salida de la capa (entradas dadas a la capa base), no de la capa.
mathtick