Spaces:
Running
Running
| """Per-context-window min-max normalization used by the encoder path.""" | |
| from __future__ import annotations | |
| import torch | |
| import torch.nn as nn | |
| class WindowMinMax(nn.Module): | |
| """Per-context-window min-max normalization for the encoder path. | |
| Per-window statistics: | |
| x_min = x.min(1, keepdim=True)[0].detach() | |
| x_max = x.max(1, keepdim=True)[0].detach() | |
| x_range = (x_max - x_min).clamp(min=1e-5).detach() | |
| x_norm = (x - x_min) / x_range # → [0, 1] | |
| Stats are detached: gradients do NOT flow through normalization. | |
| Uses explicit ``transform`` / ``inverse_transform`` so the backbone can | |
| hold the (x_min, x_range) tuple across encoder + decoder and apply the | |
| inverse at loss / forecast time. | |
| """ | |
| def __init__(self, eps_clamp: float = 1e-5) -> None: | |
| super().__init__() | |
| self.eps_clamp = eps_clamp | |
| def transform( | |
| self, x: torch.Tensor | |
| ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: | |
| """Normalize ``x: (B, L, 1)`` to [0, 1] per context window. | |
| Returns ``(x_normalized, x_min, x_range)`` with stats detached and | |
| ``x_range >= eps_clamp`` to avoid div-by-zero on constant series. | |
| Robust to NaN / +inf / -inf in ``x``: those positions are filled | |
| with 0 BEFORE computing min/max. | |
| """ | |
| x_filled = torch.nan_to_num(x, nan=0.0, posinf=0.0, neginf=0.0) | |
| x_min = x_filled.min(dim=1, keepdim=True).values.detach() | |
| x_max = x_filled.max(dim=1, keepdim=True).values.detach() | |
| x_range = (x_max - x_min).clamp(min=self.eps_clamp).detach() | |
| x_normalized = (x_filled - x_min) / x_range | |
| return x_normalized, x_min, x_range | |
| def inverse_transform( | |
| y_pred_normalized: torch.Tensor, | |
| x_min: torch.Tensor, | |
| x_range: torch.Tensor, | |
| ) -> torch.Tensor: | |
| """Un-normalize ``(B, p)`` predictions back to raw magnitude. | |
| ``x_min`` / ``x_range`` come from a prior ``transform`` call and are | |
| ``(B, 1, 1)``; the trailing dim is squeezed for broadcasting against | |
| ``(B, p)``. | |
| """ | |
| return y_pred_normalized * x_range.squeeze(-1) + x_min.squeeze(-1) | |