Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OpenDAS
vision
Commits
8fb76e8f
Unverified
Commit
8fb76e8f
authored
May 17, 2021
by
Nicolas Hug
Committed by
GitHub
May 17, 2021
Browse files
Port NMS tests to use pytest and introduce the cpu_only decorator (#3852)
parent
e68d6b03
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
75 additions
and
52 deletions
+75
-52
test/common_utils.py
test/common_utils.py
+13
-0
test/test_ops.py
test/test_ops.py
+62
-52
No files found.
test/common_utils.py
View file @
8fb76e8f
...
...
@@ -409,6 +409,7 @@ def call_args_to_kwargs_only(call_args, *callable_or_arg_names):
def
cpu_and_gpu
():
# TODO: make this properly handle CircleCI
import
pytest
# noqa
# ignore CPU tests in RE as they're already covered by another contbuild
...
...
@@ -430,6 +431,7 @@ def cpu_and_gpu():
def
needs_cuda
(
test_func
):
# TODO: make this properly handle CircleCI
import
pytest
# noqa
if
IN_FBCODE
and
not
IN_RE_WORKER
:
...
...
@@ -441,3 +443,14 @@ def needs_cuda(test_func):
return
test_func
else
:
return
pytest
.
mark
.
skip
(
reason
=
CUDA_NOT_AVAILABLE_MSG
)(
test_func
)
def
cpu_only
(
test_func
):
# TODO: make this properly handle CircleCI
import
pytest
# noqa
if
IN_RE_WORKER
:
# The assumption is that all RE workers have GPUs.
return
pytest
.
mark
.
dont_collect
(
test_func
)
else
:
return
test_func
test/test_ops.py
View file @
8fb76e8f
from
common_utils
import
set_rng_seed
from
common_utils
import
needs_cuda
,
cpu_only
import
math
import
unittest
import
pytest
import
numpy
as
np
...
...
@@ -437,8 +438,8 @@ class MultiScaleRoIAlignTester(unittest.TestCase):
self
.
assertEqual
(
t
.
__repr__
(),
expected_string
)
class
NMS
Test
er
(
unittest
.
TestCase
)
:
def
reference_nms
(
self
,
boxes
,
scores
,
iou_threshold
):
class
Test
NMS
:
def
_
reference_nms
(
self
,
boxes
,
scores
,
iou_threshold
):
"""
Args:
box_scores (N, 5): boxes in corner-form and probabilities.
...
...
@@ -478,65 +479,73 @@ class NMSTester(unittest.TestCase):
scores
=
torch
.
rand
(
N
)
return
boxes
,
scores
def
test_nms
(
self
):
@
cpu_only
@
pytest
.
mark
.
parametrize
(
"iou"
,
(.
2
,
.
5
,
.
8
))
def
test_nms_ref
(
self
,
iou
):
err_msg
=
'NMS incompatible between CPU and reference implementation for IoU={}'
for
iou
in
[
0.2
,
0.5
,
0.8
]:
boxes
,
scores
=
self
.
_create_tensors_with_iou
(
1000
,
iou
)
keep_ref
=
self
.
reference_nms
(
boxes
,
scores
,
iou
)
keep
=
ops
.
nms
(
boxes
,
scores
,
iou
)
self
.
assertTrue
(
torch
.
allclose
(
keep
,
keep_ref
),
err_msg
.
format
(
iou
))
self
.
assertRaises
(
RuntimeError
,
ops
.
nms
,
torch
.
rand
(
4
),
torch
.
rand
(
3
),
0.5
)
self
.
assertRaises
(
RuntimeError
,
ops
.
nms
,
torch
.
rand
(
3
,
5
),
torch
.
rand
(
3
),
0.5
)
self
.
assertRaises
(
RuntimeError
,
ops
.
nms
,
torch
.
rand
(
3
,
4
),
torch
.
rand
(
3
,
2
),
0.5
)
self
.
assertRaises
(
RuntimeError
,
ops
.
nms
,
torch
.
rand
(
3
,
4
),
torch
.
rand
(
4
),
0.5
)
def
test_qnms
(
self
):
boxes
,
scores
=
self
.
_create_tensors_with_iou
(
1000
,
iou
)
keep_ref
=
self
.
_reference_nms
(
boxes
,
scores
,
iou
)
keep
=
ops
.
nms
(
boxes
,
scores
,
iou
)
assert
torch
.
allclose
(
keep
,
keep_ref
),
err_msg
.
format
(
iou
)
@
cpu_only
def
test_nms_input_errors
(
self
):
with
pytest
.
raises
(
RuntimeError
):
ops
.
nms
(
torch
.
rand
(
4
),
torch
.
rand
(
3
),
0.5
)
with
pytest
.
raises
(
RuntimeError
):
ops
.
nms
(
torch
.
rand
(
3
,
5
),
torch
.
rand
(
3
),
0.5
)
with
pytest
.
raises
(
RuntimeError
):
ops
.
nms
(
torch
.
rand
(
3
,
4
),
torch
.
rand
(
3
,
2
),
0.5
)
with
pytest
.
raises
(
RuntimeError
):
ops
.
nms
(
torch
.
rand
(
3
,
4
),
torch
.
rand
(
4
),
0.5
)
@
cpu_only
@
pytest
.
mark
.
parametrize
(
"iou"
,
(.
2
,
.
5
,
.
8
))
@
pytest
.
mark
.
parametrize
(
"scale, zero_point"
,
((
1
,
0
),
(
2
,
50
),
(
3
,
10
)))
def
test_qnms
(
self
,
iou
,
scale
,
zero_point
):
# Note: we compare qnms vs nms instead of qnms vs reference implementation.
# This is because with the int convertion, the trick used in _create_tensors_with_iou
# doesn't really work (in fact, nms vs reference implem will also fail with ints)
err_msg
=
'NMS and QNMS give different results for IoU={}'
for
iou
in
[
0.2
,
0.5
,
0.8
]:
for
scale
,
zero_point
in
((
1
,
0
),
(
2
,
50
),
(
3
,
10
)):
boxes
,
scores
=
self
.
_create_tensors_with_iou
(
1000
,
iou
)
scores
*=
100
# otherwise most scores would be 0 or 1 after int convertion
boxes
,
scores
=
self
.
_create_tensors_with_iou
(
1000
,
iou
)
scores
*=
100
# otherwise most scores would be 0 or 1 after int convertion
qboxes
=
torch
.
quantize_per_tensor
(
boxes
,
scale
=
scale
,
zero_point
=
zero_point
,
dtype
=
torch
.
quint8
)
qscores
=
torch
.
quantize_per_tensor
(
scores
,
scale
=
scale
,
zero_point
=
zero_point
,
dtype
=
torch
.
quint8
)
qboxes
=
torch
.
quantize_per_tensor
(
boxes
,
scale
=
scale
,
zero_point
=
zero_point
,
dtype
=
torch
.
quint8
)
qscores
=
torch
.
quantize_per_tensor
(
scores
,
scale
=
scale
,
zero_point
=
zero_point
,
dtype
=
torch
.
quint8
)
boxes
=
qboxes
.
dequantize
()
scores
=
qscores
.
dequantize
()
boxes
=
qboxes
.
dequantize
()
scores
=
qscores
.
dequantize
()
keep
=
ops
.
nms
(
boxes
,
scores
,
iou
)
qkeep
=
ops
.
nms
(
qboxes
,
qscores
,
iou
)
keep
=
ops
.
nms
(
boxes
,
scores
,
iou
)
qkeep
=
ops
.
nms
(
qboxes
,
qscores
,
iou
)
self
.
assert
True
(
torch
.
allclose
(
qkeep
,
keep
),
err_msg
.
format
(
iou
)
)
assert
torch
.
allclose
(
qkeep
,
keep
),
err_msg
.
format
(
iou
)
@
unittest
.
skipIf
(
not
torch
.
cuda
.
is_available
(),
"CUDA unavailable"
)
def
test_nms_cuda
(
self
,
dtype
=
torch
.
float64
):
@
needs_cuda
@
pytest
.
mark
.
parametrize
(
"iou"
,
(.
2
,
.
5
,
.
8
))
def
test_nms_cuda
(
self
,
iou
,
dtype
=
torch
.
float64
):
tol
=
1e-3
if
dtype
is
torch
.
half
else
1e-5
err_msg
=
'NMS incompatible between CPU and CUDA for IoU={}'
for
iou
in
[
0.2
,
0.5
,
0.8
]:
boxes
,
scores
=
self
.
_create_tensors_with_iou
(
1000
,
iou
)
r_cpu
=
ops
.
nms
(
boxes
,
scores
,
iou
)
r_cuda
=
ops
.
nms
(
boxes
.
cuda
(),
scores
.
cuda
(),
iou
)
is_eq
=
torch
.
allclose
(
r_cpu
,
r_cuda
.
cpu
())
if
not
is_eq
:
# if the indices are not the same, ensure that it's because the scores
# are duplicate
is_eq
=
torch
.
allclose
(
scores
[
r_cpu
],
scores
[
r_cuda
.
cpu
()],
rtol
=
tol
,
atol
=
tol
)
self
.
assertTrue
(
is_eq
,
err_msg
.
format
(
iou
))
@
unit
test
.
skipIf
(
not
torch
.
cuda
.
is_available
(),
"CUDA unavailable"
)
def
test_autocast
(
se
lf
)
:
for
dtype
in
(
torch
.
float
,
torch
.
half
):
with
torch
.
cuda
.
amp
.
autocast
():
self
.
test_nms_cuda
(
dtype
=
dtype
)
@
unittest
.
skipIf
(
not
torch
.
cuda
.
is_available
(),
"CUDA unavailable"
)
boxes
,
scores
=
self
.
_create_tensors_with_iou
(
1000
,
iou
)
r_cpu
=
ops
.
nms
(
boxes
,
scores
,
iou
)
r_cuda
=
ops
.
nms
(
boxes
.
cuda
()
,
scores
.
cuda
()
,
iou
)
is_eq
=
torch
.
allclose
(
r_cpu
,
r_cuda
.
cpu
())
if
not
is_eq
:
#
if
the indices are not the same, ensure that it's because the scores
# are duplicate
is_eq
=
torch
.
allclose
(
scores
[
r_cpu
],
scores
[
r_cuda
.
cpu
()],
rtol
=
tol
,
atol
=
tol
)
assert
is_eq
,
err_msg
.
format
(
iou
)
@
needs_cuda
@
py
test
.
mark
.
parametrize
(
"iou"
,
(.
2
,
.
5
,
.
8
)
)
@
pytest
.
mark
.
parametrize
(
"dtype"
,
(
torch
.
float
,
torch
.
ha
lf
)
)
def
test_autocast
(
self
,
iou
,
dtype
):
with
torch
.
cuda
.
amp
.
autocast
():
self
.
test_nms_cuda
(
iou
=
iou
,
dtype
=
dtype
)
@
needs_cuda
def
test_nms_cuda_float16
(
self
):
boxes
=
torch
.
tensor
([[
285.3538
,
185.5758
,
1193.5110
,
851.4551
],
[
285.1472
,
188.7374
,
1192.4984
,
851.0669
],
...
...
@@ -546,8 +555,9 @@ class NMSTester(unittest.TestCase):
iou_thres
=
0.2
keep32
=
ops
.
nms
(
boxes
,
scores
,
iou_thres
)
keep16
=
ops
.
nms
(
boxes
.
to
(
torch
.
float16
),
scores
.
to
(
torch
.
float16
),
iou_thres
)
self
.
assert
True
(
torch
.
all
(
torch
.
eq
(
keep32
,
keep16
))
)
assert
torch
.
all
(
torch
.
eq
(
keep32
,
keep16
))
@
cpu_only
def
test_batched_nms_implementations
(
self
):
"""Make sure that both implementations of batched_nms yield identical results"""
...
...
@@ -564,11 +574,11 @@ class NMSTester(unittest.TestCase):
keep_trick
=
ops
.
boxes
.
_batched_nms_coordinate_trick
(
boxes
,
scores
,
idxs
,
iou_threshold
)
err_msg
=
"The vanilla and the trick implementation yield different nms outputs."
self
.
assert
True
(
torch
.
allclose
(
keep_vanilla
,
keep_trick
),
err_msg
)
assert
torch
.
allclose
(
keep_vanilla
,
keep_trick
),
err_msg
# Also make sure an empty tensor is returned if boxes is empty
empty
=
torch
.
empty
((
0
,),
dtype
=
torch
.
int64
)
self
.
assert
True
(
torch
.
allclose
(
empty
,
ops
.
batched_nms
(
empty
,
None
,
None
,
None
))
)
assert
torch
.
allclose
(
empty
,
ops
.
batched_nms
(
empty
,
None
,
None
,
None
))
class
DeformConvTester
(
OpTester
,
unittest
.
TestCase
):
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment