Module keras.api.keras.layers.experimental.preprocessing

Public API for tf.keras.layers.experimental.preprocessing namespace.

Expand source code
# This file is MACHINE GENERATED! Do not edit.
# Generated by: tensorflow/python/tools/api/generator/create_python_api.py script.
"""Public API for tf.keras.layers.experimental.preprocessing namespace.
"""

from __future__ import print_function as _print_function

import sys as _sys

from keras.engine.base_preprocessing_layer import PreprocessingLayer
from keras.layers.preprocessing.category_crossing import CategoryCrossing
from keras.layers.preprocessing.category_encoding import CategoryEncoding
from keras.layers.preprocessing.discretization import Discretization
from keras.layers.preprocessing.hashing import Hashing
from keras.layers.preprocessing.image_preprocessing import CenterCrop
from keras.layers.preprocessing.image_preprocessing import RandomContrast
from keras.layers.preprocessing.image_preprocessing import RandomCrop
from keras.layers.preprocessing.image_preprocessing import RandomFlip
from keras.layers.preprocessing.image_preprocessing import RandomHeight
from keras.layers.preprocessing.image_preprocessing import RandomRotation
from keras.layers.preprocessing.image_preprocessing import RandomTranslation
from keras.layers.preprocessing.image_preprocessing import RandomWidth
from keras.layers.preprocessing.image_preprocessing import RandomZoom
from keras.layers.preprocessing.image_preprocessing import Rescaling
from keras.layers.preprocessing.image_preprocessing import Resizing
from keras.layers.preprocessing.normalization import Normalization

del _print_function

from tensorflow.python.util import module_wrapper as _module_wrapper

if not isinstance(_sys.modules[__name__], _module_wrapper.TFModuleWrapper):
  _sys.modules[__name__] = _module_wrapper.TFModuleWrapper(
      _sys.modules[__name__], "keras.layers.experimental.preprocessing", public_apis=None, deprecation=True,
      has_lite=False)

Classes

class CategoryCrossing (depth=None, name=None, separator='_X_', **kwargs)

Category crossing layer.

This layer concatenates multiple categorical inputs into a single categorical output (similar to Cartesian product). The output dtype is string.

Usage:

>>> inp_1 = ['a', 'b', 'c']
>>> inp_2 = ['d', 'e', 'f']
>>> layer = tf.keras.layers.experimental.preprocessing.CategoryCrossing()
>>> layer([inp_1, inp_2])
<tf.Tensor: shape=(3, 1), dtype=string, numpy=
  array([[b'a_X_d'],
         [b'b_X_e'],
         [b'c_X_f']], dtype=object)>
>>> inp_1 = ['a', 'b', 'c']
>>> inp_2 = ['d', 'e', 'f']
>>> layer = tf.keras.layers.experimental.preprocessing.CategoryCrossing(
...    separator='-')
>>> layer([inp_1, inp_2])
<tf.Tensor: shape=(3, 1), dtype=string, numpy=
  array([[b'a-d'],
         [b'b-e'],
         [b'c-f']], dtype=object)>

Args

depth
depth of input crossing. By default None, all inputs are crossed into one output. It can also be an int or tuple/list of ints. Passing an integer will create combinations of crossed outputs with depth up to that integer, i.e., [1, 2, …, depth), and passing a tuple of integers will create crossed outputs with depth for the specified values in the tuple, i.e., depth=(N1, N2) will create all possible crossed outputs with depth equal to N1 or N2. Passing None means a single crossed output with all inputs. For example, with inputs a, b and c, depth=2 means the output will be [a;b;c;cross(a, b);cross(bc);cross(ca)].
separator
A string added between each input being joined. Defaults to 'X'.
name
Name to give to the layer.
**kwargs
Keyword arguments to construct a layer.

Input shape: a list of string or int tensors or sparse tensors of shape [batch_size, d1, …, dm]

Output shape: a single string or int tensor or sparse tensor of shape [batch_size, d1, …, dm]

Returns

If any input is RaggedTensor, the output is RaggedTensor. Else, if any input is SparseTensor, the output is SparseTensor. Otherwise, the output is Tensor. Example: (depth=None) If the layer receives three inputs: a=[[1], [4]], b=[[2], [5]], c=[[3], [6]] the output will be a string tensor: [[b'1_X_2_X_3'], [b'4_X_5_X_6']]

Example: (depth is an integer) With the same input above, and if depth=2, the output will be a list of 6 string tensors: [[b'1'], [b'4']] [[b'2'], [b'5']] [[b'3'], [b'6']] [[b'1_X_2'], [b'4_X_5']], [[b'2_X_3'], [b'5_X_6']], [[b'3_X_1'], [b'6_X_4']]

Example: (depth is a tuple/list of integers) With the same input above, and if depth=(2, 3) the output will be a list of 4 string tensors: [[b'1_X_2'], [b'4_X_5']], [[b'2_X_3'], [b'5_X_6']], [[b'3_X_1'], [b'6_X_4']], [[b'1_X_2_X_3'], [b'4_X_5_X_6']]

Expand source code
class CategoryCrossing(base_layer.Layer):
  """Category crossing layer.

  This layer concatenates multiple categorical inputs into a single categorical
  output (similar to Cartesian product). The output dtype is string.

  Usage:
  >>> inp_1 = ['a', 'b', 'c']
  >>> inp_2 = ['d', 'e', 'f']
  >>> layer = tf.keras.layers.experimental.preprocessing.CategoryCrossing()
  >>> layer([inp_1, inp_2])
  <tf.Tensor: shape=(3, 1), dtype=string, numpy=
    array([[b'a_X_d'],
           [b'b_X_e'],
           [b'c_X_f']], dtype=object)>


  >>> inp_1 = ['a', 'b', 'c']
  >>> inp_2 = ['d', 'e', 'f']
  >>> layer = tf.keras.layers.experimental.preprocessing.CategoryCrossing(
  ...    separator='-')
  >>> layer([inp_1, inp_2])
  <tf.Tensor: shape=(3, 1), dtype=string, numpy=
    array([[b'a-d'],
           [b'b-e'],
           [b'c-f']], dtype=object)>

  Args:
    depth: depth of input crossing. By default None, all inputs are crossed into
      one output. It can also be an int or tuple/list of ints. Passing an
      integer will create combinations of crossed outputs with depth up to that
      integer, i.e., [1, 2, ..., `depth`), and passing a tuple of integers will
      create crossed outputs with depth for the specified values in the tuple,
      i.e., `depth`=(N1, N2) will create all possible crossed outputs with depth
      equal to N1 or N2. Passing `None` means a single crossed output with all
      inputs. For example, with inputs `a`, `b` and `c`, `depth=2` means the
      output will be [a;b;c;cross(a, b);cross(bc);cross(ca)].
    separator: A string added between each input being joined. Defaults to
      '_X_'.
    name: Name to give to the layer.
    **kwargs: Keyword arguments to construct a layer.

  Input shape: a list of string or int tensors or sparse tensors of shape
    `[batch_size, d1, ..., dm]`

  Output shape: a single string or int tensor or sparse tensor of shape
    `[batch_size, d1, ..., dm]`

  Returns:
    If any input is `RaggedTensor`, the output is `RaggedTensor`.
    Else, if any input is `SparseTensor`, the output is `SparseTensor`.
    Otherwise, the output is `Tensor`.

  Example: (`depth`=None)
    If the layer receives three inputs:
    `a=[[1], [4]]`, `b=[[2], [5]]`, `c=[[3], [6]]`
    the output will be a string tensor:
    `[[b'1_X_2_X_3'], [b'4_X_5_X_6']]`

  Example: (`depth` is an integer)
    With the same input above, and if `depth`=2,
    the output will be a list of 6 string tensors:
    `[[b'1'], [b'4']]`
    `[[b'2'], [b'5']]`
    `[[b'3'], [b'6']]`
    `[[b'1_X_2'], [b'4_X_5']]`,
    `[[b'2_X_3'], [b'5_X_6']]`,
    `[[b'3_X_1'], [b'6_X_4']]`

  Example: (`depth` is a tuple/list of integers)
    With the same input above, and if `depth`=(2, 3)
    the output will be a list of 4 string tensors:
    `[[b'1_X_2'], [b'4_X_5']]`,
    `[[b'2_X_3'], [b'5_X_6']]`,
    `[[b'3_X_1'], [b'6_X_4']]`,
    `[[b'1_X_2_X_3'], [b'4_X_5_X_6']]`
  """

  def __init__(self, depth=None, name=None, separator='_X_', **kwargs):
    super(CategoryCrossing, self).__init__(name=name, **kwargs)
    base_preprocessing_layer.keras_kpl_gauge.get_cell(
        'CategoryCrossing').set(True)
    self.depth = depth
    self.separator = separator
    if isinstance(depth, (tuple, list)):
      self._depth_tuple = depth
    elif depth is not None:
      self._depth_tuple = tuple([i for i in range(1, depth + 1)])

  def partial_crossing(self, partial_inputs, ragged_out, sparse_out):
    """Gets the crossed output from a partial list/tuple of inputs."""
    # If ragged_out=True, convert output from sparse to ragged.
    if ragged_out:
      # TODO(momernick): Support separator with ragged_cross.
      if self.separator != '_X_':
        raise ValueError('Non-default separator with ragged input is not '
                         'supported yet, given {}'.format(self.separator))
      return tf.ragged.cross(partial_inputs)
    elif sparse_out:
      return tf.sparse.cross(partial_inputs, separator=self.separator)
    else:
      return tf.sparse.to_dense(
          tf.sparse.cross(partial_inputs, separator=self.separator))

  def _preprocess_input(self, inp):
    if isinstance(inp, (list, tuple, np.ndarray)):
      inp = tf.convert_to_tensor(inp)
    if inp.shape.rank == 1:
      inp = tf.expand_dims(inp, axis=-1)
    return inp

  def call(self, inputs):
    inputs = [self._preprocess_input(inp) for inp in inputs]
    depth_tuple = self._depth_tuple if self.depth else (len(inputs),)
    ragged_out = sparse_out = False
    if any(tf_utils.is_ragged(inp) for inp in inputs):
      ragged_out = True
    elif any(isinstance(inp, tf.SparseTensor) for inp in inputs):
      sparse_out = True

    outputs = []
    for depth in depth_tuple:
      if len(inputs) < depth:
        raise ValueError(
            'Number of inputs cannot be less than depth, got {} input tensors, '
            'and depth {}'.format(len(inputs), depth))
      for partial_inps in itertools.combinations(inputs, depth):
        partial_out = self.partial_crossing(
            partial_inps, ragged_out, sparse_out)
        outputs.append(partial_out)
    if sparse_out:
      return tf.sparse.concat(axis=1, sp_inputs=outputs)
    return tf.concat(outputs, axis=1)

  def compute_output_shape(self, input_shape):
    if not isinstance(input_shape, (tuple, list)):
      raise ValueError('A `CategoryCrossing` layer should be called '
                       'on a list of inputs.')
    input_shapes = input_shape
    batch_size = None
    for inp_shape in input_shapes:
      inp_tensor_shape = tf.TensorShape(inp_shape).as_list()
      if len(inp_tensor_shape) != 2:
        raise ValueError('Inputs must be rank 2, get {}'.format(input_shapes))
      if batch_size is None:
        batch_size = inp_tensor_shape[0]
    # The second dimension is dynamic based on inputs.
    output_shape = [batch_size, None]
    return tf.TensorShape(output_shape)

  def compute_output_signature(self, input_spec):
    input_shapes = [x.shape for x in input_spec]
    output_shape = self.compute_output_shape(input_shapes)
    if any(
        isinstance(inp_spec, tf.RaggedTensorSpec)
        for inp_spec in input_spec):
      return tf.TensorSpec(shape=output_shape, dtype=tf.string)
    elif any(
        isinstance(inp_spec, tf.SparseTensorSpec)
        for inp_spec in input_spec):
      return tf.SparseTensorSpec(
          shape=output_shape, dtype=tf.string)
    return tf.TensorSpec(shape=output_shape, dtype=tf.string)

  def get_config(self):
    config = {
        'depth': self.depth,
        'separator': self.separator,
    }
    base_config = super(CategoryCrossing, self).get_config()
    return dict(list(base_config.items()) + list(config.items()))

Ancestors

  • Layer
  • tensorflow.python.module.module.Module
  • tensorflow.python.training.tracking.tracking.AutoTrackable
  • tensorflow.python.training.tracking.base.Trackable
  • LayerVersionSelector

Methods

def partial_crossing(self, partial_inputs, ragged_out, sparse_out)

Gets the crossed output from a partial list/tuple of inputs.

Expand source code
def partial_crossing(self, partial_inputs, ragged_out, sparse_out):
  """Gets the crossed output from a partial list/tuple of inputs."""
  # If ragged_out=True, convert output from sparse to ragged.
  if ragged_out:
    # TODO(momernick): Support separator with ragged_cross.
    if self.separator != '_X_':
      raise ValueError('Non-default separator with ragged input is not '
                       'supported yet, given {}'.format(self.separator))
    return tf.ragged.cross(partial_inputs)
  elif sparse_out:
    return tf.sparse.cross(partial_inputs, separator=self.separator)
  else:
    return tf.sparse.to_dense(
        tf.sparse.cross(partial_inputs, separator=self.separator))

Inherited members

class CategoryEncoding (num_tokens=None, output_mode='multi_hot', sparse=False, **kwargs)

Category encoding layer.

This layer provides options for condensing data into a categorical encoding when the total number of tokens are known in advance. It accepts integer values as inputs, and it outputs a dense representation of those inputs. For integer inputs where the total number of tokens is not known, use instead tf.keras.layers.IntegerLookup.

Examples:

One-hot encoding data

>>> layer = tf.keras.layers.CategoryEncoding(
...           num_tokens=4, output_mode="one_hot")
>>> layer([3, 2, 0, 1])
<tf.Tensor: shape=(4, 4), dtype=float32, numpy=
  array([[0., 0., 0., 1.],
         [0., 0., 1., 0.],
         [1., 0., 0., 0.],
         [0., 1., 0., 0.]], dtype=float32)>

Multi-hot encoding data

>>> layer = tf.keras.layers.CategoryEncoding(
...           num_tokens=4, output_mode="multi_hot")
>>> layer([[0, 1], [0, 0], [1, 2], [3, 1]])
<tf.Tensor: shape=(4, 4), dtype=float32, numpy=
  array([[1., 1., 0., 0.],
         [1., 0., 0., 0.],
         [0., 1., 1., 0.],
         [0., 1., 0., 1.]], dtype=float32)>

Using weighted inputs in "count" mode

>>> layer = tf.keras.layers.CategoryEncoding(
...           num_tokens=4, output_mode="count")
>>> count_weights = np.array([[.1, .2], [.1, .1], [.2, .3], [.4, .2]])
>>> layer([[0, 1], [0, 0], [1, 2], [3, 1]], count_weights=count_weights)
<tf.Tensor: shape=(4, 4), dtype=float64, numpy=
  array([[0.1, 0.2, 0. , 0. ],
         [0.2, 0. , 0. , 0. ],
         [0. , 0.2, 0.3, 0. ],
         [0. , 0.2, 0. , 0.4]])>

Args

num_tokens
The total number of tokens the layer should support. All inputs to the layer must integers in the range 0 <= value < num_tokens, or an error will be thrown.
output_mode
Specification for the output of the layer. Defaults to "multi_hot". Values can be "one_hot", "multi_hot" or "count", configuring the layer as follows: - "one_hot": Encodes each individual element in the input into an array of num_tokens size, containing a 1 at the element index. If the last dimension is size 1, will encode on that dimension. If the last dimension is not size 1, will append a new dimension for the encoded output. - "multi_hot": Encodes each sample in the input into a single array of num_tokens size, containing a 1 for each vocabulary term present in the sample. Treats the last dimension as the sample dimension, if input shape is (…, sample_length), output shape will be (…, num_tokens). - "count": Like "multi_hot", but the int array contains a count of the number of times the token at that index appeared in the sample. For all output modes, currently only output up to rank 2 is supported.
sparse
Boolean. If true, returns a SparseTensor instead of a dense Tensor. Defaults to False.

Call arguments: inputs: A 1D or 2D tensor of integer inputs. count_weights: A tensor in the same shape as inputs indicating the weight for each sample value when summing up in count mode. Not used in "multi_hot" or "one_hot" modes.

Expand source code
class CategoryEncoding(base_layer.Layer):
  """Category encoding layer.

  This layer provides options for condensing data into a categorical encoding
  when the total number of tokens are known in advance. It accepts integer
  values as inputs, and it outputs a dense representation of those
  inputs. For integer inputs where the total number of tokens is not known,
  use instead `tf.keras.layers.IntegerLookup`.

  Examples:

  **One-hot encoding data**

  >>> layer = tf.keras.layers.CategoryEncoding(
  ...           num_tokens=4, output_mode="one_hot")
  >>> layer([3, 2, 0, 1])
  <tf.Tensor: shape=(4, 4), dtype=float32, numpy=
    array([[0., 0., 0., 1.],
           [0., 0., 1., 0.],
           [1., 0., 0., 0.],
           [0., 1., 0., 0.]], dtype=float32)>

  **Multi-hot encoding data**

  >>> layer = tf.keras.layers.CategoryEncoding(
  ...           num_tokens=4, output_mode="multi_hot")
  >>> layer([[0, 1], [0, 0], [1, 2], [3, 1]])
  <tf.Tensor: shape=(4, 4), dtype=float32, numpy=
    array([[1., 1., 0., 0.],
           [1., 0., 0., 0.],
           [0., 1., 1., 0.],
           [0., 1., 0., 1.]], dtype=float32)>

  **Using weighted inputs in `"count"` mode**

  >>> layer = tf.keras.layers.CategoryEncoding(
  ...           num_tokens=4, output_mode="count")
  >>> count_weights = np.array([[.1, .2], [.1, .1], [.2, .3], [.4, .2]])
  >>> layer([[0, 1], [0, 0], [1, 2], [3, 1]], count_weights=count_weights)
  <tf.Tensor: shape=(4, 4), dtype=float64, numpy=
    array([[0.1, 0.2, 0. , 0. ],
           [0.2, 0. , 0. , 0. ],
           [0. , 0.2, 0.3, 0. ],
           [0. , 0.2, 0. , 0.4]])>

  Args:
    num_tokens: The total number of tokens the layer should support. All inputs
      to the layer must integers in the range `0 <= value < num_tokens`, or an
      error will be thrown.
    output_mode: Specification for the output of the layer.
      Defaults to `"multi_hot"`. Values can be `"one_hot"`, `"multi_hot"` or
      `"count"`, configuring the layer as follows:
        - `"one_hot"`: Encodes each individual element in the input into an
          array of `num_tokens` size, containing a 1 at the element index. If
          the last dimension is size 1, will encode on that dimension. If the
          last dimension is not size 1, will append a new dimension for the
          encoded output.
        - `"multi_hot"`: Encodes each sample in the input into a single array
          of `num_tokens` size, containing a 1 for each vocabulary term present
          in the sample. Treats the last dimension as the sample dimension, if
          input shape is `(..., sample_length)`, output shape will be
          `(..., num_tokens)`.
        - `"count"`: Like `"multi_hot"`, but the int array contains a count of
          the number of times the token at that index appeared in the sample.
      For all output modes, currently only output up to rank 2 is supported.
    sparse: Boolean. If true, returns a `SparseTensor` instead of a dense
      `Tensor`. Defaults to `False`.

  Call arguments:
    inputs: A 1D or 2D tensor of integer inputs.
    count_weights: A tensor in the same shape as `inputs` indicating the
      weight for each sample value when summing up in `count` mode. Not used in
      `"multi_hot"` or `"one_hot"` modes.
  """

  def __init__(self,
               num_tokens=None,
               output_mode="multi_hot",
               sparse=False,
               **kwargs):
    # max_tokens is an old name for the num_tokens arg we continue to support
    # because of usage.
    if "max_tokens" in kwargs:
      logging.warning(
          "max_tokens is deprecated, please use num_tokens instead.")
      num_tokens = kwargs["max_tokens"]
      del kwargs["max_tokens"]

    super(CategoryEncoding, self).__init__(**kwargs)
    base_preprocessing_layer.keras_kpl_gauge.get_cell("CategoryEncoding").set(
        True)

    # Support deprecated names for output_modes.
    if output_mode == "binary":
      output_mode = MULTI_HOT
    # 'output_mode' must be one of (COUNT, ONE_HOT, MULTI_HOT)
    layer_utils.validate_string_arg(
        output_mode,
        allowable_strings=(COUNT, ONE_HOT, MULTI_HOT),
        layer_name="CategoryEncoding",
        arg_name="output_mode")

    if num_tokens is None:
      raise ValueError("num_tokens must be set to use this layer. If the "
                       "number of tokens is not known beforehand, use the "
                       "IntegerLookup layer instead.")
    if num_tokens < 1:
      raise ValueError("num_tokens must be >= 1.")

    self.num_tokens = num_tokens
    self.output_mode = output_mode
    self.sparse = sparse

  def compute_output_shape(self, input_shape):
    if not input_shape:
      return tf.TensorShape([self.num_tokens])
    if self.output_mode == ONE_HOT and input_shape[-1] != 1:
      return tf.TensorShape(input_shape + [self.num_tokens])
    else:
      return tf.TensorShape(input_shape[:-1] + [self.num_tokens])

  def compute_output_signature(self, input_spec):
    output_shape = self.compute_output_shape(input_spec.shape.as_list())
    if self.sparse:
      return tf.SparseTensorSpec(
          shape=output_shape, dtype=tf.int64)
    else:
      return tf.TensorSpec(shape=output_shape, dtype=tf.int64)

  def get_config(self):
    config = {
        "num_tokens": self.num_tokens,
        "output_mode": self.output_mode,
        "sparse": self.sparse,
    }
    base_config = super(CategoryEncoding, self).get_config()
    return dict(list(base_config.items()) + list(config.items()))

  def call(self, inputs, count_weights=None):
    if isinstance(inputs, (list, np.ndarray)):
      inputs = tf.convert_to_tensor(inputs)

    def expand_dims(inputs, axis):
      if tf_utils.is_sparse(inputs):
        return tf.sparse.expand_dims(inputs, axis)
      else:
        return tf.expand_dims(inputs, axis)

    original_shape = inputs.shape
    # In all cases, we should uprank scalar input to a single sample.
    if inputs.shape.rank == 0:
      inputs = expand_dims(inputs, -1)
    # One hot will unprank only if the final output dimension is not already 1.
    if self.output_mode == ONE_HOT:
      if inputs.shape[-1] != 1:
        inputs = expand_dims(inputs, -1)

    # TODO(b/190445202): remove output rank restriction.
    if inputs.shape.rank > 2:
      raise ValueError(
          "Received input shape {}, which would result in output rank {}. "
          "Currently only outputs up to rank 2 are supported.".format(
              original_shape, inputs.shape.rank))

    if count_weights is not None and self.output_mode != COUNT:
      raise ValueError(
          "`count_weights` is not used when `output_mode` is not `'count'`. "
          "Received `count_weights={}`.".format(count_weights))

    out_depth = self.num_tokens
    binary_output = self.output_mode in (MULTI_HOT, ONE_HOT)
    if isinstance(inputs, tf.SparseTensor):
      max_value = tf.reduce_max(inputs.values)
      min_value = tf.reduce_min(inputs.values)
    else:
      max_value = tf.reduce_max(inputs)
      min_value = tf.reduce_min(inputs)
    condition = tf.logical_and(
        tf.greater(
            tf.cast(out_depth, max_value.dtype), max_value),
        tf.greater_equal(
            min_value, tf.cast(0, min_value.dtype)))
    assertion = tf.Assert(condition, [
        "Input values must be in the range 0 <= values < num_tokens"
        " with num_tokens={}".format(out_depth)
    ])
    with tf.control_dependencies([assertion]):
      if self.sparse:
        return sparse_bincount(inputs, out_depth, binary_output,
                               count_weights)
      else:
        return dense_bincount(inputs, out_depth, binary_output,
                              count_weights)

Ancestors

  • Layer
  • tensorflow.python.module.module.Module
  • tensorflow.python.training.tracking.tracking.AutoTrackable
  • tensorflow.python.training.tracking.base.Trackable
  • LayerVersionSelector

Inherited members

class CenterCrop (height, width, **kwargs)

Crop the central portion of the images to target height and width.

Input shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, height, width, channels), in "channels_last" format.

Output shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, target_height, target_width, channels).

If the input height/width is even and the target height/width is odd (or inversely), the input image is left-padded by 1 pixel.

Args

height
Integer, the height of the output shape.
width
Integer, the width of the output shape.
Expand source code
class CenterCrop(base_layer.Layer):
  """Crop the central portion of the images to target height and width.

  Input shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., height, width, channels)`, in `"channels_last"` format.

  Output shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., target_height, target_width, channels)`.

  If the input height/width is even and the target height/width is odd (or
  inversely), the input image is left-padded by 1 pixel.

  Args:
    height: Integer, the height of the output shape.
    width: Integer, the width of the output shape.
  """

  def __init__(self, height, width, **kwargs):
    self.target_height = height
    self.target_width = width
    super(CenterCrop, self).__init__(**kwargs)
    base_preprocessing_layer.keras_kpl_gauge.get_cell('CenterCrop').set(True)

  def call(self, inputs):
    inputs = tf.convert_to_tensor(inputs)
    inputs_shape = tf.shape(inputs)
    unbatched = inputs.shape.rank == 3
    img_hd = inputs_shape[H_AXIS]
    img_wd = inputs_shape[W_AXIS]
    img_hd_diff = img_hd - self.target_height
    img_wd_diff = img_wd - self.target_width
    checks = []
    checks.append(
        tf.debugging.assert_non_negative(
            img_hd_diff,
            message='The crop height {} should not be greater than input '
            'height.'.format(self.target_height)))
    checks.append(
        tf.debugging.assert_non_negative(
            img_wd_diff,
            message='The crop width {} should not be greater than input '
            'width.'.format(self.target_width)))
    with tf.control_dependencies(checks):
      bbox_h_start = tf.cast(img_hd_diff / 2, tf.int32)
      bbox_w_start = tf.cast(img_wd_diff / 2, tf.int32)
      if unbatched:
        bbox_begin = tf.stack([bbox_h_start, bbox_w_start, 0])
        bbox_size = tf.stack([self.target_height, self.target_width, -1])
      else:
        bbox_begin = tf.stack([0, bbox_h_start, bbox_w_start, 0])
        bbox_size = tf.stack([-1, self.target_height, self.target_width, -1])
      outputs = tf.slice(inputs, bbox_begin, bbox_size)
      return outputs

  def compute_output_shape(self, input_shape):
    input_shape = tf.TensorShape(input_shape).as_list()
    input_shape[H_AXIS] = self.target_height
    input_shape[W_AXIS] = self.target_width
    return tf.TensorShape(input_shape)

  def get_config(self):
    config = {
        'height': self.target_height,
        'width': self.target_width,
    }
    base_config = super(CenterCrop, self).get_config()
    return dict(list(base_config.items()) + list(config.items()))

Ancestors

  • Layer
  • tensorflow.python.module.module.Module
  • tensorflow.python.training.tracking.tracking.AutoTrackable
  • tensorflow.python.training.tracking.base.Trackable
  • LayerVersionSelector

Inherited members

class Discretization (bin_boundaries=None, num_bins=None, epsilon=0.01, **kwargs)

Buckets data into discrete ranges.

This layer will place each element of its input data into one of several contiguous ranges and output an integer index indicating which range each element was placed in.

Input shape: Any tf.Tensor or tf.RaggedTensor of dimension 2 or higher.

Output shape: Same as input shape.

Attributes

bin_boundaries
A list of bin boundaries. The leftmost and rightmost bins will always extend to -inf and inf, so bin_boundaries=[0., 1., 2.] generates bins (-inf, 0.), [0., 1.), [1., 2.), and [2., +inf). If this option is set, adapt should not be called.
num_bins
The integer number of bins to compute. If this option is set, adapt should be called to learn the bin boundaries.
epsilon
Error tolerance, typically a small fraction close to zero (e.g. 0.01). Higher values of epsilon increase the quantile approximation, and hence result in more unequal buckets, but could improve performance and resource consumption.

Examples:

Bucketize float values based on provided buckets.

>>> input = np.array([[-1.5, 1.0, 3.4, .5], [0.0, 3.0, 1.3, 0.0]])
>>> layer = tf.keras.layers.Discretization(bin_boundaries=[0., 1., 2.])
>>> layer(input)
<tf.Tensor: shape=(2, 4), dtype=int64, numpy=
array([[0, 2, 3, 1],
       [1, 3, 2, 1]], dtype=int64)>

Bucketize float values based on a number of buckets to compute.

>>> input = np.array([[-1.5, 1.0, 3.4, .5], [0.0, 3.0, 1.3, 0.0]])
>>> layer = tf.keras.layers.Discretization(num_bins=4, epsilon=0.01)
>>> layer.adapt(input)
>>> layer(input)
<tf.Tensor: shape=(2, 4), dtype=int64, numpy=
array([[0, 2, 3, 2],
       [1, 3, 3, 1]], dtype=int64)>
