12  Aprendizaje Automático para Series Temporales

Todos los modelos de los capítulos anteriores —ARIMA, SARIMA, VAR, GARCH, alisado exponencial— comparten una característica: son modelos paramétricos, con una forma funcional que el analista elige de antemano (lineal en el caso de un ARMA, exponencial en el de un GARCH) y unos pocos parámetros que se estiman a partir de los datos. Esa elección tiene una ventaja enorme —los modelos son interpretables, sus supuestos son explícitos y se pueden contrastar— pero también un coste: si la relación real entre el pasado y el futuro de la serie no es lineal, o tiene una forma que el analista no anticipó, el modelo paramétrico la representará mal por definición, por bien que se estimen sus parámetros. Este capítulo cierra el Bloque VI introduciendo el aprendizaje automático (machine learning) aplicado a la predicción de series temporales: métodos que aprenden la relación entre el pasado y el futuro directamente de los datos, sin imponer una forma funcional concreta. Veremos que esa flexibilidad no es gratuita —exige convertir la serie en una tabla de variables explicativas, entender arquitecturas nuevas como las redes neuronales recurrentes, y adaptar las técnicas al hecho de que los árboles de decisión, en particular, tienen una limitación seria para extrapolar tendencias—, y aplicaremos estas ideas, con honestidad sobre sus resultados, al IPC que nos ha acompañado desde el Capítulo 1.

12.1 Ingeniería de características para series temporales

Un modelo de aprendizaje automático genérico —un árbol de decisión, un conjunto de árboles, una red neuronal densa— no entiende, por sí mismo, qué es “el pasado” de una serie: espera una tabla de observaciones independientes, cada una con sus variables explicativas (features) y su variable objetivo. La ingeniería de características es el proceso de traducir una serie temporal a ese formato tabular, construyendo explícitamente las variables que, en un modelo ARIMA, la propia estructura autorregresiva se encargaba de captar de forma implícita:

  • Retardos (lags): el valor de la serie en periodos anteriores, \(y_{t-1}, y_{t-2}, \dots, y_{t-12}\), usados como variables explicativas del valor en \(t\). Es la forma más directa de darle al modelo la misma información que ya usa un AR: qué pasó recientemente.
  • Estadísticos móviles (rolling statistics): la media, la desviación típica o el máximo de una ventana reciente de observaciones (p. ej., la media de los últimos 3 meses), que resumen el nivel o la volatilidad reciente sin depender de un único retardo concreto.
  • Variables de calendario: mes, trimestre, día de la semana, o un índice temporal que capture la tendencia —el equivalente, en este marco, a la estacionalidad y la tendencia determinista de los Capítulos 1 y 6—.
Cuadro 12.1: Primeras filas de la tabla de caracteristicas construida sobre el IPC: retardos, media movil y variables de calendario.
Fecha IPC Retardo 1 Retardo 12 Media móvil 3 Mes Índice t
2003-01-01 60.90 61.15 58.72 60.98 1 13
2003-02-01 61.03 60.90 58.77 61.00 2 14
2003-03-01 61.45 61.03 59.25 61.02 3 15
2003-04-01 61.95 61.45 60.06 61.12 4 16
2003-05-01 61.89 61.95 60.27 61.47 5 17
2003-06-01 61.93 61.89 60.28 61.76 6 18

La Cuadro 12.1 muestra el resultado sobre el IPC: cada fila ya no es “el IPC en el mes \(t\)” a secas, sino un vector de variables explicativas —de dónde venía la serie, cuál era su nivel reciente, en qué mes del año estamos— más el valor a predecir. A partir de aquí, predecir el IPC es, formalmente, un problema de regresión igual que cualquier otro en estadística aplicada, y puede abordarse con cualquier algoritmo de aprendizaje supervisado.

12.2 Redes neuronales: del perceptrón a las redes recurrentes

12.2.1 La neurona artificial

El bloque elemental de una red neuronal es la neurona artificial: toma un vector de entradas \(x=(x_1,\dots,x_p)\) —en nuestro caso, los retardos y variables de calendario de la sección anterior—, las combina linealmente con unos pesos \(w=(w_1,\dots,w_p)\) y un sesgo \(b\), y pasa el resultado por una función de activación \(\phi\) que introduce no linealidad:

\[\hat y = \phi\!\left(\sum_{i=1}^p w_i x_i + b\right).\]

Sin la función de activación, apilar varias neuronas en capas sucesivas seguiría siendo, matemáticamente, una combinación lineal de las entradas —una regresión lineal disfrazada—; es \(\phi\) quien permite que la red aproxime relaciones no lineales. Las funciones de activación más habituales son la sigmoide, \(\phi(z)=1/(1+e^{-z})\), que comprime cualquier valor real al intervalo \((0,1)\); la tangente hiperbólica, \(\phi(z)=\tanh(z)\), análoga pero con recorrido \((-1,1)\); y la ReLU (rectified linear unit), \(\phi(z)=\max(0,z)\), que simplemente anula las entradas negativas y dejaba pasar las positivas sin cambios, mucho más barata de calcular y, por eso, la más usada en redes profundas modernas. Una red neuronal densa (feedforward) apila varias capas de estas neuronas: la salida de una capa es la entrada de la siguiente, y los pesos de todas las conexiones se ajustan mediante retropropagación del error (backpropagation) para minimizar una función de pérdida sobre los datos de entrenamiento, el mismo principio de ajuste por mínimos cuadrados o máxima verosimilitud que venimos usando desde el Capítulo 3, solo que con un algoritmo numérico distinto por la complejidad del modelo.

Aplicada a una serie temporal con las variables de la Cuadro 12.1 como entrada, una red densa no deja de ser, conceptualmente, un AR no lineal: en lugar de \(y_t=\phi_1 y_{t-1}+\dots+\phi_p y_{t-p}+\varepsilon_t\) (Capítulo 3), se tiene \(y_t = \text{Red}(y_{t-1},\dots,y_{t-p}) + \varepsilon_t\), con “Red” una función no lineal flexible en lugar de una combinación lineal fija.

12.2.2 Redes neuronales recurrentes (RNN) y LSTM

Una red densa con retardos como entrada tiene una limitación: la “memoria” de la red está fijada de antemano por cuántos retardos se incluyan como variables —si se decide usar \(y_{t-1},\dots,y_{t-12}\), la red nunca podrá aprovechar información de \(y_{t-13}\)—. Las redes neuronales recurrentes (RNN) resuelven esto de otra manera: mantienen un estado oculto \(h_t\) que se actualiza en cada paso temporal combinando la entrada actual con el propio estado del paso anterior,

\[h_t = \phi\big(W_x\,x_t + W_h\,h_{t-1} + b\big),\]

de modo que \(h_t\) acumula, en principio, información de toda la historia de la serie, no solo de un número fijo de retardos, y los mismos pesos \(W_x,W_h\) se reutilizan en cada instante temporal (parameter sharing), en lugar de tener un conjunto de pesos distinto para cada retardo como en una red densa.

En la práctica, una RNN simple tiene dificultades para aprender dependencias que abarcan muchos periodos: al retropropagar el error a través de decenas de pasos temporales, el gradiente tiende a anularse o a explotar numéricamente —el llamado problema del gradiente desvaneciente—, lo que en la práctica le impide “recordar” información de mucho más allá de unos pocos periodos atrás. Hochreiter y Schmidhuber (1997) propusieron la arquitectura LSTM (Long Short-Term Memory, “memoria a largo y corto plazo”) para resolverlo, añadiendo una celda de memoria \(c_t\) y tres puertas (gates) que regulan, con valores entre 0 y 1, cuánta información se olvida, cuánta se incorpora y cuánta se deja salir:

\[\begin{aligned} f_t &= \sigma(W_f[h_{t-1},x_t]+b_f) &&\text{(puerta de olvido)}\\ i_t &= \sigma(W_i[h_{t-1},x_t]+b_i) &&\text{(puerta de entrada)}\\ o_t &= \sigma(W_o[h_{t-1},x_t]+b_o) &&\text{(puerta de salida)}\\ c_t &= f_t \odot c_{t-1} + i_t \odot \tanh(W_c[h_{t-1},x_t]+b_c) &&\text{(actualización de la celda)}\\ h_t &= o_t \odot \tanh(c_t) &&\text{(estado oculto)} \end{aligned}\]

donde \(\sigma\) es la función sigmoide, \([h_{t-1},x_t]\) denota la concatenación de ambos vectores, y \(\odot\) es el producto elemento a elemento. La puerta de olvido \(f_t\) decide qué parte de la memoria anterior \(c_{t-1}\) se descarta; la de entrada \(i_t\), cuánta información nueva se añade; la de salida \(o_t\), cuánta de la memoria actualizada se expone como estado oculto. Este diseño permite que el gradiente fluya por la celda de memoria \(c_t\) con muchas menos interferencias que en una RNN simple, y es la arquitectura que ha dominado la predicción de secuencias mediante deep learning durante más de dos décadas.

No entrenamos una LSTM sobre el IPC en este capítulo: hacerlo con garantías —arquitectura, número de capas, regularización, cientos de iteraciones de entrenamiento— requiere un motor de cómputo especializado (TensorFlow o PyTorch) que no forma parte del entorno de R de este manual, y que además, con una serie tan corta como el IPC (poco más de 280 observaciones mensuales), suele estar sobredimensionado: las redes profundas necesitan, típicamente, miles de observaciones para superar a los métodos más sencillos. Las dos secciones siguientes muestran alternativas de aprendizaje automático que sí aplicamos, con código y resultados reales, sobre el IPC.

12.3 Gradient boosting: XGBoost sobre el IPC

El gradient boosting construye un modelo de predicción combinando muchos árboles de decisión pequeños de forma secuencial: el primer árbol \(h_1\) se ajusta directamente a los datos; cada árbol siguiente \(h_m\) se ajusta, no a la variable original, sino al error que cometía el modelo hasta ese momento —de ahí “boosting”, potenciar un modelo débil de forma iterativa—, y se añade con un peso pequeño \(\nu\) (la tasa de aprendizaje) que evita que cada árbol individual domine el resultado:

\[F_m(x) = F_{m-1}(x) + \nu\, h_m(x).\]

XGBoost (Chen and Guestrin 2016) es la implementación de gradient boosting más usada en la práctica, por su velocidad y por el buen comportamiento de sus valores por defecto; ganó, o formó parte de la solución ganadora, en una proporción muy alta de las competiciones de predicción de la plataforma Kaggle durante varios años.

Un primer intento, aparentemente razonable, es entrenar XGBoost sobre el nivel del IPC, usando como variables explicativas los retardos 1 y 12 y las variables de calendario, reservando los últimos 12 meses para evaluar —el mismo tipo de partición del Capítulo 10, ahora con timetk::time_series_split()—. El resultado es malo: RECM = 2.55, EAM = 2.314, muy por encima de cualquiera de los modelos clásicos de los Capítulos 6 y 10. La causa se ve con claridad comparando rangos: el IPC de entrenamiento se mueve entre 60.9 y 99.7, pero las predicciones de XGBoost apenas oscilan entre 98.7 y 99 —prácticamente pegadas al límite superior del rango de entrenamiento—. No es un error de programación: es una limitación estructural de los árboles de decisión. Un árbol predice promediando la variable objetivo dentro de las observaciones de entrenamiento que caen en cada hoja; nunca puede predecir un valor fuera del rango que ha visto en el entrenamiento, porque no tiene ningún mecanismo para extrapolar una tendencia más allá de sus datos —a diferencia de un ARIMA, cuya ecuación sí extrapola una tendencia lineal o una raíz unitaria de forma explícita—. Makridakis, Spiliotis y Assimakopoulos (2018), tras la competición de predicción M4, documentan exactamente este patrón a gran escala: los métodos de aprendizaje automático puro, sin adaptaciones, tienden a rendir peor que los métodos estadísticos clásicos en series con tendencia marcada.

La solución es la misma que el Capítulo 4 ya enseñó para los modelos ARIMA: diferenciar la serie antes de modelizarla. Entrenamos XGBoost sobre \(\Delta\text{IPC}_t\) en lugar de sobre el nivel, con retardos de la propia diferencia como variables explicativas, y reconstruimos el nivel acumulando las predicciones de las diferencias a partir del último valor observado. El error mejora de forma sustancial: RECM = 1.544, EAM = 1.449, muy por debajo del intento anterior sobre el nivel. La razón es la misma diferenciación que estabiliza un ARIMA: \(\Delta\text{IPC}_t\) no tiene tendencia, así que el árbol ya no necesita extrapolar fuera de su rango de entrenamiento para predecir razonablemente bien.

12.4 Prophet

Prophet (Taylor and Letham 2018), desarrollado originalmente en Meta para automatizar la predicción de miles de series de negocio, plantea la serie como una suma de componentes interpretables, en el espíritu de la descomposición clásica del Capítulo 1 pero estimada de forma conjunta y con más flexibilidad:

\[y(t) = g(t) + s(t) + h(t) + \varepsilon(t),\]

donde \(g(t)\) es una tendencia lineal a trozos, con puntos de cambio de pendiente (changepoints) que el propio algoritmo detecta automáticamente en lugar de exigir que el analista los especifique a mano —una automatización de la idea de cambio estructural del Capítulo 4—; \(s(t)\) es la estacionalidad, modelizada con una serie de Fourier (una suma de senos y cosenos de distintas frecuencias) en lugar de con los coeficientes estacionales fijos de un SARIMA, lo que le permite adaptarse a patrones estacionales más suaves o cambiantes; \(h(t)\) recoge el efecto de festivos o eventos especiales conocidos de antemano; y \(\varepsilon(t)\) es el error, con el mismo papel que en cualquier otro modelo de este manual.

Figura 12.1: Descomposicion de Prophet sobre el IPC: tendencia (arriba) y componente estacional anual (abajo).

La tendencia estimada por Prophet reproduce la trayectoria general del IPC, incluidos los cambios de ritmo visibles a simple vista en la crisis financiera de 2008 y en el repunte inflacionario de 2021-2022; el componente estacional, en el panel inferior, tiene la misma forma —caída en enero, repunte hacia mitades de año— que ya encontramos con X-13ARIMA-SEATS en el Capítulo 6, una buena señal de que ambos métodos, pese a ser completamente distintos en su formulación, coinciden en lo esencial sobre los mismos datos.

12.5 Comparación con validación temporal: el ecosistema modeltime

El paquete modeltime, junto con timetk, ofrece un flujo de trabajo unificado para entrenar y comparar modelos de naturalezas distintas —estadísticos clásicos y de aprendizaje automático— con la misma interfaz, extendiendo aquí la validación temporal del Capítulo 11: time_series_split() reserva un tramo final para evaluación igual que hicimos a mano en capítulos anteriores, y modeltime_calibrate() junto con modeltime_accuracy() calculan las métricas de error sobre ese tramo para cualquier modelo, sin importar si procede de forecast, de prophet o de un workflow de tidymodels.

Cuadro 12.2: Comparacion de modelos sobre el IPC (12 meses reservados para evaluacion): ARIMA automatico, Prophet y XGBoost sobre la serie diferenciada.
Modelo EAM RECM EAMP (%)
ARIMA(1,1,1)(2,0,0)[12] 0.617 0.743 0.606
XGBOOST (sobre diferencias) 1.449 1.544 1.427
PROPHET 2.280 2.310 2.252

