# Copyright 2022 The Plenoptix Team. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Multi Layer Perceptron """ from typing import Optional, Tuple import torch from torch import nn from torchtyping import TensorType from nerfactory.fields.modules.base import FieldModule class MLP(FieldModule): """Multilayer perceptron Args: in_dim: Input layer dimension num_layers: Number of network layers layer_width: Width of each MLP layer out_dim: Ouput layer dimension. Uses layer_width if None. activation: intermediate layer activation function. out_activation: output activation function. """ def __init__( self, in_dim: int, num_layers: int, layer_width: int, out_dim: Optional[int] = None, skip_connections: Optional[Tuple[int]] = None, activation: Optional[nn.Module] = nn.ReLU(), out_activation: Optional[nn.Module] = None, ) -> None: super().__init__() self.in_dim = in_dim assert self.in_dim > 0 self.out_dim = out_dim if out_dim is not None else layer_width self.num_layers = num_layers self.layer_width = layer_width self.skip_connections = skip_connections self.activation = activation self.out_activation = out_activation self.net = None self.build_nn_modules() def build_nn_modules(self) -> None: """Initialize multi-layer perceptron.""" layers = [] if self.num_layers == 1: layers.append(nn.Linear(self.in_dim, self.out_dim)) else: for i in range(self.num_layers - 1): if i == 0: if self.skip_connections is not None: assert i not in list(self.skip_connections), "Skip connection at layer 0 doesn't make sense." layers.append(nn.Linear(self.in_dim, self.layer_width)) elif self.skip_connections is not None and i in self.skip_connections: layers.append(nn.Linear(self.layer_width + self.in_dim, self.layer_width)) else: layers.append(nn.Linear(self.layer_width, self.layer_width)) layers.append(nn.Linear(self.layer_width, self.out_dim)) self.layers = nn.ModuleList(layers) def forward(self, in_tensor: TensorType["bs":..., "in_dim"]) -> TensorType["bs":..., "out_dim"]: """Process input with a multilayer perceptron. Args: in_tensor: Network input Returns: MLP network output """ x = in_tensor for i, layer in enumerate(self.layers): if self.skip_connections is not None and i in self.skip_connections: x = torch.cat([in_tensor, x], -1) x = layer(x) if self.activation: x = self.activation(x) return x