Path: blob/master/site/es-419/guide/keras/rnn.ipynb
25118 views
Copyright 2020 The TensorFlow Authors.
Redes neuronales recurrentes (RNN) con Keras
Introducción
Las redes neuronales recurrentes (RNN) son una clase de redes neuronales potentes para modelar datos secuenciales, como series temporales o lenguaje natural.
En términos esquemáticos, una capa RNN utiliza un bucle for
para iterar sobre los pasos de tiempo de una secuencia, mientras mantiene un estado interno que codifica la información sobre los pasos de tiempo que ha visto hasta el momento.
La API Keras RNN está diseñada con un enfoque en:
Facilidad de uso: las capas incorporadas
keras.layers.RNN
,keras.layers.LSTM
,keras.layers.GRU
permiten construir rápidamente modelos recurrentes sin tener que tomar decisiones difíciles sobre la configuración.Facilidad de personalización: También puede definir su propia capa de celdas RNN (la parte interna del bucle
for
) con un comportamiento personalizado, y utilizarla con la capa genéricakeras.layers.RNN
(el propio buclefor
). Esto le permite crear rápidamente prototipos de diferentes ideas de investigación de una manera flexible con un código mínimo.
Preparación
Capas RNN integradas: un ejemplo simple
Hay tres capas RNN integradas en Keras:
keras.layers.SimpleRNN
, una RNN completamente conectada en la que la salida del paso de tiempo anterior debe alimentar el siguiente paso de tiempo.keras.layers.GRU
, propuesto por primera vez en Cho et al., 2014.keras.layers.LSTM
, propuesto por primera vez en Hochreiter & Schmidhuber, 1997.
A principios del 2015, Keras tuvo las primeras implementaciones reutilizables de código abierto en Python de LSTM y GRU.
Este es un ejemplo sencillo de un modelo de Sequential
que procesa secuencias de números enteros, incrusta cada número entero en un vector de 64 dimensiones y, a continuación, procesa la secuencia de vectores utilizando una capa LSTM
.
Las RNN integradas admiten varias funciones útiles:
Abandono recurrente, mediante los argumentos
dropout
yrecurrent_dropout
.Capacidad para procesar una secuencia de entrada en sentido inverso, mediante el argumento
go_backwards
.Desenrollar bucles (que puede suponer un gran aumento de velocidad al procesar secuencias cortas en el CPU), mediante el argumento
unroll
....y mucho más.
Para obtener más información, consulte la Documentación de la API RNN.
Salidas y estados
De forma predeterminada, la salida de una capa RNN contiene un único vector por muestra. Este vector es la salida de la célula RNN correspondiente al último paso de tiempo, que contiene información sobre toda la secuencia de entrada. La forma de esta salida es (batch_size, units)
donde units
corresponde al argumento units
pasado al constructor de la capa.
Una capa RNN también puede devolver la secuencia completa de salidas para cada muestra (un vector por paso de tiempo por muestra), si se establece return_sequences=True
. La forma de esta salida es (batch_size, timesteps, units)
.
Además, una capa RNN puede devolver su estados internos finales. Los estados devueltos pueden utilizarse para reanudar la ejecución de la RNN más tarde, o para inicializar otra RNN. Esta configuración se utiliza comúnmente en el modelo secuencia-a-secuencia codificador-decodificador, donde el estado final del codificador se utiliza como estado inicial del decodificador.
Para configurar una capa RNN para que devuelva su estado interno, establezca el parámetro return_state
en True
al crear la capa. Tenga en cuenta que LSTM
tiene 2 tensores de estado, pero GRU
solo tiene uno.
Para configurar el estado inicial de la capa, basta con llamar a la capa con el argumento adicional initial_state
. Tenga en cuenta que la forma del estado debe coincidir con el tamaño de la unidad de la capa, como en el siguiente ejemplo.
Capas RNN y celdas RNN
Además de las capas RNN integradas, la API RNN también proporciona APIs a nivel de celda. A diferencia de las capas RNN, que procesan lotes enteros de secuencias de entrada, la celda RNN solo procesa un único paso temporal.
La celda es el interior del bucle for
de una capa RNN. Al envolver una celda dentro de una capa keras.layers.RNN
se obtiene una capa capaz de procesar lotes de secuencias, por ejemplo RNN(LSTMCell(10))
.
Matemáticamente, RNN(LSTMCell(10))
produce el mismo resultado que LSTM(10)
. De hecho, la implementación de esta capa en TF v1.x era simplemente crear la celda RNN correspondiente y envolverla en una capa RNN. Sin embargo, el uso de las capas integradas GRU
y LSTM
permite el uso de CuDNN y se puede ver un mejor rendimiento.
Hay tres celdas RNN integradas, cada una de las cuales corresponde a la capa RNN correspondiente.
keras.layers.SimpleRNNCell
corresponde a la capaSimpleRNN
.keras.layers.GRUCell
corresponde a la capaGRU
.keras.layers.LSTMCell
corresponde a la capaLSTM
.
La abstracción de celdas, junto con la clase genérica keras.layers.RNN
, hacen que sea muy fácil implementar arquitecturas RNN personalizadas para su investigación.
Estado de los lotes cruzados
Al procesar secuencias muy largas (posiblemente infinitas), es posible que desee utilizar el patrón de estado de los lotes cruzados.
Normalmente, el estado interno de una capa RNN se restablece cada vez que ve un nuevo lote (es decir, se supone que cada muestra vista por la capa es independiente del pasado). La capa solo mantendrá un estado mientras procesa una muestra determinada.
Sin embargo, si tiene secuencias muy largas, es útil dividirlas en secuencias más cortas y alimentar estas secuencias más cortas secuencialmente en una capa RNN sin restablecer el estado de la capa. De este modo, la capa puede retener información sobre toda la secuencia, aunque solo vea una subsecuencia cada vez.
Puede hacerlo estableciendo stateful=True
en el constructor.
Si tiene una secuencia s = [t0, t1, ... t1546, t1547]
, la dividiría, por ejemplo, en:
Entonces lo procesaría a través de:
Cuando desee borrar el estado, puede utilizar layer.reset_states()
.
Nota: En esta configuración, se supone que la muestra
i
de un lote determinado es la continuación de la muestrai
del lote anterior. Esto significa que todos los lotes deben contener el mismo número de muestras (tamaño del lote). Por ejemplo, si un lote contiene[sequence_A_from_t0_to_t100, sequence_B_from_t0_to_t100]
, el siguiente lote debería contener[sequence_A_from_t101_to_t200, sequence_B_from_t101_to_t200]
.
Este es un ejemplo completo:
Los estados registrados de la capa RNN no se incluyen en layer.weights()
. Si desea reutilizar el estado de una capa RNN, puede recuperar el valor de los estados mediante layer.states
y utilizarlo como estado inicial para una nueva capa mediante la API funcional de Keras como new_layer(inputs, initial_state=layer.states)
, o la subclase de modelos.
Tenga en cuenta también que el modelo sequential no se puede utilizar en este caso, ya que solo admite capas con una entrada y una salida, y la entrada adicional del estado inicial hace que sea imposible utilizarlo aquí.
RNNs bidireccionales
Para secuencias que no sean series temporales (por ejemplo, texto), suele ocurrir que un modelo RNN puede funcionar mejor si no solo procesa la secuencia de principio a fin, sino también hacia atrás. Por ejemplo, para predecir la siguiente palabra de una frase, suele ser útil disponer del contexto que rodea a la palabra, no únicamente de las palabras que la preceden.
Keras proporciona una API sencilla para construir RNNs bidireccionales: el envoltorio keras.layers.Bidirectional
.
En este caso, Bidirectional
copiará la capa RNN introducida e invertirá el campo go_backwards
de la nueva capa copiada, de forma que procesará las entradas en orden inverso.
La salida de la RNN Bidirectional
será, de forma predeterminada, la concatenación de la salida de la capa hacia adelante y la salida de la capa hacia atrás. Si necesita un comportamiento de fusión diferente, por ejemplo, que una concatenación, cambie el parámetro merge_mode
en el constructor de la envoltura Bidirectional
. Para obtener más información sobre Bidirectional
, consulte la documentación de la API.
Optimización del rendimiento y kernels CuDNN
En TensorFlow 2.0, las capas LSTM y GRU incorporadas se actualizaron para aprovechar los núcleos CuDNN de forma predeterminada cuando está disponible una GPU. Con este cambio, las capas anteriores keras.layers.CuDNNLSTM/CuDNNGRU
quedaron obsoletas, y podrá construir su modelo sin preocuparse por el hardware en el que se ejecutará.
Dado que el kernel CuDNN se construye con ciertas suposiciones, esto significa que la capa no podrá utilizar el kernel CuDNN si cambia los valores predeterminados de las capas incorporadas LSTM o GRU. Por ejemplo:
Cambiar la función
activation
detanh
a algo distinto.Cambiar la función
recurrent_activation
desigmoide
a algo diferente.Usar
recurrent_dropout
> 0.Establecer
unroll
en True, lo que obligará a LSTM/GRU a descomponer el bucletf.while_loop
interno en un buclefor
desenrollado.Establecer
use_bias
en False.Utilizar el enmascaramiento cuando los datos de entrada no están estrictamente rellenados a la derecha (si la máscara corresponde a datos estrictamente rellenados a la derecha, CuDNN puede seguir utilizándose. Este es el caso más común).
Para obtener la lista detallada de restricciones, consulte la documentación de las capas LSTM y GRU.
Cómo utilizar los kernels CuDNN cuando estén disponibles
Construyamos un modelo LSTM sencillo para demostrar la diferencia de rendimiento.
Utilizaremos como secuencias de entrada la secuencia de filas de dígitos MNIST (tratando cada fila de pixeles como un paso de tiempo), y predeciremos la etiqueta del dígito.
Carguemos el conjunto de datos MNIST:
Creemos una instancia del modelo y vamos a entrenarla.
Elegimos sparse_categorical_crossentropy
como función de pérdida para el modelo. La salida del modelo tiene forma de [batch_size, 10]
. El objetivo para el modelo es un vector entero, cada uno de los enteros está en el rango de 0 a 9.
Ahora, comparemos con un modelo que no utiliza el kernel CuDNN:
Cuando se ejecuta en una máquina con una GPU NVIDIA y CuDNN instalada, el modelo construido con CuDNN es mucho más rápido de entrenar en comparación con el modelo que utiliza el kernel TensorFlow normal.
El mismo modelo CuDNN habilitado también se puede utilizar para ejecutar la inferencia en un entorno basado únicamente en el CPU. La anotación tf.device
de abajo solo está forzando la colocación del dispositivo. El modelo se ejecutará en la CPU de forma predeterminada si no hay una GPU disponible.
Ya no tendrá que preocuparse por el hardware que utiliza. ¿No es genial?
RNNs con entradas de lista/dict, o entradas anidadas
Las estructuras anidadas permiten a los programadores incluir más información en un solo paso de tiempo. Por ejemplo, un fotograma de video podría tener entrada de audio y video al mismo tiempo. La forma de los datos en este caso podría ser:
[batch, timestep, {"video": [height, width, channel], "audio": [frequency]}]
En otro ejemplo, los datos de escritura manual podrían tener coordenadas x y y para la posición actual del bolígrafo, así como información sobre la presión. Así que la representación de los datos podría ser la siguiente:
[batch, timestep, {"location": [x, y], "pressure": [force]}]
El siguiente código proporciona un ejemplo de cómo construir una celda RNN personalizada que acepte tales entradas estructuradas.
Cómo definir una celda personalizada que admita entradas/salidas anidadas
Consulte Creación de nuevas capas y modelos mediante subclases para obtener más información sobre cómo crear sus propias capas.
Cómo construir un modelo RNN con entrada/salida anidada
Vamos a construir un modelo Keras que utilice una capa keras.layers.RNN
y la celda personalizada que acabamos de definir.
Entrene el modelo con datos generados aleatoriamente
Dado que no existe un buen conjunto de datos para este modelo, utilizaremos datos aleatorios de Numpy para realizar la demostración.
Con la capa keras.layers.RNN
de Keras, solo se espera que defina la lógica matemática para un paso individual dentro de la secuencia, y la capa keras.layers.RNN
administrará la iteración de la secuencia por usted. Es una forma increíblemente potente de crear rápidamente prototipos de nuevos tipos de RNN (por ejemplo, una variante de LSTM).
Para obtener más información, visite los documentos de la API.