class_docstr: This class defines an application for image-level classification
  problems mapping from images to scalar labels.
  
  This is the application class to be instantiated by the driver
  and referred to in configuration files.
  
  Although structurally similar to segmentation, this application
  supports different samplers/aggregators (because patch-based
  processing is not appropriate), and monitoring metrics.
code: def connect_data_and_network(
      self, outputs_collector=None, gradients_collector=None
  ):
      def switch_sampler(for_training):
          with tf.name_scope("train" if for_training else "validation"):
              sampler = self.get_sampler()[0][0 if for_training else -1]
              return sampler.pop_batch_op()
  
      if self.is_training:
          self.patience = self.action_param.patience
          self.mode = self.action_param.early_stopping_mode
          if self.action_param.validation_every_n > 0:
              data_dict = tf.cond(
                  tf.logical_not(self.is_validation),
                  lambda: switch_sampler(for_training=True),
                  lambda: switch_sampler(for_training=False),
              )
          else:
              data_dict = switch_sampler(for_training=True)
  
          image = tf.cast(data_dict["image"], tf.float32)
          net_args = {
              "is_training": self.is_training,
              "keep_prob": self.net_param.keep_prob,
          }
          net_out = self.net(image, **net_args)
  
          with tf.name_scope("Optimiser"):
              optimiser_class = OptimiserFactory.create(
                  name=self.action_param.optimiser
              )
              self.optimiser = optimiser_class.get_instance(
                  learning_rate=self.action_param.lr
              )
          loss_func = LossFunction(
              n_class=self.classification_param.num_classes,
              loss_type=self.action_param.loss_type,
          )
          data_loss = loss_func(
              prediction=net_out,
              ground_truth=data_dict.get("label", None),
          )
          reg_losses = tf.get_collection(
              tf.GraphKeys.REGULARIZATION_LOSSES
          )
          if self.net_param.decay > 0.0 and reg_losses:
              reg_loss = tf.reduce_mean(
                  [tf.reduce_mean(reg_loss) for reg_loss in reg_losses]
              )
              loss = data_loss + reg_loss
          else:
              loss = data_loss
  
          self.total_loss = loss
  
          grads = self.optimiser.compute_gradients(
              loss, colocate_gradients_with_ops=True
          )
  
          outputs_collector.add_to_collection(
              var=self.total_loss,
              name="total_loss",
              average_over_devices=True,
              collection=CONSOLE,
          )
          outputs_collector.add_to_collection(
              var=self.total_loss,
              name="total_loss",
              average_over_devices=True,
              summary_type="scalar",
              collection=TF_SUMMARIES,
          )
          # collecting gradients variables
          gradients_collector.add_to_collection([grads])
          # collecting output variables
          outputs_collector.add_to_collection(
              var=data_loss,
              name="data_loss",
              average_over_devices=False,
              collection=CONSOLE,
          )
          outputs_collector.add_to_collection(
              var=data_loss,
              name="data_loss",
              average_over_devices=True,
              summary_type="scalar",
              collection=TF_SUMMARIES,
          )
          self.add_confusion_matrix_summaries_(
              outputs_collector, net_out, data_dict
          )
      else:
          # converting logits into final output for
          # classification probabilities or argmax classification labels
          data_dict = switch_sampler(for_training=False)
          image = tf.cast(data_dict["image"], tf.float32)
          net_args = {
              "is_training": self.is_training,
              "keep_prob": self.net_param.keep_prob,
          }
          net_out = self.net(image, **net_args)
          tf.logging.info(
              "net_out.shape may need to be resized: %s", net_out.shape
          )
          output_prob = self.classification_param.output_prob
          num_classes = self.classification_param.num_classes
          if output_prob and num_classes > 1:
              post_process_layer = PostProcessingLayer(
                  "SOFTMAX", num_classes=num_classes
              )
          elif not output_prob and num_classes > 1:
              post_process_layer = PostProcessingLayer(
                  "ARGMAX", num_classes=num_classes
              )
          else:
              post_process_layer = PostProcessingLayer(
                  "IDENTITY", num_classes=num_classes
              )
          net_out = post_process_layer(net_out)
  
          outputs_collector.add_to_collection(
              var=net_out,
              name="window",
              average_over_devices=False,
              collection=NETWORK_OUTPUT,
          )
          outputs_collector.add_to_collection(
              var=data_dict["image_location"],
              name="location",
              average_over_devices=False,
              collection=NETWORK_OUTPUT,
          )
          self.initialise_aggregator()
  
imports: import os
  import tensorflow
  from niftynet.application.base_application import BaseApplication
  from niftynet.engine.application_factory import ApplicationNetFactory, InitializerFactory, OptimiserFactory
  from niftynet.engine.application_variables import CONSOLE, NETWORK_OUTPUT, TF_SUMMARIES
  from niftynet.engine.sampler_resize_v2 import ResizeSampler
  from niftynet.engine.windows_aggregator_resize import ResizeSamplesAggregator
  from niftynet.engine.windows_aggregator_grid import GridSamplesAggregator
  from niftynet.io.image_reader import ImageReader
  from niftynet.layer.discrete_label_normalisation import DiscreteLabelNormalisationLayer
  from niftynet.layer.histogram_normalisation import HistogramNormalisationLayer
  from niftynet.layer.binary_masking import BinaryMaskingLayer
  from niftynet.layer.post_processing import PostProcessingLayer
  from niftynet.layer.loss_classification import LossFunction
  from niftynet.layer.mean_variance_normalisation import MeanVarNormalisationLayer
  from niftynet.layer.rand_flip import RandomFlipLayer
  from niftynet.layer.rand_rotation import RandomRotationLayer
  from niftynet.layer.rand_spatial_scaling import RandomSpatialScalingLayer
  from niftynet.evaluation.classification_evaluator import ClassificationEvaluator
label_desc: Use image node_id 8 for calling the ClassificationApplication obj's underlying member method code with example usage: obj.connect_data_and_network(outputs_collector, gradients_collector) and returns: sampler