Skip to content

Commit 860669e

Browse files
authored
Merge branch 'master' into feature/retina_emptyannotation
2 parents c5d2868 + b8b08ac commit 860669e

23 files changed

+252
-215
lines changed

.circleci/unittest/linux/scripts/run_test.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ set -e
55
eval "$(./conda/bin/conda shell.bash hook)"
66
conda activate ./env
77

8+
export PYTORCH_TEST_WITH_SLOW='1'
89
python -m torch.utils.collect_env
9-
pytest --cov=torchvision --junitxml=test-results/junit.xml -v --durations 20 test --ignore=test/test_datasets_download.py
10+
pytest --cov=torchvision --junitxml=test-results/junit.xml -v --durations 20 test --ignore=test/test_datasets_download.py

.circleci/unittest/windows/scripts/run_test.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ set -e
55
eval "$(./conda/Scripts/conda.exe 'shell.bash' 'hook')"
66
conda activate ./env
77

8+
export PYTORCH_TEST_WITH_SLOW='1'
89
python -m torch.utils.collect_env
9-
pytest --cov=torchvision --junitxml=test-results/junit.xml -v --durations 20 test --ignore=test/test_datasets_download.py
10+
pytest --cov=torchvision --junitxml=test-results/junit.xml -v --durations 20 test --ignore=test/test_datasets_download.py

docs/source/io.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Example of inspecting a video:
3535
import torchvision
3636
video_path = "path to a test video"
3737
# Constructor allocates memory and a threaded decoder
38-
# instance per video. At the momet it takes two arguments:
38+
# instance per video. At the moment it takes two arguments:
3939
# path to the video file, and a wanted stream.
4040
reader = torchvision.io.VideoReader(video_path, "video")
4141

mypy.ini

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ ignore_errors = True
1212

1313
ignore_errors = True
1414

15+
[mypy-torchvision.models.densenet.*]
16+
17+
ignore_errors=True
18+
1519
[mypy-torchvision.models.detection.*]
1620

1721
ignore_errors = True

test/common_utils.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import sys
88
import io
99
import torch
10-
import errno
10+
import warnings
1111
import __main__
1212

1313
from numbers import Number
@@ -265,14 +265,21 @@ def assertTensorsEqual(a, b):
265265
else:
266266
super(TestCase, self).assertEqual(x, y, message)
267267

268-
def checkModule(self, nn_module, args, unwrapper=None, skip=False):
268+
def check_jit_scriptable(self, nn_module, args, unwrapper=None, skip=False):
269269
"""
270270
Check that a nn.Module's results in TorchScript match eager and that it
271271
can be exported
272272
"""
273273
if not TEST_WITH_SLOW or skip:
274274
# TorchScript is not enabled, skip these tests
275-
return
275+
msg = "The check_jit_scriptable test for {} was skipped. " \
276+
"This test checks if the module's results in TorchScript " \
277+
"match eager and that it can be exported. To run these " \
278+
"tests make sure you set the environment variable " \
279+
"PYTORCH_TEST_WITH_SLOW=1 and that the test is not " \
280+
"manually skipped.".format(nn_module.__class__.__name__)
281+
warnings.warn(msg, RuntimeWarning)
282+
return None
276283

277284
sm = torch.jit.script(nn_module)
278285

@@ -284,7 +291,7 @@ def checkModule(self, nn_module, args, unwrapper=None, skip=False):
284291
if unwrapper:
285292
script_out = unwrapper(script_out)
286293

287-
self.assertEqual(eager_out, script_out)
294+
self.assertEqual(eager_out, script_out, prec=1e-4)
288295
self.assertExportImportModule(sm, args)
289296

290297
return sm

test/test_datasets_video_utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,16 @@ def test_compute_clips_for_video(self):
119119
self.assertTrue(clips.equal(idxs))
120120
self.assertTrue(idxs.flatten().equal(resampled_idxs))
121121

122+
# case 3: frames aren't enough for a clip
123+
num_frames = 32
124+
orig_fps = 30
125+
new_fps = 13
126+
with self.assertWarns(UserWarning):
127+
clips, idxs = VideoClips.compute_clips_for_video(video_pts, num_frames, num_frames,
128+
orig_fps, new_fps)
129+
self.assertEqual(len(clips), 0)
130+
self.assertEqual(len(idxs), 0)
131+
122132

123133
if __name__ == '__main__':
124134
unittest.main()

test/test_image.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@
22
import io
33
import glob
44
import unittest
5-
import sys
65

76
import torch
8-
import torchvision
97
from PIL import Image
108
from torchvision.io.image import (
119
decode_png, decode_jpeg, encode_jpeg, write_jpeg, decode_image, read_file,
12-
encode_png, write_png, write_file)
10+
encode_png, write_png, write_file, ImageReadMode)
1311
import numpy as np
1412

1513
from common_utils import get_tmp_dir
@@ -49,9 +47,9 @@ def normalize_dimensions(img_pil):
4947

5048
class ImageTester(unittest.TestCase):
5149
def test_decode_jpeg(self):
52-
conversion = [(None, 0), ("L", 1), ("RGB", 3)]
50+
conversion = [(None, ImageReadMode.UNCHANGED), ("L", ImageReadMode.GRAY), ("RGB", ImageReadMode.RGB)]
5351
for img_path in get_images(IMAGE_ROOT, ".jpg"):
54-
for pil_mode, channels in conversion:
52+
for pil_mode, mode in conversion:
5553
with Image.open(img_path) as img:
5654
is_cmyk = img.mode == "CMYK"
5755
if pil_mode is not None:
@@ -66,7 +64,7 @@ def test_decode_jpeg(self):
6664

6765
img_pil = normalize_dimensions(img_pil)
6866
data = read_file(img_path)
69-
img_ljpeg = decode_image(data, channels=channels)
67+
img_ljpeg = decode_image(data, mode=mode)
7068

7169
# Permit a small variation on pixel values to account for implementation
7270
# differences between Pillow and LibJPEG.
@@ -165,17 +163,18 @@ def test_write_jpeg(self):
165163
self.assertEqual(torch_bytes, pil_bytes)
166164

167165
def test_decode_png(self):
168-
conversion = [(None, 0), ("L", 1), ("LA", 2), ("RGB", 3), ("RGBA", 4)]
166+
conversion = [(None, ImageReadMode.UNCHANGED), ("L", ImageReadMode.GRAY), ("LA", ImageReadMode.GRAY_ALPHA),
167+
("RGB", ImageReadMode.RGB), ("RGBA", ImageReadMode.RGB_ALPHA)]
169168
for img_path in get_images(FAKEDATA_DIR, ".png"):
170-
for pil_mode, channels in conversion:
169+
for pil_mode, mode in conversion:
171170
with Image.open(img_path) as img:
172171
if pil_mode is not None:
173172
img = img.convert(pil_mode)
174173
img_pil = torch.from_numpy(np.array(img))
175174

176175
img_pil = normalize_dimensions(img_pil)
177176
data = read_file(img_path)
178-
img_lpng = decode_image(data, channels=channels)
177+
img_lpng = decode_image(data, mode=mode)
179178

180179
tol = 0 if conversion is None else 1
181180
self.assertTrue(img_lpng.allclose(img_pil, atol=tol))

test/test_models.py

Lines changed: 19 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -38,44 +38,16 @@ def get_available_video_models():
3838
return [k for k, v in models.video.__dict__.items() if callable(v) and k[0].lower() == k[0] and k[0] != "_"]
3939

4040

41-
# models that are in torch hub, as well as r3d_18. we tried testing all models
42-
# but the test was too slow. not included are detection models, because
43-
# they are not yet supported in JIT.
4441
# If 'unwrapper' is provided it will be called with the script model outputs
4542
# before they are compared to the eager model outputs. This is useful if the
4643
# model outputs are different between TorchScript / Eager mode
47-
script_test_models = {
48-
'deeplabv3_resnet50': {},
49-
'deeplabv3_resnet101': {},
50-
'mobilenet_v2': {},
51-
'resnext50_32x4d': {},
52-
'fcn_resnet50': {},
53-
'fcn_resnet101': {},
54-
'googlenet': {
55-
'unwrapper': lambda x: x.logits
56-
},
57-
'densenet121': {},
58-
'resnet18': {},
59-
'alexnet': {},
60-
'shufflenet_v2_x1_0': {},
61-
'squeezenet1_0': {},
62-
'vgg11': {},
63-
'inception_v3': {
64-
'unwrapper': lambda x: x.logits
65-
},
66-
'r3d_18': {},
67-
"fasterrcnn_resnet50_fpn": {
68-
'unwrapper': lambda x: x[1]
69-
},
70-
"maskrcnn_resnet50_fpn": {
71-
'unwrapper': lambda x: x[1]
72-
},
73-
"keypointrcnn_resnet50_fpn": {
74-
'unwrapper': lambda x: x[1]
75-
},
76-
"retinanet_resnet50_fpn": {
77-
'unwrapper': lambda x: x[1]
78-
}
44+
script_model_unwrapper = {
45+
'googlenet': lambda x: x.logits,
46+
'inception_v3': lambda x: x.logits,
47+
"fasterrcnn_resnet50_fpn": lambda x: x[1],
48+
"maskrcnn_resnet50_fpn": lambda x: x[1],
49+
"keypointrcnn_resnet50_fpn": lambda x: x[1],
50+
"retinanet_resnet50_fpn": lambda x: x[1],
7951
}
8052

8153

@@ -97,12 +69,6 @@ def get_available_video_models():
9769

9870

9971
class ModelTester(TestCase):
100-
def checkModule(self, model, name, args):
101-
if name not in script_test_models:
102-
return
103-
unwrapper = script_test_models[name].get('unwrapper', None)
104-
return super(ModelTester, self).checkModule(model, args, unwrapper=unwrapper, skip=False)
105-
10672
def _test_classification_model(self, name, input_shape, dev):
10773
set_rng_seed(0)
10874
# passing num_class equal to a number other than 1000 helps in making the test
@@ -114,7 +80,7 @@ def _test_classification_model(self, name, input_shape, dev):
11480
out = model(x)
11581
self.assertExpected(out.cpu(), prec=0.1, strip_suffix="_" + dev)
11682
self.assertEqual(out.shape[-1], 50)
117-
self.checkModule(model, name, (x,))
83+
self.check_jit_scriptable(model, (x,), unwrapper=script_model_unwrapper.get(name, None))
11884

11985
if dev == "cuda":
12086
with torch.cuda.amp.autocast():
@@ -134,7 +100,7 @@ def _test_segmentation_model(self, name, dev):
134100
x = torch.rand(input_shape).to(device=dev)
135101
out = model(x)
136102
self.assertEqual(tuple(out["out"].shape), (1, 50, 300, 300))
137-
self.checkModule(model, name, (x,))
103+
self.check_jit_scriptable(model, (x,), unwrapper=script_model_unwrapper.get(name, None))
138104

139105
if dev == "cuda":
140106
with torch.cuda.amp.autocast():
@@ -209,18 +175,7 @@ def compute_mean_std(tensor):
209175
return True # Full validation performed
210176

211177
full_validation = check_out(out)
212-
213-
scripted_model = torch.jit.script(model)
214-
scripted_model.eval()
215-
scripted_out = scripted_model(model_input)[1]
216-
self.assertEqual(scripted_out[0]["boxes"], out[0]["boxes"])
217-
self.assertEqual(scripted_out[0]["scores"], out[0]["scores"])
218-
# labels currently float in script: need to investigate (though same result)
219-
self.assertEqual(scripted_out[0]["labels"].to(dtype=torch.long), out[0]["labels"])
220-
# don't check script because we are compiling it here:
221-
# TODO: refactor tests
222-
# self.check_script(model, name)
223-
self.checkModule(model, name, ([x],))
178+
self.check_jit_scriptable(model, ([x],), unwrapper=script_model_unwrapper.get(name, None))
224179

225180
if dev == "cuda":
226181
with torch.cuda.amp.autocast():
@@ -270,7 +225,7 @@ def _test_video_model(self, name, dev):
270225
# RNG always on CPU, to ensure x in cuda tests is bitwise identical to x in cpu tests
271226
x = torch.rand(input_shape).to(device=dev)
272227
out = model(x)
273-
self.checkModule(model, name, (x,))
228+
self.check_jit_scriptable(model, (x,), unwrapper=script_model_unwrapper.get(name, None))
274229
self.assertEqual(out.shape[-1], 50)
275230

276231
if dev == "cuda":
@@ -345,11 +300,13 @@ def test_inceptionv3_eval(self):
345300
kwargs['transform_input'] = True
346301
kwargs['aux_logits'] = True
347302
kwargs['init_weights'] = False
303+
name = "inception_v3"
348304
model = models.Inception3(**kwargs)
349305
model.aux_logits = False
350306
model.AuxLogits = None
351-
m = torch.jit.script(model.eval())
352-
self.checkModule(m, "inception_v3", torch.rand(1, 3, 299, 299))
307+
model = model.eval()
308+
x = torch.rand(1, 3, 299, 299)
309+
self.check_jit_scriptable(model, (x,), unwrapper=script_model_unwrapper.get(name, None))
353310

354311
def test_fasterrcnn_double(self):
355312
model = models.detection.fasterrcnn_resnet50_fpn(num_classes=50, pretrained_backbone=False)
@@ -371,12 +328,14 @@ def test_googlenet_eval(self):
371328
kwargs['transform_input'] = True
372329
kwargs['aux_logits'] = True
373330
kwargs['init_weights'] = False
331+
name = "googlenet"
374332
model = models.GoogLeNet(**kwargs)
375333
model.aux_logits = False
376334
model.aux1 = None
377335
model.aux2 = None
378-
m = torch.jit.script(model.eval())
379-
self.checkModule(m, "googlenet", torch.rand(1, 3, 224, 224))
336+
model = model.eval()
337+
x = torch.rand(1, 3, 224, 224)
338+
self.check_jit_scriptable(model, (x,), unwrapper=script_model_unwrapper.get(name, None))
380339

381340
@unittest.skipIf(not torch.cuda.is_available(), 'needs GPU')
382341
def test_fasterrcnn_switch_devices(self):
Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
from collections import OrderedDict
22
import torch
3-
import unittest
3+
from common_utils import TestCase
44
from torchvision.models.detection.anchor_utils import AnchorGenerator
55
from torchvision.models.detection.image_list import ImageList
66

77

8-
class Tester(unittest.TestCase):
8+
class Tester(TestCase):
99
def test_incorrect_anchors(self):
1010
incorrect_sizes = ((2, 4, 8), (32, 8), )
1111
incorrect_aspects = (0.5, 1.0)
@@ -16,40 +16,46 @@ def test_incorrect_anchors(self):
1616
self.assertRaises(ValueError, anc, image_list, feature_maps)
1717

1818
def _init_test_anchor_generator(self):
19-
anchor_sizes = tuple((x,) for x in [32, 64, 128])
20-
aspect_ratios = ((0.5, 1.0, 2.0),) * len(anchor_sizes)
19+
anchor_sizes = ((10,),)
20+
aspect_ratios = ((1,),)
2121
anchor_generator = AnchorGenerator(anchor_sizes, aspect_ratios)
2222

2323
return anchor_generator
2424

2525
def get_features(self, images):
2626
s0, s1 = images.shape[-2:]
27-
features = [
28-
('0', torch.rand(2, 8, s0 // 4, s1 // 4)),
29-
('1', torch.rand(2, 16, s0 // 8, s1 // 8)),
30-
('2', torch.rand(2, 32, s0 // 16, s1 // 16)),
31-
]
32-
features = OrderedDict(features)
27+
features = [torch.rand(2, 8, s0 // 5, s1 // 5)]
3328
return features
3429

3530
def test_anchor_generator(self):
36-
images = torch.randn(2, 3, 16, 32)
31+
images = torch.randn(2, 3, 15, 15)
3732
features = self.get_features(images)
38-
features = list(features.values())
3933
image_shapes = [i.shape[-2:] for i in images]
4034
images = ImageList(images, image_shapes)
4135

4236
model = self._init_test_anchor_generator()
4337
model.eval()
4438
anchors = model(images, features)
4539

46-
# Compute target anchors numbers
40+
# Estimate the number of target anchors
4741
grid_sizes = [f.shape[-2:] for f in features]
4842
num_anchors_estimated = 0
4943
for sizes, num_anchors_per_loc in zip(grid_sizes, model.num_anchors_per_location()):
5044
num_anchors_estimated += sizes[0] * sizes[1] * num_anchors_per_loc
5145

52-
self.assertEqual(num_anchors_estimated, 126)
46+
anchors_output = torch.tensor([[-5., -5., 5., 5.],
47+
[0., -5., 10., 5.],
48+
[5., -5., 15., 5.],
49+
[-5., 0., 5., 10.],
50+
[0., 0., 10., 10.],
51+
[5., 0., 15., 10.],
52+
[-5., 5., 5., 15.],
53+
[0., 5., 10., 15.],
54+
[5., 5., 15., 15.]])
55+
56+
self.assertEqual(num_anchors_estimated, 9)
5357
self.assertEqual(len(anchors), 2)
54-
self.assertEqual(tuple(anchors[0].shape), (num_anchors_estimated, 4))
55-
self.assertEqual(tuple(anchors[1].shape), (num_anchors_estimated, 4))
58+
self.assertEqual(tuple(anchors[0].shape), (9, 4))
59+
self.assertEqual(tuple(anchors[1].shape), (9, 4))
60+
self.assertEqual(anchors[0], anchors_output)
61+
self.assertEqual(anchors[1], anchors_output)

0 commit comments

Comments
 (0)