Spaces:
Sleeping
Sleeping
| import math | |
| import torch | |
| import torch.nn as nn | |
| from torch.hub import load_state_dict_from_url | |
| __all__ = ['mobilenet_v2_x1_0'] | |
| model_urls = { | |
| #currently hadn't found a pretrained weight | |
| 'mobilenet_v2_x1_0': None, | |
| } | |
| def _make_divisible(v, divisor, min_value=None): | |
| """ | |
| This function is taken from the original tf repo. | |
| It ensures that all layers have a channel number that is divisible by 8 | |
| It can be seen here: | |
| https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py | |
| :param v: | |
| :param divisor: | |
| :param min_value: | |
| :return: | |
| """ | |
| if min_value is None: | |
| min_value = divisor | |
| new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) | |
| # Make sure that round down does not go down by more than 10%. | |
| if new_v < 0.9 * v: | |
| new_v += divisor | |
| return new_v | |
| class InvertedResidual(nn.Module): | |
| def __init__(self, inp, oup, stride, expand_ratio): | |
| super(InvertedResidual, self).__init__() | |
| if not (1 <= stride <= 2): | |
| raise ValueError('illegal stride value') | |
| self.stride = stride | |
| self.exp_r = expand_ratio; | |
| hidden_dim = round(inp * self.exp_r); | |
| if self.exp_r == 1: | |
| self.branch = nn.Sequential( | |
| # dw conv | |
| self.depthwise_conv(hidden_dim, hidden_dim, 3, stride, padding = 1, bias=False), | |
| nn.BatchNorm2d(hidden_dim), | |
| nn.ReLU6(inplace=True), | |
| # pw-linear | |
| nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), | |
| nn.BatchNorm2d(oup), | |
| ) | |
| else: | |
| self.branch = nn.Sequential( | |
| # pw | |
| nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False), | |
| nn.BatchNorm2d(hidden_dim), | |
| nn.ReLU6(inplace=True), | |
| # dw | |
| self.depthwise_conv(hidden_dim, hidden_dim, 3, stride, padding = 1, bias=False), | |
| nn.BatchNorm2d(hidden_dim), | |
| nn.ReLU6(inplace=True), | |
| # pw-linear | |
| nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), | |
| nn.BatchNorm2d(oup), | |
| ) | |
| self.downsample = nn.Sequential( | |
| nn.Conv2d(inp, oup, kernel_size=1, stride=stride, bias=False), | |
| nn.BatchNorm2d(oup), | |
| ) | |
| self.identity = stride == 1 | |
| def depthwise_conv(i, o, kernel_size, stride, padding, bias=False): | |
| return nn.Conv2d(i, o, kernel_size, stride, padding, bias=bias, groups=i) | |
| def forward(self, x): | |
| if self.identity: | |
| downsampx = self.downsample(x) | |
| return downsampx + self.branch(x) | |
| else: | |
| return self.branch(x) | |
| class MobileNetV2(nn.Module): | |
| def __init__(self, stages_repeats, stages_out_channels, num_classes=1000, width_mult=1.): | |
| super(MobileNetV2, self).__init__() | |
| if len(stages_repeats) != 5: | |
| raise ValueError('expected stages_repeats as list of 4 positive ints') | |
| if len(stages_out_channels) != 6: | |
| raise ValueError('expected stages_out_channels as list of 5 positive ints') | |
| self._stage_out_channels = stages_out_channels | |
| self.tlist = [1,6,6,6,6] | |
| self.slist = [1,2,2,2,1] | |
| input_channels = 3 | |
| output_channels = self._stage_out_channels[0] # 32 | |
| #output_channels = _make_divisible(output_channels * width_mult, 4 if width_mult == 0.1 else 8) | |
| self.conv1 = nn.Sequential( | |
| nn.Conv2d(input_channels, output_channels, 3, 2, 1, bias=False), | |
| nn.BatchNorm2d(output_channels), | |
| nn.ReLU6(inplace=True), | |
| ) | |
| input_channels = output_channels | |
| stage_names = ['stage{}'.format(i) for i in [2, 3, 4, 5, 6]] | |
| for name, repeats, output_channels, t, s in zip( | |
| stage_names, stages_repeats, self._stage_out_channels[1:], self.tlist, self.slist): | |
| #output_channels = _make_divisible(output_channels * width_mult, 4 if width_mult == 0.1 else 8) | |
| seq = [InvertedResidual(input_channels, output_channels, s, t)] | |
| for i in range(repeats - 1): | |
| seq.append(InvertedResidual(output_channels, output_channels, 1, t)) | |
| setattr(self, name, nn.Sequential(*seq)) | |
| input_channels = output_channels | |
| output_channels = self._stage_out_channels[-1] | |
| self.conv9 = nn.Sequential( | |
| nn.Conv2d(input_channels, output_channels, 1, 1, 0, bias=False), | |
| nn.BatchNorm2d(output_channels), | |
| nn.ReLU6(inplace=True), | |
| ) | |
| def forward(self, x): | |
| x = self.conv1(x) | |
| c2 = self.stage2(x) | |
| c3 = self.stage3(c2) | |
| c4 = self.stage4(c3) | |
| c5 = self.stage5(c4) | |
| c6 = self.stage6(c5) | |
| #c7 = self.stage7(c6) | |
| #c8 = self.stage8(c7) | |
| #c9 = self.conv9(c8) | |
| return c3, c4, c5, c6 | |
| def _mobilenetv2(arch, pretrained, progress, *args, **kwargs): | |
| model = MobileNetV2(*args, **kwargs) | |
| if pretrained: | |
| model_url = model_urls[arch] | |
| 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,strict=False) | |
| return model | |
| def mobilenet_v2_x1_0(pretrained=False, progress=True, **kwargs): | |
| """ | |
| Constructs a ShuffleNetV2 with 0.5x output channels, as described in | |
| `"ShuffleNet V2: Practical Guidelines for Efficient CNN Architecture Design" | |
| <https://arxiv.org/abs/1807.11164>`_. | |
| Args: | |
| pretrained (bool): If True, returns a model pre-trained on ImageNet | |
| progress (bool): If True, displays a progress bar of the download to stderr | |
| """ | |
| return _mobilenetv2('mobilenet_v2_x1_0', pretrained, progress, | |
| [1, 2, 3, 4, 3], [32, 16, 24, 40, 160, 160], **kwargs) |