Más sobre forecasting en: cienciadedatos.net
- Forecasting series temporales con machine learning
- Modelos ARIMA y SARIMAX
- Forecasting series temporales con gradient boosting: XGBoost, LightGBM y CatBoost
- Global Forecasting: Multi-series forecasting
- Forecasting de la demanda eléctrica con machine learning
- Forecasting con deep learning
- Forecasting con modelos fundacionales
- Forecasting de visitas a página web con machine learning
- Forecasting del precio de Bitcoin
- Forecasting probabilístico
- Forecasting de demanda intermitente
- Reducir el impacto del Covid en modelos de forecasting
- Modelar series temporales con tendencia utilizando modelos de árboles
Modelos fundacionales¶
Los modelos fundacionales (FMs) han desencadenado un cambio de paradigma fundamental en el pronóstico de series temporales, alejando el campo del modelado por conjunto de datos individual hacia el aprendizaje de representaciones generalizadas. Impulsados por los mismos avances arquitectónicos que potencian los Grandes Modelos de Lenguaje (LLMs), los FMs aportan capacidades de aprendizaje zero-shot y en contexto a los datos temporales.
En el contexto del pronóstico, un modelo fundacional es una red neuronal de enorme escala (típicamente basada en Transformers) que ha sido preentrenada en conjuntos de datos altamente diversos entre dominios, abarcando finanzas, meteorología, tráfico web, comercio minorista y más.
Modelos como AWS Chronos, Google TimesFM 2.5 y Salesforce Moirai plantean el pronóstico temporal como un problema de modelado de secuencias y procesan datos temporales ya sea como tokens discretos cuantizados (como en Chronos, que aplica cuantización escalar) o como embeddings de parches continuos (como en TimesFM y Moirai, que agrupan steps de tiempo consecutivos en parches de longitud fija antes de codificarlos). Habiendo ya internalizado los priors estructurales de millones de series durante el preentrenamiento, pueden inferir instantáneamente tendencias, estacionalidad y dinámicas complejas en datos completamente nuevos, eliminando la necesidad de actualizaciones de pesos específicas del dominio.
Modelos Fundacionales vs. Modelos de Machine Learning¶
Los modelos fundacionales y los modelos de machine learning tradicionales abordan el pronóstico de maneras fundamentalmente diferentes. Comprender estas distinciones es crucial para saber cuándo y cómo desplegar cada método.
Predicción Zero-Shot
Los modelos de machine learning requieren una fase de entrenamiento. Debes ajustar el modelo sobre tus datos históricos objetivo para que el algoritmo pueda aprender los pesos y parámetros óptimos para tu serie temporal específica. Los modelos fundacionales, sin embargo, son capaces de inferencia zero-shot. Dado que sus pesos altamente generalizados ya están congelados desde la masiva fase de preentrenamiento, pueden generar pronósticos precisos sobre tus datos de inmediato, aprovechando sus representaciones latentes preexistentes en lugar de aprender tu conjunto de datos desde cero.
El Rol del Método fit
Los modelos de machine learning deben ser entrenados: llamar a .fit() optimiza los parámetros internos del modelo minimizando una función de pérdida sobre tus datos históricos. Los modelos fundacionales, por el contrario, llegan preentrenados: sus pesos están fijos y nunca se actualizan. Llamar a .fit() en un modelo fundacional no es un paso de entrenamiento; simplemente almacena el contexto histórico (observaciones, frecuencia y cualquier factor de escala) necesario en el momento de inferencia. En algunas implementaciones, llamar a .fit() es completamente opcional antes de la predicción.
Ventana de Contexto vs. Lags Diseñados
Los modelos de machine learning dependen de características explícitamente diseñadas; requieren crear un conjunto de datos tabular donde los valores pasados se usan como columnas para predecir el objetivo. Los modelos fundacionales dependen de una ventana de contexto. Pasas directamente al modelo un fragmento secuencial sin procesar de datos históricos recientes (por ejemplo, las últimas 512 observaciones) en el momento de inferencia. El mecanismo de atención dentro del modelo decide automáticamente qué puntos de datos pasados son más relevantes.
En resumen, los modelos fundacionales representan un cambio de paradigma fundamental, reemplazando el pipeline tradicional de entrenar -> predecir por un enfoque de preentrenar -> (contexto + predecir). Mientras las principales instituciones de investigación con acceso a millones de series temporales diversas llevan a cabo la fase de preentrenamiento computacionalmente intensiva, los usuarios finales quedan completamente liberados del entrenamiento del modelo.
Sin embargo, en machine learning no existe nada gratuito. Saltarse la fase de entrenamiento supone una mayor carga durante la fase de inferencia. Dado que sus pesos están congelados, estos modelos no pueden adaptarse a tus datos mediante el entrenamiento. En cambio, se adaptan implícitamente en el momento de inferencia procesando el contexto histórico a través de su mecanismo de atención. Por lo tanto, cada predicción requiere ingerir y atender sobre una larga secuencia de observaciones sin procesar en tiempo real. En consecuencia, la principal desventaja del pronóstico zero-shot es que el proceso de inferencia es significativamente más lento, más costoso computacionalmente y requiere que tu pipeline de datos proporcione continuamente grandes cantidades de contexto histórico en tiempo de ejecución.
| - | Modelo ML | Modelo Fundacional |
|---|---|---|
| fit | Entrena el modelo, actualiza pesos | Almacena contexto y metadatos |
| predict | Usa pesos aprendidos | Procesa contexto vía atención |
| Datos requeridos en entrenamiento | Historial completo | No requeridos |
| Datos requeridos en predicción | Últimas lags observaciones | Ventana de contexto completa |
| Coste computacional | En entrenamiento | En inferencia |
✏️ Nota
Para más detalles sobre el pronóstico con modelos fundacionales, visita Forecasting: Principles and Practice, the Pythonic Way.
Modelos Fundacionales en skforecast¶
La integración de Skforecast se basa en dos capas. Primero, FoundationModel actúa como un wrapper unificado que adapta la API nativa de cada modelo (Chronos-2, TimesFM 2.5, Moirai-2, TabICL) detrás de una interfaz familiar de scikit-learn (fit, predict, get_params). Segundo, ForecasterFoundation envuelve dicho estimador para desbloquear el ecosistema completo de skforecast. Expone la misma interfaz que cualquier otro pronosticador de skforecast, lo que significa que los usuarios pueden utilizar backtesting, intervalos de predicción y soporte multi-series con exactamente el mismo código.
</div>
Modelos Fundacionales Soportados¶
| Model | Provider | GitHub | Documentation | Available model IDs | Backend | Default context length | Max context length | Max horizon | Point forecast | Covariate support (exog) | Cross learning | Install command |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Chronos | Amazon | GitHub | Docs | amazon/chronos-2 autogluon/chronos-2-small autogluon/chronos-2-synth |
PyTorch | 8192 | 8192 | No hard limit, set via steps |
Median (0.5 quantile) | Yes | Multi-series only |
pip install chronos-forecasting
|
| TimesFM | GitHub | Docs | google/timesfm-2.5-200m-pytorch | PyTorch | 512 | 16384 | 512 | Mean (dedicated output) | No | No |
pip install timesfm
|
|
| Moirai | Salesforce | GitHub | Docs | Salesforce/moirai-2.0-R-small | PyTorch | 2048 | 2048 | No hard limit, set via steps |
Median (0.5 quantile) | No | No |
pip install uni2ts
|
| TabICL | Soda-Inria | GitHub | Docs | soda-inria/tabicl | PyTorch | 4096 | 4096 | No hard limit, set via steps |
Mean (default) | Yes | No |
pip install tabicl[forecast]
|
| TabPFN-TS | Prior Labs | GitHub | Docs | priorlabs/tabpfn-ts | PyTorch | 32768 | No hard limit (~65536) | No hard limit, set via steps |
Median (default) | Yes | No |
pip install tabpfn-time-series
|
| T0 | The Forecasting Company | GitHub | Docs | theforecastingcompany/t0-alpha | PyTorch | 8192 | No hard limit | No hard limit, set via steps |
Median (0.5 quantile) | Yes | No |
pip install tfc-t0
|
| Nori | Synthefy | GitHub | Docs | Synthefy/Nori | PyTorch | 4096 | No hard limit | No hard limit, set via steps |
Mean (default) | Yes | No |
pip install synthefy-nori
|
| TS-ICL | EDF Lab | GitHub | Docs | taharnbl/TS-ICL | PyTorch | 4096 | No hard limit | No hard limit, set via steps |
Median (0.5) | Yes | No |
pip install tsicl
|
💡 Consejo
Los cuatro modelos funcionan en CPU. Sin embargo, se recomienda una GPU CUDA para una inferencia más rápida, especialmente con ventanas de contexto largas. El backend MPS también es detectado automáticamente por PyTorch y puede beneficiar a los usuarios de Apple Silicon.
Es importante tener en cuenta que la longitud del contexto afecta significativamente a la velocidad de inferencia. Los contextos más largos proporcionan a los modelos más información, pero aumentan el tiempo de procesamiento. Aunque estos modelos presumen de enormes capacidades de contexto, los contextos más cortos suelen lograr resultados similares mucho más rápido en la mayoría de los casos de uso.
Formatos de Datos de Entrada¶
ForecasterFoundation acepta varios formatos de datos tanto para la serie objetivo como para las variables exógenas.
Serie Objetivo (series)
El parámetro series en el método .fit() admite configuraciones tanto de serie única como de múltiples series (modelo global).
| Modo | Tipo de Dato Permitido | Descripción |
|---|---|---|
| Serie Única | pd.Series |
Una sola serie temporal con un índice con nombre. |
| Multi-Series (Wide) | pd.DataFrame |
Cada columna representa una serie temporal independiente. |
| Multi-Series (Long) | pd.DataFrame |
MultiIndex (Nivel 0: ID de serie, Nivel 1: DatetimeIndex). |
| Multi-Series (Dict) | dict[str, pd.Series] |
Las claves son identificadores de series, los valores son Series de pandas. |
💡 Consejo
Aunque los DataFrames en formato Long están soportados, se convierten internamente a diccionarios. Para un mejor rendimiento, pasa directamente un dict[str, pd.Series].
Variables Exógenas (exog)
Las variables exógenas deben estar alineadas con el índice de la serie objetivo. Actualmente, solo Chronos y TabICL admiten covariables (ver la tabla de Modelos Fundacionales Soportados). TimesFM 2.5 y Moirai-2 no aceptan variables exógenas.
| Modo | Tipo de Dato Permitido | Descripción |
|---|---|---|
| Serie Única | pd.Series o pd.DataFrame |
Alineado al índice de la serie objetivo. |
| Multi-Series (Dict) | dict[str, pd.Series | pd.DataFrame | None] |
Una entrada por serie. |
| Multi-Series (Broadcast) | pd.Series o pd.DataFrame |
Aplicado automáticamente a todas las series. |
| Multi-Series (Long) | pd.DataFrame |
MultiIndex (Nivel 0: ID de serie, Nivel 1: DatetimeIndex). |
Librerías y datos¶
# Librerías
# ==============================================================================
import pandas as pd
import time
import torch
import matplotlib.pyplot as plt
from skforecast.datasets import fetch_dataset
from skforecast.foundation import FoundationModel, ForecasterFoundation
from skforecast.model_selection import (
TimeSeriesFold,
backtesting_foundation,
bayesian_search_foundation
)
from skforecast.plot import set_dark_theme
color = '\033[1m\033[38;5;208m'
print(f"{color}versión de torch: {torch.__version__}")
print(f" Cuda disponible : {torch.cuda.is_available()}")
print(f" MPS disponible : {torch.backends.mps.is_available()}")
versión de torch: 2.8.0+cu128
Cuda disponible : True
MPS disponible : False
# Descarga de datos
# ==============================================================================
datos = fetch_dataset(name='vic_electricity')
# Agregando en intervalos de 1H
# ==============================================================================
# Se elimina la columna Date para evitar errores al agregar.
datos = datos.drop(columns="Date")
datos = (
datos
.resample(rule="h", closed="left", label="right")
.agg({
"Demand": "mean",
"Temperature": "mean",
"Holiday": "mean",
})
)
datos.head(3)
╭──────────────────────────── vic_electricity ─────────────────────────────╮ │ Description: │ │ Half-hourly electricity demand for Victoria, Australia │ │ │ │ Source: │ │ O'Hara-Wild M, Hyndman R, Wang E, Godahewa R (2022).tsibbledata: Diverse │ │ Datasets for 'tsibble'. https://tsibbledata.tidyverts.org/, │ │ https://github.com/tidyverts/tsibbledata/. │ │ https://tsibbledata.tidyverts.org/reference/vic_elec.html │ │ │ │ URL: │ │ https://raw.githubusercontent.com/skforecast/skforecast- │ │ datasets/main/data/vic_electricity.csv │ │ │ │ Shape: 52608 rows x 4 columns │ ╰──────────────────────────────────────────────────────────────────────────╯
| Demand | Temperature | Holiday | |
|---|---|---|---|
| Time | |||
| 2011-12-31 14:00:00 | 4323.095350 | 21.225 | 1.0 |
| 2011-12-31 15:00:00 | 3963.264688 | 20.625 | 1.0 |
| 2011-12-31 16:00:00 | 3950.913495 | 20.325 | 1.0 |
# División de datos en entrenamiento-test
# ==============================================================================
datos = datos.loc['2012-01-01 00:00:00':'2014-12-30 23:00:00', :].copy()
fin_entrenamiento = '2014-11-30 23:59:00'
datos_entrenamiento = datos.loc[: fin_entrenamiento, :].copy()
datos_test = datos.loc[fin_entrenamiento:, :].copy()
print(f"Fechas de entrenamiento: {datos_entrenamiento.index.min()} --- {datos_entrenamiento.index.max()} (n={len(datos_entrenamiento)})")
print(f"Fechas de test : {datos_test.index.min()} --- {datos_test.index.max()} (n={len(datos_test)})")
Fechas de entrenamiento: 2012-01-01 00:00:00 --- 2014-11-30 23:00:00 (n=25560) Fechas de test : 2014-12-01 00:00:00 --- 2014-12-30 23:00:00 (n=720)
Forecasting de una única serie¶
Se crea un ForecasterFoundation utilizando el modelo Chronos-2-small de Amazon.
# Crear ForecasterFoundation
# ==============================================================================
estimador = FoundationModel(model_id="autogluon/chronos-2-small", context_length=500)
pronosticador = ForecasterFoundation(estimator=estimador)
Cada adaptador acepta argumentos adicionales que controlan el comportamiento específico del modelo (por ejemplo, context_length, device_map, torch_dtype). Estos pueden pasarse directamente a través del constructor de FoundationModel.
Para la lista completa de parámetros disponibles, consulta la referencia de la API: ChronosAdapter, TimesFMAdapter, MoiraiAdapter, TabICLAdapter.
💡 Consejo
Aunque aquí se utiliza .fit() para almacenar el contexto histórico y los metadatos, no es estrictamente necesario. Los modelos fundacionales pueden generar pronósticos pasando el contexto directamente a .predict() mediante el parámetro context. Sin embargo, llamar a .fit() primero simplifica las llamadas posteriores a .predict(), .predict_interval() y .predict_quantiles().
# Entrenar ForecasterFoundation
# ==============================================================================
pronosticador.fit(
series = datos_entrenamiento["Demand"],
exog = datos_entrenamiento[["Temperature", "Holiday"]]
)
pronosticador
ForecasterFoundation
General Information
- Model ID: autogluon/chronos-2-small
- Context length: 500
- Window size: 500
- Series names: Demand
- Exogenous included: True
- Creation date: 2026-07-28 12:59:21
- Last fit date: 2026-07-28 12:59:21
- Skforecast version: 0.24.0
- Python version: 3.13.14
- Forecaster id: None
Exogenous Variables
Temperature, Holiday
Training Information
- Context range: 'Demand': ['2012-01-01 00:00:00', '2014-11-30 23:00:00']
- Training index type: DatetimeIndex
- Training index frequency: h
Model Parameters
- cross_learning: False
- context_length: 500
- device_map: auto
- torch_dtype: None
- predict_kwargs: None
Se pueden usar tres métodos para predecir los próximos $n$ steps: predict(), predict_interval() y predict_quantiles(). Todos estos métodos permiten pasar context y context_exog para reemplazar el contexto histórico utilizado por el modelo subyacente al generar predicciones.
# Predicciones: pronóstico puntual
# ==============================================================================
steps = 24
predicciones = pronosticador.predict(
steps = steps,
exog = datos_test[["Temperature", "Holiday"]]
)
predicciones.head(3)
Loading weights: 0%| | 0/92 [00:00<?, ?it/s]
| level | pred | |
|---|---|---|
| 2014-12-01 00:00:00 | Demand | 5527.678711 |
| 2014-12-01 01:00:00 | Demand | 5511.500977 |
| 2014-12-01 02:00:00 | Demand | 5457.791992 |
# Predicciones: intervalos
# ==============================================================================
predicciones_intervalos = pronosticador.predict_interval(
steps = steps,
exog = datos_test[["Temperature", "Holiday"]],
interval = [0.1, 0.9], # intervalo de predicción al 80%
)
predicciones_intervalos.head(3)
| level | pred | lower_bound | upper_bound | |
|---|---|---|---|---|
| 2014-12-01 00:00:00 | Demand | 5527.678711 | 5372.815918 | 5689.793457 |
| 2014-12-01 01:00:00 | Demand | 5511.500977 | 5318.045410 | 5733.492188 |
| 2014-12-01 02:00:00 | Demand | 5457.791992 | 5241.040527 | 5717.424805 |
# Backtesting
# ==============================================================================
cv = TimeSeriesFold(
steps = 24,
initial_train_size = len(datos.loc[:fin_entrenamiento]),
refit = False
)
inicio = time.perf_counter()
metricas_chronos, predicciones_backtest = backtesting_foundation(
forecaster = pronosticador,
series = datos['Demand'],
exog = datos[["Temperature", "Holiday"]],
cv = cv,
metric = 'mean_absolute_error',
suppress_warnings = True
)
tiempo_transcurrido_chronos = time.perf_counter() - inicio
print(f"Backtesting completado en {tiempo_transcurrido_chronos:.4f} segundos.")
print("Métricas de backtesting")
display(metricas_chronos)
print("")
print("Predicciones del backtesting")
predicciones_backtest.head(4)
0%| | 0/30 [00:00<?, ?it/s]
Backtesting completado en 0.7737 segundos. Métricas de backtesting
| mean_absolute_error | |
|---|---|
| 0 | 171.266953 |
Predicciones del backtesting
| level | fold | pred | |
|---|---|---|---|
| 2014-12-01 00:00:00 | Demand | 0 | 5527.678711 |
| 2014-12-01 01:00:00 | Demand | 0 | 5511.500977 |
| 2014-12-01 02:00:00 | Demand | 0 | 5457.791992 |
| 2014-12-01 03:00:00 | Demand | 0 | 5402.819336 |
# Graficar predicciones
# ==============================================================================
set_dark_theme()
fig, ax = plt.subplots(figsize=(7, 3))
datos_test['Demand'].plot(ax=ax, label='test')
predicciones_backtest['pred'].plot(ax=ax, label='predicciones')
ax.legend();
Múltiples series (modelo global)¶
La clase ForecasterFoundation permite modelar y pronosticar múltiples series con un único modelo.
# Datos
# ==============================================================================
datos_multiseries = fetch_dataset(name="items_sales")
display(datos_multiseries.head(3))
╭─────────────────────── items_sales ───────────────────────╮ │ Description: │ │ Simulated time series for the sales of 3 different items. │ │ │ │ Source: │ │ Simulated data. │ │ │ │ URL: │ │ https://raw.githubusercontent.com/skforecast/skforecast- │ │ datasets/main/data/simulated_items_sales.csv │ │ │ │ Shape: 1097 rows x 3 columns │ ╰───────────────────────────────────────────────────────────╯
| item_1 | item_2 | item_3 | |
|---|---|---|---|
| date | |||
| 2012-01-01 | 8.253175 | 21.047727 | 19.429739 |
| 2012-01-02 | 22.777826 | 26.578125 | 28.009863 |
| 2012-01-03 | 27.549099 | 31.751042 | 32.078922 |
# División de datos en entrenamiento-test
# ==============================================================================
fin_entrenamiento = '2014-07-15 23:59:00'
datos_multiseries_entrenamiento = datos_multiseries.loc[:fin_entrenamiento, :]
datos_multiseries_test = datos_multiseries.loc[fin_entrenamiento:, :]
# Graficar series temporales
# ==============================================================================
set_dark_theme()
fig, ejes = plt.subplots(nrows=3, ncols=1, figsize=(7, 5), sharex=True)
for i, columna in enumerate(datos_multiseries.columns):
datos_multiseries_entrenamiento[columna].plot(ax=ejes[i], label='entrenamiento')
datos_multiseries_test[columna].plot(ax=ejes[i], label='test')
ejes[i].set_title(columna)
ejes[i].set_ylabel('ventas')
ejes[i].set_xlabel('')
ejes[i].legend(loc='upper left')
fig.tight_layout()
plt.show();
En este ejemplo, en lugar de llamar a fit(), el contexto se pasa directamente al método predict().
# Crear y entrenar ForecasterFoundation
# ==============================================================================
estimador = FoundationModel(model_id = "autogluon/chronos-2-small", context_length=500)
pronosticador = ForecasterFoundation(estimator = estimador)
# fit() es opcional; el contexto se pasa directamente a predict()
# pronosticador.fit(series=datos_multiseries_entrenamiento)
# Predicciones para todas las series (niveles)
# ==============================================================================
steps = len(datos_multiseries_test)
predicciones_articulos = pronosticador.predict(
steps = steps,
levels = None, # Se predicen todos los niveles
context = datos_multiseries_entrenamiento
)
predicciones_articulos.head()
╭────────────────────────────────── InputTypeWarning ──────────────────────────────────╮ │ Passing a DataFrame (either wide or long format) as `series` requires additional │ │ internal transformations, which can increase computational time. It is recommended │ │ to use a dictionary of pandas Series instead. For more details, see: │ │ https://skforecast.org/latest/user_guides/independent-multi-time-series-forecasting. │ │ html#input-data │ │ │ │ Category : skforecast.exceptions.InputTypeWarning │ │ Location : │ │ c:\Users\Joaquin\miniconda3\envs\skforecast_24_py13\Lib\site-packages\skforecast\uti │ │ ls\utils.py:3388 │ │ Suppress : warnings.simplefilter('ignore', category=InputTypeWarning) │ ╰──────────────────────────────────────────────────────────────────────────────────────╯
Loading weights: 0%| | 0/92 [00:00<?, ?it/s]
| level | pred | |
|---|---|---|
| 2014-07-16 | item_1 | 25.523064 |
| 2014-07-16 | item_2 | 10.456666 |
| 2014-07-16 | item_3 | 11.862236 |
| 2014-07-17 | item_1 | 25.296782 |
| 2014-07-17 | item_2 | 10.701235 |
# Graficar predicciones
# ==============================================================================
set_dark_theme()
fig, ejes = plt.subplots(nrows=3, ncols=1, figsize=(7, 5), sharex=True)
for i, columna in enumerate(datos_multiseries.columns):
datos_multiseries_entrenamiento[columna].plot(ax=ejes[i], label='entrenamiento')
datos_multiseries_test[columna].plot(ax=ejes[i], label='test')
predicciones_articulos.query(f"level == '{columna}'").plot(
ax=ejes[i], label='predicciones', color='white'
)
ejes[i].set_title(columna)
ejes[i].set_ylabel('ventas')
ejes[i].set_xlabel('')
ejes[i].legend(loc='upper left')
fig.tight_layout()
plt.show();
# Predicciones de intervalo para item_1 e item_2
# ==============================================================================
predicciones_intervalos = pronosticador.predict_interval(
steps = 24,
levels = ['item_1', 'item_2'],
context = datos_multiseries_entrenamiento,
interval = [0.1, 0.9], # intervalo de predicción al 80%
)
predicciones_intervalos.head()
╭────────────────────────────────── InputTypeWarning ──────────────────────────────────╮ │ Passing a DataFrame (either wide or long format) as `series` requires additional │ │ internal transformations, which can increase computational time. It is recommended │ │ to use a dictionary of pandas Series instead. For more details, see: │ │ https://skforecast.org/latest/user_guides/independent-multi-time-series-forecasting. │ │ html#input-data │ │ │ │ Category : skforecast.exceptions.InputTypeWarning │ │ Location : │ │ c:\Users\Joaquin\miniconda3\envs\skforecast_24_py13\Lib\site-packages\skforecast\uti │ │ ls\utils.py:3388 │ │ Suppress : warnings.simplefilter('ignore', category=InputTypeWarning) │ ╰──────────────────────────────────────────────────────────────────────────────────────╯
| level | pred | lower_bound | upper_bound | |
|---|---|---|---|---|
| 2014-07-16 | item_1 | 25.464174 | 24.582005 | 26.430853 |
| 2014-07-16 | item_2 | 10.649370 | 8.679634 | 13.302807 |
| 2014-07-17 | item_1 | 25.270247 | 24.255964 | 26.327969 |
| 2014-07-17 | item_2 | 10.834244 | 8.717453 | 13.826941 |
| 2014-07-18 | item_1 | 25.175861 | 24.079086 | 26.286255 |
Impacto de la longitud de contexto¶
Dado que los modelos fundacionales de pronóstico son altamente generalizados, carecen de conocimiento intrínseco de tu conjunto de datos específico. Para compensar, dependen de una "ventana de contexto", un período específico de datos históricos recientes, para adaptarse a tu escenario único en tiempo real. Este contexto actúa como la memoria a corto plazo del modelo, permitiéndole calcular la trayectoria actual de tus datos e identificar si la serie tiene tendencia al alza, está acelerando o aplanándose.
La longitud de esta ventana de contexto es absolutamente crítica para capturar la estacionalidad y los eventos recurrentes. Para predecir con precisión un patrón, como un pico semanal en ventas o un ciclo anual, el modelo debe observar efectivamente ese patrón dentro del historial proporcionado. Por ejemplo, si tus datos tienen una estacionalidad de 365 días, proporcionar 400 días de contexto permite al modelo reconocer y proyectar el ciclo, mientras que una ventana de 30 días haría que el modelo perdiera el patrón por completo, resultando en un pronóstico plano o inexacto.
Sin embargo, aumentar la longitud del contexto para mejorar la precisión introduce un compromiso computacional significativo. Dado que la mayoría de los modelos fundacionales están construidos sobre arquitecturas Transformer, la complejidad computacional de su mecanismo de atención escala cuadráticamente ($O(N^2)$) con la longitud de la secuencia de entrada. En consecuencia, duplicar la ventana de contexto puede cuadruplicar la memoria y el procesamiento requeridos. Este crecimiento cuadrático significa que llevar las longitudes de contexto a su máximo teórico a menudo produce ganancias de precisión marginales con costos computacionales rápidamente crecientes.
En definitiva, utilizar eficazmente los modelos fundacionales requiere evaluar cuidadosamente este compromiso. La mejor práctica es analizar el tamaño del contexto y seleccionar la ventana más corta posible que aún logre un alto rendimiento predictivo. Encontrar este equilibrio garantiza pronósticos precisos y conscientes de patrones, evitando el desperdicio innecesario de recursos computacionales y ancho de banda de transferencia de datos.
El siguiente código muestra cómo evaluar el impacto de la longitud del contexto en la precisión predictiva y el tiempo de inferencia de un modelo base. El ejemplo utiliza el modelo Amazon Chronos-2-small, pero el mismo enfoque puede aplicarse a cualquier otro modelo base compatible con skforecast.
# Influencia de la longitud del contexto en la precisión y la velocidad
# ==============================================================================
model_id = 'autogluon/chronos-2-small'
context_lengths = [100, 500, 1000, 5000]
model_ids_allow_exog = {
'autogluon/chronos-2-small', 'soda-inria/tabicl',
'priorlabs/tabpfn-ts', 'theforecastingcompany/t0-alpha',
'Synthefy/Nori', 'taharnbl/TS-ICL'
}
if model_id in model_ids_allow_exog:
exog = datos[["Temperature", "Holiday"]]
else:
exog = None
cv = TimeSeriesFold(
steps = 24,
initial_train_size = len(datos.loc[:fin_entrenamiento]),
refit = False
)
results_metrics = []
results_elapsed_time = []
for context_length in context_lengths:
estimator = FoundationModel(model_id=model_id, context_length=context_length)
forecaster = ForecasterFoundation(estimator=estimator)
start = time.perf_counter()
metrics, predicciones_backtest = backtesting_foundation(
forecaster = forecaster,
series = datos['Demand'],
exog = exog,
cv = cv,
metric = 'mean_absolute_error',
suppress_warnings = True,
show_progress = False
)
elapsed_time = time.perf_counter() - start
results_metrics.append(metrics.at[0, 'mean_absolute_error'])
results_elapsed_time.append(elapsed_time)
results = pd.DataFrame(
{
'metric': results_metrics,
'elapsed_time': results_elapsed_time
},
index=context_lengths
)
results
Loading weights: 0%| | 0/92 [00:00<?, ?it/s]
Loading weights: 0%| | 0/92 [00:00<?, ?it/s]
Loading weights: 0%| | 0/92 [00:00<?, ?it/s]
Loading weights: 0%| | 0/92 [00:00<?, ?it/s]
| metric | elapsed_time | |
|---|---|---|
| 100 | 213.935341 | 4.489171 |
| 500 | 132.459974 | 4.577228 |
| 1000 | 127.277556 | 4.782205 |
| 5000 | 123.635550 | 4.924833 |
# Plot
# ==============================================================================
set_dark_theme()
fig, ax = plt.subplots(figsize=(6, 2.5))
results['metric'].plot(ax = ax, marker = 'o')
ax.set_title("Prediction error vs context length")
ax.set_xlabel("context length")
ax.set_ylabel("mean absolute error")
fig, ax = plt.subplots(figsize=(6, 2.5))
results['elapsed_time'].plot(ax = ax, marker = 'o')
ax.set_title("Elapsed time vs context length")
ax.set_xlabel("context length")
ax.set_ylabel("Elapsed time");
El ejemplo anterior era ilustrativo y mostraba cómo la longitud del contexto puede afectar a los resultados, pero no es la mejor estrategia de búsqueda. Skforecast permite optimizar la longitud del contexto junto con otros parámetros de FoundationModel mediante la búsqueda bayesiana.
# Dividir los datos en conjuntos de entrenamiento, validación y prueba
# ==============================================================================
datos = datos.loc['2012-01-01 00:00:00':'2014-12-30 23:00:00', :].copy()
end_train = '2014-10-30 23:59:00'
end_validation = '2014-11-30 23:59:00'
datos_train = datos.loc[: end_train, :].copy()
datos_val = datos.loc[end_train : end_validation, :].copy()
datos_test = datos.loc[end_validation:, :].copy()
print(f"Train dates: {datos_train.index.min()} --- {datos_train.index.max()} (n={len(datos_train)})")
print(f"Train val : {datos_val.index.min()} --- {datos_val.index.max()} (n={len(datos_val)})")
print(f"Test dates : {datos_test.index.min()} --- {datos_test.index.max()} (n={len(datos_test)})")
Train dates: 2012-01-01 00:00:00 --- 2014-10-30 23:00:00 (n=24816) Train val : 2014-10-31 00:00:00 --- 2014-11-30 23:00:00 (n=744) Test dates : 2014-12-01 00:00:00 --- 2014-12-30 23:00:00 (n=720)
# Bayesian search
# ==============================================================================
cv = TimeSeriesFold(
steps = 24,
initial_train_size = len(datos.loc[:end_train]),
refit = False
)
estimator = FoundationModel(model_id="autogluon/chronos-2-small", context_length=150)
forecaster = ForecasterFoundation(estimator=estimator)
# Search space
def search_space(trial):
search_space = {
'context_length': trial.suggest_int('context_length', 100, 5000, step=100),
}
return search_space
results, study = bayesian_search_foundation(
forecaster = forecaster,
series = datos.loc[:end_validation, 'Demand'],
exog = datos.loc[:end_validation, ['Temperature', 'Holiday']],
search_space = search_space,
cv = cv,
metric = 'mean_absolute_error',
n_trials = 20
)
results
0%| | 0/20 [00:00<?, ?it/s]
Loading weights: 0%| | 0/92 [00:00<?, ?it/s]
| trial_number | levels | params | mean_absolute_error | context_length | |
|---|---|---|---|---|---|
| 0 | 4 | [Demand] | {'context_length': 3600} | 139.470447 | 3600 |
| 1 | 6 | [Demand] | {'context_length': 5000} | 139.935649 | 5000 |
| 2 | 12 | [Demand] | {'context_length': 5000} | 139.935649 | 5000 |
| 3 | 11 | [Demand] | {'context_length': 5000} | 139.935649 | 5000 |
| 4 | 18 | [Demand] | {'context_length': 5000} | 139.935649 | 5000 |
| 5 | 13 | [Demand] | {'context_length': 4000} | 140.103792 | 4000 |
| 6 | 14 | [Demand] | {'context_length': 4100} | 140.149167 | 4100 |
| 7 | 10 | [Demand] | {'context_length': 4300} | 140.168050 | 4300 |
| 8 | 16 | [Demand] | {'context_length': 4900} | 140.202668 | 4900 |
| 9 | 2 | [Demand] | {'context_length': 1200} | 142.167335 | 1200 |
| 10 | 5 | [Demand] | {'context_length': 2200} | 142.248174 | 2200 |
| 11 | 1 | [Demand] | {'context_length': 1500} | 142.699084 | 1500 |
| 12 | 9 | [Demand] | {'context_length': 2000} | 143.176399 | 2000 |
| 13 | 3 | [Demand] | {'context_length': 2800} | 143.253429 | 2800 |
| 14 | 7 | [Demand] | {'context_length': 3500} | 143.339500 | 3500 |
| 15 | 0 | [Demand] | {'context_length': 3500} | 143.339500 | 3500 |
| 16 | 17 | [Demand] | {'context_length': 3500} | 143.339500 | 3500 |
| 17 | 19 | [Demand] | {'context_length': 3500} | 143.339500 | 3500 |
| 18 | 8 | [Demand] | {'context_length': 2500} | 143.521964 | 2500 |
| 19 | 15 | [Demand] | {'context_length': 100} | 252.934367 | 100 |
Otros modelos fundacionales¶
Los ejemplos anteriores utilizan el modelo Amazon Chronos, pero la misma estructura de código se aplica a cualquier otro modelo fundacional compatible con skforecast. Las siguientes subsecciones demuestran que el pipeline es idéntico independientemente del modelo subyacente; solo cambia el model_id. Para utilizar un modelo diferente, simplemente pásalo al instanciar el wrapper FoundationModel.
TimesFM 2.5¶
Se crea un ForecasterFoundation utilizando el modelo TimesFM-2.5-200m de Google.
# Crear ForecasterFoundation
# ==============================================================================
estimador = FoundationModel(model_id="google/timesfm-2.5-200m-pytorch", context_length=500)
pronosticador = ForecasterFoundation(estimator = estimador)
# Entrenar ForecasterFoundation
# ==============================================================================
pronosticador.fit(series=datos_entrenamiento["Demand"])
# Predicciones: pronóstico puntual
# ==============================================================================
steps = 24
predicciones = pronosticador.predict(steps=steps)
predicciones.head(3)
| level | pred | |
|---|---|---|
| 2014-12-01 00:00:00 | Demand | 5658.415039 |
| 2014-12-01 01:00:00 | Demand | 5671.861816 |
| 2014-12-01 02:00:00 | Demand | 5747.938477 |
# Predicciones: intervalos
# ==============================================================================
predicciones_intervalos = pronosticador.predict_interval(
steps = steps,
interval = [0.1, 0.9], # intervalo de predicción al 80%
)
predicciones_intervalos.head(3)
| level | pred | lower_bound | upper_bound | |
|---|---|---|---|---|
| 2014-12-01 00:00:00 | Demand | 5658.415039 | 5541.004883 | 5790.344238 |
| 2014-12-01 01:00:00 | Demand | 5671.861816 | 5470.189453 | 5899.113281 |
| 2014-12-01 02:00:00 | Demand | 5747.938477 | 5450.534180 | 6064.879883 |
# Backtesting
# ==============================================================================
cv = TimeSeriesFold(
steps = 24,
initial_train_size = len(datos.loc[:fin_entrenamiento]),
refit = False
)
inicio = time.perf_counter()
metricas_timesfm, predicciones_backtest = backtesting_foundation(
forecaster = pronosticador,
series = datos['Demand'],
cv = cv,
metric = 'mean_absolute_error',
suppress_warnings = True
)
tiempo_transcurrido_timesfm = time.perf_counter() - inicio
print(f"Backtesting completado en {tiempo_transcurrido_timesfm:.4f} segundos.")
print("Métricas de backtesting")
display(metricas_timesfm)
print("")
print("Predicciones del backtesting")
predicciones_backtest.head(4)
0%| | 0/168 [00:00<?, ?it/s]
Backtesting completado en 24.8245 segundos. Métricas de backtesting
| mean_absolute_error | |
|---|---|
| 0 | 160.357018 |
Predicciones del backtesting
| level | fold | pred | |
|---|---|---|---|
| 2014-07-16 00:00:00 | Demand | 0 | 6189.843750 |
| 2014-07-16 01:00:00 | Demand | 0 | 5988.112793 |
| 2014-07-16 02:00:00 | Demand | 0 | 5830.692383 |
| 2014-07-16 03:00:00 | Demand | 0 | 5696.288086 |
Moirai¶
Se crea un ForecasterFoundation utilizando el modelo Moirai-2.0-R-small de Salesforce.
# Crear ForecasterFoundation
# ==============================================================================
estimador = FoundationModel(model_id="Salesforce/moirai-2.0-R-small", context_length=500)
pronosticador = ForecasterFoundation(estimator=estimador)
# Entrenar ForecasterFoundation
# ==============================================================================
pronosticador.fit(series=datos_entrenamiento["Demand"])
# Predicciones: pronóstico puntual
# ==============================================================================
steps = 24
predicciones = pronosticador.predict(steps=steps)
predicciones.head(3)
# Predicciones: intervalos
# ==============================================================================
predicciones_intervalos = pronosticador.predict_interval(
steps = steps,
interval = [0.1, 0.9], # intervalo de predicción al 80%
)
predicciones_intervalos.head(3)
| level | pred | lower_bound | upper_bound | |
|---|---|---|---|---|
| 2014-12-01 00:00:00 | Demand | 5731.725098 | 5517.737793 | 5940.646484 |
| 2014-12-01 01:00:00 | Demand | 5870.827148 | 5548.743164 | 6176.801270 |
| 2014-12-01 02:00:00 | Demand | 5959.207031 | 5599.376953 | 6323.206055 |
# Backtesting
# ==============================================================================
cv = TimeSeriesFold(
steps = 24,
initial_train_size = len(datos.loc[:fin_entrenamiento]),
refit = False
)
inicio = time.perf_counter()
metricas_moirai, predicciones_backtest = backtesting_foundation(
forecaster = pronosticador,
series = datos['Demand'],
cv = cv,
metric = 'mean_absolute_error',
suppress_warnings = True
)
tiempo_transcurrido_moirai = time.perf_counter() - inicio
print(f"Backtesting completado en {tiempo_transcurrido_moirai:.4f} segundos.")
print("Métricas de backtesting")
display(metricas_moirai)
print("")
print("Predicciones del backtesting")
predicciones_backtest.head(4)
0%| | 0/168 [00:00<?, ?it/s]
Backtesting completed in 9.3907 seconds. Backtest metrics
| mean_absolute_error | |
|---|---|
| 0 | 161.691106 |
Backtest predictions
| level | fold | pred | |
|---|---|---|---|
| 2014-07-16 00:00:00 | Demand | 0 | 6222.097656 |
| 2014-07-16 01:00:00 | Demand | 0 | 6114.366699 |
| 2014-07-16 02:00:00 | Demand | 0 | 5969.839844 |
| 2014-07-16 03:00:00 | Demand | 0 | 5920.479492 |
TabICL¶
Se crea un ForecasterFoundation utilizando el modelo TabICL de Soda-Inria.
# Crear ForecasterFoundation
# ==============================================================================
estimador = FoundationModel(model_id="soda-inria/tabicl", context_length=500)
pronosticador = ForecasterFoundation(estimator=estimador)
# Entrenar ForecasterFoundation
# ==============================================================================
pronosticador.fit(
series = datos_entrenamiento["Demand"],
exog = datos_entrenamiento[["Temperature", "Holiday"]]
)
# Predicciones: pronóstico puntual
# ==============================================================================
steps = 24
predicciones = pronosticador.predict(
steps = steps,
exog = datos_test[["Temperature", "Holiday"]]
)
predicciones.head(3)
| level | pred | |
|---|---|---|
| 2014-12-01 00:00:00 | Demand | 5590.919922 |
| 2014-12-01 01:00:00 | Demand | 5662.485840 |
| 2014-12-01 02:00:00 | Demand | 5661.672852 |
# Predicciones: intervalos
# ==============================================================================
predicciones_intervalos = pronosticador.predict_interval(
steps = steps,
exog = datos_test[["Temperature", "Holiday"]],
interval = [0.1, 0.9], # intervalo de predicción al 80%
)
predicciones_intervalos.head(3)
| level | pred | lower_bound | upper_bound | |
|---|---|---|---|---|
| 2014-12-01 00:00:00 | Demand | 5617.177246 | 5159.889160 | 5952.954590 |
| 2014-12-01 01:00:00 | Demand | 5678.132812 | 5215.843262 | 6059.710938 |
| 2014-12-01 02:00:00 | Demand | 5677.454590 | 5131.910156 | 6149.853027 |
# Backtesting
# ==============================================================================
cv = TimeSeriesFold(
steps = 24,
initial_train_size = len(datos.loc[:fin_entrenamiento]),
refit = False
)
inicio = time.perf_counter()
metricas_tabicl, predicciones_backtest = backtesting_foundation(
forecaster = pronosticador,
series = datos['Demand'],
exog = datos[["Temperature", "Holiday"]],
cv = cv,
metric = 'mean_absolute_error',
suppress_warnings = True
)
tiempo_transcurrido_tabicl = time.perf_counter() - inicio
print(f"Backtesting completado en {tiempo_transcurrido_tabicl:.4f} segundos.")
print("Métricas de backtesting")
display(metricas_tabicl)
print("")
print("Predicciones del backtesting")
predicciones_backtest.head(4)
0%| | 0/168 [00:00<?, ?it/s]
Backtesting completado en 157.4076 segundos. Métricas de backtesting
| mean_absolute_error | |
|---|---|
| 0 | 170.101609 |
Predicciones del backtesting
| level | fold | pred | |
|---|---|---|---|
| 2014-07-16 00:00:00 | Demand | 0 | 6371.091797 |
| 2014-07-16 01:00:00 | Demand | 0 | 6311.226562 |
| 2014-07-16 02:00:00 | Demand | 0 | 6289.089355 |
| 2014-07-16 03:00:00 | Demand | 0 | 6268.155762 |
TabPFN-TS¶
Un ForecasterFoundation se crea utilizando el modelo TabPFN-TS de Prior Labs. TabPFN-TS requiere una clave API gratuita de Prior Labs para ejecutar la inferencia. Puedes obtenerla registrándote en https://priorlabs.ai. Por defecto, la inferencia se ejecuta de forma local (mode='local', CUDA > MPS > CPU); pasa mode='client' para utilizar en su lugar la API en la nube de Prior Labs (no requiere GPU, necesita una clave API).
# Crear ForecasterFoundation
# ==============================================================================
estimator = FoundationModel(model_id="priorlabs/tabpfn-ts", context_length=500)
forecaster = ForecasterFoundation(estimator=estimator)
# Entrenar ForecasterFoundation
# ==============================================================================
forecaster.fit(
series = datos_entrenamiento["Demand"],
exog = datos_entrenamiento[["Temperature", "Holiday"]]
)
# Predicciones: pronóstico puntual
# ==============================================================================
steps = 24
predictions = forecaster.predict(
steps = steps,
exog = datos_test[["Temperature", "Holiday"]]
)
predictions.head(3)
| level | pred | |
|---|---|---|
| 2014-12-01 00:00:00 | Demand | 5530.493652 |
| 2014-12-01 01:00:00 | Demand | 5536.577148 |
| 2014-12-01 02:00:00 | Demand | 5492.600586 |
# Predicciones: intervalos
# ==============================================================================
predictions_intervals = forecaster.predict_interval(
steps = steps,
exog = datos_test[["Temperature", "Holiday"]],
interval = [0.1, 0.9], # 80% prediction interval
)
predictions_intervals.head(3)
| level | pred | lower_bound | upper_bound | |
|---|---|---|---|---|
| 2014-12-01 00:00:00 | Demand | 5530.493652 | 5016.985840 | 5742.127441 |
| 2014-12-01 01:00:00 | Demand | 5536.577148 | 5077.645020 | 5806.521973 |
| 2014-12-01 02:00:00 | Demand | 5492.600586 | 4959.099121 | 5818.618164 |
# Backtesting
# ==============================================================================
cv = TimeSeriesFold(
steps = 24,
initial_train_size = len(datos.loc[:fin_entrenamiento]),
refit = False
)
start = time.perf_counter()
metricas_tabpfn, predicciones_backtest = backtesting_foundation(
forecaster = forecaster,
series = datos['Demand'],
exog = datos[["Temperature", "Holiday"]],
cv = cv,
metric = 'mean_absolute_error',
suppress_warnings = True
)
tiempo_transcurrido_tabpfn = time.perf_counter() - inicio
print(f"Backtesting completado en {tiempo_transcurrido_tabpfn:.4f} segundos.")
print("Métricas de backtesting")
display(metricas_tabpfn)
print("")
print("Predicciones del backtesting")
predicciones_backtest.head(4)
0%| | 0/168 [00:00<?, ?it/s]
Backtesting completado en 408.7843 segundos. Métricas de backtesting
| mean_absolute_error | |
|---|---|
| 0 | 159.175397 |
Predicciones del backtesting
| level | fold | pred | |
|---|---|---|---|
| 2014-07-16 00:00:00 | Demand | 0 | 6208.879395 |
| 2014-07-16 01:00:00 | Demand | 0 | 6112.437500 |
| 2014-07-16 02:00:00 | Demand | 0 | 6066.314453 |
| 2014-07-16 03:00:00 | Demand | 0 | 6032.233398 |
The Forecasting Company T0¶
⚠ Warning
theforecastingcompany/t0* los puntos de control están restringidos en Hugging Face Hub. Antes de ejecutar las celdas que aparecen a continuación, inicia sesión en la página del modelo (por ejemplo, huggingface.co/theforecastingcompany/t0-alpha) para aceptar su licencia y, a continuación, autentícate localmente, por ejemplo, con hf auth login o configurando la variable de entorno HF_TOKEN.
# Crear ForecasterFoundation
# ==============================================================================
estimator = FoundationModel(model_id="theforecastingcompany/t0-alpha", context_length=500)
forecaster = ForecasterFoundation(estimator=estimator)
# Backtesting
# ==============================================================================
cv = TimeSeriesFold(
steps = 24,
initial_train_size = len(datos.loc[:end_train]),
refit = False
)
start = time.perf_counter()
metricas_t0, predicciones_backtest = backtesting_foundation(
forecaster = forecaster,
series = datos['Demand'],
exog = datos[["Temperature", "Holiday"]],
cv = cv,
metric = 'mean_absolute_error',
suppress_warnings = True
)
tiempo_transcurrido_t0 = time.perf_counter() - start
print(f"Backtesting completado en {tiempo_transcurrido_t0:.4f} seconds.")
print("Métricas de backtesting")
display(metricas_t0)
print("")
print("Predicciones del backtesting")
predicciones_backtest.head(4)
0%| | 0/61 [00:00<?, ?it/s]
Backtesting completado en 5.2062 seconds. Métricas de backtesting
| mean_absolute_error | |
|---|---|
| 0 | 144.131344 |
Predicciones del backtesting
| level | fold | pred | |
|---|---|---|---|
| 2014-10-31 00:00:00 | Demand | 0 | 4904.634766 |
| 2014-10-31 01:00:00 | Demand | 0 | 4869.083496 |
| 2014-10-31 02:00:00 | Demand | 0 | 4887.306641 |
| 2014-10-31 03:00:00 | Demand | 0 | 4923.373047 |
Synthefy Nori¶
# Crear ForecasterFoundation
# ==============================================================================
estimator = FoundationModel(model_id="Synthefy/Nori", context_length=500)
forecaster = ForecasterFoundation(estimator=estimator)
# Backtesting
# ==============================================================================
cv = TimeSeriesFold(
steps = 24,
initial_train_size = len(datos.loc[:end_train]),
refit = False
)
start = time.perf_counter()
metricas_nori, predicciones_backtest = backtesting_foundation(
forecaster = forecaster,
series = datos['Demand'],
exog = datos[["Temperature", "Holiday"]],
cv = cv,
metric = 'mean_absolute_error',
suppress_warnings = True
)
tiempo_transcurrido_nori = time.perf_counter() - start
print(f"Backtesting completado en {tiempo_transcurrido_nori:.4f} seconds.")
print("Métricas de backtesting")
display(metricas_nori)
print("")
print("Predicciones del backtesting")
predicciones_backtest.head(4)
0%| | 0/61 [00:00<?, ?it/s]
Backtesting completado en 71.3532 seconds. Métricas de backtesting
| mean_absolute_error | |
|---|---|
| 0 | 193.843538 |
Predicciones del backtesting
| level | fold | pred | |
|---|---|---|---|
| 2014-10-31 00:00:00 | Demand | 0 | 4867.585570 |
| 2014-10-31 01:00:00 | Demand | 0 | 5005.078139 |
| 2014-10-31 02:00:00 | Demand | 0 | 5091.125827 |
| 2014-10-31 03:00:00 | Demand | 0 | 5124.809975 |
EDF-Lab TS-ICL¶
# Create ForecasterFoundation
# ==============================================================================
estimator = FoundationModel(model_id="taharnbl/TS-ICL", context_length=500)
forecaster = ForecasterFoundation(estimator=estimator)
# Backtesting
# ==============================================================================
cv = TimeSeriesFold(
steps = 24,
initial_train_size = len(datos.loc[:end_train]),
refit = False
)
start = time.perf_counter()
metricas_tsicl, predicciones_backtest = backtesting_foundation(
forecaster = forecaster,
series = datos['Demand'],
exog = datos[["Temperature", "Holiday"]],
cv = cv,
metric = 'mean_absolute_error',
suppress_warnings = True
)
tiempo_transcurrido_tsicl = time.perf_counter() - start
print(f"Backtesting completado en {tiempo_transcurrido_tsicl:.4f} seconds.")
print("Métricas de backtesting")
display(metricas_tsicl)
print("")
print("Predicciones del backtesting")
predicciones_backtest.head(4)
0%| | 0/61 [00:00<?, ?it/s]
Backtesting completado en 3.6210 seconds. Métricas de backtesting
| mean_absolute_error | |
|---|---|
| 0 | 139.935565 |
Predicciones del backtesting
| level | fold | pred | |
|---|---|---|---|
| 2014-10-31 00:00:00 | Demand | 0 | 4934.014648 |
| 2014-10-31 01:00:00 | Demand | 0 | 4937.680176 |
| 2014-10-31 02:00:00 | Demand | 0 | 4976.268555 |
| 2014-10-31 03:00:00 | Demand | 0 | 5069.974121 |
Comparación de modelos¶
La siguiente tabla resume los resultados del backtesting (Error Absoluto Medio) para los cuatro modelos fundacionales sobre el mismo conjunto de datos.
# Comparación de métricas de backtesting
# ==============================================================================
comparison = pd.DataFrame({
"Model": [
"Chronos-2 (small)*",
"TimesFM-2.5 (200m)",
"Moirai-2.0-R (small)",
"TabICLv2*",
"TabPFN-TS*",
"T0",
"Nori",
"TS-ICL"
],
"mean_absolute_error": [
metricas_chronos["mean_absolute_error"].iloc[0],
metricas_timesfm["mean_absolute_error"].iloc[0],
metricas_moirai["mean_absolute_error"],
metricas_tabicl["mean_absolute_error"].iloc[0],
metricas_tabpfn["mean_absolute_error"].iloc[0],
metricas_t0["mean_absolute_error"].iloc[0],
metricas_nori["mean_absolute_error"].iloc[0],
metricas_tsicl["mean_absolute_error"].iloc[0],
],
"Elapsed time": [
tiempo_transcurrido_chronos,
tiempo_transcurrido_timesfm,
tiempo_transcurrido_moirai,
tiempo_transcurrido_tabicl,
tiempo_transcurrido_tabpfn,
tiempo_transcurrido_t0,
tiempo_transcurrido_nori,
tiempo_transcurrido_tsicl
]
}).sort_values(by="mean_absolute_error")
display(
comparison.style.highlight_min(
subset="mean_absolute_error", color="green"
).format(precision=4)
)
print("* Chronos-2 (small), TabICL, TabPFN-TS, TFC T0, Nori and TS-ICL permiten variables exógenas.")
| Model | mean_absolute_error | Elapsed time | |
|---|---|---|---|
| 7 | TS-ICL | 139.9356 | 3.6210 |
| 5 | T0 | 144.1313 | 5.2062 |
| 4 | TabPFN-TS* | 159.1754 | 408.7843 |
| 1 | TimesFM-2.5 (200m) | 160.3570 | 24.8245 |
| 2 | Moirai-2.0-R (small) | 161.6911 | 9.3907 |
| 3 | TabICLv2* | 170.1016 | 157.4076 |
| 0 | Chronos-2 (small)* | 171.2670 | 0.7737 |
| 6 | Nori | 193.8435 | 71.3532 |
* Chronos-2 (small), TabICL, TabPFN-TS, TFC T0, Nori and TS-ICL permiten variables exógenas.
⚠️ Advertencia
Este ejemplo utiliza un conjunto de datos público ampliamente disponible con fines ilustrativos. Es muy probable que los modelos fundacionales (Chronos, TimesFM, Moirai...) hayan sido expuestos a estos datos durante su fase de preentrenamiento. Como resultado, las predicciones pueden ser más optimistas de lo que se lograría en un entorno de producción real con datos privados o nuevos.
Información de la sesión¶
import session_info
session_info.show(html=False)
----- matplotlib 3.10.8 pandas 2.3.3 session_info v1.0.1 skforecast 0.22.0 torch 2.6.0+cu124 ----- IPython 9.12.0 jupyter_client 8.8.0 jupyter_core 5.9.1 ----- Python 3.12.13 | packaged by conda-forge | (main, Mar 5 2026, 16:36:12) [MSC v.1944 64 bit (AMD64)] Windows-11-10.0.26200-SP0 ----- Session information updated at 2026-04-24 17:03
Instrucciones para citar¶
Cómo citar este documento
Si utilizas este documento o cualquier parte del mismo, por favor reconoce la fuente, ¡muchas gracias!
Forecasting con modelos fundacionales por Joaquín Amat Rodrigo y Javier Escobar Ortiz, disponible bajo una licencia Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0 DEED) en https://cienciadedatos.net/documentos/py79-forecasting-con-modelos-fundacionales.html
¿Cómo citar skforecast?
Si utilizas skforecast, te agradeceríamos mucho que lo cites. ¡Muchas gracias!
Zenodo:
Amat Rodrigo, Joaquin, & Escobar Ortiz, Javier. (2024). skforecast (v0.24.0). Zenodo. https://doi.org/10.5281/zenodo.8382788
APA:
Amat Rodrigo, J., & Escobar Ortiz, J. (2024). skforecast (Version 0.24.0) [Computer software]. https://doi.org/10.5281/zenodo.8382788
BibTeX:
@software{skforecast, author = {Amat Rodrigo, Joaquin and Escobar Ortiz, Javier}, title = {skforecast}, version = {0.24.0}, month = {07}, year = {2026}, license = {BSD-3-Clause}, url = {https://skforecast.org/}, doi = {10.5281/zenodo.8382788} }
¿Te gustó el artículo? Tu apoyo es importante
Tu contribución me ayudará a seguir generando contenido educativo gratuito. ¡Muchas gracias! 😊
Este documento creado por Joaquín Amat Rodrigo y Javier Escobar Ortiz tiene licencia Attribution-NonCommercial-ShareAlike 4.0 International.
Se permite:
-
Compartir: copiar y redistribuir el material en cualquier medio o formato.
-
Adaptar: remezclar, transformar y crear a partir del material.
Bajo los siguientes términos:
-
Atribución: Debes otorgar el crédito adecuado, proporcionar un enlace a la licencia e indicar si se realizaron cambios. Puedes hacerlo de cualquier manera razonable, pero no de una forma que sugiera que el licenciante te respalda o respalda tu uso.
-
No-Comercial: No puedes utilizar el material para fines comerciales.
-
Compartir-Igual: Si remezclas, transformas o creas a partir del material, debes distribuir tus contribuciones bajo la misma licencia que el original.
