Pronóstico de Series Temporales con Redes Neuronales en Python

En el artículo de hoy veremos qué son las series temporales y cómo predecir su comportamiento utilizando redes neuronales con Keras y Tensorflow. Repasaremos el código completo en Python y la descarga del archivo csv del ejercicio propuesto con los datos de entrada.

¿Qué es una serie temporal y qué tiene de especial?

Una serie temporal es un conjunto de muestras tomadas a intervalos de tiempo regulares. Es interesante analizar su comportamiento al mediano y largo plazo, intentando detectar patrones y poder hacer pronósticos de cómo será su comportamiento futuro. Lo que hace <<especial>> a una Time Series a diferencia de un «problema» de Regresión son dos cosas:

  1. Es dependiente del Tiempo. Esto rompe con el requerimiento que tiene la regresión lineal de que sus observaciones sean independientes.
  2. Suelen tener algún tipo de estacionalidad, ó de tendencias a crecer ó decrecer. Pensemos en cuánto más producto vende una heladería en sólo 4 meses al año que en el resto de estaciones.

Ejemplo de series temporales son:

  • Capturar la temperatura, humedad y presión de una zona a intervalos de 15 minutos.
  • Valor de las acciones de una empresa en la bolsa minuto a minuto.
  • Ventas diarias (ó mensuales) de una empresa.
  • Producción en Kg de una cosecha cada semestre.

Creo que con eso ya se dan una idea 🙂 Como también pueden entrever, las series temporales pueden ser de 1 sóla variable, ó de múltiples.

Vamos a comenzar con la práctica, cargando un dataset que contiene información de casi 2 años de ventas diarias de productos. Los campos que contiene son fecha y la cantidad de unidades vendidas.

Requerimientos para el Ejercicio

Como siempre, para poder realizar las prácticas, les recomiendo tener instalado un entorno Python 3.6 como el de Anaconda que ya nos provee las Jupyter Notebooks como se explica en este artículo. También se puede ejecutar en línea de comandos sin problemas. Este ejercicio además requiere tener instalado Keras y Tensorflow (u otro similar), como se explica en el artículo antes mencionado.

Al final del texto entontrarás enlaces al GitHub con la Notebook y el enlace de descarga del archivo csv del ejemplo.

Cargar el Ejemplo con librería Pandas

Aprovecharemos las bondades de Pandas para cargar y tratar nuestros datos. Comenzamos importando las librerías que utilizaremos y leyendo el archivo csv.

fecha
2017-01-02 236
2017-01-03 237
2017-01-04 290
2017-01-05 221
2017-01-07 128
Name: unidades, dtype: int64

Notemos una cosa antes de seguir: el dataframe que cargamos con pandas tiene como Indice nuestra primera columna con las fechas. Esto es para que nos permita hacer filtrados por fecha directamente y algunas operaciones especiales.

Por ejemplo, podemos ver de qué fechas tenemos datos con:

2017-01-02 00:00:00
2018-11-30 00:00:00

Presumiblemente tenemos las ventas diarias de 2017 y de 2018 hasta el mes de noviembre. Y ahora veamos cuantas muestras tenemos de cada año:

315
289

Como este comercio cierra los domingos, vemos que de 2017 no tenemos 365 días como erróneamente podíamos presuponer. Y en 2018 nos falta el último mes… que será lo que trataremos de pronosticar.

Visualización de datos

Veamos algunas gráficas sobre los datos que tenemos. Pero antes… aprovechemos los datos estadísticos que nos brinda pandas con describe()

count 604.000000
mean 215.935430
std 75.050304
min 51.000000
25% 171.000000
50% 214.000000
75% 261.250000
max 591.000000
Name: unidades, dtype: float64

Son un total de 604 registros, la media de venta de unidades es de 215 y un desvío de 75, es decir que por lo general estaremos entre 140 y 290 unidades.

De hecho aprovechemos el tener indice de fechas con pandas y saquemos los promedios mensuales:

