Tabby / input_preprocessing.py
XSF0528's picture
Upload 14 files
a5d67fc verified
Raw
History Blame Contribute Delete
2.53 kB
"""Trimmed from utils/input_preprocessing.py: the inference normalization only.
Verbatim apart from dropping the PatchTSTFMConfig type annotation; `cfg` needs
only `.eps`, `.patch_size` and `.num_patches`.
"""
from typing import Tuple
import torch
def mask_aware_normalize_for_inference(
x: torch.Tensor,
observed_mask: torch.Tensor,
pred_mask: torch.Tensor,
padding_mask: torch.Tensor,
cfg,
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
"""Normalize using only visible historical values.
Robust GIFT-Eval handling
-------------------------
Some GIFT-Eval rolling windows, especially after multivariate-to-univariate
expansion, can contain channels with zero or one finite historical value.
We should still return a valid forecast instead of aborting evaluation.
Let
n_i = number of visible finite historical values in series i.
We use:
- n_i >= 2: usual mask-aware mean/std;
- n_i = 1 : mean = the single observed value, std = 1;
- n_i = 0 : mean = 0, std = 1.
This keeps forecasts finite and lets GluonTS mask invalid labels during
metric computation.
"""
missing_mask = (~observed_mask) & (~padding_mask) & (~pred_mask)
union_mask = pred_mask | missing_mask | padding_mask
visible_mask = observed_mask & (~pred_mask) & (~padding_mask)
count = visible_mask.sum(dim=1, keepdim=True).to(x.dtype)
x_visible = torch.where(visible_mask, x, torch.zeros_like(x))
safe_count = count.clamp_min(1.0)
raw_mean = x_visible.sum(dim=1, keepdim=True) / safe_count
mean = torch.where(count > 0, raw_mean, torch.zeros_like(raw_mean))
raw_var = torch.where(visible_mask, (x - mean).pow(2), torch.zeros_like(x)).sum(
dim=1, keepdim=True
) / safe_count
# If there are fewer than two visible points, variance is not identifiable.
# Use unit scale rather than sqrt(eps), because sqrt(eps) would make the
# reverse normalization almost constant and numerically brittle.
std = torch.where(count >= 2, torch.sqrt(raw_var + cfg.eps), torch.ones_like(raw_var))
x_norm_all = torch.asinh((x - mean) / std)
x_norm_input = torch.where(union_mask, torch.zeros_like(x_norm_all), x_norm_all)
B, T = x.shape
L, N = cfg.patch_size, cfg.num_patches
patch_padding = padding_mask.reshape(B, N, L).all(dim=-1)
return x_norm_input, mean, std, union_mask, patch_padding