Pronóstico de Ventas con Redes Neuronales – Parte 2

Mejora del modelo de Series Temporales con Múltiples Variables y Embeddings

Este artículo es la continuación del post anterior “Pronóstico de Series Temporales con Redes Neuronales en Python” en donde vimos cómo a partir de un archivo de entrada con las unidades vendidas por una empresa durante años anteriores, podíamos estimar las ventas de la próxima semana. Continuaremos a partir de ese modelo -por lo que te recomiendo leer antes de continuar- y haremos propuestas para mejorar la predicción.

Breve Repaso de lo que hicimos

En el modelo del capitulo anterior creamos una Red Neuronal MLP (Multilayered Perceptron) feedforward de pocas capas, y el mayor trabajo que hicimos fue en los datos de entrada. Puesto que sólo tenemos un archivo csv con 2 columnas: fecha y unidades vendidas lo que hicimos fue transformar esa entrada en un “problema de aprendizaje supervisado“. Para ello, creamos un “nuevo archivo” de entrada con 7 columnas en donde poníamos la cantidad de unidades vendidas en los 7 días anteriores y de salida la cantidad de unidades vendidas en “la fecha actual”. De esa manera alimentamos la red y ésta fue capaz de realizar pronósticos aceptables. Sólo utilizamos la columna de unidades. Pero no utilizamos la columna de fecha. ¿Podría ser la columna de fecha un dato importante? ¿podría mejorar nuestra predicción de ventas?

Mejoras al modelo de Series Temporales

Esto es lo que haremos hoy: propongo 2 nuevos modelos con Redes Neuronales Feedforward para intentar mejorar los pronósticos de ventas:

  • Un primer modelo tomando la fecha como nueva variable de entrada valiosa y que aporta datos.
  • Un segundo modelo también usando la fecha como variable adicional, pero utilizándola con Embeddings… y a ver si mejora el pronóstico.

Por lo tanto explicaremos lo qué son los embeddings utilizados en variables categóricas (se utiliza mucho en problemas de Procesamiento del Lenguaje Natural NLP para modelar).

Para estos modelos propuestos haremos la transformación a “problema de aprendizaje supervisado”. Para ello usaremos la misma función series_to_supervised() de la web machinelearningmastery como en el artículo anterior.

Primer Mejora: Serie Temporal de múltilples Variables

Puede que el “ejemplo clásico” para comprender lo que son las Series Temporales de Múltiples Variables sea el pronóstico del tiempo, en donde tenemos varias “columnas de entrada” con la temperatura, la presión atmosférica, humedad. Con esas tres variables tendremos una mejor predicción de “la temperatura de mañana” que si tan sólo usásemos una sola feature.

Usar Fecha como variable de entrada

Como solamente tenemos un dato de entrada (las unidades vendidas en el día), intentaremos enriquecer a la red con más entradas. Para ello, usaremos la fecha. ¿Pero cómo? Bueno, aprovecharemos que podemos saber cada día que hubo ventas si fue un lunes, martes… por ejemplo algunos comercios venden más los viernes y sábados. También, como vimos que en cuanto a estacionalidad, en verano (europeo) subían las ventas, la red neuronal debería percatarse de eso y “mejorar su puntería” entendiendo que eso ocurre en los meses 7 y 8 (¿lo hará..?). No agregaré los años, pues sólo tenemos 2017 y 2018 (muy pocos), pero si tu cuentas con un dataset con muchos años, sería bueno agregar como entrada también los años.

En Limpio: Usaremos el día como variable categórica con valores de 0 a 6 indicando día de semana y usaremos el número de mes como otra variable categórica. La “intuición” es que la red <<entenderá>> las estacionalidades dadas entre semana y mensuales.

Segunda mejora: Embeddings en variables categóricas

Bien, para el segundo modelo, utilizaremos embeddings en las variables categóricas, es decir, en la columna de día y de mes. Los valores de día van del 0 al 6 representando los días de la semana. Pero no quiere decir que el día 6 “vale” más que el día 0. Son identificadores. No tendría sentido decir que jueves es mayor que domingo. Sin embargo la red neuronal esto no lo sabe y podría interpretar erróneamente esos valores (categóricos)… Con los meses lo mismo; van del 1 al 12 pero no quiere decir que “diciembre valga más que agosto”. Y de hecho, sabemos en la práctica para este ejercicio, que realmente en julio y agosto, es cuando más aumentan las ventas. Para intentar resolver esta problemática, es que aparecen los Embeddings.

¿Qué son los Embeddings? ¿por qué? ¿para qué?

La traducción al español de “Embed” es Incrustar… y esto a simple vista no nos ayuda mucho. Google lo traduce en uno de sus tutoriales como “incorporaciones“. Los embeddings son una manera de dar valoración útil- a datos categóricos. Para ello asignaremos una profundidad a cada “identificador”, es decir un vector con valores continuos inicialmente aleatorios. Esos valores se ajustarán con backpropagation al igual que nuestra red neuronal. Y finalmente nuestros datos categóricos quedan enriquecidos y dejan de ser “lunes” para ser unos vectores con valores que “significan algo”. ¿Qué significan? para simplificar e intentar entenderlo podemos decir que esos vectores “acercan identificadores similares entre sí y distancia a los opuestos”. Un ejemplo: cuando se utiliza en Natural Language Processing (NLP) con un gran número de palabras, los Embeddings logran hacer que palabras sobre sentimientos positivos -“alegría”,”felicidad”- queden cercanas pero distanciadas de las que significan sentimientos negativos “odio”,”tristeza”.

Hay un caso también muy usado llamado: Filtrado colaborativo en el cual se crea un motor de recomendaciones de películas. En ese ejemplo, se sitúan en un espacio vectorial las películas infantiles en extremo y las de adultos en otro. A su vez, otra coordenada indica si la película es “más comercial/taquillera” ó más “artística”.

En este caso “simplificado” los Embeddings se pueden ver como vectores de coordenadas (x,y) que acercan películas similares en 2 dimensiones y a su vez quedan distanciadas de Identificadores opuestos.

Sobre la dimensionalidad de los Embeddings

Es bueno usar muchas dimensiones (profundidad) para modelar nuestras variables categóricas. Pero ojo!, si tiene demasiadas, puede ocurrir overfitting. Entonces habrá que hacer prueba y error. Hay una regla que dice que hay que usar “una cuarta parte” del tamaño de la variable categórica: si tenemos 100 identificadores, usaremos como profundidad 25. En el curso de Fast.ai recomiendan un máximo de 50 ó “la cantidad de elementos categóricos más uno, dividido dos” (si es menor a 50). Pero siempre dependerá del caso.

Conclusión de Embeddings

Al asignarle vectores con valor numérico continuo a entradas categóricas , estos terminan funcionando como “una mini red neuronal” dentro de la red principal. Aprenden con backpropagation. Y resuelven como valores continuos esos identificadores discretos, acentuando su valor intrínseco.

Una ventaja de usar Embeddings es que se pueden reutilizar. Una vez entrenados podemos guardarlos para utilizarlos en otra red. Gracias a esto es que encontramos archivos de Embeddings con millones de palabras “ya entrenadas” por Google, listos para descargar y usar.

¿Y el código fuente de los modelos? Quiero Python! Exijo mi Jupyter Notebook!

Dejaré enlace a los códigos, pues me quiero centrar un más en las comparaciones y conclusiones de cada modelo, y no tanto en su implementación (cualquier duda, me escriben comentarios!). Aquí los enlaces de las 3 notebooks puedes abrirlas en pestañas nuevas

Los Resultados de los 3 modelos: Comparemos

Para intentar que las comparaciones sean lo más “justas” posibles, utilizaremos en las 3 redes neuronales las mismas funciones de activación (tanh), misma optimización (Adam) y métricas loss y score (mean_absolute_error y mean_squared_error). Además en todos los casos ejecutaremos 40 EPOCHS.

Por comodidad llamaremos a los 3 modelos:

  1. Serie Temporal de 1 variable = ST1
  2. Serie Temporal de Multiples Variables = STMV
  3. Serie Temporal con Embeddings = STE

Comparemos las Métricas

Vemos los valores finales de las métricas tras las 40 EPOCHS

Modelolossval_lossMSEval_MSE
1)ST10.16820.14020.05550.0353
2)STMV0.15830.14280.05100.0429
3)STE0.10860.09540.02270.0199

El modelo ST1 y STMV quedan prácticamente iguales. Es decir que tras agregar las variables de fecha no pareciera haber mejoría en las métricas. Sin embargo el modelo con embeddings sí que logra una mejora algo más evidente: el validation_loss pasa de 0.14 a 0.09 y el validation_MSE de 0.04 a 0.02.

Comparemos Gráficas de Pérdida (loss)

En las tres gráficas vemos que la métrica de loss en los sets de entrenamiento y validación descienden y se mantiene estables. Bueno, en la del segundo modelo STMV la curva de validación es algo errática. Las curvas del modelo 1 y 2 se mantienen sobre el 0.15 mientras que la del 3er modelo desciende algo más en torno del 0.10

Modelo 1) ST1: En azul el Entrenamiento y naranja el set de Validación.
Modelo 2) STMV: En azul el Entrenamiento y naranja el set de Validación.
Modelo 3) STE: En azul el Entrenamiento y naranja el set de Validación.

Comparemos las Gráficas de Accuracy

Utilizamos la métrica de MSE, y vemos que nuevamente el modelo 1 y 2 se comportan similar y se sitúan sobre el 0.06 y el modelo 3 con Embeddings desciende hasta el 0.02 (indicando una mejora) .

Comparamos los pronósticos y sus aciertos

Vamos a hacer una comparación visual, de los pronósticos realizados sobre el set de validación y marcar los aciertos. En azul, los puntos reales y en naranja las predicciones.

Modelo 1) ST1: con aciertos, pero pronóstico conservador
Modelo 2) STMV: Bastante similar al modelo 1 pero con algo mayor de amplitud.
Modelo 3) STE: Los Embeddings proveen mayor flexibilidad a la curva de pronóstico y aciertos.

Podemos ver que la primera red es “más conservadora”, manteniéndoselo en “la media” de 200, sin picos bruscos. El segundo modelo tiene algo más de amplitud en sus predicciones y la red neuronal que mejor se comporta es la tercera, que evidentemente gracias a los Embeddings logra pronosticar mejor los valores y vemos picos “más alejados” de la media de 200 que son <<buenos aciertos>>.

Extra!: publica tu modelo con Flask

Ahora está disponible un nuevo artículo “Tu propio Servicio de Machine Learning” en el cual se toma el modelo de embeddings tratado aqui y se publica mediante una API creada con Flask. Te recomiendo que lo leas y así publicas tu modelo online!

Conclusiones

Como primer conclusión podemos decir que mejoran las predicciones al agregar más variables de entrada a la red. Realmente notamos mejoría con nuestro modelo 3 al usar Embeddings en la red neuronal.

NOTA 1: recordemos que hay muchos de los parámetros para “tunear” que hemos fijado arbitrariamente. Al igual que en artículo anterior, animo al lector a variar esos parámetros en los 3 modelos para mejorarlos, empezando por la cantidad de EPOCHS=40 (aumentar a 100), ó la variable de PASOS que está en 7 (probar con 4 ó con 10).

NOTA 2: en el modelo de múltiples variables “hicimos un truco” tomando como variable adicional la fecha, pero realmente estaría bien tener otras variables con datos útiles como en el ejemplo de pronóstico del clima: 3 variables con temperatura, presión y humedad.

Podemos ver cómo el Machine Learning, puede a partir de relativamente pocos datos, sacar de su galera nuevas herramientas y adaptar y mejorar el modelo. En este caso, sabemos que podemos utilizar las variables categóricas a través de Embeddings y mejorar sustancialmente los resultados obtenidos.

Te recomiendo leer “Interpretación de Modelos de Machine Learning”, pues se comentan diversas herramientas que te ayudarán a seleccionar y valorar la características más importantes que uses en tus modelos.

Suscripción al Blog – recibe nuevos artículos

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

NOTA: algunos usuarios reportaron que el email de confirmación a la suscripción entraron en la carpeta SPAM. Te sugiero que revises y recomiendo agregar el remitente a tus contactos. Gracias!

Recursos del Artículo y Enlaces

Otros Artículos recomendados (en inglés)

Libros Relacionados

24 comments

  1. José Maglione · May 12, 2019

    Hola, yo otra vez. Me queda la duda de como puedo “guardar”la red entrenada para seguir con las predicciones pero sin tener que volver a entender. La idea que tengo es hacer una función pasando como parámetro el mes que quiero predecir. Dentro de la función, tengo el modelo ya entrenado.
    Incluso más. Obtener la matriz W de los pesos y ponerlo en un Excel para predecir donde sea, incluido desde el celular.
    Gracias otra vez.

  2. Jhonatan Artigas Alegría · July 3, 2019

    Hola, por casualidad conoces de algún artículo que hable sobre la predicción de delitos? Me sería de gran ayuda, ya que códigos no he encontrado…

    Saludos y éxito! Muy buenos los artículos, me han ayudado muchísimo.

    • Na8 · July 8, 2019

      Prueba a buscar en los datasets de kaggle. Recién hice una búsqueda con “crime” y me apareció este notebook con gráficas y todo te puede ayudar. Y había más!
      SAludos

  3. Jhonatan Artigas · August 27, 2019

    Hola Juan, estoy tratando de ajustar tu código y me gustaría que en la capa de salida tuviera 2 nodos (2 variables distintas). Como tendría que modificarlo?

    Agradecería mucho de tu ayuda.

    Saludos!

  4. mayra jimenez · September 15, 2019

    Hola Juan, estoy intenatnto usar tu código con mis datos y me sale el sgte mensaje al usar este bloque de código: EPOCHS=40

    model = crear_modeloFF()

    history=model.fit(x_train,y_train,epochs=EPOCHS,validation_data=(x_val,y_val),batch_size=PASOS)

    Y el error:
    RuntimeError: It looks like you are trying to use a version of multi-backend Keras that does not support TensorFlow 2.0. We recommend using tf.keras, or alternatively, downgrading to TensorFlow 1.14.

    Quisiera saber qué versiones de python, tensorflow y keras utilizas con tu código, muchas gracias.

    • Na8 · September 21, 2019

      Por lo que veo, vos debes tener instalada la versión de Tensorflow 2 y en este ejercicio estaba usando Tensorflow 1.14.
      Intentaré actualizar el código en el futuro… pero necesitare de vuestra paciencia… y tiempo!.
      Saludos

  5. Eliana · March 7, 2020

    Hola! Tu blog me ha servido mucho.
    Tengo una duda.

    Se supone que todas las entradas a la red deben estar normalizadas entre -1 y 1; pero noto que la información del día y el mes no están normalizadas ¿Eso afecta a los resultados de la red?

  6. Diego Diaz · September 7, 2020

    hola.
    Felicidades por el blog. estoy deseando leer el libro!
    Estoy repasando los dos ejemplos de series temporales publicados, pero hay algo que no acabo de entender, en el primer ejemplo divides los 597 datos en 567 para train y 30 para validation. En cambio en el segundo articulo en el codigo del 3 modelo (https://nbviewer.jupyter.org/github/jbagnato/machine-learning/blob/master/Series_Temporales_Embeddings.ipynb) divides los 595 datos en 595 para train y 30 para validation. Esto se correcto? no tendrían que ser 595 datos, donde 565 son para train y 60 para validation? gracias

    • Na8 · September 9, 2020

      Hola Diego, gracias por escribir.
      Creo que debería cambiar las líneas por:

      training_data = training_data[0:595-30]
      target_data=target_data[0:595-30]

      En cuanto lo pueda probar, lo cambiaré en el artículo.

      El libro se puede ir viendo en https://leanpub.com/aprendeml/ Saludos!

  7. Jose · November 30, 2020

    Hola, gracias por tu blog y tus artículos, son muy interesantes. Tengo una pregunta sobre el modelo con Embeddings. Cuando construyes el modelo, tenemos los inputs, luego las capas de embeddings para las variables categóricas, luego las concatenas y luego aplanas y pasas a las capas Dense. Sin embargo, en el modelo no veo que se usen los datos no categóricos (‘cli’). Donde y cómo se deberían introducir esos datos?
    Tal y como está ahora, se está entrenando al modelo con ellos?

    Gracias

    • Na8 · December 5, 2020

      Hola Jose, gracias por escribir. Si te fijas en la línea donde crea el Model() agrega tanto el día, mes y los pasos que están en la variable in_cli.
      Saludos!

      • Jose · December 27, 2020

        Gracias. Ahora lo he visto. He hecho mis propias pruebas con series temporales comparando LSTM y MLP. Quizá sea por los datos, pero parece dar mejor resultado redes MLP normales y no entiendo por que. Además, el añadir capas extra solo hace que la predicción tienda mas a la media y no sea capaz de predecir la variabilidad de los valores. Sabes por qué se comporta asi? Cuando es mejor añadir mas capas y cuando no? No he encontrado ningún sitio donde realmente explique cual es el efecto de las capas adicionales

  8. tony l · March 9, 2021

    Hola que tal he estado siguiendo tu blog y leyendo tu libro, hasta ahora me han parecido una fuente muy buena de donde aprender, pero al estar haciendo una serie de tiempo con los embeddings siguiendo tu ejemplo, me he dado cuenta que tengo varias dudas ya que no explicas claramente el por que de algunas cosas te las enlisto, si pudiera especificar un poco mejor que se hace en estas lineas se lo agradeceria.

    ¿cual es el fin de este for, por que [i+8] a que se refiere ese i+8?

    contador=0
    reframed[‘weekday’]=df[‘weekday’]
    reframed[‘month’]=df[‘month’]

    for i in range(reframed.index[0],reframed.index[-1]):
    reframed[‘weekday’].loc[contador]=df[‘weekday’][i+8]
    reframed[‘month’].loc[contador]=df[‘month’][i+8]
    contador=contador+1

    ¿como decides que la profundidad seran 2,4 ?

    emb_dias = 2 #tamanio profundidad de embeddings
    emb_meses = 4

  9. Gustavo · April 1, 2021

    Hola, como va? Estuve intentando entrar a los links de los códigos y generan un error 503. No sé si podrás verlo para poder analizar mejor los casos que propones. Desde ya muchas gracias!!

    • Na8 · April 6, 2021

      Hola, acabo de comprobar todos los enlaces eme funcionan correctamente, pudo haber sido algo temporal de github.
      Cualquier cosa me dices, saludos y gracias por escribir

      • Andrés · May 4, 2021

        Hola Juan, me pasa lo mismo que a Gustavo: al tratar de entrar a los links de los códigos genera un error.

        • Na8 · June 20, 2021

          Hola Andrés, podrías ser más específico en qué enlace?, acabo de comprobar las 3 notebooks y me cargan correctamente. ¿a cual se refieren?
          Saludos

  10. Daniel Cofre · April 27, 2021

    Que tal estimado. Ya llevo un año siguiendote (la pandemia nos conectó jejeje). Has tenido algun acercamiento de aplicación de RRNN con trading? especificamente con criptomonedas. No se si las series temporales se amoldan a este tópico. Saludos desde Chile

    • Na8 · June 20, 2021

      Hola Daniel, gracias por escribir y por seguirme. La verdad que hace poco me estaba enterando un poco del mundo “trading con crypto” y está muy interesante… aunque también es terroríficamente volátil!.
      Las series temporales deberían servir para este tipo de problemas, pero como te decía antes, al tener tanta volatilidad, es probable que fallen mucho. Tal vez sirvan para intentar detectar tendencias.
      No estaría mal hacer un artículo sobre esto, me lo apunto para el futuro 🙂
      Saludos a vos y a Chile!

  11. Noe Lujan · February 13, 2023

    Los Resultados de los 3 modelos el código donde puedo conseguir

  12. carlos patiño · 23 Days Ago

    Hola estimado, un placer saludarte.
    Te comento que estoy interesado en implementar uno de estos códigos tuyos de pronostico con series temporales en Python de Power Bi, pero la verdad no me son funcionales, por ejemplo la importación de keras no es funcional, agradezco por favor si me pudieras orientar.

Leave a Reply