class_docstr: A generalized version of all possible networks implemented using tensorflow.
class_docstr_tok: ["A","generalized","version","of","all","possible","networks","implemented","using","tensorflow","."]
code: def get_model(self) -> List:
      # validate the configuration
      if len(self.network_parameters.input_embedders_parameters) == 0:
          raise ValueError("At least one input type should be defined")
  
      if len(self.network_parameters.heads_parameters) == 0:
          raise ValueError("At least one output type should be defined")
  
      if self.network_parameters.middleware_parameters is None:
          raise ValueError(
              "Exactly one middleware type should be defined"
          )
  
      # ops for defining the training / testing phase
      self.is_training = tf.Variable(
          False,
          trainable=False,
          collections=[tf.GraphKeys.LOCAL_VARIABLES],
      )
      self.is_training_placeholder = tf.placeholder("bool")
      self.assign_is_training = tf.assign(
          self.is_training, self.is_training_placeholder
      )
  
      for network_idx in range(self.num_networks):
          with tf.variable_scope("network_{}".format(network_idx)):
              ####################
              # Input Embeddings #
              ####################
  
              state_embedding = []
              for input_name in sorted(
                  self.network_parameters.input_embedders_parameters
              ):
                  input_type = self.network_parameters.input_embedders_parameters[
                      input_name
                  ]
                  # get the class of the input embedder
                  input_embedder = self.get_input_embedder(
                      input_name, input_type
                  )
                  self.input_embedders.append(input_embedder)
  
                  # input placeholders are reused between networks. on the first network, store the placeholders
                  # generated by the input_embedders in self.inputs. on the rest of the networks, pass
                  # the existing input_placeholders into the input_embedders.
                  if network_idx == 0:
                      input_placeholder, embedding = input_embedder()
                      self.inputs[input_name] = input_placeholder
                  else:
                      input_placeholder, embedding = input_embedder(
                          self.inputs[input_name]
                      )
  
                  state_embedding.append(embedding)
  
              ##########
              # Merger #
              ##########
  
              if len(state_embedding) == 1:
                  state_embedding = state_embedding[0]
              else:
                  if (
                      self.network_parameters.embedding_merger_type
                      == EmbeddingMergerType.Concat
                  ):
                      state_embedding = tf.concat(
                          state_embedding, axis=-1, name="merger"
                      )
                  elif (
                      self.network_parameters.embedding_merger_type
                      == EmbeddingMergerType.Sum
                  ):
                      state_embedding = tf.add_n(
                          state_embedding, name="merger"
                      )
  
              ##############
              # Middleware #
              ##############
  
              self.middleware = self.get_middleware(
                  self.network_parameters.middleware_parameters
              )
              _, self.state_embedding = self.middleware(state_embedding)
  
              ################
              # Output Heads #
              ################
  
              head_count = 0
              for head_idx in range(self.num_heads_per_network):
                  if (
                      self.network_parameters.use_separate_networks_per_head
                  ):
                      # if we use separate networks per head, then the head type corresponds to the network idx
                      head_type_idx = network_idx
                      head_count = network_idx
                  else:
                      # if we use a single network with multiple embedders, then the head type is the current head idx
                      head_type_idx = head_idx
                  head_params = (
                      self.network_parameters.heads_parameters[
                          head_type_idx
                      ]
                  )
  
                  for head_copy_idx in range(
                      head_params.num_output_head_copies
                  ):
                      # create output head and add it to the output heads list
                      self.output_heads.append(
                          self.get_output_head(
                              head_params,
                              head_idx
                              * head_params.num_output_head_copies
                              + head_copy_idx,
                          )
                      )
  
                      # rescale the gradients from the head
                      self.gradients_from_head_rescalers.append(
                          tf.get_variable(
                              "gradients_from_head_{}-{}_rescalers".format(
                                  head_idx, head_copy_idx
                              ),
                              initializer=float(
                                  head_params.rescale_gradient_from_head_by_factor
                              ),
                              dtype=tf.float32,
                          )
                      )
  
                      self.gradients_from_head_rescalers_placeholders.append(
                          tf.placeholder(
                              "float",
                              name="gradients_from_head_{}-{}_rescalers".format(
                                  head_type_idx, head_copy_idx
                              ),
                          )
                      )
  
                      self.update_head_rescaler_value_ops.append(
                          self.gradients_from_head_rescalers[
                              head_count
                          ].assign(
                              self.gradients_from_head_rescalers_placeholders[
                                  head_count
                              ]
                          )
                      )
  
                      head_input = (
                          1
                          - self.gradients_from_head_rescalers[
                              head_count
                          ]
                      ) * tf.stop_gradient(
                          self.state_embedding
                      ) + self.gradients_from_head_rescalers[
                          head_count
                      ] * self.state_embedding
  
                      # build the head
                      if self.network_is_local:
                          (
                              output,
                              target_placeholder,
                              input_placeholders,
                              importance_weight_ph,
                          ) = self.output_heads[-1](head_input)
  
                          self.targets.extend(target_placeholder)
                          self.importance_weights.extend(
                              importance_weight_ph
                          )
                      else:
                          (
                              output,
                              input_placeholders,
                          ) = self.output_heads[-1](head_input)
  
                      self.outputs.extend(output)
                      # TODO: use head names as well
                      for (
                          placeholder_index,
                          input_placeholder,
                      ) in enumerate(input_placeholders):
                          self.inputs[
                              "output_{}_{}".format(
                                  head_type_idx, placeholder_index
                              )
                          ] = input_placeholder
  
                      head_count += 1
  
      # model weights
      if not self.distributed_training or self.network_is_global:
          self.weights = [
              var
              for var in tf.get_collection(
                  tf.GraphKeys.GLOBAL_VARIABLES, scope=self.full_name
              )
              if "global_step" not in var.name
          ]
      else:
          self.weights = [
              var
              for var in tf.get_collection(
                  tf.GraphKeys.TRAINABLE_VARIABLES, scope=self.full_name
              )
          ]
  
      # Losses
      self.losses = tf.losses.get_losses(self.full_name)
  
      # L2 regularization
      if self.network_parameters.l2_regularization != 0:
          self.l2_regularization = (
              tf.add_n([tf.nn.l2_loss(v) for v in self.weights])
              * self.network_parameters.l2_regularization
          )
          self.losses += self.l2_regularization
  
      self.total_loss = tf.reduce_sum(self.losses)
      # tf.summary.scalar('total_loss', self.total_loss)
  
      # Learning rate
      if self.network_parameters.learning_rate_decay_rate != 0:
          self.adaptive_learning_rate_scheme = tf.train.exponential_decay(
              self.network_parameters.learning_rate,
              self.global_step,
              decay_steps=self.network_parameters.learning_rate_decay_steps,
              decay_rate=self.network_parameters.learning_rate_decay_rate,
              staircase=True,
          )
  
          self.current_learning_rate = (
              self.adaptive_learning_rate_scheme
          )
      else:
          self.current_learning_rate = (
              self.network_parameters.learning_rate
          )
  
      # Optimizer
      if (
          self.distributed_training
          and self.network_is_local
          and self.network_parameters.shared_optimizer
      ):
          # distributed training + is a local network + optimizer shared -> take the global optimizer
          self.optimizer = self.global_network.optimizer
      elif (
          (
              self.distributed_training
              and self.network_is_local
              and not self.network_parameters.shared_optimizer
          )
          or self.network_parameters.shared_optimizer
          or not self.distributed_training
      ):
          # distributed training + is a global network + optimizer shared
          # OR
          # distributed training + is a local network + optimizer not shared
          # OR
          # non-distributed training
          # -> create an optimizer
  
          if self.network_parameters.optimizer_type == "Adam":
              self.optimizer = tf.train.AdamOptimizer(
                  learning_rate=self.current_learning_rate,
                  beta1=self.network_parameters.adam_optimizer_beta1,
                  beta2=self.network_parameters.adam_optimizer_beta2,
                  epsilon=self.network_parameters.optimizer_epsilon,
              )
          elif self.network_parameters.optimizer_type == "RMSProp":
              self.optimizer = tf.train.RMSPropOptimizer(
                  self.current_learning_rate,
                  decay=self.network_parameters.rms_prop_optimizer_decay,
                  epsilon=self.network_parameters.optimizer_epsilon,
              )
          elif self.network_parameters.optimizer_type == "LBFGS":
              self.optimizer = tf.contrib.opt.ScipyOptimizerInterface(
                  self.total_loss,
                  method="L-BFGS-B",
                  options={"maxiter": 25},
              )
          else:
              raise Exception(
                  "{} is not a valid optimizer type".format(
                      self.network_parameters.optimizer_type
                  )
              )
  
      return self.weights
  
imports: import copy
  from types import MethodType
  from typing import Dict, List, Union
  import numpy
  import tensorflow
  from rl_coach.architectures.embedder_parameters import InputEmbedderParameters
  from rl_coach.architectures.head_parameters import HeadParameters
  from rl_coach.architectures.middleware_parameters import MiddlewareParameters
  from rl_coach.architectures.tensorflow_components.architecture import TensorFlowArchitecture
  from rl_coach.architectures.tensorflow_components import utils
  from rl_coach.base_parameters import AgentParameters, Device, DeviceType, EmbeddingMergerType
  from rl_coach.core_types import PredictionType
  from rl_coach.logger import screen
  from rl_coach.spaces import SpacesDefinition, PlanarMapsObservationSpace, TensorObservationSpace
  from rl_coach.utils import get_all_subclasses, dynamic_import_and_instantiate_module_from_params, indent_string
label_desc: Use image node_id 9 for calling the GeneralTensorFlowNetwork obj's underlying member method code with example usage: obj.get_model() and returns: self