Skip to content

Commit e881383

Browse files
authored
Merge pull request #154 from rwightman/tests_bugfixes
Add backward and default_cfg tests and fix a few issues found. Fix #153
2 parents ea2e59c + 4d13db5 commit e881383

File tree

12 files changed

+110
-36
lines changed

12 files changed

+110
-36
lines changed

tests/test_inference.py

Lines changed: 0 additions & 19 deletions
This file was deleted.

tests/test_models.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import pytest
2+
import torch
3+
import platform
4+
import os
5+
import fnmatch
6+
7+
from timm import list_models, create_model
8+
9+
10+
if 'GITHUB_ACTIONS' in os.environ and 'Linux' in platform.system():
11+
# GitHub Linux runner is slower and hits memory limits sooner than MacOS, exclude bigger models
12+
EXCLUDE_FILTERS = ['*efficientnet_l2*', '*resnext101_32x48d']
13+
else:
14+
EXCLUDE_FILTERS = []
15+
MAX_FWD_SIZE = 384
16+
MAX_BWD_SIZE = 128
17+
MAX_FWD_FEAT_SIZE = 448
18+
19+
20+
@pytest.mark.timeout(120)
21+
@pytest.mark.parametrize('model_name', list_models(exclude_filters=EXCLUDE_FILTERS))
22+
@pytest.mark.parametrize('batch_size', [1])
23+
def test_model_forward(model_name, batch_size):
24+
"""Run a single forward pass with each model"""
25+
model = create_model(model_name, pretrained=False)
26+
model.eval()
27+
28+
input_size = model.default_cfg['input_size']
29+
if any([x > MAX_FWD_SIZE for x in input_size]):
30+
# cap forward test at max res 448 * 448 to keep resource down
31+
input_size = tuple([min(x, MAX_FWD_SIZE) for x in input_size])
32+
inputs = torch.randn((batch_size, *input_size))
33+
outputs = model(inputs)
34+
35+
assert outputs.shape[0] == batch_size
36+
assert not torch.isnan(outputs).any(), 'Output included NaNs'
37+
38+
39+
@pytest.mark.timeout(120)
40+
# DLA models have an issue TBD, add them to exclusions
41+
@pytest.mark.parametrize('model_name', list_models(exclude_filters=EXCLUDE_FILTERS + ['dla*']))
42+
@pytest.mark.parametrize('batch_size', [2])
43+
def test_model_backward(model_name, batch_size):
44+
"""Run a single forward pass with each model"""
45+
model = create_model(model_name, pretrained=False, num_classes=42)
46+
num_params = sum([x.numel() for x in model.parameters()])
47+
model.eval()
48+
49+
input_size = model.default_cfg['input_size']
50+
if any([x > MAX_BWD_SIZE for x in input_size]):
51+
# cap backward test at 128 * 128 to keep resource usage down
52+
input_size = tuple([min(x, MAX_BWD_SIZE) for x in input_size])
53+
inputs = torch.randn((batch_size, *input_size))
54+
outputs = model(inputs)
55+
outputs.mean().backward()
56+
num_grad = sum([x.grad.numel() for x in model.parameters() if x.grad is not None])
57+
58+
assert outputs.shape[-1] == 42
59+
assert num_params == num_grad, 'Some parameters are missing gradients'
60+
assert not torch.isnan(outputs).any(), 'Output included NaNs'
61+
62+
63+
@pytest.mark.timeout(120)
64+
@pytest.mark.parametrize('model_name', list_models())
65+
@pytest.mark.parametrize('batch_size', [1])
66+
def test_model_default_cfgs(model_name, batch_size):
67+
"""Run a single forward pass with each model"""
68+
model = create_model(model_name, pretrained=False)
69+
model.eval()
70+
state_dict = model.state_dict()
71+
cfg = model.default_cfg
72+
73+
classifier = cfg['classifier']
74+
first_conv = cfg['first_conv']
75+
pool_size = cfg['pool_size']
76+
input_size = model.default_cfg['input_size']
77+
78+
if all([x <= MAX_FWD_FEAT_SIZE for x in input_size]) and \
79+
not any([fnmatch.fnmatch(model_name, x) for x in EXCLUDE_FILTERS]):
80+
# pool size only checked if default res <= 448 * 448 to keep resource down
81+
input_size = tuple([min(x, MAX_FWD_FEAT_SIZE) for x in input_size])
82+
outputs = model.forward_features(torch.randn((batch_size, *input_size)))
83+
assert outputs.shape[-1] == pool_size[-1] and outputs.shape[-2] == pool_size[-2]
84+
assert any([k.startswith(classifier) for k in state_dict.keys()]), f'{classifier} not in model params'
85+
assert any([k.startswith(first_conv) for k in state_dict.keys()]), f'{first_conv} not in model params'

