Ejemplo n.º 1
0
def _fill_and_one_pad_stride(stride, n):
    """Expands the provided stride to size n and pads it with 1s."""
    try:
        return (1, ) + _fill_shape(stride, n) + (1, )
    except TypeError:
        raise base.IncompatibleShapeError(
            "stride is {} ({}), must be either an integer or an iterable of "
            "integers of size {}".format(stride, type(stride), n))
Ejemplo n.º 2
0
    def _build(self, inputs):
        """Connects the Linear module into the graph, with input Tensor `inputs`.

    If this is not the first time the module has been connected to the graph,
    the Tensor provided here must have the same final dimension, in order for
    the existing variables to be the correct size for the multiplication. The
    batch size may differ for each connection.

    Args:
      inputs: A 2D Tensor of size [batch_size, input_size].

    Returns:
      A 2D Tensor of size [batch_size, output_size].

    Raises:
      base.IncompatibleShapeError: If the input is not a 2-D `Tensor` with
          the size of the second dimension specified.
      base.IncompatibleShapeError: If reconnecting an already connected module
          into the graph, and the shape of the input is not compatible with
          previous inputs.
    """
        input_shape = tuple(inputs.get_shape().as_list())

        if len(input_shape) != 2:
            raise base.IncompatibleShapeError(
                "{}: rank of shape must be 2 not: {}".format(
                    self.name, len(input_shape)))

        if input_shape[1] is None:
            raise base.IncompatibleShapeError(
                "{}: Input size must be specified at module build time".format(
                    self.name))

        if self._input_shape is not None and input_shape[
                1] != self._input_shape[1]:
            raise base.IncompatibleShapeError(
                "{}: Input shape must be [batch_size, {}] not: [batch_size, {}]"
                .format(self.name, self._input_shape[1], input_shape[1]))

        self._input_shape = input_shape

        if "w" not in self._initializers:
            self._initializers["w"] = create_linear_initializer(
                self._input_shape[1])

        if "b" not in self._initializers and self._use_bias:
            self._initializers["b"] = create_linear_initializer(
                self._input_shape[1])

        weight_shape = (self._input_shape[1], self.output_size)
        dtype = inputs.dtype
        self._w = tf.get_variable("w",
                                  shape=weight_shape,
                                  dtype=dtype,
                                  initializer=self._initializers["w"],
                                  partitioner=self._partitioners.get(
                                      "w", None))
        outputs = tf.matmul(inputs, self._w)

        if self._use_bias:
            bias_shape = (self.output_size, )
            self._b = tf.get_variable("b",
                                      shape=bias_shape,
                                      dtype=dtype,
                                      initializer=self._initializers["b"],
                                      partitioner=self._partitioners.get(
                                          "b", None))
            outputs += self._b

        return outputs
Ejemplo n.º 3
0
  def _build(self, input_batch, is_training=True, test_local_stats=True):
    """Connects the BatchNorm module into the graph.

    Args:
      input_batch: A Tensor of arbitrary dimension. By default, the final
        dimension is not reduced over when computing the minibatch statistics.
      is_training: A boolean to indicate if the module should be connected in
        training mode, meaning the moving averages are updated. By default
        `True`. Can be a Tensor.
      test_local_stats: A boolean to indicate if local batch statistics should
        be used when `is_training=False`. If not, moving averages are used.
        By default `True`. Can be a Tensor.

    Returns:
      A tensor with the same shape as `input_batch`.

    Raises:
      base.IncompatibleShapeError: If `reduction_indices` is not valid for the
        input shape or has negative entries.
      base.NotSupportedError: If `input_batch` has data type of `tf.float16`.
    """
    input_shape = input_batch.get_shape()

    if self._reduction_indices is not None:
      if len(self._reduction_indices) > len(input_shape):
        raise base.IncompatibleShapeError(
            "Too many reduction indices specified.")

      if max(self._reduction_indices) >= len(input_shape):
        raise base.IncompatibleShapeError(
            "Reduction index too large for input shape.")

      if min(self._reduction_indices) < 0:
        raise base.IncompatibleShapeError(
            "Reduction indeces must be non-negative.")

      reduction_indices = self._reduction_indices
    else:
      # Reduce over all dimensions except the last.
      reduction_indices = range(len(input_shape))[:-1]

    if input_batch.dtype == tf.float16:
      raise base.NotSupportedError(
          "BatchNorm does not support `tf.float16`, insufficient "
          "precision for calculating sufficient statistics.")

    self._mean_shape = input_batch.get_shape().as_list()
    for index in reduction_indices:
      self._mean_shape[index] = 1

    use_batch_stats = is_training | test_local_stats

    # Use the legacy moving second moment if the flag is set.
    if self._use_legacy_moving_second_moment:
      tf.logging.warning(
          "nn.BatchNorm `use_legacy_second_moment=True` is deprecated.")

      mean, variance, second_moment = self._build_statistics_second_moment(
          input_batch,
          reduction_indices,
          use_batch_stats)

      self._build_update_ops_second_moment(mean, second_moment, is_training)
    else:
      mean, variance = self._build_statistics_variance(
          input_batch,
          reduction_indices,
          use_batch_stats)

      self._build_update_ops_variance(mean, variance, is_training)

    # Set up optional scale and offset factors.
    if self._offset:
      self._set_default_initializer(self.BETA)
      self._beta = tf.get_variable(
          self.BETA,
          shape=self._mean_shape,
          initializer=self._initializers[self.BETA])
    else:
      self._beta = None

    if self._scale:
      self._set_default_initializer(self.GAMMA)
      self._gamma = tf.get_variable(
          self.GAMMA,
          shape=self._mean_shape,
          initializer=self._initializers[self.GAMMA])
    else:
      self._gamma = None

    out = tf.nn.batch_normalization(
        input_batch,
        mean,
        variance,
        self._beta,
        self._gamma,
        self._eps,
        name="batch_norm")

    return out
Ejemplo n.º 4
0
def _fill_and_verify_kernel_shape(x, n):
    """Expands x if necessary into a `n`-D kernel shape and reports errors."""
    try:
        return _fill_shape(x, n)
    except TypeError as e:
        raise base.IncompatibleShapeError("Invalid kernel shape: {}".format(e))
Ejemplo n.º 5
0
    def _build(self, inputs):
        """Connects the Conv2DTranspose module into the graph.

    If this is not the first time the module has been connected to the graph,
    the input Tensor provided here must have the same final 3 dimensions, in
    order for the existing variables to be the correct size for the
    multiplication. The batch size may differ for each connection.

    Args:
      inputs: A 4D Tensor of shape [batch_size, input_height, input_width,
          input_channels].

    Returns:
      A 4D Tensor of shape [batch_size, output_height, output_width,
          output_channels].

    Raises:
      ValueError: If connecting the module into the graph any time after the
          first time and the inferred size of the input does not match previous
          invocations.
      base.IncompatibleShapeError: If the input tensor has the wrong number of
          dimensions; or if the input tensor has an unknown `input_channels`; or
          or if `output_shape` is an iterable and is not in the format
          `(out_height, out_width)`.
      TypeError: If input Tensor dtype is not `tf.float32`.
    """
        # Handle input whose shape is unknown during graph creation.
        self._input_shape = tuple(inputs.get_shape().as_list())

        if len(self._input_shape) != 4:
            raise base.IncompatibleShapeError(
                "Input Tensor must have shape (batch_size, input_height, "
                "input_width, input_channels)")

        if self._input_shape[3] is None:
            raise base.IncompatibleShapeError(
                "Number of input channels must be known at module build time")
        input_channels = self._input_shape[3]

        if inputs.dtype != tf.float32:
            raise TypeError(
                "Input must have dtype tf.float32, but dtype was " +
                inputs.dtype)

        if len(self.output_shape) != 2:
            raise base.IncompatibleShapeError(
                "Output shape must be specified as "
                "(output_height, output_width)")

        weight_shape = (self._kernel_shape[0], self._kernel_shape[1],
                        self.output_channels, input_channels)

        bias_shape = (self.output_channels, )

        if "w" not in self._initializers:
            fan_in_shape = weight_shape[:2] + (weight_shape[3], )
            self._initializers["w"] = create_weight_initializer(fan_in_shape)

        if "b" not in self._initializers and self._use_bias:
            self._initializers["b"] = create_bias_initializer(bias_shape)

        self._w = tf.get_variable("w",
                                  shape=weight_shape,
                                  initializer=self._initializers["w"])

        # Use tensorflow shape op to manipulate inputs shape, so that unknown batch
        # size - which can happen when using input placeholders - is handled
        # correcly.
        batch_size = tf.expand_dims(tf.shape(inputs)[0], 0)
        conv_output_shape = tf.convert_to_tensor(
            tuple(self.output_shape) + (self.output_channels, ))
        output_shape = tf.concat(0, [batch_size, conv_output_shape])

        outputs = tf.nn.conv2d_transpose(inputs,
                                         self._w,
                                         output_shape,
                                         strides=self._stride,
                                         padding=self._padding)

        if self._use_bias:
            self._b = tf.get_variable("b",
                                      shape=bias_shape,
                                      initializer=self._initializers["b"])
            outputs += self._b

        # Recover output tensor shape value and pass it to set_shape in order to
        # enable shape inference.
        batch_size_value = inputs.get_shape()[0]
        output_shape_value = ((batch_size_value, ) + self.output_shape +
                              (self.output_channels, ))
        outputs.set_shape(output_shape_value)

        return outputs
