python - instalar - Entendiendo Keras LSTMs
instalar keras in python (3)
Como complemento de la respuesta aceptada, esta respuesta muestra comportamientos keras y cómo lograr cada imagen.
Comportamiento general de Keras
El procesamiento interno estándar de keras siempre es de muchos a muchos como en la siguiente imagen (donde utilicé
features=2
, presión y temperatura, solo como ejemplo):
En esta imagen, aumenté el número de pasos a 5, para evitar confusiones con las otras dimensiones.
Para este ejemplo:
- Tenemos N tanques de aceite
- Pasamos 5 horas tomando medidas por hora (pasos de tiempo)
-
Medimos dos características:
- Presión P
- Temperatura T
Nuestra matriz de entrada debería tener entonces la forma de
(N,5,2)
:
[ Step1 Step2 Step3 Step4 Step5
Tank A: [[Pa1,Ta1], [Pa2,Ta2], [Pa3,Ta3], [Pa4,Ta4], [Pa5,Ta5]],
Tank B: [[Pb1,Tb1], [Pb2,Tb2], [Pb3,Tb3], [Pb4,Tb4], [Pb5,Tb5]],
....
Tank N: [[Pn1,Tn1], [Pn2,Tn2], [Pn3,Tn3], [Pn4,Tn4], [Pn5,Tn5]],
]
Entradas para ventanas correderas
A menudo, se supone que las capas LSTM procesan las secuencias completas. Dividir ventanas puede no ser la mejor idea. La capa tiene estados internos sobre cómo evoluciona una secuencia a medida que avanza. Windows elimina la posibilidad de aprender secuencias largas, limitando todas las secuencias al tamaño de la ventana.
En ventanas, cada ventana es parte de una secuencia original larga, pero Keras las verá como una secuencia independiente:
[ Step1 Step2 Step3 Step4 Step5
Window A: [[P1,T1], [P2,T2], [P3,T3], [P4,T4], [P5,T5]],
Window B: [[P2,T2], [P3,T3], [P4,T4], [P5,T5], [P6,T6]],
Window C: [[P3,T3], [P4,T4], [P5,T5], [P6,T6], [P7,T7]],
....
]
Tenga en cuenta que en este caso, inicialmente tiene solo una secuencia, pero la está dividiendo en muchas secuencias para crear ventanas.
El concepto de "qué es una secuencia" es abstracto. Las partes importantes son:
- puedes tener lotes con muchas secuencias individuales
- lo que hace que las secuencias sean secuencias es que evolucionan en pasos (generalmente pasos de tiempo)
Logrando cada caso con "capas individuales"
Alcanzar el estándar de muchos a muchos:
Puede lograr muchos a muchos con una capa LSTM simple, utilizando
return_sequences=True
:
outputs = LSTM(units, return_sequences=True)(inputs)
#output_shape -> (batch_size, steps, units)
Logrando muchos a uno:
Usando exactamente la misma capa, los keras harán exactamente el mismo preprocesamiento interno, pero cuando use
return_sequences=False
(o simplemente ignore este argumento), los keras descartarán automáticamente los pasos anteriores al último:
outputs = LSTM(units)(inputs)
#output_shape -> (batch_size, units) --> steps were discarded, only the last was returned
Logrando uno a muchos
Ahora, esto no es compatible solo con las capas Keras LSTM. Tendrás que crear tu propia estrategia para multiplicar los pasos. Hay dos buenos enfoques:
- Cree una entrada constante de varios pasos repitiendo un tensor
-
Use un
stateful=True
para tomar la salida de un paso de forma recurrente y servirla como entrada del siguiente paso (necesitaoutput_features == input_features
)
Uno a muchos con vector de repetición
Para ajustarnos al comportamiento estándar de Keras, necesitamos entradas en pasos, por lo tanto, simplemente repetimos las entradas para la longitud que queremos:
outputs = RepeatVector(steps)(inputs) #where inputs is (batch,features)
outputs = LSTM(units,return_sequences=True)(outputs)
#output_shape -> (batch_size, steps, units)
Comprensión stateful = True
Ahora viene uno de los posibles usos de
stateful=True
(además de evitar cargar datos que no pueden caber en la memoria de su computadora a la vez)
Stateful nos permite ingresar "partes" de las secuencias en etapas. La diferencia es:
-
En
stateful=False
, el segundo lote contiene secuencias completamente nuevas, independientes del primer lote -
En
stateful=True
, el segundo lote continúa el primer lote, extendiendo las mismas secuencias.
Es como dividir las secuencias en ventanas también, con estas dos diferencias principales:
- ¡Estas ventanas no se superponen!
-
stateful=True
verá estas ventanas conectadas como una sola secuencia larga
En
stateful=True
, cada nuevo lote se interpretará como una continuación del lote anterior (hasta que llame a
model.reset_states()
).
- La secuencia 1 en el lote 2 continuará la secuencia 1 en el lote 1.
- La secuencia 2 en el lote 2 continuará la secuencia 2 en el lote 1.
- La secuencia n en el lote 2 continuará la secuencia n en el lote 1.
Ejemplo de entradas, el lote 1 contiene los pasos 1 y 2, el lote 2 contiene los pasos 3 a 5:
BATCH 1 BATCH 2
[ Step1 Step2 | [ Step3 Step4 Step5
Tank A: [[Pa1,Ta1], [Pa2,Ta2], | [Pa3,Ta3], [Pa4,Ta4], [Pa5,Ta5]],
Tank B: [[Pb1,Tb1], [Pb2,Tb2], | [Pb3,Tb3], [Pb4,Tb4], [Pb5,Tb5]],
.... |
Tank N: [[Pn1,Tn1], [Pn2,Tn2], | [Pn3,Tn3], [Pn4,Tn4], [Pn5,Tn5]],
] ]
¡Observe la alineación de los tanques en el lote 1 y el lote 2!
Es por eso que necesitamos
shuffle=False
(a menos que estemos usando solo una secuencia, por supuesto).
Puede tener cualquier cantidad de lotes, indefinidamente.
(Para tener longitudes variables en cada lote, use
input_shape=(None,features)
.
Uno a muchos con stateful = True
Para nuestro caso aquí, vamos a usar solo 1 paso por lote, porque queremos obtener un paso de salida y hacer que sea una entrada.
Tenga en cuenta que el comportamiento en la imagen no es "causado por"
stateful=True
.
Forzaremos ese comportamiento en un bucle manual a continuación.
En este ejemplo,
stateful=True
es lo que "nos permite" detener la secuencia, manipular lo que queremos y continuar desde donde nos detuvimos.
Honestamente, el enfoque repetido es probablemente una mejor opción para este caso.
Pero ya que estamos investigando
stateful=True
, este es un buen ejemplo.
La mejor manera de usar esto es el próximo caso "muchos a muchos".
Capa:
outputs = LSTM(units=features,
stateful=True,
return_sequences=True, #just to keep a nice output shape even with length 1
input_shape=(None,features))(inputs)
#units = features because we want to use the outputs as inputs
#None because we want variable length
#output_shape -> (batch_size, steps, units)
Ahora, vamos a necesitar un bucle manual para las predicciones:
input_data = someDataWithShape((batch, 1, features))
#important, we''re starting new sequences, not continuing old ones:
model.reset_states()
output_sequence = []
last_step = input_data
for i in steps_to_predict:
new_step = model.predict(last_step)
output_sequence.append(new_step)
last_step = new_step
#end of the sequences
model.reset_states()
Muchos a muchos con stateful = True
Ahora, aquí, tenemos una aplicación muy agradable: dada una secuencia de entrada, intente predecir sus futuros pasos desconocidos.
Estamos utilizando el mismo método que en el "uno a muchos" anterior, con la diferencia de que:
- utilizaremos la secuencia misma para ser los datos objetivo, un paso adelante
- conocemos parte de la secuencia (por lo que descartamos esta parte de los resultados).
Capa (igual que la anterior):
outputs = LSTM(units=features,
stateful=True,
return_sequences=True,
input_shape=(None,features))(inputs)
#units = features because we want to use the outputs as inputs
#None because we want variable length
#output_shape -> (batch_size, steps, units)
Formación:
Vamos a entrenar nuestro modelo para predecir el siguiente paso de las secuencias:
totalSequences = someSequencesShaped((batch, steps, features))
#batch size is usually 1 in these cases (often you have only one Tank in the example)
X = totalSequences[:,:-1] #the entire known sequence, except the last step
Y = totalSequences[:,1:] #one step ahead of X
#loop for resetting states at the start/end of the sequences:
for epoch in range(epochs):
model.reset_states()
model.train_on_batch(X,Y)
Prediciendo:
La primera etapa de nuestra predicción implica "ajustar los estados". Es por eso que vamos a predecir la secuencia completa nuevamente, incluso si ya conocemos esta parte de ella:
model.reset_states() #starting a new sequence
predicted = model.predict(totalSequences)
firstNewStep = predicted[:,-1:] #the last step of the predictions is the first future step
Ahora vamos al bucle como en el caso uno a muchos. ¡Pero no restablezca los estados aquí! . Queremos que el modelo sepa en qué paso de la secuencia se encuentra (y sabe que está en el primer paso nuevo debido a la predicción que acabamos de hacer)
output_sequence = [firstNewStep]
last_step = firstNewStep
for i in steps_to_predict:
new_step = model.predict(last_step)
output_sequence.append(new_step)
last_step = new_step
#end of the sequences
model.reset_states()
Este enfoque se utilizó en estas respuestas y archivo:
- Predecir un paso de tiempo de avance múltiple de una serie de tiempo usando LSTM
- ¿Cómo usar el modelo de Keras para pronosticar fechas o eventos futuros?
- https://github.com/danmoller/TestRepo/blob/master/TestBookLSTM.ipynb
Logrando configuraciones complejas
En todos los ejemplos anteriores, mostré el comportamiento de "una capa".
Por supuesto, puede apilar muchas capas una encima de la otra, no necesariamente todas siguiendo el mismo patrón, y crear sus propios modelos.
Un ejemplo interesante que ha estado apareciendo es el "autoencoder" que tiene un codificador "muchos a uno" seguido de un decodificador "uno a muchos":
Codificador:
inputs = Input((steps,features))
#a few many to many layers:
outputs = LSTM(hidden1,return_sequences=True)(inputs)
outputs = LSTM(hidden2,return_sequences=True)(outputs)
#many to one layer:
outputs = LSTM(hidden3)(outputs)
encoder = Model(inputs,outputs)
Descifrador:
Usando el método "repetir";
inputs = Input((hidden3,))
#repeat to make one to many:
outputs = RepeatVector(steps)(inputs)
#a few many to many layers:
outputs = LSTM(hidden4,return_sequences=True)(outputs)
#last layer
outputs = LSTM(features,return_sequences=True)(outputs)
decoder = Model(inputs,outputs)
Autoencoder:
inputs = Input((steps,features))
outputs = encoder(inputs)
outputs = decoder(outputs)
autoencoder = Model(inputs,outputs)
Entrena con
fit(X,X)
Explicaciones adicionales
Si desea detalles sobre cómo se calculan los pasos en los LSTM, o detalles sobre los casos con
stateful=True
anteriores, puede leer más en esta respuesta:
Dudas con respecto a `Entender los LSTM de Keras`
Estoy tratando de conciliar mi comprensión de los LSTM y señalé aquí en esta publicación de Christopher Olah implementado en Keras. Estoy siguiendo el blog escrito por Jason Brownlee para el tutorial de Keras. Lo que más me confunde es,
-
La remodelación de la serie de datos en
[samples, time steps, features]
y, - Los LSTM con estado
Vamos a concentrarnos en las dos preguntas anteriores con referencia al código pegado a continuación:
# reshape into X=t and Y=t+1
look_back = 3
trainX, trainY = create_dataset(train, look_back)
testX, testY = create_dataset(test, look_back)
# reshape input to be [samples, time steps, features]
trainX = numpy.reshape(trainX, (trainX.shape[0], look_back, 1))
testX = numpy.reshape(testX, (testX.shape[0], look_back, 1))
########################
# The IMPORTANT BIT
##########################
# create and fit the LSTM network
batch_size = 1
model = Sequential()
model.add(LSTM(4, batch_input_shape=(batch_size, look_back, 1), stateful=True))
model.add(Dense(1))
model.compile(loss=''mean_squared_error'', optimizer=''adam'')
for i in range(100):
model.fit(trainX, trainY, nb_epoch=1, batch_size=batch_size, verbose=2, shuffle=False)
model.reset_states()
Nota: create_dataset toma una secuencia de longitud N y devuelve una matriz
N-look_back
de la cual cada elemento es una secuencia de longitud
look_back
.
¿Qué son los Pasos de tiempo y las características?
Como se puede ver, TrainX es una matriz tridimensional con Time_steps y Feature siendo las dos últimas dimensiones respectivamente (3 y 1 en este código en particular).
Con respecto a la imagen a continuación, ¿significa esto que estamos considerando el caso de
many to one
, donde el número de cajas rosadas es 3?
¿O significa literalmente que la longitud de la cadena es 3 (es decir, solo se consideran 3 casillas verdes)?
¿El argumento de las características se vuelve relevante cuando consideramos series multivariadas? por ejemplo, modelar dos acciones financieras simultáneamente?
LSTM con estado
¿Los LSTM con estado significan que guardamos los valores de memoria de la celda entre ejecuciones de lotes?
Si este es el caso,
batch_size
es uno y la memoria se restablece entre las ejecuciones de entrenamiento, entonces, ¿cuál era el punto de decir que tenía estado?
Supongo que esto está relacionado con el hecho de que los datos de entrenamiento no se mezclan, pero no estoy seguro de cómo hacerlo.
¿Alguna idea? Referencia de la imagen: http://karpathy.github.io/2015/05/21/rnn-effectiveness/
Editar 1:
Un poco confundido sobre el comentario de @ van sobre las cajas rojas y verdes que son iguales.
Entonces, solo para confirmar, ¿las siguientes llamadas a la API corresponden a los diagramas desenrollados?
Especialmente observando el segundo diagrama (se eligió arbitrariamente el
batch_size
):
Edición 2:
Para las personas que han realizado el curso de aprendizaje profundo de Udacity y aún están confundidos sobre el argumento time_step, miren la siguiente discusión: https://discussions.udacity.com/t/rnn-lstm-use-implementation/163169
Actualizar:
Resulta que
model.add(TimeDistributed(Dense(vocab_len)))
era lo que estaba buscando.
Aquí hay un ejemplo:
https://github.com/sachinruk/ShakespeareBot
Actualización2:
He resumido la mayor parte de mi comprensión de los LSTM aquí: https://www.youtube.com/watch?v=ywinX5wgdEU
Cuando tiene return_sequences en su última capa de RNN, no puede usar una simple capa densa en lugar de usar TimeDistributed.
Aquí hay un ejemplo de código que podría ayudar a otros.
palabras = keras.layers.Input (batch_shape = (None, self.maxSequenceLength), name = "input")
# Build a matrix of size vocabularySize x EmbeddingDimension
# where each row corresponds to a "word embedding" vector.
# This layer will convert replace each word-id with a word-vector of size Embedding Dimension.
embeddings = keras.layers.embeddings.Embedding(self.vocabularySize, self.EmbeddingDimension,
name = "embeddings")(words)
# Pass the word-vectors to the LSTM layer.
# We are setting the hidden-state size to 512.
# The output will be batchSize x maxSequenceLength x hiddenStateSize
hiddenStates = keras.layers.GRU(512, return_sequences = True,
input_shape=(self.maxSequenceLength,
self.EmbeddingDimension),
name = "rnn")(embeddings)
hiddenStates2 = keras.layers.GRU(128, return_sequences = True,
input_shape=(self.maxSequenceLength, self.EmbeddingDimension),
name = "rnn2")(hiddenStates)
denseOutput = TimeDistributed(keras.layers.Dense(self.vocabularySize),
name = "linear")(hiddenStates2)
predictions = TimeDistributed(keras.layers.Activation("softmax"),
name = "softmax")(denseOutput)
# Build the computational graph by specifying the input, and output of the network.
model = keras.models.Model(input = words, output = predictions)
# model.compile(loss=''kullback_leibler_divergence'', /
model.compile(loss=''sparse_categorical_crossentropy'', /
optimizer = keras.optimizers.Adam(lr=0.009, /
beta_1=0.9,/
beta_2=0.999, /
epsilon=None, /
decay=0.01, /
amsgrad=False))
En primer lugar, elige excelentes tutoriales ( 1 , http://karpathy.github.io/2015/05/21/rnn-effectiveness/ ) para comenzar.
Qué significa Time-step
:
Time-steps==3
en X.shape (Describiendo la forma de los datos) significa que hay tres cuadros rosados.
Dado que en Keras cada paso requiere una entrada, por lo tanto, el número de cuadros verdes generalmente debería ser igual al número de cuadros rojos.
A menos que hackees la estructura.
muchos a muchos versus muchos a uno
: en keras, hay un parámetro
return_sequences
al inicializar
LSTM
o
GRU
o
SimpleRNN
.
Cuando
return_sequences
es
False
(por defecto), entonces es
muchos a uno
como se muestra en la imagen.
Su forma de retorno es
(batch_size, hidden_unit_length)
, que representa el último estado.
Cuando
return_sequences
es
True
, entonces es de
muchos a muchos
.
Su forma de retorno es
(batch_size, time_step, hidden_unit_length)
¿El argumento de las características se vuelve relevante
?
Si desea predecir, por ejemplo, 8 tipos de información de mercado, puede generar sus datos con la
feature==8
.
Con estado
: puede buscar
el código fuente
.
Al inicializar el estado, si
stateful==True
, entonces el estado del último entrenamiento se usará como el estado inicial, de lo contrario generará un nuevo estado.
No he
stateful
todavía.
Sin embargo, no estoy de acuerdo con que el
batch_size
del
batch_size
solo puede ser 1 cuando
stateful==True
.
Actualmente, genera sus datos con los datos recopilados.
La imagen de su información de stock viene como transmisión, en lugar de esperar un día para recopilar toda la secuencia, le gustaría generar datos de entrada en
línea
mientras entrena / predice con la red.
Si tiene 400 acciones que comparten una misma red, puede configurar
batch_size==400
.