예제 #1
0
def define_binary_op(op_name, elementwise_function):
    """Define a binary operation that broadcasts labeled tensors.

  Args:
    op_name: string name of the TensorFlow op.
    elementwise_function: function to call to evaluate the op on tf.Tensor
      objects. This function must accept three arguments: two tf.Tensor objects,
      and an optional `name`.

  Returns:
    Function defining the given op that acts on LabeledTensors.
  """

    default_name = 'lt_%s' % op_name

    @tc.returns(LabeledTensor)
    @tc.accepts(LabeledTensorLike, LabeledTensorLike,
                tc.Optional(string_types))
    def op(labeled_tensor_0, labeled_tensor_1, name=None):
        """LabeledTensor version of `tf.{op_name}` with label based alignment.

    See `tf.{op_name}` for full details.

    Args:
      labeled_tensor_0: Input tensor.
      labeled_tensor_1: Input tensor.
      name: Optional op name.

    Returns:
      A LabeledTensor with result of applying `tf.{op_name}` elementwise.
    """
        with ops.name_scope(name, default_name,
                            [labeled_tensor_0, labeled_tensor_1]) as scope:

            align_0, align_1, broadcast_axes = align(labeled_tensor_0,
                                                     labeled_tensor_1)

            tensor = elementwise_function(align_0.tensor,
                                          align_1.tensor,
                                          name=scope)

            return LabeledTensor(tensor, broadcast_axes)

    op.__doc__ = op.__doc__.format(op_name=op_name)
    op.__name__ = op_name

    return op
예제 #2
0
def define_unary_op(op_name, elementwise_function):
    """Define a unary operation for labeled tensors.

  Args:
    op_name: string name of the TensorFlow op.
    elementwise_function: function to call to evaluate the op on a single
      tf.Tensor object. This function must accept two arguments: a tf.Tensor
      object, and an optional `name`.

  Returns:
    Function defining the given op that acts on LabeledTensors.
  """

    default_name = 'lt_%s' % op_name

    @tc.returns(LabeledTensor)
    @tc.accepts(LabeledTensorLike, tc.Optional(string_types))
    def op(labeled_tensor, name=None):
        """LabeledTensor version of `tf.{op_name}`.

    See `tf.{op_name}` for full details.

    Args:
      labeled_tensor: Input tensor.
      name: Optional op name.

    Returns:
      A LabeledTensor with result of applying `tf.{op_name}` elementwise.
    """
        with ops.name_scope(name, default_name, [labeled_tensor]) as scope:
            labeled_tensor = convert_to_labeled_tensor(labeled_tensor)
            result_tensor = elementwise_function(labeled_tensor.tensor,
                                                 name=scope)
            return LabeledTensor(result_tensor, labeled_tensor.axes)

    op.__doc__ = op.__doc__.format(op_name=op_name)
    op.__name__ = op_name

    return op
예제 #3
0
class Axis(object):
  """Size and label information for an axis.

  Axis contains either a tf.Dimension indicating the size of an axis,
  or a tuple of tick labels for the axis.

  If tick labels are provided, they must be unique.
  """

  @tc.accepts(object, string_types, AxisValue)
  def __init__(self, name, value):
    """Construct an Axis.

    Args:
      name: Name of the axis.
      value: Either None, an int or tf.Dimension giving the size of the axis,
        or a sequence that is not a string additionally providing coordinate
        (tick) labels.

    Raises:
      ValueError: If the user provides labels with duplicate values.
    """
    if isinstance(value, tensor_shape.Dimension):
      dimension = value
      labels = None
    elif isinstance(value, int) or value is None:
      dimension = tensor_shape.Dimension(value)
      labels = None
    else:
      dimension = tensor_shape.Dimension(len(value))
      labels = tuple(value)

    if dimension.value == 0:
      # Treat a zero-length axis as if it has labels.
      labels = ()

    if labels is not None:
      index = dict(zip(labels, range(len(labels))))
      if len(index) != len(labels):
        raise ValueError('Tick labels must be unique, but got {}'
                         .format(labels))
    else:
      index = None

    self._name = name  # type: string_types
    self._dimension = dimension  # type: tensor_shape.Dimension
    self._labels = labels  # type: Optional[tuple]
    self._index = index  # type: Optional[Dict[Any, int]]

  @property
  @tc.returns(string_types)
  def name(self):
    return self._name

  @tc.returns(string_types)
  def __repr__(self):
    # Axis('x', Dimension(2))
    # TODO(shoyer): make very long reprs more succint?
    return "%s('%s', %r)" % (type(self).__name__, self.name, self.value)

  @tc.returns(bool)
  def __eq__(self, other):
    return (isinstance(other, Axis) and
            self.name == other.name and
            self.size == other.size and
            self.labels == other.labels)

  def __hash__(self):
    return hash((self.name, self.size, self.labels))

  @tc.returns(bool)
  def __ne__(self, other):
    return not self == other

  @tc.returns(int)
  def __len__(self):
    size = self.size
    if size is None:
      raise ValueError('axis %r has unknown length' % self.name)
    return size

  @property
  @tc.returns(tc.Optional(tensor_shape.Dimension))
  def dimension(self):
    return self._dimension

  @property
  @tc.returns(tc.Optional(int))
  def size(self):
    return self._dimension.value

  @property
  @tc.returns(tc.Union(tuple, tensor_shape.Dimension))
  def value(self):
    """Returns the tf.Dimension or tuple specifying axis ticks."""
    if self.labels is None:
      return self.dimension
    else:
      return self.labels

  @property
  @tc.returns(tc.Optional(tuple))
  def labels(self):
    """Returns the tuple containing coordinate labels, else None."""
    return self._labels

  def index(self, value):
    """Returns the integer position of the given tick label."""
    if self._index is None:
      raise ValueError('Axis does not have tick labels')
    return self._index[value]
예제 #4
0
from tensorflow.contrib.labeled_tensor.python.ops import _typecheck as tc
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import ops
from tensorflow.python.framework import tensor_shape
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import math_ops


# pylint: disable=invalid-name

# Types coercible to Axis.labels
# We use this instead of collections.Sequence to exclude strings.
LabelsLike = tc.Union(np.ndarray, range, list, tuple)

# Types coercible to a tf.Dimension
DimensionLike = tc.Optional(tc.Union(tensor_shape.Dimension, int))

# Types usable for axis values
AxisValue = tc.Union(LabelsLike, DimensionLike)

# Valid scalar values for TensorFlow
Scalar = tc.Union(numbers.Number, bool, binary_type, text_type)

# pylint: enable=invalid-name


