Keras, Come ottenere l'output di ogni livello?


155

Ho addestrato un modello di classificazione binaria con la CNN, ed ecco il mio codice

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

E qui, voglio ottenere l'output di ogni livello proprio come TensorFlow, come posso farlo?

Risposte:


182

Puoi ottenere facilmente gli output di qualsiasi livello usando: model.layers[index].output

Per tutti i livelli usare questo:

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: per simulare l'uso di Dropout learning_phasecome 1.in layer_outsuso altrimenti0.

Modifica: (basato sui commenti)

K.function crea funzioni tensore teano / tensorflow che vengono successivamente utilizzate per ottenere l'output dal grafico simbolico dato l'input.

Ora K.learning_phase()è richiesto come input poiché molti livelli di Keras come Dropout / Batchnomalization dipendono da esso per cambiare comportamento durante l'allenamento e il tempo di test.

Quindi se rimuovi il livello di abbandono nel tuo codice puoi semplicemente usare:

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

Modifica 2: più ottimizzato

Mi sono appena reso conto che la risposta precedente non è così ottimizzata in quanto per ogni valutazione delle funzioni i dati verranno trasferiti CPU-> memoria GPU e anche i calcoli del tensore devono essere eseguiti per i livelli inferiori over-n-over.

Invece questo è un modo molto migliore in quanto non sono necessarie più funzioni ma un'unica funzione che ti fornisce l'elenco di tutti gli output:

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

2
signore, la tua risposta è buona, che K.function([inp]+ [K.learning_phase()], [out])significa nel tuo codice?
GoingMyWay,

Ottima risposta, np.random.random(input_shape)[np.newaxis,...]può anche essere scritta comenp.random.random(input_shape)[np.newaxis,:]
Tom

Che cos'è K.function? come è passato a GPU (MPI?)? cosa c'è dietro la scena? Come si parla con CUDA? dov'è il codice sorgente?
Stav Bodik,

3
Il modello @StavBodik crea la funzione di previsione usando K.function qui e predice la utilizza nel ciclo di previsione qui . Prevedere loop sulla dimensione del batch (se non impostato come predefinito su 32) ma questo per mitigare i vincoli sulla memoria GPU. Quindi non sono sicuro del motivo per cui stai osservando model.predictè più veloce.
indraforyou,

1
Sto ottenendo questo: InvalidArgumentError: S_input_39: 0 viene sia alimentato che recuperato. ... qualcuno con idee?
matematico

138

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

Un modo semplice è quello di creare un nuovo modello che produrrà i livelli che ti interessano:

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)

In alternativa, puoi creare una funzione di Keras che restituirà l'output di un determinato livello dato un certo input, ad esempio:

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]

se potessi te ne darei due ^, in questo modo è semplicemente molto più conveniente quando hai un sacco di input.
Dan Erez,

È abbastanza chiaro dal tuo codice sopra, ma solo per ricontrollare la mia comprensione: dopo aver creato un modello da un modello esistente (supponendo che sia già addestrato), non è necessario chiamare set_weights sul nuovo modello. È corretto?
JZ,

qual è la differenza tra layer_output = get_3rd_layer_output([X, 0])[0]e layer_output = get_3rd_layer_output([X, 1])[0]I documenti menzionano la modalità treno e la modalità test
Jason

scusa, puoi spiegarmi cosa fa esattamente questo modello? Devi addestrarlo anche tu? Non riesco a immaginare alcun diagramma ad esso. Aggiungete lo strato di input di un altro modello, quindi aggiungete uno strato intermedio casuale di quell'altro modello come output e gli input di input? Perché questo invece di alimentare il modello originale e ottenere l'accesso diretto a qualsiasi livello intermedio in esso? Perché creare questo modello extra strano? E non influenzerà l'output? non proverà ad imparare o richiedere allenamento, o il livello porta i suoi pesi pre-allenati dal modello originale?
PedroD

19

Sulla base di tutte le buone risposte di questo thread, ho scritto una libreria per recuperare l'output di ogni livello. Sottrae tutta la complessità ed è stato progettato per essere il più intuitivo possibile:

https://github.com/philipperemy/keract

Gestisce quasi tutti i casi limite

Spero che sia d'aiuto!


8

Di seguito mi sembra molto semplice:

model.layers[idx].output

Sopra c'è un oggetto tensore, quindi puoi modificarlo usando operazioni che possono essere applicate a un oggetto tensore.

Ad esempio, per ottenere la forma model.layers[idx].output.get_shape()

