Exemplo n.º 1
0
 def test_same_padding_corr(self):
     for ishape in [[10], [11]]:
         inputs = np.zeros(ishape, dtype=np.float32)
         inputs[len(inputs) // 2] = 1
         for kshape in [[4], [5]]:
             kernel = np.zeros(kshape, dtype=np.float32)
             kernel[len(kernel) // 2] = 1
             outputs = tf.nn.convolution(tf.reshape(inputs, (1, 1, -1, 1)),
                                         tf.reshape(kernel, (1, -1, 1, 1)),
                                         padding="VALID",
                                         data_format="NHWC")
             outputs = np.squeeze(outputs.numpy())
             pos_inp = np.squeeze(np.nonzero(inputs > .5))
             pos_out = np.squeeze(np.nonzero(outputs > .5))
             padding = padding_ops.same_padding_for_kernel(kshape, True)
             self.assertEqual(padding[0][0], pos_inp - pos_out)
Exemplo n.º 2
0
 def test_same_padding_corr(self):
   for ishape in [[10], [11]]:
     inputs = np.zeros(ishape, dtype=np.float32)
     inputs[len(inputs) // 2] = 1
     for kshape in [[4], [5]]:
       kernel = np.zeros(kshape, dtype=np.float32)
       kernel[len(kernel) // 2] = 1
       outputs = tf.nn.convolution(
           tf.reshape(inputs, (1, 1, -1, 1)),
           tf.reshape(kernel, (1, -1, 1, 1)),
           padding="VALID", data_format="NHWC")
       with self.test_session() as sess:
         outputs = np.squeeze(sess.run(outputs))
       pos_inp = np.squeeze(np.nonzero(inputs))
       pos_out = np.squeeze(np.nonzero(outputs))
       padding = padding_ops.same_padding_for_kernel(kshape, True)
       self.assertEqual(padding[0][0], pos_inp - pos_out)
 def test_same_padding_conv(self):
   for ishape in [[10], [11]]:
     inputs = np.zeros(ishape, dtype=np.float32)
     inputs[len(inputs) // 2] = 1
     for kshape in [[4], [5]]:
       kernel = np.zeros(kshape, dtype=np.float32)
       kernel[len(kernel) // 2] = 1
       outputs = tf.nn.conv2d_transpose(tf.reshape(inputs, (1, 1, -1, 1)),
                                        tf.reshape(kernel, (1, -1, 1, 1)),
                                        (1, 1, ishape[0] + kshape[0] - 1, 1),
                                        strides=(1, 1, 1, 1), padding="VALID",
                                        data_format="NHWC")
       outputs = outputs[:, :, (kshape[0] - 1):-(kshape[0] - 1), :]
       with self.test_session() as sess:
         outputs = np.squeeze(sess.run(outputs))
       pos_inp = np.squeeze(np.nonzero(inputs))
       pos_out = np.squeeze(np.nonzero(outputs))
       padding = padding_ops.same_padding_for_kernel(kshape, False)
       self.assertEqual(padding[0][0], pos_inp - pos_out)
Exemplo n.º 4
0
 def test_same_padding_conv(self):
   for ishape in [[10], [11]]:
     inputs = np.zeros(ishape, dtype=np.float32)
     inputs[len(inputs) // 2] = 1
     for kshape in [[4], [5]]:
       kernel = np.zeros(kshape, dtype=np.float32)
       kernel[len(kernel) // 2] = 1
       outputs = tf.nn.conv2d_transpose(
           tf.reshape(inputs, (1, 1, -1, 1)),
           tf.reshape(kernel, (1, -1, 1, 1)),
           (1, 1, ishape[0] + kshape[0] - 1, 1),
           strides=(1, 1, 1, 1), padding="VALID", data_format="NHWC")
       outputs = outputs[:, :, (kshape[0] - 1):-(kshape[0] - 1), :]
       with self.test_session() as sess:
         outputs = np.squeeze(sess.run(outputs))
       pos_inp = np.squeeze(np.nonzero(inputs))
       pos_out = np.squeeze(np.nonzero(outputs))
       padding = padding_ops.same_padding_for_kernel(kshape, False)
       self.assertEqual(padding[0][0], pos_inp - pos_out)
Exemplo n.º 5
0
    def call(self, inputs):
        inputs = tf.convert_to_tensor(inputs)
        outputs = inputs

        # Not for all possible combinations of (`kernel_support`, `corr`,
        # `strides_up`, `strides_down`) TF ops exist. We implement some additional
        # combinations by manipulating the kernels and toggling `corr`.
        kernel = self.kernel
        corr = self.corr

        # If a convolution with no upsampling is desired, we flip the kernels and
        # use cross correlation to implement it, provided the kernels are odd-length
        # in every dimension (with even-length kernels, the boundary handling
        # would have to change).
        if (not corr and all(s == 1 for s in self.strides_up)
                and all(s % 2 == 1 for s in self.kernel_support)):
            corr = True
            slices = self._rank * (slice(None, None,
                                         -1), ) + 2 * (slice(None), )
            kernel = kernel[slices]

        # Similarly, we can implement a cross correlation using convolutions.
        # However, we do this only if upsampling is requested, as we are potentially
        # wasting computation in the boundaries whenever we call the transpose ops.
        elif (corr and any(s != 1 for s in self.strides_up)
              and all(s % 2 == 1 for s in self.kernel_support)):
            corr = False
            slices = self._rank * (slice(None, None,
                                         -1), ) + 2 * (slice(None), )
            kernel = kernel[slices]

        # Compute amount of necessary padding, and determine whether to use built-in
        # padding or to pre-pad with a separate op.
        if self.padding == "valid" or all(s == 1 for s in self.kernel_support):
            prepadding = self._rank * [[0, 0]]
        else:  # same_*
            padding = padding_ops.same_padding_for_kernel(
                self.kernel_support, corr, self.strides_up)
            # Pre-pad and then use built-in valid padding mode.
            outputs = tf.pad(outputs, self._padded_tuple(padding, (0, 0)),
                             self._pad_mode)
            prepadding = padding

        # Compute the convolution/correlation.
        if corr and all(s == 1 for s in self.strides_up):
            outputs = self._correlate_down_valid(outputs, kernel)
        elif not corr:
            outputs = self._up_convolve_transpose_valid(
                outputs, kernel, prepadding)
        else:
            self._raise_notimplemented()

        # Now, add bias if requested.
        if self.bias is not None:
            bias = self.bias
            if self.data_format == "channels_first":
                # As of Mar 2017, direct addition is significantly slower than
                # bias_add when computing gradients.
                if self._rank == 1:
                    # tf.nn.bias_add does not accept a 1D input tensor.
                    outputs = tf.expand_dims(outputs, 2)
                    outputs = tf.nn.bias_add(outputs, bias, data_format="NCHW")
                    outputs = tf.squeeze(outputs, [2])
                elif self._rank == 2:
                    outputs = tf.nn.bias_add(outputs, bias, data_format="NCHW")
                elif self._rank >= 3:
                    shape = tf.shape(outputs)
                    outputs = tf.reshape(outputs,
                                         tf.concat([shape[:3], [-1]], axis=0))
                    outputs = tf.nn.bias_add(outputs, bias, data_format="NCHW")
                    outputs = tf.reshape(outputs, shape)
            else:
                outputs = tf.nn.bias_add(outputs, bias)

        # Finally, pass through activation function if requested.
        if self.activation is not None:
            outputs = self.activation(outputs)  # pylint:disable=not-callable

        # Aid shape inference, for some reason shape info is not always available.
        if not tf.executing_eagerly():
            outputs.set_shape(self.compute_output_shape(inputs.shape))

        return outputs
Exemplo n.º 6
0
    def call(self, inputs) -> tf.Tensor:
        if inputs.shape.rank != self._rank + 2:
            raise ValueError(f"Input tensor must have rank {self._rank + 2}, "
                             f"received shape {inputs.shape}.")
        outputs = inputs

        # Not for all possible combinations of (`kernel_support`, `corr`,
        # `strides_up`, `strides_down`) TF ops exist. We implement some additional
        # combinations by manipulating the kernels and toggling `corr`.
        kernel = self.kernel
        corr = self.corr

        # If a convolution with no upsampling is desired, we flip the kernels and
        # use cross correlation to implement it, provided the kernels are odd-length
        # in every dimension (with even-length kernels, the boundary handling
        # would have to change).
        if (not corr and all(s == 1 for s in self.strides_up)
                and all(s % 2 == 1 for s in self.kernel_support)):
            corr = True
            slices = self._rank * (slice(None, None,
                                         -1), ) + 2 * (slice(None), )
            kernel = kernel[slices]

        # Similarly, we can implement a cross correlation using convolutions.
        # However, we do this only if upsampling is requested, as we are potentially
        # wasting computation in the boundaries whenever we call the transpose ops.
        elif (corr and any(s != 1 for s in self.strides_up)
              and all(s % 2 == 1 for s in self.kernel_support)):
            corr = False
            slices = self._rank * (slice(None, None,
                                         -1), ) + 2 * (slice(None), )
            kernel = kernel[slices]

        # Compute amount of necessary padding, and determine whether to use built-in
        # padding or to pre-pad with a separate op.
        if self.padding == "valid":
            padding = prepadding = self._rank * ((0, 0), )
        else:  # same_*
            padding = padding_ops.same_padding_for_kernel(
                self.kernel_support, corr, self.strides_up)
            if (self.padding == "same_zeros" and not self.channel_separable
                    and 1 <= self._rank <= 2 and self.use_explicit):
                # Don't pre-pad and use built-in EXPLICIT mode.
                prepadding = self._rank * ((0, 0), )
            else:
                # Pre-pad and then use built-in valid padding mode.
                outputs = tf.pad(outputs, self._padded_tuple(padding, (0, 0)),
                                 self._pad_mode)
                prepadding = padding
                padding = self._rank * ((0, 0), )

        # Compute the convolution/correlation. Prefer EXPLICIT padding ops where
        # possible, but don't use them to implement VALID padding.
        if (corr and all(s == 1 for s in self.strides_up)
                and not self.channel_separable and 1 <= self._rank <= 2
                and not all(p[0] == p[1] == 0 for p in padding)):
            outputs = self._correlate_down_explicit(outputs, kernel, padding)
        elif (corr and all(s == 1 for s in self.strides_up)
              and all(p[0] == p[1] == 0 for p in padding)):
            outputs = self._correlate_down_valid(outputs, kernel)
        elif (not corr and not self.channel_separable and 1 <= self._rank <= 2
              and self.use_explicit):
            outputs = self._up_convolve_transpose_explicit(
                outputs, kernel, prepadding)
        elif not corr:
            outputs = self._up_convolve_transpose_valid(
                outputs, kernel, prepadding)
        else:
            self._raise_notimplemented()

        # Now, add bias if requested.
        if self.use_bias:
            bias = self.bias
            if self.data_format == "channels_first":
                # As of Mar 2017, direct addition is significantly slower than
                # bias_add when computing gradients.
                if self._rank == 1:
                    # tf.nn.bias_add does not accept a 1D input tensor.
                    outputs = tf.expand_dims(outputs, 2)
                    outputs = tf.nn.bias_add(outputs, bias, data_format="NCHW")
                    outputs = tf.squeeze(outputs, [2])
                elif self._rank == 2:
                    outputs = tf.nn.bias_add(outputs, bias, data_format="NCHW")
                elif self._rank >= 3:
                    shape = tf.shape(outputs)
                    outputs = tf.reshape(outputs,
                                         tf.concat([shape[:3], [-1]], axis=0))
                    outputs = tf.nn.bias_add(outputs, bias, data_format="NCHW")
                    outputs = tf.reshape(outputs, shape)
            else:
                outputs = tf.nn.bias_add(outputs, bias)

        # Finally, pass through activation function if requested.
        if self.activation is not None:
            outputs = self.activation(outputs)

        return outputs
Exemplo n.º 7
0
    def call(self, inputs):
        inputs = ops.convert_to_tensor(inputs, dtype=self.dtype)
        input_shape = array_ops.shape(inputs)
        outputs = inputs

        # First, perform any requested padding.
        if self.padding in ("same_zeros", "same_reflect"):
            padding = padding_ops.same_padding_for_kernel(
                self.kernel_support, self.corr, self.strides_up)
            if self.data_format == "channels_last":
                padding = [[0, 0]] + list(padding) + [[0, 0]]
            else:
                padding = [[0, 0], [0, 0]] + list(padding)
            outputs = array_ops.pad(outputs, padding, self._pad_mode)

        # Now, perform valid convolutions/correlations.

        # Not for all possible combinations of (`kernel_support`, `corr`,
        # `strides_up`, `strides_down`) TF ops exist. We implement some additional
        # combinations by manipulating the kernels and toggling `corr`.
        kernel = self.kernel
        corr = self.corr

        # If a convolution with no upsampling is desired, we flip the kernels and
        # use cross correlation to implement it, provided the kernels are odd-length
        # in every dimension (with even-length kernels, the boundary handling
        # would have to change, so we'll throw an error instead).
        if (not corr and all(s == 1 for s in self.strides_up)
                and all(s % 2 == 1 for s in self.kernel_support)):
            corr = True
            slices = self._rank * (slice(None, None,
                                         -1), ) + 2 * (slice(None), )
            kernel = kernel[slices]

        # Similarly, we can implement a cross correlation with no downsampling using
        # convolutions. However, we do this only if upsampling is requested, as we
        # are wasting computation in the boundaries whenever we call the transpose
        # convolution ops.
        if (corr and all(s == 1 for s in self.strides_down)
                and any(s != 1 for s in self.strides_up)
                and all(s % 2 == 1 for s in self.kernel_support)):
            corr = False
            slices = self._rank * (slice(None, None,
                                         -1), ) + 2 * (slice(None), )
            kernel = kernel[slices]

        data_format = utils.convert_data_format(self.data_format,
                                                self._rank + 2)
        if (corr and self.channel_separable and self._rank == 2
                and all(s == 1 for s in self.strides_up)
                and all(s == self.strides_down[0] for s in self.strides_down)):
            # `nn.depthwise_conv2d_native` performs channel-separable correlations
            # followed by optional downsampling.
            outputs = nn.depthwise_conv2d_native(outputs,
                                                 kernel,
                                                 strides=self._pad_strides(
                                                     self.strides_down),
                                                 padding="VALID",
                                                 data_format=data_format)
        elif (corr and all(s == 1 for s in self.strides_up)
              and not self.channel_separable):
            # `nn.convolution` performs correlations followed by optional
            # downsampling.
            outputs = nn.convolution(outputs,
                                     kernel,
                                     strides=self.strides_down,
                                     padding="VALID",
                                     data_format=data_format)
        elif (not corr and all(s == 1 for s in self.strides_down)
              and ((not self.channel_separable and 1 <= self._rank <= 3) or
                   (self.channel_separable and self.filters == 1
                    and self._rank == 2 and all(s == self.strides_up[0]
                                                for s in self.strides_up)))):
            # `nn.conv?d_transpose` perform convolutions, preceded by optional
            # upsampling. Generally, they increase the spatial support of their
            # inputs, so in order to implement 'valid', we need to crop their outputs.

            # Transpose convolutions expect the output and input channels in reversed
            # order. We implement this by swapping those dimensions of the kernel.
            # For channel separable convolutions, we can't currently perform anything
            # other than one filter per channel, so the last dimension needs to be of
            # length one. Since this happens to be the format that the op expects it,
            # we can skip the transpose in that case.
            if not self.channel_separable:
                kernel = array_ops.transpose(
                    kernel,
                    list(range(self._rank)) + [self._rank + 1, self._rank])

            # Compute shape of temporary.
            pad_shape = array_ops.shape(outputs)
            temp_shape = [pad_shape[0]] + (self._rank + 1) * [None]
            if self.data_format == "channels_last":
                spatial_axes = range(1, self._rank + 1)
                if self.channel_separable:
                    temp_shape[-1] = input_shape[-1]
                else:
                    temp_shape[-1] = self.filters
            else:
                spatial_axes = range(2, self._rank + 2)
                if self.channel_separable:
                    temp_shape[1] = input_shape[1]
                else:
                    temp_shape[1] = self.filters
            if self.extra_pad_end:
                get_length = lambda l, s, k: l * s + (k - 1)
            else:
                get_length = lambda l, s, k: l * s + (k - s)
            for i, a in enumerate(spatial_axes):
                temp_shape[a] = get_length(pad_shape[a], self.strides_up[i],
                                           self.kernel_support[i])

            # Compute convolution.
            if self._rank == 1 and not self.channel_separable:
                # There's no 1D transpose convolution op, so we insert an extra
                # dimension and use 2D.
                extradim = {
                    "channels_first": 2,
                    "channels_last": 1
                }[self.data_format]
                strides = self._pad_strides(self.strides_up)
                temp = array_ops.squeeze(
                    nn.conv2d_transpose(
                        array_ops.expand_dims(outputs, extradim),
                        array_ops.expand_dims(kernel, 0),
                        temp_shape[:extradim] + [1] + temp_shape[extradim:],
                        strides=strides[:extradim] + (1, ) +
                        strides[extradim:],
                        padding="VALID",
                        data_format=data_format.replace("W", "HW")),
                    [extradim])
            elif self._rank == 2 and self.channel_separable:
                temp = nn.depthwise_conv2d_native_backprop_input(
                    temp_shape,
                    kernel,
                    outputs,
                    strides=self._pad_strides(self.strides_up),
                    padding="VALID",
                    data_format=data_format)
            elif self._rank == 2 and not self.channel_separable:
                temp = nn.conv2d_transpose(outputs,
                                           kernel,
                                           temp_shape,
                                           strides=self._pad_strides(
                                               self.strides_up),
                                           padding="VALID",
                                           data_format=data_format)
            elif self._rank == 3 and not self.channel_separable:
                temp = nn.conv3d_transpose(outputs,
                                           kernel,
                                           temp_shape,
                                           strides=self._pad_strides(
                                               self.strides_up),
                                           padding="VALID",
                                           data_format=data_format)
            else:
                assert False  # Should never reach this.

            # Perform crop.
            slices = [slice(None)] * (self._rank + 2)
            if self.padding == "valid":
                # Take `kernel_support - 1` samples away from both sides. This leaves
                # just samples computed without padding.
                for i, a in enumerate(spatial_axes):
                    slices[a] = slice(
                        self.kernel_support[i] - 1,
                        None if self.kernel_support[i] == 1 else 1 -
                        self.kernel_support[i])
            else:
                # Take `kernel_support // 2` plus the padding away from beginning, and
                # crop end to input length multiplied by upsampling factor.
                for i, a in enumerate(spatial_axes):
                    offset = padding[a][0] * self.strides_up[i]
                    offset += self.kernel_support[i] // 2
                    length = get_length(input_shape[a], self.strides_up[i],
                                        offset + 1)
                    slices[a] = slice(offset, length)
            outputs = temp[slices]
        else:
            raise NotImplementedError(
                "The provided combination of SignalConv arguments is not currently "
                "implemented (kernel_support={}, corr={}, strides_down={}, "
                "strides_up={}, channel_separable={}, filters={}). "
                "Try using odd-length kernels or turning off separability?".
                format(self.kernel_support, self.corr, self.strides_down,
                       self.strides_up, self.channel_separable, self.filters))

        # Now, add bias if requested.
        if self.bias is not None:
            if self.data_format == "channels_first":
                # As of Mar 2017, direct addition is significantly slower than
                # bias_add when computing gradients.
                if self._rank == 1:
                    # nn.bias_add does not accept a 1D input tensor.
                    outputs = array_ops.expand_dims(outputs, 2)
                    outputs = nn.bias_add(outputs,
                                          self.bias,
                                          data_format="NCHW")
                    outputs = array_ops.squeeze(outputs, [2])
                elif self._rank == 2:
                    outputs = nn.bias_add(outputs,
                                          self.bias,
                                          data_format="NCHW")
                elif self._rank >= 3:
                    shape = array_ops.shape(outputs)
                    outputs = array_ops.reshape(outputs, shape[:3] + [-1])
                    outputs = nn.bias_add(outputs,
                                          self.bias,
                                          data_format="NCHW")
                    outputs = array_ops.reshape(outputs, shape)
            else:
                outputs = nn.bias_add(outputs, self.bias)

        # Finally, pass through activation function if requested.
        if self.activation is not None:
            outputs = self.activation(outputs)  # pylint:disable=not-callable

        # Aid shape inference, for some reason shape info is not always available.
        if not context.executing_eagerly():
            outputs.set_shape(self.compute_output_shape(inputs.shape))

        return outputs