timm/models/dla.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,11 @@ def __init__(self, levels, block, in_channels, out_channels, stride=1,
237237

238238
def forward(self, x, residual=None, children=None):
239239
children = [] if children is None else children
240-
bottom = self.downsample(x) if self.downsample else x
241-
residual = self.project(bottom) if self.project else bottom
240+
# FIXME the way downsample / project are used here and residual is passed to next level up
241+
# the tree, the residual is overridden and some project weights are thus never used and
242+
# have no gradients. This appears to be an issue with the original model / weights.
243+
bottom = self.downsample(x) if self.downsample is not None else x
244+
residual = self.project(bottom) if self.project is not None else bottom
242245
if self.level_root:
243246
children.append(bottom)
244247
x1 = self.tree1(x, residual)
@@ -354,7 +357,8 @@ def dla60_res2next(pretrained=None, num_classes=1000, in_chans=3, **kwargs):
354357
@register_model
355358
def dla34(pretrained=None, num_classes=1000, in_chans=3, **kwargs): # DLA-34
356359
default_cfg = default_cfgs['dla34']
357-
model = DLA([1, 1, 1, 2, 2, 1], [16, 32, 64, 128, 256, 512], block=DlaBasic, **kwargs)
360+
model = DLA([1, 1, 1, 2, 2, 1], [16, 32, 64, 128, 256, 512], block=DlaBasic,
361+
num_classes=num_classes, in_chans=in_chans, **kwargs)
358362
model.default_cfg = default_cfg
359363
if pretrained:
360364
load_pretrained(model, default_cfg, num_classes, in_chans)

timm/models/gluon_xception.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
'url': '',
3737
'input_size': (3, 299, 299),
3838
'crop_pct': 0.875,
39-
'pool_size': (10, 10),
39+
'pool_size': (5, 5),
4040
'interpolation': 'bicubic',
4141
'mean': IMAGENET_DEFAULT_MEAN,
4242
'std': IMAGENET_DEFAULT_STD,

timm/models/hrnet.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def _cfg(url='', **kwargs):
3434
'num_classes': 1000, 'input_size': (3, 224, 224), 'pool_size': (7, 7),
3535
'crop_pct': 0.875, 'interpolation': 'bilinear',
3636
'mean': IMAGENET_DEFAULT_MEAN, 'std': IMAGENET_DEFAULT_STD,
37-
'first_conv': 'conv1', 'classifier': 'fc',
37+
'first_conv': 'conv1', 'classifier': 'classifier',
3838
**kwargs
3939
}
4040

timm/models/inception_v3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def _cfg(url='', **kwargs):
1515
'num_classes': 1000, 'input_size': (3, 299, 299), 'pool_size': (8, 8),
1616
'crop_pct': 0.875, 'interpolation': 'bicubic',
1717
'mean': IMAGENET_INCEPTION_MEAN, 'std': IMAGENET_INCEPTION_STD,
18-
'first_conv': 'conv1', 'classifier': 'fc',
18+
'first_conv': 'Conv2d_1a_3x3', 'classifier': 'fc',
1919
**kwargs
2020
}
2121

