New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add PixelUnshuffle #49334
Add PixelUnshuffle #49334
Conversation
508bddc
to
d5aa90c
Compare
This pull request was exported from Phabricator. Differential Revision: D25401439 |
d5aa90c
to
cbb0615
Compare
int64_t h = self.size(-2); | ||
int64_t w = self.size(-1); | ||
const auto NUM_NON_BATCH_DIMS = 3; | ||
const auto last_batch_dim = self.sizes().end() - NUM_NON_BATCH_DIMS; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I would definitely expect something called last_batch_dim
to be an integer type, that it's only valid on self_sizes should probably be part of the name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, doesn't this point to the first non-batch dim? I.e. it's only the last_batch_dim because you are calling it in a function that "ends" one past. So maybe a better name is "self_sizes_batch_end" or something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you're right- it points to just after the last batch dim. To be more precise, I'll change this to self_sizes_batch_end
as suggested.
// (ow, downscale_factor) dims. This allows unshuffling to be done next by permuting dims. | ||
std::vector<int64_t> expanded_shape(self.sizes().begin(), last_batch_dim); | ||
expanded_shape.insert(expanded_shape.end(), {c, oh, downscale_factor, ow, downscale_factor}); | ||
const auto input_expanded = self.reshape(expanded_shape); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"expand" suggests something particular, which is that it called "expand" (i.e. didn't allocate new memory). Is that what you expect here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point- expand
as in tensor.expand()
is not what is intended here. Ideally, I want to signal that the input has been reshaped, resulting in an increased number of input dimensions with no change to the number of elements. Would input_reshaped
work here or is there a more specific way to name this? Any suggestions on the name for the shape itself?
torch/nn/functional.py
Outdated
pixel_unshuffle = _add_docstr(torch.pixel_unshuffle, r""" | ||
pixel_unshuffle(input, downscale_factor) -> Tensor | ||
|
||
Rearranges elements in a tensor of shape :math:`(*, C, H \times r, W \times r)` to a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we use *
in the docs consistently how you've used it here (i.e. correctly?). I'm particularly asking about the case where *
translates to no dimensions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm good question! From the nn.Linear docs:
"Input: (N, *, H_{in})
, where *
means any number of additional dimensions and H_{in}
= in_features"
This description indicates to me that N
and H_{in}
are required dimensions, and *
can be any number of additional dimensions in between those two. However, surprisingly to me, nn.Linear
does accept an input shape of (H_{in},)
. It seems that the *
functions like "0 or more of the previous" (i.e. the Kleene star). I think it is the use of the word "additional" that makes this surprising to me, but maybe this is only a problem for me.
Similarly in nn.L1Loss:
"Input: (N, *)
where *
means, any number of additional dimensions"
nn.L1Loss
does indeed accept tensors of 0 dimensions, so again the *
functions like a Kleene star.
TL;DR: To match what's been done in the two referenced modules, the line here should change to: (N, *, C, H \times r, W \times r)
. Similar updates should be done in PixelShuffle
for consistency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do typically use *
for 0 or more dimensions. See, for example, the torch.svd documentation: https://pytorch.org/docs/master/generated/torch.svd.html?highlight=svd#torch.svd. We're probably inconsistent about this in our documentation.
I could see N*
or *
as appropriate. I wouldn't want to use (N, *, C...)
unless zero or more dimensions could be inserted between a required dimension N
and a required dimension C
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could see N* or * as appropriate. I wouldn't want to use (N, *, C...) unless zero or more dimensions could be inserted between a required dimension N and a required dimension C.
I agree with this as it matches my intuition, and I think we should make a note to eventually make this consistent across torch.nn modules (including nn.Linear
and nn.L1Loss
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as this PR is concerned, I will add a note to the docs indicating what * represents here, with similar phrasing to that of torch.svd
.
torch/nn/functional.py
Outdated
pixel_unshuffle(input, downscale_factor) -> Tensor | ||
|
||
Rearranges elements in a tensor of shape :math:`(*, C, H \times r, W \times r)` to a | ||
tensor of shape :math:`(*, C \times r^2, H, W)`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like the pixel shuffle documentation, this references r
before it's defined. The input argument is "downscale_factor". Also, while this first statement is true, I think the highest order bit to communicate when describing this function is that it reverses the pixel shuffle operation, which can can be linked to in the first sentence. The identify can even be elaborated on in a mathematical line showing that unshuffle(shuffle(...)) is an identity. This first sentence is OK, and the reshaping performed is important, but it loses the context of what the reshape does, and there's no mention of how the values in the tensor are permuted. That is, an extremely cynical reading of this first sentence is simply that it randomly rearranges all the elements in the tensor and puts them into the new shape.
Rearranges elements in a tensor of shape :math:`(*, C, H \times r, W \times r)` to a | ||
tensor of shape :math:`(*, C \times r^2, H, W)`. | ||
|
||
See :class:`~torch.nn.PixelUnshuffle` for details. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It always seems like an interesting quirk to me that we refer to the module over the function as our source of truth, but I think this is how most of our nn documents work.
torch/nn/modules/pixelshuffle.py
Outdated
|
||
|
||
class PixelUnshuffle(Module): | ||
r"""Rearranges elements in a tensor of shape :math:`(*, C, H \times r, W \times r)` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comments as for above here.
torch/nn/modules/pixelshuffle.py
Outdated
to a tensor of shape :math:`(*, C \times r^2, H, W)`. This is the inverse operation | ||
of :class:`~torch.nn.PixelShuffle`. | ||
|
||
Note that this function can take inputs with any number of batch dimensions: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need this note and should reinforce that *
is zero or more batch dimensions, since that pattern appears consistently in the docs.
@@ -51,4 +51,49 @@ Tensor pixel_shuffle(const Tensor& self, int64_t upscale_factor) { | |||
return input_permuted.reshape(final_shape); | |||
} | |||
|
|||
|
|||
Tensor pixel_unshuffle(const Tensor& self, int64_t downscale_factor) { | |||
TORCH_CHECK(self.dim() >= 3, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there also be a check that downscale factor is > 0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this, especially because the below checks of h % downscale_factor == 0
and w % downscale_factor == 0
may pass even with a negative downscale_factor
due to the specific way mod is implemented in C++.
For consistency, what do you think about me adding an upscale_factor check in pixel_shuffle
in this PR?
torch/nn/functional.py
Outdated
pixel_unshuffle = _add_docstr(torch.pixel_unshuffle, r""" | ||
pixel_unshuffle(input, downscale_factor) -> Tensor | ||
|
||
Reverses the :class:`~torch.nn.PixelShuffle` operation by rearranging elements in a tensor of shape :math:`(*, C, H \times r, W \times r)` to a tensor of shape :math:`(*, C \times r^2, H, W)`, where r is a downscale factor. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might cause lint problem.
max number of character per line is 120.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Just setup my local dev environment and the python linter (flake8) is not functioning correctly yet :(
torch/nn/modules/pixelshuffle.py
Outdated
|
||
|
||
class PixelUnshuffle(Module): | ||
r"""Reverses the :class:`~torch.nn.PixelShuffle` operation by rearranging elements in a tensor of shape :math:`(*, C, H \times r, W \times r)` to a tensor of shape :math:`(*, C \times r^2, H, W)`, where r is a downscale factor. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here.
This might cause lint problem.
max number of character per line is 120.
I reviewed the test failures, most are:
Is caused because the function is missing an override entry. See @soulitzer's recent PR adding Line 774 in c0deb23
@heitorschueroff has also been interested in helping improve our documentation for adding a new operator. There's a lot to it. A few builds are also failing due to a couple mypy issues, and rebasing is likely to fix the rest. |
Summary: Pull Request resolved: pytorch#49334 Adds an implementation of `torch.nn.PixelUnshuffle` as the inverse operation of `torch.nn.PixelShuffle`. This addresses pytorch#2456 Test Plan: `buck test caffe2/test:nn -- test_pixel_unshuffle` Differential Revision: D25401439 fbshipit-source-id: edf92f0ac884410c287d7afc91506d50b52597cd
ad17f9f
to
cbe6c1c
Compare
Codecov Report
@@ Coverage Diff @@
## master #49334 +/- ##
==========================================
+ Coverage 75.24% 80.56% +5.32%
==========================================
Files 1883 1887 +4
Lines 204470 204663 +193
==========================================
+ Hits 153851 164885 +11034
+ Misses 50619 39778 -10841 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jbschlosser has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
int64_t c = self.size(-3); | ||
int64_t h = self.size(-2); | ||
int64_t w = self.size(-1); | ||
const auto NUM_NON_BATCH_DIMS = 3; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: constexpr
torch/nn/functional.py
Outdated
@@ -2799,7 +2799,7 @@ def multi_margin_loss(input, target, p=1, margin=1., weight=None, size_average=N | |||
pixel_shuffle(input, upscale_factor) -> Tensor | |||
|
|||
Rearranges elements in a tensor of shape :math:`(*, C \times r^2, H, W)` to a | |||
tensor of shape :math:`(*, C, H \times r, W \times r)`. | |||
tensor of shape :math:`(*, C, H \times r, W \times r)`, where r is an upscale factor. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"r is the :attr:`upscale_factor`."
torch/nn/functional.py
Outdated
|
||
Reverses the :class:`~torch.nn.PixelShuffle` operation by rearranging elements in a | ||
tensor of shape :math:`(*, C, H \times r, W \times r)` to a tensor of shape | ||
:math:`(*, C \times r^2, H, W)`, where r is a downscale factor. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Analogous change here as above.
@@ -6,26 +6,30 @@ | |||
|
|||
class PixelShuffle(Module): | |||
r"""Rearranges elements in a tensor of shape :math:`(*, C \times r^2, H, W)` | |||
to a tensor of shape :math:`(*, C, H \times r, W \times r)`. | |||
to a tensor of shape :math:`(*, C, H \times r, W \times r)`, where r is an upscale factor. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using "an upscale factor" seems OK here since the name of the input is not available
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Just some minor inline comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jbschlosser
This generally looks good to me now, I have one nit comment, not mandatory.
/// PixelUnshuffle model(PixelUnshuffleOptions(5)); | ||
/// ``` | ||
struct TORCH_API PixelUnshuffleOptions { | ||
PixelUnshuffleOptions(int64_t downscale_factor) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: for single arg unique constructor, we normally put explicit before, this can avoid warnings in phabricator as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for catching this! I might be wrong, but it looks like the implicit behavior is desired for C++ module options. None of the option structs defined in torch/csrc/api/include/torch/nn/options
use explicit
, and in fact several have the /* implicit */
comment. Further, the C++ module tests are often written to use the implicit conversion. So to follow our standards and match these, I think I should add a similar /* implicit */
comment here as well. Thoughts on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jbschlosser
The C++ lib is contributed by many external contributors. Multiple coding styles are mixed together at the moment.
I plan to standardized it a bit in the future.
Adding /*implicit*/ is fine, I will update them in one shot later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jbschlosser has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
@jbschlosser merged this pull request in 68d438c. |
Summary: Adds an implementation of
torch.nn.PixelUnshuffle
as the inverse operation oftorch.nn.PixelShuffle
. This addresses #2456Test Plan:
Differential Revision: D25401439
Screenshots of rendered docs: