-
Notifications
You must be signed in to change notification settings - Fork 7k
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
Adds torchscript Compatibility to box_convert #2737
Conversation
# def test_bbox_convert_jit(self): | ||
# box_tensor = torch.tensor([[0, 0, 100, 100], [0, 0, 0, 0], | ||
# [10, 15, 30, 35], [23, 35, 93, 95]], dtype=torch.float) | ||
def test_bbox_convert_jit(self): |
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 wondering if it would be more simple to check scripted function in all your test_bbox_*
:
scripted_fn = torch.jit.script(ops.box_convert)
for fn in [ops.box_convert, scripted_fn]:
# ...
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.
Possible, this PR is open for a lot of refactoring 👍 .
Error says it is missing |
Is there a need for |
I think we have to recode def box_convert(boxes: Tensor, in_fmt: str, out_fmt: str) -> Tensor:
if in_fmt == out_fmt:
boxes_converted = boxes.clone()
return boxes_converted
if in_fmt == "xyxy":
if out_fmt == "cxcywh":
return _box_xyxy_to_cxcywh(boxes)
else:
raise ValueError("...")
elif in_fmt == "xywh":
if out_fmt == "xyxy":
return _box_xywh_to_xyxy(boxes)
else:
raise ValueError("...")
elif in_fmt == "cxcywh":
if out_fmt == "xyxy":
return _box_cxcywh_to_xyxy(boxes)
else:
raise ValueError("...")
else:
raise ValueError("...") Any thoughts ? PS: maybe we have to replace assert by vision/torchvision/ops/boxes.py Line 158 in 6cbdf27
|
Let @fmassa have a look. |
Because some clauses do not define the output and it is not evident whether they couldn't happen in runtime. |
Oh now I get the point. IIRC JIT needs to know what will exactly happen to code in future so it needs all possibilities right? |
Codecov Report
@@ Coverage Diff @@
## master #2737 +/- ##
==========================================
+ Coverage 72.42% 72.75% +0.32%
==========================================
Files 96 96
Lines 8313 8309 -4
Branches 1293 1292 -1
==========================================
+ Hits 6021 6045 +24
+ Misses 1903 1871 -32
- Partials 389 393 +4
Continue to review full report at Codecov.
|
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 update.
torchvision/ops/boxes.py
Outdated
@@ -167,25 +167,35 @@ def box_convert(boxes: Tensor, in_fmt: str, out_fmt: str) -> Tensor: | |||
boxes_xyxy = _box_xywh_to_xyxy(boxes) | |||
if out_fmt == "cxcywh": | |||
boxes_converted = _box_xyxy_to_cxcywh(boxes_xyxy) | |||
else: | |||
raise ValueError("Unsupported Boundig Box Conversions for given in_fmt and out_fmt") |
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 would be nice to add tests to improve the coverage.
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 assert statement before these ValueError
codeblocks. So the test will never reach this stage. Should I remove the assert
statements?
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, please
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.
Added 👍
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 a lot for the work @oke-aditya !
TorchScript can be tricky at times, and many times we don't quite understand what's going on and why it doesn't want to accept our code.
Your PR looks ok to me (although I would prefer to keep the assertion checks in lines 157-159).
I've also committed two different refactoring proposals in https://github.com/fmassa/vision-1/tree/proposal_box_convert_torchscript (one in each commit), which I think could make the code reading easier and with less checks.
Let me know what you think!
torchvision/ops/boxes.py
Outdated
allowed_fmts = ("xyxy", "xywh", "cxcywh") | ||
assert in_fmt in allowed_fmts | ||
assert out_fmt in allowed_fmts |
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.
Is this not supported by torchscript?
I just tried the following and it worked:
In [1]: import torch
In [2]: def f(x: str):
...: L = ('a', 'b')
...: assert x in L
...: return x
...:
In [3]: ff = torch.jit.script(f)
In [4]: print(ff.code)
def f(x: str) -> str:
if torch.__contains__(["a", "b"], x):
pass
else:
ops.prim.RaiseException("AssertionError: ")
return x
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 asked @vfdev-5 and removed it. We removed it since then tests will not trigger value error as assert would stop this. I added tests to check ValueError
too. I am not sure if I should add it 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.
If assert
is not good, then let's change instead to ValueError
, something like
if in_fmt not in allowed_fmts:
raise ValueError(...)
Having those checks is important for better user experience, otherwise their code could silently fail
torchvision/ops/boxes.py
Outdated
@@ -167,25 +164,35 @@ def box_convert(boxes: Tensor, in_fmt: str, out_fmt: str) -> Tensor: | |||
boxes_xyxy = _box_xywh_to_xyxy(boxes) | |||
if out_fmt == "cxcywh": | |||
boxes_converted = _box_xyxy_to_cxcywh(boxes_xyxy) | |||
else: | |||
raise ValueError("Unsupported Bounding Box Conversions for given in_fmt and out_fmt") |
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.
You don't actually need those to make torchscript happy. The problem is that boxes_converted
is not defined in the other branch.
You can define it outside of the if
so that torchscript knows its type.
Both proposals look fine to me. I guess it is matter of coding style. |
@oke-aditya we don't need def f(a):
a = a + 1
return a doesn't change the original def f(a):
a += 1
return a performs it in-place. The advantage of proposal 2 is that it's slightly simpler, as we don't need to have some torchscript-specific constructs like defining first the type of the variable, and having to assert it being |
Sounds great. Let me re-run with proposal2 with the ValueError tests as well. Once the tests pass. I will push 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.
Thanks a lot!
* fixies small bug in box_convert * activates jit test * Passes JIT test * fixes typo * adds error tests, removes assert * implements to proposal2
* fixies small bug in box_convert * activates jit test * Passes JIT test * fixes typo * adds error tests, removes assert * implements to proposal2
Follow up of #2710 .