Source code for genformer.noise

import torch
import torch.nn as nn

[docs] class GaussianNoise(nn.Module): r""" Gaussian noise injection module for the engression paradigm. This module injects additive Gaussian noise into the continuous representations of the sequence, acting as a pre-additive stochastic noise layer. According to the paper "Deep Generative Transformers for Probabilistic Time Series and Spatiotemporal Forecasting", injecting this stochastic noise enables the Transformer architecture to learn the full conditional predictive distribution rather than point predictions. Args: std (float): The standard deviation (:math:`\sigma`) of the Gaussian noise. seed (int | None): An optional seed for reproducible noise generation. Mathematical Definition: The noise :math:`\epsilon^{(m)}` is sampled from a Gaussian distribution: .. math:: \epsilon^{(m)} \sim \mathcal{N}(\mathbf{0}, \sigma^2 \mathbf{I}) The noise is then added to the input sequence: .. math:: \mathbf{X'}_{batch}^{(m)} = \mathbf{X}_{batch}^{(m)} + \epsilon^{(m)} """ def __init__(self, std: float, seed: int | None = None): super().__init__() self.std = std self.seed = seed self._gen = None self._gen_device = None def _get_generator(self, device: torch.device): if self._gen is None or self._gen_device != device: self._gen = torch.Generator(device=device) self._gen_device = device if self.seed is not None: self._gen.manual_seed(self.seed) return self._gen
[docs] def reset_seed(self, seed: int | None = None): """Reset the internal generator seed.""" if seed is not None: self.seed = seed if self.seed is None: raise ValueError("No seed set for this module.") if self._gen is not None: self._gen.manual_seed(self.seed)
[docs] def reset_std(self, std: float | None = None): """Update the standard deviation of the noise.""" self.std = std
[docs] def forward(self, x: torch.Tensor): """ Injects Gaussian noise into the input tensor. Args: x (torch.Tensor): The input sequence representations. Returns: torch.Tensor: The noise-perturbed representations. """ g = self._get_generator(x.device) noise = torch.randn( x.shape, dtype=x.dtype, device=x.device, generator=g, ) return x + noise * self.std
[docs] class UniformNoise(nn.Module): r""" Uniform noise injection module for the engression paradigm. This module injects additive Uniform noise into the continuous representations of the sequence. Args: std (float): The scale parameter (:math:`\sigma`) defining the noise boundaries. seed (int | None): An optional seed for reproducible noise generation. Mathematical Definition: The noise :math:`\epsilon^{(m)}` is sampled from a Uniform distribution: .. math:: \epsilon^{(m)} \sim \mathcal{U}(-\sigma, \sigma) The noise is then added to the input sequence: .. math:: \mathbf{X'}_{batch}^{(m)} = \mathbf{X}_{batch}^{(m)} + \epsilon^{(m)} """ def __init__(self, std: float, seed: int | None = None): super().__init__() self.std = std self.seed = seed self._gen = None self._gen_device = None def _get_generator(self, device: torch.device): if self._gen is None or self._gen_device != device: self._gen = torch.Generator(device=device) self._gen_device = device if self.seed is not None: self._gen.manual_seed(self.seed) return self._gen
[docs] def reset_seed(self, seed: int | None = None): """Reset the internal generator seed.""" if seed is not None: self.seed = seed if self.seed is None: raise ValueError("No seed set for this module.") if self._gen is not None: self._gen.manual_seed(self.seed)
[docs] def reset_std(self, std: float | None = None): """Update the standard deviation parameter.""" self.std = std
[docs] def forward(self, x: torch.Tensor): """ Injects Uniform noise into the input tensor. Args: x (torch.Tensor): The input sequence representations. Returns: torch.Tensor: The noise-perturbed representations. """ g = self._get_generator(x.device) noise = torch.rand( x.shape, dtype=x.dtype, device=x.device, generator=g, ) return x + (2 * self.std) * noise - self.std