示例#1
0
def _train_prebatch_processing(rim, rla, params):
  """
  TODO(panos): add more info...

  Arguments:
    rim: raw images, as extracted from tfrecords
    rla: raw labels, as extracted from tfrecords
    params: object with with the following attributes:
      height_feature_extractor: ...
      width_feature_extractor: ...
      training_lids2cids: ...
  """

  sfe = (params.height_feature_extractor, params.width_feature_extractor)

  ## prepare
  rim = tf.image.convert_image_dtype(rim, dtype=tf.float32)
  training_lids2cids = _replacevoids(params.training_problem_def['lids2cids'])
  rla = tf.gather(tf.cast(training_lids2cids, tf.int32), tf.to_int32(rla))

  ## preprocess
  rim.set_shape((None, None, None))
  rla.set_shape((None, None))
  proimages, prolabels = resize_images_and_labels(rim[tf.newaxis, ...],
                                                 rla[tf.newaxis, ...],
                                                 sfe,
                                                 preserve_aspect_ratio=params.preserve_aspect_ratio)
  proimage, prolabel = proimages[0], prolabels[0]

  # pre-batching augmentations
  pass

  return rim, rla, proimage, prolabel
示例#2
0
    def parse_args(self, argv):
        # parse all arguments and add manually additional arguments
        self.args = self._parser.parse_args(argv)

        # problem definitions
        self.args.training_problem_def = json.load(
            open(self.args.training_problem_def_path, 'r'))
        if hasattr(self.args, 'inference_problem_def_path'):
            if self.args.inference_problem_def_path is None:
                # TODO: implement the use of inference problem_def for training results in all functions
                self.args.inference_problem_def = self.args.training_problem_def
            else:
                self.args.inference_problem_def = json.load(
                    open(self.args.inference_problem_def_path, 'r'))
        if hasattr(self.args, 'evaluation_problem_def_path'):
            if self.args.evaluation_problem_def_path is None:
                self.args.evaluation_problem_def = self.args.training_problem_def
            else:
                self.args.evaluation_problem_def = json.load(
                    open(self.args.evaluation_problem_def_path, 'r'))
        if hasattr(self.args, 'additional_problem_def_path'):
            if self.args.additional_problem_def_path is None:
                self.args.additional_problem_def = self.args.training_problem_def
            else:
                self.args.additional_problem_def = json.load(
                    open(self.args.additional_problem_def_path, 'r'))

        # _validate_problem_config(self.args.config)
        # by convention class ids start at 0
        number_of_training_classes = max(
            self.args.training_problem_def['lids2cids']) + 1
        trained_with_void_class = -1 in self.args.training_problem_def[
            'lids2cids']
        self.args.training_Nclasses = number_of_training_classes + trained_with_void_class

        self.args.training_lids2cids = _replacevoids(
            self.args.training_problem_def['lids2cids'])

        if hasattr(self.args, 'additional_problem_def'):
            self.args.additional_lids2cids = _replacevoids(
                self.args.additional_problem_def['lids2cids'])

        # define dict from objects' attributes so as they are ordered for printing alignment
        # self.args_dict = collections.OrderedDict(sorted(vars(self.args).items()))

        return self.args
 class params(object):
     height_feature_extractor = 1024
     width_feature_extractor = 2048
     preserve_aspect_ratio = False
     # Ntrain = 2975
     tfrecords_path = _PATH_CITYS
     Nb = 12
     training_lids2cids = [
         -1, 0, -1, -1, -1, -1, -1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1,
         12, -1, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25
     ]
     training_problem_def = {'lids2cids': copy.copy(training_lids2cids)}
     training_lids2cids = _replacevoids(training_lids2cids)
     plotting = False
     distribute = False