class Axis(object):
  """Size and label information for an axis.

  Axis contains either a tf.Dimension indicating the size of an axis,
  or a tuple of tick labels for the axis.
예제 #5
0
def define_reduce_op(op_name, reduce_fn):
    """Define a reduction op for labeled tensors.

  Args:
    op_name: string name of the TensorFlow op.
    reduce_fn: function to call to evaluate the op on a tf.Tensor.

  Returns:
    Function defining the given reduction op that acts on a LabeledTensor.
  """

    default_name = 'lt_%s' % op_name

    @tc.returns(core.LabeledTensor)
    @tc.accepts(core.LabeledTensorLike, ReduceAxes, tc.Optional(string_types))
    def op(labeled_tensor, axes=None, name=None):
        """Computes the given reduction across the given axes of a LabeledTensor.

    See `tf.{op_name}` for full details.

    Args:
      labeled_tensor: The input tensor.
      axes: A set of axes or None.
        If None, all axes will be reduced.
        Axes must all be strings, in which case those dimensions will be
        removed, or pairs of (name, None) or (name, label), in which case those
        dimensions will be kept.
      name: Optional op name.

    Returns:
      The reduced LabeledTensor.

    Raises:
      ValueError: if any of the axes to reduce over are not found on
        `labeled_tensor`.
    """
        with ops.name_scope(name, default_name, [labeled_tensor]) as scope:
            labeled_tensor = core.convert_to_labeled_tensor(labeled_tensor)

            if axes is None:
                axes = labeled_tensor.axes.keys()

            if isinstance(axes, (string_types, tuple)):
                axes = [axes]

            reduction_axes = {}
            axes_to_squeeze = []
            for a in axes:
                if isinstance(a, string_types):
                    # We squeeze out this axis.
                    reduction_axes[a] = a
                    axes_to_squeeze.append(a)
                else:
                    # We keep this axis, with the user-provided labels.
                    (axis_name, label) = a
                    if label is not None:
                        # The input was a single label, so make it a list so it can be
                        # turned into an Axis.
                        label = [label]
                    reduction_axes[axis_name] = (axis_name, label)

            for axis_name in reduction_axes:
                if axis_name not in labeled_tensor.axes:
                    raise ValueError('Axis %s not in axes %s' %
                                     (axis_name, labeled_tensor.axes))

            intermediate_axes = []
            reduction_dimensions = []
            for i, axis in enumerate(labeled_tensor.axes.values()):
                if axis.name in reduction_axes:
                    intermediate_axes.append(reduction_axes[axis.name])
                    reduction_dimensions.append(i)
                else:
                    intermediate_axes.append(axis)

            reduce_op = reduce_fn(labeled_tensor.tensor,
                                  reduction_dimensions,
                                  keepdims=True)
            reduce_lt = core.LabeledTensor(reduce_op, intermediate_axes)

            return squeeze(reduce_lt, axes_to_squeeze, name=scope)

    op.__doc__ = op.__doc__.format(op_name=op_name)
    op.__name__ = op_name

    return op
예제 #6
0
from tensorflow.contrib.labeled_tensor.python.ops import _typecheck as tc
from tensorflow.contrib.labeled_tensor.python.ops import core
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import ops
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import functional_ops
from tensorflow.python.ops import map_fn as map_fn_lib
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import numerics
from tensorflow.python.ops import random_ops
from tensorflow.python.training import input  # pylint: disable=redefined-builtin


@tc.returns(core.LabeledTensor)
@tc.accepts(core.LabeledTensor, ops.Tensor, core.Axis,
            tc.Optional(string_types))
def _gather_1d_on_axis(labeled_tensor, indexer, axis, name=None):
    with ops.name_scope(name, 'lt_take', [labeled_tensor]) as scope:
        temp_axes = core.Axes(
            [axis] + list(labeled_tensor.axes.remove(axis.name).values()))
        transposed = core.transpose(labeled_tensor, temp_axes.keys())
        indexed = core.LabeledTensor(
            array_ops.gather(transposed.tensor, indexer), temp_axes)
        return core.transpose(indexed, labeled_tensor.axes.keys(), name=scope)


@tc.returns(core.LabeledTensor)
@tc.accepts(core.LabeledTensorLike,
            tc.Mapping(string_types, tc.Union(slice,
                                              collections.Hashable, list)),
            tc.Optional(string_types))
예제 #7
0
    for name, labeled_feature in features.items():
        shape = [ax.size for ax in labeled_feature.axes]
        if any(size is None for size in shape):
            # This should be caught on the TensorFlow side, but it isn't yet:
            # https://github.com/tensorflow/tensorflow/issues/2874
            raise ValueError('axes with unknown size are not supported')
        dtype = labeled_feature.dtype
        default_value = labeled_feature.default_value
        unlabeled_features[name] = parsing_ops.FixedLenFeature(
            shape, dtype, default_value)
    return unlabeled_features


@tc.returns(tc.Dict(string_types, core.LabeledTensor))
@tc.accepts(core.LabeledTensorLike, tc.Mapping(string_types, FixedLenFeature),
            tc.Optional(string_types), object)
def parse_example(serialized, features, name=None, example_names=None):
    """Parse `Example` protos into a `dict` of labeled tensors.

  See tf.parse_example.

  Args:
    serialized: A 1-D LabeledTensor of strings, a batch of binary serialized
      `Example` protos.
    features: A `dict` mapping feature keys to `labeled_tensor.FixedLenFeature`
      values.
    name: A name for this operation (optional).
    example_names: A vector (1-D Tensor) of strings (optional), the names of
      the serialized protos in the batch.

  Returns:
예제 #8
0
class ReshapeCoder(object):
    """Utility class for mapping to and from another shape.

  For example, say you have a function `crop_center` which expects a
  LabeledTensor with axes named ['batch', 'row', 'column', 'depth'], and
  you have a LabeledTensor `masked_image_lt` with axes ['batch', 'row',
  'column', 'channel', 'mask'].

  To call `crop_center` with `masked_image_lt` you'd normally have to write:

  >>> reshape_lt = lt.reshape(masked_image_lt, ['channel', 'mask'], ['depth'])
  >>> crop_lt = crop_center(reshape_lt)
  >>> result_lt = lt.reshape(crop_lt, ['depth'],
  ...   [masked_image_lt.axes['channel'], masked_image_lt.axes['mask']])

  ReshapeCoder takes care of this renaming logic for you, allowing you to
  instead write:

  >>> rc = ReshapeCoder(['channel', 'mask'], ['depth'])
  >>> result_lt = rc.decode(crop_center(rc.encode(masked_image_lt)))

  Here, `decode` restores the original axes 'channel' and 'mask', so
  `crop_center` must not have modified the size of the 'depth' axis.
  """
    @tc.accepts(object, tc.Collection(str),
                tc.Collection(tc.Union(str, core.AxisLike)), tc.Optional(str))
    def __init__(self, existing_axis_names, new_axes, name=None):
        self._name = name
        self._existing_axis_names = existing_axis_names
        self._new_axes = new_axes

        self._existing_axes = None

    @tc.returns(core.LabeledTensor)
    @tc.accepts(object, core.LabeledTensorLike)
    def encode(self, labeled_tensor):
        """Reshape the input to the target shape.

    If called several times, the axes named in existing_axis_names must be
    identical.

    Args:
      labeled_tensor: The input tensor.

    Returns:
      The input reshaped to the target shape.

    Raises:
      ValueError: If the axes in existing_axis_names don't match the axes of
        a tensor in a previous invocation of this method.
    """
        with tf_ops.name_scope(self._name, 'lt_reshape_encode',
                               [labeled_tensor]) as scope:
            labeled_tensor = core.convert_to_labeled_tensor(labeled_tensor)

            reshape_lt = ops.reshape(labeled_tensor,
                                     self._existing_axis_names,
                                     self._new_axes,
                                     name=scope)

            axes = [labeled_tensor.axes[n] for n in self._existing_axis_names]
            if self._existing_axes is not None and self._existing_axes != axes:
                raise ValueError(
                    'input axes %r do not match axes from previous method call %r'
                    % (axes, self._existing_axes))
            else:
                self._existing_axes = axes

            return reshape_lt

    @tc.returns(core.LabeledTensor)
    @tc.accepts(object, core.LabeledTensorLike)
    def decode(self, labeled_tensor):
        """Reshape the input to the original shape.

    This is the inverse of encode.
    Encode must have been called at least once prior to this method being
    called.

    Args:
      labeled_tensor: The input tensor.

    Returns:
      The input reshaped to the original shape.

    Raises:
      ValueError: If this method was called before encode was called.
    """
        if self._existing_axes is None:
            raise ValueError('decode called before encode')

        with tf_ops.name_scope(self._name, 'lt_reshape_decode',
                               [labeled_tensor]) as scope:
            labeled_tensor = core.convert_to_labeled_tensor(labeled_tensor)

            new_axis_names = [
                axis
                if isinstance(axis, string_types) else core.as_axis(axis).name
                for axis in self._new_axes
            ]

            return ops.reshape(labeled_tensor,
                               new_axis_names,
                               self._existing_axes,
                               name=scope)