Example #1
0
    def _build(self, y_pred, y_true):
        """One-time setup of metric objects."""

        if self._output_names is None:
            # Subclass output names like 'output_1' are used for `Metric` names.
            self._output_names = create_pseudo_output_names(y_pred)

        # If a single metric or flat list of metrics, apply to all outputs.
        self._metrics = self._maybe_broadcast(self._metrics, y_pred)
        self._weighted_metrics = self._maybe_broadcast(self._weighted_metrics,
                                                       y_pred)

        # Accept a dict of metrics keyed by output_name when outputs are a flat
        # list.
        self._metrics = map_to_output_names(y_pred, self._output_names,
                                            self._metrics)
        self._weighted_metrics = map_to_output_names(y_pred,
                                                     self._output_names,
                                                     self._weighted_metrics)

        # Standardize on tuple since `tf.data` turns lists into `Tensor`s.
        # pylint: disable=protected-access
        y_pred = nest._list_to_tuple(y_pred)
        y_true = nest._list_to_tuple(y_true)
        self._metrics = nest._list_to_tuple(self._metrics)
        self._weighted_metrics = nest._list_to_tuple(self._weighted_metrics)
        # pylint: enable=protected-access

        # Convert to `Metric` objects, potentially disambiguating based on output
        # properties.
        self._metrics = nest.map_structure_up_to(y_pred,
                                                 self._get_metric_objects,
                                                 self._metrics, y_true, y_pred)
        self._weighted_metrics = nest.map_structure_up_to(
            y_pred, self._get_metric_objects, self._weighted_metrics, y_true,
            y_pred)

        self._metrics = nest.flatten_up_to(y_pred,
                                           self._metrics,
                                           check_types=False)
        self._weighted_metrics = nest.flatten_up_to(y_pred,
                                                    self._weighted_metrics,
                                                    check_types=False)

        # Assumes metrics, weighted_metrics have been flattened up to outputs.
        self._set_metric_names()

        # Cache the flat order needed when returning metrics, for backwards compat.
        self._metrics_in_order = []
        for output_metrics, output_weighted_metrics in zip(
                self._metrics, self._weighted_metrics):
            for m in nest.flatten(output_metrics):
                if m is not None:
                    self._metrics_in_order.append(m)
            for wm in nest.flatten(output_weighted_metrics):
                if wm is not None:
                    self._metrics_in_order.append(wm)

        self._built = True
def _process_tensorlike(inputs):
  """Process tensor-like inputs.

  This function:

  (1) Converts `Numpy` arrays to `Tensor`s.
  (2) Converts `Scipy` sparse matrices to `SparseTensor`s.
  (2) Converts `list`s to `tuple`s (for `tf.data` support).

  Args:
    inputs: Structure of `Tensor`s, `NumPy` arrays, or tensor-like.

  Returns:
    Structure of `Tensor`s or tensor-like.
  """

  def _convert_numpy_and_scipy(x):
    if isinstance(x, np.ndarray):
      dtype = None
      if issubclass(x.dtype.type, np.floating):
        dtype = backend.floatx()
      return ops.convert_to_tensor(x, dtype=dtype)
    elif scipy_sparse and scipy_sparse.issparse(x):
      return _scipy_sparse_to_sparse_tensor(x)
    return x

  inputs = nest.map_structure(_convert_numpy_and_scipy, inputs)
  return nest._list_to_tuple(inputs)  # pylint: disable=protected-access
    def _build(self, y_pred, y_true):
        """One-time setup of metric objects."""
        super(MetricsContainer, self)._build(y_pred)

        self._metrics = self._maybe_broadcast_to_outputs(y_pred, self._metrics)
        self._metrics = self._conform_to_outputs(y_pred, self._metrics)

        self._weighted_metrics = self._maybe_broadcast_to_outputs(
            y_pred, self._weighted_metrics)
        self._weighted_metrics = self._conform_to_outputs(
            y_pred, self._weighted_metrics)

        # Standardize on tuple since `tf.data` turns lists into `Tensor`s.
        # pylint: disable=protected-access
        y_pred = nest._list_to_tuple(y_pred)
        y_true = nest._list_to_tuple(y_true)
        self._metrics = nest._list_to_tuple(self._metrics)
        self._weighted_metrics = nest._list_to_tuple(self._weighted_metrics)
        # pylint: enable=protected-access

        # Convert to `Metric` objects, potentially disambiguating based on output
        # properties.
        self._metrics = nest.map_structure_up_to(y_pred,
                                                 self._get_metric_objects,
                                                 self._metrics, y_true, y_pred)
        self._weighted_metrics = nest.map_structure_up_to(
            y_pred, self._get_metric_objects, self._weighted_metrics, y_true,
            y_pred)

        self._metrics = nest.flatten_up_to(y_pred,
                                           self._metrics,
                                           check_types=False)
        self._weighted_metrics = nest.flatten_up_to(y_pred,
                                                    self._weighted_metrics,
                                                    check_types=False)

        # Assumes metrics, weighted_metrics have been flattened up to outputs.
        self._set_metric_names()
        self._create_ordered_metrics()
        self._built = True
Example #4
0
 def map_fn(x, y=None, sample_weights=None):
   """Tensor manipulation portion of standardization for Dataset.map."""
   standardized = model._standardize_tensors(
       x, y, sample_weights,
       run_eagerly=False,
       dict_inputs=isinstance(x, dict),
       is_dataset=False,
       class_weight=class_weights,
       batch_size=None)
   x, y, sample_weights = nest._list_to_tuple(standardized)
   if y is None:
     return (x,)
   if sample_weights is None:
     return x, y
   return x, y, sample_weights
  def _standardize_batch(self, data):
    """Standardizes a batch output by a generator."""
    # Removes `None`s.
    x, y, sample_weight = unpack_x_y_sample_weight(data)
    data = pack_x_y_sample_weight(x, y, sample_weight)

    data = nest._list_to_tuple(data)  # pylint: disable=protected-access

    def _convert_dtype(t):
      if (isinstance(t, np.ndarray) and issubclass(t.dtype.type, np.floating)):
        return np.array(t, dtype=backend.floatx())
      return t

    data = nest.map_structure(_convert_dtype, data)
    return data
Example #6
0
    def wrapped_generator():
      """Remove Nones and lists before invoking Dataset.from_generator."""
      for batch in generator_fn():
        if wrap_in_tuple:
          batch = (batch,)

        if must_extract_lists:
          batch = nest._list_to_tuple(batch)  # pylint: disable=protected-access

        if must_prune_nones:
          batch = batch[:elements_to_keep]

        if partial_sample_weight:
          sample_weights, _, _ = training_utils.handle_partial_sample_weights(
              batch[1], batch[2], sample_weight_modes, check_all_flat=False)
          batch = batch[:2] + (sample_weights,)

        yield batch
Example #7
0
      def map_fn(x, y=None, sample_weights=None):
        """Tensor manipulation portion of standardization for Dataset.map."""
        if (y is None and sample_weights is None):
          # namedtuples are forbidden because it is ambiguous if they should be
          # unpacked. If y or sample_weights is present then `x` was not the
          # top level structure, and the correct behavior is unambiguous.
          data_adapter.assert_not_namedtuple(x)

        standardized = model._standardize_tensors(
            x, y, sample_weights,
            run_eagerly=False,
            dict_inputs=isinstance(x, dict),
            is_dataset=False,
            class_weight=class_weights,
            batch_size=None)
        x, y, sample_weights = nest._list_to_tuple(standardized)
        if y is None:
          return (x,)
        if sample_weights is None:
          return x, y
        return x, y, sample_weights
Example #8
0
    def _make_bridging_callable(generator_fn, wrap_in_tuple, peek,
                                elements_to_keep, partial_sample_weight,
                                sample_weight_modes):
        """Optional compatibility layer between user's data and Dataset."""
        must_prune_nones = (elements_to_keep != len(peek))
        try:
            nest.assert_same_structure(peek, nest._list_to_tuple(peek))  # pylint: disable=protected-access
            must_extract_lists = False
        except TypeError:
            must_extract_lists = True

        # No additional transformations are needed.
        if not (wrap_in_tuple or must_extract_lists or must_prune_nones
                or partial_sample_weight):
            return generator_fn

        def wrapped_generator():
            """Remove Nones and lists before invoking Dataset.from_generator."""
            for batch in generator_fn():
                if wrap_in_tuple:
                    batch = (batch, )

                if must_extract_lists:
                    batch = nest._list_to_tuple(batch)  # pylint: disable=protected-access

                if must_prune_nones:
                    batch = batch[:elements_to_keep]

                if partial_sample_weight:
                    sample_weights, _, _ = training_utils.handle_partial_sample_weights(
                        batch[1],
                        batch[2],
                        sample_weight_modes,
                        check_all_flat=False)
                    batch = batch[:2] + (sample_weights, )

                yield batch

        return wrapped_generator
Example #9
0
    def _canonicalize_peek(self, peek, sample_weight_modes):
        """Map the peeked batch into a regular form.

    This function serves two purposes. First, it determines if per-batch
    transformations are needed. Second, it extracts the structure to be used
    by Dataset.from_generator.

    Args:
      peek: The first batch of the user's data
      sample_weight_modes: Optional structure indicating how to handle sample
        weights. If it is a string, it will be mapped to match the target
        structure.

    Returns:
      An updated peek and various inspection results.
    """
        wrap_in_tuple = False
        if not isinstance(peek, tuple):
            peek, wrap_in_tuple = (peek, ), True

        if len(peek) not in (1, 2, 3):
            raise ValueError(
                "Output of generator should be a tuple of 1 or 2 or 3 elements: "
                "(input,) or (input, target) or (input, target, sample_weights). "
                "Received {}".format(peek))

        x_peek, y_peek, sample_weights_peek = list(peek) + [None
                                                            ] * (3 - len(peek))

        any_sample_weight, partial_sample_weight = False, False
        sample_weight_modes = broadcast_sample_weight_modes(
            sample_weights_peek if sample_weights_peek is not None else y_peek,
            sample_weight_modes)

        if len(peek) == 3:
            (sample_weights_peek, any_sample_weight, partial_sample_weight
             ) = training_utils.handle_partial_sample_weights(
                 y_peek,
                 sample_weights_peek,
                 sample_weight_modes,
                 check_all_flat=True)
            peek = (x_peek, y_peek, sample_weights_peek)

        # Users often return None for fields which are not used. For instance:
        # (x, y, None) to indicate no sample weights.
        if len(peek) >= 2 and y_peek is None:
            if any_sample_weight:
                raise ValueError(
                    "Found sample weights but no targets\n{}".format(peek))
            elements_to_keep = 1
        elif len(peek) == 3 and not any_sample_weight:
            elements_to_keep = 2
        else:
            elements_to_keep = len(peek)

        def dynamic_shape_like(t):
            return tuple(None for _ in t.shape)

        def convert_for_inspection(t):
            if getattr(t, "shape", None) and getattr(t, "dtype", None):
                return t
            return np.array(t, dtype=backend.floatx())

        canonicalized_peek = nest._list_to_tuple(  # pylint: disable=protected-access
            nest.map_structure(convert_for_inspection,
                               peek[:elements_to_keep]))
        nested_dtypes = nest.map_structure(lambda t: t.dtype,
                                           canonicalized_peek)
        nested_shape = nest.map_structure(dynamic_shape_like,
                                          canonicalized_peek)

        try:
            self._first_batch_size = int(
                nest.flatten(canonicalized_peek)[0].shape[0])
        except IndexError:
            raise IndexError(
                "Could not infer batch size from: {}".format(peek))

        return (peek, wrap_in_tuple, elements_to_keep, partial_sample_weight,
                sample_weight_modes, nested_shape, nested_dtypes)