timm/models/mobilenetv3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
def _cfg(url='', **kwargs):
2323
return {
24-
'url': url, 'num_classes': 1000, 'input_size': (3, 224, 224), 'pool_size': (7, 7),
24+
'url': url, 'num_classes': 1000, 'input_size': (3, 224, 224), 'pool_size': (1, 1),
2525
'crop_pct': 0.875, 'interpolation': 'bilinear',
2626
'mean': IMAGENET_DEFAULT_MEAN, 'std': IMAGENET_DEFAULT_STD,
2727
'first_conv': 'conv_stem', 'classifier': 'classifier',

timm/models/nasnet.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
'mean': (0.5, 0.5, 0.5),
2020
'std': (0.5, 0.5, 0.5),
2121
'num_classes': 1001,
22-
'first_conv': 'conv_0.conv',
22+
'first_conv': 'conv0.conv',
2323
'classifier': 'last_linear',
2424
},
2525
}
@@ -612,7 +612,7 @@ def nasnetalarge(pretrained=False, num_classes=1000, in_chans=3, **kwargs):
612612
"""NASNet-A large model architecture.
613613
"""
614614
default_cfg = default_cfgs['nasnetalarge']
615-
model = NASNetALarge(num_classes=1000, in_chans=in_chans, **kwargs)
615+
model = NASNetALarge(num_classes=num_classes, in_chans=in_chans, **kwargs)
616616
model.default_cfg = default_cfg
617617
if pretrained:
618618
load_pretrained(model, default_cfg, num_classes, in_chans)

timm/models/resnest.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,14 @@ def _cfg(url='', **kwargs):
3838
'resnest50d': _cfg(
3939
url='https://hangzh.s3.amazonaws.com/encoding/models/resnest50-528c19ca.pth'),
4040
'resnest101e': _cfg(
41-
url='https://hangzh.s3.amazonaws.com/encoding/models/resnest101-22405ba7.pth', input_size=(3, 256, 256)),
41+
url='https://hangzh.s3.amazonaws.com/encoding/models/resnest101-22405ba7.pth',
42+
input_size=(3, 256, 256), pool_size=(8, 8)),
4243
'resnest200e': _cfg(
43-
url='https://hangzh.s3.amazonaws.com/encoding/models/resnest200-75117900.pth', input_size=(3, 320, 320)),
44+
url='https://hangzh.s3.amazonaws.com/encoding/models/resnest200-75117900.pth',
45+
input_size=(3, 320, 320), pool_size=(10, 10)),
4446
'resnest269e': _cfg(
45-
url='https://hangzh.s3.amazonaws.com/encoding/models/resnest269-0cc87c48.pth', input_size=(3, 416, 416)),
47+
url='https://hangzh.s3.amazonaws.com/encoding/models/resnest269-0cc87c48.pth',
48+
input_size=(3, 416, 416), pool_size=(13, 13)),
4649
'resnest50d_4s2x40d': _cfg(
4750
url='https://hangzh.s3.amazonaws.com/encoding/models/resnest50_fast_4s2x40d-41d14ed0.pth',
4851
interpolation='bicubic'),

timm/models/selecsls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
def _cfg(url='', **kwargs):
2727
return {
2828
'url': url,
29-
'num_classes': 1000, 'input_size': (3, 224, 224), 'pool_size': (3, 3),
29+
'num_classes': 1000, 'input_size': (3, 224, 224), 'pool_size': (4, 4),
3030
'crop_pct': 0.875, 'interpolation': 'bilinear',
3131
'mean': IMAGENET_DEFAULT_MEAN, 'std': IMAGENET_DEFAULT_STD,
3232
'first_conv': 'stem', 'classifier': 'fc',

0 commit comments

Comments
 (0)