diff --git a/oneflow/python/nn/modules/interpolate.py b/oneflow/python/nn/modules/interpolate.py index 64b07535a54cf6afcb67bf003e68942181b87d45..54158cfc13d1002f0a8955a6b4ef1c7ea141cc0c 100644 --- a/oneflow/python/nn/modules/interpolate.py +++ b/oneflow/python/nn/modules/interpolate.py @@ -83,7 +83,7 @@ class Interpolate(Module): raise ValueError("only one of size or scale_factor should be defined") elif self.size is not None: assert self.scale_factor is None - scale_factors = None + scale_factors = [] if isinstance(self.size, (list, tuple)): if len(self.size) != dim: raise ValueError( @@ -93,6 +93,8 @@ class Interpolate(Module): output_size = self.size else: output_size = [self.size for _ in range(dim)] + for i in range(dim): + scale_factors.append(output_size[i] / x.shape[i + 2]) elif self.scale_factor is not None: assert self.size is None output_size = None @@ -132,13 +134,15 @@ class Interpolate(Module): if self.mode == "area" and output_size is None: self.recompute_scale_factor = True - if self.recompute_scale_factor is not None and self.recompute_scale_factor: + if self.recompute_scale_factor is True: assert scale_factors is not None output_size = [ int(math.floor(float(input.size(i + 2)) * scale_factors[i])) for i in range(dim) ] - scale_factors = None + scale_factors = [] + for i in range(dim): + scale_factors.append(output_size[i] / x.shape[2 + i]) if len(x.shape) == 3 and self.mode == "nearest": return flow.F.upsample_nearest_1d( diff --git a/oneflow/python/nn/modules/upsampling.py b/oneflow/python/nn/modules/upsampling.py index 9316525cdc884db77cffc5548b48feea56c29195..729acbe487c8fa440b0264b7a6a68479cf0ec005 100644 --- a/oneflow/python/nn/modules/upsampling.py +++ b/oneflow/python/nn/modules/upsampling.py @@ -23,34 +23,40 @@ from typing import Optional, Union, Tuple @oneflow_export("nn.Upsample") @experimental_api class Upsample(Module): - r"""Upsamples a given multi-channel 2D (spatial) data. + r"""The interface is consistent with PyTorch. + + The documentation is referenced from: https://pytorch.org/docs/1.9.0/_modules/torch/nn/modules/upsampling.html#Upsample + + Upsamples a given multi-channel 1D (temporal), 2D (spatial) or 3D (volumetric) data. The input data is assumed to be of the form - `minibatch x channels x height x width`. - Hence, for spatial inputs, we expect a 4D Tensor. + `minibatch x channels x [optional depth] x [optional height] x width`. + Hence, for spatial inputs, we expect a 4D Tensor and for volumetric inputs, we expect a 5D Tensor. - The algorithms available for upsampling are nearest neighbor, - bilinear, 4D input Tensor, respectively. + The algorithms available for upsampling are nearest neighbor and linear, + bilinear, bicubic and trilinear for 3D, 4D and 5D input Tensor, + respectively. One can either give a :attr:`scale_factor` or the target output :attr:`size` to calculate the output size. (You cannot give both, as it is ambiguous) Args: - size (int or Tuple[int, int] optional): + size (int or Tuple[int] or Tuple[int, int] or Tuple[int, int, int], optional): output spatial sizes - scale_factor (float or Tuple[float, float], optional): + scale_factor (float or Tuple[float] or Tuple[float, float] or Tuple[float, float, float], optional): multiplier for spatial size. Has to match input size if it is a tuple. mode (str, optional): the upsampling algorithm: one of ``'nearest'``, - ``'bilinear'``. + ``'linear'``, ``'bilinear'``, ``'bicubic'`` and ``'trilinear'``. Default: ``'nearest'`` align_corners (bool, optional): if ``True``, the corner pixels of the input and output tensors are aligned, and thus preserving the values at - those pixels. This only has effect when :attr:`mode` is ``'bilinear'``. - Default: ``False`` + those pixels. This only has effect when :attr:`mode` is + ``'linear'``, ``'bilinear'``, or ``'trilinear'``. Default: ``False`` Shape: - - Input: : :math:`(N, C, H_{in}, W_{in})` - - Output: :math:`(N, C, H_{out}, W_{out})` , where + - Input: :math:`(N, C, W_{in})`, :math:`(N, C, H_{in}, W_{in})` or :math:`(N, C, D_{in}, H_{in}, W_{in})` + - Output: :math:`(N, C, W_{out})`, :math:`(N, C, H_{out}, W_{out})` + or :math:`(N, C, D_{out}, H_{out}, W_{out})`, where .. math:: D_{out} = \left\lfloor D_{in} \times \text{scale_factor} \right\rfloor @@ -61,16 +67,25 @@ class Upsample(Module): .. math:: W_{out} = \left\lfloor W_{in} \times \text{scale_factor} \right\rfloor + .. warning:: + With ``align_corners = True``, the linearly interpolating modes + (`linear`, `bilinear`, `bicubic`, and `trilinear`) don't proportionally + align the output and input pixels, and thus the output values can depend + on the input size. This was the default behavior for these modes up to + version 0.3.1. Since then, the default behavior is + ``align_corners = False``. See below for concrete examples on how this + affects the outputs. + .. note:: If you want downsampling/general resizing, you should use :func:`~nn.functional.interpolate`. + For example: .. code-block:: python >>> import numpy as np >>> import oneflow.experimental as flow - >>> flow.enable_eager_execution() >>> input = flow.Tensor(np.arange(1, 5).reshape((1, 1, 2, 2)), dtype=flow.float32) >>> input = input.to("cuda") @@ -92,59 +107,18 @@ class Upsample(Module): ): super().__init__() self.size = size - if isinstance(scale_factor, tuple): - self.scale_factor = tuple(float(factor) for factor in scale_factor) - else: - self.scale_factor = float(scale_factor) if scale_factor else None - + self.scale_factor = scale_factor self.mode = mode - if align_corners == None: - align_corners = False - self.align_corners = align_corners - self.height_scale = None - self.width_scale = None - - if isinstance(self.scale_factor, float): - self.height_scale = self.scale_factor - self.width_scale = self.scale_factor - elif isinstance(self.scale_factor, tuple): - self.height_scale = self.scale_factor[0] - self.width_scale = self.scale_factor[1] - else: - pass - - if self.mode != "nearest" and self.mode != "bilinear": - raise ValueError('interpolation must be "nearest" or "bilinear".') - - if self.mode == "nearest" and self.align_corners: - raise ValueError('interpolation "nearest" does not support align_corners.') def forward(self, x): - assert ( - self.size != None or self.scale_factor != None - ), f"size and scale_factor can not be none at the same time!" - h, w = x.shape[2], x.shape[3] - if self.height_scale == None: - if isinstance(self.size, int): - self.height_scale = 1.0 * self.size / h - else: - self.height_scale = 1.0 * self.size[0] / h - if self.width_scale == None: - if isinstance(self.size, int): - self.width_scale = 1.0 * self.size / w - else: - self.width_scale = 1.0 * self.size[1] / w - - res = flow.F.upsample( + return flow.experimental.nn.functional.interpolate( x, - height_scale=self.height_scale, - width_scale=self.width_scale, + size=self.size, + scale_factor=self.scale_factor, + mode=self.mode, align_corners=self.align_corners, - interpolation=self.mode, - data_format="channels_first", ) - return res def extra_repr(self) -> str: if self.scale_factor is not None: