Instructions to use thu-sail-lab/Time-RCD with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use thu-sail-lab/Time-RCD with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("feature-extraction", model="thu-sail-lab/Time-RCD", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("thu-sail-lab/Time-RCD", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
| """Minimal inference backend packaged with the public Time-RCD API.""" | |
| from __future__ import annotations | |
| from pathlib import Path | |
| from typing import Tuple | |
| import numpy as np | |
| import torch | |
| import torch.nn.functional as F | |
| from torch.utils.data import DataLoader, Dataset | |
| from ._core.TimeRCD_pretrain_multi import TimeSeriesPretrainModel | |
| from ._core.time_rcd_config import TimeRCDConfig | |
| class _WindowDataset(Dataset): | |
| """Split a normalized time series into non-overlapping padded windows.""" | |
| def __init__(self, data: np.ndarray, window_size: int) -> None: | |
| mean = np.mean(data, axis=0) | |
| std = np.where(np.std(data, axis=0) == 0, 1e-8, np.std(data, axis=0)) | |
| normalized = (data - mean) / std | |
| padding = (-len(normalized)) % window_size | |
| if padding: | |
| normalized = np.vstack( | |
| [normalized, np.repeat(normalized[-1:, :], padding, axis=0)] | |
| ) | |
| self.data = normalized | |
| self.window_size = window_size | |
| self.original_length = len(data) | |
| def __len__(self) -> int: | |
| return len(self.data) // self.window_size | |
| def __getitem__(self, index: int) -> Tuple[torch.Tensor, torch.Tensor]: | |
| start = index * self.window_size | |
| end = start + self.window_size | |
| valid_length = min(self.window_size, self.original_length - start) | |
| mask = torch.zeros(self.window_size, dtype=torch.bool) | |
| mask[:valid_length] = True | |
| return ( | |
| torch.tensor(self.data[start:end], dtype=torch.float32), | |
| mask, | |
| ) | |
| def _collate_windows( | |
| batch: list[Tuple[torch.Tensor, torch.Tensor]], | |
| ) -> dict[str, torch.Tensor]: | |
| time_series, attention_mask = zip(*batch) | |
| return { | |
| "time_series": torch.stack(time_series), | |
| "attention_mask": torch.stack(attention_mask), | |
| } | |
| class TimeRCDPretrainTester: | |
| """Inference-only wrapper for a pretrained Time-RCD checkpoint.""" | |
| def __init__(self, checkpoint_path: str, config: TimeRCDConfig) -> None: | |
| self.config = config | |
| self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
| self.win_size = config.win_size | |
| self.batch_size = config.batch_size | |
| self.model = TimeSeriesPretrainModel(config).to(self.device) | |
| self.load_checkpoint(checkpoint_path) | |
| self.model.eval() | |
| def load_checkpoint(self, checkpoint_path: str) -> None: | |
| path = Path(checkpoint_path) | |
| if not path.is_file(): | |
| raise FileNotFoundError(f"Checkpoint not found: {path}") | |
| try: | |
| checkpoint = torch.load( | |
| path, map_location=self.device, weights_only=True | |
| ) | |
| except TypeError: | |
| checkpoint = torch.load(path, map_location=self.device) | |
| state_dict = checkpoint.get("model_state_dict", checkpoint) | |
| state_dict = { | |
| key.removeprefix("module."): value for key, value in state_dict.items() | |
| } | |
| self.model.load_state_dict(state_dict) | |
| def zero_shot( | |
| self, data: np.ndarray | |
| ) -> tuple[list[np.ndarray], list[np.ndarray]]: | |
| window_size = min(len(data), self.win_size) | |
| dataset = _WindowDataset(data, window_size) | |
| loader = DataLoader( | |
| dataset, | |
| batch_size=self.batch_size, | |
| collate_fn=_collate_windows, | |
| num_workers=0, | |
| shuffle=False, | |
| ) | |
| scores: list[np.ndarray] = [] | |
| logits: list[np.ndarray] = [] | |
| with torch.no_grad(): | |
| for batch in loader: | |
| time_series = batch["time_series"].to(self.device) | |
| attention_mask = batch["attention_mask"].to(self.device) | |
| local_embeddings = self.model( | |
| time_series=time_series, mask=attention_mask | |
| ) | |
| anomaly_logits = self.model.anomaly_head(local_embeddings) | |
| anomaly_logits = torch.mean(anomaly_logits, dim=-2) | |
| anomaly_probs = F.softmax(anomaly_logits, dim=-1)[..., 1] | |
| scores.append(anomaly_probs.cpu().numpy()) | |
| logits.append( | |
| (anomaly_logits[..., 1] - anomaly_logits[..., 0]).cpu().numpy() | |
| ) | |
| return scores, logits | |