Spaces:
Running on Zero
Running on Zero
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| from typing import Any | |
| import numpy as np | |
| TEMPORAL_ARCHITECTURE = "bilstm" | |
| TEMPORAL_HIDDEN_SIZE = 73 | |
| TEMPORAL_NUM_LAYERS = 1 | |
| TEMPORAL_DROPOUT_RATE = 0.2174 | |
| TEMPORAL_LEARNING_RATE = 0.0004 | |
| TEMPORAL_BATCH_SIZE = 54 | |
| TEMPORAL_DEFAULT_EPOCHS = 73 | |
| class TemporalRouterConfig: | |
| input_size: int | |
| label_count: int | |
| hidden_size: int = TEMPORAL_HIDDEN_SIZE | |
| num_layers: int = TEMPORAL_NUM_LAYERS | |
| bidirectional: bool = True | |
| dropout_rate: float = TEMPORAL_DROPOUT_RATE | |
| def build_temporal_router_model(config: TemporalRouterConfig) -> Any: | |
| torch, nn = _torch_modules() | |
| class BiLSTMRouter(nn.Module): | |
| def __init__(self) -> None: | |
| super().__init__() | |
| self.lstm = nn.LSTM( | |
| input_size=config.input_size, | |
| hidden_size=config.hidden_size, | |
| num_layers=config.num_layers, | |
| batch_first=True, | |
| bidirectional=config.bidirectional, | |
| ) | |
| direction_count = 2 if config.bidirectional else 1 | |
| self.dropout = nn.Dropout(config.dropout_rate) | |
| self.head = nn.Linear(config.hidden_size * direction_count, config.label_count) | |
| def forward(self, inputs: Any) -> Any: | |
| _outputs, (hidden, _cell) = self.lstm(inputs) | |
| if config.bidirectional: | |
| pooled = torch.cat((hidden[-2], hidden[-1]), dim=1) | |
| else: | |
| pooled = hidden[-1] | |
| return self.head(self.dropout(pooled)) | |
| return BiLSTMRouter() | |
| class TorchTemporalRouter: | |
| def __init__( | |
| self, | |
| *, | |
| checkpoint: dict[str, Any], | |
| labels: tuple[str, ...], | |
| device: str = "cpu", | |
| ) -> None: | |
| torch, _nn = _torch_modules() | |
| self.labels = labels | |
| self.device = torch.device(device) | |
| self.config = TemporalRouterConfig( | |
| input_size=int(checkpoint["input_size"]), | |
| label_count=len(labels), | |
| hidden_size=int(checkpoint.get("hidden_size", TEMPORAL_HIDDEN_SIZE)), | |
| num_layers=int(checkpoint.get("num_layers", TEMPORAL_NUM_LAYERS)), | |
| bidirectional=bool(checkpoint.get("bidirectional", True)), | |
| dropout_rate=float(checkpoint.get("dropout_rate", TEMPORAL_DROPOUT_RATE)), | |
| ) | |
| self.model = build_temporal_router_model(self.config) | |
| self.model.load_state_dict(checkpoint["state_dict"]) | |
| self.model.to(self.device) | |
| self.model.eval() | |
| def predict_proba(self, values: np.ndarray) -> np.ndarray: | |
| torch, _nn = _torch_modules() | |
| inputs = torch.tensor(values, dtype=torch.float32, device=self.device) | |
| with torch.no_grad(): | |
| logits = self.model(inputs) | |
| probabilities = torch.softmax(logits, dim=1) | |
| return probabilities.detach().cpu().numpy() | |
| def _torch_modules() -> tuple[Any, Any]: | |
| try: | |
| import torch | |
| from torch import nn | |
| except ImportError as exc: # pragma: no cover - optional runtime dependency | |
| raise RuntimeError( | |
| "torch is required to load temporal exercise router artifacts" | |
| ) from exc | |
| return torch, nn | |