Foundation Model Forecasting (Zero-Shot)
When to Use
Use ForecasterFoundation when:
- You want a zero-shot baseline before investing in model training.
- You have very short histories where ML models struggle.
- You need to forecast cold-start series (new product, new sensor).
- You want to compare against pre-trained generalist models.
Foundation models are pre-trained on massive corpora — fit() does not train them; it only stores the recent context and metadata.
Related skills
- Prerequisite:
choosing-a-forecaster(decide whether a zero-shot model fits the problem at all) - Alongside:
baseline-forecasting(compare the zero-shot model against a naive rule with MASE) - Next:
hyperparameter-optimization(tunecontext_lengthwithbayesian_search_foundation) - Next:
metric-selection(probabilistic metrics for the native quantile output)
Stop Conditions
Scan before writing code. Each row lists a rule, the symptom when it is broken, and the recovery. Full pitfall catalog: the troubleshooting-common-errors skill.
| Rule | Symptom | Recovery |
|---|---|---|
fit() stores context only; it never trains the model |
Expecting training to happen or weights to update | Treat the model as pre-trained; evaluate with backtesting_foundation |
cv.refit and cv.fixed_train_size are overridden by backtesting_foundation |
IgnoredArgumentWarning when refit=True or fixed_train_size=False |
Leave them at their defaults; the context window expands per fold either way |
Only Chronos-2, TimesFM 3.0, TabICL, TabPFN-TS, T0, Nori, and TS-ICL use exog; TimesFM 2.5 and Moirai-2 ignore it |
exog silently dropped, no error raised |
Pick an exog-capable adapter when covariates matter |
TimesFM (2.5 and 3.0) and Moirai-2 restrict quantiles to [0.1, 0.2, ..., 0.9] |
Requested quantile rejected or unsupported | Request only supported quantiles, or use an adapter allowing any quantile in (0, 1) |
| Each backend library must be installed separately | ModuleNotFoundError / ImportError on first use |
pip install the matching backend (see Installation) |
Tuning uses bayesian_search_foundation, never bayesian_search_forecaster* |
TypeError on the forecaster type or on OneStepAheadFold |
Call bayesian_search_foundation with a TimeSeriesFold |
| Weights for TimesFM 3.0, Moirai-2, TabPFN-TS, and TS-ICL are released under known non-commercial licenses (terms vary, e.g. TabPFN-TS allows commercial use under an enterprise license); no warning does not mean a model is unrestricted | LicenseWarning on first model load |
Review the linked license before commercial use; suppress the warning with suppress_warnings=True if already reviewed |
Installation
Foundation model backends are not bundled with skforecast. Install only the backend(s) you need:
pip install chronos-forecasting # For Chronos-2
pip install "timesfm[torch]" # For TimesFM 2.5 and 3.0
pip install uni2ts # For Moirai-2
pip install tabicl[forecast] # For TabICL
pip install tabpfn-time-series # For TabPFN-TS
pip install tfc-t0 # For T0
pip install tsicl # For TS-ICL
pip install synthefy-nori # For Nori
Models are downloaded from HuggingFace on first use.
Quick Start (single series)
import pandas as pd
from skforecast.foundation import FoundationModel, ForecasterFoundation
# Data must have a DatetimeIndex with a frequency
data = pd.read_csv('data.csv', index_col='date', parse_dates=True).asfreq('h')
# 1. Configure a foundation model (adapter is resolved from model_id)
model = FoundationModel(
model_id='autogluon/chronos-2-small',
context_length=2048, # Adapter-specific default: see reference
device_map='auto', # 'auto' picks CUDA > MPS > CPU
)
# 2. Wrap it in ForecasterFoundation for the skforecast API
forecaster = ForecasterFoundation(estimator=model)
# 3. "Fit" only stores the last context_length observations (no training)
forecaster.fit(series=data['target'])
# 4. Point forecast — returns long-format DataFrame: columns ['level', 'pred']
predictions = forecaster.predict(steps=24)
Multi-Series (Global Zero-Shot Model)
Pass a wide DataFrame, a long-format DataFrame (MultiIndex), or a
dict[str, pd.Series] to fit.
The series do not need to be aligned: they can have different lengths and
time spans, a different subset of exog columns each, and NaN values. Each series
is forecast from its own context and horizon, so no padding or imputation is
required before fit. See the user guide
Foundation models with heterogeneous series
for the per-backend rules.
# series: wide DataFrame — each column is one series
forecaster.fit(series=series)
# Forecast all series
predictions = forecaster.predict(steps=24)
# Forecast a subset
predictions = forecaster.predict(steps=24, levels=['series_1', 'series_2'])
Chronos-2 supports cross_learning=True to share information across series
in the batch (ignored in single-series mode):
model = FoundationModel(
model_id='autogluon/chronos-2-small',
cross_learning=True,
)
With Exogenous Variables (Chronos-2, TimesFM 3.0, TabICL, TabPFN-TS, TFC-T0, Nori, and TS-ICL)
Chronos-2, TimesFM 3.0, TabICL, TabPFN-TS, TFC-T0, Nori, and TS-ICL (allow_exog=True) accept exogenous variables. TimesFM 2.5 and Moirai-2 ignore them. At predict time the future exog columns are validated per series against the historical exog: a future column with no history raises ValueError; a historical column with no future values is a past-only covariate, used by Chronos-2, TS-ICL and TimesFM 3.0 (supports_past_only_covariates=True) and ignored with an IgnoredArgumentWarning by TabICL, TabPFN-TS, TFC-T0 and Nori. Series in a multi-series input may differ in length and in their exog columns: each series is forecast with its own columns, and adapters whose backend needs identical columns per batch (supports_heterogeneous_covariates=False: Chronos-2, TS-ICL, TabICL, TimesFM 3.0) are called once per group of series sharing the same columns, so the forecast of a series never depends on the exog of the others.
# Historical + future exog (must cover the forecast horizon)
forecaster.fit(series=data['target'], exog=exog_train)
predictions = forecaster.predict(steps=24, exog=exog_test)
Prediction Intervals and Quantiles
Foundation models output native quantile forecasts — no bootstrapping or conformal calibration is required.
# Interval (lower/upper bounds from the model's quantiles)
predictions = forecaster.predict_interval(
steps=24,
interval=[0.1, 0.9], # 80% prediction interval (quantiles, 0-1 scale)
)
# Columns: ['level', 'pred', 'lower_bound', 'upper_bound']
# Explicit quantiles
predictions = forecaster.predict_quantiles(
steps=24,
quantiles=[0.1, 0.5, 0.9],
)
# Columns: ['level', 'q_0.1', 'q_0.5', 'q_0.9']
For TimesFM (2.5 and 3.0) and Moirai-2, requested quantiles must be a subset of [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]. TS-ICL accepts any level on a finer 0.01 grid in [0.01, 0.99] (e.g. 0.05, 0.37); off-grid levels raise a ValueError. Chronos-2, TabICL, TabPFN-TS, TFC-T0 and Nori support any quantile in (0, 1).
Choosing a Model
Model (model_id prefix) |
Exog | Default context | Best for |
|---|---|---|---|
autogluon/chronos-2-* (Amazon) |
Yes | 8192 | General-purpose, exog-friendly, cross-series info |
google/timesfm-2.5-* (Google) |
No | 512 | Long-horizon point/quantile forecasts |
google/timesfm-3.0-* (Google) |
Yes | 2048 | Long-horizon point/quantile forecasts, exog-aware |
Salesforce/moirai-2.0-* (Salesforce) |
No | 2048 | Multivariate pretraining, probabilistic forecasts |
soda-inria/tabicl (Soda-INRIA) |
Yes | 4096 | Tabular in-context learning, exog-aware |
priorlabs/tabpfn-ts (Prior Labs) |
Yes | 32768 | Tabular foundation model, exog-aware, long context |
theforecastingcompany/t0 (TFC) |
Yes | 8192 | Probabilistic forecasts, exog-aware (future covariates) |
Synthefy/Nori (Synthefy) |
Yes | 4096 | Tabular foundation model, exog-aware |
taharnbl/TS-ICL (EDF Lab) |
Yes | 4096 | Past & future covariates, fine-grained (0.01) quantile grid |
The adapter is resolved automatically from the model_id prefix — no need to import adapter classes directly.
TimesFM 3.0, Moirai-2, TabPFN-TS, and TS-ICL weights are released under known non-commercial licenses; loading them raises a LicenseWarning naming the license and a link to the model card. Terms vary by provider (e.g. TabPFN-TS permits commercial use under an enterprise license), so review the linked license rather than the warning text alone. A model id not covered by this warning is not confirmed to be unrestricted.
Backtesting
Use the dedicated backtesting_foundation function — it is the only backtester that accepts a ForecasterFoundation. Internally cv is deep-copied and forced to refit=True, fixed_train_size=False, so the context window expands with each fold up to context_length; no weights are ever trained. Probabilistic output is requested via quantiles, not interval.
from skforecast.model_selection import backtesting_foundation, TimeSeriesFold
cv = TimeSeriesFold(
steps=24,
initial_train_size=len(series) - 200,
refit=False, # Overridden internally; passing True emits IgnoredArgumentWarning
)
metric, predictions = backtesting_foundation(
forecaster=forecaster,
series=series,
cv=cv,
metric='mean_absolute_error',
quantiles=[0.1, 0.5, 0.9], # Native model quantiles; no bootstrapping
)
Tuning Inference-Time Parameters
No weights are trained, so tuning means choosing how the pre-trained model is
queried. context_length is the highest-impact parameter. Use
bayesian_search_foundation (TimeSeriesFold only, no lags, no n_jobs):
from skforecast.model_selection import bayesian_search_foundation, TimeSeriesFold
def search_space(trial):
return {
'context_length': trial.suggest_categorical('context_length', [512, 1024, 2048, 4096]),
}
results, study = bayesian_search_foundation(
forecaster=forecaster,
series=series,
cv=cv,
search_space=search_space,
metric='mean_absolute_error',
n_trials=30,
return_best=True,
)
Keys are validated against the adapter's get_params(). Search
context_length and the adapter's quality-relevant parameters; runtime
settings (device, torch_dtype, mode, show_progress, max_horizon) are
accepted but cannot improve accuracy, and several parameters force an expensive
model reload per trial. Per-adapter matrix:
references/adapter-parameters.md.
Override the Stored Context
Pass context at predict time to forecast from a different window without
refitting — useful for one-off predictions or custom backtesting loops:
predictions = forecaster.predict(
steps=24,
context=new_window, # pandas Series / DataFrame / dict
context_exog=new_exog, # Only with exog-aware adapters
exog=future_exog,
)
If context is longer than the adapter's context_length, it is trimmed
automatically to the last context_length observations.
Common Mistakes
- Expecting
fit()to train the model: it only stores context. The weights come from HuggingFace. - Index without frequency: call
series.asfreq('h')(or similar) beforefit— skforecast requires a frequency. - Passing
exogto TimesFM 2.5 / Moirai-2: ignored. Only Chronos-2, TimesFM 3.0, TabICL, TabPFN-TS, TFC-T0, Nori, and TS-ICL support exogenous variables. - Requesting unsupported quantiles: TimesFM (2.5 and 3.0) and Moirai-2 are restricted to the nine deciles
0.1 … 0.9; TS-ICL is restricted to a 0.01 grid in[0.01, 0.99]. - Large model downloads: first call can be slow; consider using smaller variants (
*-small) for experimentation. - Forgetting to install the backend: each foundation model requires its own library (
chronos-forecasting,timesfm,uni2ts,tabicl,tabpfn-time-series,tfc-t0,synthefy-nori,tsicl). Install only the one(s) you need. - Tuning a parameter that forces a model reload:
model_idand device/dtype arguments reload the model on every trial, andcontext_lengthdoes the same on TimesFM 2.5 (but not TimesFM 3.0), Moirai-2, TabICL and TabPFN-TS. - Assuming TimesFM 3.0 accepts categorical covariates: it does not; encode categoricals as numeric (e.g. via
transformer_exog) before passing them, same as Nori, T0, and TS-ICL. - Passing a future
exogcolumn that was not in the historical exog (fitwithout that column, orcontextwithoutcontext_exog):ValueErroron every adapter. Pass the same columns tofit(orcontext_exog) and topredict. - Predicting without
exogafter fitting with exog on TabICL, TabPFN-TS, T0 or Nori: the historical columns are ignored (IgnoredArgumentWarning) and the forecast uses no covariates. Only Chronos-2, TS-ICL and TimesFM 3.0 use them as past-only covariates.
References
See references/adapter-parameters.md for the per-adapter constructor parameters of ChronosAdapter, TimesFM25Adapter, TimesFM3Adapter, MoiraiAdapter, TabICLAdapter, TabPFNAdapter, T0Adapter, NoriAdapter, and TSICLAdapter.
See the user guide Foundation models with heterogeneous series for a worked example with series of different lengths, different exog columns and NaN, and for the table of what each backend requires and tolerates.