Expand source code
class Discretization(base_preprocessing_layer.PreprocessingLayer):
  """Buckets data into discrete ranges.

  This layer will place each element of its input data into one of several
  contiguous ranges and output an integer index indicating which range each
  element was placed in.

  Input shape:
    Any `tf.Tensor` or `tf.RaggedTensor` of dimension 2 or higher.

  Output shape:
    Same as input shape.

  Attributes:
    bin_boundaries: A list of bin boundaries. The leftmost and rightmost bins
      will always extend to `-inf` and `inf`, so `bin_boundaries=[0., 1., 2.]`
      generates bins `(-inf, 0.)`, `[0., 1.)`, `[1., 2.)`, and `[2., +inf)`. If
      this option is set, `adapt` should not be called.
    num_bins: The integer number of bins to compute. If this option is set,
      `adapt` should be called to learn the bin boundaries.
    epsilon: Error tolerance, typically a small fraction close to zero (e.g.
      0.01). Higher values of epsilon increase the quantile approximation, and
      hence result in more unequal buckets, but could improve performance
      and resource consumption.

  Examples:

  Bucketize float values based on provided buckets.
  >>> input = np.array([[-1.5, 1.0, 3.4, .5], [0.0, 3.0, 1.3, 0.0]])
  >>> layer = tf.keras.layers.Discretization(bin_boundaries=[0., 1., 2.])
  >>> layer(input)
  <tf.Tensor: shape=(2, 4), dtype=int64, numpy=
  array([[0, 2, 3, 1],
         [1, 3, 2, 1]], dtype=int64)>

  Bucketize float values based on a number of buckets to compute.
  >>> input = np.array([[-1.5, 1.0, 3.4, .5], [0.0, 3.0, 1.3, 0.0]])
  >>> layer = tf.keras.layers.Discretization(num_bins=4, epsilon=0.01)
  >>> layer.adapt(input)
  >>> layer(input)
  <tf.Tensor: shape=(2, 4), dtype=int64, numpy=
  array([[0, 2, 3, 2],
         [1, 3, 3, 1]], dtype=int64)>
  """

  def __init__(self,
               bin_boundaries=None,
               num_bins=None,
               epsilon=0.01,
               **kwargs):
    # bins is a deprecated arg for setting bin_boundaries or num_bins that still
    # has some usage.
    if "bins" in kwargs:
      logging.warning(
          "bins is deprecated, please use bin_boundaries or num_bins instead.")
      if isinstance(kwargs["bins"], int) and num_bins is None:
        num_bins = kwargs["bins"]
      elif bin_boundaries is None:
        bin_boundaries = kwargs["bins"]
      del kwargs["bins"]
    super().__init__(**kwargs)
    base_preprocessing_layer.keras_kpl_gauge.get_cell("Discretization").set(
        True)
    if num_bins is not None and num_bins < 0:
      raise ValueError("`num_bins` must be must be greater than or equal to 0. "
                       "You passed `num_bins={}`".format(num_bins))
    if num_bins is not None and bin_boundaries is not None:
      raise ValueError("Both `num_bins` and `bin_boundaries` should not be "
                       "set. You passed `num_bins={}` and "
                       "`bin_boundaries={}`".format(num_bins, bin_boundaries))
    bin_boundaries = self._convert_to_list(bin_boundaries)
    self.input_bin_boundaries = bin_boundaries
    self.bin_boundaries = bin_boundaries if bin_boundaries is not None else []
    self.num_bins = num_bins
    self.epsilon = epsilon

  def build(self, input_shape):
    super().build(input_shape)

    if self.input_bin_boundaries is not None:
      return

    # Summary contains two equal length vectors of bins at index 0 and weights
    # at index 1.
    self.summary = self.add_weight(
        name="summary",
        shape=(2, None),
        dtype=tf.float32,
        initializer=lambda shape, dtype: [[], []],  # pylint: disable=unused-arguments
        trainable=False)

  def update_state(self, data):
    if self.input_bin_boundaries is not None:
      raise ValueError(
          "Cannot adapt a Discretization layer that has been initialized with "
          "`bin_boundaries`, use `num_bins` instead. You passed "
          "`bin_boundaries={}`.".format(self.input_bin_boundaries))

    if not self.built:
      raise RuntimeError("`build` must be called before `update_state`.")

    data = tf.convert_to_tensor(data)
    if data.dtype != tf.float32:
      data = tf.cast(data, tf.float32)
    summary = summarize(data, self.epsilon)
    self.summary.assign(merge_summaries(summary, self.summary, self.epsilon))

  def finalize_state(self):
    if self.input_bin_boundaries is not None or not self.built:
      return

    # The bucketize op only support list boundaries.
    self.bin_boundaries = self._convert_to_list(
        get_bin_boundaries(self.summary, self.num_bins))

  def reset_state(self):  # pylint: disable=method-hidden
    if self.input_bin_boundaries is not None or not self.built:
      return

    self.summary.assign([[], []])

  def get_config(self):
    config = super().get_config()
    config.update({
        "bin_boundaries": self.input_bin_boundaries,
        "num_bins": self.num_bins,
        "epsilon": self.epsilon,
    })
    return config

  def compute_output_shape(self, input_shape):
    return input_shape

  def compute_output_signature(self, input_spec):
    output_shape = self.compute_output_shape(input_spec.shape.as_list())
    output_dtype = tf.int64
    if isinstance(input_spec, tf.SparseTensorSpec):
      return tf.SparseTensorSpec(
          shape=output_shape, dtype=output_dtype)
    return tf.TensorSpec(shape=output_shape, dtype=output_dtype)

  def call(self, inputs):
    def bucketize(inputs):
      outputs = tf.raw_ops.Bucketize(
          input=inputs, boundaries=self.bin_boundaries)
      # All other preprocessing layers use int64 for int output, so we conform
      # here. Sadly the underlying op only supports int32, so we need to cast.
      return tf.cast(outputs, tf.int64)

    if tf_utils.is_ragged(inputs):
      integer_buckets = tf.ragged.map_flat_values(bucketize, inputs)
      # Ragged map_flat_values doesn't touch the non-values tensors in the
      # ragged composite tensor. If this op is the only op a Keras model,
      # this can cause errors in Graph mode, so wrap the tensor in an identity.
      return tf.identity(integer_buckets)
    elif tf_utils.is_sparse(inputs):
      return tf.SparseTensor(
          indices=tf.identity(inputs.indices),
          values=bucketize(inputs.values),
          dense_shape=tf.identity(inputs.dense_shape))
    else:
      return bucketize(inputs)

  def _convert_to_list(self, inputs):
    if tf.is_tensor(inputs):
      inputs = inputs.numpy()
    if isinstance(inputs, (np.ndarray)):
      inputs = inputs.tolist()
      inputs = list(inputs)
    return inputs

Ancestors

Inherited members

class Hashing (num_bins, mask_value=None, salt=None, **kwargs)

Implements categorical feature hashing, also known as "hashing trick".

This layer transforms single or multiple categorical inputs to hashed output. It converts a sequence of int or string to a sequence of int. The stable hash function uses tensorflow::ops::Fingerprint to produce the same output consistently across all platforms.

This layer uses FarmHash64 by default, which provides a consistent hashed output across different platforms and is stable across invocations, regardless of device and context, by mixing the input bits thoroughly.

If you want to obfuscate the hashed output, you can also pass a random salt argument in the constructor. In that case, the layer will use the SipHash64 hash function, with the salt value serving as additional input to the hash function.

Example (FarmHash64)

>>> layer = tf.keras.layers.Hashing(num_bins=3)
>>> inp = [['A'], ['B'], ['C'], ['D'], ['E']]
>>> layer(inp)
<tf.Tensor: shape=(5, 1), dtype=int64, numpy=
  array([[1],
         [0],
         [1],
         [1],
         [2]])>

Example (FarmHash64) with a mask value

>>> layer = tf.keras.layers.Hashing(num_bins=3, mask_value='')
>>> inp = [['A'], ['B'], [''], ['C'], ['D']]
>>> layer(inp)
<tf.Tensor: shape=(5, 1), dtype=int64, numpy=
  array([[1],
         [1],
         [0],
         [2],
         [2]])>

Example (SipHash64)

>>> layer = tf.keras.layers.Hashing(num_bins=3, salt=[133, 137])
>>> inp = [['A'], ['B'], ['C'], ['D'], ['E']]
>>> layer(inp)
<tf.Tensor: shape=(5, 1), dtype=int64, numpy=
  array([[1],
         [2],
         [1],
         [0],
         [2]])>

Example (Siphash64 with a single integer, same as salt=[133, 133])

>>> layer = tf.keras.layers.Hashing(num_bins=3, salt=133)
>>> inp = [['A'], ['B'], ['C'], ['D'], ['E']]
>>> layer(inp)
<tf.Tensor: shape=(5, 1), dtype=int64, numpy=
  array([[0],
         [0],
         [2],
         [1],
         [0]])>

Args

num_bins
Number of hash bins. Note that this includes the mask_value bin, so the effective number of bins is (num_bins - 1) if mask_value is set.
mask_value
A value that represents masked inputs, which are mapped to index 0. Defaults to None, meaning no mask term will be added and the hashing will start at index 0.
salt
A single unsigned integer or None. If passed, the hash function used will be SipHash64, with these values used as an additional input (known as a "salt" in cryptography). These should be non-zero. Defaults to None (in that case, the FarmHash64 hash function is used). It also supports tuple/list of 2 unsigned integer numbers, see reference paper for details.
**kwargs
Keyword arguments to construct a layer.

Input shape: A single or list of string, int32 or int64 Tensor, SparseTensor or RaggedTensor of shape (batch_size, …,)

Output shape: An int64 Tensor, SparseTensor or RaggedTensor of shape (batch_size, …). If any input is RaggedTensor then output is RaggedTensor, otherwise if any input is SparseTensor then output is SparseTensor, otherwise the output is Tensor.

Reference

Expand source code
class Hashing(base_layer.Layer):
  """Implements categorical feature hashing, also known as "hashing trick".

  This layer transforms single or multiple categorical inputs to hashed output.
  It converts a sequence of int or string to a sequence of int. The stable hash
  function uses `tensorflow::ops::Fingerprint` to produce the same output
  consistently across all platforms.

  This layer uses [FarmHash64](https://github.com/google/farmhash) by default,
  which provides a consistent hashed output across different platforms and is
  stable across invocations, regardless of device and context, by mixing the
  input bits thoroughly.

  If you want to obfuscate the hashed output, you can also pass a random `salt`
  argument in the constructor. In that case, the layer will use the
  [SipHash64](https://github.com/google/highwayhash) hash function, with
  the `salt` value serving as additional input to the hash function.

  **Example (FarmHash64)**

  >>> layer = tf.keras.layers.Hashing(num_bins=3)
  >>> inp = [['A'], ['B'], ['C'], ['D'], ['E']]
  >>> layer(inp)
  <tf.Tensor: shape=(5, 1), dtype=int64, numpy=
    array([[1],
           [0],
           [1],
           [1],
           [2]])>

  **Example (FarmHash64) with a mask value**

  >>> layer = tf.keras.layers.Hashing(num_bins=3, mask_value='')
  >>> inp = [['A'], ['B'], [''], ['C'], ['D']]
  >>> layer(inp)
  <tf.Tensor: shape=(5, 1), dtype=int64, numpy=
    array([[1],
           [1],
           [0],
           [2],
           [2]])>

  **Example (SipHash64)**

  >>> layer = tf.keras.layers.Hashing(num_bins=3, salt=[133, 137])
  >>> inp = [['A'], ['B'], ['C'], ['D'], ['E']]
  >>> layer(inp)
  <tf.Tensor: shape=(5, 1), dtype=int64, numpy=
    array([[1],
           [2],
           [1],
           [0],
           [2]])>

  **Example (Siphash64 with a single integer, same as `salt=[133, 133]`)**

  >>> layer = tf.keras.layers.Hashing(num_bins=3, salt=133)
  >>> inp = [['A'], ['B'], ['C'], ['D'], ['E']]
  >>> layer(inp)
  <tf.Tensor: shape=(5, 1), dtype=int64, numpy=
    array([[0],
           [0],
           [2],
           [1],
           [0]])>

  Args:
    num_bins: Number of hash bins. Note that this includes the `mask_value` bin,
      so the effective number of bins is `(num_bins - 1)` if `mask_value` is
      set.
    mask_value: A value that represents masked inputs, which are mapped to
      index 0. Defaults to None, meaning no mask term will be added and the
      hashing will start at index 0.
    salt: A single unsigned integer or None.
      If passed, the hash function used will be SipHash64, with these values
      used as an additional input (known as a "salt" in cryptography).
      These should be non-zero. Defaults to `None` (in that
      case, the FarmHash64 hash function is used). It also supports
      tuple/list of 2 unsigned integer numbers, see reference paper for details.
    **kwargs: Keyword arguments to construct a layer.

  Input shape:
    A single or list of string, int32 or int64 `Tensor`,
    `SparseTensor` or `RaggedTensor` of shape `(batch_size, ...,)`

  Output shape:
    An int64 `Tensor`, `SparseTensor` or `RaggedTensor` of shape
    `(batch_size, ...)`. If any input is `RaggedTensor` then output is
    `RaggedTensor`, otherwise if any input is `SparseTensor` then output is
    `SparseTensor`, otherwise the output is `Tensor`.

  Reference:
    - [SipHash with salt](https://www.131002.net/siphash/siphash.pdf)

  """

  def __init__(self, num_bins, mask_value=None, salt=None, **kwargs):
    if num_bins is None or num_bins <= 0:
      raise ValueError('`num_bins` cannot be `None` or non-positive values.')
    super().__init__(**kwargs)
    base_preprocessing_layer.keras_kpl_gauge.get_cell('Hashing').set(True)
    self.num_bins = num_bins
    self.mask_value = mask_value
    self.strong_hash = True if salt is not None else False
    self.salt = None
    if salt is not None:
      if isinstance(salt, (tuple, list)) and len(salt) == 2:
        self.salt = salt
      elif isinstance(salt, int):
        self.salt = [salt, salt]
      else:
        raise ValueError('`salt can only be a tuple of size 2 integers, or a '
                         'single integer, given {}'.format(salt))

  def call(self, inputs):
    if isinstance(inputs, (list, tuple, np.ndarray)):
      inputs = tf.convert_to_tensor(inputs)
    if isinstance(inputs, tf.SparseTensor):
      return tf.SparseTensor(
          indices=inputs.indices,
          values=self._hash_values_to_bins(inputs.values),
          dense_shape=inputs.dense_shape)
    return self._hash_values_to_bins(inputs)

  def _hash_values_to_bins(self, values):
    """Converts a non-sparse tensor of values to bin indices."""
    str_to_hash_bucket = self._get_string_to_hash_bucket_fn()
    num_available_bins = self.num_bins
    mask = None
    # If mask_value is set, the zeroth bin is reserved for it.
    if self.mask_value is not None and num_available_bins > 1:
      num_available_bins -= 1
      mask = tf.equal(values, self.mask_value)
    # Convert all values to strings before hashing.
    if values.dtype.is_integer:
      values = tf.as_string(values)
    values = str_to_hash_bucket(values, num_available_bins, name='hash')
    if mask is not None:
      values = tf.add(values, tf.ones_like(values))
      values = tf.where(mask, tf.zeros_like(values), values)
    return values

  def _get_string_to_hash_bucket_fn(self):
    """Returns the string_to_hash_bucket op to use based on `hasher_key`."""
    # string_to_hash_bucket_fast uses FarmHash64 as hash function.
    if not self.strong_hash:
      return tf.strings.to_hash_bucket_fast
    # string_to_hash_bucket_strong uses SipHash64 as hash function.
    else:
      return functools.partial(
          tf.strings.to_hash_bucket_strong, key=self.salt)

  def compute_output_shape(self, input_shape):
    return input_shape

  def compute_output_signature(self, input_spec):
    output_shape = self.compute_output_shape(input_spec.shape)
    output_dtype = tf.int64
    if isinstance(input_spec, tf.SparseTensorSpec):
      return tf.SparseTensorSpec(
          shape=output_shape, dtype=output_dtype)
    else:
      return tf.TensorSpec(shape=output_shape, dtype=output_dtype)

  def get_config(self):
    config = super().get_config()
    config.update({
        'num_bins': self.num_bins,
        'salt': self.salt,
        'mask_value': self.mask_value,
    })
    return config

Ancestors

  • Layer
  • tensorflow.python.module.module.Module
  • tensorflow.python.training.tracking.tracking.AutoTrackable
  • tensorflow.python.training.tracking.base.Trackable
  • LayerVersionSelector

Inherited members

class Normalization (axis=-1, mean=None, variance=None, **kwargs)

Feature-wise normalization of the data.

This layer will coerce its inputs into a distribution centered around 0 with standard deviation 1. It accomplishes this by precomputing the mean and variance of the data, and calling (input - mean) / sqrt(var) at runtime.

What happens in adapt(): Compute mean and variance of the data and store them as the layer's weights. adapt() should be called before fit(), evaluate(), or predict().

Args

axis
Integer, tuple of integers, or None. The axis or axes that should have a separate mean and variance for each index in the shape. For example, if shape is (None, 5) and axis=1, the layer will track 5 separate mean and variance values for the last axis. If axis is set to None, the layer will normalize all elements in the input by a scalar mean and variance. Defaults to -1, where the last axis of the input is assumed to be a feature dimension and is normalized per index. Note that in the specific case of batched scalar inputs where the only axis is the batch axis, the default will normalize each index in the batch separately. In this case, consider passing axis=None.
mean
The mean value(s) to use during normalization. The passed value(s) will be broadcast to the shape of the kept axes above; if the value(s) cannot be broadcast, an error will be raised when this layer's build() method is called.
variance
The variance value(s) to use during normalization. The passed value(s) will be broadcast to the shape of the kept axes above; if the value(s) cannot be broadcast, an error will be raised when this layer's build() method is called.

Examples:

Calculate a global mean and variance by analyzing the dataset in adapt().

>>> adapt_data = np.array([1., 2., 3., 4., 5.], dtype='float32')
>>> input_data = np.array([1., 2., 3.], dtype='float32')
>>> layer = tf.keras.layers.Normalization(axis=None)
>>> layer.adapt(adapt_data)
>>> layer(input_data)
<tf.Tensor: shape=(3,), dtype=float32, numpy=
array([-1.4142135, -0.70710677, 0.], dtype=float32)>

Calculate a mean and variance for each index on the last axis.

>>> adapt_data = np.array([[0., 7., 4.],
...                        [2., 9., 6.],
...                        [0., 7., 4.],
...                        [2., 9., 6.]], dtype='float32')
>>> input_data = np.array([[0., 7., 4.]], dtype='float32')
>>> layer = tf.keras.layers.Normalization(axis=-1)
>>> layer.adapt(adapt_data)
>>> layer(input_data)
<tf.Tensor: shape=(1, 3), dtype=float32, numpy=
array([0., 0., 0.], dtype=float32)>

