code: def construct_tower(
      phoenix_spec,
      input_tensor,
      tower_name,
      architecture,
      is_training,
      lengths,
      logits_dimension,
      is_frozen,
      hparams,
      model_directory,
      dropout_rate=None,
      allow_auxiliary_head=False,
  ):
      """Creates a tower giving an architecture.
  
      Args:
        phoenix_spec: The trial's `phoenix_spec_pb2.PhoenixSpec` proto.
        input_tensor: An input `tf.Tensor` to build the network on top of. Can be
          also a list of tensors if the first build block expects a list. If you
          are using our own default blocks, then using a list will ignore all
          tensors except of the last one.
        tower_name: a unique name for the tower (string).
        architecture: np.array of ints (`block_builder.BlockType`) with the
          architecture of the neural network to build.
        is_training: a boolean indicating if we are in training.
        lengths: A `tf.Tensor` with the lengths (dimenions: [batch_size]) holding
          the length of each sequence for sequential problems. Keep as None, for non
          sequential problems.
        logits_dimension: The last axis dimension of the logits.
        is_frozen: Is the tower frozen - integer and not boolean.
        hparams: The hparams for the tower.
        model_directory: current trial model directory (a string).
        dropout_rate: a float indicating the rate of dropouts to apply between
          block_builder. Applied only if the value is above zero.
        allow_auxiliary_head: Whether to allow importing the tower's auxiliary head,
          if the tower has one. Only applicable for CNNs.
  
      Returns:
        The output `tf.Tensor` of the last layer in the built neural network.
      """
      blocks = block_builder.Blocks()
      if isinstance(input_tensor, list):
          tf.compat.v1.logging.warning(
              "Input layer fn is producing a list of tensors. This works with custom "
              "blocks that expect a list. Otherwise (if you are running our own "
              "architecture search), this is an error - you must concat all input "
              "tensors into one. The current run ignores all tensors in the list but "
              "the last."
          )
          output = input_tensor
      else:
          output = [input_tensor]
      block_index = 1
      str_signature = ""
      for block_type in architecture:
          str_signature += str(block_type)
          # TODO(b/172564129): Should block_index also be ignored when uniform
          # average transfer learning? How would we handle repeated blocks, e.g. two
          # FC layers stacked on top of each other.
          scope = "{0}_{1}_{2}".format(
              str(block_index),
              block_builder.BlockType(block_type).name,
              str_signature,
          )
          scope = strip_scope(
              scope,
              phoenix_spec.transfer_learning_spec.transfer_learning_type,
              str_signature,
          )
          with arg_scope(
              DATA_FORMAT_OPS, data_format=phoenix_spec.cnn_data_format
          ):
              output = blocks.get_new(
                  block_builder.BlockType(block_type),
                  override_name=scope,
              )(
                  inputs=output,
                  is_training=is_training,
                  lengths=lengths,
                  hparams=get_block_hparams(
                      hparams, block_builder.BlockType(block_type).name
                  ),
              )
              if dropout_rate and dropout_rate > 0:
                  output[-1] = tf.compat.v1.layers.dropout(
                      output[-1],
                      rate=dropout_rate,
                      training=is_training,
                  )
              block_index += 1
  
      # Create the logits.
      scope = "last_dense_{}".format(str_signature)
      scope = strip_scope(
          scope,
          phoenix_spec.transfer_learning_spec.transfer_learning_type,
          str_signature,
      )
      with tf.compat.v1.variable_scope(scope):
          tower_spec = create_tower_spec(
              phoenix_spec,
              output,
              architecture,
              logits_dimension,
              is_frozen,
              lengths,
              allow_auxiliary_head,
          )
  
      set_architecture(architecture, tower_name)
      set_parameter(
          tower_name,
          DROPOUTS,
          (-1.0 if dropout_rate is None else dropout_rate),
          tf.float32,
      )
      set_parameter(tower_name, IS_FROZEN, int(is_frozen))
      if not tf.io.gfile.exists(model_directory):
          tf.io.gfile.makedirs(model_directory)
      store_hparams_to_dir(
          hparams=hparams,
          model_directory=model_directory,
          tower_name=tower_name,
      )
      return tower_spec
  
imports: import collections
  import os
  import string
  import time
  from absl import flags
  from absl import logging
  from model_search import block
  from model_search import block_builder
  from model_search import hparam
  from model_search import utils
  from model_search.proto import hparam_pb2
  from model_search.proto import phoenix_spec_pb2
  from model_search.proto import transfer_learning_spec_pb2
  import numpy
  import tensorflow.compat.v2
  import tf_slim
  from google.protobuf import text_format
label_desc: Use image node_id 16 for calling a global function with example usage: construct_tower(phoenix_spec, input_tensor, tower_name, architecture, is_training, lengths, logits_dimension, is_frozen, hparams, model_directory, dropout_rate, allow_auxiliary_head) and returns: tower_spec