fecha
2017-01-31 203.923077
2017-02-28 184.666667
2017-03-31 182.964286
2017-04-30 198.960000
2017-05-31 201.185185
2017-06-30 209.518519
2017-07-31 278.923077
2017-08-31 316.000000
2017-09-30 222.925926
2017-10-31 207.851852
2017-11-30 185.925926
2017-12-31 213.200000
2018-01-31 201.384615
2018-02-28 190.625000
2018-03-31 174.846154
2018-04-30 186.000000
2018-05-31 190.666667
2018-06-30 196.037037
2018-07-31 289.500000
2018-08-31 309.038462
2018-09-30 230.518519
2018-10-31 209.444444
2018-11-30 184.481481
Freq: M, Name: unidades, dtype: float64

Y visualicemos esas medias mensuales:

Vemos que en 2017 (en azul) tenemos un inicio de año con un descenso en la cantidad de unidades, luego comienza a subir hasta la llegada del verano europeo en donde en los meses junio y julio tenemos la mayor cantidad de ventas. Finalmente vuelve a disminuir y tiene un pequeño pico en diciembre con la Navidad.

También vemos que 2018 (naranja) se comporta prácticamente igual. Es decir que pareciera que tenemos una estacionalidad. Por ejemplo podríamos aventurarnos a pronosticar que «el verano de 2019 también tendrá un pico de ventas».

Veamos la gráfica de ventas diarias (en unidades) en junio y julio

¿Cómo hacer pronóstico de series temporales?

Una vez que tenemos confirmado que nuestra serie es estacionaria, podemos hacer pronóstico. Existen diversos métodos para hacer pronóstico. En nuestro caso, las ventas parecen comportarse bastante parecidas al año, con lo cual un método sencillo si por ejemplo quisiéramos proveer el stock que necesitaría este comercio, sería decir «Si en 2017 en diciembre vendimos promedio 213 unidades, pronostico que en diciembre será similar». Otro método muy utilizado en estadística es el llamado ARIMA, el cual no explicaré aquí, pero les dejo un enlace por si están interesados. Aquí un gráfica que encontré en Twitter sobre la evolución del Forecasting:

Pues nosotros que somos unos alumnos tan avanzados y aplicados utilizaremos Machine Learning: una red neuronal para hacer el pronóstico. Curiosamente crear esta red es algo relativamente sencillo, y en poco tiempo estaremos usando un modelo de lo más moderno para hacer el pronóstico.

Pronóstico de Ventas Diarias con Redes Neuronal

Usaremos una arquitectura sencilla de red neuronal FeedForward (también llamada MLP por sus siglas Multi-Layered Perceptron), con pocas neuronas y como método de activación tangente hiperbólica pues entregaremos valores transformados entre -1 y 1.

Si aún no manejas del todo bien redes Neuronales, te recomiendo repasar rápidamente estos artículos y luego continuar con el ejercicio:

Vamos al ejemplo!

Preparamos los datos

Este puede que sea uno de los pasos más importantes de este ejercicio.

Lo que haremos es alterar nuestro flujo de entrada del archivo csv que contiene una columna con las unidades despachadas, y lo convertiremos en varias columnas. ¿Y porqué hacer esto? En realidad lo que haremos es tomar nuestra serie temporal y la convertiremos en un «problema de tipo supervisado« para poder alimentar nuestra red neuronal y poder entrenarla con backpropagation («como es habitual»). Para hacerlo, debemos tener unas entradas y unas salidas para entrenar al modelo.

Lo que haremos -en este ejemplo- es tomar los 7 días previos para «obtener» el octavo. Podríamos intentar entrenar a la red con 2, ó 3 días. O también podríamos tener 1 sola salida, ó hasta «atrevernos» intentar predecir más de un «día futuro». Eso lo dejo a ustedes cómo actividad extra. Pero entonces quedémonos con que:

  • Entradas: serán «7 columnas» que representan las ventas en unidades de los 7 días anteriores.
  • Salida: El valor del «8vo día». Es decir, las ventas (en unids) de ese día.

Para hacer esta transformación usaré una función llamada series_to_supervised() creada y explicada en este blog. (La verás en el código, a continuación)

Antes de usar la función, utilizamos el MinMaxScaler para transformar el rango de nuestros valores entre -1 y 1 (pues sabemos que a nuestra red neuronal, le favorece para realizar los cálculos).

Entonces aqui vemos cómo queda nuestro set de datos de entrada.

Usaremos como entradas las columnas encabezadas como var1(t-7) a (t-1) y nuestra salida (lo que sería el valor «Y» de la función) será el var1(t) -la última columna-.

Creamos la Red Neuronal Artificial

Antes de crear la red neuronal, subdividiremos nuestro conjunto de datos en train y en validación. ATENCIÓN, algo importante de este procedimiento, a diferencia de en otros problemas en los que podemos «mezclar» los datos de entrada, es que en este caso nos importa mantener el orden en el que alimentaremos la red. Por lo tanto, haremos una subdivisión de los primeros 567 días consecutivos para entrenamiento de la red y los siguientes 30 para su validación. Esta es una proporción que elegí yo, y que me pareció conveniente, pero definitivamente, puede no ser la óptima (queda propuesto al lector, variar esta proporción por ejemplo a 80-20 y comparar resultados )

(567, 1, 7) (567,) (30, 1, 7) (30,)

Hemos transformado la entrada en un arreglo con forma (567,1,7) esto al castellano significa algo así como «567 entradas con vectores de 1×7».

La arquitectura de la red neuronal será:

  • Entrada 7 inputs, como dijimos antes
  • 1 capa oculta con 7 neuronas (este valor lo escogí yo, pero se puede variar)
  • La salida será 1 sola neurona
  • Como función de activación utilizamos tangente hiperbólica puesto que utilizaremos valores entre -1 y 1.
  • Utilizaremos como optimizador Adam y métrica de pérdida (loss) Mean Absolute Error
  • Como la predicción será un valor continuo y no discreto, para calcular el Acuracy utilizaremos Mean Squared Error y para saber si mejora con el entrenamiento se debería ir reduciendo con las EPOCHS.

Entrenamiento y Resultados

Veamos cómo se comporta nuestra máquina al cabo de 40 épocas.

En pocos segundos vemos una reducción del valor de pérdida tanto del set de entrenamiento como del de validación.

Epoch 40/40
567/567 [==============================] – 0s 554us/step – loss: 0.1692 – mean_squared_error: 0.0551 – val_loss: 0.1383 – val_mean_squared_error: 0.03

Visualizamos al conjunto de validación (recordemos que eran 30 días)

En la gráfica vemos que los puntitos verdes intentan aproximarse a los rojos. Cuanto más cerca ó superpuestos mejor. TIP: Si aumentamos la cantidad de EPOCHS mejora cada vez más.

Veamos y comparemos también cómo disminuye el LOSS tanto en el conjunto de train como el de Validate, esto es bueno ya que indica que el modelo está aprendiendo. A su vez pareciera no haber overfitting, pues las curvas de train y validate son distintas.

Pronóstico de ventas futuras

Ahora que tenemos nuestra red y -suponiendo que realizamos los 7 pasos del ML– la damos por buena, probaremos a realizar una nueva predicción, en este caso, usaremos los últimos días de noviembre 2018 para calcular la primer semana de diciembre. Veamos:

fecha
2018-11-16 152
2018-11-17 111
2018-11-19 207
2018-11-20 206
2018-11-21 183
2018-11-22 200
2018-11-23 187
2018-11-24 189
2018-11-25 76
2018-11-26 276
2018-11-27 220
2018-11-28 183
2018-11-29 251
2018-11-30 189
Name: unidades, dtype: int64

Y ahora seguiremos el mismo preprocesado de datos que hicimos para el entrenamiento: escalando los valores, llamando a la función series_to_supervised pero esta vez sin incluir la columna de salida «Y» pues es la que queremos hallar. Por eso, verán en el código que hacemos drop() de la última columna.

De este conjunto «ultimosDias» tomamos sólo la última fila, pues es la que correspondería a la última semana de noviembre y la dejamos en el formato correcto para la red neuronal con reshape:

array([[[ 0.11000001, 0.13 , -1. , 1. ,
0.44000006, 0.06999993, 0.75 ]]], dtype=float32)

Ahora crearemos una función para ir «rellenando» el desplazamiento que hacemos por cada predicción. Esto es porque queremos predecir los 7 primeros días de diciembre. Entonces para el 1 de diciembre, ya tenemos el set con los últimos 7 días de noviembre. Pero para pronosticar el 2 de diciembre necesitamos los 7 días anteriores que INCLUYEN al 1 de diciembre y ese valor, lo obtenemos en nuestra predicción anterior. Y así hasta el 7 de diciembre.

Ya casi lo tenemos… Ahora las predicciones están en el dominio del -1 al 1 y nosotros lo queremos en nuestra escala «real» de unidades vendidas. Entonces vamos a «re-transformar» los datos con el objeto «scaler» que creamos antes.

array([[174.48904094],
[141.26934129],
[225.49292353],
[203.73262324],
[177.30941712],
[208.1552254 ],
[175.23698644]])

Ya podemos crear un nuevo DataFrame Pandas por si quisiéramos guardar un nuevo csv con el pronóstico. Y lo visualizamos.

A partir de los últimos 7 días de noviembre 2018 y utilizando nuestra red neuronal, hicimos el siguiente pronóstico de venta de unidades para la primer semana de diciembre.

No te pierdas la Segunda Parte de este artículo sobre Pronóstico de Series Temporales con Redes Neuronales en Python: Multiples Variables y Embeddings

Conclusiones y propuesta al lector

Durante este nuevo capítulo del aprendizaje automático, diferenciamos lo que son las Series Temporales y su predicción de los problemas de Regresión. Aprovechamos la capacidad de las redes neuronales de generalizar y lograr predecir ventas futuras. Uno de los pasos más importantes, al realizar el pre procesado, consiste en convertir nuestra serie en un modelo de aprendizaje supervisado, donde tenemos valores de entrada y salida, para poder entrenar la red. Y finalizamos realizando pronóstico de una semana utilizando la red neuronal creada.

Propongo al lector hacer diversas pruebas para mejorar las predicciones, alterando parámetros del ejercicio:

  • Variar la cantidad de EPOCHS
  • Probar otro optimizador distinto a Adam, ó configurar valores distintos de Learning Rate.
  • Cambiar la arquitectura de la Red Neuronal:
    • Cambiar la cantidad de Neuronas de la capa oculta.
    • Agregar más capas ocultas
  • Probar utilizando más de 7 días previos para predecir. O probar con menos días.
  • Se puede probar de intentar predecir más de 1 día por vez (sin iterar el resultado como hice con la función agregarNuevoValor() )

En el próximo artículo (YA DISPONIBLE) retoma este ejercicio pero aplicando Embeddings que puede mejorar la precisión de las predicciones poniendo en juego el día de la semana y mes que estamos pronosticando, considerándolos como datos adicionales de entrada a la red neuronal para preservar mejor la estacionalidad.

NOTA 1: recordemos que el futuro es IMPREDECIBLE… por lo que debo decir al Científico de datos: cuidado sobre todo si debemos predecir resultados de series con comportamiento errático, como los valores de la bolsa! y también cautela en asuntos sensibles sobretodo relacionados con la salud.

NOTA 2: Debo confesar que inicialmente implementé el ejercicio utilizando Redes Neuronales Recurrentes, de tipo LSTM pero obtuve pésimos resultados… encontré mucho mejor performante a nuestras queridas «redes neuronales tradicionales» MLP.

Unete al Blog y Recibe el próximo artículo!

Recibe los nuevos artículos sobre Machine Learning, redes neuronales y todo el código Python en tu casilla de correo!

NOTA: muchos usuarios reportaron que el email de confirmación y/o posteriores a la suscripción entraron en su carpeta de SPAM. Te sugiero que revises y recomiendo que agregues nuestro remitente a tus contactos para evitar problemas. Gracias!

Recursos del Artículo y Enlaces

Artículos recomendados en Inglés (I’m sorry)

24 comments

  1. IVAN DIAZ · febrero 26

    Muy interesante!
    Si, por ejemplo, quisiéramos tener en cuenta otros factores asociados, por ejemplo temperatura ambiente y cómo está está relacionada con el volumen de ventas ¿qué orientación deberíamos tomar? Aquí, por lo que veo, únicamente analizamos las ventas de helados en sí, pero no estamos haciendo un análisis multifactorial.

    • Na8 · febrero 26

      Hola Ivan Diaz, gracias por escribir. Te comento, en este ejercicio, es tal como tu dices de «una» variable. Pero se puede hacer pronóstico de series «multivariable». En ese caso, deberemos tener más columnas como las llamadas var1(t-n) pero agregando por ejemplo var2(t-n), etc.
      En el próximo artículo puede que escriba sobre eso mismo. Entre tanto te dejo este enlace que te resultará útil: Multivariate Time Series Forecasting

      Saludos!

  2. Cristian Sanchez · marzo 6

    De gran ayuda! Estoy iniciando en este campo para el desarrollo de mi tesis universitaria y menos mal encontre este blog tan bueno, gracias y felicidades!

    • Na8 · marzo 8

      Hola Cristian, gracias por tu comentario!, espero poder seguir ayudando a todos quienes se inician en Aprendizaje Automático.
      ¿Sobre qué será tu tesis universitaria?
      Si quieres puedes suscribirte al blog para recibir los próximos artículos!
      Saludos

  3. Ricardo · abril 11

    Muy buen artículo.
    Una consulta, he observado que cuando se realiza el pronostico de la primer semana de diciembre, los valores se escalan sólo considerando el valor máximo y mínimo entre estas fechas df[‘2018-11-16′:’2018-11-30’]
    ¿Deberían escalarse considerando el maximo y minimo de todos de los valores con los que fue entrenada la red?
    Gracias

  4. José Maglione · abril 22

    Gracias. Me pasa de entender las ideas pero no lo tengo tan claro leyendo el código. Me pareció buenisimo lo de “crear” la columna de salida para poder hacer supervisado.
    Por otra parte, ¿Cómo garantizamos estacionalidad? ¿Es crucial que lo sea? Los métodos estadísticos para averiguarlo son más largos que ir probando diferentes redes neuronales. Sigo aprendiendo.

  5. Jorge Marin · abril 29

    Hola Juan, Te felicito por esta buena iniciativa, actualmente estoy trabajando en mi propuesta de Tesis de Grado Universitaria y encontrar un lugar que explique de esta manera tan organizada los propósitos, usos y aplicación de Machine Learning, a sido complicado.

    Mi tesis de grado es Desarrollar un servicio de predicción de ventas basado en machine learning, que pueda ser usado por todas las empresas que facturen en un Sistema de Facturación.

    Te quería preguntar, basado en tu experiencia, cuanto tiempo aproximadamente conlleva elaborar un proyecto de machine learning, esta pregunta la hago ya que necesito realizar mi crono grama, pero sin la experiencia de haberlo realizado antes no se bien como

  6. Pablo Rosa · mayo 4

    Hola Juan!

    Muchas gracias por divulgar tu conocimiento!
    Hace aprox un año que estoy metiendome en el mundo de la programación, data science, ML, IA, etc y este articulo es de lo mas claro y completo que me he topado.

    seguiré de cerca tu blog para seguir aprendiendo, comentando y quien dice, en algun momento sumar algo!

    Saludos desde Córdoba!

    • Na8 · mayo 4

      Genial, me alegra saber que los artículos ayudan a aprender 🙂 Espero que sigamos en contacto y espero tus comentarios y aportes!
      Saludos

  7. José Maglione · mayo 8

    Hola, no me queda claro la necesidad de convertir la serie para que se calcule el valor dels el 8vo día. Si tuviera mes del año, cantidad de clientes activos en ese mes y como resultado, las cantidad de pedidos procesados en el mes, ¿No estaría bien asi para entrenar una red para predecir la cantidad de envíos, teniendo como entradas al mes y cantidad de clientes en ese mes? Muchas gracias Juan!

    • Na8 · mayo 16

      Hola José en este caso intentamos hacer que la red «entienda» los ciclos de estacionalidad del comercio. No siempre será igual y puede que dependiendo las ventas pasadas cambien ligeramente las predicciones. Pero ojo, no descarto tu solución y de hecho hasta ahora los pronósticos se venían haciendo con fórmulas estadísticas y no con redes neuronales! Saludos

  8. Lourdes Gallego · mayo 14

    Buenas Juan, muchas gracias por el post, pero me surge la siguiente duda en la última parte. Cuando predices el primer día de diciembre omites el últiimo día de noviembre al tener pasos = 7; para este caso no se debería incluir este último dia de noviembre tambien?.

    • Na8 · mayo 16

      Hola Lourdes, yo juraría que sí está incluyendo el último dia de noviembre, el 30. ¿Acaso encontraste un error? si es así te pido que me lo expliques para poder corregirlo!
      Saludos y muchas gracias!!

  9. Ricardo Ruiz · junio 19

    hola despues de revisar varias veces los resultados al menos en mi caso concreto los calculos que hace para la media, la desviacion estandar e incluso para la prediccion la hace sobre el valor de dias no sobre las ventas, es decir calcula siempre sobre la primer columna no sobre la segunda que es donde tengo las ventas, tu ejemplo dice «Son un total de 604 registros, la media de venta de unidades es de 215 y un desvío de 75, es decir que por lo general estaremos entre 140 y 290 unidades.» pero eso se refiere a los dias no a las ventas del dia.. espero haberme explicado y me pudieras ayudar a entender este punto, de antemano te agradezco y te felicito por tu valioso aporte a los que recien nos iniciamos en esta materia.

    • Na8 · junio 19

      Hola Ricardo, en cuanto pueda lo reviso y te escribo, si es un error mío lo cambiaré! Muchas gracias por escribir!!

    • Na8 · junio 25

      Hola Ricardo, gracias nuevamente por escribir. Según veo yo, el artículo está correcto, pues mira que cuando cargamos el archivo csv con Pandas, utilizamos la columna de fecha como «indice», entonces al hacer el df.describe() sólo tenemos en cuenta la columna de ventas, no la de los días. Por lo tanto los valores estadísticos que ves se refieren a las ventas.
      Saludos!

  10. wezen · junio 26

    Hola. Primero que nada Felicitaciones y gracias por tu blog. Es de mucha ayuda. Tal vez sea una pregunta muy simple de contestar pero soy nuevo en el tema. La información de salida de los Epoch como deberíamos leerla? Loss: 0.0809 es equivalente a un 8% de error? y el mean_squared_error?
    Desde ya muchas gracias

    • Na8 · julio 3

      Hola Wezen, no es esa relación! el 0.08 no significa el 8% error!
      Realmente se refiere al valor de «loss» de la función de coste. Pero no sigue una escala comparable con alguna unidad.
      Lo que tu dices de 8% error, podría ser el accuracy, es decir, si tienes un 92% de aciertos, podríamos decir que tienes un 8% de error… aunque también te diría no te fíes de esas métricas, como lo comento en el artículo Clasificación con datos desbalanceados.
      Saludos

  11. idan · julio 6

    Hola Juan, como estas!! Muchas Gracias por la detallada explicación.
    He leído varios artículos del blog y tengo una duda de los diferentes de algoritmos que puedo utilizar en mi proyecto de Grado, el cual consiste en predecir la cantidad de llamadas que van a ingresar a un call center. Tengo como dataset el servicio,fecha y numLlamadas de 3 años, pero no se si implementarlo con series de tiempo con redes neuronales o regresión lineal, cual es el recomendado.

    De ante mano muchas Gracias por la colaboración.

    • Na8 · julio 8

      A ver, en principio descartaría clasificación, con lo cual nos queda regresión. Y dentro de regresión podrías usar Time Series si hubiera estacionalidad. Para implementar el Time Series podrías usar Redes Neuronales.
      Saludos!

  12. Angel Bobadilla · julio 11

    POR FAVOR PON PARA PODER DESCARGAR EL CODIGO

    • Na8 · julio 11

      Hola Angel, gracias por escribir. Puedes descargar TODO el código de todos los artículos desde mi cuenta Github. Todos los artículos tienen enlaces a la Notebook y a su correspondiente archivo CSV.
      Si necesitas ayuda me dices! Saludos

  13. Jhonatan Artigas · agosto 27

    Hola Juan, aún no logro encontrar en qué parte del código colocas que es solo 1 capa oculta con 7 nodos.

    Si pudieras aclararme esa duda sería genial!

    Saludos y gracias.

  14. mayra jimenez · 8 Days Ago

    Muchas gracias, muy útil tu página para mí. Tengo una pregunta: ¿Qué tipo de método usaste para hacer una prueba de respaldo teniendo en cuenta: https://machinelearningmastery.com/backtest-machine-learning-models-time-series-forecasting/
    Pregunto ya que es una serie temporal y me gustaría usar su código y la validación de mi red neuronal. Muchas gracias

Responder a IVAN DIAZ Cancelar respuesta