Keras, comment obtenir la sortie de chaque couche?

155

J'ai formé un modèle de classification binaire avec CNN, et voici mon code

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))

Et ici, je veux obtenir la sortie de chaque couche comme TensorFlow, comment puis-je faire cela?

GoingMyWay
la source

Réponses:

182

Vous pouvez facilement obtenir les sorties de n'importe quelle couche en utilisant: model.layers[index].output

Pour toutes les couches, utilisez ceci:

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

Remarque: pour simuler l'utilisation de Dropout learning_phasecomme 1.dans le layer_outscas contraire0.

Edit: (basé sur les commentaires)

K.function crée les fonctions tensorielles ano / tensorflow qui sont utilisées plus tard pour obtenir la sortie du graphe symbolique étant donné l'entrée.

Il K.learning_phase()est désormais nécessaire en tant qu'entrée car de nombreuses couches Keras telles que Dropout / Batchnomalization en dépendent pour changer de comportement pendant l'entraînement et le test.

Donc, si vous supprimez la couche d'exclusion dans votre code, vous pouvez simplement utiliser:

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

Edit 2: plus optimisé

Je viens de réaliser que la réponse précédente n'est pas optimisée car pour chaque évaluation de fonction, les données seront transférées CPU-> GPU et les calculs de tenseur doivent également être effectués pour les couches inférieures over-n-over.

Au lieu de cela, c'est une bien meilleure façon car vous n'avez pas besoin de plusieurs fonctions mais d'une seule fonction vous donnant la liste de toutes les sorties:

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
la source
2
monsieur, votre réponse est bonne, que K.function([inp]+ [K.learning_phase()], [out])signifie votre code?
GoingMyWay
Excellente réponse, np.random.random(input_shape)[np.newaxis,...]peut aussi s'écrirenp.random.random(input_shape)[np.newaxis,:]
Tom
Qu'est-ce que K.function? comment est-il passé au GPU (MPI?)? qu'y a-t-il derrière la scène? Comment ça se passe avec CUDA? où est le code source?
Stav Bodik
3
@StavBodik Model construit la fonction de prédiction en utilisant K.function ici , et prédire l'utilise dans la boucle de prédiction ici . Prédire les boucles sur la taille du lot (si elle n'est pas définie par défaut sur 32), mais c'est pour atténuer les contraintes sur la mémoire du GPU. Je ne sais donc pas pourquoi vous observez model.predictest plus rapide.
indraforyou
1
J'obtiens ceci: InvalidArgumentError: S_input_39: 0 est à la fois alimenté et récupéré. ... quelqu'un avec des idées?
mathtick
138

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

Un moyen simple consiste à créer un nouveau modèle qui produira les couches qui vous intéressent:

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)

Vous pouvez également créer une fonction Keras qui retournera la sortie d'une certaine couche en fonction d'une certaine entrée, par exemple:

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]
ciel bleu
la source
si je pouvais, je vous en donnerais deux ^, Cette façon est tellement plus pratique lorsque vous avez un tas d'entrées.
Dan Erez
C'est assez clair d'après votre code ci-dessus, mais juste pour vérifier ma compréhension: après avoir créé un modèle à partir d'un modèle existant (en supposant qu'il soit déjà formé), il n'est pas nécessaire d'appeler set_weights sur le nouveau modèle. Est-ce exact?
JZ
Quelle est la différence entre layer_output = get_3rd_layer_output([X, 0])[0]et layer_output = get_3rd_layer_output([X, 1])[0]Les documents mentionnent le mode train et le mode test
Jason
désolé, pouvez-vous m'expliquer ce que fait exactement ce modèle? Devez-vous aussi le former? Je ne peux imaginer aucun schéma. Vous ajoutez la couche d'entrée d'un autre modèle, puis ajoutez une couche intermédiaire aléatoire de cet autre modèle en tant que sortie, et y alimentez-vous des entrées? Pourquoi faire cela au lieu d'alimenter le modèle d'origine et d'accéder directement à n'importe quelle couche intermédiaire dans laquelle il se trouve? Pourquoi créer ce modèle extra étrange? Et cela n'affectera-t-il pas la sortie? N'essaiera-t-il pas d'apprendre ou de nécessiter une formation, ou la couche apporte-t-elle ses propres poids pré-entraînés à partir du modèle d'origine?
PedroD
19

Sur la base de toutes les bonnes réponses de ce fil, j'ai écrit une bibliothèque pour récupérer la sortie de chaque couche. Il résume toute la complexité et a été conçu pour être le plus convivial possible:

https://github.com/philipperemy/keract

Il gère presque tous les cas de bord

J'espère que ça aide!

Philippe Remy
la source
8

La suite me semble très simple:

model.layers[idx].output

Au-dessus se trouve un objet tenseur, vous pouvez donc le modifier à l'aide d'opérations qui peuvent être appliquées à un objet tenseur.

Par exemple, pour obtenir la forme model.layers[idx].output.get_shape()

idx est l'indice du calque et vous pouvez le trouver à partir de model.summary()

diable dans les détails
la source
1
Quel est le problème avec cette réponse? Pourquoi n'est-ce pas la meilleure réponse?
Black Jack 21
1
Il renvoie un objet tenseur, pas un dataframe. Les objets tf sont bizarres à travailler.
HashRocketSyntax
7

J'ai écrit cette fonction pour moi-même (dans Jupyter) et elle a été inspirée par la réponse d' indraforyou . Il tracera automatiquement toutes les sorties de couche. Vos images doivent avoir une forme (x, y, 1) où 1 représente 1 canal. Vous appelez simplement plot_layer_outputs (...) pour tracer.

%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')
Miladiouss
la source
Et si le modèle a plusieurs entrées? Comment spécifier les entrées?
Antonio Sesto
Dans cette ligne: layer_outputs_list = [op ([test_image, 1.]). 1. doit-il être égal à 0? Il semble que 1 signifie formation et 0 signifie test? N'est-ce pas?
Kongsea
Cela ne fonctionne pas pour moi. J'ai utilisé une image couleur et cela me donne une erreur: InvalidArgumentError: input_2: 0 est à la fois alimenté et récupéré.
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
la source
Le lien est obsolète.
Saeed
5

Je voulais ajouter ceci en tant que commentaire (mais je n'ai pas assez de rep.) À la réponse de @ indraforyou pour corriger le problème mentionné dans le commentaire de @ mathtick. Pour éviter l' InvalidArgumentError: input_X:Y is both fed and fetched.exception, remplacez simplement la ligne outputs = [layer.output for layer in model.layers]par outputs = [layer.output for layer in model.layers][1:], ie

adapter l'exemple de travail minimal d'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 mes tentatives d'essayer des choses telles que outputs = [layer.output for layer in model.layers[1:]]ne fonctionnaient pas.

KamKam
la source
1
ce n'est pas tout à fait correct. Ce n'est que si la couche d'entrée est la première définie.
Mpizos Dimitris
Merci, cela a fonctionné pour moi et je veux juste vérifier que je comprends pourquoi, basé sur le commentaire de Mpizos: mon modèle est juste 3 couches (word embeddings - BiLSTM - CRF), donc je suppose que j'ai dû exclure la couche [0] car c'est juste des incorporations et ne devrait pas avoir d'activation, non?
KMunro
@MpizosDimitris oui c'est correct, mais dans l'exemple fourni par @indraforyou (que je modifiais), c'était le cas. @KMunro si je comprends bien, alors la raison pour laquelle vous ne vous souciez pas de votre sortie de la première couche est parce que c'est simplement la sortie du mot incorporant qui est juste le mot incorporant lui-même sous forme de tenseur (qui est juste le entrée dans la partie "réseau" de votre kerasmodèle). Votre calque d'intégration de mots est équivalent au calque d'entrée dans l'exemple fourni ici.
KamKam
3

En supposant que vous ayez:

1- Keras pré-formé model.

2- Saisie xcomme image ou ensemble d'images. La résolution de l'image doit être compatible avec la dimension de la couche d'entrée. Par exemple 80 * 80 * 3 pour une image à 3 canaux (RVB).

3- Le nom de la sortie layerpour obtenir l'activation. Par exemple, le calque "flatten_2". Cela devrait être inclus dans la layer_namesvariable, représente le nom des couches de la donnée model.

4- batch_sizeest un argument optionnel.

Ensuite, vous pouvez facilement utiliser la get_activationfonction pour obtenir l'activation de la sortie layerpour une entrée donnée xet pré-entraînée 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
la source
2

Dans le cas où vous avez l'un des cas suivants:

  • Erreur: InvalidArgumentError: input_X:Y is both fed and fetched
  • cas de plusieurs entrées

Vous devez effectuer les modifications suivantes:

  • ajouter un filtre pour les couches d'entrée dans la outputsvariable
  • changement mineur en functorsboucle

Exemple minimum:

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
la source
Que veut-on dire [x1, x2, xn, 1]? Mon x1 n'est pas défini et j'aimerais comprendre ce que vous y définissez.
HashRocketSyntax
@HashRocketSyntax x1et x2sont les entrées du modèle. Comme indiqué, au cas où vous auriez 2 entrées sur votre modèle.
Mpizos Dimitris
1

Eh bien, d'autres réponses sont très complètes, mais il existe un moyen très basique de "voir", et non de "récupérer" les formes.

Faites simplement un model.summary(). Il imprimera tous les calques et leurs formes de sortie. Les valeurs "Aucune" indiqueront des dimensions variables, et la première dimension sera la taille du lot.

Daniel Möller
la source
Il s'agit de la sortie de la couche (données entrées dans la couche de base) et non de la couche.
mathtick