docs: def build_graph(self, data_paths, batch_size, graph_mod):
      """Builds generic graph for training or eval."""
      tensors = GraphReferences()
      is_training = graph_mod == GraphMod.TRAIN
      if data_paths:
        _, tensors.examples = _util.read_examples(
            data_paths,
            batch_size,
            shuffle=is_training,
            num_epochs=None if is_training else 2)
      else:
        tensors.examples = tf.placeholder(tf.string, name='input', shape=(None,))
  
      if graph_mod == GraphMod.PREDICT:
        inception_input, inception_embeddings = self.build_inception_graph()
        # Build the Inception graph. We later add final training layers
        # to this graph. This is currently used only for prediction.
        # For training, we use pre-processed data, so it is not needed.
        embeddings = inception_embeddings
        tensors.input_jpeg = inception_input
      else:
        # For training and evaluation we assume data is preprocessed, so the
        # inputs are tf-examples.
        # Generate placeholders for examples.
        with tf.name_scope('inputs'):
          feature_map = {
              'image_uri':
                  tf.FixedLenFeature(
                      shape=[], dtype=tf.string, default_value=['']),
              # Some images may have no labels. For those, we assume a default
              # label. So the number of labels is label_count+1 for the default
              # label.
              'label':
                  tf.FixedLenFeature(
                      shape=[1], dtype=tf.int64,
                      default_value=[len(self.labels)]),
              'embedding':
                  tf.FixedLenFeature(
                      shape=[BOTTLENECK_TENSOR_SIZE], dtype=tf.float32)
          }
          parsed = tf.parse_example(tensors.examples, features=feature_map)
          labels = tf.squeeze(parsed['label'])
          uris = tf.squeeze(parsed['image_uri'])
          embeddings = parsed['embedding']
  
      # We assume a default label, so the total number of labels is equal to
      # label_count+1.
      all_labels_count = len(self.labels) + 1
      with tf.name_scope('final_ops'):
        softmax, logits = self.add_final_training_ops(
            embeddings,
            all_labels_count,
            BOTTLENECK_TENSOR_SIZE,
            dropout_keep_prob=self.dropout if is_training else None)
  
      # Prediction is the index of the label with the highest score. We are
      # interested only in the top score.
      prediction = tf.argmax(softmax, 1)
      tensors.predictions = [prediction, softmax, embeddings]
  
      if graph_mod == GraphMod.PREDICT:
        return tensors
  
      with tf.name_scope('evaluate'):
        loss_value = loss(logits, labels)
  
      # Add to the Graph the Ops that calculate and apply gradients.
      if is_training:
        tensors.train, tensors.global_step = training(loss_value)
      else:
        tensors.global_step = tf.Variable(0, name='global_step', trainable=False)
        tensors.uris = uris
  
      # Add means across all batches.
      loss_updates, loss_op = _util.loss(loss_value)
      accuracy_updates, accuracy_op = _util.accuracy(logits, labels)
  
      if not is_training:
        tf.summary.scalar('accuracy', accuracy_op)
        tf.summary.scalar('loss', loss_op)
  
      tensors.metric_updates = loss_updates + accuracy_updates
      tensors.metric_values = [loss_op, accuracy_op]
      return tensors