Source code for pretrain.models.neds.masker

from typing import Literal, get_args

import torch
from torch import nn

from .neds import ALL_MODALITIES

NEDSMaskType = Literal[
    "neural_masking",
    "behavior_masking",
    "within_neural",
    "within_behavior",
    "cross_modality",
]


# Mask is a classic NDT mask modeling (BERT style)
[docs] class NEDSMasker(nn.Module): def __init__( self, mask_ratio, modalities=ALL_MODALITIES, mask_types=None, ): super().__init__() self.mask_ratio = mask_ratio self.modalities = modalities self.mask_types = mask_types if mask_types is not None else list(get_args(NEDSMaskType)) def _random_mask(self, shape, device, generator=None): return torch.rand(shape, device=device, generator=generator) < self.mask_ratio def _generate_mask_map(self, shape, device, generator=None): mask_map = {} all_masked = torch.ones(shape, dtype=torch.bool, device=device) not_masked = torch.zeros(shape, dtype=torch.bool, device=device) # one shared draw, so a cross-modality sample masks the same timesteps everywhere cross_modality_random_masked = self._random_mask(shape, device, generator) for modality in self.modalities: if modality == "spikes": mask_map[modality] = { "neural_masking": all_masked, "behavior_masking": not_masked, "within_neural": self._random_mask(shape, device, generator), "within_behavior": not_masked, "cross_modality": cross_modality_random_masked, } else: mask_map[modality] = { "neural_masking": not_masked, "behavior_masking": all_masked, "within_neural": not_masked, "within_behavior": self._random_mask(shape, device, generator), "cross_modality": cross_modality_random_masked, } return mask_map
[docs] def forward(self, input, generator=None): """``generator``: draw from it instead of the global RNG, to hold the masks fixed across epochs (the validation pass seeds one per epoch). """ B, T, *_ = input.size() shape = (B, T, 1) device = input.device modality_mask = {} scheme_idx = torch.randint( len(self.mask_types), (B,), device=device, generator=generator ) # (B,) mask_map = self._generate_mask_map(shape, device, generator) # {mod: (B, T, 1)} rows = torch.arange(B, device=device) for modality in self.modalities: # (S, B, T, 1) -> (B, T, 1), keeping each sample's own scheme and draw per_scheme = torch.stack([mask_map[modality][scheme] for scheme in self.mask_types]) modality_mask[modality] = per_scheme[scheme_idx, rows] return modality_mask