idx è l'indice del livello e puoi trovarlo da model.summary()


1
Cosa c'è di sbagliato in questa risposta? Perché questo non è votato come la risposta migliore?
Black Jack 21

1
Restituisce un oggetto tensore, non un frame di dati. tf gli oggetti sono strani con cui lavorare.
HashRocketSyntax il

7

Ho scritto questa funzione per me (in Jupyter) ed è stata ispirata dalla risposta di indraforyou . Traccia automaticamente tutti gli output del layer. Le immagini devono avere una forma (x, y, 1) dove 1 sta per 1 canale. Basta chiamare plot_layer_outputs (...) per tracciare.

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

Cosa succede se il modello ha diversi input? Come si specificano gli input?
Antonio Sesto,

In questa riga: layer_outputs_list = [op ([test_image, 1.]). 1. deve essere 0? Sembra 1 sta per allenamento e 0 sta per test? Non è?
Kongsea,

Questo non funziona per me. Ho usato un'immagine a colori e mi sta dando un errore: InvalidArgumentError: input_2: 0 viene sia alimentato che recuperato.
Vaibhav K,

5

Da: 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

Il link è obsoleto.
Saeed


5

Volevo aggiungere questo come commento (ma non ho abbastanza rappresentante) alla risposta di @ indraforyou per correggere il problema menzionato nel commento di @mathtick. Per evitare l' InvalidArgumentError: input_X:Y is both fed and fetched.eccezione, è sufficiente sostituire la linea outputs = [layer.output for layer in model.layers]con outputs = [layer.output for layer in model.layers][1:], ad es

adattando l'esempio di lavoro minimo di 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 i miei tentativi di provare cose come outputs = [layer.output for layer in model.layers[1:]]non hanno funzionato.


1
questo non è esattamente corretto. Questo è solo se il livello di input è il primo definito.
Mpizos Dimitris,

Grazie, questo ha funzionato per me e voglio solo verificare capisco perché, in base al commento di Mpizos: il mio modello è composto da 3 livelli (parole incorporate - BiLSTM - CRF), quindi immagino di dover escludere il livello [0] poiché è solo matrimoni e non dovresti avere un'attivazione, giusto?
KMunro,

@MpizosDimitris sì, è corretto, ma nell'esempio fornito da @indraforyou (che stavo modificando), è stato così. @KMunro se sto capendo correttamente, allora la ragione per cui non ti interessa l'output del primo livello è perché è semplicemente l'output della parola embedding che è solo la parola che si incorpora in forma tensoriale (che è solo il input per la parte "rete" del kerasmodello). Il livello di incorporamenti delle parole è equivalente al livello di input nell'esempio fornito qui.
KamKam,

3

Supponendo di avere:

1- Keras pre-allenate model.

2- Input xcome immagine o insieme di immagini. La risoluzione dell'immagine dovrebbe essere compatibile con la dimensione del livello di input. Ad esempio 80 * 80 * 3 per l'immagine a 3 canali (RGB).

3- Il nome dell'output layerper ottenere l'attivazione. Ad esempio, livello "flatten_2". Questo dovrebbe essere incluso nella layer_namesvariabile, rappresenta il nome dei livelli del dato model.

4- batch_sizeè un argomento facoltativo.

Quindi puoi facilmente usare la get_activationfunzione per ottenere l'attivazione dell'output layerper un dato input xe pre-addestrato 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

2

Nel caso in cui tu abbia uno dei seguenti casi:

  • errore: InvalidArgumentError: input_X:Y is both fed and fetched
  • caso di più ingressi

È necessario apportare le seguenti modifiche:

  • aggiungi il filtro per i livelli di input in outputsvariabile
  • Minnor Change on functorsloop

Esempio minimo:

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]

Cosa si intende per [x1, x2, xn, 1]? Il mio x1 non è definito e vorrei capire cosa stai definendo lì.
HashRocketSyntax,

@HashRocketSyntax x1e x2sono gli input del modello. Come detto, in caso di necessità hai 2 input sul tuo modello.
Mpizos Dimitris,

1

Bene, altre risposte sono molto complete, ma esiste un modo molto semplice per "vedere", non per "ottenere" le forme.

Basta fare un model.summary(). Stampa tutti i livelli e le loro forme di output. I valori "Nessuno" indicheranno dimensioni variabili e la prima dimensione sarà la dimensione del batch.


Riguarda l'output del layer (dati input al layer base) e non il layer.
matematico
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.