Source code for dgs.utils.nn

"""
Helper and utility methods for creating neural networks using PyTorch.
"""

from typing import Union

from torch import nn


[docs] def set_up_hidden_layer_sizes( input_size: int, output_size: int, hidden_sizes: Union[list[int], None] = None ) -> list[int]: """Given the input and output size of an FC-NN, create a list of the sizes containing each hidden layer in the network. There might be zero hidden layers. Params: input_size: The size of the input to the FC-Layers. output_size: Output-size of the FC-Layers. hidden_layers: The dimensionality of each hidden layer in this network. Default None means no hidden layers. Returns: The sizes of the hidden layers including input and output size. """ layers: list[int] = [input_size] if not (hidden_sizes is None or len(hidden_sizes) == 0): for hidden_layer in hidden_sizes: layers.append(int(hidden_layer)) layers.append(output_size) return layers
[docs] def fc_linear( hidden_layers: list[int], bias: Union[bool, list[bool]] = True, act_func: Union[ list[Union[str, None, nn.Module]], tuple[Union[str, None, nn.Module], ...], ] = None, ) -> nn.Sequential: """Create a Network consisting of one or more fully connected linear layers with input and output sizes given by the ``hidden_layers``. Args: hidden_layers: A list containing the sizes of the input, hidden- and output layers. It is possible to use the :func:`set_up_hidden_layer_sizes` function to create this list. The length of the hidden layers is denoted ``L``. bias: Whether to use a bias in every layer. Can be a single value for the whole network or a list of length ``L - 1`` containing one value per layer. Default is ``True``. act_func: A list containing the activation function after each of the fully connected layers. There can be a single activation function after every layer. Therefore, ``act_func`` should have a length of ``L``. Every value can either be the :class:`torch.nn.Module` or the string representing the activation function. E.g. "ReLU" for :class:`~torch.nn.ReLU` Defaults to adding no activation functions. Returns: A sequential model containing ``N-1`` fully-connected layers. """ # pylint: disable=too-many-branches L = len(hidden_layers) # validate bias if isinstance(bias, bool): bias = [bias] * (L - 1) elif isinstance(bias, list): if len(bias) != (L - 1): raise ValueError(f"Length of bias {len(bias)} should be the same as L - 1 but got: {L - 1}") else: raise NotImplementedError(f"Bias should be a boolean or a list of booleans. Got: {bias}") # validate activation functions if act_func is None: act_func = [None] * (L - 1) elif isinstance(act_func, str): act_func = [act_func] * (L - 1) if not isinstance(act_func, (list, tuple)) or len(act_func) != (L - 1): raise ValueError(f"The activation functions should be a list of length L - 1, but got: {act_func}") if not all( af is None or isinstance(af, str) or (isinstance(af, type) and issubclass(af, nn.Module)) for af in act_func ): raise ValueError(f"Expected all activation functions to be None, strings, or a nn.Module, but got: {act_func}") # validate hidden layers if any(l <= 0 for l in hidden_layers): raise ValueError(f"Input, hidden or output size is <= 0. Got: {hidden_layers}") layers = [] for i in range(L - 1): layers.append(nn.Linear(in_features=hidden_layers[i], out_features=hidden_layers[i + 1], bias=bias[i])) a_i = act_func[i] if a_i is not None: if isinstance(a_i, str): try: a_i = getattr(nn, a_i) except AttributeError as e: raise AttributeError(f"Tried to load non-existent activation function '{a_i}'.") from e layers.append(a_i()) # make sure to call / instantiate the function here return nn.Sequential(*layers)