Tutorial de RNN (red neuronal recurrente): ejemplo de TensorFlow
⚡ Resumen inteligente
Las redes neuronales recurrentes añaden un estado de memoria a las neuronas convencionales, realimentando cada salida como la siguiente entrada para que el modelo pueda aprender del orden. Este tutorial muestra cómo construir una en TensorFlow y pronosticar una serie temporal.
¿Por qué necesitamos una red neuronal recurrente (RNN)?
La red neuronal recurrente (RNN) le permite modelar unidades de memoria para conservar datos y modelar dependencias a corto plazo. También se utiliza en pronósticos de series de tiempo para la identificación de correlaciones y patrones de datos. También ayuda a producir resultados predictivos para datos secuenciales al ofrecer un comportamiento similar al del cerebro humano.
La estructura de un Red neuronal artificial Es relativamente sencillo y se basa principalmente en la multiplicación de matrices. En el primer paso, las entradas se multiplican por pesos y sesgos inicialmente aleatorios, se transforman mediante una función de activación y los valores de salida se utilizan para realizar una predicción. Este paso permite hacerse una idea de cuán alejada está la red de la realidad.
La métrica aplicada es la pérdida. Cuanto mayor sea la función de pérdida, más tonto será el modelo. Para mejorar el conocimiento de la red, se requiere cierta optimización mediante el ajuste de los pesos de la red. El descenso de gradiente estocástico es el método empleado para cambiar los valores de los pesos en la dirección correcta. Una vez realizado el ajuste, la red puede utilizar otro lote de datos para probar su nuevo conocimiento.
Afortunadamente, el error es menor que antes, aunque no lo suficientemente pequeño. El paso de optimización se realiza de forma iterativa hasta que el error se minimiza, es decir, hasta que no se puede extraer más información.tracted
El problema de este tipo de modelo es que no tiene memoria. Significa que la entrada y la salida son independientes. En otras palabras, al modelo no le importa lo que vino antes. Plantea algunas dudas cuando es necesario predecir series de tiempo o oraciones porque la red necesita tener información sobre datos históricos o palabras pasadas.
Para superar este problema, se ha desarrollado un nuevo tipo de arquitectura: Red neuronal recurrente (RNN en adelante).
¿Qué es una red neuronal recurrente (RNN)?
Una red neuronal recurrente (RNN) es un tipo de red neuronal artificial en la que la conexión entre diferentes nodos forma un grafo dirigido que proporciona un comportamiento dinámico temporal. Ayuda a modelar datos secuenciales derivados de redes de propagación directa. Funciona de forma similar al cerebro humano para ofrecer resultados predictivos.
Una red neuronal recurrente se parece bastante a una red neuronal tradicional, excepto que se agrega un estado de memoria a las neuronas. El cálculo para incluir una memoria es sencillo.
Imagina un modelo simple con una sola neurona alimentada por un lote de datos. En una red neuronal tradicional, el modelo produce la salida multiplicando la entrada por el peso y la función de activación. Con una RNN, esta salida se envía de vuelta a sí misma varias veces. La llamamos hora de caminar la cantidad de veces que la salida se convierte en la entrada de la siguiente multiplicación de matrices.
Por ejemplo, en la imagen siguiente, puede ver que la red está compuesta por una neurona. La red calcula la multiplicación de matrices entre la entrada y el peso y agrega no linealidad con la función de activación. Se convierte en la salida en t-1. Esta salida es la entrada de la segunda multiplicación de matrices.
Red neuronal recurrente (RNN)
A continuación, codificamos un RNN simple en TensorFlow para comprender el paso y también la forma de la salida.
La red está compuesta por:
- Cuatro entradas
- Seis neuronas
- pasos de 2 veces
La red procederá como se muestra en la siguiente imagen.
La red se denomina "recurrente" porque realiza la misma operación en cada casilla de activación. La red calcula los pesos de las entradas y la salida anterior antes de utilizar una función de activación.
Nota de versión: El código de este tutorial está dirigido a la API de gráficos de TensorFlow 1.x. En TensorFlow 2.x, el mismo script sigue ejecutándose después import tensorflow.compat.v1 as tf más tf.disable_v2_behavior(), porque los marcadores de posición, las sesiones y el módulo rnn_cell ahora viven bajo tf.compat.v1. El equivalente idiomático 2.x es tf.keras.layers.SimpleRNN.
import numpy as np import tensorflow as tf n_inputs = 4 n_neurons = 6 n_timesteps = 2 The data is a sequence of a number from 0 to 9 and divided into three batches of data. ## Data X_batch = np.array([ [[0, 1, 2, 5], [9, 8, 7, 4]], # Batch 1 [[3, 4, 5, 2], [0, 0, 0, 0]], # Batch 2 [[6, 7, 8, 5], [6, 5, 4, 2]], # Batch 3 ])
Podemos construir la red con un marcador de posición para los datos, la etapa recurrente y la salida.
Definir el marcador de posición para los datos.
X = tf.placeholder(tf.float32, [None, n_timesteps, n_inputs])
Aquí:
- Ninguno: Desconocido y tomará el tamaño del lote.
- n_timesteps: número de veces que la red enviará la salida a la neurona
- n_inputs: número de entradas por lote
Definir la red recurrente
Como se menciona en la imagen de arriba, la red está compuesta por 6 neuronas. La red calculará dos productos escalares:
- Ingrese datos con el primer conjunto de pesos (es decir, 6: igual al número de neuronas)
- Salida anterior con un segundo conjunto de pesos (es decir, 6: correspondiente al número de salida)
Tenga en cuenta que, durante el primer avance, los valores de la salida anterior son iguales a ceros porque no tenemos ningún valor disponible.
El objeto para construir un RNN es tf.contrib.rnn.BasicRNNCell con el argumento num_units para definir el número de entradas
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons)
Ahora que la red está definida, puede calcular las salidas y los estados.
outputs, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)
Este objeto utiliza un bucle interno para multiplicar las matrices el número apropiado de veces.
Cabe destacar que la neurona recurrente es una función de todas las entradas de los pasos de tiempo anteriores. Así es como la red construye su propia memoria. La información del tiempo anterior puede propagarse al tiempo futuro. Esta es la magia de las redes neuronales recurrentes.
## Define the shape of the tensor X = tf.placeholder(tf.float32, [None, n_timesteps, n_inputs]) ## Define the network basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons) outputs, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32) init = tf.global_variables_initializer() init = tf.global_variables_initializer() with tf.Session() as sess: init.run() outputs_val = outputs.eval(feed_dict={X: X_batch}) print(states.eval(feed_dict={X: X_batch})) [[ 0.38941205 -0.9980438 0.99750966 0.7892596 0.9978241 0.9999997 ] [ 0.61096436 0.7255889 0.82977575 -0.88226104 0.29261455 -0.15597084] [ 0.62091285 -0.87023467 0.99729395 -0.58261937 0.9811445 0.99969864]]
Para fines explicativos, se imprimen los valores del estado anterior. La salida mostrada arriba corresponde al último estado. Ahora, al imprimir toda la salida, se puede observar que los estados son la salida anterior de cada lote. Es decir, la salida anterior contiene la información de toda la secuencia.
print(outputs_val) print(outputs_val.shape) [[[-0.75934666 -0.99537754 0.9735819 -0.9722234 -0.14234993 -0.9984044 ] [ 0.99975264 -0.9983206 0.9999993 -1. -0.9997506 -1. ]] [[ 0.97486496 -0.98773265 0.9969686 -0.99950117 -0.7092863 -0.99998885] [ 0.9326837 0.2673438 0.2808514 -0.7535883 -0.43337247 0.5700631 ]] [[ 0.99628735 -0.9998728 0.99999213 -0.99999976 -0.9884324 -1. ] [ 0.99962527 -0.9467421 0.9997403 -0.99999714 -0.99929446 -0.9999795 ]]] (3, 2, 6)
La vista anotada que se muestra a continuación desglosa el mismo tensor impreso lote por lote.
La salida tiene la forma de (3, 2, 6):
- 3: Número de lotes
- 2: Número del paso de tiempo
- 6: Número de neuronas
La optimización de una red neuronal recurrente es idéntica a la de una red neuronal tradicional. Verá con más detalle cómo optimizar el código en la siguiente parte de este tutorial de Red neuronal recurrente.
Aplicaciones de RNN
RNN tiene múltiples usos, especialmente cuando se trata de predecir el futuro. En la industria financiera, RNN puede resultar útil para predecir los precios de las acciones o el signo de la dirección del mercado de valores (es decir, positivo o negativo).
RNN es útil para un coche autónomo ya que puede evitar un accidente automovilístico anticipando la trayectoria del vehículo.
RNN se utiliza ampliamente en análisis de texto, subtítulos de imágenes, análisis de sentimientos y traducción automática. Por ejemplo, se puede utilizar una reseña de una película para comprender el sentimiento que percibió el espectador después de ver la película. Automatizar esta tarea es muy útil cuando la productora cinematográfica no tiene tiempo suficiente para revisar, etiquetar, consolidar y analizar las reseñas. La máquina puede hacer el trabajo con un mayor nivel de precisión.
Limitaciones de RNN
En teoría, se supone que RNN transporta la información varias veces. Sin embargo, es bastante difícil propagar toda esta información cuando el paso de tiempo es demasiado largo. Cuando una red tiene demasiadas capas profundas, se vuelve imposible de entrenar. Este problema se llama: problema de gradiente de fuga. Si recuerdas, la red neuronal actualiza el peso mediante el algoritmo de descenso de gradiente. Los gradientes se hacen más pequeños a medida que la red avanza hacia capas inferiores.
En conclusión, los gradientes permanecen constantes, lo que significa que no hay margen de mejora. El modelo aprende de los cambios en el gradiente, los cuales afectan la salida de la red. Sin embargo, si la diferencia en el gradiente es demasiado pequeña (es decir, los pesos cambian ligeramente), la red no puede aprender nada y, por lo tanto, la salida tampoco. En consecuencia, una red que se enfrenta al problema del gradiente evanescente no puede converger hacia una buena solución.
Mejora LSTM
Para superar el problema potencial del desvanecimiento del gradiente que enfrentan las redes neuronales recurrentes (RNN), tres investigadores, Hochreiter, Schmidhuber y Bengio, mejoraron las RNN con una arquitectura llamada Memoria a Largo y Corto Plazo (LSTM). En resumen, LSTM proporciona a la red información relevante del pasado para momentos más recientes. La máquina utiliza una arquitectura mejorada para seleccionar y transportar información hacia tiempos posteriores.
La arquitectura LSTM está disponible en TensorFlow, tf.contrib.rnn.LSTMCell. LSTM está fuera del alcance de este tutorial. Puede consultar la documentación oficial. documentación para mayor información
En TensorFlow 2.x se llega a esa celda como tf.compat.v1.nn.rnn_cell.LSTMCell, mientras que el código nuevo normalmente utiliza tf.keras.layers.LSTM o el encendedor tf.keras.layers.GRU, que alcanza una precisión comparable con menos parámetros.
RNN en series de tiempo
En este tutorial de RNN de TensorFlow, utilizarás una RNN con datos de series temporales. Las series temporales dependen de datos anteriores, lo que significa que los valores pasados contienen información relevante de la que la red puede aprender. La idea detrás de la predicción de series temporales es estimar el valor futuro de una serie, por ejemplo, el precio de las acciones, la temperatura, el PIB, etc.
La preparación de datos para Keras RNN y series temporales puede ser un poco complicada. En primer lugar, el objetivo es predecir el siguiente valor de la serie, es decir, utilizará la información pasada para estimar el valor en t + 1. La etiqueta es igual a la secuencia de entrada y se adelantó un período. En segundo lugar, el número de entradas se establece en 1, es decir, una observación por vez. Por último, el paso de tiempo es igual a la secuencia del valor numérico. Por ejemplo, si establece el paso de tiempo en 10, la secuencia de entrada regresará diez veces consecutivas.
Mire el gráfico a continuación, hemos representado los datos de la serie temporal a la izquierda y una secuencia de entrada ficticia a la derecha. Crea una función para devolver un conjunto de datos con valor aleatorio para cada día desde enero de 2001 hasta diciembre de 2016.
# To plot pretty figures %matplotlib inline import matplotlib import matplotlib.pyplot as plt import pandas as pd def create_ts(start = '2001', n = 201, freq = 'M'): rng = pd.date_range(start=start, periods=n, freq=freq) ts = pd.Series(np.random.uniform(-18, 18, size=len(rng)), rng).cumsum() return ts ts= create_ts(start = '2001', n = 192, freq = 'M') ts.tail(5)
Resultado
2016-08-31 -93.459631 2016-09-30 -95.264791 2016-10-31 -95.551935 2016-11-30 -105.879611 2016-12-31 -123.729319 Freq: M, dtype: float64
ts = create_ts(start = '2001', n = 222) # Left plt.figure(figsize=(11,4)) plt.subplot(121) plt.plot(ts.index, ts) plt.plot(ts.index[90:100], ts[90:100], "b-", linewidth=3, label="A training instance") plt.title("A time series (generated)", fontsize=14) # Right plt.subplot(122) plt.title("A training instance", fontsize=14) plt.plot(ts.index[90:100], ts[90:100], "b-", markersize=8, label="instance") plt.plot(ts.index[91:101], ts[91:101], "bo", markersize=10, label="target", markerfacecolor='red') plt.legend(loc="upper left") plt.xlabel("Time") plt.show()
La parte derecha del gráfico muestra todas las series. Comienza en 2001 y termina en 2019. No tiene sentido introducir todos los datos en la red; en su lugar, es necesario crear un lote de datos con una longitud igual al intervalo de tiempo. Este lote será la variable X. La variable Y es igual que X, pero desplazada un período (es decir, se desea pronosticar t+1).
Ambos vectores tienen la misma longitud. Puedes verlo en la parte derecha del gráfico anterior. La línea representa los diez valores de la entrada X, mientras que los puntos rojos son los diez valores de la etiqueta, Y. Tenga en cuenta que la etiqueta comienza un período antes de X y termina un período después.
Cree un RNN para predecir series temporales en TensorFlow
Ahora, en este entrenamiento de RNN, es el momento de crear su primer RNN para predecir la serie anterior. Debe especificar algunos hiperparámetros (los parámetros del modelo, es decir, el número de neuronas, etc.) para el modelo:
- Número de entrada: 1
- Paso de tiempo (ventanas en series de tiempo): 10
- Número de neuronas: 120
- Número de salida: 1
Tu red aprenderá de una secuencia de 10 días y contendrá 120 neuronas recurrentes. Alimentas el modelo con una sola entrada, es decir, un día. Puedes modificar los valores para comprobar si el modelo mejora.
Antes de construir el modelo, debes dividir el conjunto de datos en un conjunto de entrenamiento y un conjunto de prueba. El conjunto de datos completo tiene 222 puntos; usarás los primeros 201 puntos para entrenar el modelo y los últimos 21 para probarlo.
Tras definir los conjuntos de entrenamiento y prueba, es necesario crear un objeto que contenga los lotes. Estos lotes contienen valores X e Y. Recuerde que los valores X tienen un desfase de un periodo. Por lo tanto, se utilizan las primeras 200 observaciones y el paso de tiempo es de 10. El objeto `X_batches` debe contener 20 lotes de tamaño 10*1. El objeto `Y_batches` tiene la misma estructura que el objeto `X_batches`, pero con un desfase de un periodo.
Paso 1) Crea el tren y prueba.
En primer lugar, conviertes la serie en una NumPy matriz; luego, define las ventanas (es decir, la cantidad de veces que la red aprenderá), la cantidad de entradas, salidas y el tamaño del conjunto de entrenamiento como se muestra en el ejemplo de TensorFlow RNN a continuación.
series = np.array(ts) n_windows = 20 n_input = 1 n_output = 1 size_train = 201
Después de eso, simplemente divide la matriz en dos conjuntos de datos.
## Split data train = series[:size_train] test = series[size_train:] print(train.shape, test.shape) (201,) (21,)
Paso 2) Cree la función para devolver X_batches e y_batches
Para hacerlo más fácil, puede crear una función que devuelva dos matrices diferentes, una para X_batches y otra para y_batches.
Escribamos una función RNN TensorFlow para construir los lotes.
Nótese que los lotes X están retrasados por un período (tomamos el valor t-1). La salida de la función debe tener tres dimensiones. La primera dimensión equivale al número de lotes, la segunda al tamaño de las ventanas y la última al número de entradas.
La parte complicada es seleccionar los puntos de datos correctamente. Para los puntos de datos X, elige las observaciones de t = 1 a t =200, mientras que para el punto de datos Y, devuelve las observaciones de t = 2 a 201. Una vez que tenga los puntos de datos correctos, es sencillo remodelar las series.
Para construir el objeto con los lotes, debe dividir el conjunto de datos en diez lotes de igual longitud (es decir, 20). Puede utilizar el método de remodelación y pasar -1 para que la serie sea similar al tamaño del lote. El valor 20 es el número de observaciones por lote y 1 es el número de entradas.
Debes hacer el mismo paso pero para la etiqueta.
Ten en cuenta que debes desplazar los datos según el número de veces que quieras realizar la predicción. Por ejemplo, si quieres predecir con un día de antelación, desplaza la serie en 1. Si quieres predecir con dos días de antelación, desplaza los datos en 2.
x_data = train[:size_train-1]: Select all the training instance minus one day X_batches = x_data.reshape(-1, windows, input): create the right shape for the batch e.g (10, 20, 1) def create_batches(df, windows, input, output): ## Create X x_data = train[:size_train-1] # Select the data X_batches = x_data.reshape(-1, windows, input) # Reshape the data ## Create y y_data = train[n_output:size_train] y_batches = y_data.reshape(-1, windows, output) return X_batches, y_batches
Ahora que la función está definida, puede llamarla para crear los lotes como se muestra en el siguiente ejemplo de RNN.
X_batches, y_batches = create_batches(df = train,
windows = n_windows,
input = n_input,
output = n_output)
Puede imprimir la forma para asegurarse de que las dimensiones sean correctas.
print(X_batches.shape, y_batches.shape)
(10, 20, 1) (10, 20, 1)
Debe crear el conjunto de prueba con solo un lote de datos y 20 observaciones.
Tenga en cuenta que, si pronostica días tras días, significa que el segundo valor previsto se basará en el valor real del primer día (t+1) del conjunto de datos de prueba. De hecho, se conocerá el verdadero valor.
Si desea pronosticar t+2 (es decir, con dos días de antelación), debe utilizar el valor previsto t+1; Si va a predecir t+3 (con tres días de anticipación), debe usar el valor predicho t+1 y t+2. Tiene sentido que sea difícil predecir con precisión t+n días de antelación.
X_test, y_test = create_batches(df = test, windows = 20,input = 1, output = 1)
print(X_test.shape, y_test.shape)
(10, 20, 1) (10, 20, 1)
Muy bien, el tamaño del lote está listo, puedes construir la arquitectura de la red neuronal recurrente. Recuerda que tienes 120 neuronas recurrentes.
Paso 3) Construye el modelo
Para crear el modelo, es necesario definir tres partes:
- La variable con los tensores.
- el RNN
- La pérdida y la optimización.
Paso 3.1) Variables
Debe especificar las variables X e y con la forma adecuada. Este paso es trivial. El tensor tiene la misma dimensión que los objetos X_batches e y_batches.
Por ejemplo, el tensor X es un marcador de posición (Consulta el tutorial en Introducción a TensorFlow para refrescar su mente sobre la declaración de variables) tiene tres dimensiones:
- Nota: tamaño del lote
- n_windows: Longitud de las ventanas. Es decir, el número de veces que el modelo mira hacia atrás.
- n_input: número de entrada
El resultado es:
tf.placeholder(tf.float32, [None, n_windows, n_input])
## 1. Construct the tensors X = tf.placeholder(tf.float32, [None, n_windows, n_input]) y = tf.placeholder(tf.float32, [None, n_windows, n_output])
Paso 3.2) Crear el RNN
En la segunda parte de este ejemplo de RNN de TensorFlow, debe definir la arquitectura de la red. Como antes, utilice el objeto BasicRNNCell y dynamic_rnn del estimador de TensorFlow.
## 2. create the model
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=r_neuron, activation=tf.nn.relu)
rnn_output, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)
La siguiente parte es un poco más complicada pero permite un cálculo más rápido. Debe transformar la salida de la ejecución en una capa densa y luego convertirla nuevamente para que tenga la misma dimensión que la entrada.
stacked_rnn_output = tf.reshape(rnn_output, [-1, r_neuron]) stacked_outputs = tf.layers.dense(stacked_rnn_output, n_output) outputs = tf.reshape(stacked_outputs, [-1, n_windows, n_output])
Paso 3.3) Crear la pérdida y optimización.
La optimización del modelo depende de la tarea que esté realizando. En el tutorial anterior sobre CNNTu objetivo era clasificar imágenes; en este tutorial de RNN, el objetivo es ligeramente diferente. Se te pide que hagas una predicción sobre una variable continua en comparación con una clase.
Esta diferencia es importante porque cambiará el problema de optimización. El problema de optimización para una variable continua es minimizar el error cuadrático medio. Para construir estas métricas en TF, puede utilizar:
tf.reduce_sum(tf.square(salidas – y))
El resto del código RNN es el mismo que antes; utiliza un optimizador Adam para reducir la pérdida (es decir, MSE):
tf.train.AdamOptimizer(tasa_de_aprendizaje=tasa_de_aprendizaje)
optimizador.minimizar (pérdida)
Eso es todo, puedes empacar todo junto y tu modelo estará listo para entrenar.
tf.reset_default_graph() r_neuron = 120 ## 1. Construct the tensors X = tf.placeholder(tf.float32, [None, n_windows, n_input]) y = tf.placeholder(tf.float32, [None, n_windows, n_output]) ## 2. create the model basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=r_neuron, activation=tf.nn.relu) rnn_output, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32) stacked_rnn_output = tf.reshape(rnn_output, [-1, r_neuron]) stacked_outputs = tf.layers.dense(stacked_rnn_output, n_output) outputs = tf.reshape(stacked_outputs, [-1, n_windows, n_output]) ## 3. Loss + optimization learning_rate = 0.001 loss = tf.reduce_sum(tf.square(outputs - y)) optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate) training_op = optimizer.minimize(loss) init = tf.global_variables_initializer()
Entrenará el modelo utilizando 1500 épocas e imprimirá la pérdida cada 150 iteraciones. Una vez que se entrena el modelo, se evalúa el modelo en el conjunto de prueba y se crea un objeto que contiene las predicciones como se muestra en el siguiente ejemplo de Red neuronal recurrente.
iteration = 1500 with tf.Session() as sess: init.run() for iters in range(iteration): sess.run(training_op, feed_dict={X: X_batches, y: y_batches}) if iters % 150 == 0: mse = loss.eval(feed_dict={X: X_batches, y: y_batches}) print(iters, "\tMSE:", mse) y_pred = sess.run(outputs, feed_dict={X: X_test}) 0 MSE: 502893.34 150 MSE: 13839.129 300 MSE: 3964.835 450 MSE: 2619.885 600 MSE: 2418.772 750 MSE: 2110.5923 900 MSE: 1887.9644 1050 MSE: 1747.1377 1200 MSE: 1556.3398 1350 MSE: 1384.6113
Por fin, en este tutorial de aprendizaje profundo de RNN, puede trazar el valor real de la serie con el valor previsto. Si su modelo está corregido, los valores predichos deben colocarse encima de los valores reales.
Como puedes ver, el modelo tiene margen de mejora. Depende de ti modificar los hiperparámetros, como las ventanas, el tamaño del lote o el número de neuronas recurrentes.
plt.title("Forecast vs Actual", fontsize=14) plt.plot(pd.Series(np.ravel(y_test)), "bo", markersize=8, label="Actual", color='green') plt.plot(pd.Series(np.ravel(y_pred)), "r.", markersize=8, label="Forecast", color='red') plt.legend(loc="lower left") plt.xlabel("Time") plt.show()
Pronóstico versus real





