-
Notifications
You must be signed in to change notification settings - Fork 7.1k
[prototype] Align and Clean up transform types #6627
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.
Some clarifications:
if has_any(inputs, PIL.Image.Image, features.BoundingBox, features.Mask, features.Label): | ||
raise TypeError( | ||
f"{type(self).__name__}() does not support PIL images, bounding boxes, masks and plain labels." | ||
) |
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 believe we previously missed PIL images which are not supported by this class.
@@ -223,7 +223,7 @@ def _copy_paste( | |||
# This is something different to TF implementation we introduced here as | |||
# originally the algorithm works on equal-sized data | |||
# (for example, coming from LSJ data augmentations) | |||
size1 = image.shape[-2:] | |||
size1 = cast(List[int], image.shape[-2:]) |
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.
mypy started to randomly complain once the image type was defined, so I choose the cast it. Happy to ignore for performance reasons.
@@ -52,7 +52,7 @@ def __init__(self, num_output_channels: Literal[1, 3] = 1) -> None: | |||
super().__init__() | |||
self.num_output_channels = num_output_channels | |||
|
|||
def _transform(self, inpt: DType, params: Dict[str, Any]) -> DType: | |||
def _transform(self, inpt: ImageType, params: Dict[str, Any]) -> ImageType: |
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.
Previously defined incorrect type.
@@ -68,7 +68,7 @@ def forward(self, *inputs: Any) -> Any: | |||
|
|||
return super().forward(*inputs) | |||
|
|||
def _transform(self, inpt: Union[torch.Tensor, features._Feature], params: Dict[str, Any]) -> torch.Tensor: | |||
def _transform(self, inpt: TensorImageType, params: Dict[str, Any]) -> 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.
Incorrectly defined features._Feature
previously. It should have been features.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.
I'm ok with the approach. I think we can update fill type handling and avoid mutiple fill = self.fill[type(inpt)] if self.fill is not None else None
-> fill = self.fill[type(inpt)]
To do this, we need to force |
Co-authored-by: vfdev <[email protected]>
They will already different if you pass a scalar right ? |
Sounds good, let me try your proposal to force |
_check_fill_arg(fill) | ||
|
||
if isinstance(fill, dict): | ||
return fill | ||
|
||
return defaultdict(lambda: fill) # type: ignore[return-value] | ||
return defaultdict(lambda: fill) # type: ignore[return-value, arg-type] |
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 don't do the arg-type
ignore we get:
torchvision/prototype/transforms/_utils.py:42: error: Argument 1 to
"defaultdict" has incompatible type
"Callable[[], Union[Union[int, float, Sequence[int], Sequence[float], None], Dict[Type[Any], Union[int, float, Sequence[int], Sequence[float], None]]]]";
expected "Optional[Callable[[], Union[float, Sequence[float], None]]]"
[arg-type]
return defaultdict(lambda: fill) # type: ignore[return-value]
^
torchvision/prototype/transforms/_utils.py:42: note: Error code "arg-type" not covered by "type: ignore" comment
Found 1 error in 1 file (checked 222 source files)
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 this is worth investigating. At some point mypy
thinks that fill can also be an int
. Although that is part of the correct type, it shouldn't be for the actual type. I guess we forgot to update something. @datumbox I can look into this if you want.
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 agree. I've also felt that this was worth noting. I have no idea if this is a failure or mypy or it's seeing something I dont. If you have any input, I would love to get an advice.
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 false alarm by mypy
right before the line mypy
thinks that fill
has type
Union[builtins.float, typing.Sequence[builtins.float], None]
Note that for mypy
float == Union[int, float]
or to illustrate this more: for mypy
the following is true while this will fail for Python isinstance(0, float)
. I'm not sure why they went for this, but generic numbers support is sketchy in general. This is why you don't see the int
and Sequence[int]
here because for mypy
they are subtypes of float
and Sequence[float]
and therefore redundant.
However, at the time we arrive at the line, mypy
suddenly thinks that fill
can be a Dict
again. I can't find the issue now, but this is longstanding. For some reason mypy
doesn't forward the more concrete type to inner functions like this lambda
.
Both
return defaultdict(lambda: fill) # type: ignore[return-value, arg-type] | |
return defaultdict(lambda: cast(FillType, fill)) |
and
return defaultdict(lambda: fill) # type: ignore[return-value, arg-type] | |
return defaultdict(cast(Callable[[], FillType], lambda: fill)) |
will eliminate both previously ignored errors, but will raise a new redundant-cast
error 🙄
Thus, we have to ignore one either way and not having cast
in there makes it more readable.
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.
One comment inline other LGTM. I'm kinda worried that we need more and more of these workaround to avoid import cycles in the future though.
_check_fill_arg(fill) | ||
|
||
if isinstance(fill, dict): | ||
return fill | ||
|
||
return defaultdict(lambda: fill) # type: ignore[return-value] | ||
return defaultdict(lambda: fill) # type: ignore[return-value, arg-type] |
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 this is worth investigating. At some point mypy
thinks that fill can also be an int
. Although that is part of the correct type, it shouldn't be for the actual type. I guess we forgot to update something. @datumbox I can look into this if you want.
There is an organic way to solve this, which is leaving the definitions as part of |
a0e1673
to
1d6d12b
Compare
InputType = Union[torch.Tensor, PIL.Image.Image, _Feature] | ||
InputTypeJIT = 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.
Previously DType
.
FillType = Union[int, float, Sequence[int], Sequence[float], None] | ||
FillTypeJIT = Union[int, float, List[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.
Could move to another file if we don't want it 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.
I think it is ok there. I would prefer to have everything in a _typing.py
file, but as you explained this will give us circular imports due to the method signatures. Thus, let's take the way of the least resistance.
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 a lot better now, thanks a lot @datumbox! One question inline. I'll have a look at the mypy
ignore now.
1d6d12b
to
ad60b50
Compare
There seems to be a breakage from TorchData on our tests. |
16e00b0
to
f3cefdf
Compare
The tests are now passing. I'll restore the TorchData tests and merge. |
Summary: * Align and Clean up transform types * Move type definitions to `_utils.py` * fixing error message on tests * Apply code review suggestions * Centralizing types and switching to always getting dicts. * Fixing linter * Refactoring typing definitions. * Remove relative imports. * Reuse type. * Temporarily remove the TorchData tests. * Restore the TorchData tests. Reviewed By: YosuaMichael Differential Revision: D39885420 fbshipit-source-id: bcc71cdea6dfd797eb8ac27ee9cb49deaf4dd95f Co-authored-by: vfdev <[email protected]> Co-authored-by: vfdev <[email protected]>
This PR: