-
Notifications
You must be signed in to change notification settings - Fork 7.1k
[proto] Added mid-level ops and feature-based ops #6219
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
Conversation
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 @vfdev-5. Looks good overall. I did a high-level review on the patterns you used. I didn't check every single invocation and method call you make on the PR, assuming you complete the pattern (aka you call the right low-level kernel for every high-level one). I've added a few comments to get your input. Let me know what you think.
def erase(self, i: int, j: int, h: int, w: int, v: torch.Tensor) -> BoundingBox: | ||
raise TypeError("Erase transformation does not support bounding boxes") | ||
|
||
def mixup(self, lam: float) -> BoundingBox: | ||
raise TypeError("Mixup transformation does not support bounding boxes") | ||
|
||
def cutmix(self, box: Tuple[int, int, int, int], lam_adjusted: float) -> BoundingBox: |
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 believe these operations should be kernels. Mixup, Cutmix etc are augmentation strategies. I would even be inclined not to add erase and let people to access the functional directly. The rational is that we should keep the kernels low. As we keep adding augmentations, we do this on the Transforms side. Thoughts?
# How dangerous to do this instead of raising an error ? | ||
return self | ||
|
||
def resize( # type: ignore[override] |
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.
Why ignore[override]
is needed 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.
there is torch.Tensor built-in deprecated op: Tensor.resize : https://github.com/pytorch/pytorch/blob/e8727994eb7cdb2ab642749d6549bc497563aa06/torch/_tensor.py#L588-L593
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.
holly molly! that's not good. Does it make sense to rename the method? I can see that Tensor.resize_ exists still on the docs but can't find the resize.
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.
non-inplace Tensor.resize is deprecated -> no docs. IMO, we can keep both.
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.
Add a big TODO to get feedback on this. cc @NicolasHug thoughts?
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.
A bit late to the party: looks like the original Tensor.resize
was deprecated 4+ years ago, so I would agree that it's fine to override it IMHO.
Minor note: it's not super clear what the TODO above is about, i.e. it's not obvious what needs to be done about it?
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.
@NicolasHug I agree that the TODO note is unclear. I'll update in a follow-up PR. The point is to fix # type: ignore[override]
and make mypy happy.
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.
Will this be actually possible? As far as I understand, mypy
will throw an error as long as the 2 resize()
implementations have different signatures?
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 is a good question. Maybe it is impossible. I was thinking about mypy overload: https://mypy.readthedocs.io/en/stable/more_types.html#function-overloading if this could help...
elif isinstance(inpt, PIL.Image.Image): | ||
# Shouldn't we implement a fallback to tensor ? | ||
raise RuntimeError("Not implemented") | ||
elif isinstance(inpt, torch.Tensor): |
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.
How would the idiom would look like if we had a fallback? Do you plan to do something like the following?
elif isinstance(inpt, (PIL.Image.Image, torch.Tensor)):
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 can just repeat one line:
elif isinstance(inpt, PIL.Image.Image):
inpt_tensor = some_pil_to_tensor_method(inpt)
output_tensor = F.erase_image_tensor(inpt_tensor, **params)
return some_tensor_to_pil_method(output_tensor)
elif isinstance(inpt, torch.Tensor):
return F.erase_image_tensor(inpt, **params)
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.
Sure but what if you have the kernel. I'm asking in general what idiom you will favour when you call the same F.
return features.OneHotLabel.new_like(input, output) | ||
def _transform(self, inpt: Any, params: Dict[str, Any]) -> Any: | ||
if isinstance(inpt, features._Feature): | ||
return inpt.mixup(**params) |
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.
Curious to see how this will be adapted if we adopt my recommendation above to lose the augmentation specific kernels.
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.
me too :)
b023a27
to
97acb26
Compare
@@ -69,3 +70,142 @@ def to_format(self, format: Union[str, BoundingBoxFormat]) -> BoundingBox: | |||
return BoundingBox.new_like( | |||
self, convert_bounding_box_format(self, old_format=self.format, new_format=format), format=format | |||
) | |||
|
|||
def horizontal_flip(self) -> BoundingBox: | |||
from torchvision.prototype.transforms import functional as _F |
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.
Btw if this happens on a hot path, this import (even if it's not the first run) may hurt perf (from my experience of a few years back)
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.
Yeah, this is not ideal solution. Previously I tried to add the submodule as an attribute and somehow dataloader with multiple processes hangs due to that...
If you have any better ideas, please share
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.
Yeah that's a fair point. This workaround is temporary for as long as we work on the API. We should seek a better solution prior finalising it. Potentially a refactoring and reorganization of the modules might be a solution here but that can be decided 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.
Here is an alternative approach for this at #6476
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 understand that things started piling up and it will help if we do the changes incrementally. I'm approving as in principle, I believe the approach is correct. Below I have some comments and questions for your consideration.
# Just output itself | ||
# How dangerous to do this instead of raising an error ? | ||
def pad( | ||
self, padding: List[int], fill: Union[int, float, Sequence[float]] = 0, padding_mode: str = "constant" |
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.
Why don't we support Sequence[int]
as well? Did you face JIT-scriptability issues related to how it handles seq of ints and floats? If we add support, we need to put them in all places.
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 think we can add Sequence[int]
to the type hint (I didn't add it as it starts looking very bulky). JIT is not concerned as pad
is not scriptable (in general torch script does not recognize Sequence
and we should map it to List
).
In following PRs, we have to make same type hint for all fill
usages.
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.
Sounds good. You wanna put a TODO on the code or you keep track of this elsewhere?
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'll be dealing with this in Transforms PR (next one, not yet sent)
@@ -40,12 +40,58 @@ def horizontal_flip_bounding_box( | |||
).view(shape) | |||
|
|||
|
|||
def horizontal_flip(inpt: Any) -> Any: |
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.
Why Any and not Union[Tensor, PIL.Image]?
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.
If we replace Any by union[tensor, pil-image]
should we keepelse
branch ? In this case, it could be
def horizontal_flip(inpt: Union[torch.Tensor, PIL.Image.Image]) -> Union[torch.Tensor, PIL.Image.Image]:
if isinstance(inpt, features._Feature):
return inpt.horizontal_flip()
elif isinstance(inpt, PIL.Image.Image):
return horizontal_flip_image_pil(inpt)
# elif isinstance(inpt, torch.Tensor):
# return horizontal_flip_image_tensor(inpt)
# else:
# return inpt
else:
return horizontal_flip_image_tensor(inpt)
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.
Agreed. As discussed earlier, the middle-layer shouldn't return the input as-is on else. I assume this will be removed on a follow up PR? Also we might want to play around with the order of the ifs and the type info, to see if there is any way we can make the middle layer JIT-scriptable.
perspective_coeffs: List[float], | ||
interpolation: int = _pil_constants.BICUBIC, | ||
fill: Optional[Union[float, List[float], Tuple[float, ...]]] = 0, | ||
fill: Optional[Union[float, List[float], Tuple[float, ...]]] = None, |
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.
Here is my understanding for why this is OK. The change on fill
doesn't have any effect on the actual operation due to the call on _parse_fill()
. Concerning the type of perspective_coeffs
I believe that the change actually fixes a bug because PIL breaks if you pass a number:
>>> img.transform(img.size, Image.PERSPECTIVE, 128).show()
TypeError: 'int' object is not subscriptable
a81e0a1
to
5bed289
Compare
5bed289
to
7b8d79b
Compare
Hey @vfdev-5! You merged this PR, but no labels were added. The list of valid labels is available at https://github.com/pytorch/vision/blob/main/.github/process_commit.py |
Summary: * Added mid-level ops and feature-based ops * Fixing deadlock in dataloader with circular imports * Added non-scalar fill support workaround for pad * Removed comments * int/float support for fill in pad op * Updated type hints and removed bypass option from mid-level methods * Minor nit fixes Reviewed By: jdsgomes Differential Revision: D37643902 fbshipit-source-id: e62e7274b3ead0c4e68ec5cf1fc8da7f2c0b72bf
Description:
Image.resize
etcSource: #6205