Readout Modules
Modules that map core feature representations to individual neuron response predictions.
Base Readout
Readout
Bases: Module
Base readout class for all individual readouts. The MultiReadout will expect its readouts to inherit from this base class.
initialize
initialize(*args: Any, **kwargs: Any) -> None
Source code in openretina/modules/readout/base.py
23 24 | |
regularizer
regularizer(
reduction: Literal["sum", "mean", None] = "sum",
) -> Tensor
Source code in openretina/modules/readout/base.py
26 27 28 29 30 | |
apply_reduction
apply_reduction(
x: Tensor,
reduction: Literal["sum", "mean", None] = "mean",
) -> Tensor
Applies a reduction on the output of the regularizer. Args: x: output of the regularizer reduction: method of reduction for the regularizer. Currently possible are ['mean', 'sum', None].
Returns: reduced value of the regularizer
Source code in openretina/modules/readout/base.py
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | |
initialize_bias
initialize_bias(
mean_activity: Optional[
Float[Tensor, " n_neurons"]
] = None,
) -> None
Initialize the biases in readout. Args: mean_activity: Tensor containing the mean activity of neurons.
Returns:
Source code in openretina/modules/readout/base.py
53 54 55 56 57 58 59 60 61 62 63 64 65 66 | |
PointGaussianReadout
Spatial readout using learned Gaussian-sampled grid positions.
PointGaussianReadout
PointGaussianReadout(
in_shape: tuple[int, int, int, int],
outdims,
bias,
init_mu_range=0.1,
init_sigma_range=0.15,
batch_sample=True,
align_corners=True,
gauss_type="full",
grid_mean_predictor=None,
shared_features=None,
shared_grid=None,
init_grid=None,
source_grid=None,
mean_activity=None,
gamma_readout=1.0,
**kwargs,
)
Bases: Readout
A readout module that samples the output for each neuron at a single spatial location from the core feature map, where the location is drawn from a learned 2D Gaussian distribution for each neuron.
First introduced in Lurz et al., 2021: https://openreview.net/forum?id=Tp7kI90Htd
Key notes
-
Unlike mask-based readouts (
GaussianMaskReadout,FactorisedReadout), this readout does NOT produce a spatial mask over the input. Instead, each neuron's response is determined by interpolating the feature map at a point sampled from a Gaussian distribution (parameterized by mean and covariance) for each neuron. -
This mechanism results in each neuron having a flexible receptive field location within the Gaussian window during training (as the location is sampled from the Gaussian distribution), and a fixed location during inference (as the location is then fixed to the mean of the Gaussian distribution).
-
Instead of a spatial mask, the readout spatial location is a single "point" (x, y) in feature space per neuron per sample: there is no spatial integration or summing across a spatial region as in mask-based readouts.
-
Feature weights are still learned and behave like in the classic FactorisedReadout and GaussianMaskReadout.
| PARAMETER | DESCRIPTION |
|---|---|
in_shape
|
shape of the input feature map [channels, width, height]
TYPE:
|
outdims
|
number of output units
TYPE:
|
bias
|
adds a bias term
TYPE:
|
init_mu_range
|
initialises the mean with Uniform([-init_range, init_range]) [expected: positive value <=1]. Default: 0.1
TYPE:
|
init_sigma_range
|
The standard deviation of the Gaussian with
TYPE:
|
batch_sample
|
if True, samples a position for each image in the batch separately [default: True as it decreases convergence time and performs just as well]
TYPE:
|
align_corners
|
Keyword agrument to gridsample for bilinear interpolation. It changed behavior in PyTorch 1.3. The default of align_corners = True is setting the behavior to pre PyTorch 1.3 functionality for comparability.
TYPE:
|
gauss_type
|
Which Gaussian to use. Options are 'isotropic', 'uncorrelated', or 'full' (default).
TYPE:
|
grid_mean_predictor
|
Parameters for a predictor of the mean grid locations. Has to have a form like { 'hidden_layers':0, 'hidden_features':20, 'final_tanh': False, }
TYPE:
|
shared_features
|
Used when the feature vectors are shared (within readout between neurons) or between
this readout and other readouts. Has to be a dictionary of the form
{
'match_ids': (numpy.array),
'shared_features': torch.nn.Parameter or None
}
The match_ids are used to match things that should be shared within or across scans.
If
TYPE:
|
shared_grid
|
Like
TYPE:
|
source_grid
|
TYPE:
|
init_grid
|
Initial grid locations for the neurons. Only set if both shared_grid and grid_mean_predictor are None.
TYPE:
|
Source code in openretina/modules/readout/gaussian.py
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | |
forward
forward(x, sample=None, shift=None, out_idx=None, **kwargs)
Propagates the input forwards through the readout Args: x: input data sample (bool/None): sample determines whether we draw a sample from Gaussian distribution, N(mu,sigma), defined per neuron or use the mean, mu, of the Gaussian distribution without sampling. if sample is None (default), samples from the N(mu,sigma) during training phase and fixes to the mean, mu, during evaluation phase. if sample is True/False, overrides the model_state (i.e training or eval) and does as instructed shift (bool): shifts the location of the grid (from eye-tracking data) out_idx (bool): index of neurons to be predicted
| RETURNS | DESCRIPTION |
|---|---|
y
|
neuronal activity |
Source code in openretina/modules/readout/gaussian.py
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 | |
sample_grid
sample_grid(batch_size, sample=None)
Returns the grid locations from the core by sampling from a Gaussian distribution Args: batch_size (int): size of the batch sample (bool/None): sample determines whether we draw a sample from Gaussian distribution, N(mu,sigma), defined per neuron or use the mean, mu, of the Gaussian distribution without sampling. if sample is None (default), samples from the N(mu,sigma) during training phase and fixes to the mean, mu, during evaluation phase. if sample is True/False, overrides the model_state (i.e training or eval) and does as instructed
Source code in openretina/modules/readout/gaussian.py
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | |
regularizer
regularizer(reduction='sum', average=None)
Source code in openretina/modules/readout/gaussian.py
209 210 | |
GaussianMaskReadout
Readout using a factorized Gaussian mask over spatial feature maps.
GaussianMaskReadout
GaussianMaskReadout(
in_shape: tuple[int, int, int, int],
outdims: int,
mean_activity: Float[Tensor, " outdims"] | None = None,
gaussian_mean_scale: float = 1.0,
gaussian_var_scale: float = 1.0,
positive: bool = False,
scale: bool = False,
bias: bool = True,
nonlinearity_function=softplus,
mask_l1_reg: float = 1.0,
feature_weights_l1_reg: float = 1.0,
)
Bases: Readout
A readout module that computes each neuron's output as a weighted sum (dot product) across the spatial extent of the core feature map, using a 2D Gaussian mask per neuron. It can be considered as an extension of the classic FactorisedReadout, where the spatial mask is enforced to have a Gaussian shape.
First introduced in Hoefling et al., 2024: https://doi.org/10.7554/eLife.86860
Key notes
-
Unlike point-based Gaussian readouts (see
PointGaussianReadout), this class produces a full spatial mask for each neuron, effectively performing spatial integration (weighted by a Gaussian) across the entire input feature map in the spatial dimensions. -
Each neuron has a single mask_log_var scalar (not per-axis), used as variance for both x and y, so the receptive field is circular (in the normalized grid), axis-aligned, and isotropic.
| PARAMETER | DESCRIPTION |
|---|---|
in_shape
|
The shape of the input tensor (c, t, w, h).
TYPE:
|
outdims
|
The number of output dimensions (usually the number of neurons in the session).
TYPE:
|
mean_activity
|
The mean activity of the neurons, used to initialize the bias. Defaults to None.
TYPE:
|
gaussian_mean_scale
|
The scale factor for the Gaussian mask mean. Defaults to 1e0.
TYPE:
|
gaussian_var_scale
|
The scale factor for the Gaussian mask variance. Defaults to 1e0.
TYPE:
|
positive
|
Whether the output should be positive. Defaults to False.
TYPE:
|
scale
|
Whether to include a scale parameter. Defaults to False.
TYPE:
|
bias
|
Whether to include a bias parameter. Defaults to True.
TYPE:
|
nonlinearity_function
|
torch nonlinearity function , e.g. nn.functional.softplus
DEFAULT:
|
mask_l1_reg
|
The regularization strength for the sparsity of the spatial mask. Defaults to 1.0.
TYPE:
|
feature_weights_l1_reg
|
The regularization strength for the sparsity of feature weights. Defaults to 1.0.
TYPE:
|
Source code in openretina/modules/readout/factorized_gaussian.py
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | |
forward
forward(x: Tensor) -> Tensor
Source code in openretina/modules/readout/factorized_gaussian.py
151 152 153 154 155 156 157 158 159 160 | |
regularizer
regularizer(
reduction: Literal["sum", "mean", None] = "sum",
) -> Tensor
Source code in openretina/modules/readout/factorized_gaussian.py
119 120 121 122 123 124 | |
FactorizedReadout
FactorizedReadout
FactorizedReadout(
in_shape: tuple[int, int, int, int],
outdims: int,
mask_l1_reg: float,
weights_l1_reg: float,
laplace_mask_reg: float,
mask_size: int | tuple[int, int],
readout_bias: bool = False,
weights_constraint: Literal["abs", "norm", "absnorm"]
| None = None,
mask_constraint: Literal["abs"] | None = None,
init_mask: Tensor | None = None,
init_weights: Tensor | None = None,
init_scales: Sequence[tuple[float, float]]
| None = None,
mean_activity: Float[Tensor, " outdims"] | None = None,
)
Bases: Readout
The canonical factorized readout module: each neuron's output is the dot product of a learned 2D spatial mask and feature weights.
This module implements the general Factorized (a.k.a. "Klindt") Readout, where—for each neuron—the spatial integration is performed via a freeform, unconstrained (but sparse) mask, and the stimulus dimensions (e.g., features, channels, time) are combined by a separate learned vector of feature weights. The spatial mask is independently learned for every neuron without any restriction to a particular functional form, but with sparsity penalties.
First introduced in Klindt et al., 2017: https://doi.org/10.48550/arXiv.1711.02653
Key notes
-
Unlike parametric-masked readouts (see
GaussianMaskReadout), this class allows the spatial mask to take any shape, offering maximum expressive power for fitting the spatial receptive field. -
Typical regularizations include sparsity (L1), Laplace smoothness penalties, and optional constraints (non-negativity, normalization) on the mask or weights.
Initializes the FactorizedReadout module : (2d spatial mask + feature weights) / cell. Args: in_shape: The shape of the input tensor (c, t, w, h). outdims (int): Number of output neurons. mask_l1_reg (float): L1 regularization strength for mask. weights_l1_reg (float): L1 regularization strength for weights. laplace_mask_reg (float): Laplace regularization strength for mask. mask_size (int | Tuple[int, int]): Size of the mask (height, width) or (height). readout_bias (bool, optional): If True, includes bias in readout. Defaults to False. weights_constraint (Optional[str], optional): Constraint for weights. Defaults to None. mask_constraint (Optional[str], optional): Constraint for mask. Defaults to None. init_mask (Optional[torch.Tensor], optional): Initial mask tensor. Defaults to None. init_weights (Optional[torch.Tensor], optional): Initial weights tensor. Defaults to None. init_scales (Optional[Sequence[Tuple[float, float]]], optional): Initialization scales for mask and weights. Defaults to None. mean_activity (Float[torch.Tensor, " outdims"] | None): Mean activity of neurons. Defaults to None. Raises: ValueError: If neither init_mask nor init_scales is provided.
Source code in openretina/modules/readout/factorized.py
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | |
forward
forward(x: Tensor, **kwargs: Any) -> Tensor
Source code in openretina/modules/readout/factorized.py
146 147 148 149 150 151 152 153 154 155 156 157 | |
regularizer
regularizer(
reduction: Optional[Literal["sum", "mean"]] = None,
) -> Tensor
Source code in openretina/modules/readout/factorized.py
159 160 161 162 163 164 165 166 167 | |
LNPReadout
LNPReadout
LNPReadout(
in_shape: Int[tuple, "channel time height width"],
outdims: int,
mean_activity: Float[Tensor, " outdims"] | None = None,
smooth_weight: float = 0.0,
sparse_weight: float = 0.0,
smooth_regularizer: str = "LaplaceL2norm",
laplace_padding=None,
nonlinearity: str = "exp",
bias: bool = False,
**kwargs,
)
Bases: Readout
Linear Nonlinear Poisson Readout (LNP) For use as an LNP Model use this readout with a DummyCore that passes the input through.
Source code in openretina/modules/readout/linear_nonlinear_poison.py
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | |
forward
forward(
x: Float[Tensor, "batch channels t h w"],
data_key=None,
**kwargs,
)
Source code in openretina/modules/readout/linear_nonlinear_poison.py
57 58 59 60 61 | |
regularizer
regularizer(**kwargs)
Source code in openretina/modules/readout/linear_nonlinear_poison.py
78 79 | |
Multi-Session Readouts
Wrappers that manage one readout instance per recording session.
MultiReadoutBase
MultiReadoutBase(
in_shape: tuple[int, int, int, int],
n_neurons_dict: dict[str, int],
base_readout: type[Readout] | None = None,
mean_activity_dict: dict[str, Float[Tensor, " neurons"]]
| None = None,
clone_readout=False,
readout_reg_avg: bool = False,
**kwargs,
)
Bases: ModuleDict
Base class for MultiReadouts. It is a dictionary of data keys and readouts to the corresponding datasets.
Adapted from neuralpredictors. Original code at: https://github.com/sinzlab/neuralpredictors/blob/v0.3.0.pre/neuralpredictors/layers/readouts/multi_readout.py
| PARAMETER | DESCRIPTION |
|---|---|
in_shape_dict
|
dictionary of data_key and the corresponding dataset's shape as an output of the core.
TYPE:
|
n_neurons_dict
|
dictionary of data_key and the corresponding dataset's number of neurons
TYPE:
|
base_readout
|
base readout class. If None, self._base_readout must be set manually in the inheriting class's definition.
TYPE:
|
mean_activity_dict
|
dictionary of data_key and the corresponding dataset's mean responses. Used to initialize the readout bias with. If None, the bias is initialized with 0.
TYPE:
|
clone_readout
|
whether to clone the first data_key's readout to all other readouts, only allowing for a scale and offset. This is a rather simple method to enforce parameter-sharing between readouts.
TYPE:
|
gamma_readout
|
regularization strength
TYPE:
|
**kwargs
|
additional keyword arguments to be passed to the base_readout's constructor
DEFAULT:
|
Source code in openretina/modules/readout/multi_readout.py
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | |
forward
forward(
*args, data_key: str | None = None, **kwargs
) -> Tensor
Source code in openretina/modules/readout/multi_readout.py
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | |
add_sessions
add_sessions(
n_neurons_dict: dict[str, int],
mean_activity_dict: dict[str, Float[Tensor, " neurons"]]
| None = None,
) -> None
Wrapper method to add new sessions to the readout wrapper. Can be called to add new sessions to an existing readout wrapper. Individual readouts should override this method to add additional checks.
Source code in openretina/modules/readout/multi_readout.py
91 92 93 94 95 96 97 98 99 100 | |
regularizer
regularizer(
data_key: str | None = None,
reduction: Literal["sum", "mean"] | None = None,
)
Source code in openretina/modules/readout/multi_readout.py
151 152 153 154 155 156 157 158 | |
MultiGaussianMaskReadout
MultiGaussianMaskReadout(
in_shape: tuple[int, int, int, int],
n_neurons_dict: dict[str, int],
scale: bool,
bias: bool,
gaussian_mean_scale: float,
gaussian_var_scale: float,
positive: bool,
mask_l1_reg: float = 1.0,
feature_weights_l1_reg: float = 1.0,
readout_reg_avg: bool = False,
mean_activity_dict: dict[str, Float[Tensor, " neurons"]]
| None = None,
)
Bases: MultiReadoutBase
Multiple Sessions version of the GaussianMaskReadout factorised gaussian readout.
Source code in openretina/modules/readout/multi_readout.py
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | |
MultiFactorizedReadout
MultiFactorizedReadout(
in_shape: tuple[int, int, int, int],
n_neurons_dict: dict[str, int],
mask_l1_reg: float,
weights_l1_reg: float,
laplace_mask_reg: float,
readout_bias: bool = False,
weights_constraint: Literal["abs", "norm", "absnorm"]
| None = None,
mask_constraint: Literal["abs"] | None = None,
init_mask: Optional[Tensor] = None,
init_weights: Optional[Tensor] = None,
init_scales: Optional[Iterable[Iterable[float]]] = None,
readout_reg_avg: bool = False,
mean_activity_dict: dict[str, Float[Tensor, " neurons"]]
| None = None,
)
Bases: MultiReadoutBase
Multiple Sessions version of the classic factorized readout.
Source code in openretina/modules/readout/multi_readout.py
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 | |
MultiSampledGaussianReadout
MultiSampledGaussianReadout(
in_shape: tuple[int, int, int, int],
n_neurons_dict: dict[str, int],
bias: bool,
init_mu_range: float,
init_sigma_range: float,
batch_sample: bool = True,
align_corners: bool = True,
gauss_type: Literal["full", "iso"] = "full",
grid_mean_predictor=None,
shared_features=None,
shared_grid=None,
init_grid=None,
gamma: float = 1.0,
reg_avg: bool = False,
nonlinearity_function: Callable[
[Tensor], Tensor
] = softplus,
mean_activity_dict: dict[str, Float[Tensor, " neurons"]]
| None = None,
)
Bases: MultiReadoutBase
Multiple Sessions version of the sampled point gaussian readout.
Source code in openretina/modules/readout/multi_readout.py
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 | |
MultipleLNPReadout
MultipleLNPReadout(
in_shape: tuple[int, int, int, int],
n_neurons_dict: dict[str, int],
**kwargs,
)
Bases: MultiReadoutBase
Multiple Linear Nonlinear Poisson Readout (LNP) For use as an LNP Model use this readout with a DummyCore that passes the input through.
Source code in openretina/modules/readout/multi_readout.py
336 337 338 339 340 341 342 343 344 345 346 | |