Ejemplo n.º 6
0
    def __init__(self,
                 output_channels,
                 output_shape,
                 kernel_shape,
                 stride=1,
                 padding=SAME,
                 use_bias=True,
                 initializers=None,
                 name="conv_2d_transpose"):
        """Constructs a `Conv2DTranspose module`.

    See the following documentation for an explanation of VALID versus SAME
    padding modes:
    https://www.tensorflow.org/versions/r0.8/api_docs/python/nn.html#convolution

    Args:
      output_channels: Number of output channels.
          Can be either a number or a callable. In the latter case, since the
          function invocation is deferred to graph construction time, the user
          must only ensure `output_channels` can be called, returning an
          integer, when build is called.
      output_shape: Output shape of transpose convolution.
          Can be either an iterable of integers or a callable. In the latter
          case, since the function invocation is deferred to graph construction
          time, the user must only ensure that `output_shape` can be called,
          returning an iterable of format `(out_height, out_width)` when
          `_build` is called. Note that `output_shape` defines the size of
          output signal domain, as opposed to the shape of the output `Tensor`.
      kernel_shape: List of kernel sizes, must be length 2.
      stride: List of kernel strides.
      padding: Padding algorithm, either `nn.SAME` or `nn.VALID`.
      use_bias: Whether to include bias parameters. Default `True`.
      initializers: Optional dict containing ops to initialize the filters (with
          key 'w') or biases (with key 'b').
      name: Name of the module.

    Raises:
      base.IncompatibleShapeError: If the given kernel shape is neither an
          integer nor a sequence of two integers.
      base.IncompatibleShapeError: If the given stride is neither an integer nor
          a sequence of two or four integers.
      ValueError: If the given padding is not `nn.VALID` or `nn.SAME`.
      KeyError: If `initializers` contains any keys other than 'w' or 'b'.
      TypeError: If any of the given initializers are not callable.
    """
        super(Conv2DTranspose, self).__init__(name)

        self._output_channels = output_channels
        if callable(output_shape):
            self._output_shape = output_shape
        else:
            self._output_shape = tuple(output_shape)
        self._input_shape = None

        self._kernel_shape = _fill_and_verify_kernel_shape(kernel_shape, 2)
        # We want to support passing native strides akin to [1, m, n, 1].
        if isinstance(stride, collections.Iterable) and len(stride) == 4:
            if not stride[0] == stride[3] == 1:
                raise base.IncompatibleShapeError(
                    "Invalid stride: First and last element must be 1.")
            self._stride = tuple(stride)
        else:
            self._stride = _fill_and_one_pad_stride(stride, 2)

        self._padding = _verify_padding(padding)
        self._use_bias = use_bias
        self.possible_keys = self.get_possible_initializer_keys(
            use_bias=use_bias)
        self._initializers = util.check_initializers(initializers,
                                                     self.possible_keys)