Pass the mean and variance directly.

>>> input_data = np.array([[1.], [2.], [3.]], dtype='float32')
>>> layer = tf.keras.layers.Normalization(mean=3., variance=2.)
>>> layer(input_data)
<tf.Tensor: shape=(3, 1), dtype=float32, numpy=
array([[-1.4142135 ],
       [-0.70710677],
       [ 0.        ]], dtype=float32)>
Expand source code
class Normalization(base_preprocessing_layer.PreprocessingLayer):
  """Feature-wise normalization of the data.

  This layer will coerce its inputs into a distribution centered around
  0 with standard deviation 1. It accomplishes this by precomputing the mean and
  variance of the data, and calling `(input - mean) / sqrt(var)` at runtime.

  What happens in `adapt()`: Compute mean and variance of the data and store
  them as the layer's weights. `adapt()` should be called before `fit()`,
  `evaluate()`, or `predict()`.

  Args:
      axis: Integer, tuple of integers, or None. The axis or axes that should
        have a separate mean and variance for each index in the shape. For
        example, if shape is `(None, 5)` and `axis=1`, the layer will track 5
        separate mean and variance values for the last axis. If `axis` is set to
        `None`, the layer will normalize all elements in the input by a scalar
        mean and variance. Defaults to -1, where the last axis of the input is
        assumed to be a feature dimension and is normalized per index. Note that
        in the specific case of batched scalar inputs where the only axis is the
        batch axis, the default will normalize each index in the batch
        separately. In this case, consider passing `axis=None`.
      mean: The mean value(s) to use during normalization. The passed value(s)
        will be broadcast to the shape of the kept axes above; if the value(s)
        cannot be broadcast, an error will be raised when this layer's `build()`
        method is called.
      variance: The variance value(s) to use during normalization. The passed
        value(s) will be broadcast to the shape of the kept axes above; if the
        value(s) cannot be broadcast, an error will be raised when this layer's
        `build()` method is called.

  Examples:

  Calculate a global mean and variance by analyzing the dataset in `adapt()`.

  >>> adapt_data = np.array([1., 2., 3., 4., 5.], dtype='float32')
  >>> input_data = np.array([1., 2., 3.], dtype='float32')
  >>> layer = tf.keras.layers.Normalization(axis=None)
  >>> layer.adapt(adapt_data)
  >>> layer(input_data)
  <tf.Tensor: shape=(3,), dtype=float32, numpy=
  array([-1.4142135, -0.70710677, 0.], dtype=float32)>

  Calculate a mean and variance for each index on the last axis.

  >>> adapt_data = np.array([[0., 7., 4.],
  ...                        [2., 9., 6.],
  ...                        [0., 7., 4.],
  ...                        [2., 9., 6.]], dtype='float32')
  >>> input_data = np.array([[0., 7., 4.]], dtype='float32')
  >>> layer = tf.keras.layers.Normalization(axis=-1)
  >>> layer.adapt(adapt_data)
  >>> layer(input_data)
  <tf.Tensor: shape=(1, 3), dtype=float32, numpy=
  array([0., 0., 0.], dtype=float32)>

  Pass the mean and variance directly.

  >>> input_data = np.array([[1.], [2.], [3.]], dtype='float32')
  >>> layer = tf.keras.layers.Normalization(mean=3., variance=2.)
  >>> layer(input_data)
  <tf.Tensor: shape=(3, 1), dtype=float32, numpy=
  array([[-1.4142135 ],
         [-0.70710677],
         [ 0.        ]], dtype=float32)>
  """

  def __init__(self, axis=-1, mean=None, variance=None, **kwargs):
    super().__init__(**kwargs)
    base_preprocessing_layer.keras_kpl_gauge.get_cell('Normalization').set(True)

    # Standardize `axis` to a tuple.
    if axis is None:
      axis = ()
    elif isinstance(axis, int):
      axis = (axis,)
    else:
      axis = tuple(axis)
    self.axis = axis

    # Set `mean` and `variance` if passed.
    if isinstance(mean, tf.Variable):
      raise ValueError('Normalization does not support passing a Variable '
                       'for the `mean` init arg.')
    if isinstance(variance, tf.Variable):
      raise ValueError('Normalization does not support passing a Variable '
                       'for the `variance` init arg.')
    if (mean is not None) != (variance is not None):
      raise ValueError(
          'When setting values directly, both `mean` and `variance` '
          'must be set. Got mean: {} and variance: {}'.format(mean, variance))
    self.input_mean = mean
    self.input_variance = variance

  def build(self, input_shape):
    super().build(input_shape)

    if (isinstance(input_shape, (list, tuple)) and
        all(isinstance(shape, tf.TensorShape) for shape in input_shape)):
      raise ValueError('Normalization only accepts a single input. If you are '
                       'passing a python list or tuple as a single input, '
                       'please convert to a numpy array or `tf.Tensor`.')

    input_shape = tf.TensorShape(input_shape).as_list()
    ndim = len(input_shape)

    if any(a < -ndim or a >= ndim for a in self.axis):
      raise ValueError('All `axis` values must be in the range [-ndim, ndim). '
                       'Found ndim: `{}`, axis: {}'.format(ndim, self.axis))

    # Axes to be kept, replacing negative values with positive equivalents.
    # Sorted to avoid transposing axes.
    self._keep_axis = sorted([d if d >= 0 else d + ndim for d in self.axis])
    # All axes to be kept should have known shape.
    for d in self._keep_axis:
      if input_shape[d] is None:
        raise ValueError(
            'All `axis` values to be kept must have known shape. Got axis: {}, '
            'input shape: {}, with unknown axis at index: {}'.format(
                self.axis, input_shape, d))
    # Axes to be reduced.
    self._reduce_axis = [d for d in range(ndim) if d not in self._keep_axis]
    # 1 if an axis should be reduced, 0 otherwise.
    self._reduce_axis_mask = [
        0 if d in self._keep_axis else 1 for d in range(ndim)
    ]
    # Broadcast any reduced axes.
    self._broadcast_shape = [
        input_shape[d] if d in self._keep_axis else 1 for d in range(ndim)
    ]
    mean_and_var_shape = tuple(input_shape[d] for d in self._keep_axis)

    if self.input_mean is None:
      self.adapt_mean = self.add_weight(
          name='mean',
          shape=mean_and_var_shape,
          dtype=self.dtype,
          initializer='zeros',
          trainable=False)
      self.adapt_variance = self.add_weight(
          name='variance',
          shape=mean_and_var_shape,
          dtype=self.dtype,
          initializer='ones',
          trainable=False)
      self.count = self.add_weight(
          name='count',
          shape=(),
          dtype=tf.int64,
          initializer='zeros',
          trainable=False)
      self.finalize_state()
    else:
      # In the no adapt case, make constant tensors for mean and variance with
      # proper broadcast shape for use during call.
      mean = self.input_mean * np.ones(mean_and_var_shape)
      variance = self.input_variance * np.ones(mean_and_var_shape)
      mean = tf.reshape(mean, self._broadcast_shape)
      variance = tf.reshape(variance, self._broadcast_shape)
      self.mean = tf.cast(mean, self.compute_dtype)
      self.variance = tf.cast(variance, self.compute_dtype)

  def update_state(self, data):
    if self.input_mean is not None:
      raise ValueError(
          'Cannot `adapt` a Normalization layer that is initialized with '
          'static `mean` and `variance`, you passed mean {} and variance {}.'
          .format(self.input_mean, self.input_variance))

    if not self.built:
      raise RuntimeError('`build` must be called before `update_state`.')

    data = self._standardize_inputs(data)
    data = tf.cast(data, self.adapt_mean.dtype)
    batch_mean, batch_variance = tf.nn.moments(data, axes=self._reduce_axis)
    batch_shape = tf.shape(data, out_type=self.count.dtype)
    if self._reduce_axis:
      batch_reduce_shape = tf.gather(batch_shape, self._reduce_axis)
      batch_count = tf.reduce_prod(batch_reduce_shape)
    else:
      batch_count = 1

    total_count = batch_count + self.count
    batch_weight = (
        tf.cast(batch_count, dtype=self.dtype) /
        tf.cast(total_count, dtype=self.dtype))
    existing_weight = 1. - batch_weight

    total_mean = self.adapt_mean * existing_weight + batch_mean * batch_weight
    # The variance is computed using the lack-of-fit sum of squares
    # formula (see https://en.wikipedia.org/wiki/Lack-of-fit_sum_of_squares).
    total_variance = ((self.adapt_variance +
                       (self.adapt_mean - total_mean)**2) * existing_weight +
                      (batch_variance +
                       (batch_mean - total_mean)**2) * batch_weight)
    self.adapt_mean.assign(total_mean)
    self.adapt_variance.assign(total_variance)
    self.count.assign(total_count)

  def reset_state(self):  # pylint: disable=method-hidden
    if self.input_mean is not None or not self.built:
      return

    self.adapt_mean.assign(tf.zeros_like(self.adapt_mean))
    self.adapt_variance.assign(tf.ones_like(self.adapt_variance))
    self.count.assign(tf.zeros_like(self.count))

  def finalize_state(self):
    if self.input_mean is not None or not self.built:
      return

    # In the adapt case, we make constant tensors for mean and variance with
    # proper broadcast shape and dtype each time `finalize_state` is called.
    self.mean = tf.reshape(self.adapt_mean, self._broadcast_shape)
    self.mean = tf.cast(self.mean, self.compute_dtype)
    self.variance = tf.reshape(self.adapt_variance, self._broadcast_shape)
    self.variance = tf.cast(self.variance, self.compute_dtype)

  def call(self, inputs):
    inputs = self._standardize_inputs(inputs)
    # The base layer automatically casts floating-point inputs, but we
    # explicitly cast here to also allow integer inputs to be passed
    inputs = tf.cast(inputs, self.compute_dtype)
    return ((inputs - self.mean) /
            tf.maximum(tf.sqrt(self.variance), backend.epsilon()))

  def compute_output_shape(self, input_shape):
    return input_shape

  def compute_output_signature(self, input_spec):
    return input_spec

  def get_config(self):
    config = super().get_config()
    config.update({
        'axis': self.axis,
        'mean': self._convert_to_list(self.input_mean),
        'variance': self._convert_to_list(self.input_variance),
    })
    return config

  def _standardize_inputs(self, inputs):
    inputs = tf.convert_to_tensor(inputs)
    if inputs.dtype != self.dtype:
      inputs = tf.cast(inputs, self.dtype)
    return inputs

  def _convert_to_list(self, inputs):
    if tf.is_tensor(inputs):
      inputs = inputs.numpy()
    if isinstance(inputs, (np.ndarray)):
      inputs = inputs.tolist()
      inputs = list(inputs)
    return inputs

Ancestors

Inherited members

class PreprocessingLayer (**kwargs)

Base class for Preprocessing Layers.

Don't use this class directly: it's an abstract base class! You may be looking for one of the many built-in preprocessing layers instead.

Preprocessing layers are layers whose state gets computed before model training starts. They do not get updated during training. Most preprocessing layers implement an adapt() method for state computation.

The PreprocessingLayer class is the base class you would subclass to implement your own preprocessing layers.

Expand source code
class PreprocessingLayer(Layer, metaclass=abc.ABCMeta):
  """Base class for Preprocessing Layers.

  **Don't use this class directly: it's an abstract base class!** You may
  be looking for one of the many built-in
  [preprocessing layers](https://keras.io/guides/preprocessing_layers/)
  instead.

  Preprocessing layers are layers whose state gets computed before model
  training starts. They do not get updated during training.
  Most preprocessing layers implement an `adapt()` method for state computation.

  The `PreprocessingLayer` class is the base class you would subclass to
  implement your own preprocessing layers.
  """
  _must_restore_from_config = True

  def __init__(self, **kwargs):
    super(PreprocessingLayer, self).__init__(**kwargs)
    self._is_compiled = False
    self._is_adapted = False

    # Sets `is_adapted=False` when `reset_state` is called.
    self._reset_state_impl = self.reset_state
    self.reset_state = self._reset_state_wrapper

    self._adapt_function = None

  @property
  def is_adapted(self):
    """Whether the layer has been fit to data already."""
    return self._is_adapted

  @doc_controls.do_not_generate_docs
  def update_state(self, data):
    """Accumulates statistics for the preprocessing layer.

    Arguments:
      data: A mini-batch of inputs to the layer.
    """
    raise NotImplementedError

  @doc_controls.do_not_generate_docs
  def reset_state(self):  # pylint: disable=method-hidden
    """Resets the statistics of the preprocessing layer."""
    raise NotImplementedError

  @doc_controls.do_not_generate_docs
  def finalize_state(self):
    """Finalize the statistics for the preprocessing layer.

    This method is called at the end of `adapt` or after restoring a serialized
    preprocessing layer's state. This method handles any one-time operations
    that should occur on the layer's state before `Layer.__call__`.
    """
    pass

  @doc_controls.do_not_generate_docs
  def make_adapt_function(self):
    """Creates a function to execute one step of `adapt`.

    This method can be overridden to support custom adapt logic.
    This method is called by `PreprocessingLayer.adapt`.

    Typically, this method directly controls `tf.function` settings,
    and delegates the actual state update logic to
    `PreprocessingLayer.update_state`.

    This function is cached the first time `PreprocessingLayer.adapt`
    is called. The cache is cleared whenever `PreprocessingLayer.compile`
    is called.

    Returns:
      Function. The function created by this method should accept a
      `tf.data.Iterator`, retrieve a batch, and update the state of the
      layer.
    """
    if self._adapt_function is not None:
      return self._adapt_function

    def adapt_step(iterator):
      data = next(iterator)
      self._adapt_maybe_build(data)
      self.update_state(data)

    if self._steps_per_execution.numpy().item() == 1:
      adapt_fn = adapt_step
    else:

      def adapt_fn(iterator):
        for _ in tf.range(self._steps_per_execution):
          adapt_step(iterator)

    if not self._run_eagerly:
      adapt_fn = tf.function(adapt_fn)

    self._adapt_function = adapt_fn
    return self._adapt_function

  def compile(self, run_eagerly=None, steps_per_execution=None):
    """Configures the layer for `adapt`.

    Arguments:
      run_eagerly: Bool. Defaults to `False`. If `True`, this `Model`'s logic
        will not be wrapped in a `tf.function`. Recommended to leave this as
        `None` unless your `Model` cannot be run inside a `tf.function`.
        steps_per_execution: Int. Defaults to 1. The number of batches to run
          during each `tf.function` call. Running multiple batches inside a
          single `tf.function` call can greatly improve performance on TPUs or
          small models with a large Python overhead.
    """
    if steps_per_execution is None:
      steps_per_execution = 1
    self._configure_steps_per_execution(steps_per_execution)

    if run_eagerly is None:
      run_eagerly = self.dynamic
    self._run_eagerly = run_eagerly

    self._is_compiled = True

  def adapt(self, data, batch_size=None, steps=None):
    """Fits the state of the preprocessing layer to the data being passed.

    After calling `adapt` on a layer, a preprocessing layer's state will not
    update during training. In order to make preprocessing layers efficient in
    any distribution context, they are kept constant with respect to any
    compiled `tf.Graph`s that call the layer. This does not affect the layer use
    when adapting each layer only once, but if you adapt a layer multiple times
    you will need to take care to re-compile any compiled functions as follows:

     * If you are adding a preprocessing layer to a `keras.Model`, you need to
       call `model.compile` after each subsequent call to `adapt`.
     * If you are calling a preprocessing layer inside `tf.data.Dataset.map`,
       you should call `map` again on the input `tf.data.Dataset` after each
       `adapt`.
     * If you are using a `tf.function` directly which calls a preprocessing
       layer, you need to call `tf.function` again on your callable after
       each subsequent call to `adapt`.

    `tf.keras.Model` example with multiple adapts:

    >>> layer = tf.keras.layers.experimental.preprocessing.Normalization(
    ...     axis=None)
    >>> layer.adapt([0, 2])
    >>> model = tf.keras.Sequential(layer)
    >>> model.predict([0, 1, 2])
    array([-1.,  0.,  1.], dtype=float32)
    >>> layer.adapt([-1, 1])
    >>> model.compile() # This is needed to re-compile model.predict!
    >>> model.predict([0, 1, 2])
    array([0., 1., 2.], dtype=float32)

    `tf.data.Dataset` example with multiple adapts:

    >>> layer = tf.keras.layers.experimental.preprocessing.Normalization(
    ...     axis=None)
    >>> layer.adapt([0, 2])
    >>> input_ds = tf.data.Dataset.range(3)
    >>> normalized_ds = input_ds.map(layer)
    >>> list(normalized_ds.as_numpy_iterator())
    [array([-1.], dtype=float32),
     array([0.], dtype=float32),
     array([1.], dtype=float32)]
    >>> layer.adapt([-1, 1])
    >>> normalized_ds = input_ds.map(layer) # Re-map over the input dataset.
    >>> list(normalized_ds.as_numpy_iterator())
    [array([0.], dtype=float32),
     array([1.], dtype=float32),
     array([2.], dtype=float32)]

    Arguments:
        data: The data to train on. It can be passed either as a tf.data
          Dataset, or as a numpy array.
        batch_size: Integer or `None`.
            Number of samples per state update.
            If unspecified, `batch_size` will default to 32.
            Do not specify the `batch_size` if your data is in the
            form of datasets, generators, or `keras.utils.Sequence` instances
            (since they generate batches).
        steps: Integer or `None`.
            Total number of steps (batches of samples)
            When training with input tensors such as
            TensorFlow data tensors, the default `None` is equal to
            the number of samples in your dataset divided by
            the batch size, or 1 if that cannot be determined. If x is a
            `tf.data` dataset, and 'steps' is None, the epoch will run until
            the input dataset is exhausted. When passing an infinitely
            repeating dataset, you must specify the `steps` argument. This
            argument is not supported with array inputs.
    """
    _disallow_inside_tf_function('adapt')
    if not version_utils.should_use_v2():
      raise RuntimeError('`adapt` is only supported in tensorflow v2.')  # pylint: disable=g-doc-exception
    if not self._is_compiled:
      self.compile()  # Compile with defaults.
    if self.built:
      self.reset_state()
    data_handler = data_adapter.DataHandler(
        data,
        batch_size=batch_size,
        steps_per_epoch=steps,
        epochs=1,
        steps_per_execution=self._steps_per_execution,
        distribute=False)
    self._adapt_function = self.make_adapt_function()
    for _, iterator in data_handler.enumerate_epochs():
      with data_handler.catch_stop_iteration():
        for _ in data_handler.steps():
          self._adapt_function(iterator)
          if data_handler.should_sync:
            context.async_wait()
    self.finalize_state()
    self._is_adapted = True

  def _reset_state_wrapper(self):
    """Calls `reset_state` and sets `adapted` to `False`."""
    self._reset_state_impl()
    self._is_adapted = False

  @tf.__internal__.tracking.no_automatic_dependency_tracking
  def _configure_steps_per_execution(self, steps_per_execution):
    self._steps_per_execution = tf.Variable(
        steps_per_execution,
        dtype='int64',
        aggregation=tf.VariableAggregation.ONLY_FIRST_REPLICA)

  # TODO(omalleyt): Unify this logic with `Layer._maybe_build`.
  def _adapt_maybe_build(self, data):
    if not self.built:
      try:
        # If this is a Numpy array or tensor, we can get shape from .shape.
        # If not, an attribute error will be thrown.
        data_shape = data.shape
        data_shape_nones = tuple([None] * len(data.shape))
      except AttributeError:
        # The input has an unknown number of dimensions.
        data_shape = None
        data_shape_nones = None

      # TODO (b/159261555): move this to base layer build.
      batch_input_shape = getattr(self, '_batch_input_shape', None)
      if batch_input_shape is None:
        # Set the number of dimensions.
        self._batch_input_shape = data_shape_nones
      self.build(data_shape)
      self.built = True

Ancestors

  • Layer
  • tensorflow.python.module.module.Module
  • tensorflow.python.training.tracking.tracking.AutoTrackable
  • tensorflow.python.training.tracking.base.Trackable
  • LayerVersionSelector

Subclasses

Instance variables

var is_adapted

Whether the layer has been fit to data already.

Expand source code
@property
def is_adapted(self):
  """Whether the layer has been fit to data already."""
  return self._is_adapted

Methods

def adapt(self, data, batch_size=None, steps=None)

Fits the state of the preprocessing layer to the data being passed.

After calling adapt on a layer, a preprocessing layer's state will not update during training. In order to make preprocessing layers efficient in any distribution context, they are kept constant with respect to any compiled tf.Graphs that call the layer. This does not affect the layer use when adapting each layer only once, but if you adapt a layer multiple times you will need to take care to re-compile any compiled functions as follows:

  • If you are adding a preprocessing layer to a keras.Model, you need to call model.compile after each subsequent call to adapt.
  • If you are calling a preprocessing layer inside tf.data.Dataset.map, you should call map again on the input tf.data.Dataset after each adapt.
  • If you are using a tf.function directly which calls a preprocessing layer, you need to call tf.function again on your callable after each subsequent call to adapt.

tf.keras.Model example with multiple adapts:

>>> layer = tf.keras.layers.experimental.preprocessing.Normalization(
...     axis=None)
>>> layer.adapt([0, 2])
>>> model = tf.keras.Sequential(layer)
>>> model.predict([0, 1, 2])
array([-1.,  0.,  1.], dtype=float32)
>>> layer.adapt([-1, 1])
>>> model.compile() # This is needed to re-compile model.predict!
>>> model.predict([0, 1, 2])
array([0., 1., 2.], dtype=float32)

tf.data.Dataset example with multiple adapts:

>>> layer = tf.keras.layers.experimental.preprocessing.Normalization(
...     axis=None)
>>> layer.adapt([0, 2])
>>> input_ds = tf.data.Dataset.range(3)
>>> normalized_ds = input_ds.map(layer)
>>> list(normalized_ds.as_numpy_iterator())
[array([-1.], dtype=float32),
 array([0.], dtype=float32),
 array([1.], dtype=float32)]
>>> layer.adapt([-1, 1])
>>> normalized_ds = input_ds.map(layer) # Re-map over the input dataset.
>>> list(normalized_ds.as_numpy_iterator())
[array([0.], dtype=float32),
 array([1.], dtype=float32),
 array([2.], dtype=float32)]

Arguments

data: The data to train on. It can be passed either as a tf.data Dataset, or as a numpy array. batch_size: Integer or None. Number of samples per state update. If unspecified, batch_size will default to 32. Do not specify the batch_size if your data is in the form of datasets, generators, or keras.utils.Sequence instances (since they generate batches). steps: Integer or None. Total number of steps (batches of samples) When training with input tensors such as TensorFlow data tensors, the default None is equal to the number of samples in your dataset divided by the batch size, or 1 if that cannot be determined. If x is a tf.data dataset, and 'steps' is None, the epoch will run until the input dataset is exhausted. When passing an infinitely repeating dataset, you must specify the steps argument. This argument is not supported with array inputs.

Expand source code
def adapt(self, data, batch_size=None, steps=None):
  """Fits the state of the preprocessing layer to the data being passed.

  After calling `adapt` on a layer, a preprocessing layer's state will not
  update during training. In order to make preprocessing layers efficient in
  any distribution context, they are kept constant with respect to any
  compiled `tf.Graph`s that call the layer. This does not affect the layer use
  when adapting each layer only once, but if you adapt a layer multiple times
  you will need to take care to re-compile any compiled functions as follows:

   * If you are adding a preprocessing layer to a `keras.Model`, you need to
     call `model.compile` after each subsequent call to `adapt`.
   * If you are calling a preprocessing layer inside `tf.data.Dataset.map`,
     you should call `map` again on the input `tf.data.Dataset` after each
     `adapt`.
   * If you are using a `tf.function` directly which calls a preprocessing
     layer, you need to call `tf.function` again on your callable after
     each subsequent call to `adapt`.

  `tf.keras.Model` example with multiple adapts:

  >>> layer = tf.keras.layers.experimental.preprocessing.Normalization(
  ...     axis=None)
  >>> layer.adapt([0, 2])
  >>> model = tf.keras.Sequential(layer)
  >>> model.predict([0, 1, 2])
  array([-1.,  0.,  1.], dtype=float32)
  >>> layer.adapt([-1, 1])
  >>> model.compile() # This is needed to re-compile model.predict!
  >>> model.predict([0, 1, 2])
  array([0., 1., 2.], dtype=float32)

  `tf.data.Dataset` example with multiple adapts:

  >>> layer = tf.keras.layers.experimental.preprocessing.Normalization(
  ...     axis=None)
  >>> layer.adapt([0, 2])
  >>> input_ds = tf.data.Dataset.range(3)
  >>> normalized_ds = input_ds.map(layer)
  >>> list(normalized_ds.as_numpy_iterator())
  [array([-1.], dtype=float32),
   array([0.], dtype=float32),
   array([1.], dtype=float32)]
  >>> layer.adapt([-1, 1])
  >>> normalized_ds = input_ds.map(layer) # Re-map over the input dataset.
  >>> list(normalized_ds.as_numpy_iterator())
  [array([0.], dtype=float32),
   array([1.], dtype=float32),
   array([2.], dtype=float32)]

  Arguments:
      data: The data to train on. It can be passed either as a tf.data
        Dataset, or as a numpy array.
      batch_size: Integer or `None`.
          Number of samples per state update.
          If unspecified, `batch_size` will default to 32.
          Do not specify the `batch_size` if your data is in the
          form of datasets, generators, or `keras.utils.Sequence` instances
          (since they generate batches).
      steps: Integer or `None`.
          Total number of steps (batches of samples)
          When training with input tensors such as
          TensorFlow data tensors, the default `None` is equal to
          the number of samples in your dataset divided by
          the batch size, or 1 if that cannot be determined. If x is a
          `tf.data` dataset, and 'steps' is None, the epoch will run until
          the input dataset is exhausted. When passing an infinitely
          repeating dataset, you must specify the `steps` argument. This
          argument is not supported with array inputs.
  """
  _disallow_inside_tf_function('adapt')
  if not version_utils.should_use_v2():
    raise RuntimeError('`adapt` is only supported in tensorflow v2.')  # pylint: disable=g-doc-exception
  if not self._is_compiled:
    self.compile()  # Compile with defaults.
  if self.built:
    self.reset_state()
  data_handler = data_adapter.DataHandler(
      data,
      batch_size=batch_size,
      steps_per_epoch=steps,
      epochs=1,
      steps_per_execution=self._steps_per_execution,
      distribute=False)
  self._adapt_function = self.make_adapt_function()
  for _, iterator in data_handler.enumerate_epochs():
    with data_handler.catch_stop_iteration():
      for _ in data_handler.steps():
        self._adapt_function(iterator)
        if data_handler.should_sync:
          context.async_wait()
  self.finalize_state()
  self._is_adapted = True
def compile(self, run_eagerly=None, steps_per_execution=None)

Configures the layer for adapt.

Arguments

run_eagerly: Bool. Defaults to False. If True, this Model's logic will not be wrapped in a tf.function. Recommended to leave this as None unless your Model cannot be run inside a tf.function. steps_per_execution: Int. Defaults to 1. The number of batches to run during each tf.function call. Running multiple batches inside a single tf.function call can greatly improve performance on TPUs or small models with a large Python overhead.

Expand source code
def compile(self, run_eagerly=None, steps_per_execution=None):
  """Configures the layer for `adapt`.

  Arguments:
    run_eagerly: Bool. Defaults to `False`. If `True`, this `Model`'s logic
      will not be wrapped in a `tf.function`. Recommended to leave this as
      `None` unless your `Model` cannot be run inside a `tf.function`.
      steps_per_execution: Int. Defaults to 1. The number of batches to run
        during each `tf.function` call. Running multiple batches inside a
        single `tf.function` call can greatly improve performance on TPUs or
        small models with a large Python overhead.
  """
  if steps_per_execution is None:
    steps_per_execution = 1
  self._configure_steps_per_execution(steps_per_execution)

  if run_eagerly is None:
    run_eagerly = self.dynamic
  self._run_eagerly = run_eagerly

  self._is_compiled = True
def finalize_state(self)

Finalize the statistics for the preprocessing layer.

This method is called at the end of adapt or after restoring a serialized preprocessing layer's state. This method handles any one-time operations that should occur on the layer's state before Layer.__call__.

Expand source code
@doc_controls.do_not_generate_docs
def finalize_state(self):
  """Finalize the statistics for the preprocessing layer.

  This method is called at the end of `adapt` or after restoring a serialized
  preprocessing layer's state. This method handles any one-time operations
  that should occur on the layer's state before `Layer.__call__`.
  """
  pass
def make_adapt_function(self)

Creates a function to execute one step of adapt.

This method can be overridden to support custom adapt logic. This method is called by PreprocessingLayer.adapt().

Typically, this method directly controls tf.function settings, and delegates the actual state update logic to PreprocessingLayer.update_state().

This function is cached the first time PreprocessingLayer.adapt() is called. The cache is cleared whenever PreprocessingLayer.compile() is called.

Returns

Function. The function created by this method should accept a tf.data.Iterator, retrieve a batch, and update the state of the layer.

Expand source code
@doc_controls.do_not_generate_docs
def make_adapt_function(self):
  """Creates a function to execute one step of `adapt`.

  This method can be overridden to support custom adapt logic.
  This method is called by `PreprocessingLayer.adapt`.

  Typically, this method directly controls `tf.function` settings,
  and delegates the actual state update logic to
  `PreprocessingLayer.update_state`.

  This function is cached the first time `PreprocessingLayer.adapt`
  is called. The cache is cleared whenever `PreprocessingLayer.compile`
  is called.

  Returns:
    Function. The function created by this method should accept a
    `tf.data.Iterator`, retrieve a batch, and update the state of the
    layer.
  """
  if self._adapt_function is not None:
    return self._adapt_function

  def adapt_step(iterator):
    data = next(iterator)
    self._adapt_maybe_build(data)
    self.update_state(data)

  if self._steps_per_execution.numpy().item() == 1:
    adapt_fn = adapt_step
  else:

    def adapt_fn(iterator):
      for _ in tf.range(self._steps_per_execution):
        adapt_step(iterator)

  if not self._run_eagerly:
    adapt_fn = tf.function(adapt_fn)

  self._adapt_function = adapt_fn
  return self._adapt_function
def reset_state(self)

Resets the statistics of the preprocessing layer.

Expand source code
@doc_controls.do_not_generate_docs
def reset_state(self):  # pylint: disable=method-hidden
  """Resets the statistics of the preprocessing layer."""
  raise NotImplementedError
def update_state(self, data)

Accumulates statistics for the preprocessing layer.

Arguments

data: A mini-batch of inputs to the layer.

Expand source code
@doc_controls.do_not_generate_docs
def update_state(self, data):
  """Accumulates statistics for the preprocessing layer.

  Arguments:
    data: A mini-batch of inputs to the layer.
  """
  raise NotImplementedError

Inherited members

class RandomContrast (factor, seed=None, **kwargs)

Adjust the contrast of an image or images by a random factor.

Contrast is adjusted independently for each channel of each image during training.

For each channel, this layer computes the mean of the image pixels in the channel and then adjusts each component x of each pixel to (x - mean) * contrast_factor + mean.

Input shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, height, width, channels), in "channels_last" format.

Output shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, height, width, channels), in "channels_last" format.

Attributes

factor
a positive float represented as fraction of value, or a tuple of size 2 representing lower and upper bound. When represented as a single float, lower = upper. The contrast factor will be randomly picked between [1.0 - lower, 1.0 + upper].
seed
Integer. Used to create a random seed.
Expand source code
class RandomContrast(base_layer.Layer):
  """Adjust the contrast of an image or images by a random factor.

  Contrast is adjusted independently for each channel of each image during
  training.

  For each channel, this layer computes the mean of the image pixels in the
  channel and then adjusts each component `x` of each pixel to
  `(x - mean) * contrast_factor + mean`.

  Input shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., height, width, channels)`, in `"channels_last"` format.

  Output shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., height, width, channels)`, in `"channels_last"` format.

  Attributes:
    factor: a positive float represented as fraction of value, or a tuple of
      size 2 representing lower and upper bound. When represented as a single
      float, lower = upper. The contrast factor will be randomly picked between
      `[1.0 - lower, 1.0 + upper]`.
    seed: Integer. Used to create a random seed.
  """

  def __init__(self, factor, seed=None, **kwargs):
    self.factor = factor
    if isinstance(factor, (tuple, list)):
      self.lower = factor[0]
      self.upper = factor[1]
    else:
      self.lower = self.upper = factor
    if self.lower < 0. or self.upper < 0. or self.lower > 1.:
      raise ValueError('Factor cannot have negative values or greater than 1.0,'
                       ' got {}'.format(factor))
    self.seed = seed
    self._rng = make_generator(self.seed)
    super(RandomContrast, self).__init__(**kwargs)
    base_preprocessing_layer.keras_kpl_gauge.get_cell('RandomContrast').set(
        True)

  def call(self, inputs, training=True):
    if training is None:
      training = backend.learning_phase()

    def random_contrasted_inputs():
      return tf.image.stateless_random_contrast(inputs, 1. - self.lower,
                                                1. + self.upper,
                                                self._rng.make_seeds()[:, 0])

    output = control_flow_util.smart_cond(training, random_contrasted_inputs,
                                          lambda: inputs)
    output.set_shape(inputs.shape)
    return output

  def compute_output_shape(self, input_shape):
    return input_shape

  def get_config(self):
    config = {
        'factor': self.factor,
        'seed': self.seed,
    }
    base_config = super(RandomContrast, self).get_config()
    return dict(list(base_config.items()) + list(config.items()))

Ancestors

  • Layer
  • tensorflow.python.module.module.Module
  • tensorflow.python.training.tracking.tracking.AutoTrackable
  • tensorflow.python.training.tracking.base.Trackable
  • LayerVersionSelector

Inherited members

class RandomCrop (height, width, seed=None, **kwargs)

Randomly crop the images to target height and width.

This layer will crop all the images in the same batch to the same cropping location. By default, random cropping is only applied during training. At inference time, the images will be first rescaled to preserve the shorter side, and center cropped. If you need to apply random cropping at inference time, set training to True when calling the layer.

Input shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, height, width, channels), in "channels_last" format.

Output shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, target_height, target_width, channels).

Args

height
Integer, the height of the output shape.
width
Integer, the width of the output shape.
seed
Integer. Used to create a random seed.
Expand source code
class RandomCrop(base_layer.Layer):
  """Randomly crop the images to target height and width.

  This layer will crop all the images in the same batch to the same cropping
  location.
  By default, random cropping is only applied during training. At inference
  time, the images will be first rescaled to preserve the shorter side, and
  center cropped. If you need to apply random cropping at inference time,
  set `training` to True when calling the layer.

  Input shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., height, width, channels)`, in `"channels_last"` format.

  Output shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., target_height, target_width, channels)`.

  Args:
    height: Integer, the height of the output shape.
    width: Integer, the width of the output shape.
    seed: Integer. Used to create a random seed.
  """

  def __init__(self, height, width, seed=None, **kwargs):
    self.height = height
    self.width = width
    self.seed = seed
    self._rng = make_generator(self.seed)
    super(RandomCrop, self).__init__(**kwargs)
    base_preprocessing_layer.keras_kpl_gauge.get_cell('RandomCrop').set(True)

  def call(self, inputs, training=True):
    if training is None:
      training = backend.learning_phase()

    inputs = tf.convert_to_tensor(inputs)
    unbatched = inputs.shape.rank == 3

    def random_cropped_inputs():
      """Cropped inputs with stateless random ops."""
      shape = tf.shape(inputs)
      if unbatched:
        crop_size = tf.stack([self.height, self.width, shape[-1]])
      else:
        crop_size = tf.stack([shape[0], self.height, self.width, shape[-1]])
      check = tf.Assert(
          tf.reduce_all(shape >= crop_size),
          [self.height, self.width])
      with tf.control_dependencies([check]):
        limit = shape - crop_size + 1
        offset = stateless_random_ops.stateless_random_uniform(
            tf.shape(shape),
            dtype=crop_size.dtype,
            maxval=crop_size.dtype.max,
            seed=self._rng.make_seeds()[:, 0]) % limit
        return tf.slice(inputs, offset, crop_size)

    # TODO(b/143885775): Share logic with Resize and CenterCrop.
    def resize_and_center_cropped_inputs():
      """Deterministically resize to shorter side and center crop."""
      input_shape = tf.shape(inputs)
      input_height_t = input_shape[H_AXIS]
      input_width_t = input_shape[W_AXIS]
      ratio_cond = (input_height_t / input_width_t > (self.height / self.width))
      # pylint: disable=g-long-lambda
      resized_height = control_flow_util.smart_cond(
          ratio_cond,
          lambda: tf.cast(self.width * input_height_t / input_width_t,
                          input_height_t.dtype), lambda: self.height)
      resized_width = control_flow_util.smart_cond(
          ratio_cond, lambda: self.width,
          lambda: tf.cast(self.height * input_width_t / input_height_t,
                          input_width_t.dtype))
      # pylint: enable=g-long-lambda
      resized_inputs = tf.image.resize(
          images=inputs, size=tf.stack([resized_height, resized_width]))

      img_hd_diff = resized_height - self.height
      img_wd_diff = resized_width - self.width
      bbox_h_start = tf.cast(img_hd_diff / 2, tf.int32)
      bbox_w_start = tf.cast(img_wd_diff / 2, tf.int32)
      if unbatched:
        bbox_begin = tf.stack([bbox_h_start, bbox_w_start, 0])
        bbox_size = tf.stack([self.height, self.width, -1])
      else:
        bbox_begin = tf.stack([0, bbox_h_start, bbox_w_start, 0])
        bbox_size = tf.stack([-1, self.height, self.width, -1])
      outputs = tf.slice(resized_inputs, bbox_begin, bbox_size)
      return outputs

    output = control_flow_util.smart_cond(training, random_cropped_inputs,
                                          resize_and_center_cropped_inputs)
    input_shape = inputs.shape.as_list()
    if unbatched:
      output_shape = [self.height, self.width, input_shape[-1]]
    else:
      output_shape = [input_shape[0], self.height, self.width, input_shape[-1]]
    output.set_shape(output_shape)
    return output

  def compute_output_shape(self, input_shape):
    input_shape = tf.TensorShape(input_shape).as_list()
    input_shape[H_AXIS] = self.height
    input_shape[W_AXIS] = self.width
    return tf.TensorShape(input_shape)

  def get_config(self):
    config = {
        'height': self.height,
        'width': self.width,
        'seed': self.seed,
    }
    base_config = super(RandomCrop, self).get_config()
    return dict(list(base_config.items()) + list(config.items()))

Ancestors

  • Layer
  • tensorflow.python.module.module.Module
  • tensorflow.python.training.tracking.tracking.AutoTrackable
  • tensorflow.python.training.tracking.base.Trackable
  • LayerVersionSelector

Inherited members

class RandomFlip (mode='horizontal_and_vertical', seed=None, **kwargs)

Randomly flip each image horizontally and vertically.

This layer will flip the images based on the mode attribute. During inference time, the output will be identical to input. Call the layer with training=True to flip the input.

Input shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, height, width, channels), in "channels_last" format.

Output shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, height, width, channels), in "channels_last" format.

Attributes

mode
String indicating which flip mode to use. Can be "horizontal", "vertical", or "horizontal_and_vertical". Defaults to "horizontal_and_vertical". "horizontal" is a left-right flip and "vertical" is a top-bottom flip.
seed
Integer. Used to create a random seed.
Expand source code
class RandomFlip(base_layer.Layer):
  """Randomly flip each image horizontally and vertically.

  This layer will flip the images based on the `mode` attribute.
  During inference time, the output will be identical to input. Call the layer
  with `training=True` to flip the input.

  Input shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., height, width, channels)`, in `"channels_last"` format.

  Output shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., height, width, channels)`, in `"channels_last"` format.

  Attributes:
    mode: String indicating which flip mode to use. Can be `"horizontal"`,
      `"vertical"`, or `"horizontal_and_vertical"`. Defaults to
      `"horizontal_and_vertical"`. `"horizontal"` is a left-right flip and
      `"vertical"` is a top-bottom flip.
    seed: Integer. Used to create a random seed.
  """

  def __init__(self,
               mode=HORIZONTAL_AND_VERTICAL,
               seed=None,
               **kwargs):
    super(RandomFlip, self).__init__(**kwargs)
    base_preprocessing_layer.keras_kpl_gauge.get_cell('RandomFlip').set(True)
    self.mode = mode
    if mode == HORIZONTAL:
      self.horizontal = True
      self.vertical = False
    elif mode == VERTICAL:
      self.horizontal = False
      self.vertical = True
    elif mode == HORIZONTAL_AND_VERTICAL:
      self.horizontal = True
      self.vertical = True
    else:
      raise ValueError('RandomFlip layer {name} received an unknown mode '
                       'argument {arg}'.format(name=self.name, arg=mode))
    self.seed = seed
    self._rng = make_generator(self.seed)

  def call(self, inputs, training=True):
    if training is None:
      training = backend.learning_phase()

    def random_flipped_inputs():
      flipped_outputs = inputs
      if self.horizontal:
        flipped_outputs = tf.image.stateless_random_flip_left_right(
            flipped_outputs,
            self._rng.make_seeds()[:, 0])
      if self.vertical:
        flipped_outputs = tf.image.stateless_random_flip_up_down(
            flipped_outputs,
            self._rng.make_seeds()[:, 0])
      return flipped_outputs

    output = control_flow_util.smart_cond(training, random_flipped_inputs,
                                          lambda: inputs)
    output.set_shape(inputs.shape)
    return output

  def compute_output_shape(self, input_shape):
    return input_shape

  def get_config(self):
    config = {
        'mode': self.mode,
        'seed': self.seed,
    }
    base_config = super(RandomFlip, self).get_config()
    return dict(list(base_config.items()) + list(config.items()))

Ancestors

  • Layer
  • tensorflow.python.module.module.Module
  • tensorflow.python.training.tracking.tracking.AutoTrackable
  • tensorflow.python.training.tracking.base.Trackable
  • LayerVersionSelector

Inherited members

class RandomHeight (factor, interpolation='bilinear', seed=None, **kwargs)

Randomly vary the height of a batch of images during training.

Adjusts the height of a batch of images by a random factor. The input should be a 3D (unbatched) or 4D (batched) tensor in the "channels_last" image data format.

By default, this layer is inactive during inference.

Args

