-
Notifications
You must be signed in to change notification settings - Fork 7.1k
Added typing annotations to models/detection [1/n] #4220
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.
models/detection
FasterRCNN
, MaskRCNN
and RetinaNet
models/detection
FasterRCNN
, MaskRCNN
and RetinaNet
models/detection
FasterRCNN
, MaskRCNN
and RetinaNet
models/detection
FasterRCNN
, MaskRCNN
and RetinaNet
models/detection
FasterRCNN
, MaskRCNN
and RetinaNet
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 @oke-aditya.
I had a first pass and added a few comments. Let me know what you think. Please note that my current setup does not allow me to do thorough reviews, so I will leave it for someone else from the team to approve (or wait until I'm properly back. :) )
min_size: int = 800, | ||
max_size: int = 1333, | ||
image_mean: Optional[Tuple[float]] = None, | ||
image_std: Optional[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.
I think it should be List[float]
instead of Tuple[float]
?
Similar to:
vision/torchvision/models/detection/ssd.py
Line 169 in 3e7653c
image_mean: Optional[List[float]] = None, image_std: Optional[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.
I would argue that the typing in ssd is too specific: it can be a Optional[List[float]]
indeed, but Optional[Tuple[float, float, float]]
is accepted since it will be passed down to https://github.com/pytorch/vision/blob/master/torchvision/models/detection/transform.py#L136-L137 for the transformations
Should we use a Union
or enforce a List
?
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.
actually, I just saw your comment later on, since the backbone can take any number of channels in the input tensor, the second option is actually Optional[Tuple[float, ...]]
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 have changed to Optional[Tuple[float, ...]]
Since in the docstring we mention Tuple[float, float, float]
.
models/detection
FasterRCNN
, MaskRCNN
and RetinaNet
models/detection
FasterRCNN
, MaskRCNN
and RetinaNet
, generalized_rcnn
, keypoint_rcnn
, rpn
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 the help!
You forgot typing annotations on the forward
methods of most classes of faster_rcnn.py
module, I added comments about things that weren't already pointed out by @datumbox!
min_size: int = 800, | ||
max_size: int = 1333, | ||
image_mean: Optional[Tuple[float]] = None, | ||
image_std: Optional[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.
I would argue that the typing in ssd is too specific: it can be a Optional[List[float]]
indeed, but Optional[Tuple[float, float, float]]
is accepted since it will be passed down to https://github.com/pytorch/vision/blob/master/torchvision/models/detection/transform.py#L136-L137 for the transformations
Should we use a Union
or enforce a List
?
min_size: int = 800, | ||
max_size: int = 1333, | ||
image_mean: Optional[Tuple[float]] = None, | ||
image_std: Optional[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.
actually, I just saw your comment later on, since the backbone can take any number of channels in the input tensor, the second option is actually Optional[Tuple[float, ...]]
rpn_score_thresh: float = 0.0, | ||
# Box parameters | ||
box_roi_pool: Optional[MultiScaleRoIAlign] = None, | ||
box_head: Optional[nn.Module] = 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.
box_head: Optional[RPNHead] = None
I believe
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 was bit unsure on these. I think that user can write their own classes and just pass them to FasterRCNN
.
Hence I thought it shouldn't be so particular class like RPNHead
.
Let me know, I will change all of these otherwise.
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 allow passing custom classes, we need structural duck typing here. Meaning we need to define a custom typing.Protocol
that defines how these objects need to look like. @oke-aditya can you make a list of all the attributes we access and methods we call on these objects?
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 unsure of what you mean @pmeier . I'm new to type hints.
I think trying to be very strict in typing by custom classes might be too much. If nn.Module
satisfies the purpose I think it should be good enough?
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 nn.Module satisfies the purpose I think it should be good enough?
Yes. As a rule of thumb, the input types should be as loose as possible and the output types as strict as possible.
After some digging, I think
box_features = self.box_head(box_features) |
is the only time we do anything with the object passed as box_head
. Since it is possible to call an nn.Module
, using it as annotation is fine.
In other cases @frgfm's comment might have some merit in other cases: for example, if we at some point access box_head.foo
, nn.Module
is not sufficient as annotation, since it doesn't define a foo
attribute. In such a case we have two options:
- Annotate with a custom class such as
RPNHead
that has this attribute. - Create a
typing.Protocol
that defines the interface we are looking for. In the example above the object passed tobox_head
needs to be callable and define an attributefoo
.
Especially if users are allowed to pass custom objects, I would always prefer 2. over 1. because it doesn't require any changes on the user side. I don't know if this works with torchscript though.
Let's discuss this if the need for something like this arises.
I have combined the PRs. I will address all the above 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.
min_size: int = 800, | ||
max_size: int = 1333, | ||
image_mean: Optional[Tuple[float]] = None, | ||
image_std: Optional[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.
I have changed to Optional[Tuple[float, ...]]
Since in the docstring we mention Tuple[float, float, float]
.
rpn_score_thresh: float = 0.0, | ||
# Box parameters | ||
box_roi_pool: Optional[MultiScaleRoIAlign] = None, | ||
box_head: Optional[nn.Module] = 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.
I was bit unsure on these. I think that user can write their own classes and just pass them to FasterRCNN
.
Hence I thought it shouldn't be so particular class like RPNHead
.
Let me know, I will change all of these otherwise.
models/detection
FasterRCNN
, MaskRCNN
and RetinaNet
, generalized_rcnn
, keypoint_rcnn
, rpn
models/detection
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 small changelog, we aren't done yet but would say halfway there.
Mypy now complains slightly less.
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.
Hey @oke-aditya, I did another pass. I have some comments inline. This gets harder and harder to review due to the sheer amount of changes. I suggest we split this into multiple PRs for each file for the "final" review, if we are mostly happy here.
@@ -461,11 +461,10 @@ def _onnx_paste_masks_in_image_loop(masks, boxes, im_h, im_w): | |||
return res_append | |||
|
|||
|
|||
def paste_masks_in_image(masks, boxes, img_shape, padding=1): | |||
# type: (Tensor, Tensor, Tuple[int, int], int) -> Tensor | |||
def paste_masks_in_image(masks: Tensor, boxes: Tensor, img_shape: List[int], padding: int = 1) -> 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.
Since this file is currently ignored by mypy
, let's not introduce annotations here.
@@ -222,8 +237,8 @@ def batch_images(self, images: List[Tensor], size_divisible: int = 32) -> Tensor | |||
|
|||
def postprocess(self, | |||
result: List[Dict[str, Tensor]], | |||
image_shapes: List[Tuple[int, int]], |
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.
Thought the convention seems to be using
List[List[int]]
everywhere
If that is the convention, let's also roll with it here. Although I would prefer Tuple[int, int]
, because an image size is always of length 2 and not arbitrary.
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.
HI! Feel free to add your thoughts. I get that this PR modfiies lot of files which makes reviewing a harder as we progress.
Once we finalize, I will send PR for each file individually!
@@ -461,11 +461,10 @@ def _onnx_paste_masks_in_image_loop(masks, boxes, im_h, im_w): | |||
return res_append | |||
|
|||
|
|||
def paste_masks_in_image(masks, boxes, img_shape, padding=1): | |||
# type: (Tensor, Tensor, Tuple[int, int], int) -> Tensor | |||
def paste_masks_in_image(masks: Tensor, boxes: Tensor, img_shape: List[int], padding: int = 1) -> 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.
Well I have to annotate it due to the img_shape
which should be Tuple[int, int], Otherwise mypy will compain in other places.
@@ -222,8 +237,8 @@ def batch_images(self, images: List[Tensor], size_divisible: int = 32) -> Tensor | |||
|
|||
def postprocess(self, | |||
result: List[Dict[str, Tensor]], | |||
image_shapes: List[Tuple[int, int]], |
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.
Ok so I will refactor to Tuple[int, int]
. This might be bc breaking as it changes the old annotation of List[int]
and users would get error if they were not passing List of length 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.
I get that this PR modfiies lot of files which makes reviewing a harder as we progress. Once we finalize, I will send PR for each file individually!
I would prefer to split sooner than later. It is really hard to judge what comments need addressing since there are 10+ open conversations.
I can split it, bit since the files are highly coupled, it makes hard to annotate in multiple PRs. |
Start with the one that has no dependencies and work up the chain:
|
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 small review, I will split this PR soon. I have substantially reduced mypy errors.
I think we can start with _utils and utility files, these would help making this PR easier.
size_divisible: int = 32, | ||
fixed_size: Optional[Tuple[int, int]] = None | ||
) -> None: | ||
|
||
super(GeneralizedRCNNTransform, self).__init__() | ||
if not isinstance(min_size, (list, tuple)): |
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.
So basically it should Union[int, List[int]]
. Since if we are passing int values in some cases, we will be converting it to tuple / list.
One of the problems in typing utils and leading errors to mypy are because of #4389, as per discussion there, we can't use nn.Module to type the detection utils, so it some errors for mypy remain. |
Using μfmt and setting mypy setting to allow redefinition has made this PR lot easier !!!! Current error count has decreased to.
If I recall well the error count was close to 100 in the beginning. We are getting there. I'm sorry that It's bit ugly work from my side by not splitting the PR and tackling easier files first. I have been very slow on working this PR :( . |
No worries, this PR is not blocking anything. We are grateful that you are picking up the work! Just note, that I'm not going to review this PR any further before the split. It just takes to much time to read all the discussions over and over again to get up to speed. |
Hi all. I'm closing this PR and today I will split this up into multiple ones. Each file will unblock only the particular file which mypy needs to address. That is mypy will ignore all the other files that aren't going to be modified by each independent PR. |
Linked with #2025
I'm unsure of few types. I will tag them.
EDIT:
I will go 3 files at a time, otherwise the diff would be too big to review.I will finish major files in this PR and follow-up another for utils.
Also note that I have re-written the code a bit so that it looks similar to black / vertical code formatting which would reduce the diffs when any such tool is used.
cc @pmeier