Ejemplo n.º 7
0
    def _build(self, inputs):
        """Connects the Conv2D module into the graph, with input Tensor `inputs`.

    If this is not the first time the module has been connected to the graph,
    the input Tensor provided here must have the same final 3 dimensions, in
    order for the existing variables to be the correct size for the
    multiplication. The batch size may differ for each connection.

    Args:
      inputs: A 4D Tensor of shape [batch_size, input_height, input_width,
          input_channels].

    Returns:
      A 4D Tensor of shape [batch_size, output_height, output_width,
          output_channels].

    Raises:
      ValueError: If connecting the module into the graph any time after the
          first time and the inferred size of the input does not match previous
          invocations.
      base.IncompatibleShapeError: If the input tensor has the wrong number
          of dimensions.
      base.IncompatibleShapeError: If a mask is present and its shape is
          incompatible with the shape of the weights.
      base.UnderspecifiedError: If the input tensor has an unknown
          `input_channels`.
      base.UnderspecifiedError: If rate > 1 is used with an input tensor with
          unknown `input_width` or `input_height`.
      TypeError: If input Tensor dtype is not `tf.float32`.
    """
        # Handle input whose shape is unknown during graph creation.
        self._input_shape = tuple(inputs.get_shape().as_list())

        if len(self._input_shape) != 4:
            raise base.IncompatibleShapeError(
                "Input Tensor must have shape (batch_size, input_height, input_"
                "width, input_channels)")

        if self._input_shape[3] is None:
            raise base.UnderSpecifiedError(
                "Number of input channels must be known at module build time")
        else:
            input_channels = self._input_shape[3]

        if inputs.dtype != tf.float32:
            raise TypeError(
                "Input must have dtype tf.float32, but dtype was {}".format(
                    inputs.dtype))

        weight_shape = (self._kernel_shape[0], self._kernel_shape[1],
                        input_channels, self.output_channels)

        bias_shape = (self.output_channels, )

        if "w" not in self._initializers:
            self._initializers["w"] = create_weight_initializer(
                weight_shape[:3])

        if "b" not in self._initializers and self._use_bias:
            self._initializers["b"] = create_bias_initializer(bias_shape)

        self._w = tf.get_variable("w",
                                  shape=weight_shape,
                                  initializer=self._initializers["w"])

        w = self._w

        if self._mask is not None:
            mask_rank = self._mask.ndim
            mask_shape = self._mask.shape
            if mask_rank == 2:
                if mask_shape != self._kernel_shape:
                    raise base.IncompatibleShapeError(
                        "Invalid mask shape: {}".format(mask_shape))
                mask = np.reshape(self._mask, self._kernel_shape + (1, 1))
            elif mask_rank == 4:
                if mask_shape != tuple(weight_shape):
                    raise base.IncompatibleShapeError(
                        "Invalid mask shape: {}".format(mask_shape))
                mask = self._mask
            mask_tensor, = tf.py_func(lambda: mask, [], [w.dtype],
                                      stateful=False)
            mask_tensor.set_shape(weight_shape)
            w *= mask

        if self._rate > 1:
            if any(x is None for x in self._input_shape[1:-1]):
                raise base.UnderspecifiedError(
                    "Can't use atrous convolutions with unknown input_width or "
                    "input_height at graph build time")
            outputs = tf.nn.atrous_conv2d(inputs,
                                          w,
                                          rate=self._rate,
                                          padding=self._padding)
        else:
            outputs = tf.nn.conv2d(inputs,
                                   w,
                                   strides=self._stride,
                                   padding=self._padding)

        if self._use_bias:
            self._b = tf.get_variable("b",
                                      shape=bias_shape,
                                      initializer=self._initializers["b"])
            outputs += self._b

        return outputs
Ejemplo n.º 8
0
    def __init__(self,
                 output_channels,
                 kernel_shape,
                 stride=1,
                 rate=1,
                 padding=SAME,
                 use_bias=True,
                 initializers=None,
                 mask=None,
                 name="conv_2d"):
        """Constructs a Conv2D module.

    See the following documentation for an explanation of VALID versus SAME
    padding modes:
    https://www.tensorflow.org/versions/r0.8/api_docs/python/nn.html#convolution

    Args:
      output_channels: Number of output channels. `output_channels` can be
          either a number or a callable. In the latter case, since the function
          invocation is deferred to graph construction time, the user must only
          ensure that output_channels can be called, returning an integer,
          when `_build` is called.
      kernel_shape: List of kernel sizes, or integer that is used to define
          kernel size in all dimensions.
      stride: List of kernel strides, or integer that is used to define
          stride in all dimensions.
      rate: A positive integer, `rate=1` corresponds to standard 2D convolution,
          `rate > 1` corresponds to dilated convolution.
      padding: Padding algorithm, either `nn.SAME` or `nn.VALID`.
      use_bias: Whether to include bias parameters. Default `True`.
      initializers: Optional dict containing ops to initialize the filters (with
          key 'w') or biases (with key 'b'). The default initializers are
          truncated normal initializers, which are commonly used when the inputs
          are zero centered (see https://arxiv.org/pdf/1502.03167v3.pdf).
      mask: Optional 2D or 4D array, tuple or numpy array containing values to
          multiply the weights by component-wise.
      name: Name of the module.

    Raises:
      base.IncompatibleShapeError: If the given kernel shape is not an integer;
          or if the given kernel shape is not a sequence of two integers.
      base.IncompatibleShapeError: If the given stride is not an integer; or if
          the given stride is not a sequence of two or four integers.
      base.IncompatibleShapeError: If a mask is given and its rank is neither 2
          nor 4.
      base.NotSupportedError: If the given dilation rate is not a positive
          integer.
      base.NotSupportedError: If rate > 1 and the stride in any dimension is
          > 1.
      ValueError: If the given padding is not `nn.VALID` or `nn.SAME`.
      KeyError: If initializers contains any keys other than 'w' or 'b'.
      TypeError: If any of the given initializers are not callable.
      TypeError: If mask is given and is not an array, tuple or a numpy array.
    """
        super(Conv2D, self).__init__(name=name)

        self._output_channels = output_channels
        self._input_shape = None
        self._kernel_shape = _fill_and_verify_kernel_shape(kernel_shape, 2)
        try:
            self._stride = (1, ) + _fill_shape(stride, 2) + (1, )
        except TypeError as e:
            # We want to support passing native strides akin to [1, m, n, 1].
            if len(stride) == 4:
                self._stride = tuple(stride)
            else:
                raise base.IncompatibleShapeError(
                    "Invalid stride: {}".format(e))

        if not isinstance(rate, numbers.Integral) or rate < 1:
            raise base.NotSupportedError(
                "Rate, {}, must be integer >= 1".format(rate))
        elif any(x > 1 for x in self._stride) and rate > 1:
            raise base.NotSupportedError(
                "Cannot have stride > 1 with rate > 1")
        else:
            self._rate = rate

        self._padding = _verify_padding(padding)
        self._use_bias = use_bias
        self.possible_keys = self.get_possible_initializer_keys(
            use_bias=use_bias)
        self._initializers = util.check_initializers(initializers,
                                                     self.possible_keys)

        if mask is not None:
            if not isinstance(mask, (list, tuple, np.ndarray)):
                raise TypeError("Invalid type for mask: {}".format(type(mask)))
            self._mask = np.asanyarray(mask)
            mask_rank = mask.ndim
            if mask_rank != 2 and mask_rank != 4:
                raise base.IncompatibleShapeError(
                    "Invalid mask rank: {}".format(mask_rank))
        else:
            self._mask = None