factor
A positive float (fraction of original height), or a tuple of size 2 representing lower and upper bound for resizing vertically. When represented as a single float, this value is used for both the upper and lower bound. For instance, factor=(0.2, 0.3) results in an output with height changed by a random amount in the range [20%, 30%]. factor=(-0.2, 0.3) results in an output with height changed by a random amount in the range [-20%, +30%].factor=0.2results in an output with height changed by a random amount in the range[-20%, +20%]`.
interpolation
String, the interpolation method. Defaults to "bilinear". Supports "bilinear", "nearest", "bicubic", "area", "lanczos3", "lanczos5", "gaussian", "mitchellcubic".
seed
Integer. Used to create a random seed.

Input shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, height, width, channels), in "channels_last" format.

Output shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, random_height, width, channels).

Expand source code
class RandomHeight(base_layer.Layer):
  """Randomly vary the height of a batch of images during training.

  Adjusts the height of a batch of images by a random factor. The input
  should be a 3D (unbatched) or 4D (batched) tensor in the `"channels_last"`
  image data format.

  By default, this layer is inactive during inference.

  Args:
    factor: A positive float (fraction of original height), or a tuple of size 2
      representing lower and upper bound for resizing vertically. When
      represented as a single float, this value is used for both the upper and
      lower bound. For instance, `factor=(0.2, 0.3)` results in an output with
      height changed by a random amount in the range `[20%, 30%]`.
      `factor=(-0.2, 0.3)` results in an output with height changed by a random
      amount in the range `[-20%, +30%]. `factor=0.2` results in an output with
      height changed by a random amount in the range `[-20%, +20%]`.
    interpolation: String, the interpolation method. Defaults to `"bilinear"`.
      Supports `"bilinear"`, `"nearest"`, `"bicubic"`, `"area"`,
      `"lanczos3"`, `"lanczos5"`, `"gaussian"`, `"mitchellcubic"`.
    seed: Integer. Used to create a random seed.

  Input shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., height, width, channels)`, in `"channels_last"` format.

  Output shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., random_height, width, channels)`.
  """

  def __init__(self,
               factor,
               interpolation='bilinear',
               seed=None,
               **kwargs):
    self.factor = factor
    if isinstance(factor, (tuple, list)):
      self.height_lower = factor[0]
      self.height_upper = factor[1]
    else:
      self.height_lower = -factor
      self.height_upper = factor

    if self.height_upper < self.height_lower:
      raise ValueError('`factor` cannot have upper bound less than '
                       'lower bound, got {}'.format(factor))
    if self.height_lower < -1. or self.height_upper < -1.:
      raise ValueError('`factor` must have values larger than -1, '
                       'got {}'.format(factor))
    self.interpolation = interpolation
    self._interpolation_method = get_interpolation(interpolation)
    self.seed = seed
    self._rng = make_generator(self.seed)
    super(RandomHeight, self).__init__(**kwargs)
    base_preprocessing_layer.keras_kpl_gauge.get_cell('RandomHeight').set(True)

  def call(self, inputs, training=True):
    if training is None:
      training = backend.learning_phase()

    def random_height_inputs():
      """Inputs height-adjusted with random ops."""
      inputs_shape = tf.shape(inputs)
      img_hd = tf.cast(inputs_shape[H_AXIS], tf.float32)
      img_wd = inputs_shape[W_AXIS]
      height_factor = self._rng.uniform(
          shape=[],
          minval=(1.0 + self.height_lower),
          maxval=(1.0 + self.height_upper))
      adjusted_height = tf.cast(height_factor * img_hd, tf.int32)
      adjusted_size = tf.stack([adjusted_height, img_wd])
      output = tf.image.resize(
          images=inputs, size=adjusted_size, method=self._interpolation_method)
      output_shape = inputs.shape.as_list()
      output_shape[H_AXIS] = None
      output.set_shape(output_shape)
      return output

    return control_flow_util.smart_cond(training, random_height_inputs,
                                        lambda: inputs)

  def compute_output_shape(self, input_shape):
    input_shape = tf.TensorShape(input_shape).as_list()
    input_shape[H_AXIS] = None
    return tf.TensorShape(input_shape)

  def get_config(self):
    config = {
        'factor': self.factor,
        'interpolation': self.interpolation,
        'seed': self.seed,
    }
    base_config = super(RandomHeight, self).get_config()
    return dict(list(base_config.items()) + list(config.items()))

Ancestors

  • Layer
  • tensorflow.python.module.module.Module
  • tensorflow.python.training.tracking.tracking.AutoTrackable
  • tensorflow.python.training.tracking.base.Trackable
  • LayerVersionSelector

Inherited members

class RandomRotation (factor, fill_mode='reflect', interpolation='bilinear', seed=None, fill_value=0.0, **kwargs)

Randomly rotate each image.

By default, random rotations are only applied during training. At inference time, the layer does nothing. If you need to apply random rotations at inference time, set training to True when calling the layer.

Input shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, height, width, channels), in "channels_last" format

Output shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, height, width, channels), in "channels_last" format

Attributes

factor
a float represented as fraction of 2 Pi, or a tuple of size 2 representing lower and upper bound for rotating clockwise and counter-clockwise. A positive values means rotating counter clock-wise, while a negative value means clock-wise. When represented as a single float, this value is used for both the upper and lower bound. For instance, factor=(-0.2, 0.3) results in an output rotation by a random amount in the range [-20% * 2pi, 30% * 2pi]. factor=0.2 results in an output rotating by a random amount in the range [-20% * 2pi, 20% * 2pi].
fill_mode
Points outside the boundaries of the input are filled according to the given mode (one of {"constant", "reflect", "wrap", "nearest"}). - reflect: (d c b a | a b c d | d c b a) The input is extended by reflecting about the edge of the last pixel. - constant: (k k k k | a b c d | k k k k) The input is extended by filling all values beyond the edge with the same constant value k = 0. - wrap: (a b c d | a b c d | a b c d) The input is extended by wrapping around to the opposite edge. - nearest: (a a a a | a b c d | d d d d) The input is extended by the nearest pixel.
interpolation
Interpolation mode. Supported values: "nearest", "bilinear".
seed
Integer. Used to create a random seed.
fill_value
a float represents the value to be filled outside the boundaries when fill_mode="constant".
Expand source code
class RandomRotation(base_layer.Layer):
  """Randomly rotate each image.

  By default, random rotations are only applied during training.
  At inference time, the layer does nothing. If you need to apply random
  rotations at inference time, set `training` to True when calling the layer.

  Input shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., height, width, channels)`, in `"channels_last"` format

  Output shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., height, width, channels)`, in `"channels_last"` format

  Attributes:
    factor: a float represented as fraction of 2 Pi, or a tuple of size 2
      representing lower and upper bound for rotating clockwise and
      counter-clockwise. A positive values means rotating counter clock-wise,
      while a negative value means clock-wise. When represented as a single
      float, this value is used for both the upper and lower bound. For
      instance, `factor=(-0.2, 0.3)` results in an output rotation by a random
      amount in the range `[-20% * 2pi, 30% * 2pi]`. `factor=0.2` results in an
      output rotating by a random amount in the range `[-20% * 2pi, 20% * 2pi]`.
    fill_mode: Points outside the boundaries of the input are filled according
      to the given mode (one of `{"constant", "reflect", "wrap", "nearest"}`).
      - *reflect*: `(d c b a | a b c d | d c b a)` The input is extended by
        reflecting about the edge of the last pixel.
      - *constant*: `(k k k k | a b c d | k k k k)` The input is extended by
        filling all values beyond the edge with the same constant value k = 0.
      - *wrap*: `(a b c d | a b c d | a b c d)` The input is extended by
        wrapping around to the opposite edge.
      - *nearest*: `(a a a a | a b c d | d d d d)` The input is extended by the
        nearest pixel.
    interpolation: Interpolation mode. Supported values: `"nearest"`,
      `"bilinear"`.
    seed: Integer. Used to create a random seed.
    fill_value: a float represents the value to be filled outside the boundaries
      when `fill_mode="constant"`.
  """

  def __init__(self,
               factor,
               fill_mode='reflect',
               interpolation='bilinear',
               seed=None,
               fill_value=0.0,
               **kwargs):
    self.factor = factor
    if isinstance(factor, (tuple, list)):
      self.lower = factor[0]
      self.upper = factor[1]
    else:
      self.lower = -factor
      self.upper = factor
    if self.upper < self.lower:
      raise ValueError('Factor cannot have negative values, '
                       'got {}'.format(factor))
    check_fill_mode_and_interpolation(fill_mode, interpolation)
    self.fill_mode = fill_mode
    self.fill_value = fill_value
    self.interpolation = interpolation
    self.seed = seed
    self._rng = make_generator(self.seed)
    super(RandomRotation, self).__init__(**kwargs)
    base_preprocessing_layer.keras_kpl_gauge.get_cell('RandomRotation').set(
        True)

  def call(self, inputs, training=True):
    if training is None:
      training = backend.learning_phase()

    inputs = tf.convert_to_tensor(inputs)
    original_shape = inputs.shape
    unbatched = inputs.shape.rank == 3
    # The transform op only accepts rank 4 inputs, so if we have an unbatched
    # image, we need to temporarily expand dims to a batch.
    if unbatched:
      inputs = tf.expand_dims(inputs, 0)

    def random_rotated_inputs():
      """Rotated inputs with random ops."""
      inputs_shape = tf.shape(inputs)
      batch_size = inputs_shape[0]
      img_hd = tf.cast(inputs_shape[H_AXIS], tf.float32)
      img_wd = tf.cast(inputs_shape[W_AXIS], tf.float32)
      min_angle = self.lower * 2. * np.pi
      max_angle = self.upper * 2. * np.pi
      angles = self._rng.uniform(
          shape=[batch_size], minval=min_angle, maxval=max_angle)
      return transform(
          inputs,
          get_rotation_matrix(angles, img_hd, img_wd),
          fill_mode=self.fill_mode,
          fill_value=self.fill_value,
          interpolation=self.interpolation)

    output = control_flow_util.smart_cond(training, random_rotated_inputs,
                                          lambda: inputs)
    if unbatched:
      output = tf.squeeze(output, 0)
    output.set_shape(original_shape)
    return output

  def compute_output_shape(self, input_shape):
    return input_shape

  def get_config(self):
    config = {
        'factor': self.factor,
        'fill_mode': self.fill_mode,
        'fill_value': self.fill_value,
        'interpolation': self.interpolation,
        'seed': self.seed,
    }
    base_config = super(RandomRotation, self).get_config()
    return dict(list(base_config.items()) + list(config.items()))

Ancestors

  • Layer
  • tensorflow.python.module.module.Module
  • tensorflow.python.training.tracking.tracking.AutoTrackable
  • tensorflow.python.training.tracking.base.Trackable
  • LayerVersionSelector

Inherited members

class RandomTranslation (height_factor, width_factor, fill_mode='reflect', interpolation='bilinear', seed=None, fill_value=0.0, **kwargs)

Randomly translate each image during training.

Args

height_factor
a float represented as fraction of value, or a tuple of size 2 representing lower and upper bound for shifting vertically. A negative value means shifting image up, while a positive value means shifting image down. When represented as a single positive float, this value is used for both the upper and lower bound. For instance, height_factor=(-0.2, 0.3) results in an output shifted by a random amount in the range [-20%, +30%]. height_factor=0.2 results in an output height shifted by a random amount in the range [-20%, +20%].
width_factor
a float represented as fraction of value, or a tuple of size 2 representing lower and upper bound for shifting horizontally. A negative value means shifting image left, while a positive value means shifting image right. When represented as a single positive float, this value is used for both the upper and lower bound. For instance, width_factor=(-0.2, 0.3) results in an output shifted left by 20%, and shifted right by 30%. width_factor=0.2 results in an output height shifted left or right by 20%.
fill_mode
Points outside the boundaries of the input are filled according to the given mode (one of {"constant", "reflect", "wrap", "nearest"}). - reflect: (d c b a | a b c d | d c b a) The input is extended by reflecting about the edge of the last pixel. - constant: (k k k k | a b c d | k k k k) The input is extended by filling all values beyond the edge with the same constant value k = 0. - wrap: (a b c d | a b c d | a b c d) The input is extended by wrapping around to the opposite edge. - nearest: (a a a a | a b c d | d d d d) The input is extended by the nearest pixel.
interpolation
Interpolation mode. Supported values: "nearest", "bilinear".
seed
Integer. Used to create a random seed.
fill_value
a float represents the value to be filled outside the boundaries when fill_mode="constant".

Input shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, height, width, channels), in "channels_last" format.

Output shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, height, width, channels), in "channels_last" format.

Expand source code
class RandomTranslation(base_layer.Layer):
  """Randomly translate each image during training.

  Args:
    height_factor: a float represented as fraction of value, or a tuple of size
      2 representing lower and upper bound for shifting vertically. A negative
      value means shifting image up, while a positive value means shifting image
      down. When represented as a single positive float, this value is used for
      both the upper and lower bound. For instance, `height_factor=(-0.2, 0.3)`
      results in an output shifted by a random amount in the range
      `[-20%, +30%]`.
      `height_factor=0.2` results in an output height shifted by a random amount
      in the range `[-20%, +20%]`.
    width_factor: a float represented as fraction of value, or a tuple of size 2
      representing lower and upper bound for shifting horizontally. A negative
      value means shifting image left, while a positive value means shifting
      image right. When represented as a single positive float, this value is
      used for both the upper and lower bound. For instance,
      `width_factor=(-0.2, 0.3)` results in an output shifted left by 20%, and
      shifted right by 30%. `width_factor=0.2` results in an output height
      shifted left or right by 20%.
    fill_mode: Points outside the boundaries of the input are filled according
      to the given mode (one of `{"constant", "reflect", "wrap", "nearest"}`).
      - *reflect*: `(d c b a | a b c d | d c b a)` The input is extended by
        reflecting about the edge of the last pixel.
      - *constant*: `(k k k k | a b c d | k k k k)` The input is extended by
        filling all values beyond the edge with the same constant value k = 0.
      - *wrap*: `(a b c d | a b c d | a b c d)` The input is extended by
        wrapping around to the opposite edge.
      - *nearest*: `(a a a a | a b c d | d d d d)` The input is extended by the
        nearest pixel.
    interpolation: Interpolation mode. Supported values: `"nearest"`,
      `"bilinear"`.
    seed: Integer. Used to create a random seed.
    fill_value: a float represents the value to be filled outside the boundaries
      when `fill_mode="constant"`.

  Input shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., height, width, channels)`,  in `"channels_last"` format.

  Output shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., height, width, channels)`,  in `"channels_last"` format.
  """

  def __init__(self,
               height_factor,
               width_factor,
               fill_mode='reflect',
               interpolation='bilinear',
               seed=None,
               fill_value=0.0,
               **kwargs):
    self.height_factor = height_factor
    if isinstance(height_factor, (tuple, list)):
      self.height_lower = height_factor[0]
      self.height_upper = height_factor[1]
    else:
      self.height_lower = -height_factor
      self.height_upper = height_factor
    if self.height_upper < self.height_lower:
      raise ValueError('`height_factor` cannot have upper bound less than '
                       'lower bound, got {}'.format(height_factor))
    if abs(self.height_lower) > 1. or abs(self.height_upper) > 1.:
      raise ValueError('`height_factor` must have values between [-1, 1], '
                       'got {}'.format(height_factor))

    self.width_factor = width_factor
    if isinstance(width_factor, (tuple, list)):
      self.width_lower = width_factor[0]
      self.width_upper = width_factor[1]
    else:
      self.width_lower = -width_factor
      self.width_upper = width_factor
    if self.width_upper < self.width_lower:
      raise ValueError('`width_factor` cannot have upper bound less than '
                       'lower bound, got {}'.format(width_factor))
    if abs(self.width_lower) > 1. or abs(self.width_upper) > 1.:
      raise ValueError('`width_factor` must have values between [-1, 1], '
                       'got {}'.format(width_factor))

    check_fill_mode_and_interpolation(fill_mode, interpolation)

    self.fill_mode = fill_mode
    self.fill_value = fill_value
    self.interpolation = interpolation
    self.seed = seed
    self._rng = make_generator(self.seed)
    super(RandomTranslation, self).__init__(**kwargs)
    base_preprocessing_layer.keras_kpl_gauge.get_cell('RandomTranslation').set(
        True)

  def call(self, inputs, training=True):
    if training is None:
      training = backend.learning_phase()

    inputs = tf.convert_to_tensor(inputs)
    original_shape = inputs.shape
    unbatched = inputs.shape.rank == 3
    # The transform op only accepts rank 4 inputs, so if we have an unbatched
    # image, we need to temporarily expand dims to a batch.
    if unbatched:
      inputs = tf.expand_dims(inputs, 0)

    def random_translated_inputs():
      """Translated inputs with random ops."""
      inputs_shape = tf.shape(inputs)
      batch_size = inputs_shape[0]
      img_hd = tf.cast(inputs_shape[H_AXIS], tf.float32)
      img_wd = tf.cast(inputs_shape[W_AXIS], tf.float32)
      height_translate = self._rng.uniform(
          shape=[batch_size, 1],
          minval=self.height_lower,
          maxval=self.height_upper,
          dtype=tf.float32)
      height_translate = height_translate * img_hd
      width_translate = self._rng.uniform(
          shape=[batch_size, 1],
          minval=self.width_lower,
          maxval=self.width_upper,
          dtype=tf.float32)
      width_translate = width_translate * img_wd
      translations = tf.cast(
          tf.concat([width_translate, height_translate], axis=1),
          dtype=tf.float32)
      return transform(
          inputs,
          get_translation_matrix(translations),
          interpolation=self.interpolation,
          fill_mode=self.fill_mode,
          fill_value=self.fill_value)

    output = control_flow_util.smart_cond(training, random_translated_inputs,
                                          lambda: inputs)
    if unbatched:
      output = tf.squeeze(output, 0)
    output.set_shape(original_shape)
    return output

  def compute_output_shape(self, input_shape):
    return input_shape

  def get_config(self):
    config = {
        'height_factor': self.height_factor,
        'width_factor': self.width_factor,
        'fill_mode': self.fill_mode,
        'fill_value': self.fill_value,
        'interpolation': self.interpolation,
        'seed': self.seed,
    }
    base_config = super(RandomTranslation, self).get_config()
    return dict(list(base_config.items()) + list(config.items()))

Ancestors

  • Layer
  • tensorflow.python.module.module.Module
  • tensorflow.python.training.tracking.tracking.AutoTrackable
  • tensorflow.python.training.tracking.base.Trackable
  • LayerVersionSelector

Inherited members

class RandomWidth (factor, interpolation='bilinear', seed=None, **kwargs)

Randomly vary the width of a batch of images during training.

Adjusts the width of a batch of images by a random factor. The input should be a 3D (unbatched) or 4D (batched) tensor in the "channels_last" image data format.

By default, this layer is inactive during inference.

Args

factor
A positive float (fraction of original height), or a tuple of size 2 representing lower and upper bound for resizing vertically. When represented as a single float, this value is used for both the upper and lower bound. For instance, factor=(0.2, 0.3) results in an output with width changed by a random amount in the range [20%, 30%]. factor=(-0.2, 0.3) results in an output with width changed by a random amount in the range [-20%, +30%]. factor=0.2 results in an output with width changed by a random amount in the range [-20%, +20%].
interpolation
String, the interpolation method. Defaults to bilinear. Supports "bilinear", "nearest", "bicubic", "area", "lanczos3", "lanczos5", "gaussian", "mitchellcubic".
seed
Integer. Used to create a random seed.

Input shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, height, width, channels), in "channels_last" format.

Output shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, random_height, width, channels).

Expand source code
class RandomWidth(base_layer.Layer):
  """Randomly vary the width of a batch of images during training.

  Adjusts the width of a batch of images by a random factor. The input
  should be a 3D (unbatched) or 4D (batched) tensor in the `"channels_last"`
  image data format.

  By default, this layer is inactive during inference.

  Args:
    factor: A positive float (fraction of original height), or a tuple of size 2
      representing lower and upper bound for resizing vertically. When
      represented as a single float, this value is used for both the upper and
      lower bound. For instance, `factor=(0.2, 0.3)` results in an output with
      width changed by a random amount in the range `[20%, 30%]`. `factor=(-0.2,
      0.3)` results in an output with width changed by a random amount in the
      range `[-20%, +30%]`. `factor=0.2` results in an output with width changed
      by a random amount in the range `[-20%, +20%]`.
    interpolation: String, the interpolation method. Defaults to `bilinear`.
      Supports `"bilinear"`, `"nearest"`, `"bicubic"`, `"area"`, `"lanczos3"`,
      `"lanczos5"`, `"gaussian"`, `"mitchellcubic"`.
    seed: Integer. Used to create a random seed.

  Input shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., height, width, channels)`, in `"channels_last"` format.

  Output shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., random_height, width, channels)`.
  """

  def __init__(self,
               factor,
               interpolation='bilinear',
               seed=None,
               **kwargs):
    self.factor = factor
    if isinstance(factor, (tuple, list)):
      self.width_lower = factor[0]
      self.width_upper = factor[1]
    else:
      self.width_lower = -factor
      self.width_upper = factor
    if self.width_upper < self.width_lower:
      raise ValueError('`factor` cannot have upper bound less than '
                       'lower bound, got {}'.format(factor))
    if self.width_lower < -1. or self.width_upper < -1.:
      raise ValueError('`factor` must have values larger than -1, '
                       'got {}'.format(factor))
    self.interpolation = interpolation
    self._interpolation_method = get_interpolation(interpolation)
    self.seed = seed
    self._rng = make_generator(self.seed)
    super(RandomWidth, self).__init__(**kwargs)
    base_preprocessing_layer.keras_kpl_gauge.get_cell('RandomWidth').set(True)

  def call(self, inputs, training=True):
    if training is None:
      training = backend.learning_phase()

    def random_width_inputs():
      """Inputs width-adjusted with random ops."""
      inputs_shape = tf.shape(inputs)
      img_hd = inputs_shape[H_AXIS]
      img_wd = tf.cast(inputs_shape[W_AXIS], tf.float32)
      width_factor = self._rng.uniform(
          shape=[],
          minval=(1.0 + self.width_lower),
          maxval=(1.0 + self.width_upper))
      adjusted_width = tf.cast(width_factor * img_wd, tf.int32)
      adjusted_size = tf.stack([img_hd, adjusted_width])
      output = tf.image.resize(
          images=inputs, size=adjusted_size, method=self._interpolation_method)
      output_shape = inputs.shape.as_list()
      output_shape[W_AXIS] = None
      output.set_shape(output_shape)
      return output

    return control_flow_util.smart_cond(training, random_width_inputs,
                                        lambda: inputs)

  def compute_output_shape(self, input_shape):
    input_shape = tf.TensorShape(input_shape).as_list()
    input_shape[W_AXIS] = None
    return tf.TensorShape(input_shape)

  def get_config(self):
    config = {
        'factor': self.factor,
        'interpolation': self.interpolation,
        'seed': self.seed,
    }
    base_config = super(RandomWidth, self).get_config()
    return dict(list(base_config.items()) + list(config.items()))

Ancestors

  • Layer
  • tensorflow.python.module.module.Module
  • tensorflow.python.training.tracking.tracking.AutoTrackable
  • tensorflow.python.training.tracking.base.Trackable
  • LayerVersionSelector

Inherited members

class RandomZoom (height_factor, width_factor=None, fill_mode='reflect', interpolation='bilinear', seed=None, fill_value=0.0, **kwargs)

Randomly zoom each image during training.

Args

height_factor
a float represented as fraction of value, or a tuple of size 2 representing lower and upper bound for zooming vertically. When represented as a single float, this value is used for both the upper and lower bound. A positive value means zooming out, while a negative value means zooming in. For instance, height_factor=(0.2, 0.3) result in an output zoomed out by a random amount in the range [+20%, +30%]. height_factor=(-0.3, -0.2) result in an output zoomed in by a random amount in the range [+20%, +30%].
width_factor
a float represented as fraction of value, or a tuple of size 2 representing lower and upper bound for zooming horizontally. When represented as a single float, this value is used for both the upper and lower bound. For instance, width_factor=(0.2, 0.3) result in an output zooming out between 20% to 30%. width_factor=(-0.3, -0.2) result in an output zooming in between 20% to 30%. Defaults to None, i.e., zooming vertical and horizontal directions by preserving the aspect ratio.
fill_mode
Points outside the boundaries of the input are filled according to the given mode (one of {"constant", "reflect", "wrap", "nearest"}). - reflect: (d c b a | a b c d | d c b a) The input is extended by reflecting about the edge of the last pixel. - constant: (k k k k | a b c d | k k k k) The input is extended by filling all values beyond the edge with the same constant value k = 0. - wrap: (a b c d | a b c d | a b c d) The input is extended by wrapping around to the opposite edge. - nearest: (a a a a | a b c d | d d d d) The input is extended by the nearest pixel.
interpolation
Interpolation mode. Supported values: "nearest", "bilinear".
seed
Integer. Used to create a random seed.
fill_value
a float represents the value to be filled outside the boundaries when fill_mode="constant".

Example:

>>> input_img = np.random.random((32, 224, 224, 3))
>>> layer = tf.keras.layers.RandomZoom(.5, .2)
>>> out_img = layer(input_img)
>>> out_img.shape
TensorShape([32, 224, 224, 3])

Input shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, height, width, channels), in "channels_last" format.

Output shape: 3D (unbatched) or 4D (batched) tensor with shape: (…, height, width, channels), in "channels_last" format.

Expand source code
class RandomZoom(base_layer.Layer):
  """Randomly zoom each image during training.

  Args:
    height_factor: a float represented as fraction of value, or a tuple of size
      2 representing lower and upper bound for zooming vertically. When
      represented as a single float, this value is used for both the upper and
      lower bound. A positive value means zooming out, while a negative value
      means zooming in. For instance, `height_factor=(0.2, 0.3)` result in an
      output zoomed out by a random amount in the range `[+20%, +30%]`.
      `height_factor=(-0.3, -0.2)` result in an output zoomed in by a random
      amount in the range `[+20%, +30%]`.
    width_factor: a float represented as fraction of value, or a tuple of size 2
      representing lower and upper bound for zooming horizontally. When
      represented as a single float, this value is used for both the upper and
      lower bound. For instance, `width_factor=(0.2, 0.3)` result in an output
      zooming out between 20% to 30%. `width_factor=(-0.3, -0.2)` result in an
      output zooming in between 20% to 30%. Defaults to `None`, i.e., zooming
      vertical and horizontal directions by preserving the aspect ratio.
    fill_mode: Points outside the boundaries of the input are filled according
      to the given mode (one of `{"constant", "reflect", "wrap", "nearest"}`).
      - *reflect*: `(d c b a | a b c d | d c b a)` The input is extended by
        reflecting about the edge of the last pixel.
      - *constant*: `(k k k k | a b c d | k k k k)` The input is extended by
        filling all values beyond the edge with the same constant value k = 0.
      - *wrap*: `(a b c d | a b c d | a b c d)` The input is extended by
        wrapping around to the opposite edge.
      - *nearest*: `(a a a a | a b c d | d d d d)` The input is extended by the
        nearest pixel.
    interpolation: Interpolation mode. Supported values: `"nearest"`,
      `"bilinear"`.
    seed: Integer. Used to create a random seed.
    fill_value: a float represents the value to be filled outside the boundaries
      when `fill_mode="constant"`.

  Example:

  >>> input_img = np.random.random((32, 224, 224, 3))
  >>> layer = tf.keras.layers.RandomZoom(.5, .2)
  >>> out_img = layer(input_img)
  >>> out_img.shape
  TensorShape([32, 224, 224, 3])

  Input shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., height, width, channels)`, in `"channels_last"` format.

  Output shape:
    3D (unbatched) or 4D (batched) tensor with shape:
    `(..., height, width, channels)`, in `"channels_last"` format.
  """

  def __init__(self,
               height_factor,
               width_factor=None,
               fill_mode='reflect',
               interpolation='bilinear',
               seed=None,
               fill_value=0.0,
               **kwargs):
    self.height_factor = height_factor
    if isinstance(height_factor, (tuple, list)):
      self.height_lower = height_factor[0]
      self.height_upper = height_factor[1]
    else:
      self.height_lower = -height_factor
      self.height_upper = height_factor

    if abs(self.height_lower) > 1. or abs(self.height_upper) > 1.:
      raise ValueError('`height_factor` must have values between [-1, 1], '
                       'got {}'.format(height_factor))

    self.width_factor = width_factor
    if width_factor is not None:
      if isinstance(width_factor, (tuple, list)):
        self.width_lower = width_factor[0]
        self.width_upper = width_factor[1]
      else:
        self.width_lower = -width_factor  # pylint: disable=invalid-unary-operand-type
        self.width_upper = width_factor

      if self.width_lower < -1. or self.width_upper < -1.:
        raise ValueError('`width_factor` must have values larger than -1, '
                         'got {}'.format(width_factor))

    check_fill_mode_and_interpolation(fill_mode, interpolation)

    self.fill_mode = fill_mode
    self.fill_value = fill_value
    self.interpolation = interpolation
    self.seed = seed
    self._rng = make_generator(self.seed)
    super(RandomZoom, self).__init__(**kwargs)
    base_preprocessing_layer.keras_kpl_gauge.get_cell('RandomZoom').set(True)

  def call(self, inputs, training=True):
    if training is None:
      training = backend.learning_phase()

    inputs = tf.convert_to_tensor(inputs)
    original_shape = inputs.shape
    unbatched = inputs.shape.rank == 3
    # The transform op only accepts rank 4 inputs, so if we have an unbatched
    # image, we need to temporarily expand dims to a batch.
    if unbatched:
      inputs = tf.expand_dims(inputs, 0)

    def random_zoomed_inputs():
      """Zoomed inputs with random ops."""
      inputs_shape = tf.shape(inputs)
      batch_size = inputs_shape[0]
      img_hd = tf.cast(inputs_shape[H_AXIS], tf.float32)
      img_wd = tf.cast(inputs_shape[W_AXIS], tf.float32)
      height_zoom = self._rng.uniform(
          shape=[batch_size, 1],
          minval=1. + self.height_lower,
          maxval=1. + self.height_upper)
      if self.width_factor is not None:
        width_zoom = self._rng.uniform(
            shape=[batch_size, 1],
            minval=1. + self.width_lower,
            maxval=1. + self.width_upper)
      else:
        width_zoom = height_zoom
      zooms = tf.cast(
          tf.concat([width_zoom, height_zoom], axis=1),
          dtype=tf.float32)
      return transform(
          inputs,
          get_zoom_matrix(zooms, img_hd, img_wd),
          fill_mode=self.fill_mode,
          fill_value=self.fill_value,
          interpolation=self.interpolation)

    output = control_flow_util.smart_cond(training, random_zoomed_inputs,
                                          lambda: inputs)
    if unbatched:
      output = tf.squeeze(output, 0)
    output.set_shape(original_shape)
    return output

  def compute_output_shape(self, input_shape):
    return input_shape

  def get_config(self):
    config = {
        'height_factor': self.height_factor,
        'width_factor': self.width_factor,
        'fill_mode': self.fill_mode,
        'fill_value': self.fill_value,
        'interpolation': self.interpolation,
        'seed': self.seed,
    }
    base_config = super(RandomZoom, self).get_config()
    return dict(list(base_config.items()) + list(config.items()))

Ancestors

  • Layer
  • tensorflow.python.module.module.Module
  • tensorflow.python.training.tracking.tracking.AutoTrackable
  • tensorflow.python.training.tracking.base.Trackable
  • LayerVersionSelector

Inherited members

class Rescaling (scale, offset=0.0, **kwargs)

Multiply inputs by scale and adds offset.

For instance:

  1. To rescale an input in the [0, 255] range to be in the [0, 1] range, you would pass scale=1./255.

  2. To rescale an input in the [0, 255] range to be in the [-1, 1] range, you would pass scale=1./127.5, offset=-1.

The rescaling is applied both during training and inference.

Input shape: Arbitrary.

Output shape: Same as input.

Args

scale
Float, the scale to apply to the inputs.
offset
Float, the offset to apply to the inputs.
Expand source code
class Rescaling(base_layer.Layer):
  """Multiply inputs by `scale` and adds `offset`.

  For instance:

  1. To rescale an input in the `[0, 255]` range
  to be in the `[0, 1]` range, you would pass `scale=1./255`.

  2. To rescale an input in the `[0, 255]` range to be in the `[-1, 1]` range,
  you would pass `scale=1./127.5, offset=-1`.

  The rescaling is applied both during training and inference.

  Input shape:
    Arbitrary.

  Output shape:
    Same as input.

  Args:
    scale: Float, the scale to apply to the inputs.
    offset: Float, the offset to apply to the inputs.
  """

  def __init__(self, scale, offset=0., **kwargs):
    self.scale = scale
    self.offset = offset
    super(Rescaling, self).__init__(**kwargs)
    base_preprocessing_layer.keras_kpl_gauge.get_cell('Rescaling').set(True)

  def call(self, inputs):
    dtype = self._compute_dtype
    scale = tf.cast(self.scale, dtype)
    offset = tf.cast(self.offset, dtype)
    return tf.cast(inputs, dtype) * scale + offset

  def compute_output_shape(self, input_shape):
    return input_shape

  def get_config(self):
    config = {
        'scale': self.scale,
        'offset': self.offset,
    }
    base_config = super(Rescaling, self).get_config()
    return dict(list(base_config.items()) + list(config.items()))

Ancestors

  • Layer
  • tensorflow.python.module.module.Module
  • tensorflow.python.training.tracking.tracking.AutoTrackable
  • tensorflow.python.training.tracking.base.Trackable
  • LayerVersionSelector

Inherited members

class Resizing (height, width, interpolation='bilinear', crop_to_aspect_ratio=False, **kwargs)

Image resizing layer.

Resize the batched image input to target height and width. The input should be a 4D (batched) or 3D (unbatched) tensor in "channels_last" format.

Args

height
Integer, the height of the output shape.
width
Integer, the width of the output shape.
interpolation
String, the interpolation method. Defaults to "bilinear". Supports "bilinear", "nearest", "bicubic", "area", "lanczos3", "lanczos5", "gaussian", "mitchellcubic".
crop_to_aspect_ratio
If True, resize the images without aspect ratio distortion. When the original aspect ratio differs from the target aspect ratio, the output image will be cropped so as to return the largest possible window in the image (of size (height, width)) that matches the target aspect ratio. By default (crop_to_aspect_ratio=False), aspect ratio may not be preserved.
Expand source code
class Resizing(base_layer.Layer):
  """Image resizing layer.

  Resize the batched image input to target height and width. The input should
  be a 4D (batched) or 3D (unbatched) tensor in `"channels_last"` format.

  Args:
    height: Integer, the height of the output shape.
    width: Integer, the width of the output shape.
    interpolation: String, the interpolation method. Defaults to `"bilinear"`.
      Supports `"bilinear"`, `"nearest"`, `"bicubic"`, `"area"`, `"lanczos3"`,
      `"lanczos5"`, `"gaussian"`, `"mitchellcubic"`.
    crop_to_aspect_ratio: If True, resize the images without aspect
      ratio distortion. When the original aspect ratio differs from the target
      aspect ratio, the output image will be cropped so as to return the largest
      possible window in the image (of size `(height, width)`) that matches
      the target aspect ratio. By default (`crop_to_aspect_ratio=False`),
      aspect ratio may not be preserved.
  """

  def __init__(self,
               height,
               width,
               interpolation='bilinear',
               crop_to_aspect_ratio=False,
               **kwargs):
    self.target_height = height
    self.target_width = width
    self.interpolation = interpolation
    self.crop_to_aspect_ratio = crop_to_aspect_ratio
    self._interpolation_method = get_interpolation(interpolation)
    super(Resizing, self).__init__(**kwargs)
    base_preprocessing_layer.keras_kpl_gauge.get_cell('Resizing').set(True)

  def call(self, inputs):
    if self.crop_to_aspect_ratio:
      outputs = image_preprocessing.smart_resize(
          inputs,
          size=[self.target_height, self.target_width],
          interpolation=self._interpolation_method)
    else:
      outputs = tf.image.resize(
          inputs,
          size=[self.target_height, self.target_width],
          method=self._interpolation_method)
    return outputs

  def compute_output_shape(self, input_shape):
    input_shape = tf.TensorShape(input_shape).as_list()
    input_shape[H_AXIS] = self.target_height
    input_shape[W_AXIS] = self.target_width
    return tf.TensorShape(input_shape)

  def get_config(self):
    config = {
        'height': self.target_height,
        'width': self.target_width,
        'interpolation': self.interpolation,
        'crop_to_aspect_ratio': self.crop_to_aspect_ratio,
    }
    base_config = super(Resizing, self).get_config()
    return dict(list(base_config.items()) + list(config.items()))

Ancestors

  • Layer
  • tensorflow.python.module.module.Module
  • tensorflow.python.training.tracking.tracking.AutoTrackable
  • tensorflow.python.training.tracking.base.Trackable
  • LayerVersionSelector

Inherited members