refactor(skill): replace claude-specific dirs with agentskills.io standard
Replace AGENTS.md / claude-skill/ with a proper agentskills.io-compliant
skill directory. Any AI agent that supports the open Agent Skills standard
(Claude Code, OpenCode, Cursor, Codex, etc.) can now install and use this
skill generically.
Changes:
- Remove AGENTS.md (was Claude-specific convention)
- Remove claude-skill/ directory (was Claude-specific naming)
- Add timesfm-forecasting/SKILL.md with compliant frontmatter:
name: timesfm-forecasting
description: ...
license: Apache-2.0
metadata: author, version
- Rename claude-skill/examples/ → timesfm-forecasting/examples/
- Rename claude-skill/scripts/ → timesfm-forecasting/scripts/
- Rename claude-skill/references/ → timesfm-forecasting/references/
- Update .gitattributes paths to match new directory
Skill installs via:
cp -r timesfm-forecasting/ ~/.claude/skills/
cp -r timesfm-forecasting/ ~/.cursor/skills/
# or any agent that supports agentskills.io
Spec: https://agentskills.io/specification
This commit is contained in:
@@ -0,0 +1,231 @@
|
||||
# TimesFM API Reference
|
||||
|
||||
## Model Classes
|
||||
|
||||
### `timesfm.TimesFM_2p5_200M_torch`
|
||||
|
||||
The primary model class for TimesFM 2.5 (200M parameters, PyTorch backend).
|
||||
|
||||
#### `from_pretrained()`
|
||||
|
||||
```python
|
||||
model = timesfm.TimesFM_2p5_200M_torch.from_pretrained(
|
||||
"google/timesfm-2.5-200m-pytorch",
|
||||
cache_dir=None, # Optional: custom cache directory
|
||||
force_download=True, # Re-download even if cached
|
||||
)
|
||||
```
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
| --------- | ---- | ------- | ----------- |
|
||||
| `model_id` | str | `"google/timesfm-2.5-200m-pytorch"` | Hugging Face model ID |
|
||||
| `revision` | str \| None | None | Specific model revision |
|
||||
| `cache_dir` | str \| Path \| None | None | Custom cache directory |
|
||||
| `force_download` | bool | True | Force re-download of weights |
|
||||
|
||||
**Returns**: Initialized `TimesFM_2p5_200M_torch` instance (not yet compiled).
|
||||
|
||||
#### `compile()`
|
||||
|
||||
Compiles the model with the given forecast configuration. **Must be called before `forecast()`.**
|
||||
|
||||
```python
|
||||
model.compile(
|
||||
timesfm.ForecastConfig(
|
||||
max_context=1024,
|
||||
max_horizon=256,
|
||||
normalize_inputs=True,
|
||||
per_core_batch_size=32,
|
||||
use_continuous_quantile_head=True,
|
||||
force_flip_invariance=True,
|
||||
infer_is_positive=True,
|
||||
fix_quantile_crossing=True,
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
**Raises**: Nothing (but `forecast()` will raise `RuntimeError` if not compiled).
|
||||
|
||||
#### `forecast()`
|
||||
|
||||
Run inference on one or more time series.
|
||||
|
||||
```python
|
||||
point_forecast, quantile_forecast = model.forecast(
|
||||
horizon=24,
|
||||
inputs=[array1, array2, ...],
|
||||
)
|
||||
```
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `horizon` | int | Number of future steps to forecast |
|
||||
| `inputs` | list[np.ndarray] | List of 1-D numpy arrays (each is a time series) |
|
||||
|
||||
**Returns**: `tuple[np.ndarray, np.ndarray]`
|
||||
|
||||
- `point_forecast`: shape `(batch_size, horizon)` — median (0.5 quantile)
|
||||
- `quantile_forecast`: shape `(batch_size, horizon, 10)` — [mean, q10, q20, ..., q90]
|
||||
|
||||
**Raises**: `RuntimeError` if model is not compiled.
|
||||
|
||||
**Key behaviors**:
|
||||
|
||||
- Leading NaN values are stripped automatically
|
||||
- Internal NaN values are linearly interpolated
|
||||
- Series longer than `max_context` are truncated (last `max_context` points used)
|
||||
- Series shorter than `max_context` are padded
|
||||
|
||||
#### `forecast_with_covariates()`
|
||||
|
||||
Run inference with exogenous variables (requires `timesfm[xreg]`).
|
||||
|
||||
```python
|
||||
point, quantiles = model.forecast_with_covariates(
|
||||
inputs=inputs,
|
||||
dynamic_numerical_covariates={"temp": [temp_array1, temp_array2]},
|
||||
dynamic_categorical_covariates={"dow": [dow_array1, dow_array2]},
|
||||
static_categorical_covariates={"region": ["east", "west"]},
|
||||
xreg_mode="xreg + timesfm",
|
||||
)
|
||||
```
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `inputs` | list[np.ndarray] | Target time series |
|
||||
| `dynamic_numerical_covariates` | dict[str, list[np.ndarray]] | Time-varying numeric features |
|
||||
| `dynamic_categorical_covariates` | dict[str, list[np.ndarray]] | Time-varying categorical features |
|
||||
| `static_categorical_covariates` | dict[str, list[str]] | Fixed categorical features per series |
|
||||
| `xreg_mode` | str | `"xreg + timesfm"` or `"timesfm + xreg"` |
|
||||
|
||||
**Note**: Dynamic covariates must have length `context + horizon` for each series.
|
||||
|
||||
---
|
||||
|
||||
## `timesfm.ForecastConfig`
|
||||
|
||||
Immutable dataclass controlling all forecast behavior.
|
||||
|
||||
```python
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class ForecastConfig:
|
||||
max_context: int = 0
|
||||
max_horizon: int = 0
|
||||
normalize_inputs: bool = False
|
||||
per_core_batch_size: int = 1
|
||||
use_continuous_quantile_head: bool = False
|
||||
force_flip_invariance: bool = True
|
||||
infer_is_positive: bool = True
|
||||
fix_quantile_crossing: bool = False
|
||||
return_backcast: bool = False
|
||||
quantiles: list[float] = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
|
||||
decode_index: int = 5
|
||||
```
|
||||
|
||||
### Parameter Details
|
||||
|
||||
#### `max_context` (int, default=0)
|
||||
|
||||
Maximum number of historical time points to use as context.
|
||||
|
||||
- **0**: Use the model's maximum supported context (16,384 for v2.5)
|
||||
- **N**: Truncate series to last N points
|
||||
- **Best practice**: Set to the length of your longest series, or 512–2048 for speed
|
||||
|
||||
#### `max_horizon` (int, default=0)
|
||||
|
||||
Maximum forecast horizon.
|
||||
|
||||
- **0**: Use the model's maximum
|
||||
- **N**: Forecasts up to N steps (can still call `forecast(horizon=M)` where M ≤ N)
|
||||
- **Best practice**: Set to your expected maximum forecast length
|
||||
|
||||
#### `normalize_inputs` (bool, default=False)
|
||||
|
||||
Whether to z-normalize each series before feeding to the model.
|
||||
|
||||
- **True** (RECOMMENDED): Normalizes each series to zero mean, unit variance
|
||||
- **False**: Raw values are passed directly
|
||||
- **When False is OK**: Only if your series are already normalized or very close to scale 1.0
|
||||
|
||||
#### `per_core_batch_size` (int, default=1)
|
||||
|
||||
Number of series processed per device in each batch.
|
||||
|
||||
- Increase for throughput, decrease if OOM
|
||||
- See `references/system_requirements.md` for recommended values by hardware
|
||||
|
||||
#### `use_continuous_quantile_head` (bool, default=False)
|
||||
|
||||
Use the 30M-parameter continuous quantile head for better interval calibration.
|
||||
|
||||
- **True** (RECOMMENDED): More accurate prediction intervals, especially for longer horizons
|
||||
- **False**: Uses fixed quantile buckets (faster but less accurate intervals)
|
||||
|
||||
#### `force_flip_invariance` (bool, default=True)
|
||||
|
||||
Ensures the model satisfies `f(-x) = -f(x)`.
|
||||
|
||||
- **True** (RECOMMENDED): Mathematical consistency — forecasts are invariant to sign flip
|
||||
- **False**: Slightly faster but may produce asymmetric forecasts
|
||||
|
||||
#### `infer_is_positive` (bool, default=True)
|
||||
|
||||
Automatically detect if all input values are positive and clamp forecasts ≥ 0.
|
||||
|
||||
- **True**: Safe for sales, demand, counts, prices, volumes
|
||||
- **False**: Required for temperature, returns, PnL, any series that can be negative
|
||||
|
||||
#### `fix_quantile_crossing` (bool, default=False)
|
||||
|
||||
Post-process quantiles to ensure monotonicity (q10 ≤ q20 ≤ ... ≤ q90).
|
||||
|
||||
- **True** (RECOMMENDED): Guarantees well-ordered quantiles
|
||||
- **False**: Slightly faster but quantiles may occasionally cross
|
||||
|
||||
#### `return_backcast` (bool, default=False)
|
||||
|
||||
Return the model's reconstruction of the input (backcast) in addition to forecast.
|
||||
|
||||
- **True**: Used for covariate workflows and diagnostics
|
||||
- **False**: Only return forecast
|
||||
|
||||
---
|
||||
|
||||
## Available Model Checkpoints
|
||||
|
||||
| Model ID | Version | Params | Backend | Context |
|
||||
| -------- | ------- | ------ | ------- | ------- |
|
||||
| `google/timesfm-2.5-200m-pytorch` | 2.5 | 200M | PyTorch | 16,384 |
|
||||
| `google/timesfm-2.5-200m-flax` | 2.5 | 200M | JAX/Flax | 16,384 |
|
||||
| `google/timesfm-2.5-200m-transformers` | 2.5 | 200M | Transformers | 16,384 |
|
||||
| `google/timesfm-2.0-500m-pytorch` | 2.0 | 500M | PyTorch | 2,048 |
|
||||
| `google/timesfm-2.0-500m-jax` | 2.0 | 500M | JAX | 2,048 |
|
||||
| `google/timesfm-1.0-200m-pytorch` | 1.0 | 200M | PyTorch | 2,048 |
|
||||
| `google/timesfm-1.0-200m` | 1.0 | 200M | JAX | 2,048 |
|
||||
|
||||
---
|
||||
|
||||
## Output Shape Reference
|
||||
|
||||
| Output | Shape | Description |
|
||||
| ------ | ----- | ----------- |
|
||||
| `point_forecast` | `(B, H)` | Median forecast for B series, H steps |
|
||||
| `quantile_forecast` | `(B, H, 10)` | Full quantile distribution |
|
||||
| `quantile_forecast[:,:,0]` | `(B, H)` | Mean |
|
||||
| `quantile_forecast[:,:,1]` | `(B, H)` | 10th percentile |
|
||||
| `quantile_forecast[:,:,5]` | `(B, H)` | 50th percentile (= point_forecast) |
|
||||
| `quantile_forecast[:,:,9]` | `(B, H)` | 90th percentile |
|
||||
|
||||
Where `B` = batch size (number of input series), `H` = forecast horizon.
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Cause | Fix |
|
||||
| ----- | ----- | --- |
|
||||
| `RuntimeError: Model is not compiled` | Called `forecast()` before `compile()` | Call `model.compile(ForecastConfig(...))` first |
|
||||
| `torch.cuda.OutOfMemoryError` | Batch too large for GPU | Reduce `per_core_batch_size` |
|
||||
| `ValueError: inputs must be list` | Passed array instead of list | Wrap in list: `[array]` |
|
||||
| `HfHubHTTPError` | Download failed | Check internet, set `HF_HOME` to writable dir |
|
||||
@@ -0,0 +1,272 @@
|
||||
# Data Preparation for TimesFM
|
||||
|
||||
## Input Format
|
||||
|
||||
TimesFM accepts a **list of 1-D numpy arrays**. Each array represents one
|
||||
univariate time series.
|
||||
|
||||
```python
|
||||
inputs = [
|
||||
np.array([1.0, 2.0, 3.0, 4.0, 5.0]), # Series 1
|
||||
np.array([10.0, 20.0, 15.0, 25.0]), # Series 2 (different length)
|
||||
np.array([100.0, 110.0, 105.0, 115.0, 120.0, 130.0]), # Series 3
|
||||
]
|
||||
```
|
||||
|
||||
### Key Properties
|
||||
|
||||
- **Variable lengths**: Series in the same batch can have different lengths
|
||||
- **Float values**: Use `np.float32` or `np.float64`
|
||||
- **1-D only**: Each array must be 1-dimensional (not 2-D matrix rows)
|
||||
- **NaN handling**: Leading NaNs are stripped; internal NaNs are linearly interpolated
|
||||
|
||||
## Loading from Common Formats
|
||||
|
||||
### CSV — Single Series (Long Format)
|
||||
|
||||
```python
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
df = pd.read_csv("data.csv", parse_dates=["date"])
|
||||
values = df["value"].values.astype(np.float32)
|
||||
inputs = [values]
|
||||
```
|
||||
|
||||
### CSV — Multiple Series (Wide Format)
|
||||
|
||||
```python
|
||||
df = pd.read_csv("data.csv", parse_dates=["date"], index_col="date")
|
||||
inputs = [df[col].dropna().values.astype(np.float32) for col in df.columns]
|
||||
```
|
||||
|
||||
### CSV — Long Format with ID Column
|
||||
|
||||
```python
|
||||
df = pd.read_csv("data.csv", parse_dates=["date"])
|
||||
inputs = []
|
||||
for series_id, group in df.groupby("series_id"):
|
||||
values = group.sort_values("date")["value"].values.astype(np.float32)
|
||||
inputs.append(values)
|
||||
```
|
||||
|
||||
### Pandas DataFrame
|
||||
|
||||
```python
|
||||
# Single column
|
||||
inputs = [df["temperature"].values.astype(np.float32)]
|
||||
|
||||
# Multiple columns
|
||||
inputs = [df[col].dropna().values.astype(np.float32) for col in numeric_cols]
|
||||
```
|
||||
|
||||
### Numpy Arrays
|
||||
|
||||
```python
|
||||
# 2-D array (rows = series, cols = time steps)
|
||||
data = np.load("timeseries.npy") # shape (N, T)
|
||||
inputs = [data[i] for i in range(data.shape[0])]
|
||||
|
||||
# Or from 1-D
|
||||
inputs = [np.sin(np.linspace(0, 10, 200))]
|
||||
```
|
||||
|
||||
### Excel
|
||||
|
||||
```python
|
||||
df = pd.read_excel("data.xlsx", sheet_name="Sheet1")
|
||||
inputs = [df[col].dropna().values.astype(np.float32) for col in df.select_dtypes(include=[np.number]).columns]
|
||||
```
|
||||
|
||||
### Parquet
|
||||
|
||||
```python
|
||||
df = pd.read_parquet("data.parquet")
|
||||
inputs = [df[col].dropna().values.astype(np.float32) for col in df.select_dtypes(include=[np.number]).columns]
|
||||
```
|
||||
|
||||
### JSON
|
||||
|
||||
```python
|
||||
import json
|
||||
|
||||
with open("data.json") as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Assumes {"series_name": [values...], ...}
|
||||
inputs = [np.array(values, dtype=np.float32) for values in data.values()]
|
||||
```
|
||||
|
||||
## NaN Handling
|
||||
|
||||
TimesFM handles NaN values automatically:
|
||||
|
||||
### Leading NaNs
|
||||
|
||||
Stripped before feeding to the model:
|
||||
|
||||
```python
|
||||
# Input: [NaN, NaN, 1.0, 2.0, 3.0]
|
||||
# Actual: [1.0, 2.0, 3.0]
|
||||
```
|
||||
|
||||
### Internal NaNs
|
||||
|
||||
Linearly interpolated:
|
||||
|
||||
```python
|
||||
# Input: [1.0, NaN, 3.0, NaN, NaN, 6.0]
|
||||
# Actual: [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
|
||||
```
|
||||
|
||||
### Trailing NaNs
|
||||
|
||||
**Not handled** — drop them before passing to the model:
|
||||
|
||||
```python
|
||||
values = df["value"].values.astype(np.float32)
|
||||
# Remove trailing NaNs
|
||||
while len(values) > 0 and np.isnan(values[-1]):
|
||||
values = values[:-1]
|
||||
inputs = [values]
|
||||
```
|
||||
|
||||
### Best Practice
|
||||
|
||||
```python
|
||||
def clean_series(arr: np.ndarray) -> np.ndarray:
|
||||
"""Clean a time series for TimesFM input."""
|
||||
arr = np.asarray(arr, dtype=np.float32)
|
||||
# Remove trailing NaNs
|
||||
while len(arr) > 0 and np.isnan(arr[-1]):
|
||||
arr = arr[:-1]
|
||||
# Replace inf with NaN (will be interpolated)
|
||||
arr[np.isinf(arr)] = np.nan
|
||||
return arr
|
||||
|
||||
inputs = [clean_series(df[col].values) for col in cols]
|
||||
```
|
||||
|
||||
## Context Length Considerations
|
||||
|
||||
| Context Length | Use Case | Notes |
|
||||
| -------------- | -------- | ----- |
|
||||
| 64–256 | Quick prototyping | Minimal context, fast |
|
||||
| 256–512 | Daily data, ~1 year | Good balance |
|
||||
| 512–1024 | Daily data, ~2-3 years | Standard production |
|
||||
| 1024–4096 | Hourly data, weekly patterns | More context = better |
|
||||
| 4096–16384 | High-frequency, long patterns | TimesFM 2.5 maximum |
|
||||
|
||||
**Rule of thumb**: Provide at least 3–5 full cycles of the dominant pattern
|
||||
(e.g., for weekly seasonality with daily data, provide at least 21–35 days).
|
||||
|
||||
## Covariates (XReg)
|
||||
|
||||
TimesFM 2.5 supports exogenous variables through the `forecast_with_covariates()` API.
|
||||
|
||||
### Types of Covariates
|
||||
|
||||
| Type | Description | Example |
|
||||
| ---- | ----------- | ------- |
|
||||
| **Dynamic numerical** | Time-varying numeric features | Temperature, price, promotion spend |
|
||||
| **Dynamic categorical** | Time-varying categorical features | Day of week, holiday flag |
|
||||
| **Static categorical** | Fixed per-series features | Store ID, region, product category |
|
||||
|
||||
### Preparing Covariates
|
||||
|
||||
Each covariate must have length `context + horizon` for each series:
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
|
||||
context_len = 100 # length of historical data
|
||||
horizon = 24 # forecast horizon
|
||||
total_len = context_len + horizon
|
||||
|
||||
# Dynamic numerical: temperature forecast for each series
|
||||
temp = [
|
||||
np.random.randn(total_len).astype(np.float32), # Series 1
|
||||
np.random.randn(total_len).astype(np.float32), # Series 2
|
||||
]
|
||||
|
||||
# Dynamic categorical: day of week (0-6) for each series
|
||||
dow = [
|
||||
np.tile(np.arange(7), total_len // 7 + 1)[:total_len], # Series 1
|
||||
np.tile(np.arange(7), total_len // 7 + 1)[:total_len], # Series 2
|
||||
]
|
||||
|
||||
# Static categorical: one label per series
|
||||
regions = ["east", "west"]
|
||||
|
||||
# Forecast with covariates
|
||||
point, quantiles = model.forecast_with_covariates(
|
||||
inputs=[values1, values2],
|
||||
dynamic_numerical_covariates={"temperature": temp},
|
||||
dynamic_categorical_covariates={"day_of_week": dow},
|
||||
static_categorical_covariates={"region": regions},
|
||||
xreg_mode="xreg + timesfm",
|
||||
)
|
||||
```
|
||||
|
||||
### XReg Modes
|
||||
|
||||
| Mode | Description |
|
||||
| ---- | ----------- |
|
||||
| `"xreg + timesfm"` | Covariates processed first, then combined with TimesFM forecast |
|
||||
| `"timesfm + xreg"` | TimesFM forecast first, then adjusted by covariates |
|
||||
|
||||
## Common Data Issues
|
||||
|
||||
### Issue: Series too short
|
||||
|
||||
TimesFM needs at least 1 data point, but more context = better forecasts.
|
||||
|
||||
```python
|
||||
MIN_LENGTH = 32 # Practical minimum for meaningful forecasts
|
||||
|
||||
inputs = [
|
||||
arr for arr in raw_inputs
|
||||
if len(arr[~np.isnan(arr)]) >= MIN_LENGTH
|
||||
]
|
||||
```
|
||||
|
||||
### Issue: Series with constant values
|
||||
|
||||
Constant series may produce NaN or zero-width prediction intervals:
|
||||
|
||||
```python
|
||||
for i, arr in enumerate(inputs):
|
||||
if np.std(arr[~np.isnan(arr)]) < 1e-10:
|
||||
print(f"⚠️ Series {i} is constant — forecast will be flat")
|
||||
```
|
||||
|
||||
### Issue: Extreme outliers
|
||||
|
||||
Large outliers can destabilize forecasts even with normalization:
|
||||
|
||||
```python
|
||||
def clip_outliers(arr: np.ndarray, n_sigma: float = 5.0) -> np.ndarray:
|
||||
"""Clip values beyond n_sigma standard deviations."""
|
||||
mu = np.nanmean(arr)
|
||||
sigma = np.nanstd(arr)
|
||||
if sigma > 0:
|
||||
arr = np.clip(arr, mu - n_sigma * sigma, mu + n_sigma * sigma)
|
||||
return arr
|
||||
```
|
||||
|
||||
### Issue: Mixed frequencies in batch
|
||||
|
||||
TimesFM handles each series independently, so you can mix frequencies:
|
||||
|
||||
```python
|
||||
inputs = [
|
||||
daily_sales, # 365 points
|
||||
weekly_revenue, # 52 points
|
||||
monthly_users, # 24 points
|
||||
]
|
||||
# All forecasted in one batch — TimesFM handles different lengths
|
||||
point, q = model.forecast(horizon=12, inputs=inputs)
|
||||
```
|
||||
|
||||
However, the `horizon` is shared. If you need different horizons per series,
|
||||
forecast in separate calls.
|
||||
@@ -0,0 +1,201 @@
|
||||
# System Requirements for TimesFM
|
||||
|
||||
## Hardware Tiers
|
||||
|
||||
TimesFM can run on a variety of hardware configurations. This guide helps you
|
||||
choose the right setup and tune performance for your machine.
|
||||
|
||||
### Tier 1: Minimal (CPU-Only, 4–8 GB RAM)
|
||||
|
||||
- **Use case**: Light exploration, single-series forecasting, prototyping
|
||||
- **Model**: TimesFM 2.5 (200M) only
|
||||
- **Batch size**: `per_core_batch_size=4`
|
||||
- **Context**: Limit `max_context=512`
|
||||
- **Expected speed**: ~2–5 seconds per 100-point series
|
||||
|
||||
```python
|
||||
model.compile(timesfm.ForecastConfig(
|
||||
max_context=512,
|
||||
max_horizon=128,
|
||||
per_core_batch_size=4,
|
||||
normalize_inputs=True,
|
||||
use_continuous_quantile_head=True,
|
||||
fix_quantile_crossing=True,
|
||||
))
|
||||
```
|
||||
|
||||
### Tier 2: Standard (CPU 16 GB or GPU 4–8 GB VRAM)
|
||||
|
||||
- **Use case**: Batch forecasting (dozens of series), evaluation, production prototypes
|
||||
- **Model**: TimesFM 2.5 (200M)
|
||||
- **Batch size**: `per_core_batch_size=32` (CPU) or `64` (GPU)
|
||||
- **Context**: `max_context=1024`
|
||||
- **Expected speed**: ~0.5–1 second per 100-point series (GPU)
|
||||
|
||||
```python
|
||||
model.compile(timesfm.ForecastConfig(
|
||||
max_context=1024,
|
||||
max_horizon=256,
|
||||
per_core_batch_size=64,
|
||||
normalize_inputs=True,
|
||||
use_continuous_quantile_head=True,
|
||||
fix_quantile_crossing=True,
|
||||
))
|
||||
```
|
||||
|
||||
### Tier 3: Production (GPU 16+ GB VRAM or Apple Silicon 32+ GB)
|
||||
|
||||
- **Use case**: Large-scale batch forecasting (thousands of series), long context
|
||||
- **Model**: TimesFM 2.5 (200M)
|
||||
- **Batch size**: `per_core_batch_size=128–256`
|
||||
- **Context**: `max_context=4096` or higher
|
||||
- **Expected speed**: ~0.1–0.3 seconds per 100-point series
|
||||
|
||||
```python
|
||||
model.compile(timesfm.ForecastConfig(
|
||||
max_context=4096,
|
||||
max_horizon=256,
|
||||
per_core_batch_size=128,
|
||||
normalize_inputs=True,
|
||||
use_continuous_quantile_head=True,
|
||||
fix_quantile_crossing=True,
|
||||
))
|
||||
```
|
||||
|
||||
### Tier 4: Legacy Models (v1.0/v2.0 — 500M parameters)
|
||||
|
||||
- **⚠️ WARNING**: TimesFM v2.0 (500M) requires **≥ 16 GB RAM** (CPU) or **≥ 8 GB VRAM** (GPU)
|
||||
- **⚠️ WARNING**: TimesFM v1.0 legacy JAX version may require **≥ 32 GB RAM**
|
||||
- **Recommendation**: Unless you specifically need a legacy checkpoint, use TimesFM 2.5
|
||||
|
||||
## Memory Estimation
|
||||
|
||||
### CPU Memory (RAM)
|
||||
|
||||
Approximate RAM usage during inference:
|
||||
|
||||
| Component | TimesFM 2.5 (200M) | TimesFM 2.0 (500M) |
|
||||
| --------- | ------------------- | ------------------- |
|
||||
| Model weights | ~800 MB | ~2 GB |
|
||||
| Runtime overhead | ~500 MB | ~1 GB |
|
||||
| Input/output buffers | ~200 MB per 1000 series | ~500 MB per 1000 series |
|
||||
| **Total (small batch)** | **~1.5 GB** | **~3.5 GB** |
|
||||
| **Total (large batch)** | **~3 GB** | **~6 GB** |
|
||||
|
||||
**Formula**: `RAM ≈ model_weights + 0.5 GB + (0.2 MB × num_series × context_length / 1000)`
|
||||
|
||||
### GPU Memory (VRAM)
|
||||
|
||||
| Component | TimesFM 2.5 (200M) |
|
||||
| --------- | ------------------- |
|
||||
| Model weights | ~800 MB |
|
||||
| KV cache + activations | ~200–500 MB (scales with context) |
|
||||
| Batch buffers | ~100 MB per 100 series at context=1024 |
|
||||
| **Total (batch=32)** | **~1.2 GB** |
|
||||
| **Total (batch=128)** | **~1.8 GB** |
|
||||
| **Total (batch=256)** | **~2.5 GB** |
|
||||
|
||||
### Disk Space
|
||||
|
||||
| Item | Size |
|
||||
| ---- | ---- |
|
||||
| TimesFM 2.5 safetensors | ~800 MB |
|
||||
| Hugging Face cache overhead | ~200 MB |
|
||||
| **Total download** | **~1 GB** |
|
||||
|
||||
Model weights are downloaded once from Hugging Face Hub and cached in
|
||||
`~/.cache/huggingface/` (or `$HF_HOME`).
|
||||
|
||||
## GPU Selection Guide
|
||||
|
||||
### NVIDIA GPUs (CUDA)
|
||||
|
||||
| GPU | VRAM | Recommended batch | Notes |
|
||||
| --- | ---- | ----------------- | ----- |
|
||||
| RTX 3060 | 12 GB | 64 | Good entry-level |
|
||||
| RTX 3090 / 4090 | 24 GB | 256 | Excellent for production |
|
||||
| A100 (40 GB) | 40 GB | 512 | Cloud/HPC |
|
||||
| A100 (80 GB) | 80 GB | 1024 | Cloud/HPC |
|
||||
| T4 | 16 GB | 128 | Cloud (Colab, AWS) |
|
||||
| V100 | 16–32 GB | 128–256 | Cloud |
|
||||
|
||||
### Apple Silicon (MPS)
|
||||
|
||||
| Chip | Unified Memory | Recommended batch | Notes |
|
||||
| ---- | -------------- | ----------------- | ----- |
|
||||
| M1 | 8–16 GB | 16–32 | Works, slower than CUDA |
|
||||
| M1 Pro/Max | 16–64 GB | 32–128 | Good performance |
|
||||
| M2/M3/M4 Pro/Max | 18–128 GB | 64–256 | Excellent |
|
||||
|
||||
### CPU Only
|
||||
|
||||
Works on any CPU with sufficient RAM. Expect 5–20× slower than GPU.
|
||||
|
||||
## Python and Package Requirements
|
||||
|
||||
| Requirement | Minimum | Recommended |
|
||||
| ----------- | ------- | ----------- |
|
||||
| Python | 3.10 | 3.12+ |
|
||||
| numpy | 1.26.4 | latest |
|
||||
| torch | 2.0.0 | latest |
|
||||
| huggingface_hub | 0.23.0 | latest |
|
||||
| safetensors | 0.5.3 | latest |
|
||||
|
||||
### Optional Dependencies
|
||||
|
||||
| Package | Purpose | Install |
|
||||
| ------- | ------- | ------- |
|
||||
| jax | Flax backend | `pip install jax[cuda]` |
|
||||
| flax | Flax backend | `pip install flax` |
|
||||
| scikit-learn | XReg covariates | `pip install scikit-learn` |
|
||||
|
||||
## Operating System Compatibility
|
||||
|
||||
| OS | Status | Notes |
|
||||
| -- | ------ | ----- |
|
||||
| Linux (Ubuntu 20.04+) | ✅ Fully supported | Best performance with CUDA |
|
||||
| macOS 13+ (Ventura) | ✅ Fully supported | MPS acceleration on Apple Silicon |
|
||||
| Windows 11 + WSL2 | ✅ Supported | Use WSL2 for best experience |
|
||||
| Windows (native) | ⚠️ Partial | PyTorch works, some edge cases |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Out of Memory (OOM)
|
||||
|
||||
```python
|
||||
# Reduce batch size
|
||||
model.compile(timesfm.ForecastConfig(
|
||||
per_core_batch_size=4, # Start very small
|
||||
max_context=512, # Reduce context
|
||||
...
|
||||
))
|
||||
|
||||
# Process in chunks
|
||||
for i in range(0, len(inputs), 50):
|
||||
chunk = inputs[i:i+50]
|
||||
p, q = model.forecast(horizon=H, inputs=chunk)
|
||||
```
|
||||
|
||||
### Slow Inference on CPU
|
||||
|
||||
```python
|
||||
# Ensure matmul precision is set
|
||||
import torch
|
||||
torch.set_float32_matmul_precision("high")
|
||||
|
||||
# Use smaller context
|
||||
model.compile(timesfm.ForecastConfig(
|
||||
max_context=256, # Shorter context = faster
|
||||
...
|
||||
))
|
||||
```
|
||||
|
||||
### Model Download Fails
|
||||
|
||||
```bash
|
||||
# Set a different cache directory
|
||||
export HF_HOME=/path/with/more/space
|
||||
|
||||
# Or download manually
|
||||
huggingface-cli download google/timesfm-2.5-200m-pytorch
|
||||
```
|
||||
Reference in New Issue
Block a user