Cómo interpretar pesos en una capa LSTM en Keras

Actualmente estoy entrenando una neural network recurrente para el pronóstico del tiempo, utilizando una capa LSTM. La red en sí es bastante simple y se parece aproximadamente a esto:

model = Sequential() model.add(LSTM(hidden_neurons, input_shape=(time_steps, feature_count), return_sequences=False)) model.add(Dense(feature_count)) model.add(Activation("linear")) 

Los pesos de la capa LSTM tienen las siguientes formas:

 for weight in model.get_weights(): # weights from Dense layer omitted print(weight.shape) > (feature_count, hidden_neurons) > (hidden_neurons, hidden_neurons) > (hidden_neurons,) > (feature_count, hidden_neurons) > (hidden_neurons, hidden_neurons) > (hidden_neurons,) > (feature_count, hidden_neurons) > (hidden_neurons, hidden_neurons) > (hidden_neurons,) > (feature_count, hidden_neurons) > (hidden_neurons, hidden_neurons) > (hidden_neurons,) 

En resumen, parece que hay cuatro “elementos” en esta capa LSTM. Me pregunto ahora cómo interpretarlos:

Aclaraciones y sugerencias serían muy apreciadas.

Si está utilizando Keras 2.2.0

Cuando imprimes

 print(model.layers[0].trainable_weights) 

debería ver tres tensores: lstm_1/kernel, lstm_1/recurrent_kernel, lstm_1/bias:0 Una de las dimensiones de cada tensor debe ser un producto de

4 * numero_de_unidades

donde número_de_unidades es tu número de neuronas. Tratar:

 units = int(int(model.layers[0].trainable_weights[0].shape[1])/4) print("No units: ", units) 

Esto se debe a que cada tensor contiene pesos para cuatro unidades LSTM (en ese orden):

i (entrada), f (olvidar), c (estado de celda) y o (salida)

Por lo tanto, para extraer pesos, simplemente puede utilizar el operador de corte:

 W = model.layers[0].get_weights()[0] U = model.layers[0].get_weights()[1] b = model.layers[0].get_weights()[2] W_i = W[:, :units] W_f = W[:, units: units * 2] W_c = W[:, units * 2: units * 3] W_o = W[:, units * 3:] U_i = U[:, :units] U_f = U[:, units: units * 2] U_c = U[:, units * 2: units * 3] U_o = U[:, units * 3:] b_i = b[:units] b_f = b[units: units * 2] b_c = b[units * 2: units * 3] b_o = b[units * 3:] 

Fuente: código keras

Probablemente no podré responder a todas sus preguntas, pero lo que sí puedo hacer es proporcionar más información sobre la celda LSTM y los diferentes componentes de los que está hecha.

Esta publicación en github propone una forma de ver el nombre de los parámetros mientras se imprimen:

 model = Sequential() model.add(LSTM(4,input_dim=5,input_length=N,return_sequences=True)) for e in zip(model.layers[0].trainable_weights, model.layers[0].get_weights()): print('Param %s:\n%s' % (e[0],e[1])) 

La salida se ve como:

 Param lstm_3_W_i: [[ 0.00069305, ...]] Param lstm_3_U_i: [[ 1.10000002, ...]] Param lstm_3_b_i: [ 0., ...] Param lstm_3_W_c: [[-1.38370085, ...]] ... 

Ahora puedes encontrar aquí más información sobre los diferentes pesos. Tienen los nombres W, U, V yb con diferentes índices.

  • Las matrices W son aquellas que transforman las entradas en algunos otros valores internos. Tienen la forma [input_dim, output_dim] .
  • Las matrices U son aquellas que transforman el estado oculto anterior en otro valor interno. Tienen la forma [output_dim, output_dim] .
  • Los vectores b son el sesgo de cada bloque. Todos tienen la forma [output_dim]
  • V se usa solo en la puerta de salida, elige qué valores se emitirán desde el nuevo estado interno. Tiene una forma [output_dim, output_dim]

En resumen, tienes 4 bloques diferentes (o capas internas).

  • olvidar la puerta: decide, en función del estado oculto anterior (h_ {t-1}) y la entrada (x), qué valores se deben olvidar del estado interno anterior de la celda (C_ {t-1}):

    f_t = sigmoide (W_f * x + U_f * h_ {t-1} + b_f)

    f_t es un vector de valores entre 0 y 1 que codificará qué conservar (= 1) y qué olvidar (= 0) del estado de celda anterior.

  • Puerta de entrada: decide, en función del estado oculto anterior (h_ {t-1}) y la entrada (x), qué valores usar de la entrada (x):

    i_t = sigmoide (W_i * x + U_i * h_ {t-1} + b_i)

    i_t es un vector de valores entre 0 y 1 que codificará qué valores usar para actualizar el nuevo estado de celda.

  • Valor de candidato: Construimos nuevos valores de candidato para actualizar el estado interno de la celda, usando la entrada (x) y el estado oculto anterior (h_ {t-1}):

    Ct_t = tanh (W_c * x + U_c * h_ {t-1} + b_c)

    Ct_t es un vector que contiene valores potenciales para actualizar el estado de la celda (C_ {t-1}).

Usamos esos tres valores para construir un nuevo estado de celda interno (C_t):

C_t = f_t * C_ {t-1} + i_t * Ct_t

Como puede ver, el nuevo estado de celda interna se compone de dos cosas: la parte que no olvidamos del último estado y lo que queríamos aprender de la entrada.

  • Puerta de salida: no queremos generar el estado de la celda, ya que podría verse como una abstracción de lo que queremos generar (h_t). Entonces construimos h_t, el resultado para este paso basado en toda la información que tenemos:

    h_t = W_o * x + U_o * h_ {t-1} + V_o * C_t + b_o

Espero que esto aclare cómo funciona una célula LSTM. Los invito a leer los tutoriales sobre LSTM, ya que usan esquemas agradables, ejemplos paso a paso, etc. Es una capa relativamente compleja.

Con respecto a sus preguntas, ahora tengo una idea de cómo hacer un seguimiento de lo que se ha utilizado desde la entrada para modificar el estado. Eventualmente podría mirar las diferentes matrices W, ya que son las que procesan la entrada. El W_c le dará información sobre lo que se usa potencialmente para actualizar el estado de la celda. W_o podría brindarle información sobre lo que se usa para producir la salida … Pero todo esto será relativo a los otros pesos, ya que los estados anteriores también tienen influencia.

Sin embargo, si ve algunos pesos fuertes en W_c, es posible que no signifique nada, porque la puerta de entrada (i_t) tal vez esté completamente cerrada y aniquilándose la actualización del estado de la celda … Es complejo, el campo de las matemáticas que remonta lo que está sucediendo en una neural network es realmente complejo.

Las redes neuronales son realmente cajas negras para el caso más general. Puede encontrar en la literatura algunos casos en los que rastrean la información desde la salida hasta la entrada, pero esto es en casos muy especiales de lo que he leído.

Espero que esto ayude 🙂