class_docstr: Base flatten and concatenation model for policies and critics.
  
  :param obs_shapes: Dictionary mapping of observation names to shapes.
  :param hidden_units: Dictionary mapping of action names to shapes.
  :param non_lin: The non-linearity to apply.
code: def __init__(
      self,
      obs_shapes: Dict[str, Sequence[int]],
      hidden_units: List[int],
      non_lin: nn.Module,
  ):
      super().__init__()
      self.hidden_units = hidden_units
      self.non_lin = non_lin
  
      self.perception_dict: Dict[str, PerceptionBlock] = dict()
  
      # first, flatten all observations
      flat_keys = []
      for obs, shape in obs_shapes.items():
          out_key = f"{obs}_flat"
          flat_keys.append(out_key)
          self.perception_dict[out_key] = FlattenBlock(
              in_keys=obs,
              out_keys=out_key,
              in_shapes=shape,
              num_flatten_dims=len(shape),
          )
  
      # next, concatenate flat observations
      in_shapes = [
          self.perception_dict[k].out_shapes()[0] for k in flat_keys
      ]
      self.perception_dict["concat"] = ConcatenationBlock(
          in_keys=flat_keys,
          out_keys="concat",
          in_shapes=in_shapes,
          concat_dim=-1,
      )
  
      # build perception part
      self.perception_dict["latent"] = DenseBlock(
          in_keys="concat",
          out_keys="latent",
          in_shapes=self.perception_dict["concat"].out_shapes(),
          hidden_units=self.hidden_units,
          non_lin=self.non_lin,
      )
  
      # initialize model weights
      module_init = make_module_init_normc(std=1.0)
      for key in self.perception_dict.keys():
          self.perception_dict[key].apply(module_init)
  
imports: from typing import Sequence, Dict, List, Tuple
  import torch
  from maze.perception.blocks.general.functional import FunctionalBlock
  from maze.train.trainers.common.value_transform import support_to_scalar
  from torch import nn
  from maze.perception.blocks import PerceptionBlock
  from maze.perception.blocks.feed_forward.dense import DenseBlock
  from maze.perception.blocks.general.concat import ConcatenationBlock
  from maze.perception.blocks.general.flatten import FlattenBlock
  from maze.perception.blocks.inference import InferenceBlock
  from maze.perception.blocks.output.linear import LinearOutputBlock
  from maze.perception.weight_init import make_module_init_normc
label_desc: Use image node_id 1 to create a new FlattenConcatBaseNet object from inherited base classes: nn with example: obj = FlattenConcatBaseNet(obs_shapes, hidden_units, non_lin)