示例#4
0
    def _map_metrics_to_evaluation_problem_def(self, metrics):
        # if a net should be evaluated with problem that is not the problem with which it was
        # trained for, then the mappings from that problem should be provided.

        # only confusion_matrix is suppported for now
        assert set(metrics.keys()) == {
            'global_step', 'loss', 'confusion_matrix'
        }, ('internal error: only confusion matrix metric is supported for mapping to'
            'a new problem definition for now. Change to training problem definition.'
            )
        assert 'training_cids2evaluation_cids' in self._settings.evaluation_problem_def.keys(
        ), ('Evaluation problem definition should have training_cids2evaluation_cids field.'
            )

        old_cm = metrics['confusion_matrix']
        tcids2ecids = np.array(
            _replacevoids(
                self._settings.
                evaluation_problem_def['training_cids2evaluation_cids']))

        # TODO: confusion matrix type and shape assertions
        assert old_cm.shape[0] == tcids2ecids.shape[
            0], 'Mapping lengths should me equal. %i (old_cm) %i (tcids2ecids)' % \
                (old_cm.shape[0], tcids2ecids.shape[0])

        temp_shape = (max(tcids2ecids) + 1, old_cm.shape[1])
        temp_cm = np.zeros(temp_shape, dtype=np.int64)

        # mas noiazei to kathe kainourio apo poio palio pairnei:
        #   i row of the new cm takes from rows of the old cm with indices:from_indices
        for i in range(temp_shape[0]):
            from_indices = [k for k, x in enumerate(tcids2ecids) if x == i]
            # print(from_indices)
            for fi in from_indices:
                temp_cm[i, :] += old_cm[fi, :].astype(np.int64)

        # oi grammes athroistikan kai tora tha athroistoun kai oi stiles
        new_shape = (max(tcids2ecids) + 1, max(tcids2ecids) + 1)
        new_cm = np.zeros(new_shape, dtype=np.int64)
        for j in range(new_shape[1]):
            from_indices = [k for k, x in enumerate(tcids2ecids) if x == j]
            # print(from_indices)
            for fi in from_indices:
                new_cm[:, j] += temp_cm[:, fi]

        metrics['confusion_matrix'] = new_cm

        return metrics
示例#5
0
def _evaluate_preprocess(image, label, params):
    _SIZE_FEATURE_EXTRACTOR = (params.height_network, params.width_network)

    ## prepare
    image = tf.image.convert_image_dtype(image, dtype=tf.float32)
    label = tf.gather(tf.cast(_replacevoids(params.evaluation_problem_def['lids2cids']), tf.int32), tf.to_int32(label))

    ## preprocess
    proimage = tf.image.resize_images(image, _SIZE_FEATURE_EXTRACTOR)
    prolabel = tf.image.resize_images(label[..., tf.newaxis],
                                      _SIZE_FEATURE_EXTRACTOR,
                                      method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)[..., 0]

    proimage, _ = preprocess_evaluate(proimage)

    print('debug: proimage, prolabel', proimage, prolabel)

    return image, label, proimage, prolabel
示例#6
0
def _lids2cids(lids2cids, lids):
    """
    Label ids to class ids conversion of ground truth using the lids2cids mapping.
    This function gathers cids from lids2cids according to indices from lids.
    Nl: number of labels
    Nc: number of classes

    Args:
    lids2cids: Nl, in [0, Nc-1]
    lids: H x W, tf.uint8, in [0, Nl-1]

    Returns:
    H x W, in [0, Nc-1]
    """
    # TODO: add type checking
    assert lids.dtype.is_integer, 'lids tensor must be integer.'

    return tf.gather(_replacevoids(lids2cids), lids)
def _map_predictions_to_new_cids(predictions, old_cids2new_cids):
    """Map training predictions to predictions according to inference problem definition."""
    # transform predictions with new class ids: decs and probs correspond to training class ids
    # for plotting they should be transformed to new (inference or evaluation) class ids
    #   e.g. output_Nclasses = 5,
    #        training_cids2inference_cids = [-1, 1, 1, 0, -1] --> [2, 1, 1, 0, 2]
    #        before: probs.shape: (..., 5) --> after: probs.shape: (..., 3)
    # for probs: using the probability of union rule: P(A ∪ B) = P(A) + P(B) - P(A ∩ B)
    #   with P(A ∩ B) = 0 probabilities have to be summed
    # TODO(panos): save computations by checking if inference and training
    #   problem defs are the same

    supported_keys = {
        'l1_probabilities', 'rawimages', 'rawimagespaths', 'decisions',
        'l1_decisions', 'l1_logits', 'l1_probabilities',
        'l2_vehicle_decisions', 'l2_vehicle_logits',
        'l2_vehicle_probabilities', 'l2_human_decisions', 'l2_human_logits',
        'l2_human_probabilities'
    }
    # check if supported_keys is a superset of predictions.keys()
    assert supported_keys >= predictions.keys(), (
        f"Supported keys are {sorted(supported_keys)} and predictions keys are {sorted(set(predictions.keys()))}. "
        f"Supported keys must be a superset of predictions keys for resizing predictions."
    )

    probs, decs = itemgetter('l1_probabilities', 'decisions')(predictions)
    old_cids2new_cids = _replacevoids(old_cids2new_cids)
    ocids2ncids = tf.cast(old_cids2new_cids, tf.int32)
    decs = tf.gather(ocids2ncids, decs)
    try:
        probs_transposed = tf.transpose(probs, (3, 0, 1, 2))
        probs_transformed = tf.unsorted_segment_sum(
            probs_transposed, ocids2ncids,
            tf.reduce_max(ocids2ncids) + 1)
        probs = tf.transpose(probs_transformed, (1, 2, 3, 0))
    except:
        tf.logging.info(
            '\nl1_probabilities are not transformed to new cids.\n')
    tf.logging.info('\nOnly decisions were transformed to new cids.\n')

    new_predictions = predictions
    new_predictions.update({'l1_probabilities': probs, 'decisions': decs})

    return new_predictions
示例#8
0
def _evaluate_preprocess(image, label, params):

  SIZE_FEATURE_EXTRACTOR = (params.height_feature_extractor, params.width_feature_extractor)

  ## prepare
  image = tf.image.convert_image_dtype(image, dtype=tf.float32)
  evaluation_lids2cids = _replacevoids(params.evaluation_problem_def['lids2cids'])
  label = tf.gather(tf.cast(evaluation_lids2cids, tf.int32), tf.to_int32(label))

  ## preprocess
  proimage = tf.image.resize_images(image, SIZE_FEATURE_EXTRACTOR)
  prolabel = tf.image.resize_images(label[..., tf.newaxis],
                                    SIZE_FEATURE_EXTRACTOR,
                                    method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)[..., 0]

  proimage = from_0_1_to_m1_1(proimage)

  print('debug: proimage, prolabel', proimage, prolabel)

  return image, label, proimage, prolabel
示例#9
0
    def __init__(self, input_fns, model_fn, settings=None):
        """Constructs a SS instance... (More info to be added...)

        Args:
          input_fns: a dictionary containing 'train', 'eval' and 'predict' keys to corresponding
            input functions. The input functions will be called by the respective functions of this
            class with the following signature: (customconfig, params) and should
            return a tuple of (features, labels) containing feature and label dictionaries with
            the needed str-tf.Tensor pairs. (required keys will be added, for now check code...).
          model_fn: model function for the fully convolutional semantic segmentation model chosen.
            It will be called with the following signature: (mode, features, labels, config, params).
          settings: an object containing all parsed parameters from command line as attributes.
          [UNUSED ARGUMENTS FOR NOW]
        Comments / design choices:
          1) Everytime a method {train, predict, evaluate} of this object is called a new local
             estimator is created with the desired properties saved in this class' members, specified
             to the respective action. This choice is made purely for memory efficiency.
        """
        assert settings is not None, ('settings must be provided for now.')
        _validate_settings(settings)

        self._input_fns = input_fns
        self._model_fn = model_fn
        self._settings = copy.deepcopy(settings)
        self._estimator = None

        # by convention class ids start at 0
        number_of_training_classes = max(
            self._settings.training_problem_def['lids2cids']) + 1
        trained_with_void_class = -1 in self._settings.training_problem_def[
            'lids2cids']  # -1 indicates a void class
        self._settings.training_Nclasses = number_of_training_classes + trained_with_void_class

        self._settings.training_lids2cids = _replacevoids(
            self._settings.training_problem_def['lids2cids'])

        # save to settings for external access
        self._settings.eval_res_dir = make_evaluation_dir(settings.log_dir)
示例#10
0
    def _map_predictions_to_inference_problem_def(self, predictions):
        assert 'training_cids2inference_cids' in self._settings.inference_problem_def.keys(
        ), ('Inference problem definition should have training_cids2inference_cids field, '
            'since provided inference problem definition file is not the same as training '
            'problem definition file.')

        tcids2pcids = np.array(
            _replacevoids(
                self._settings.
                inference_problem_def['training_cids2inference_cids']))

        for prediction in predictions:
            # only decisions is suppported for now
            assert set(prediction.keys(
            )).intersection(set(self._settings.predict_keys)) == {
                'decisions', 'rawimages', 'rawimagespaths'
            }, ('internal error: only decisions predict_key is supported for mapping to '
                'a new problem definition for now. Change to training problem definition.'
                )

            old_decisions = prediction['decisions']

            # TODO: add type and shape assertions
            assert old_decisions.ndim == 2, f"internal error: decisions shape is {old_decisions.shape}."

            new_decisions = tcids2pcids[old_decisions]
            if np.any(np.equal(new_decisions, -1)):
                log.debug(
                    'WARNING: -1 label exists in decisions, handle it properly externally.'
                )
                # raise NotImplementedError(
                #     'void mapping in different inference problem def is not yet implemented.')

            prediction['decisions'] = new_decisions

            yield prediction
def define_estimator(mode, features, labels, model_fn, config, params):
    """Add documentation... More information at tf.Estimator class.
  Assumptions:
    features: a dict containing rawfeatures and profeatures
      both: Nb x hf x wf x 3, tf.float32, in [0,1]
    labels: a dict containing rawfeatures and profeatures
      both: Nb x hf x wf, tf.int32, in [0,Nc-1]
  Args:
    features: First item returned by input_fn passed to train, evaluate, and predict.
    labels: Second item returned by input_fn passed to train, evaluate, and predict.
    mode: one of tf.estimator.ModeKeys.
    config: a tf.estimator.RunConfig object...
    parameters: a tf.train.HParams object...
    ...
  """

    assert mode in _ALLOWED_MODES, (
        'mode should be TRAIN, EVAL or PREDICT from tf.estimator.ModeKeys.')
    assert params.name_feature_extractor in {
        'resnet_v1_50', 'resnet_v1_101'
    }, ('params must have name_feature_extractor attribute in resnet_v1_{50,101}.'
        )
    if params.name_feature_extractor == 'resnet_v1_101':
        raise NotImplementedError(
            'Use of resnet_v1_101 as base feature extractor is not yet implemented.'
        )

    # unpack features
    rawimages = features['rawimages'] if 'rawimages' in features.keys(
    ) else None
    rawimagespaths = features[
        'rawimagespaths'] if 'rawimagespaths' in features.keys() else None
    proimages = features['proimages']
    prolabels = labels if labels else None

    ## build a fully convolutional model for semantic segmentation
    # predictions refer to the training class ids
    # for plotting of results (inference) or assessment, predictions should be transformed
    #   using `{inference, evaluation}_problem_def`s
    _, _, predictions = model_fn(mode, proimages, prolabels, config, params)

    # TODO(panos): assert that proimages and predictions have same spatial size

    if mode == tf.estimator.ModeKeys.TRAIN:

        # global step
        global_step = tf.train.get_or_create_global_step()

        # losses
        with tf.variable_scope('losses'):
            losses = define_losses(mode, predictions, prolabels, config,
                                   params)

        # exponential moving averages
        # creates variables in checkpoint with name: 'emas/' + <variable_name> +
        #   {'ExponentialMovingAverage,Momentum}
        # ex.: for 'classifier/logits/Conv/biases' it saves also
        #          'emas/classifier/logits/Conv/biases/ExponentialMovingAverage'
        #      and 'emas/classifier/logits/Conv/biases/Momentum'
        # create_train_op guarantees to run GraphKeys.UPDATE_OPS collection
        #   before total_loss in every step, but doesn't give any guarantee
        #   for running after some other op, and since ema need to be run
        #   after applying the gradients maybe this code needs checking
        if params.ema_decay > 0:
            with tf.variable_scope('exponential_moving_averages'):
                #for mv in slim.get_model_variables():
                #  print('slim.model_vars:', mv.op.name)
                ema = tf.train.ExponentialMovingAverage(
                    params.ema_decay,
                    num_updates=global_step,
                    zero_debias=True)
                variables_to_ema = []
                for mv in tf.model_variables():
                    if 'BatchNorm/moving' not in mv.name:
                        variables_to_ema.append(mv)
                print(
                    f"\nFound {len(tf.model_variables())} variables, saving exponential "
                    f"moving averages for {len(variables_to_ema)} of them.\n")
                maintain_ema_op = ema.apply(var_list=variables_to_ema)
                tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, maintain_ema_op)

        # create training operation
        with tf.variable_scope('train_ops'):

            # optimizer
            optimizer = define_optimizer(global_step, params)

            # training op
            train_op = create_train_op(
                losses['total'],
                optimizer,
                global_step=global_step,
                # update_ops=tf.get_collection(tf.GraphKeys.UPDATE_OPS),
                summarize_gradients=False,
                # transform_grads_fn=,
                # gradient_multipliers=gradient_multipliers,
                check_numerics=False,
            )

        # TODO: maybe parameterize it
        training_hooks = [
            _RunMetadataHook(params.log_dir,
                             every_n_iter=max(params.num_training_steps // 50,
                                              params.save_checkpoints_steps))
        ]

        # next two lines were added for distributed debugging
        if params.distribute:
            tower_context = tf.contrib.distribute.get_tower_context()
            assert tower_context
            print(
                f"Tower {tower_context.tower_id}: _RunMetadataHook is not supported "
                "yet for distributed training.")
            training_hooks = []

        replace_initializers(config, params)

        summaries_data = {
            'features': features,
            'labels': labels,
            'predictions': predictions,
            'losses': losses,
            'learning_rate': optimizer._learning_rate
        }  #pylint: disable=protected-access

        scaffold = _define_scaffold(mode, config, params, summaries_data)
        estimator_spec = tf.estimator.EstimatorSpec(
            mode,
            predictions=predictions,
            loss=losses['total'],
            train_op=train_op,
            training_hooks=training_hooks,
            scaffold=scaffold)

    if mode == tf.estimator.ModeKeys.EVAL:
        with tf.variable_scope('losses'):
            losses = define_losses(mode, predictions, prolabels, config,
                                   params)

        # returns (variable, update_op)
        # TF internal error/problem: _streaming_confusion_matrix internally casts
        # labels and predictions to int64, and since we feed a dictionary, tensors are
        # passed by reference leading them to change type, thus we send an identity
        # confusion_matrix = metrics_impl._streaming_confusion_matrix(  # pylint: disable=protected-access
        #     tf.identity(prolabels),
        #     tf.identity(predictions['decisions']),
        #     params.output_Nclasses)
        # l1_probs, decs = itemgetter('l1_probabilities', 'decisions')(predictions)
        # create a new dict with the supported keys only
        predictions = _map_predictions_to_new_cids(
            predictions, params.training_cids2evaluation_cids)
        if params.replace_voids:
            predictions = _replace_voids(predictions, params)
        # TODO(panos): confusion matrix expects prolabels and predictions to have the same shape
        #   this may not the case when preserve_aspect_ratio is set and this will give an error
        if hasattr(params, 'preserve_aspect_ratio'):
            if params.preserve_aspect_ratio:
                raise NotImplementedError(
                    'evaluation with preserving aspect ratio is not implemented.'
                )
        predictions = _resize_predictions(predictions,
                                          tf.shape(labels['prolabels'])[1:3],
                                          params)
        tcids2ecids = _replacevoids(params.training_cids2evaluation_cids)
        confusion_matrix = metrics_impl._streaming_confusion_matrix(  # pylint: disable=protected-access
            labels['prolabels'],
            predictions['decisions'],
            # +1 due to convention of starting counting at 0
            max(tcids2ecids) + 1)

        # dict of metrics keyed by name with values tuples of (metric_tensor, update_op)
        # TODO: add more semantic segmentation metrics
        eval_metric_ops = {
            'confusion_matrix':
            (tf.to_int32(confusion_matrix[0]), confusion_matrix[1])
        }

        scaffold = _define_scaffold(mode, config, params)
        estimator_spec = tf.estimator.EstimatorSpec(
            mode,
            predictions=predictions,
            loss=losses['total'],
            eval_metric_ops=eval_metric_ops,
            scaffold=scaffold)

    if mode == tf.estimator.ModeKeys.PREDICT:
        # create a new dict with the supported keys only
        l1_probs, l2_vehicle_probs, l2_human_probs, decs = itemgetter(
            'l1_probabilities', 'l2_vehicle_probabilities',
            'l2_human_probabilities', 'decisions')(predictions)
        predictions = {
            'l1_probabilities': l1_probs,
            'l2_vehicle_probabilities': l2_vehicle_probs,
            'l2_human_probabilities': l2_human_probs,
            'decisions': decs
        }
        # workaround for connecting input pipeline outputs to system output
        # TODO(panos): maybe from a system perspective makes more sense to have mapping and
        #   resizing in the system_factory
        # since these are functions of the system and not the network/estimator
        # new size defaults to provided values
        # if at least one is None then new size is the arbitrary size of rawimage in each step
        new_size = (params.height_system, params.width_system)
        is_arbitrary = not all(new_size)
        if is_arbitrary:
            if rawimages is not None:
                predictions['rawimages'] = rawimages
            if rawimagespaths is not None:
                predictions['rawimagespaths'] = rawimagespaths
            new_size = tf.shape(predictions['rawimages'])[1:3]
        predictions = _resize_predictions(predictions, new_size, params)
        tf.logging.warn(
            'Mapping of predictions to new cids is not implemented for now.')
        # predictions = _map_predictions_to_new_cids(predictions, params.training_cids2inference_cids)
        if params.replace_voids:
            predictions = _replace_voids(predictions, params)

        scaffold = _define_scaffold(mode, config, params)
        estimator_spec = tf.estimator.EstimatorSpec(mode,
                                                    predictions=predictions,
                                                    scaffold=scaffold)

    return estimator_spec