docstr: A Fully Connected (FC) network that supports:
  1. Batch Normalization
  2. Dropout
  3. Layer Normalization
  4. Output normalization
  5. Orthogonal initialization
  6. Skip (residual) connections (added only to layers with the same input and output dims)
docstr_tok: ["A","Fully","Connected","(","FC",")","network","that","supports",":","1",".","Batch","Normalization","2",".","Dropout","3",".","Layer","Normalization","4",".","Output","normalization","5",".","Orthogonal","initialization","6",".","Skip","(","residual",")","connections","(","added","only","to","layers","with","the","same","input","and","output","dims",")"]
code: def __init__(
      self,
      layers,
      activations,
      *,
      use_batch_norm: bool = False,
      min_std: float = 0.0,
      dropout_ratio: float = 0.0,
      use_layer_norm: bool = False,
      normalize_output: bool = False,
      orthogonal_init: bool = False,
      use_skip_connections: bool = False,
  ) -> None:
      """
      A Fully Connected (FC) network that supports:
      1. Batch Normalization
      2. Dropout
      3. Layer Normalization
      4. Output normalization
      5. Orthogonal initialization
      6. Skip (residual) connections (added only to layers with the same input and output dims)
      """
      super().__init__()
  
      self.input_dim = layers[0]
  
      # this list stores all the layers in the network
      modules: List[nn.Module] = []
  
      assert (
          len(layers) == len(activations) + 1
      ), f"Invalid number of layers {len(layers)} and activations {len(activations)}. Number of layers needs to be 1 + number of activations"
  
      for i, ((in_dim, out_dim), activation) in enumerate(
          zip(zip(layers, layers[1:]), activations)
      ):
          # build up each layer by stacking the components of the layer
          layer_components: List[nn.Module] = []
  
          # Add BatchNorm1d
          if use_batch_norm:
              layer_components.append(SlateBatchNorm1d(in_dim))
  
          # Add Linear
          linear = nn.Linear(in_dim, out_dim)
          # assuming activation is valid
          try:
              gain = torch.nn.init.calculate_gain(activation)
          except ValueError:
              gain = 1.0  # default value for other activation functions
          if orthogonal_init:
              # provably better https://openreview.net/forum?id=rkgqN1SYvr
              nn.init.orthogonal_(linear.weight.data, gain=gain)
          else:
              # gaussian init
              gaussian_fill_w_gain(
                  linear.weight,
                  gain=gain,
                  dim_in=in_dim,
                  min_std=min_std,
              )
          init.constant_(linear.bias, 0)  # type: ignore
          layer_components.append(linear)
  
          # Add LayerNorm
          if use_layer_norm and (
              normalize_output or i < len(activations) - 1
          ):
              layer_components.append(nn.LayerNorm(out_dim))  # type: ignore
  
          # Add activation
          if activation in ACTIVATION_MAP:
              layer_components.append(ACTIVATION_MAP[activation]())
          else:
              # See if it matches any of the nn modules
              layer_components.append(getattr(nn, activation)())
  
          # Add Dropout
          if dropout_ratio > 0.0 and (
              normalize_output or i < len(activations) - 1
          ):
              layer_components.append(nn.Dropout(p=dropout_ratio))
  
          layer = nn.Sequential(*layer_components)
          if use_skip_connections:
              if in_dim == out_dim:
                  layer = ResidualWrapper(layer)
              else:
                  logger.warn(
                      f"Skip connections are enabled, but layer in_dim ({in_dim}) != out_dim ({out_dim}). Skip connection will not be added for this layer"
                  )
          modules.append(layer)
      self.dnn = nn.Sequential(*modules)
  
imports: import logging
  import math
  from typing import List, Optional
  import torch
  import torch.nn
  import torch.nn.init
  from reagent.core import types
  from reagent.models.base import ModelBase
  from reagent.models.residual_wrapper import ResidualWrapper
label_desc: Use image node_id 1 to create a new FullyConnectedNetwork object from inherited base classes: ModelBase with example: obj = FullyConnectedNetwork(layers, activations)