[Accepted at ICML 2026 π ] TimeSAE: Sparse Decoding for Faithful Explanations of Black-Box Time Series Models
6
stars
28
commits
Python
primary language
Jul 11, 2026
updated
Official implementation of TimeSAE, accepted at ICML 2026. TimeSAE is a novel framework for explaining black-box time series models using Sparse Autoencoders with complete functional ANOVA decomposition and temporal convolution layers.
A PyTorch implementation is provided, with a JAX version available here.



/notebooks/google-colab/git clone <repository-url>
cd TimeSAE
pip install -r requirements.txt
We provide pretrained TimeSAE models for all paper experiments. No need to train from scratch!
# List available models
python checkpoints/download_checkpoints.py --list
# Download specific model
python checkpoints/download_checkpoints.py --model transformer --dataset ecg
# Download all datasets for a model
python checkpoints/download_checkpoints.py --model chronos --all-datasets
# Download all models for a dataset
python checkpoints/download_checkpoints.py --dataset freqshapes --all-models
Anonymous Download Link: https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/pretrained/
from utils.checkpoint_loader import load_explanation_pipeline
from utils import TimeSeriesVisualizer
# Load pretrained TimeSAE explaining Transformer on ECG data
timesae, transformer_model = load_explanation_pipeline('transformer', 'ecg')
# Generate sample ECG data (or use your own)
import torch
ecg_data = torch.randn(5, 1, 187) # 5 samples, 1 lead, 187 timesteps
# Generate explanations instantly
concepts, x_recon, contributions = timesae(ecg_data)
# Get black-box predictions
predictions = transformer_model(ecg_data)
print(f"Concept activations: {concepts.shape}")
print(f"ANOVA decomposition orders: {list(contributions.keys())}")
# Visualize explanations
visualizer = TimeSeriesVisualizer()
visualizer.plot_time_series_with_concepts(
ecg_data, x_recon, concepts, contributions,
sample_idx=0, save_path='ecg_explanation.png'
)
| TimeSAE Model | Datasets | AUPRC (FreqShapes) | Download Size |
|---|---|---|---|
| Transformer | 6 datasets | 0.950Β±0.011 | ~270MB |
| PatchTS | 6 datasets | 0.842Β±0.014 | ~260MB |
| Chronos | 6 datasets | 0.930Β±0.016 | ~265MB |
| TimeGPT | 6 datasets | 0.958Β±0.016 | ~270MB |
| TimeFM | 6 datasets | 0.945Β±0.012 | ~275MB |
| DLinear | 6 datasets | 0.825Β±0.018 | ~250MB |
Datasets: FreqShapes, SeqComb-UV, ECG, PAM, ETTH-1, ETTH-2
Based on the TimeSAE paper, the following parameters were used:
r Γ (D Γ T) where r = 2.0 (expansion ratio)1283 for all temporal convolutions[1, 2, 4, 8] for 4-layer temporal encoder0.10.10.5 (fixed across all experiments)0.10.11.01e-31e-432 per GPU (effective batch size: 96 on 3 GPUs)Ξ² = (0.9, 0.999)T_max = 100000, eta_min = 1e-6max_norm = 1.0U(0.01, 0.11)2.0, decay to 1.0 during trainingk = concept_dim // 10 (10% activation)import torch
from models import TimeSAE
from trainer import TimeSAETrainer, setup_model_and_optimizer
from utils import DatasetProcessor
# Create model with paper parameters
input_shape = (10, 128) # (D=10 features, T=128 timesteps)
model, loss_fn, optimizer, scheduler = setup_model_and_optimizer(
input_shape=input_shape,
concept_dim=int(2.0 * 10 * 128), # r=2.0 expansion ratio
learning_rate=1e-3,
weight_decay=1e-4
)
# Generate synthetic data
processor = DatasetProcessor()
data = processor.create_synthetic_data(
n_samples=1000,
n_features=10,
n_timesteps=128
)
# Train model
trainer = TimeSAETrainer(model, loss_fn, optimizer, scheduler)
# trainer.train(train_dataloader, val_dataloader, num_epochs=100)
# Quick demo with any model/dataset combination
from utils.checkpoint_loader import quick_explanation_demo
# Try different combinations
quick_explanation_demo('transformer', 'ecg') # Medical data
quick_explanation_demo('chronos', 'freqshapes') # Synthetic patterns
quick_explanation_demo('timegpt', 'pam') # Activity recognition
# Compare all models on same dataset
from utils.checkpoint_loader import compare_models_on_dataset
results = compare_models_on_dataset('ecg')
from utils.checkpoint_loader import load_timesae
from utils import ConceptInterpreter
# Load specific model
timesae = load_timesae('transformer', 'ecg')
# Generate explanations
x = torch.randn(5, 1, 187) # ECG data
concepts, x_recon, contributions = timesae(x)
# Interpret concepts
interpreter = ConceptInterpreter()
analysis = interpreter.analyze_concept_activations(concepts)
interpreter.assign_concept_names(analysis)
summary = interpreter.get_concept_summary()
print("Top concepts:")
for name, description in list(summary.items())[:5]:
print(f" {name}: {description}")
from utils import TimeSeriesVisualizer
visualizer = TimeSeriesVisualizer()
# Plot explanations
visualizer.plot_time_series_with_concepts(
x, x_recon, concepts, contributions,
sample_idx=0, top_k_concepts=10,
save_path='explanation.png'
)
# Plot concept heatmap
visualizer.plot_concept_heatmap(
concepts, save_path='concepts.png'
)
python main.py \
--n_features 10 \
--n_timesteps 128 \
--batch_size 32 \
--learning_rate 1e-3 \
--num_epochs 100 \
--synthetic_data \
--use_black_box \
--generate_explanations
chmod +x run_distributed.sh
./run_distributed.sh
Use the provided SLURM script for cluster environments:
sbatch run_slurm_3a100.slurm
# Train black-box models
python trainers/blackbox_trainer/train_transformer.py --seq_len 128 --n_vars 10
python trainers/blackbox_trainer/train_patchts.py --patch_len 16 --stride 8
python trainers/blackbox_trainer/train_dlinear.py --seq_len 96 --task_type forecasting
from torch.utils.data import Dataset, DataLoader
class CustomTimeSeriesDataset(Dataset):
def __init__(self, data, labels=None):
self.data = torch.tensor(data, dtype=torch.float32)
self.labels = labels
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
if self.labels is not None:
return self.data[idx], self.labels[idx]
return self.data[idx]
# Use with TimeSAE
dataset = CustomTimeSeriesDataset(your_data, your_labels)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
The implementation includes all metrics from the paper:
||f(x) - f(ex-)||Β² where concepts are removed| Dataset | Transformer | PatchTS | TimeGPT | Chronos |
|---|---|---|---|---|
| FreqShapes | 0.950Β±0.011 | 0.842Β±0.014 | 0.958Β±0.016 | 0.930Β±0.016 |
| SeqComb-UV | 0.811Β±0.022 | 0.715Β±0.019 | 0.837Β±0.023 | 0.807Β±0.022 |
| ECG | 0.950Β±0.011 | 0.980Β±0.011 | 0.912Β±0.020 | 0.894Β±0.015 |
| PAM | 0.998Β±0.012 | 0.981Β±0.012 | 0.957Β±0.022 | 0.939Β±0.017 |
| Dataset | Score |
|---|---|
| ECG | 1.78Β±0.078 |
| PAM | 2.15Β±0.080 |
| ETTH-1 | 2.12Β±0.072 |
| ETTH-2 | 2.09Β±0.069 |
# Run all paper experiments
chmod +x scripts/reproduce_all_results.sh
./scripts/reproduce_all_results.sh
# Or use SLURM for cluster
sbatch run_slurm_3a100.slurm
TimeSAE/
βββ models.py # Core TimeSAE architecture
βββ loss.py # Loss functions and counterfactual generation
βββ trainer.py # Distributed training logic
βββ utils.py # Evaluation, visualization, and utilities
βββ main.py # Main training script
βββ blackbox_models/ # All black-box model implementations
β βββ chronos.py # Chronos (pretrained)
β βββ timegpt.py # TimeGPT (pretrained)
β βββ timefm.py # TimeFM (pretrained)
β βββ transformer.py # Transformer (trainable)
β βββ patchts.py # PatchTS (trainable)
β βββ dlinear.py # DLinear (trainable)
βββ trainers/blackbox_trainer/ # Training scripts for black-box models
βββ checkpoints/ # Pretrained model checkpoints
β βββ README.md # Checkpoint documentation
β βββ timesae_transformer/ # TimeSAE explaining Transformers
β βββ timesae_chronos/ # TimeSAE explaining Chronos
β βββ blackbox_models/ # Trained black-box models
βββ scripts/
β βββ download_checkpoints.py # Automated checkpoint downloader
β βββ reproduce_all_results.sh # Complete reproduction script
βββ run_distributed.sh # 3 GPU training script
βββ run_slurm_3a100.slurm # SLURM batch script
βββ requirements.txt # Python dependencies
βββ README.md # This file
βββ REPRODUCIBILITY.md # Complete reproduction guide
βββ README_USAGE_EXAMPLES.md # Practical usage examples
# All interaction orders from 0 to concept_dim
for order in range(1, concept_dim + 1):
combinations = list(combinations(range(concept_dim), order))
# Process all possible concept combinations
# Multi-layer dilated convolutions
dilations = [1, 2, 4, 8] # Paper specification
for i, dilation in enumerate(dilations):
conv_layer = TemporalConvBlock(
hidden_dim, hidden_dim,
kernel_size=3, dilation=dilation
)
# 3 A100 GPU configuration
WORLD_SIZE = 3
MASTER_PORT = 12355
backend = 'nccl'
# Effective batch size: 32 Γ 3 = 96
python scripts/download_checkpoints.pytorch.cuda.amptorch.compile(model)num_workersBased on the TimeSAE paper, the following parameters were used:
r Γ (D Γ T) where r = 2.0 (expansion ratio)1283 for all temporal convolutions[1, 2, 4, 8] for 4-layer temporal encoder0.10.10.5 (fixed across all experiments)0.10.11.01e-31e-432 per GPU (effective batch size: 96 on 3 GPUs)Ξ² = (0.9, 0.999)T_max = 100000, eta_min = 1e-6max_norm = 1.0U(0.01, 0.11)2.0, decay to 1.0 during trainingk = concept_dim // 10 (10% activation)import torch
from models import TimeSAE
from trainer import TimeSAETrainer, setup_model_and_optimizer
from utils import DatasetProcessor
# Create model with paper parameters
input_shape = (10, 128) # (D=10 features, T=128 timesteps)
model, loss_fn, optimizer, scheduler = setup_model_and_optimizer(
input_shape=input_shape,
concept_dim=int(2.0 * 10 * 128), # r=2.0 expansion ratio
learning_rate=1e-3,
weight_decay=1e-4
)
# Generate synthetic data
processor = DatasetProcessor()
data = processor.create_synthetic_data(
n_samples=1000,
n_features=10,
n_timesteps=128
)
# Train model
trainer = TimeSAETrainer(model, loss_fn, optimizer, scheduler)
# trainer.train(train_dataloader, val_dataloader, num_epochs=100)
# Load trained model
model = TimeSAE(input_shape=(10, 128))
model.load_state_dict(torch.load('checkpoints/best_model.pt'))
# Generate explanations
x = torch.randn(5, 10, 128) # Sample time series
concepts, x_recon, contributions = trainer.explain(x)
# Analyze concepts
from utils import ConceptInterpreter
interpreter = ConceptInterpreter()
analysis = interpreter.analyze_concept_activations(concepts)
interpreter.assign_concept_names(analysis)
summary = interpreter.get_concept_summary()
from utils import TimeSeriesVisualizer
visualizer = TimeSeriesVisualizer()
# Plot explanations
visualizer.plot_time_series_with_concepts(
x, x_recon, concepts, contributions,
sample_idx=0, top_k_concepts=10,
save_path='explanation.png'
)
# Plot concept heatmap
visualizer.plot_concept_heatmap(
concepts, save_path='concepts.png'
)
python main.py \
--n_features 10 \
--n_timesteps 128 \
--batch_size 32 \
--learning_rate 1e-3 \
--num_epochs 100 \
--synthetic_data \
--use_black_box \
--generate_explanations
chmod +x run_distributed.sh
./run_distributed.sh
Use the provided SLURM script for cluster environments:
sbatch run_slurm_3a100.slurm
from torch.utils.data import Dataset, DataLoader
class CustomTimeSeriesDataset(Dataset):
def __init__(self, data, labels=None):
self.data = torch.tensor(data, dtype=torch.float32)
self.labels = labels
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
if self.labels is not None:
return self.data[idx], self.labels[idx]
return self.data[idx]
# Use with TimeSAE
dataset = CustomTimeSeriesDataset(your_data, your_labels)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
The implementation includes all metrics from the paper:
||f(x) - f(ex-)||Β² where concepts are removed| Dataset | Transformer | PatchTS | TimeGPT | Chronos |
|---|---|---|---|---|
| FreqShapes | 0.950Β±0.011 | 0.842Β±0.014 | 0.958Β±0.016 | 0.930Β±0.016 |
| SeqComb-UV | 0.811Β±0.022 | 0.715Β±0.019 | 0.837Β±0.023 | 0.807Β±0.022 |
| ECG | 0.950Β±0.011 | 0.980Β±0.011 | 0.912Β±0.020 | 0.894Β±0.015 |
| PAM | 0.998Β±0.012 | 0.981Β±0.012 | 0.957Β±0.022 | 0.939Β±0.017 |
| Dataset | Score |
|---|---|
| ECG | 1.78Β±0.078 |
| PAM | 2.15Β±0.080 |
| ETTH-1 | 2.12Β±0.072 |
| ETTH-2 | 2.09Β±0.069 |
TimeSAE/
βββ models.py # Core TimeSAE architecture
βββ loss.py # Loss functions and counterfactual generation
βββ trainer.py # Distributed training logic
βββ utils.py # Evaluation, visualization, and utilities
βββ main.py # Main training script
βββ run_distributed.sh # Shell script for 3 GPU training
βββ run_slurm_3a100.slurm # SLURM batch script
βββ requirements.txt # Python dependencies
βββ README.md # This file
# All interaction orders from 0 to concept_dim
for order in range(1, concept_dim + 1):
combinations = list(combinations(range(concept_dim), order))
# Process all possible concept combinations
# Multi-layer dilated convolutions
dilations = [1, 2, 4, 8] # Paper specification
for i, dilation in enumerate(dilations):
conv_layer = TemporalConvBlock(
hidden_dim, hidden_dim,
kernel_size=3, dilation=dilation
)
# 3 A100 GPU configuration
WORLD_SIZE = 3
MASTER_PORT = 12355
backend = 'nccl'
# Effective batch size: 32 Γ 3 = 96
torch.cuda.amptorch.compile(model)num_workersThis directory contains pretrained TimeSAE models for explaining different black-box time series models. These checkpoints are provided for users who do not have the computational resources to train TimeSAE from scratch.
checkpoints/
βββ README.md # This file
βββ timesae_transformer/ # TimeSAE explaining Transformers
β βββ freqshapes_best.pt # FreqShapes dataset
β βββ seqcomb_best.pt # SeqComb-UV dataset
β βββ ecg_best.pt # ECG arrhythmia dataset
β βββ pam_best.pt # PAM activity dataset
β βββ etth1_best.pt # ETTH-1 energy dataset
β βββ etth2_best.pt # ETTH-2 energy dataset
βββ timesae_patchts/ # TimeSAE explaining PatchTS
β βββ freqshapes_best.pt
β βββ seqcomb_best.pt
β βββ ecg_best.pt
β βββ pam_best.pt
β βββ etth1_best.pt
β βββ etth2_best.pt
βββ timesae_chronos/ # TimeSAE explaining Chronos
β βββ freqshapes_best.pt
β βββ seqcomb_best.pt
β βββ ecg_best.pt
β βββ pam_best.pt
β βββ etth1_best.pt
β βββ etth2_best.pt
βββ timesae_timegpt/ # TimeSAE explaining TimeGPT
β βββ freqshapes_best.pt
β βββ seqcomb_best.pt
β βββ ecg_best.pt
β βββ pam_best.pt
β βββ etth1_best.pt
β βββ etth2_best.pt
βββ timesae_timefm/ # TimeSAE explaining TimeFM
β βββ freqshapes_best.pt
β βββ seqcomb_best.pt
β βββ ecg_best.pt
β βββ pam_best.pt
β βββ etth1_best.pt
β βββ etth2_best.pt
βββ timesae_dlinear/ # TimeSAE explaining DLinear
β βββ freqshapes_best.pt
β βββ seqcomb_best.pt
β βββ ecg_best.pt
β βββ pam_best.pt
β βββ etth1_best.pt
β βββ etth2_best.pt
βββ blackbox_models/ # Trained black-box models
βββ transformer/
β βββ freqshapes_best.pt
β βββ ecg_best.pt
β βββ pam_best.pt
βββ patchts/
β βββ freqshapes_best.pt
β βββ ecg_best.pt
β βββ pam_best.pt
βββ dlinear/
βββ freqshapes_best.pt
βββ ecg_best.pt
βββ pam_best.pt
All pretrained checkpoints are available at: https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/
# Download all Transformer TimeSAE models
wget -r -np -nH --cut-dirs=2 https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/timesae_transformer/
# Or download specific datasets
wget https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/timesae_transformer/ecg_best.pt
wget https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/timesae_transformer/freqshapes_best.pt
wget -r -np -nH --cut-dirs=2 https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/timesae_patchts/
wget -r -np -nH --cut-dirs=2 https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/timesae_chronos/
wget -r -np -nH --cut-dirs=2 https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/timesae_timegpt/
wget -r -np -nH --cut-dirs=2 https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/timesae_timefm/
wget -r -np -nH --cut-dirs=2 https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/timesae_dlinear/
wget -r -np -nH --cut-dirs=2 https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/blackbox_models/
| Model Type | Input Shape | Concept Dim | Activation | Sparsity | Paper AUPRC |
|---|---|---|---|---|---|
| Transformer | (5, 100) | 1000 | JumpReLU | 10% | 0.950Β±0.011 |
| PatchTS | (5, 100) | 1000 | JumpReLU | 10% | 0.842Β±0.014 |
| Chronos | (5, 100) | 1000 | JumpReLU | 10% | 0.930Β±0.016 |
| TimeGPT | (5, 100) | 1000 | JumpReLU | 10% | 0.958Β±0.016 |
| TimeFM | (5, 100) | 1000 | JumpReLU | 10% | 0.945Β±0.012 |
| DLinear | (5, 100) | 1000 | JumpReLU | 10% | 0.825Β±0.018 |
| Dataset | Shape | Classes | Task | Domain |
|---|---|---|---|---|
| FreqShapes | (5, 100) | 5 | Classification | Synthetic |
| SeqComb-UV | (1, 150) | 3 | Classification | Synthetic |
| ECG | (1, 187) | 5 | Classification | Medical |
| PAM | (17, 100) | 12 | Classification | Activity |
| ETTH-1 | (7, 96) | - | Regression | Energy |
| ETTH-2 | (7, 96) | - | Regression | Energy |
import torch
from models import TimeSAE
from blackbox_models import get_model
# Load TimeSAE explaining Transformer on ECG data
def load_timesae_checkpoint(model_type: str, dataset: str, device: str = 'cuda'):
# Determine input shape based on dataset
dataset_shapes = {
'freqshapes': (5, 100),
'seqcomb': (1, 150),
'ecg': (1, 187),
'pam': (17, 100),
'etth1': (7, 96),
'etth2': (7, 96)
}
input_shape = dataset_shapes[dataset]
concept_dim = int(2.0 * input_shape[0] * input_shape[1])
# Create TimeSAE model
timesae = TimeSAE(
input_shape=input_shape,
concept_dim=concept_dim,
activation_type='jumprelu',
use_temporal_conv=True
)
# Load checkpoint
checkpoint_path = f'checkpoints/timesae_{model_type}/{dataset}_best.pt'
checkpoint = torch.load(checkpoint_path, map_location=device)
timesae.load_state_dict(checkpoint['model_state_dict'])
timesae.to(device)
timesae.eval()
return timesae
# Example usage
timesae_transformer_ecg = load_timesae_checkpoint('transformer', 'ecg')
timesae_chronos_freqshapes = load_timesae_checkpoint('chronos', 'freqshapes')
def load_blackbox_checkpoint(model_type: str, dataset: str, device: str = 'cuda'):
dataset_configs = {
'freqshapes': {'seq_len': 100, 'n_vars': 5, 'num_classes': 5},
'ecg': {'seq_len': 187, 'n_vars': 1, 'num_classes': 5},
'pam': {'seq_len': 100, 'n_vars': 17, 'num_classes': 12},
}
config = dataset_configs[dataset]
# Create black-box model
if model_type == 'transformer':
from blackbox_models import create_transformer_model
model = create_transformer_model(**config, task_type='classification')
elif model_type == 'patchts':
from blackbox_models import create_patchts_model
model = create_patchts_model(**config, task_type='classification')
elif model_type == 'dlinear':
from blackbox_models import create_dlinear_model
model = create_dlinear_model(**config, task_type='classification')
# Load checkpoint
checkpoint_path = f'checkpoints/blackbox_models/{model_type}/{dataset}_best.pt'
checkpoint = torch.load(checkpoint_path, map_location=device)
model.load_state_dict(checkpoint['model_state_dict'])
model.to(device)
model.eval()
return model
# Example usage
transformer_ecg = load_blackbox_checkpoint('transformer', 'ecg')
patchts_freqshapes = load_blackbox_checkpoint('patchts', 'freqshapes')
import torch
from utils import TimeSeriesVisualizer, ConceptInterpreter
# Load models
timesae = load_timesae_checkpoint('transformer', 'ecg')
black_box = load_blackbox_checkpoint('transformer', 'ecg')
# Generate sample ECG data (or load your own)
ecg_sample = torch.randn(5, 1, 187) # 5 samples
# Generate explanations
with torch.no_grad():
concepts, x_recon, contributions = timesae(ecg_sample)
predictions = black_box(ecg_sample)
print(f"Concept activations shape: {concepts.shape}")
print(f"Reconstruction MSE: {torch.mse_loss(x_recon, ecg_sample):.6f}")
# Interpret concepts
interpreter = ConceptInterpreter()
concept_analysis = interpreter.analyze_concept_activations(concepts)
interpreter.assign_concept_names(concept_analysis)
# Visualize explanations
visualizer = TimeSeriesVisualizer()
visualizer.plot_time_series_with_concepts(
ecg_sample, x_recon, concepts, contributions,
sample_idx=0, save_path='ecg_explanation.png'
)
print("Explanation generated and saved as 'ecg_explanation.png'")
All models were trained with exact paper parameters:
TRAINING_CONFIG = {
'eta': 0.1, # Sparsity coefficient
'alpha': 0.5, # Consistency weight
'lam': 0.1, # Contrastive weight
'temperature': 0.1, # InfoNCE temperature
'learning_rate': 1e-3,
'batch_size': 32, # Per GPU (96 effective on 3 A100s)
'num_epochs': 100,
'weight_decay': 1e-4,
'gradient_clip': 1.0
}
Each checkpoint includes validation metrics:
| Model + Dataset | AUPRC | AUP | AUR | Faithfulness |
|---|---|---|---|---|
| Transformer + FreqShapes | 0.950Β±0.011 | 0.854Β±0.017 | 0.745Β±0.011 | 1.89Β±0.071 |
| PatchTS + FreqShapes | 0.842Β±0.014 | 0.746Β±0.014 | 0.664Β±0.014 | 1.85Β±0.080 |
| Chronos + FreqShapes | 0.930Β±0.016 | 0.851Β±0.016 | 0.753Β±0.016 | 1.78Β±0.078 |
| TimeGPT + FreqShapes | 0.958Β±0.016 | 0.875Β±0.016 | 0.777Β±0.016 | 2.15Β±0.080 |
| Transformer + ECG | 0.950Β±0.011 | 0.854Β±0.017 | 0.745Β±0.011 | 1.89Β±0.071 |
| Transformer + PAM | 0.998Β±0.012 | 0.926Β±0.034 | 0.505Β±0.019 | 1.85Β±0.080 |
Each checkpoint file contains:
checkpoint = {
'epoch': int, # Training epoch
'step': int, # Training step
'model_state_dict': dict, # TimeSAE model weights
'optimizer_state_dict': dict, # Optimizer state
'scheduler_state_dict': dict, # LR scheduler state
'metrics': { # Validation metrics
'auprc': float,
'aup': float,
'aur': float,
'faithfulness': float,
'val_loss': float
},
'config': { # Model configuration
'input_shape': tuple,
'concept_dim': int,
'activation_type': str,
'eta': float,
'alpha': float,
'lam': float
},
'dataset_info': { # Dataset information
'name': str,
'num_samples': int,
'num_features': int,
'sequence_length': int
}
}
If you encounter issues with the pretrained checkpoints:
If you use our work, please cite:
@article{timesae2025,
title={TimeSAE: Sparse Decoding for Faithful Explanations of Black-Box Time Series Models},
author={Anonymous Authors},
year={2025}
}
Note: These checkpoints are provided for research purposes. For production use, we recommend training TimeSAE on your specific datasets and black-box models.
This project is licensed under the MIT License - see the LICENSE file for details. ANOVA Decomposition**: Unlike standard approaches, implements complete functional ANOVA from order 0 to concept_dim
28 commits
Python
80.4%
Jupyter Notebook
18.7%
[Accepted at ICML 2026 π ] TimeSAE: Sparse Decoding for Faithful Explanations of Black-Box Time Series Models
6
stars
28
commits
Python
primary language
Jul 11, 2026
updated
Official implementation of TimeSAE, accepted at ICML 2026. TimeSAE is a novel framework for explaining black-box time series models using Sparse Autoencoders with complete functional ANOVA decomposition and temporal convolution layers.
A PyTorch implementation is provided, with a JAX version available here.



/notebooks/google-colab/git clone <repository-url>
cd TimeSAE
pip install -r requirements.txt
We provide pretrained TimeSAE models for all paper experiments. No need to train from scratch!
# List available models
python checkpoints/download_checkpoints.py --list
# Download specific model
python checkpoints/download_checkpoints.py --model transformer --dataset ecg
# Download all datasets for a model
python checkpoints/download_checkpoints.py --model chronos --all-datasets
# Download all models for a dataset
python checkpoints/download_checkpoints.py --dataset freqshapes --all-models
Anonymous Download Link: https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/pretrained/
from utils.checkpoint_loader import load_explanation_pipeline
from utils import TimeSeriesVisualizer
# Load pretrained TimeSAE explaining Transformer on ECG data
timesae, transformer_model = load_explanation_pipeline('transformer', 'ecg')
# Generate sample ECG data (or use your own)
import torch
ecg_data = torch.randn(5, 1, 187) # 5 samples, 1 lead, 187 timesteps
# Generate explanations instantly
concepts, x_recon, contributions = timesae(ecg_data)
# Get black-box predictions
predictions = transformer_model(ecg_data)
print(f"Concept activations: {concepts.shape}")
print(f"ANOVA decomposition orders: {list(contributions.keys())}")
# Visualize explanations
visualizer = TimeSeriesVisualizer()
visualizer.plot_time_series_with_concepts(
ecg_data, x_recon, concepts, contributions,
sample_idx=0, save_path='ecg_explanation.png'
)
| TimeSAE Model | Datasets | AUPRC (FreqShapes) | Download Size |
|---|---|---|---|
| Transformer | 6 datasets | 0.950Β±0.011 | ~270MB |
| PatchTS | 6 datasets | 0.842Β±0.014 | ~260MB |
| Chronos | 6 datasets | 0.930Β±0.016 | ~265MB |
| TimeGPT | 6 datasets | 0.958Β±0.016 | ~270MB |
| TimeFM | 6 datasets | 0.945Β±0.012 | ~275MB |
| DLinear | 6 datasets | 0.825Β±0.018 | ~250MB |
Datasets: FreqShapes, SeqComb-UV, ECG, PAM, ETTH-1, ETTH-2
Based on the TimeSAE paper, the following parameters were used:
r Γ (D Γ T) where r = 2.0 (expansion ratio)1283 for all temporal convolutions[1, 2, 4, 8] for 4-layer temporal encoder0.10.10.5 (fixed across all experiments)0.10.11.01e-31e-432 per GPU (effective batch size: 96 on 3 GPUs)Ξ² = (0.9, 0.999)T_max = 100000, eta_min = 1e-6max_norm = 1.0U(0.01, 0.11)2.0, decay to 1.0 during trainingk = concept_dim // 10 (10% activation)import torch
from models import TimeSAE
from trainer import TimeSAETrainer, setup_model_and_optimizer
from utils import DatasetProcessor
# Create model with paper parameters
input_shape = (10, 128) # (D=10 features, T=128 timesteps)
model, loss_fn, optimizer, scheduler = setup_model_and_optimizer(
input_shape=input_shape,
concept_dim=int(2.0 * 10 * 128), # r=2.0 expansion ratio
learning_rate=1e-3,
weight_decay=1e-4
)
# Generate synthetic data
processor = DatasetProcessor()
data = processor.create_synthetic_data(
n_samples=1000,
n_features=10,
n_timesteps=128
)
# Train model
trainer = TimeSAETrainer(model, loss_fn, optimizer, scheduler)
# trainer.train(train_dataloader, val_dataloader, num_epochs=100)
# Quick demo with any model/dataset combination
from utils.checkpoint_loader import quick_explanation_demo
# Try different combinations
quick_explanation_demo('transformer', 'ecg') # Medical data
quick_explanation_demo('chronos', 'freqshapes') # Synthetic patterns
quick_explanation_demo('timegpt', 'pam') # Activity recognition
# Compare all models on same dataset
from utils.checkpoint_loader import compare_models_on_dataset
results = compare_models_on_dataset('ecg')
from utils.checkpoint_loader import load_timesae
from utils import ConceptInterpreter
# Load specific model
timesae = load_timesae('transformer', 'ecg')
# Generate explanations
x = torch.randn(5, 1, 187) # ECG data
concepts, x_recon, contributions = timesae(x)
# Interpret concepts
interpreter = ConceptInterpreter()
analysis = interpreter.analyze_concept_activations(concepts)
interpreter.assign_concept_names(analysis)
summary = interpreter.get_concept_summary()
print("Top concepts:")
for name, description in list(summary.items())[:5]:
print(f" {name}: {description}")
from utils import TimeSeriesVisualizer
visualizer = TimeSeriesVisualizer()
# Plot explanations
visualizer.plot_time_series_with_concepts(
x, x_recon, concepts, contributions,
sample_idx=0, top_k_concepts=10,
save_path='explanation.png'
)
# Plot concept heatmap
visualizer.plot_concept_heatmap(
concepts, save_path='concepts.png'
)
python main.py \
--n_features 10 \
--n_timesteps 128 \
--batch_size 32 \
--learning_rate 1e-3 \
--num_epochs 100 \
--synthetic_data \
--use_black_box \
--generate_explanations
chmod +x run_distributed.sh
./run_distributed.sh
Use the provided SLURM script for cluster environments:
sbatch run_slurm_3a100.slurm
# Train black-box models
python trainers/blackbox_trainer/train_transformer.py --seq_len 128 --n_vars 10
python trainers/blackbox_trainer/train_patchts.py --patch_len 16 --stride 8
python trainers/blackbox_trainer/train_dlinear.py --seq_len 96 --task_type forecasting
from torch.utils.data import Dataset, DataLoader
class CustomTimeSeriesDataset(Dataset):
def __init__(self, data, labels=None):
self.data = torch.tensor(data, dtype=torch.float32)
self.labels = labels
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
if self.labels is not None:
return self.data[idx], self.labels[idx]
return self.data[idx]
# Use with TimeSAE
dataset = CustomTimeSeriesDataset(your_data, your_labels)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
The implementation includes all metrics from the paper:
||f(x) - f(ex-)||Β² where concepts are removed| Dataset | Transformer | PatchTS | TimeGPT | Chronos |
|---|---|---|---|---|
| FreqShapes | 0.950Β±0.011 | 0.842Β±0.014 | 0.958Β±0.016 | 0.930Β±0.016 |
| SeqComb-UV | 0.811Β±0.022 | 0.715Β±0.019 | 0.837Β±0.023 | 0.807Β±0.022 |
| ECG | 0.950Β±0.011 | 0.980Β±0.011 | 0.912Β±0.020 | 0.894Β±0.015 |
| PAM | 0.998Β±0.012 | 0.981Β±0.012 | 0.957Β±0.022 | 0.939Β±0.017 |
| Dataset | Score |
|---|---|
| ECG | 1.78Β±0.078 |
| PAM | 2.15Β±0.080 |
| ETTH-1 | 2.12Β±0.072 |
| ETTH-2 | 2.09Β±0.069 |
# Run all paper experiments
chmod +x scripts/reproduce_all_results.sh
./scripts/reproduce_all_results.sh
# Or use SLURM for cluster
sbatch run_slurm_3a100.slurm
TimeSAE/
βββ models.py # Core TimeSAE architecture
βββ loss.py # Loss functions and counterfactual generation
βββ trainer.py # Distributed training logic
βββ utils.py # Evaluation, visualization, and utilities
βββ main.py # Main training script
βββ blackbox_models/ # All black-box model implementations
β βββ chronos.py # Chronos (pretrained)
β βββ timegpt.py # TimeGPT (pretrained)
β βββ timefm.py # TimeFM (pretrained)
β βββ transformer.py # Transformer (trainable)
β βββ patchts.py # PatchTS (trainable)
β βββ dlinear.py # DLinear (trainable)
βββ trainers/blackbox_trainer/ # Training scripts for black-box models
βββ checkpoints/ # Pretrained model checkpoints
β βββ README.md # Checkpoint documentation
β βββ timesae_transformer/ # TimeSAE explaining Transformers
β βββ timesae_chronos/ # TimeSAE explaining Chronos
β βββ blackbox_models/ # Trained black-box models
βββ scripts/
β βββ download_checkpoints.py # Automated checkpoint downloader
β βββ reproduce_all_results.sh # Complete reproduction script
βββ run_distributed.sh # 3 GPU training script
βββ run_slurm_3a100.slurm # SLURM batch script
βββ requirements.txt # Python dependencies
βββ README.md # This file
βββ REPRODUCIBILITY.md # Complete reproduction guide
βββ README_USAGE_EXAMPLES.md # Practical usage examples
# All interaction orders from 0 to concept_dim
for order in range(1, concept_dim + 1):
combinations = list(combinations(range(concept_dim), order))
# Process all possible concept combinations
# Multi-layer dilated convolutions
dilations = [1, 2, 4, 8] # Paper specification
for i, dilation in enumerate(dilations):
conv_layer = TemporalConvBlock(
hidden_dim, hidden_dim,
kernel_size=3, dilation=dilation
)
# 3 A100 GPU configuration
WORLD_SIZE = 3
MASTER_PORT = 12355
backend = 'nccl'
# Effective batch size: 32 Γ 3 = 96
python scripts/download_checkpoints.pytorch.cuda.amptorch.compile(model)num_workersBased on the TimeSAE paper, the following parameters were used:
r Γ (D Γ T) where r = 2.0 (expansion ratio)1283 for all temporal convolutions[1, 2, 4, 8] for 4-layer temporal encoder0.10.10.5 (fixed across all experiments)0.10.11.01e-31e-432 per GPU (effective batch size: 96 on 3 GPUs)Ξ² = (0.9, 0.999)T_max = 100000, eta_min = 1e-6max_norm = 1.0U(0.01, 0.11)2.0, decay to 1.0 during trainingk = concept_dim // 10 (10% activation)import torch
from models import TimeSAE
from trainer import TimeSAETrainer, setup_model_and_optimizer
from utils import DatasetProcessor
# Create model with paper parameters
input_shape = (10, 128) # (D=10 features, T=128 timesteps)
model, loss_fn, optimizer, scheduler = setup_model_and_optimizer(
input_shape=input_shape,
concept_dim=int(2.0 * 10 * 128), # r=2.0 expansion ratio
learning_rate=1e-3,
weight_decay=1e-4
)
# Generate synthetic data
processor = DatasetProcessor()
data = processor.create_synthetic_data(
n_samples=1000,
n_features=10,
n_timesteps=128
)
# Train model
trainer = TimeSAETrainer(model, loss_fn, optimizer, scheduler)
# trainer.train(train_dataloader, val_dataloader, num_epochs=100)
# Load trained model
model = TimeSAE(input_shape=(10, 128))
model.load_state_dict(torch.load('checkpoints/best_model.pt'))
# Generate explanations
x = torch.randn(5, 10, 128) # Sample time series
concepts, x_recon, contributions = trainer.explain(x)
# Analyze concepts
from utils import ConceptInterpreter
interpreter = ConceptInterpreter()
analysis = interpreter.analyze_concept_activations(concepts)
interpreter.assign_concept_names(analysis)
summary = interpreter.get_concept_summary()
from utils import TimeSeriesVisualizer
visualizer = TimeSeriesVisualizer()
# Plot explanations
visualizer.plot_time_series_with_concepts(
x, x_recon, concepts, contributions,
sample_idx=0, top_k_concepts=10,
save_path='explanation.png'
)
# Plot concept heatmap
visualizer.plot_concept_heatmap(
concepts, save_path='concepts.png'
)
python main.py \
--n_features 10 \
--n_timesteps 128 \
--batch_size 32 \
--learning_rate 1e-3 \
--num_epochs 100 \
--synthetic_data \
--use_black_box \
--generate_explanations
chmod +x run_distributed.sh
./run_distributed.sh
Use the provided SLURM script for cluster environments:
sbatch run_slurm_3a100.slurm
from torch.utils.data import Dataset, DataLoader
class CustomTimeSeriesDataset(Dataset):
def __init__(self, data, labels=None):
self.data = torch.tensor(data, dtype=torch.float32)
self.labels = labels
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
if self.labels is not None:
return self.data[idx], self.labels[idx]
return self.data[idx]
# Use with TimeSAE
dataset = CustomTimeSeriesDataset(your_data, your_labels)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
The implementation includes all metrics from the paper:
||f(x) - f(ex-)||Β² where concepts are removed| Dataset | Transformer | PatchTS | TimeGPT | Chronos |
|---|---|---|---|---|
| FreqShapes | 0.950Β±0.011 | 0.842Β±0.014 | 0.958Β±0.016 | 0.930Β±0.016 |
| SeqComb-UV | 0.811Β±0.022 | 0.715Β±0.019 | 0.837Β±0.023 | 0.807Β±0.022 |
| ECG | 0.950Β±0.011 | 0.980Β±0.011 | 0.912Β±0.020 | 0.894Β±0.015 |
| PAM | 0.998Β±0.012 | 0.981Β±0.012 | 0.957Β±0.022 | 0.939Β±0.017 |
| Dataset | Score |
|---|---|
| ECG | 1.78Β±0.078 |
| PAM | 2.15Β±0.080 |
| ETTH-1 | 2.12Β±0.072 |
| ETTH-2 | 2.09Β±0.069 |
TimeSAE/
βββ models.py # Core TimeSAE architecture
βββ loss.py # Loss functions and counterfactual generation
βββ trainer.py # Distributed training logic
βββ utils.py # Evaluation, visualization, and utilities
βββ main.py # Main training script
βββ run_distributed.sh # Shell script for 3 GPU training
βββ run_slurm_3a100.slurm # SLURM batch script
βββ requirements.txt # Python dependencies
βββ README.md # This file
# All interaction orders from 0 to concept_dim
for order in range(1, concept_dim + 1):
combinations = list(combinations(range(concept_dim), order))
# Process all possible concept combinations
# Multi-layer dilated convolutions
dilations = [1, 2, 4, 8] # Paper specification
for i, dilation in enumerate(dilations):
conv_layer = TemporalConvBlock(
hidden_dim, hidden_dim,
kernel_size=3, dilation=dilation
)
# 3 A100 GPU configuration
WORLD_SIZE = 3
MASTER_PORT = 12355
backend = 'nccl'
# Effective batch size: 32 Γ 3 = 96
torch.cuda.amptorch.compile(model)num_workersThis directory contains pretrained TimeSAE models for explaining different black-box time series models. These checkpoints are provided for users who do not have the computational resources to train TimeSAE from scratch.
checkpoints/
βββ README.md # This file
βββ timesae_transformer/ # TimeSAE explaining Transformers
β βββ freqshapes_best.pt # FreqShapes dataset
β βββ seqcomb_best.pt # SeqComb-UV dataset
β βββ ecg_best.pt # ECG arrhythmia dataset
β βββ pam_best.pt # PAM activity dataset
β βββ etth1_best.pt # ETTH-1 energy dataset
β βββ etth2_best.pt # ETTH-2 energy dataset
βββ timesae_patchts/ # TimeSAE explaining PatchTS
β βββ freqshapes_best.pt
β βββ seqcomb_best.pt
β βββ ecg_best.pt
β βββ pam_best.pt
β βββ etth1_best.pt
β βββ etth2_best.pt
βββ timesae_chronos/ # TimeSAE explaining Chronos
β βββ freqshapes_best.pt
β βββ seqcomb_best.pt
β βββ ecg_best.pt
β βββ pam_best.pt
β βββ etth1_best.pt
β βββ etth2_best.pt
βββ timesae_timegpt/ # TimeSAE explaining TimeGPT
β βββ freqshapes_best.pt
β βββ seqcomb_best.pt
β βββ ecg_best.pt
β βββ pam_best.pt
β βββ etth1_best.pt
β βββ etth2_best.pt
βββ timesae_timefm/ # TimeSAE explaining TimeFM
β βββ freqshapes_best.pt
β βββ seqcomb_best.pt
β βββ ecg_best.pt
β βββ pam_best.pt
β βββ etth1_best.pt
β βββ etth2_best.pt
βββ timesae_dlinear/ # TimeSAE explaining DLinear
β βββ freqshapes_best.pt
β βββ seqcomb_best.pt
β βββ ecg_best.pt
β βββ pam_best.pt
β βββ etth1_best.pt
β βββ etth2_best.pt
βββ blackbox_models/ # Trained black-box models
βββ transformer/
β βββ freqshapes_best.pt
β βββ ecg_best.pt
β βββ pam_best.pt
βββ patchts/
β βββ freqshapes_best.pt
β βββ ecg_best.pt
β βββ pam_best.pt
βββ dlinear/
βββ freqshapes_best.pt
βββ ecg_best.pt
βββ pam_best.pt
All pretrained checkpoints are available at: https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/
# Download all Transformer TimeSAE models
wget -r -np -nH --cut-dirs=2 https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/timesae_transformer/
# Or download specific datasets
wget https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/timesae_transformer/ecg_best.pt
wget https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/timesae_transformer/freqshapes_best.pt
wget -r -np -nH --cut-dirs=2 https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/timesae_patchts/
wget -r -np -nH --cut-dirs=2 https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/timesae_chronos/
wget -r -np -nH --cut-dirs=2 https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/timesae_timegpt/
wget -r -np -nH --cut-dirs=2 https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/timesae_timefm/
wget -r -np -nH --cut-dirs=2 https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/timesae_dlinear/
wget -r -np -nH --cut-dirs=2 https://anonymous.4open.science/r/TimeSAE-Checkpoints-571D/blackbox_models/
| Model Type | Input Shape | Concept Dim | Activation | Sparsity | Paper AUPRC |
|---|---|---|---|---|---|
| Transformer | (5, 100) | 1000 | JumpReLU | 10% | 0.950Β±0.011 |
| PatchTS | (5, 100) | 1000 | JumpReLU | 10% | 0.842Β±0.014 |
| Chronos | (5, 100) | 1000 | JumpReLU | 10% | 0.930Β±0.016 |
| TimeGPT | (5, 100) | 1000 | JumpReLU | 10% | 0.958Β±0.016 |
| TimeFM | (5, 100) | 1000 | JumpReLU | 10% | 0.945Β±0.012 |
| DLinear | (5, 100) | 1000 | JumpReLU | 10% | 0.825Β±0.018 |
| Dataset | Shape | Classes | Task | Domain |
|---|---|---|---|---|
| FreqShapes | (5, 100) | 5 | Classification | Synthetic |
| SeqComb-UV | (1, 150) | 3 | Classification | Synthetic |
| ECG | (1, 187) | 5 | Classification | Medical |
| PAM | (17, 100) | 12 | Classification | Activity |
| ETTH-1 | (7, 96) | - | Regression | Energy |
| ETTH-2 | (7, 96) | - | Regression | Energy |
import torch
from models import TimeSAE
from blackbox_models import get_model
# Load TimeSAE explaining Transformer on ECG data
def load_timesae_checkpoint(model_type: str, dataset: str, device: str = 'cuda'):
# Determine input shape based on dataset
dataset_shapes = {
'freqshapes': (5, 100),
'seqcomb': (1, 150),
'ecg': (1, 187),
'pam': (17, 100),
'etth1': (7, 96),
'etth2': (7, 96)
}
input_shape = dataset_shapes[dataset]
concept_dim = int(2.0 * input_shape[0] * input_shape[1])
# Create TimeSAE model
timesae = TimeSAE(
input_shape=input_shape,
concept_dim=concept_dim,
activation_type='jumprelu',
use_temporal_conv=True
)
# Load checkpoint
checkpoint_path = f'checkpoints/timesae_{model_type}/{dataset}_best.pt'
checkpoint = torch.load(checkpoint_path, map_location=device)
timesae.load_state_dict(checkpoint['model_state_dict'])
timesae.to(device)
timesae.eval()
return timesae
# Example usage
timesae_transformer_ecg = load_timesae_checkpoint('transformer', 'ecg')
timesae_chronos_freqshapes = load_timesae_checkpoint('chronos', 'freqshapes')
def load_blackbox_checkpoint(model_type: str, dataset: str, device: str = 'cuda'):
dataset_configs = {
'freqshapes': {'seq_len': 100, 'n_vars': 5, 'num_classes': 5},
'ecg': {'seq_len': 187, 'n_vars': 1, 'num_classes': 5},
'pam': {'seq_len': 100, 'n_vars': 17, 'num_classes': 12},
}
config = dataset_configs[dataset]
# Create black-box model
if model_type == 'transformer':
from blackbox_models import create_transformer_model
model = create_transformer_model(**config, task_type='classification')
elif model_type == 'patchts':
from blackbox_models import create_patchts_model
model = create_patchts_model(**config, task_type='classification')
elif model_type == 'dlinear':
from blackbox_models import create_dlinear_model
model = create_dlinear_model(**config, task_type='classification')
# Load checkpoint
checkpoint_path = f'checkpoints/blackbox_models/{model_type}/{dataset}_best.pt'
checkpoint = torch.load(checkpoint_path, map_location=device)
model.load_state_dict(checkpoint['model_state_dict'])
model.to(device)
model.eval()
return model
# Example usage
transformer_ecg = load_blackbox_checkpoint('transformer', 'ecg')
patchts_freqshapes = load_blackbox_checkpoint('patchts', 'freqshapes')
import torch
from utils import TimeSeriesVisualizer, ConceptInterpreter
# Load models
timesae = load_timesae_checkpoint('transformer', 'ecg')
black_box = load_blackbox_checkpoint('transformer', 'ecg')
# Generate sample ECG data (or load your own)
ecg_sample = torch.randn(5, 1, 187) # 5 samples
# Generate explanations
with torch.no_grad():
concepts, x_recon, contributions = timesae(ecg_sample)
predictions = black_box(ecg_sample)
print(f"Concept activations shape: {concepts.shape}")
print(f"Reconstruction MSE: {torch.mse_loss(x_recon, ecg_sample):.6f}")
# Interpret concepts
interpreter = ConceptInterpreter()
concept_analysis = interpreter.analyze_concept_activations(concepts)
interpreter.assign_concept_names(concept_analysis)
# Visualize explanations
visualizer = TimeSeriesVisualizer()
visualizer.plot_time_series_with_concepts(
ecg_sample, x_recon, concepts, contributions,
sample_idx=0, save_path='ecg_explanation.png'
)
print("Explanation generated and saved as 'ecg_explanation.png'")
All models were trained with exact paper parameters:
TRAINING_CONFIG = {
'eta': 0.1, # Sparsity coefficient
'alpha': 0.5, # Consistency weight
'lam': 0.1, # Contrastive weight
'temperature': 0.1, # InfoNCE temperature
'learning_rate': 1e-3,
'batch_size': 32, # Per GPU (96 effective on 3 A100s)
'num_epochs': 100,
'weight_decay': 1e-4,
'gradient_clip': 1.0
}
Each checkpoint includes validation metrics:
| Model + Dataset | AUPRC | AUP | AUR | Faithfulness |
|---|---|---|---|---|
| Transformer + FreqShapes | 0.950Β±0.011 | 0.854Β±0.017 | 0.745Β±0.011 | 1.89Β±0.071 |
| PatchTS + FreqShapes | 0.842Β±0.014 | 0.746Β±0.014 | 0.664Β±0.014 | 1.85Β±0.080 |
| Chronos + FreqShapes | 0.930Β±0.016 | 0.851Β±0.016 | 0.753Β±0.016 | 1.78Β±0.078 |
| TimeGPT + FreqShapes | 0.958Β±0.016 | 0.875Β±0.016 | 0.777Β±0.016 | 2.15Β±0.080 |
| Transformer + ECG | 0.950Β±0.011 | 0.854Β±0.017 | 0.745Β±0.011 | 1.89Β±0.071 |
| Transformer + PAM | 0.998Β±0.012 | 0.926Β±0.034 | 0.505Β±0.019 | 1.85Β±0.080 |
Each checkpoint file contains:
checkpoint = {
'epoch': int, # Training epoch
'step': int, # Training step
'model_state_dict': dict, # TimeSAE model weights
'optimizer_state_dict': dict, # Optimizer state
'scheduler_state_dict': dict, # LR scheduler state
'metrics': { # Validation metrics
'auprc': float,
'aup': float,
'aur': float,
'faithfulness': float,
'val_loss': float
},
'config': { # Model configuration
'input_shape': tuple,
'concept_dim': int,
'activation_type': str,
'eta': float,
'alpha': float,
'lam': float
},
'dataset_info': { # Dataset information
'name': str,
'num_samples': int,
'num_features': int,
'sequence_length': int
}
}
If you encounter issues with the pretrained checkpoints:
If you use our work, please cite:
@article{timesae2025,
title={TimeSAE: Sparse Decoding for Faithful Explanations of Black-Box Time Series Models},
author={Anonymous Authors},
year={2025}
}
Note: These checkpoints are provided for research purposes. For production use, we recommend training TimeSAE on your specific datasets and black-box models.
This project is licensed under the MIT License - see the LICENSE file for details. ANOVA Decomposition**: Unlike standard approaches, implements complete functional ANOVA from order 0 to concept_dim
28 commits
Python
80.4%
Jupyter Notebook
18.7%