Last active
May 12, 2021 14:38
-
-
Save cheadrian/f8ea250d78c2bb9bc913aa89f18f8e21 to your computer and use it in GitHub Desktop.
Patches for TorchVision 0.9.1 to add fcn_resnet18, fcn_resnet34, fcn_mobilenetv2, fcn_mobilenetv3_large, fcn_mobilenetv3_small and compatible ONNX conversion for TensorRT acceleration. Jetson Nano, Jetson Platform. Replace files in segmentation folder of torchvision package, example: "/usr/local/lib/python3.7/dist-packages/torchvision/models/seg…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from collections import OrderedDict | |
| from torch import nn | |
| from torch.nn import functional as F | |
| class _SimpleSegmentationModel(nn.Module): | |
| __constants__ = ['aux_classifier'] | |
| def __init__(self, backbone, classifier, aux_classifier=None, export_onnx=False): | |
| super(_SimpleSegmentationModel, self).__init__() | |
| self.backbone = backbone | |
| self.classifier = classifier | |
| self.aux_classifier = aux_classifier | |
| self.export_onnx = export_onnx | |
| print('torchvision.models.segmentation.FCN() => configuring model for ' + ('ONNX export' if export_onnx else 'training')) | |
| def forward(self, x): | |
| input_shape = x.shape[-2:] | |
| # contract: features is a dict of tensors | |
| features = self.backbone(x) | |
| result = OrderedDict() | |
| x = features["out"] | |
| x = self.classifier(x) | |
| #x = F.interpolate(x, size=input_shape, mode='bilinear', align_corners=False) | |
| #result["out"] = x | |
| if self.export_onnx: | |
| print('FCN configured for export to ONNX') | |
| print('FCN model input size = ' + str(input_shape)) | |
| print('FCN classifier output size = ' + str(x.size())) | |
| #x = F.interpolate(x, size=(int(input_shape[0]), int(input_shape[1])), mode='nearest') | |
| print('FCN upsample() output size = ' + str(x.size())) | |
| print('FCN => returning tensor instead of OrderedDict') | |
| return x | |
| # non-ONNX training/eval path | |
| x = F.interpolate(x, size=input_shape, mode='bilinear', align_corners=False) | |
| result = OrderedDict() | |
| result["out"] = x | |
| if self.aux_classifier is not None: | |
| x = features["aux"] | |
| x = self.aux_classifier(x) | |
| x = F.interpolate(x, size=input_shape, mode='bilinear', align_corners=False) | |
| result["aux"] = x | |
| return result |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from .._utils import IntermediateLayerGetter | |
| from ..utils import load_state_dict_from_url | |
| from .. import mobilenetv3 | |
| from .. import mobilenetv2 | |
| from .. import resnet | |
| from .deeplabv3 import DeepLabHead, DeepLabV3 | |
| from .fcn import FCN, FCNHead | |
| from .lraspp import LRASPP | |
| __all__ = ['fcn_resnet18', 'fcn_resnet34', 'fcn_resnet50', 'fcn_resnet101', 'fcn_mobilenetv3_large', 'fcn_mobilenetv3_small', 'fcn_mobilenetv2', 'deeplabv3_resnet50', 'deeplabv3_resnet101', 'deeplabv3_mobilenet_v3_large', 'lraspp_mobilenet_v3_large'] | |
| model_urls = { | |
| 'fcn_resnet18_coco': None, | |
| 'fcn_resnet34_coco': None, | |
| 'fcn_resnet50_coco': 'https://download.pytorch.org/models/fcn_resnet50_coco-1167a1af.pth', | |
| 'fcn_resnet101_coco': 'https://download.pytorch.org/models/fcn_resnet101_coco-7ecb50ca.pth', | |
| 'deeplabv3_resnet50_coco': 'https://download.pytorch.org/models/deeplabv3_resnet50_coco-cd0a2569.pth', | |
| 'deeplabv3_resnet101_coco': 'https://download.pytorch.org/models/deeplabv3_resnet101_coco-586e9e4e.pth', | |
| 'deeplabv3_mobilenet_v3_large_coco': 'https://download.pytorch.org/models/deeplabv3_mobilenet_v3_large-fc3c493d.pth', | |
| 'lraspp_mobilenet_v3_large_coco': 'https://download.pytorch.org/models/lraspp_mobilenet_v3_large-d234d4ea.pth', | |
| } | |
| def _segm_model(name, backbone_name, num_classes, aux, pretrained_backbone=True, export_onnx=False): | |
| if backbone_name == "resnet18" or backbone_name == "resnet34": | |
| replace_stride_with_dilation=[False, False, False] | |
| inplanes_scale_factor = 4 | |
| else: | |
| replace_stride_with_dilation=[False, True, True] | |
| inplanes_scale_factor = 1 | |
| if 'resnet' in backbone_name: | |
| backbone = resnet.__dict__[backbone_name]( | |
| pretrained=pretrained_backbone, | |
| replace_stride_with_dilation=replace_stride_with_dilation) | |
| out_layer = 'layer4' | |
| out_inplanes = int(2048 / inplanes_scale_factor) | |
| aux_layer = 'layer3' | |
| aux_inplanes = int(1024 / inplanes_scale_factor) | |
| elif 'mobilenet_v3' in backbone_name: | |
| backbone = mobilenetv3.__dict__[backbone_name](pretrained=pretrained_backbone, _dilated=True).features | |
| # Gather the indices of blocks which are strided. These are the locations of C1, ..., Cn-1 blocks. | |
| # The first and last blocks are always included because they are the C0 (conv1) and Cn. | |
| stage_indices = [0] + [i for i, b in enumerate(backbone) if getattr(b, "_is_cn", False)] + [len(backbone) - 1] | |
| out_pos = stage_indices[-1] # use C5 which has output_stride = 16 | |
| out_layer = str(out_pos) | |
| out_inplanes = backbone[out_pos].out_channels | |
| aux_pos = stage_indices[-4] # use C2 here which has output_stride = 8 | |
| aux_layer = str(aux_pos) | |
| aux_inplanes = backbone[aux_pos].out_channels | |
| elif 'mobilenet_v2' in backbone_name: | |
| backbone = mobilenetv2.__dict__[backbone_name](pretrained=pretrained_backbone).features | |
| # Gather the indices of blocks which are strided. These are the locations of C1, ..., Cn-1 blocks. | |
| # The first and last blocks are always included because they are the C0 (conv1) and Cn. | |
| stage_indices = [0] + [i for i, b in enumerate(backbone) if getattr(b, "_is_cn", False)] + [len(backbone) - 1] | |
| out_pos = stage_indices[-1] # use C5 which has output_stride = 16 | |
| out_layer = str(out_pos) | |
| out_inplanes = backbone[out_pos].out_channels | |
| aux_pos = stage_indices[-4] # use C2 here which has output_stride = 8 | |
| aux_layer = str(aux_pos) | |
| aux_inplanes = backbone[aux_pos].out_channels | |
| else: | |
| raise NotImplementedError('backbone {} is not supported as of now'.format(backbone_name)) | |
| return_layers = {out_layer: 'out'} | |
| if aux: | |
| return_layers[aux_layer] = 'aux' | |
| backbone = IntermediateLayerGetter(backbone, return_layers=return_layers) | |
| aux_classifier = None | |
| if aux: | |
| aux_classifier = FCNHead(aux_inplanes, num_classes) | |
| model_map = { | |
| 'deeplabv3': (DeepLabHead, DeepLabV3), | |
| 'fcn': (FCNHead, FCN), | |
| } | |
| classifier = model_map[name][0](out_inplanes, num_classes) | |
| base_model = model_map[name][1] | |
| model = base_model(backbone, classifier, aux_classifier, export_onnx) | |
| return model | |
| def _load_model(arch_type, backbone, pretrained, progress, num_classes, aux_loss, **kwargs): | |
| if pretrained: | |
| aux_loss = True | |
| kwargs["pretrained_backbone"] = False | |
| model = _segm_model(arch_type, backbone, num_classes, aux_loss, **kwargs) | |
| if pretrained: | |
| _load_weights(model, arch_type, backbone, progress) | |
| return model | |
| def _load_weights(model, arch_type, backbone, progress): | |
| arch = arch_type + '_' + backbone + '_coco' | |
| model_url = model_urls.get(arch, None) | |
| if model_url is None: | |
| raise NotImplementedError('pretrained {} is not supported as of now'.format(arch)) | |
| else: | |
| state_dict = load_state_dict_from_url(model_url, progress=progress) | |
| model.load_state_dict(state_dict) | |
| def _segm_lraspp_mobilenetv3(backbone_name, num_classes, pretrained_backbone=True): | |
| backbone = mobilenetv3.__dict__[backbone_name](pretrained=pretrained_backbone, _dilated=True).features | |
| # Gather the indices of blocks which are strided. These are the locations of C1, ..., Cn-1 blocks. | |
| # The first and last blocks are always included because they are the C0 (conv1) and Cn. | |
| stage_indices = [0] + [i for i, b in enumerate(backbone) if getattr(b, "_is_cn", False)] + [len(backbone) - 1] | |
| low_pos = stage_indices[-4] # use C2 here which has output_stride = 8 | |
| high_pos = stage_indices[-1] # use C5 which has output_stride = 16 | |
| low_channels = backbone[low_pos].out_channels | |
| high_channels = backbone[high_pos].out_channels | |
| backbone = IntermediateLayerGetter(backbone, return_layers={str(low_pos): 'low', str(high_pos): 'high'}) | |
| model = LRASPP(backbone, low_channels, high_channels, num_classes) | |
| return model | |
| def fcn_mobilenetv3_large(pretrained=False, progress=True, | |
| num_classes=21, aux_loss=None, **kwargs): | |
| print('torchvision.models.segmentation.mobilenetv3_large()') | |
| return _load_model('fcn', 'mobilenet_v3_large', pretrained, progress, num_classes, aux_loss, **kwargs) | |
| def fcn_mobilenetv3_small(pretrained=False, progress=True, | |
| num_classes=21, aux_loss=None, **kwargs): | |
| print('torchvision.models.segmentation.mobilenetv3_small()') | |
| return _load_model('fcn', 'mobilenet_v3_small', pretrained, progress, num_classes, aux_loss, **kwargs) | |
| def fcn_mobilenetv2(pretrained=False, progress=True, | |
| num_classes=21, aux_loss=None, **kwargs): | |
| print('torchvision.models.segmentation.mobilenetv2()') | |
| return _load_model('fcn', 'mobilenet_v2', pretrained, progress, num_classes, aux_loss, **kwargs) | |
| def fcn_resnet18(pretrained=False, progress=True, | |
| num_classes=21, aux_loss=None, **kwargs): | |
| """Constructs a Fully-Convolutional Network model with a ResNet-18 backbone. | |
| Args: | |
| pretrained (bool): If True, returns a model pre-trained on COCO train2017 which | |
| contains the same classes as Pascal VOC | |
| progress (bool): If True, displays a progress bar of the download to stderr | |
| """ | |
| print('torchvision.models.segmentation.fcn_resnet18()') | |
| return _load_model('fcn', 'resnet18', pretrained, progress, num_classes, aux_loss, **kwargs) | |
| def fcn_resnet34(pretrained=False, progress=True, | |
| num_classes=21, aux_loss=None, **kwargs): | |
| """Constructs a Fully-Convolutional Network model with a ResNet-34 backbone. | |
| Args: | |
| pretrained (bool): If True, returns a model pre-trained on COCO train2017 which | |
| contains the same classes as Pascal VOC | |
| progress (bool): If True, displays a progress bar of the download to stderr | |
| """ | |
| print('torchvision.models.segmentation.fcn_resnet34()') | |
| return _load_model('fcn', 'resnet34', pretrained, progress, num_classes, aux_loss, **kwargs) | |
| def fcn_resnet50(pretrained=False, progress=True, | |
| num_classes=21, aux_loss=None, **kwargs): | |
| """Constructs a Fully-Convolutional Network model with a ResNet-50 backbone. | |
| Args: | |
| pretrained (bool): If True, returns a model pre-trained on COCO train2017 which | |
| contains the same classes as Pascal VOC | |
| progress (bool): If True, displays a progress bar of the download to stderr | |
| num_classes (int): number of output classes of the model (including the background) | |
| aux_loss (bool): If True, it uses an auxiliary loss | |
| """ | |
| return _load_model('fcn', 'resnet50', pretrained, progress, num_classes, aux_loss, **kwargs) | |
| def fcn_resnet101(pretrained=False, progress=True, | |
| num_classes=21, aux_loss=None, **kwargs): | |
| """Constructs a Fully-Convolutional Network model with a ResNet-101 backbone. | |
| Args: | |
| pretrained (bool): If True, returns a model pre-trained on COCO train2017 which | |
| contains the same classes as Pascal VOC | |
| progress (bool): If True, displays a progress bar of the download to stderr | |
| num_classes (int): number of output classes of the model (including the background) | |
| aux_loss (bool): If True, it uses an auxiliary loss | |
| """ | |
| return _load_model('fcn', 'resnet101', pretrained, progress, num_classes, aux_loss, **kwargs) | |
| def deeplabv3_resnet50(pretrained=False, progress=True, | |
| num_classes=21, aux_loss=None, **kwargs): | |
| """Constructs a DeepLabV3 model with a ResNet-50 backbone. | |
| Args: | |
| pretrained (bool): If True, returns a model pre-trained on COCO train2017 which | |
| contains the same classes as Pascal VOC | |
| progress (bool): If True, displays a progress bar of the download to stderr | |
| num_classes (int): number of output classes of the model (including the background) | |
| aux_loss (bool): If True, it uses an auxiliary loss | |
| """ | |
| return _load_model('deeplabv3', 'resnet50', pretrained, progress, num_classes, aux_loss, **kwargs) | |
| def deeplabv3_resnet101(pretrained=False, progress=True, | |
| num_classes=21, aux_loss=None, **kwargs): | |
| """Constructs a DeepLabV3 model with a ResNet-101 backbone. | |
| Args: | |
| pretrained (bool): If True, returns a model pre-trained on COCO train2017 which | |
| contains the same classes as Pascal VOC | |
| progress (bool): If True, displays a progress bar of the download to stderr | |
| num_classes (int): The number of classes | |
| aux_loss (bool): If True, include an auxiliary classifier | |
| """ | |
| return _load_model('deeplabv3', 'resnet101', pretrained, progress, num_classes, aux_loss, **kwargs) | |
| def deeplabv3_mobilenet_v3_large(pretrained=False, progress=True, | |
| num_classes=21, aux_loss=None, **kwargs): | |
| """Constructs a DeepLabV3 model with a MobileNetV3-Large backbone. | |
| Args: | |
| pretrained (bool): If True, returns a model pre-trained on COCO train2017 which | |
| contains the same classes as Pascal VOC | |
| progress (bool): If True, displays a progress bar of the download to stderr | |
| num_classes (int): number of output classes of the model (including the background) | |
| aux_loss (bool): If True, it uses an auxiliary loss | |
| """ | |
| return _load_model('deeplabv3', 'mobilenet_v3_large', pretrained, progress, num_classes, aux_loss, **kwargs) | |
| def lraspp_mobilenet_v3_large(pretrained=False, progress=True, num_classes=21, **kwargs): | |
| """Constructs a Lite R-ASPP Network model with a MobileNetV3-Large backbone. | |
| Args: | |
| pretrained (bool): If True, returns a model pre-trained on COCO train2017 which | |
| contains the same classes as Pascal VOC | |
| progress (bool): If True, displays a progress bar of the download to stderr | |
| num_classes (int): number of output classes of the model (including the background) | |
| """ | |
| if kwargs.pop("aux_loss", False): | |
| raise NotImplementedError('This model does not use auxiliary loss') | |
| backbone_name = 'mobilenet_v3_large' | |
| model = _segm_lraspp_mobilenetv3(backbone_name, num_classes, **kwargs) | |
| if pretrained: | |
| _load_weights(model, 'lraspp', backbone_name, progress) | |
| return model |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment