diff --git a/timesfm-forecasting/SKILL.md b/timesfm-forecasting/SKILL.md index 2c57c35..3adc53c 100644 --- a/timesfm-forecasting/SKILL.md +++ b/timesfm-forecasting/SKILL.md @@ -4,10 +4,12 @@ description: > Zero-shot time series forecasting with Google's TimesFM foundation model. Use this skill when forecasting ANY univariate time series — sales, sensor readings, stock prices, energy demand, patient vitals, weather, or scientific measurements — without training a - custom model. Automatically checks system RAM/GPU before loading the model, supports - CSV/DataFrame/array inputs, and returns point forecasts with calibrated prediction - intervals. Includes a preflight system checker script that MUST be run before first use - to verify the machine can load the model. + custom model. Supports both basic forecasting and advanced covariate forecasting (XReg) + with dynamic and static exogenous variables. Automatically checks system RAM/GPU before + loading the model, validates dataset fit before processing, supports CSV/DataFrame/array + inputs, and returns point forecasts with calibrated prediction intervals. Includes a + preflight system checker script that MUST be run before first use to verify the machine + can load the model and handle your specific dataset. license: Apache-2.0 metadata: author: Clayton Young (@borealBytes) @@ -40,6 +42,8 @@ Use this skill when: - You have time series of **any length** (the model handles 1–16,384 context points) - You need to **batch-forecast** hundreds or thousands of series efficiently - You want a **foundation model** approach instead of hand-tuning ARIMA/ETS parameters +- You need **covariate forecasting** with exogenous variables (price, promotions, holidays, day-of-week effects) → use `forecast_with_covariates()` (TimesFM 2.5 + `pip install timesfm[xreg]`) + Do **not** use this skill when: @@ -47,6 +51,8 @@ Do **not** use this skill when: - You need time series classification or clustering → use `aeon` - You need multivariate vector autoregression or Granger causality → use `statsmodels` - Your data is tabular (not temporal) → use `scikit-learn` +- You cannot install optional dependencies → XReg requires scikit-learn and JAX + > **Note on Anomaly Detection**: TimesFM does not have built-in anomaly detection, but you > can use the **quantile forecasts as prediction intervals** — values outside the 90% CI @@ -88,6 +94,39 @@ flowchart TD disk -->|"No"| block_disk["🛑 BLOCKED
Need space for weights"] ``` +### Dataset Preflight (NEW) + +Before loading your actual data, verify it will fit in memory: + +```bash +# Quick estimate for your dataset +python scripts/check_system.py \ + --num-series 1000 \ + --context-length 1024 \ + --horizon 24 \ + --batch-size 32 \ + --estimate-only +``` + +This will show you the estimated memory requirements and warn if your dataset is too large. + +**Memory Estimation Formula**: +`RAM ≈ 0.8 GB (model) + 0.5 GB (overhead) + (0.2 MB × num_series × context_length / 1000)` + +**Example Outputs**: + +✅ **Dataset Fits**: +``` +Total CPU memory: 2.34 GB +Total GPU memory: 2.15 GB +``` + +⚠️ **Dataset Too Large**: +``` +Dataset requires ~12.5 GB RAM but system has 8.0 GB. +Try: context_length=512 or process in chunks of 50 series. +``` + ### Hardware Requirements by Model Version | Model | Parameters | RAM (CPU) | VRAM (GPU) | Disk | Context | @@ -336,11 +375,35 @@ for i in range(0, len(inputs), CHUNK): ### `scripts/check_system.py` Mandatory preflight checker — run before first model load. +Now includes **dataset-aware memory estimation** to prevent OOM errors before loading your data. ```bash +# Basic system check python scripts/check_system.py + +# Check if your specific dataset will fit +python scripts/check_system.py \ + --num-series 1000 \ + --context-length 1024 \ + --horizon 24 \ + --batch-size 32 + +# Quick memory estimate without system checks +python scripts/check_system.py \ + --num-series 5000 \ + --context-length 2048 \ + --estimate-only ``` +**What it checks**: + +1. **Available RAM** — warns if below 4 GB, blocks if below 2 GB +2. **GPU availability** — detects CUDA/MPS devices and VRAM +3. **Disk space** — verifies room for the ~800 MB model download +4. **Python version** — requires 3.10+ +5. **Existing installation** — checks if `timesfm` and `torch` are installed +6. **Dataset fit** (NEW) — estimates memory for your specific dataset and warns if it won't fit + ### `scripts/forecast_csv.py` End-to-end CSV forecasting CLI. diff --git a/timesfm-forecasting/references/api_reference.md b/timesfm-forecasting/references/api_reference.md index d361f0e..bbff790 100644 --- a/timesfm-forecasting/references/api_reference.md +++ b/timesfm-forecasting/references/api_reference.md @@ -221,6 +221,70 @@ Where `B` = batch size (number of input series), `H` = forecast horizon. --- +--- + +## Memory Estimation + +Before running forecasts on large datasets, estimate memory requirements: + +### Formula + +```mermaid +block-beta + columns 3 + ram["Total RAM Required"] model["Model Weights
~0.8 GB"] overhead["Runtime Overhead
~0.5 GB"] buffers["I/O Buffers
~0.2 MB per 1000 series
per 1000 context"] + + ram --> model + ram --> overhead + ram --> buffers +``` + +**Formula**: +`RAM (GB) ≈ 0.8 + 0.5 + (0.0002 × num_series × context_length)` + +**Variables**: +- `num_series`: Number of time series in your batch +- `context_length`: Your `max_context` value (or max series length) +- `batch_size`: Your `per_core_batch_size` (affects parallel processing overhead) + +### Quick Reference + +| Dataset Size | Context=512 | Context=1024 | Context=2048 | +|--------------|-------------|--------------|--------------| +| 100 series | ~1.4 GB | ~1.5 GB | ~1.7 GB | +| 1,000 series | ~1.9 GB | ~2.3 GB | ~3.1 GB | +| 10,000 series| ~9.0 GB | ~17.0 GB | ~33.0 GB | + +### Using the Preflight Checker + +```bash +python scripts/check_system.py \ + --num-series 1000 \ + --context-length 1024 \ + --batch-size 32 +``` + +This validates both system requirements AND dataset fit before loading the model. + +### Reducing Memory Usage + +If your dataset is too large: + +1. **Reduce context length**: Use `max_context=512` instead of 1024+ (50% reduction) +2. **Process in chunks**: Split large batches into smaller groups: + +```python +CHUNK_SIZE = 100 +for i in range(0, len(inputs), CHUNK_SIZE): + chunk = inputs[i:i+CHUNK_SIZE] + point, quantiles = model.forecast(horizon=H, inputs=chunk) + # Save chunk results +``` + +3. **Reduce batch size**: Lower `per_core_batch_size` (slower but less memory) +4. **Use CPU**: If GPU OOM, the model will automatically fall back to CPU + + ## Error Handling | Error | Cause | Fix | diff --git a/timesfm-forecasting/references/system_requirements.md b/timesfm-forecasting/references/system_requirements.md index c71e084..0f27b7f 100644 --- a/timesfm-forecasting/references/system_requirements.md +++ b/timesfm-forecasting/references/system_requirements.md @@ -5,6 +5,33 @@ TimesFM can run on a variety of hardware configurations. This guide helps you choose the right setup and tune performance for your machine. +### How Context Limits Are Determined + +The `max_context` values in each tier are **conservative recommendations** based on memory-performance tradeoffs, not hard limits. TimesFM 2.5 supports up to 16,384 context points, but smaller values are recommended for most use cases. + +**Why 512 and 1024?** + +| Factor | 512 Context | 1024 Context | +|--------|-------------|--------------| +| **Memory per 1000 series** | ~100 MB | ~200 MB | +| **Typical Use Case** | Daily data, ~1-2 years | Daily data, ~2-3 years | +| **Inference Speed** | Faster | Moderate | +| **Hardware** | 4-8 GB RAM | 16 GB RAM or GPU | + +**Memory Formula**: `RAM ≈ model_weights + 0.5 GB + (0.2 MB × num_series × context_length / 1000)` + +Where: +- `model_weights` = ~800 MB (TimesFM 2.5) +- `context_length` = your `max_context` value +- `num_series` = number of time series in your batch + +**You can use larger contexts** if your hardware supports it: +- **Up to 2048**: Requires ~16 GB RAM for moderate batch sizes +- **Up to 4096**: Requires GPU or 32+ GB RAM +- **Up to 16384**: Maximum supported, requires significant memory + +See [Data Preparation Guide](data_preparation.md) for context length recommendations by data frequency. + ### Tier 1: Minimal (CPU-Only, 4–8 GB RAM) - **Use case**: Light exploration, single-series forecasting, prototyping diff --git a/timesfm-forecasting/scripts/check_system.py b/timesfm-forecasting/scripts/check_system.py index 1a7dcc9..e61a7d0 100644 --- a/timesfm-forecasting/scripts/check_system.py +++ b/timesfm-forecasting/scripts/check_system.py @@ -25,6 +25,7 @@ import sys from dataclasses import dataclass, field from pathlib import Path from typing import Any +import math # --------------------------------------------------------------------------- @@ -424,6 +425,168 @@ def recommend_batch_size(report: SystemReport) -> int: return 4 +def estimate_memory_gb( + num_series: int, + context_length: int, + horizon: int = 0, + batch_size: int = 32, + model_version: str = "v2.5", +) -> dict[str, float]: + """Estimate memory requirements for a dataset. + + Args: + num_series: Number of time series in the dataset + context_length: Length of each time series context window + horizon: Forecast horizon (optional, for output storage) + batch_size: Batch size for inference + model_version: Model version being used + + Returns: + Dictionary with memory estimates in GB for different components + """ + # Base model memory (weights + overhead) + model_memory_gb = 0.8 # ~800MB for model weights + overhead_gb = 0.5 # Python overhead, libraries, etc. + + # Input data memory: each value is float32 (4 bytes) + # Formula: num_series * context_length * 4 bytes / (1024^3) + input_gb = (num_series * context_length * 4) / (1024**3) + + # Batch processing memory (peak during inference) + # Each batch needs: batch_size * context_length * 4 bytes + batch_input_gb = (batch_size * context_length * 4) / (1024**3) + + # Output memory: horizon * num_series * quantiles * 4 bytes + # Default is 10 quantiles (mean + 9 quantiles) + num_quantiles = 10 + output_gb = (num_series * horizon * num_quantiles * 4) / (1024**3) if horizon > 0 else 0 + + # Total memory with some headroom for intermediate computations + total_gb = model_memory_gb + overhead_gb + input_gb + batch_input_gb + output_gb + + # Add 20% buffer for intermediate tensors and OS overhead + total_with_buffer = total_gb * 1.2 + + return { + "model_weights": model_memory_gb, + "overhead": overhead_gb, + "input_data": input_gb, + "batch_processing": batch_input_gb, + "output_data": output_gb, + "total": total_gb, + "total_with_buffer": total_with_buffer, + } + + +def check_dataset_fit( + num_series: int, + context_length: int, + horizon: int = 0, + batch_size: int = 32, + model_version: str = "v2.5", +) -> tuple[bool, str, dict[str, float]]: + """Check if a dataset will fit in available memory. + + Args: + num_series: Number of time series in the dataset + context_length: Length of each time series context window + horizon: Forecast horizon (optional) + batch_size: Batch size for inference + model_version: Model version being used + + Returns: + Tuple of (fits: bool, message: str, memory_details: dict) + """ + memory = estimate_memory_gb(num_series, context_length, horizon, batch_size, model_version) + total_ram = _get_total_ram_gb() + available_ram = _get_available_ram_gb() + + required = memory["total_with_buffer"] + + # Leave 10% headroom for OS and other processes + usable_ram = total_ram * 0.9 + usable_available = available_ram * 0.9 if available_ram > 0 else usable_ram + + if required > total_ram: + return ( + False, + f"Dataset requires {required:.1f} GB but system only has {total_ram:.1f} GB RAM. " + f"Consider processing in chunks or using a machine with more RAM.", + memory, + ) + elif required > usable_available: + return ( + False, + f"Dataset requires {required:.1f} GB but only {available_ram:.1f} GB is available. " + f"Close other applications or restart to free memory.", + memory, + ) + elif required > usable_ram * 0.8: + return ( + True, + f"Dataset will fit ({required:.1f} GB needed, {total_ram:.1f} GB total) " + f"but memory usage will be high. Consider reducing batch_size.", + memory, + ) + else: + return ( + True, + f"Dataset fits comfortably: {required:.1f} GB needed, {total_ram:.1f} GB available.", + memory, + ) + + +def print_memory_estimate( + num_series: int, + context_length: int, + horizon: int = 0, + batch_size: int = 32, + model_version: str = "v2.5", +) -> None: + """Print a detailed memory estimate for a dataset. + + Args: + num_series: Number of time series in the dataset + context_length: Length of each time series context window + horizon: Forecast horizon (optional) + batch_size: Batch size for inference + model_version: Model version being used + """ + memory = estimate_memory_gb(num_series, context_length, horizon, batch_size, model_version) + total_ram = _get_total_ram_gb() + available_ram = _get_available_ram_gb() + + print(f"\n{'=' * 50}") + print(f" Memory Estimate for Dataset") + print(f"{'=' * 50}") + print(f" Dataset: {num_series:,} series × {context_length} context length") + if horizon > 0: + print(f" Horizon: {horizon} steps") + print(f" Batch size: {batch_size}") + print(f" Model: {model_version}") + print(f"{'-' * 50}") + print(f" Model weights: {memory['model_weights']:.2f} GB") + print(f" Overhead: {memory['overhead']:.2f} GB") + print(f" Input data: {memory['input_data']:.2f} GB") + print(f" Batch processing: {memory['batch_processing']:.2f} GB") + if horizon > 0: + print(f" Output data: {memory['output_data']:.2f} GB") + print(f"{'-' * 50}") + print(f" Total (raw): {memory['total']:.2f} GB") + print(f" Total (+20% buf): {memory['total_with_buffer']:.2f} GB") + print(f"{'-' * 50}") + print(f" System RAM: {total_ram:.1f} GB") + print(f" Available RAM: {available_ram:.1f} GB") + print(f"{'=' * 50}") + + fits, message, _ = check_dataset_fit( + num_series, context_length, horizon, batch_size, model_version + ) + status_icon = "✅" if fits else "🛑" + print(f" {status_icon} {message}") + print(f"{'=' * 50}\n") + + # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- @@ -490,7 +653,7 @@ def print_report(report: SystemReport) -> None: def main() -> None: parser = argparse.ArgumentParser( - description="Check system requirements for TimesFM." + description="Check system requirements for TimesFM.", ) parser.add_argument( "--model", @@ -503,10 +666,65 @@ def main() -> None: action="store_true", help="Output results as JSON (machine-readable)", ) + # Dataset preflight options (NEW) + dataset_group = parser.add_argument_group("dataset preflight (optional)") + dataset_group.add_argument( + "--num-series", + type=int, + metavar="N", + help="Number of time series in your dataset (for memory estimation)", + ) + dataset_group.add_argument( + "--context-length", + type=int, + metavar="LEN", + help="Length of each input time series (max_context value)", + ) + dataset_group.add_argument( + "--horizon", + type=int, + metavar="H", + default=24, + help="Forecast horizon length (default: 24)", + ) + dataset_group.add_argument( + "--batch-size", + type=int, + metavar="SIZE", + default=32, + help="per_core_batch_size from ForecastConfig (default: 32)", + ) + dataset_group.add_argument( + "--estimate-only", + action="store_true", + help="Only show memory estimate, skip system checks", + ) args = parser.parse_args() + # Handle dataset estimation only mode + if args.estimate_only and args.num_series and args.context_length: + print_memory_estimate( + args.num_series, + args.context_length, + args.horizon, + args.batch_size, + args.model, + ) + sys.exit(0) + + # Run system checks report = run_checks(args.model) + # Add dataset check if parameters provided + if args.num_series and args.context_length: + print_memory_estimate( + args.num_series, + args.context_length, + args.horizon, + args.batch_size, + args.model, + ) + if args.json: print(json.dumps(report.to_dict(), indent=2)) else: