Skip to content

Commit 330c403

Browse files
authored
Merge branch 'main' into master
2 parents 96eb6ef + c5fb79f commit 330c403

File tree

27 files changed

+293
-128
lines changed

27 files changed

+293
-128
lines changed

docs/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ numpy
33
sphinx-copybutton>=0.3.1
44
sphinx-gallery>=0.9.0
55
sphinx==3.5.4
6-
-e git+git://github.com/pytorch/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme
6+
-e git+https://github.com/pytorch/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme

docs/source/ops.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,6 @@ Operators
4343
MultiScaleRoIAlign
4444
FeaturePyramidNetwork
4545
StochasticDepth
46+
FrozenBatchNorm2d
47+
ConvNormActivation
48+
SqueezeExcitation

mypy.ini

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

4747
ignore_errors = True
4848

49-
[mypy-torchvision.models.detection.generalized_rcnn]
50-
51-
ignore_errors = True
52-
5349
[mypy-torchvision.models.detection.faster_rcnn]
5450

5551
ignore_errors = True

references/classification/README.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,20 @@ torchrun --nproc_per_node=8 train.py --model inception_v3\
4242
--val-resize-size 342 --val-crop-size 299 --train-crop-size 299 --test-only --pretrained
4343
```
4444

45-
### ResNext-50 32x4d
45+
### ResNet
4646
```
47-
torchrun --nproc_per_node=8 train.py\
48-
--model resnext50_32x4d --epochs 100
47+
torchrun --nproc_per_node=8 train.py --model $MODEL
4948
```
5049

50+
Here `$MODEL` is one of `resnet18`, `resnet34`, `resnet50`, `resnet101` or `resnet152`.
5151

52-
### ResNext-101 32x8d
53-
52+
### ResNext
5453
```
5554
torchrun --nproc_per_node=8 train.py\
56-
--model resnext101_32x8d --epochs 100
55+
--model $MODEL --epochs 100
5756
```
5857

58+
Here `$MODEL` is one of `resnext50_32x4d` or `resnext101_32x8d`.
5959
Note that the above command corresponds to a single node with 8 GPUs. If you use
6060
a different number of GPUs and/or a different batch size, then the learning rate
6161
should be scaled accordingly. For example, the pretrained model provided by
@@ -151,9 +151,9 @@ torchrun --nproc_per_node=8 train.py\
151151

152152
## Quantized
153153

154-
### Parameters used for generating quantized models:
154+
### Post training quantized models
155155

156-
For all post training quantized models (All quantized models except mobilenet-v2), the settings are:
156+
For all post training quantized models, the settings are:
157157

158158
1. num_calibration_batches: 32
159159
2. num_workers: 16
@@ -162,8 +162,11 @@ For all post training quantized models (All quantized models except mobilenet-v2
162162
5. backend: 'fbgemm'
163163

164164
```
165-
python train_quantization.py --device='cpu' --post-training-quantize --backend='fbgemm' --model='<model_name>'
165+
python train_quantization.py --device='cpu' --post-training-quantize --backend='fbgemm' --model='$MODEL'
166166
```
167+
Here `$MODEL` is one of `googlenet`, `inception_v3`, `resnet18`, `resnet50`, `resnext101_32x8d` and `shufflenet_v2_x1_0`.
168+
169+
### QAT MobileNetV2
167170

168171
For Mobilenet-v2, the model was trained with quantization aware training, the settings used are:
169172
1. num_workers: 16
@@ -185,6 +188,8 @@ torchrun --nproc_per_node=8 train_quantization.py --model='mobilenet_v2'
185188

186189
Training converges at about 10 epochs.
187190

191+
### QAT MobileNetV3
192+
188193
For Mobilenet-v3 Large, the model was trained with quantization aware training, the settings used are:
189194
1. num_workers: 16
190195
2. batch_size: 32

references/classification/train.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def load_data(traindir, valdir, args):
157157
crop_size=val_crop_size, resize_size=val_resize_size, interpolation=interpolation
158158
)
159159
else:
160-
fn = PM.__dict__[args.model]
160+
fn = PM.quantization.__dict__[args.model] if hasattr(args, "backend") else PM.__dict__[args.model]
161161
weights = PM._api.get_weight(fn, args.weights)
162162
preprocessing = weights.transforms()
163163

references/classification/train_quantization.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
from train import train_one_epoch, evaluate, load_data
1313

1414

15+
try:
16+
from torchvision.prototype import models as PM
17+
except ImportError:
18+
PM = None
19+
20+
1521
def main(args):
1622
if args.output_dir:
1723
utils.mkdir(args.output_dir)
@@ -46,7 +52,12 @@ def main(args):
4652

4753
print("Creating model", args.model)
4854
# when training quantized models, we always start from a pre-trained fp32 reference model
49-
model = torchvision.models.quantization.__dict__[args.model](pretrained=True, quantize=args.test_only)
55+
if not args.weights:
56+
model = torchvision.models.quantization.__dict__[args.model](pretrained=True, quantize=args.test_only)
57+
else:
58+
if PM is None:
59+
raise ImportError("The prototype module couldn't be found. Please install the latest torchvision nightly.")
60+
model = PM.quantization.__dict__[args.model](weights=args.weights, quantize=args.test_only)
5061
model.to(device)
5162

5263
if not (args.test_only or args.post_training_quantize):
@@ -251,6 +262,9 @@ def get_args_parser(add_help=True):
251262
"--train-crop-size", default=224, type=int, help="the random crop size used for training (default: 224)"
252263
)
253264

265+
# Prototype models only
266+
parser.add_argument("--weights", default=None, type=str, help="the weights enum name to load")
267+
254268
return parser
255269

256270

test/test_prototype_models.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,15 @@ def get_models_with_module_names(module):
3030
return [(fn, module_name) for fn in TM.get_models_from_module(module)]
3131

3232

33-
def test_get_weight():
34-
fn = models.resnet50
35-
weight_name = "ImageNet1K_RefV2"
36-
assert models._api.get_weight(fn, weight_name) == models.ResNet50Weights.ImageNet1K_RefV2
33+
@pytest.mark.parametrize(
34+
"model_fn, weight",
35+
[
36+
(models.resnet50, models.ResNet50Weights.ImageNet1K_RefV2),
37+
(models.quantization.resnet50, models.quantization.QuantizedResNet50Weights.ImageNet1K_FBGEMM_RefV1),
38+
],
39+
)
40+
def test_get_weight(model_fn, weight):
41+
assert models._api.get_weight(model_fn, weight.name) == weight
3742

3843

3944
@pytest.mark.parametrize("model_fn", TM.get_models_from_module(models))
@@ -43,6 +48,12 @@ def test_classification_model(model_fn, dev):
4348
TM.test_classification_model(model_fn, dev)
4449

4550

51+
@pytest.mark.parametrize("model_fn", TM.get_models_from_module(models.quantization))
52+
@pytest.mark.skipif(os.getenv("PYTORCH_TEST_WITH_PROTOTYPE", "0") == "0", reason="Prototype code tests are disabled")
53+
def test_quantized_classification_model(model_fn):
54+
TM.test_quantized_classification_model(model_fn)
55+
56+
4657
@pytest.mark.parametrize("model_fn", TM.get_models_from_module(models.segmentation))
4758
@pytest.mark.parametrize("dev", cpu_and_gpu())
4859
@pytest.mark.skipif(os.getenv("PYTORCH_TEST_WITH_PROTOTYPE", "0") == "0", reason="Prototype code tests are disabled")
@@ -60,6 +71,7 @@ def test_video_model(model_fn, dev):
6071
@pytest.mark.parametrize(
6172
"model_fn, module_name",
6273
get_models_with_module_names(models)
74+
+ get_models_with_module_names(models.quantization)
6375
+ get_models_with_module_names(models.segmentation)
6476
+ get_models_with_module_names(models.video),
6577
)
@@ -70,6 +82,9 @@ def test_old_vs_new_factory(model_fn, module_name, dev):
7082
"models": {
7183
"input_shape": (1, 3, 224, 224),
7284
},
85+
"quantization": {
86+
"input_shape": (1, 3, 224, 224),
87+
},
7388
"segmentation": {
7489
"input_shape": (1, 3, 520, 520),
7590
},

torchvision/models/detection/generalized_rcnn.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class GeneralizedRCNN(nn.Module):
2525
the model
2626
"""
2727

28-
def __init__(self, backbone, rpn, roi_heads, transform):
28+
def __init__(self, backbone: nn.Module, rpn: nn.Module, roi_heads: nn.Module, transform: nn.Module) -> None:
2929
super().__init__()
3030
_log_api_usage_once(self)
3131
self.transform = transform
@@ -36,19 +36,26 @@ def __init__(self, backbone, rpn, roi_heads, transform):
3636
self._has_warned = False
3737

3838
@torch.jit.unused
39-
def eager_outputs(self, losses, detections):
40-
# type: (Dict[str, Tensor], List[Dict[str, Tensor]]) -> Union[Dict[str, Tensor], List[Dict[str, Tensor]]]
39+
def eager_outputs(
40+
self,
41+
losses: Dict[str, Tensor],
42+
detections: List[Dict[str, Tensor]],
43+
) -> Union[Dict[str, Tensor], List[Dict[str, Tensor]]]:
44+
4145
if self.training:
4246
return losses
4347

4448
return detections
4549

46-
def forward(self, images, targets=None):
47-
# type: (List[Tensor], Optional[List[Dict[str, Tensor]]]) -> Tuple[Dict[str, Tensor], List[Dict[str, Tensor]]]
50+
def forward(
51+
self,
52+
images: List[Tensor],
53+
targets: Optional[List[Dict[str, Tensor]]] = None,
54+
) -> Union[Tuple[Dict[str, Tensor], List[Dict[str, Tensor]]], Dict[str, Tensor], List[Dict[str, Tensor]]]:
4855
"""
4956
Args:
5057
images (list[Tensor]): images to be processed
51-
targets (list[Dict[Tensor]]): ground-truth boxes present in the image (optional)
58+
targets (list[Dict[str, Tensor]]): ground-truth boxes present in the image (optional)
5259
5360
Returns:
5461
result (list[BoxList] or dict[Tensor]): the output from the model.
@@ -97,7 +104,7 @@ def forward(self, images, targets=None):
97104
features = OrderedDict([("0", features)])
98105
proposals, proposal_losses = self.rpn(images, features, targets)
99106
detections, detector_losses = self.roi_heads(features, proposals, images.image_sizes, targets)
100-
detections = self.transform.postprocess(detections, images.image_sizes, original_image_sizes)
107+
detections = self.transform.postprocess(detections, images.image_sizes, original_image_sizes) # type: ignore[operator]
101108

102109
losses = {}
103110
losses.update(detector_losses)

torchvision/ops/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from .deform_conv import deform_conv2d, DeformConv2d
1414
from .feature_pyramid_network import FeaturePyramidNetwork
1515
from .focal_loss import sigmoid_focal_loss
16+
from .misc import FrozenBatchNorm2d, ConvNormActivation, SqueezeExcitation
1617
from .poolers import MultiScaleRoIAlign
1718
from .ps_roi_align import ps_roi_align, PSRoIAlign
1819
from .ps_roi_pool import ps_roi_pool, PSRoIPool
@@ -48,4 +49,7 @@
4849
"sigmoid_focal_loss",
4950
"stochastic_depth",
5051
"StochasticDepth",
52+
"FrozenBatchNorm2d",
53+
"ConvNormActivation",
54+
"SqueezeExcitation",
5155
]

torchvision/ops/misc.py

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,3 @@
1-
"""
2-
helper class that supports empty tensors on some nn functions.
3-
4-
Ideally, add support directly in PyTorch to empty tensors in
5-
those functions.
6-
7-
This can be removed once https://github.com/pytorch/pytorch/issues/12013
8-
is implemented
9-
"""
10-
111
import warnings
122
from typing import Callable, List, Optional
133

@@ -53,8 +43,11 @@ def __init__(self, *args, **kwargs):
5343
# This is not in nn
5444
class FrozenBatchNorm2d(torch.nn.Module):
5545
"""
56-
BatchNorm2d where the batch statistics and the affine parameters
57-
are fixed
46+
BatchNorm2d where the batch statistics and the affine parameters are fixed
47+
48+
Args:
49+
num_features (int): Number of features ``C`` from an expected input of size ``(N, C, H, W)``
50+
eps (float): a value added to the denominator for numerical stability. Default: 1e-5
5851
"""
5952

6053
def __init__(
@@ -109,6 +102,23 @@ def __repr__(self) -> str:
109102

110103

111104
class ConvNormActivation(torch.nn.Sequential):
105+
"""
106+
Configurable block used for Convolution-Normalzation-Activation blocks.
107+
108+
Args:
109+
in_channels (int): Number of channels in the input image
110+
out_channels (int): Number of channels produced by the Convolution-Normalzation-Activation block
111+
kernel_size: (int, optional): Size of the convolving kernel. Default: 3
112+
stride (int, optional): Stride of the convolution. Default: 1
113+
padding (int, tuple or str, optional): Padding added to all four sides of the input. Default: None, in wich case it will calculated as ``padding = (kernel_size - 1) // 2 * dilation``
114+
groups (int, optional): Number of blocked connections from input channels to output channels. Default: 1
115+
norm_layer (Callable[..., torch.nn.Module], optional): Norm layer that will be stacked on top of the convolutiuon layer. If ``None`` this layer wont be used. Default: ``torch.nn.BatchNorm2d``
116+
activation_layer (Callable[..., torch.nn.Module], optinal): Activation function which will be stacked on top of the normalization layer (if not None), otherwise on top of the conv layer. If ``None`` this layer wont be used. Default: ``torch.nn.ReLU``
117+
dilation (int): Spacing between kernel elements. Default: 1
118+
inplace (bool): Parameter for the activation layer, which can optionally do the operation in-place. Default ``True``
119+
120+
"""
121+
112122
def __init__(
113123
self,
114124
in_channels: int,
@@ -146,6 +156,17 @@ def __init__(
146156

147157

148158
class SqueezeExcitation(torch.nn.Module):
159+
"""
160+
This block implements the Squeeze-and-Excitation block from https://arxiv.org/abs/1709.01507 (see Fig. 1).
161+
Parameters ``activation``, and ``scale_activation`` correspond to ``delta`` and ``sigma`` in in eq. 3.
162+
163+
Args:
164+
input_channels (int): Number of channels in the input image
165+
squeeze_channels (int): Number of squeeze channels
166+
activation (Callable[..., torch.nn.Module], optional): ``delta`` activation. Default: ``torch.nn.ReLU``
167+
scale_activation (Callable[..., torch.nn.Module]): ``sigma`` activation. Default: ``torch.nn.Sigmoid``
168+
"""
169+
149170
def __init__(
150171
self,
151172
input_channels: int,

0 commit comments

Comments
 (0)