code: def _evaluate(
      args, config, monitoring_report, best_error, epoch, scheduler
  ):
      comm = current_communicator()
      error_str = ""
      valid_error = 0.0
  
      def _sum_error(sum, error):
          ret = None
          if comm:
              # logger.log(99, "Calc error with communicator")
              var = [nn.NdArray()]
              var[0].data = error
              _all_reduce(comm, var, division=False, inplace=True)
              ret = sum + var[0].data
          else:
              ret = sum + error
          return ret
  
      for name, mon in config.monitors.items():
          m = mon.monitor
          error_sum_monitor = 0.0
          error_count = 0
          data_size = max([di.size for di in mon.data_iterators])
          batch_size = max([di.batch_size for di in mon.data_iterators])
  
          for i in range(data_size // batch_size):
              # Load dataset
              data = OrderedDict()
              for di in mon.data_iterators:
                  data.update(zip(di.variables, di.next()))
  
              # Set data to variable
              for v, d in m.dataset_assign.items():
                  # NOTICE: trivially increase cuda memory usage for loading all edge nodes
                  dest_context = config.global_config.default_context
                  let_data_to_variable(
                      v.variable_instance,
                      data[d],
                      ctx=dest_context,
                      data_name=d,
                      variable_name=v.name,
                  )
  
              # Generate data
              for v, generator in m.generator_assign.items():
                  # NOTICE: trivially increase cuda memory usage for loading all edge nodes
                  dest_context = config.global_config.default_context
                  let_data_to_variable(
                      v.variable_instance,
                      data=generator(v.variable_instance.d.shape),
                      ctx=dest_context,
                      variable_name=v.name,
                  )
  
              # Sum error before forward to prepare input data while processing
              # on GPU
              if error_count > 0:
                  error_sum = 0.0
                  for v in m.monitor_variables:
                      error_sum += np.mean(v.variable_instance.d)
                      # v.variable_instance.data.zero()
                  error_sum_monitor = _sum_error(
                      error_sum_monitor, error_sum
                  )
                  if single_or_rankzero():
                      progress(
                          'Evaluating "{0}"'.format(name)
                          + " : error={0:0.6f}".format(
                              error_sum_monitor / error_count
                          ),
                          di.position * 1.0 / di.size,
                      )
              error_count += comm.size if comm else 1
  
              with scheduler:
                  # Forward recursive
                  m.target.forward(
                      clear_no_need_grad=True, clear_buffer=True
                  )
  
          # Sum error at the end of dataset
          error_sum = 0.0
          for v in m.monitor_variables:
              error_sum += np.mean(v.variable_instance.d)
              # v.variable_instance.data.zero()
          error_sum_monitor = _sum_error(error_sum_monitor, error_sum)
  
          if error_count == 0:
              error = 0
          else:
              error = error_sum_monitor / error_count
  
          if np.isnan(error) or np.isinf(error):
              logger.log(99, "Validation error is Nan")
              error = 0.0
  
          monitoring_report.append("  {}: {}\n".format(name, error))
  
          callback.update_status(
              (["monitoring_report", epoch, name], error)
          )
          callback.update_status(
              (["last", name], error)
          )  # save last value
  
          if error_str != "":
              error_str += ", "
          else:
              error_str = " {"
          error_str += "{}={:.6f}".format(name, error)
          if name == "valid_error":
              valid_error = error
  
      if error_str != "":
          error_str += "}"
  
      # Save Parameters
      if single_or_rankzero():
          if (
              (not config.training_config.save_best)
              or (not best_error)
              or (best_error is not None and valid_error <= best_error)
          ):
              best_error = valid_error
              callback.update_status(("best.valid_error", best_error))
              callback.update_status(("best.epoch", epoch))
              _save_parameters(args, "best", epoch, config, True)
  
      return best_error, error_str
  
imports: import glob
  import os
  import time
  import zipfile
  from collections import OrderedDict
  import nnabla
  import nnabla.utils.callback
  import nnabla.utils.load
  import numpy
  from contextlib2 import ExitStack
  from nnabla import available_contexts
  from nnabla.config import nnabla_config
  from nnabla.logger import logger
  from nnabla.parameter import save_parameters
  from nnabla.utils.cli.utility import NodeTimeInfoCollector
  from nnabla.utils.cli.utility import get_cpu_gpu_average_load
  from nnabla.utils.cli.utility import let_data_to_variable
  from nnabla.utils.cli.utility import lms_scheduler
  from nnabla.utils.cli.utility import load_train_state
  from nnabla.utils.cli.utility import measure_cpu_gpu_instant_load
  from nnabla.utils.cli.utility import save_optimizer_states
  from nnabla.utils.cli.utility import str_to_num
  from nnabla.utils.communicator_util import current_communicator, single_or_rankzero
  from nnabla.utils.nnp_format import nnp_version
  from nnabla.utils.progress import configure_progress, progress
  from six.moves import range
label_desc: Use image node_id 4 for calling a global function with example usage: _evaluate(args, config, monitoring_report, best_error, epoch, scheduler) and returns: best_error,  error_str, ret