El ARIMA que modeltime selecciona automáticamente —, con una especificación distinta a la del Capítulo 6 porque el algoritmo de búsqueda no es idéntico, aunque la idea sea la misma— vuelve a ganar con claridad, igual que ocurría en la validación cruzada del Capítulo 11. Ni Prophet ni XGBoost, con la información disponible, consiguen superar a un modelo estadístico clásico bien especificado sobre esta serie concreta. Esto no es un argumento contra el aprendizaje automático en general —su ventaja comparativa aparece sobre todo cuando hay muchas series relacionadas sobre las que aprender patrones comunes (miles de referencias de un comercio minorista, por ejemplo, el escenario para el que Prophet fue diseñado originalmente), o cuando existen variables explicativas externas ricas que un ARIMA univariante no puede aprovechar—, sino la conclusión honesta de aplicar, sobre una única serie macroeconómica con apenas 280 observaciones, exactamente la misma metodología de evaluación rigurosa que el Capítulo 11 estableció: entrenar, reservar datos, medir el error fuera de muestra, y dejar que el número decida, no la reputación del método.

12.6 Ejercicios propuestos

  1. Añada un retardo adicional (ipc_lag2) a la tabla de características de la Sección 12.2 y compárela con la Cuadro 12.1. ¿Qué información nueva aporta frente a ipc_lag1?
  2. Sobre la serie diferenciada de la Sección 12.4, sustituya boost_tree() por un modelo lineal (linear_reg() |> set_engine("lm")) dentro del mismo workflow, manteniendo las mismas variables explicativas. Compare su RECM fuera de muestra con el de XGBoost.
  3. Ajuste Prophet con weekly.seasonality = TRUE sobre el IPC. ¿Tiene sentido económico una estacionalidad semanal en una serie mensual? ¿Qué le sugiere esto sobre la importancia de configurar correctamente la frecuencia esperada de un modelo automático?
  4. Repita la comparación de la Cuadro 12.2 reservando 24 meses en lugar de 12 para la evaluación. ¿Se mantiene el ARIMA como el modelo preferido?

12.7 Soluciones de los ejercicios

1. Retardo adicional ipc_lag2. La correlación de ipc con ipc_lag1 es 0.999 y con ipc_lag2 es 0.997: prácticamente idéntica, porque el IPC evoluciona con tanta persistencia mes a mes que el retardo 1 ya casi resume toda la información relevante de corto plazo —\(y_{t-2}\) aporta poco que \(y_{t-1}\) no aporte ya, exactamente el fenómeno de alta autocorrelación que llevó al SES del Capítulo 10 a un \(\alpha\) próximo a 1—. Añadir ipc_lag2 no debería, por tanto, mejorar mucho un modelo que ya incluye ipc_lag1.

2. Modelo lineal frente a XGBoost sobre la serie diferenciada. El RECM del modelo lineal es 0.659, frente a 1.544 de XGBoost sobre las mismas variables. El modelo lineal, mas sencillo, iguala o supera a XGBoost en este caso concreto — un recordatorio de que un algoritmo más flexible no garantiza mejor predicción cuando la relación subyacente (aquí, esencialmente autorregresiva y casi lineal tras diferenciar) no exige esa flexibilidad adicional.

3. Prophet con estacionalidad semanal en datos mensuales. Prophet SI ajusta un componente semanal aunque se le pida expresamente cuando se solicita weekly.seasonality = TRUE sobre datos mensuales. Económicamente no tiene sentido: con una sola observación por mes no existe ninguna noción de “día de la semana” en los datos, así que cualquier patrón semanal que el modelo intentara estimar sería puro artefacto numérico. La lección general es que la potencia de automatización de herramientas como Prophet no exime al analista de conocer la frecuencia y la naturaleza de sus datos: una mala configuración no siempre produce un error visible, a veces produce, en silencio, un componente sin ningún significado.

4. Comparación a 24 meses. El modelo preferido por RECM pasa a ser ARIMA(1,1,1)(2,0,0)[12]. El ARIMA mantiene su ventaja tambien a este horizonte mas largo, coherente con la advertencia central del Capítulo 11: ninguna comparación de modelos basada en una sola ventana de evaluación —sea de 12 o de 24 meses— debe tomarse como definitiva sin contrastarla con más de un horizonte.

El código de este capítulo está en scripts/T12_mST_Script_MachineLearning.R (guion teórico) y scripts/T12_mST_CP_Macro.R (caso práctico con el IPC).