Skip to content

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

Closed
wants to merge 65 commits into from

Conversation

oke-aditya
Copy link
Contributor

@oke-aditya oke-aditya commented Jul 29, 2021

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

Copy link
Contributor Author

@oke-aditya oke-aditya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @pmeier @frgfm 😄 would be great if you have a look

@oke-aditya oke-aditya changed the title Add typings to FasterRCNN, MaskRCNN and RetinaNet Added annotations to models/detection FasterRCNN, MaskRCNN and RetinaNet Jul 29, 2021
@oke-aditya oke-aditya changed the title Added annotations to models/detection FasterRCNN, MaskRCNN and RetinaNet Added annotations to models/detection FasterRCNN, MaskRCNN and RetinaNet Jul 29, 2021
@oke-aditya oke-aditya changed the title Added annotations to models/detection FasterRCNN, MaskRCNN and RetinaNet Added typing annotations to models/detection FasterRCNN, MaskRCNN and RetinaNet Jul 29, 2021
Copy link
Contributor

@datumbox datumbox left a 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,
Copy link
Contributor

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:

image_mean: Optional[List[float]] = None, image_std: Optional[List[float]] = None,

Copy link
Contributor

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?

Copy link
Contributor

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, ...]]

Copy link
Contributor Author

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].

@oke-aditya oke-aditya changed the title Added typing annotations to models/detection FasterRCNN, MaskRCNN and RetinaNet Added typing annotations to models/detection FasterRCNN, MaskRCNN and RetinaNet, generalized_rcnn, keypoint_rcnn, rpn Jul 29, 2021
Copy link
Contributor

@frgfm frgfm left a 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,
Copy link
Contributor

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,
Copy link
Contributor

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,
Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Collaborator

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?

Copy link
Contributor Author

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?

Copy link
Collaborator

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:

  1. Annotate with a custom class such as RPNHead that has this attribute.
  2. Create a typing.Protocol that defines the interface we are looking for. In the example above the object passed to box_head needs to be callable and define an attribute foo.

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.

@oke-aditya
Copy link
Contributor Author

I have combined the PRs. I will address all the above comments 😃

Copy link
Contributor Author

@oke-aditya oke-aditya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know the thoughts.

I can't type some internal forward functions. 😕

cc @frgfm

min_size: int = 800,
max_size: int = 1333,
image_mean: Optional[Tuple[float]] = None,
image_std: Optional[Tuple[float]] = None,
Copy link
Contributor Author

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,
Copy link
Contributor Author

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.

@oke-aditya oke-aditya changed the title Added typing annotations to models/detection FasterRCNN, MaskRCNN and RetinaNet, generalized_rcnn, keypoint_rcnn, rpn Added typing annotations to models/detection Jul 29, 2021
Copy link
Contributor Author

@oke-aditya oke-aditya left a 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.

Copy link
Collaborator

@pmeier pmeier left a 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:
Copy link
Collaborator

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]],
Copy link
Collaborator

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.

Copy link
Contributor Author

@oke-aditya oke-aditya left a 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:
Copy link
Contributor Author

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]],
Copy link
Contributor Author

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.

@oke-aditya oke-aditya requested a review from pmeier September 23, 2021 16:43
Copy link
Collaborator

@pmeier pmeier left a 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.

@oke-aditya
Copy link
Contributor Author

I can split it, bit since the files are highly coupled, it makes hard to annotate in multiple PRs.

@pmeier
Copy link
Collaborator

pmeier commented Sep 24, 2021

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:

  1. image_list.py
  2. _utils.py
  3. anchor_utils.py
  4. roi_heads.py
  5. transform.py
  6. rpn.py
    ...

Copy link
Contributor Author

@oke-aditya oke-aditya left a 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)):
Copy link
Contributor Author

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.

@oke-aditya
Copy link
Contributor Author

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.

@oke-aditya
Copy link
Contributor Author

Using μfmt and setting mypy setting to allow redefinition has made this PR lot easier !!!!

Current error count has decreased to.

Found 32 errors in 8 files (checked 128 source files)

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 :( .

@oke-aditya oke-aditya requested a review from pmeier October 5, 2021 18:28
@pmeier
Copy link
Collaborator

pmeier commented Oct 6, 2021

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.

@oke-aditya
Copy link
Contributor Author

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.

@oke-aditya oke-aditya closed this Oct 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants