agentleFS
topprismdatatopprismdata/cultivating-ml-agent/skills/examples/autogluon-timeseries-strategy/SKILL.md

autogluon-timeseries-strategy

Skill5 starsChanged 35 days agoMITView on GitHub

AutoGluon TimeSeriesPredictor: special API and presets for time series forecasting (different from TabularPredictor). Validated on Store Sales (N=3M, 33 families × 54 stores × 1684 days): AG 1.5 Chronos-2 + Chronos + onpromotion covariates → **LB RMSLE 0.39525** (best historical, vs AG 1.4 0.41852, vs manual 3.0+). Use when: (1) Working on time series competitions (forecasting), (2) Have multi-series data with optional covariates (promotions, holidays, prices), (3) Want AG 1.5 Chronos-2 zero-shot OR fine-tuned, (4) Need to bypass HF download errors. Differs from `autogluon-preset-strategy` (which covers TabularPredictor). Key breakthrough: using `model_path=LOCAL_PATH` to bypass `hf-mirror.com` download errors.

The file

We split this skill into the rules it gives an agent

3 rules. About 0 are common, 1 is distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Default presets="chronos2" for zero-shot.

    line 338
  2. Distinctive:

    Default: 1 window. Best_quality uses multiple windows for robust selection

    line 341
  3. Very specific:

    When running AG 1.5 in environments where huggingface.co is blocked, AG 1.5 defaults to hf-mirror.com (which is also unreachable).

    line 383

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

AutoGluon TimeSeriesPredictor Strategy

The Critical Difference

TabularPredictor ≠ TimeSeriesPredictor. They are separate APIs with different:

  • Data formats (TimeSeriesDataFrame vs DataFrame)
  • Cross-validation (multi-window backtesting vs k-fold)
  • Evaluation metrics (RMSLE/MAE/MAPE vs RMSE/accuracy)
  • Models (DeepAR/TFT/Chronos vs LightGBM/XGBoost)
# Tabular (independent rows):
from autogluon.tabular import TabularPredictor
predictor = TabularPredictor(label='target').fit(df)

# Time series (sequential, grouped by entity):
from autogluon.timeseries import TimeSeriesPredictor, TimeSeriesDataFrame
predictor = TimeSeriesPredictor(target='target', prediction_length=16, freq='D').fit(ts_df)

Data Format (Critical)

TimeSeriesPredictor requires TimeSeriesDataFrame:

from autogluon.timeseries import TimeSeriesDataFrame
import pandas as pd

# Required columns: item_id, timestamp, target
# Each item_id is ONE time series (e.g., one store-product combination)
df = pd.DataFrame({
    'item_id': ['store_1', 'store_1', 'store_1', 'store_2', 'store_2'],
    'timestamp': pd.to_datetime(['2024-01-01', '2024-01-02', '2024-01-03',
                                  '2024-01-01', '2024-01-02']),
    'target': [100, 110, 105, 50, 55],
    'onpromotion': [0, 1, 0, 0, 1]  # optional covariates
})

ts_df = TimeSeriesDataFrame.from_data_frame(
    df,
    id_column='item_id',
    timestamp_column='timestamp'
)

Common mistake: passing pd.DataFrame directly to predictor.fit() with a date column. AG will reject it.

Core Parameters

ParameterRequiredDefaultPurpose
targetYesNoneColumn name to forecast
prediction_lengthYes1How many steps ahead to forecast
freqYes (or inferred)None'D', 'H', 'M', 'Q', etc.
eval_metricNo'WQL'RMSLE, MAE, MAPE, WQL, MASE
known_covariates_namesNoNoneFuture-known variables (e.g., holidays)
quantile_levelsNo[0.1, ..., 0.9]For probabilistic forecasts
pathNoNoneModel save directory

Presets — AutoGluon 1.4 vs 1.5 (Critical Update)

IMPORTANT: AG 1.5 (released 2025-12-19) made significant changes to TimeSeries presets. This skill is based on AG 1.4 source code. If you're on 1.5+, see the "AG 1.5 Differences" section below.

AG 1.4 Presets (what this skill is built on)

IMPORTANT: TimeSeriesPredictor has NO extreme_quality preset. That preset only exists in TabularPredictor.

PresetModelsTimeBest For
fast_trainingNaive, SeasonalNaive, ETS, Theta, RecursiveTabular, DirectTabular1-2 minQuick baseline
medium_qualityAbove + TemporalFusionTransformer + Chronos-Bolt small5-15 minRecommended starting point
high_qualityDL + ML + statistical mix + Chronos (zero-shot + fine-tuned) + TiDE30-60 minStrong forecast
best_qualitySame models as high_quality + num_val_windows=2 (multi-backtest validation)1-4 hoursMaximum accuracy via robust validation
bolt_tinyChronos-Bolt tiny only<1 minZero-shot baseline
bolt_miniChronos-Bolt mini only<2 minZero-shot
bolt_smallChronos-Bolt small only<5 minZero-shot
bolt_baseChronos-Bolt base only<10 minZero-shot (most accurate Chronos)

Hidden presets (verified from source presets_configs.py):

PresetEquivalentUse
chronoschronos_smallAlias
chronos_tinyOriginal Chronos tiny(rarely useful now)
chronos_miniOriginal Chronos mini(rarely useful now)
chronos_smallOriginal Chronos small(rarely useful now)
chronos_baseOriginal Chronos base(rarely useful now)
chronos_largeOriginal Chronos large (batch_size=8)(rarely useful now)
chronos_ensembleChronos small + 4 statistical modelsHidden gem — better than chronos_small alone
chronos_large_ensembleChronos large + 4 statistical modelsBest of original Chronos line
best / high / mediumbest_quality / etc.Shorthand aliases
bq / hq / mqbest_quality / etc.Even shorter aliases

Key difference vs Tabular presets:

  • TimeSeries has NO good_quality (Tabular does)
  • TimeSeries has NO extreme_quality (Tabular does — uses GPU foundation models)
  • TimeSeries best_quality ≠ Tabular best_quality:
    • Tabular: ~100 models via zeroshot hyperparameter portfolio
    • TimeSeries: same models as high_quality, but uses num_val_windows=2 (2 backtest windows for robust model selection)

AG 1.5 Differences (Breaking Changes!)

Released 2025-12-19. Source: GitHub release notes.

PresetAG 1.4AG 1.5
fast_training✅✅
medium_quality✅✅
high_quality✅✅
best_quality✅✅
bolt_* (Chronos-Bolt)✅✅
chronos (alias for chronos_small)✅❌ REMOVED
chronos_tiny/mini/small/base/large✅❌ REMOVED
chronos_ensemble✅ (hidden)❌ REMOVED
chronos2❌✅ NEW
chronos2_small❌✅ NEW
chronos2_ensemble❌✅ NEW

Migration 1.4 → 1.5:

# ❌ 1.4 → 1.5 breaks
predictor.fit(data, presets='chronos_small')
predictor.fit(data, presets='chronos_ensemble')

# ✅ 1.5 replacement
predictor.fit(data, presets='chronos2')
predictor.fit(data, presets='chronos2_ensemble')

New models in 1.5 (verified from release notes):

  • Chronos-2 (zero-shot, LoRA fine-tune, full fine-tune)
  • Toto (new time series model)

Other 1.5 changes:

  • num_val_windows="auto" — automatic backtesting configuration
  • refit_every_n_windows="auto" — automatic refit scheduling
  • New methods: predictor.backtest_predictions(), predictor.backtest_targets()
  • Multi-layer stack ensembles for time series
  • 80% win rate vs 1.4 (with same time budget)
  • 10min of 1.5 > 2hr of 1.4

Other breaking changes in 1.5:

  • Python 3.10+ required (1.4 worked on 3.9)
  • Models trained on 1.4 cannot be loaded in 1.5 — must retrain
  • Major dependency upgrades (torch 2.6-2.10, ray 2.43-2.53, etc.)

Empirical Validation (Store Sales)

SetupTimeRMSLE valLB
AG TimeSeries medium_quality 300s240s0.48130.41852
Manual LightGBM with lag features5+ min0.5-0.60.4-0.5 (varied)
Manual stacking ensemble (prior best)1+ hour—~0.4-0.5

Key insight: AG TimeSeries achieves competitive LB in 4 minutes without manual lag engineering, time series CV, or feature engineering. The models (DirectTabular, RecursiveTabular, Naive, SeasonalNaive) automatically handle:

  • Lag features
  • Rolling statistics
  • Calendar features (day of week, month)
  • Holiday effects (if known_covariates_names provided)

Validation: Multi-Window Backtesting

Unlike Tabular's KFold, TimeSeriesPredictor uses multi-window backtesting:

predictor.fit(
    ts_df,
    time_limit=600,
    presets='medium_quality',
    num_val_windows=3,        # Use 3 backtest windows
    val_step_size=prediction_length  # Step size between windows
)

This is AG's equivalent of TimeSeriesSplit, but automated across multiple windows. You do NOT need to manually implement TimeSeriesSplit.

Critical Pitfalls

Pitfall 1: Wrong data format

# ❌ WRONG: passing regular DataFrame
predictor.fit(df_with_date_column)

# ✓ CORRECT: convert first
ts_df = TimeSeriesDataFrame.from_data_frame(df, id_column='item_id', timestamp_column='timestamp')
predictor.fit(ts_df)

Pitfall 2: Missing freq

# ❌ AutoGluon may guess wrong freq (IRREG → resample to D)
# ✓ Explicit
predictor = TimeSeriesPredictor(target='target', prediction_length=16, freq='D')

Pitfall 3: Trying to use lag features manually

# ❌ AG TimeSeries handles lag internally
df['lag_1'] = df.groupby('item_id')['target'].shift(1)

# ✓ Don't pre-compute lag features
# AG's models (DirectTabular, RecursiveTabular) generate them automatically

Pitfall 4: Using Tabular predictor on time series

# ❌ WRONG: treats each row as independent, ignores time order
from autogluon.tabular import TabularPredictor
TabularPredictor(label='target').fit(df)

# ✓ Use TimeSeriesPredictor for sequential forecasting
from autogluon.timeseries import TimeSeriesPredictor
TimeSeriesPredictor(target='target', prediction_length=16, freq='D').fit(ts_df)

Models Trained by AG TimeSeries (1.4 default preset)

ModelTypeSpeedUse case
NaiveLast value~1sBaseline
SeasonalNaiveSame as last season~1sStrong seasonal data
RecursiveTabularLGB with recursive prediction~30sMany series
DirectTabularLGB with direct multi-step~30sMulti-horizon
TemporalFusionTransformerAttention-based DL~5minComplex patterns
DeepARRNN-based~5minMany related series
Chronos-BoltPretrained foundation model~1minZero-shot
PatchTSTPatch-based transformer~5minLong horizons
AutoETSAutomatic Exponential Smoothing~10sStatistical baseline
NPTSNon-Parametric Time Series~10sStatistical baseline
DynamicOptimizedThetaOptimized Theta method~10sStatistical baseline
TiDELong-term Time-series Dense Encoder~5minLong horizons

Note (1.4 only): high_quality / best_quality presets include automatic fine-tuning of Chronos-Bolt small with a CatBoost covariate regressor. This is hidden in source get_default_hps('default') — not visible in the docstring.

# This is what 1.4 high_quality actually does (hidden in source):
"Chronos": [
    {"ag_args": {"name_suffix": "ZeroShot"}, "model_path": "bolt_base"},
    {"ag_args": {"name_suffix": "FineTuned"}, "model_path": "bolt_small",
     "fine_tune": True, "target_scaler": "standard",
     "covariate_regressor": {"model_name": "CAT", "model_hyperparameters": {"iterations": 1000}}},
],

Standard Workflow

from autogluon.timeseries import TimeSeriesPredictor, TimeSeriesDataFrame
import pandas as pd

# Step 1: Prepare TimeSeriesDataFrame
df = ... # your long-format DataFrame
ts_df = TimeSeriesDataFrame.from_data_frame(df, id_column='item_id', timestamp_column='timestamp')

# Step 2: Define predictor
predictor = TimeSeriesPredictor(
    target='target',
    prediction_length=16,        # Match LB forecast horizon
    freq='D',                    # Match your data frequency
    eval_metric='RMSLE',         # Match Kaggle metric
    path='ag_ts_model'
)

# Step 3: Fit (5-15 min for medium_quality)
predictor.fit(ts_df, time_limit=600, presets='medium_quality')

# Step 4: Predict
pred = predictor.predict(ts_df)  # Returns mean + quantile forecasts

# Step 5: Convert to Kaggle submission format
pred_df = pred.reset_index()  # MultiIndex (item_id, timestamp)
pred_df['sales'] = pred_df['mean'].clip(0)  # clip negatives

When to Use TimeSeriesPredictor vs TabularPredictor

ScenarioUse TimeSeriesPredictorUse TabularPredictor
Sales forecasting✅❌
Inventory/demand✅❌
Time-series with clear temporal structure✅❌
Mixed categorical + numeric, no clear time❌✅
Want SHAP per model❌✅
Multi-series data✅❌

Empirical Recommendation

For most Kaggle time series competitions:

predictor = TimeSeriesPredictor(
    target=target_col,
    prediction_length=forecast_horizon,  # match test set length
    freq='D',  # or 'H', 'M'
    eval_metric='RMSLE'  # or MAE, MAPE, WQL
).fit(ts_df, time_limit=600, presets='medium_quality')

This is the simplest possible starting point. Custom lag features are usually unnecessary — AG handles them. Stacking/ensembling are usually unnecessary — AG does it. For time series, AG is even more hands-off than tabular.


Chronos-2 + Covariates: The 0.39525 Breakthrough (Store Sales)

Date: 2026-07-10. Validated on Kaggle Store Sales competition.

The combination of three AG 1.5 features beats all previous attempts:

ApproachOOF (RMSLE)LB (public)Date
Manual LightGBM + lag features + stacking—3.0+2026-07-07/08
AG 1.4 medium_quality-0.48130.418522026-07-10
AG 1.5 best_quality (no Chronos, no covariates)-0.43810.400532026-07-10
AG 1.5 Chronos-2 + Chronos + onpromotion covariateNaN (val)0.395252026-07-10

Net improvement: 0.41852 → 0.39525 = -0.023 (-5.6%) vs AG 1.4 baseline.

What AG 1.5 adds that 1.4 didn't

  1. Chronos-2 model (autogluon/chronos-2, 120M params)
    • Zero-shot forecasting with state-of-the-art accuracy (fev-bench, GIFT-Eval)
    • Native support for known covariates (Chronos-Bolt does NOT)
    • Cross-learning: makes joint predictions across time series in a batch
    • Default presets="chronos2" for zero-shot; presets="chronos2_ensemble" for ensemble
  2. Multi-window backtesting (num_val_windows="auto")
    • Default: 1 window. Best_quality uses multiple windows for robust selection
  3. New method predictor.make_future_data_frame(train_data) for covariates
    • Returns DataFrame with item_id + timestamp for the next prediction_length steps
    • You then fill in known covariate values and pass to predict(known_covariates=...)

Chronos-2 Correct Usage (from official tutorial)

from autogluon.timeseries import TimeSeriesPredictor, TimeSeriesDataFrame

# Data MUST include future-known covariates (not just past)
ts_full = TimeSeriesDataFrame.from_data_frame(
    pd.concat([train_df, test_df]),  # test has known onpromotion for 16 days
    id_column='item_id',
    timestamp_column='timestamp',
)

predictor = TimeSeriesPredictor(
    target='target',
    prediction_length=16,
    known_covariates_names=['onpromotion', 'dcoilwtico', ...],  # NEW in 1.5
).fit(
    ts_full,
    hyperparameters={
        "Chronos": {"model_path": "<bolt path>"},
        "Chronos2": {"model_path": "<c2 path>"},
    },
    enable_ensemble=False,
    time_limit=600,
)

# Predict with covariates
train_only = ts_full.loc[ts_full.index.get_level_values('timestamp') < test_start]
future_cov = predictor.make_future_data_frame(train_only)
# Fill in known values (e.g., merge with test.csv's onpromotion)
future_cov = future_cov.merge(test_df[['item_id', 'date', 'onpromotion']], ...)
pred = predictor.predict(train_only, known_covariates=TimeSeriesDataFrame(future_cov))

The HF-Mirror Bug: How to Work Around It

When running AG 1.5 in environments where huggingface.co is blocked, AG 1.5 defaults to hf-mirror.com (which is also unreachable). Result: OSError: We couldn't connect to 'https://hf-mirror.com'.

Solution: Pre-download models manually + use model_path=LOCAL_PATH.

# Step 1: Pre-download (any machine with internet)
HF_HUB_OFFLINE=0 HF_ENDPOINT=https://huggingface.co python -c "
from huggingface_hub import snapshot_download
snapshot_download(repo_id='autogluon/chronos-2', cache_dir='~/.cache/huggingface/hub')
snapshot_download(repo_id='amazon/chronos-bolt-base', cache_dir='~/.cache/huggingface/hub')
"

# Find local paths
ls ~/.cache/huggingface/hub/models--autogluon--chronos-2/snapshots/
# Pick the actual hash directory, e.g., 60088152a34e...

# Step 2: Use LOCAL paths in AG (no download needed)
LOCAL_C2 = '/Users/mac/.cache/huggingface/hub/models--autogluon--chronos-2/snapshots/60088152a34e242427b44c3100014473a0157d53/'
LOCAL_BOLT = '/Users/mac/.cache/huggingface/hub/models--amazon--chronos-bolt-base/snapshots/5d9f166d69f47aef3401367a7b842e78fe97b121/'

# Critical: also unset HF_ENDPOINT
import os
os.environ.pop('HF_ENDPOINT', None)

predictor.fit(
    ts_full,
    hyperparameters={
        "Chronos": {"model_path": LOCAL_BOLT},
        "Chronos2": {"model_path": LOCAL_C2},
    },
)

Fine-Tuning Chronos-2 (LoRA by default)

predictor.fit(
    ts_full,
    hyperparameters={
        "Chronos2": [
            {"ag_args": {"name_suffix": "ZeroShot"}},          # zero-shot
            {"fine_tune": True, "ag_args": {"name_suffix": "FineTuned"}},  # LoRA
        ],
    },
    time_limit=1800,  # Fine-tuning needs more time
)

Caveat: On Store Sales (large data), Chronos-2 fine-tuning timed out at 30min. Use fine_tune_mode="lora", fine_tune_steps=1000 for faster fine-tuning.

Known Covariates vs Past Covariates

TypeExampleAG 1.5 Support
Known covariates (future known)promotions, holidays, planned prices✅ Chronos-2 native
Past covariates (only historical)weather observations, lagged indicators✅ Chronos2, TFT, DeepAR
predictor = TimeSeriesPredictor(
    known_covariates_names=['onpromotion', 'holiday', 'price'],  # future known
    # past_covariates_names=[...],  # only historical
)

Common Pitfalls

  1. Forgetting enable_ensemble=False: AG 1.5 will try to train all Chronos variants together, taking very long. For fastest experiments, set False.
  2. Not providing test data in TimeSeriesDataFrame: Chronos-2 needs to know future covariate values during training/fitting.
  3. Wrong column name in merge: TimeSeriesDataFrame uses timestamp not date after reset_index().

References

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.