MOMENT: A Family of Open Time-Series Foundation Models¶
    Multi-Task Time-Series Foundation Model
Overview¶
MOMENT is a family of open-source time-series foundation models designed to handle multiple tasks including forecasting, classification, anomaly detection, and imputation. It uses a masked autoencoder architecture with a focus on versatility across different time-series domains.
Paper¶
MOMENT: A Family of Open Time-series Foundation Models
Key Features¶
- ✅ Multi-task learning (forecasting, classification, detection, imputation)
- ✅ Masked autoencoder architecture
- ✅ Pre-trained on diverse time-series datasets
- ✅ Flexible fine-tuning strategies
- ✅ Open-source and community-driven
Quick Start¶
Forecasting¶
from samay.model import MomentModel
from samay.dataset import MomentDataset
# Model configuration
config = {
    "task_name": "forecasting",
    "forecast_horizon": 192,
    "freeze_encoder": True,
    "freeze_embedder": True,
    "freeze_head": False,
}
# Load model
model = MomentModel(config)
# Load dataset
train_dataset = MomentDataset(
    name="ett",
    datetime_col="date",
    path="./data/ETTh1.csv",
    mode="train",
    horizon_len=192,
    task_name="forecasting",
)
# Fine-tune
finetuned_model = model.finetune(train_dataset)
# Evaluate
test_dataset = MomentDataset(
    name="ett",
    datetime_col="date",
    path="./data/ETTh1.csv",
    mode="test",
    horizon_len=192,
    task_name="forecasting",
)
avg_loss, trues, preds, histories = model.evaluate(test_dataset)
Supported Tasks¶
MOMENT supports four main tasks:
1. Forecasting¶
Predict future time-series values:
config = {
    "task_name": "forecasting",
    "forecast_horizon": 192,
    "freeze_encoder": True,
    "freeze_embedder": True,
    "freeze_head": False,
}
model = MomentModel(config)
dataset = MomentDataset(
    datetime_col="date",
    path="./data/ETTh1.csv",
    mode="train",
    horizon_len=192,
    task_name="forecasting",
)
2. Classification¶
Classify time-series sequences:
config = {
    "task_name": "classification",
    "num_classes": 5,
    "freeze_encoder": True,
    "freeze_embedder": True,
    "freeze_head": False,
}
model = MomentModel(config)
dataset = MomentDataset(
    datetime_col="date",
    path="./data/classification_data.csv",
    mode="train",
    task_name="classification",
    label_col="label",
)
3. Anomaly Detection¶
Detect anomalies in time series:
config = {
    "task_name": "detection",
    "freeze_encoder": True,
    "freeze_embedder": True,
    "freeze_head": False,
}
model = MomentModel(config)
dataset = MomentDataset(
    datetime_col="date",
    path="./data/ecg_data.csv",
    mode="train",
    task_name="detection",
)
4. Imputation¶
Fill missing values:
config = {
    "task_name": "imputation",
    "freeze_encoder": True,
    "freeze_embedder": False,  # May need to train encoder
    "freeze_head": False,
}
model = MomentModel(config)
dataset = MomentDataset(
    datetime_col="date",
    path="./data/incomplete_data.csv",
    mode="train",
    task_name="imputation",
)
Configuration Parameters¶
Model Configuration¶
| Parameter | Type | Default | Description | 
|---|---|---|---|
| task_name | str | "forecasting" | Task: "forecasting","classification","detection","imputation" | 
| forecast_horizon | int | 192 | Horizon length for forecasting | 
| num_classes | int | None | Number of classes for classification | 
| freeze_encoder | bool | True | Whether to freeze the encoder | 
| freeze_embedder | bool | True | Whether to freeze the embedder | 
| freeze_head | bool | False | Whether to freeze the task head | 
| dropout | float | 0.1 | Dropout rate | 
| learning_rate | float | 1e-4 | Learning rate for fine-tuning | 
Dataset¶
MomentDataset Parameters¶
| Parameter | Type | Default | Description | 
|---|---|---|---|
| name | str | None | Dataset name | 
| datetime_col | str | None | Name of datetime column | 
| path | str | Required | Path to CSV file | 
| mode | str | "train" | "train"or"test" | 
| horizon_len | int | 0 | Forecast horizon | 
| task_name | str | "forecasting" | Task type | 
| label_col | str | "label" | Label column for classification | 
| batchsize | int | 64 | Batch size | 
| boundaries | list | [0, 0, 0] | Custom split boundaries | 
| stride | int | 10 | Stride for sliding window | 
Data Format¶
Forecasting/Imputation¶
date,HUFL,HULL,MUFL,MULL,LUFL,LULL,OT
2016-07-01 00:00:00,5.827,2.009,1.599,0.462,5.677,2.009,6.082
2016-07-01 01:00:00,5.693,2.076,1.492,0.426,5.485,1.942,5.947
...
Classification¶
date,feature1,feature2,feature3,label
2016-07-01 00:00:00,5.827,2.009,1.599,0
2016-07-01 01:00:00,5.693,2.076,1.492,1
...
Anomaly Detection¶
date,value,anomaly
2016-07-01 00:00:00,5.827,0
2016-07-01 01:00:00,5.693,0
2016-07-01 02:00:00,12.456,1
...
Training¶
Forecasting¶
from samay.model import MomentModel
from samay.dataset import MomentDataset
config = {
    "task_name": "forecasting",
    "forecast_horizon": 192,
    "freeze_encoder": True,
    "freeze_embedder": True,
    "freeze_head": False,
}
model = MomentModel(config)
train_dataset = MomentDataset(
    datetime_col="date",
    path="./data/ETTh1.csv",
    mode="train",
    horizon_len=192,
    task_name="forecasting",
    batchsize=64,
)
# Fine-tune
finetuned_model = model.finetune(train_dataset, epochs=10)
Classification¶
config = {
    "task_name": "classification",
    "num_classes": 5,
    "freeze_encoder": True,
    "freeze_embedder": True,
    "freeze_head": False,
}
model = MomentModel(config)
train_dataset = MomentDataset(
    datetime_col="date",
    path="./data/ecg_classification.csv",
    mode="train",
    task_name="classification",
    label_col="label",
    batchsize=32,
)
finetuned_model = model.finetune(train_dataset, epochs=20)
Anomaly Detection¶
config = {
    "task_name": "detection",
    "freeze_encoder": True,
    "freeze_embedder": True,
    "freeze_head": False,
}
model = MomentModel(config)
train_dataset = MomentDataset(
    datetime_col="date",
    path="./data/ecg_anomaly.csv",
    mode="train",
    task_name="detection",
    batchsize=32,
)
finetuned_model = model.finetune(train_dataset, epochs=15)
Evaluation¶
Forecasting Evaluation¶
test_dataset = MomentDataset(
    datetime_col="date",
    path="./data/ETTh1.csv",
    mode="test",
    horizon_len=192,
    task_name="forecasting",
)
avg_loss, trues, preds, histories = model.evaluate(test_dataset)
# Calculate metrics
from samay.metric import mse, mae
import numpy as np
trues = np.array(trues)
preds = np.array(preds)
print(f"MSE: {mse(trues, preds):.4f}")
print(f"MAE: {mae(trues, preds):.4f}")
Classification Evaluation¶
test_dataset = MomentDataset(
    datetime_col="date",
    path="./data/ecg_classification.csv",
    mode="test",
    task_name="classification",
    label_col="label",
)
avg_loss, labels, predictions, _ = model.evaluate(test_dataset)
# Calculate accuracy
from sklearn.metrics import accuracy_score, f1_score
accuracy = accuracy_score(labels, predictions)
f1 = f1_score(labels, predictions, average='weighted')
print(f"Accuracy: {accuracy:.4f}")
print(f"F1 Score: {f1:.4f}")
Anomaly Detection Evaluation¶
test_dataset = MomentDataset(
    datetime_col="date",
    path="./data/ecg_anomaly.csv",
    mode="test",
    task_name="detection",
)
avg_loss, true_labels, anomaly_scores, _ = model.evaluate(test_dataset)
# Calculate ROC-AUC
from sklearn.metrics import roc_auc_score
auc = roc_auc_score(true_labels, anomaly_scores)
print(f"ROC-AUC: {auc:.4f}")
Advanced Usage¶
Multi-Task Transfer Learning¶
Train on one task, fine-tune on another:
# First, train on forecasting
config_forecast = {
    "task_name": "forecasting",
    "forecast_horizon": 192,
    "freeze_encoder": False,
    "freeze_embedder": False,
    "freeze_head": False,
}
model = MomentModel(config_forecast)
forecast_dataset = MomentDataset(
    datetime_col="date",
    path="./data/ETTh1.csv",
    mode="train",
    horizon_len=192,
    task_name="forecasting",
)
model.finetune(forecast_dataset, epochs=10)
# Now switch to classification
config_class = {
    "task_name": "classification",
    "num_classes": 5,
    "freeze_encoder": True,  # Freeze what we learned
    "freeze_embedder": True,
    "freeze_head": False,
}
# Update model config
model.update_config(config_class)
class_dataset = MomentDataset(
    datetime_col="date",
    path="./data/classification.csv",
    mode="train",
    task_name="classification",
    label_col="label",
)
model.finetune(class_dataset, epochs=5)
Custom Training Loop¶
import torch
from torch.optim import AdamW
model = MomentModel(config)
train_loader = train_dataset.get_data_loader()
optimizer = AdamW(model.parameters(), lr=1e-4)
criterion = torch.nn.MSELoss()
for epoch in range(10):
    total_loss = 0
    for batch in train_loader:
        optimizer.zero_grad()
        # Unpack batch based on task
        if config["task_name"] == "forecasting":
            timeseries, input_mask, forecast = batch
            predictions = model(timeseries, input_mask)
            loss = criterion(predictions, forecast)
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    print(f"Epoch {epoch}: Loss = {total_loss / len(train_loader):.4f}")
Visualization¶
Forecasting Results¶
import matplotlib.pyplot as plt
import numpy as np
avg_loss, trues, preds, histories = model.evaluate(test_dataset)
trues = np.array(trues)
preds = np.array(preds)
histories = np.array(histories)
# Plot multiple channels
fig, axes = plt.subplots(2, 2, figsize=(16, 10))
axes = axes.flatten()
for i in range(min(4, trues.shape[1])):
    ax = axes[i]
    history = histories[0, i, :]
    true = trues[0, i, :]
    pred = preds[0, i, :]
    ax.plot(range(len(history)), history, label="History", alpha=0.7)
    ax.plot(
        range(len(history), len(history) + len(true)),
        true,
        label="Ground Truth",
        linestyle="--"
    )
    ax.plot(
        range(len(history), len(history) + len(pred)),
        pred,
        label="Prediction"
    )
    ax.set_title(f"Channel {i}")
    ax.legend()
    ax.grid(alpha=0.3)
plt.suptitle("MOMENT Multi-Channel Forecasting")
plt.tight_layout()
plt.show()
Anomaly Detection Results¶
import matplotlib.pyplot as plt
# Get anomaly scores
avg_loss, true_labels, anomaly_scores, sequences = model.evaluate(test_dataset)
plt.figure(figsize=(14, 6))
# Plot time series
plt.subplot(2, 1, 1)
plt.plot(sequences[0], label="Time Series")
plt.scatter(
    np.where(true_labels[0] == 1)[0],
    sequences[0][true_labels[0] == 1],
    color='red',
    label='True Anomalies',
    s=100
)
plt.legend()
plt.title("Time Series with Anomalies")
# Plot anomaly scores
plt.subplot(2, 1, 2)
plt.plot(anomaly_scores[0], label="Anomaly Score")
plt.axhline(y=np.percentile(anomaly_scores[0], 95), color='r', linestyle='--', label='Threshold (95%)')
plt.legend()
plt.title("Anomaly Scores")
plt.tight_layout()
plt.show()
Tips and Best Practices¶
1. Task Selection¶
- Use forecasting for predicting future values
- Use classification for categorizing sequences
- Use detection for identifying anomalies
- Use imputation for filling missing data
2. Fine-Tuning Strategy¶
- Start with frozen encoder and embedder
- Gradually unfreeze if performance is poor
- Use higher learning rates for task heads
3. Sequence Length¶
- MOMENT uses fixed sequence length of 512
- Longer sequences capture more context
- Shorter sequences are faster
4. Multi-Task Learning¶
- Pre-train on related tasks
- Fine-tune on target task
- Freeze learned features when switching tasks
Common Issues¶
Poor Classification Performance¶
Try unfreezing more layers:
config = {
    "task_name": "classification",
    "num_classes": 5,
    "freeze_encoder": False,  # Unfreeze encoder
    "freeze_embedder": False, # Unfreeze embedder
    "freeze_head": False,
}
High Memory Usage¶
Reduce batch size:
Missing Values¶
MOMENT handles missing values through imputation:
API Reference¶
For detailed API documentation, see:
Examples¶
See the Examples page for complete working examples of all tasks.