Spaces:
Sleeping
Sleeping
Download models/moirai_model.py from 3VVM/MYTHOSLIVE: direct link, hf CLI and curl.
- Browser
- Download file 5.76 kB
-
https://huggingface.co/spaces/3VVM/MYTHOSLIVE/resolve/main/models/moirai_model.py
- Command line
-
hf download hf://spaces/3VVM/MYTHOSLIVE/models/moirai_model.py
-
curl -L -o moirai_model.py https://huggingface.co/spaces/3VVM/MYTHOSLIVE/resolve/main/models/moirai_model.py
5.76 kB
| import numpy as np | |
| import pandas as pd | |
| import threading | |
| from models.base_model import BaseForecastModel, validate_history_and_horizon | |
| from config import MOIRAI_CHECKPOINT, RANDOM_SEED | |
| from features.feature_pipeline import validate_features | |
| _RNG_LOCK = threading.Lock() | |
| _MODULE_CACHE = {} | |
| _MODULE_LOCK = threading.Lock() | |
| def _step_from_history(history: pd.Series) -> pd.Timedelta: | |
| if isinstance(history.index, pd.DatetimeIndex) and len(history.index) >= 2: | |
| diffs = history.index.to_series().diff().dropna() | |
| if not diffs.empty: | |
| step = diffs.median() | |
| if step > pd.Timedelta(0): | |
| if not diffs.eq(step).all(): | |
| raise ValueError('Moirai needs a continuous history; irregular timestamps cannot be relabelled.') | |
| return step | |
| return pd.Timedelta(minutes=1) | |
| class MoiraiModel(BaseForecastModel): | |
| name = "Moirai" | |
| def __init__( | |
| self, | |
| checkpoint: str = MOIRAI_CHECKPOINT, | |
| context_length: int = 256, | |
| num_samples: int = 20, | |
| ): | |
| self.checkpoint = checkpoint | |
| self.context_length = context_length | |
| self.num_samples = num_samples | |
| self._module = None | |
| self.last_diagnostics = {} | |
| def _get_module(self): | |
| from uni2ts.model.moirai import MoiraiModule | |
| if self._module is None: | |
| with _MODULE_LOCK: | |
| self._module = _MODULE_CACHE.get(self.checkpoint) | |
| if self._module is None: | |
| self._module = MoiraiModule.from_pretrained(self.checkpoint) | |
| self._module.eval() | |
| _MODULE_CACHE[self.checkpoint] = self._module | |
| return self._module | |
| def predict( | |
| self, history: pd.Series, horizon: int = 1, features: pd.DataFrame = None | |
| ) -> list: | |
| self.last_diagnostics = {} | |
| validate_history_and_horizon(history, horizon, self.name) | |
| try: | |
| import torch | |
| except ImportError as error: | |
| raise ImportError('Moirai requires the optional foundation-model packages. Install requirements-foundation.txt.') from error | |
| with _RNG_LOCK, torch.random.fork_rng(devices=[]), torch.inference_mode(): | |
| torch.manual_seed(RANDOM_SEED) | |
| return self._predict(history, horizon, features) | |
| def _predict(self, history, horizon, features): | |
| values = np.asarray(history, dtype=np.float32) | |
| if len(values) < 10: | |
| raise ValueError("Moirai needs at least 10 candles of history.") | |
| validate_history_and_horizon(values, horizon, self.name) | |
| try: | |
| from uni2ts.model.moirai import MoiraiForecast | |
| from gluonts.dataset.pandas import PandasDataset | |
| except ImportError as e: | |
| raise ImportError( | |
| f"Moirai needs the 'uni2ts' and 'gluonts' packages. Install with:\n pip install uni2ts torch\n(original error: {e})" | |
| ) | |
| if features is not None: | |
| validate_features( | |
| features, | |
| expected_len=len(values), | |
| model_name="Moirai", | |
| check_nan=False, | |
| ) | |
| # Bound the context for the small checkpoint so a large dashboard | |
| # window cannot cause an avoidable CPU-memory spike. | |
| context_length = min(max(int(self.context_length), 32), 512) | |
| context = values[-context_length:] if len(values) > context_length else values | |
| step = _step_from_history(history) | |
| end_ts = pd.Timestamp(history.index[-1]) if isinstance(history.index, pd.DatetimeIndex) else pd.Timestamp('2000-01-01') | |
| if end_ts.tzinfo is not None: | |
| end_ts = end_ts.tz_convert('UTC').tz_localize(None) | |
| idx = pd.date_range(end=end_ts, periods=len(context), freq=step) | |
| data = {"target": context} | |
| feat_cols = [] | |
| if features is not None: | |
| feat_context = features.iloc[-len(context) :].reset_index(drop=True) | |
| validate_features( | |
| feat_context, expected_len=len(context), model_name="Moirai" | |
| ) | |
| for col in feat_context.columns: | |
| data[col] = feat_context[col].to_numpy(dtype=np.float32) | |
| feat_cols = list(feat_context.columns) | |
| series_df = pd.DataFrame(data, index=idx) | |
| dataset = PandasDataset( | |
| series_df, | |
| target="target", | |
| past_feat_dynamic_real=feat_cols if feat_cols else None, | |
| ) | |
| module = self._get_module() | |
| forecaster = MoiraiForecast( | |
| module=module, | |
| prediction_length=horizon, | |
| context_length=len(context), | |
| patch_size="auto", | |
| num_samples=self.num_samples, | |
| target_dim=1, | |
| feat_dynamic_real_dim=0, | |
| past_feat_dynamic_real_dim=len(feat_cols), | |
| ) | |
| predictor = forecaster.create_predictor(batch_size=1) | |
| forecast = next(iter(predictor.predict(dataset))) | |
| samples = np.asarray(forecast.samples, dtype=float) | |
| if samples.ndim == 3 and samples.shape[-1] == 1: | |
| samples = samples[..., 0] | |
| if samples.ndim != 2 or samples.shape != (self.num_samples, horizon): | |
| raise ValueError('Moirai returned an unexpected univariate sample/horizon shape.') | |
| out = np.median(samples, axis=0).tolist() | |
| if not all(np.isfinite(x) and x > 0 for x in out): | |
| raise ValueError( | |
| "Moirai produced a non-finite forecast (NaN/inf) for this window." | |
| ) | |
| self.last_diagnostics = {'checkpoint': self.checkpoint, 'context_candles': len(context), 'point_statistic': 'sample median', 'seed': RANDOM_SEED, 'fallback': False, 'weights_loaded': True} | |
| return out | |