CoolFace
Apppublic

RabbitRUI/ruispace

sourceHugging Facemitupdated 3y agoView on Hugging Face
0likes
networks.py522 linesDownload Raw Back to models
1"""This script defines deep neural networks for Deep3DFaceRecon_pytorch2"""3 4import os5import numpy as np6import torch.nn.functional as F7from torch.nn import init8import functools9from torch.optim import lr_scheduler10import torch11from torch import Tensor12import torch.nn as nn13try:14    from torch.hub import load_state_dict_from_url15except ImportError:16    from torch.utils.model_zoo import load_url as load_state_dict_from_url17from typing import Type, Any, Callable, Union, List, Optional18from .arcface_torch.backbones import get_model19from kornia.geometry import warp_affine20 21def resize_n_crop(image, M, dsize=112):22    # image: (b, c, h, w)23    # M   :  (b, 2, 3)24    return warp_affine(image, M, dsize=(dsize, dsize), align_corners=True)25 26def filter_state_dict(state_dict, remove_name='fc'):27    new_state_dict = {}28    for key in state_dict:29        if remove_name in key:30            continue31        new_state_dict[key] = state_dict[key]32    return new_state_dict33 34def get_scheduler(optimizer, opt):35    """Return a learning rate scheduler36 37    Parameters:38        optimizer          -- the optimizer of the network39        opt (option class) -- stores all the experiment flags; needs to be a subclass of BaseOptions. 40                              opt.lr_policy is the name of learning rate policy: linear | step | plateau | cosine41 42    For other schedulers (step, plateau, and cosine), we use the default PyTorch schedulers.43    See https://pytorch.org/docs/stable/optim.html for more details.44    """45    if opt.lr_policy == 'linear':46        def lambda_rule(epoch):47            lr_l = 1.0 - max(0, epoch + opt.epoch_count - opt.n_epochs) / float(opt.n_epochs + 1)48            return lr_l49        scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda_rule)50    elif opt.lr_policy == 'step':51        scheduler = lr_scheduler.StepLR(optimizer, step_size=opt.lr_decay_epochs, gamma=0.2)52    elif opt.lr_policy == 'plateau':53        scheduler = lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.2, threshold=0.01, patience=5)54    elif opt.lr_policy == 'cosine':55        scheduler = lr_scheduler.CosineAnnealingLR(optimizer, T_max=opt.n_epochs, eta_min=0)56    else:57        return NotImplementedError('learning rate policy [%s] is not implemented', opt.lr_policy)58    return scheduler59 60 61def define_net_recon(net_recon, use_last_fc=False, init_path=None):62    return ReconNetWrapper(net_recon, use_last_fc=use_last_fc, init_path=init_path)63 64def define_net_recog(net_recog, pretrained_path=None):65    net = RecogNetWrapper(net_recog=net_recog, pretrained_path=pretrained_path)66    net.eval()67    return net68 69class ReconNetWrapper(nn.Module):70    fc_dim=25771    def __init__(self, net_recon, use_last_fc=False, init_path=None):72        super(ReconNetWrapper, self).__init__()73        self.use_last_fc = use_last_fc74        if net_recon not in func_dict:75            return  NotImplementedError('network [%s] is not implemented', net_recon)76        func, last_dim = func_dict[net_recon]77        backbone = func(use_last_fc=use_last_fc, num_classes=self.fc_dim)78        if init_path and os.path.isfile(init_path):79            state_dict = filter_state_dict(torch.load(init_path, map_location='cpu'))80            backbone.load_state_dict(state_dict)81            print("loading init net_recon %s from %s" %(net_recon, init_path))82        self.backbone = backbone83        if not use_last_fc:84            self.final_layers = nn.ModuleList([85                conv1x1(last_dim, 80, bias=True), # id layer86                conv1x1(last_dim, 64, bias=True), # exp layer87                conv1x1(last_dim, 80, bias=True), # tex layer88                conv1x1(last_dim, 3, bias=True),  # angle layer89                conv1x1(last_dim, 27, bias=True), # gamma layer90                conv1x1(last_dim, 2, bias=True),  # tx, ty91                conv1x1(last_dim, 1, bias=True)   # tz92            ])93            for m in self.final_layers:94                nn.init.constant_(m.weight, 0.)95                nn.init.constant_(m.bias, 0.)96 97    def forward(self, x):98        x = self.backbone(x)99        if not self.use_last_fc:100            output = []101            for layer in self.final_layers:102                output.append(layer(x))103            x = torch.flatten(torch.cat(output, dim=1), 1)104        return x105 106 107class RecogNetWrapper(nn.Module):108    def __init__(self, net_recog, pretrained_path=None, input_size=112):109        super(RecogNetWrapper, self).__init__()110        net = get_model(name=net_recog, fp16=False)111        if pretrained_path:112            state_dict = torch.load(pretrained_path, map_location='cpu')113            net.load_state_dict(state_dict)114            print("loading pretrained net_recog %s from %s" %(net_recog, pretrained_path))115        for param in net.parameters():116            param.requires_grad = False117        self.net = net118        self.preprocess = lambda x: 2 * x - 1119        self.input_size=input_size120        121    def forward(self, image, M):122        image = self.preprocess(resize_n_crop(image, M, self.input_size))123        id_feature = F.normalize(self.net(image), dim=-1, p=2)124        return id_feature125 126 127# adapted from https://github.com/pytorch/vision/edit/master/torchvision/models/resnet.py128__all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101',129           'resnet152', 'resnext50_32x4d', 'resnext101_32x8d',130           'wide_resnet50_2', 'wide_resnet101_2']131 132 133model_urls = {134    'resnet18': 'https://download.pytorch.org/models/resnet18-f37072fd.pth',135    'resnet34': 'https://download.pytorch.org/models/resnet34-b627a593.pth',136    'resnet50': 'https://download.pytorch.org/models/resnet50-0676ba61.pth',137    'resnet101': 'https://download.pytorch.org/models/resnet101-63fe2227.pth',138    'resnet152': 'https://download.pytorch.org/models/resnet152-394f9c45.pth',139    'resnext50_32x4d': 'https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth',140    'resnext101_32x8d': 'https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pth',141    'wide_resnet50_2': 'https://download.pytorch.org/models/wide_resnet50_2-95faca4d.pth',142    'wide_resnet101_2': 'https://download.pytorch.org/models/wide_resnet101_2-32ee1156.pth',143}144 145 146def conv3x3(in_planes: int, out_planes: int, stride: int = 1, groups: int = 1, dilation: int = 1) -> nn.Conv2d:147    """3x3 convolution with padding"""148    return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,149                     padding=dilation, groups=groups, bias=False, dilation=dilation)150 151 152def conv1x1(in_planes: int, out_planes: int, stride: int = 1, bias: bool = False) -> nn.Conv2d:153    """1x1 convolution"""154    return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=bias)155 156 157class BasicBlock(nn.Module):158    expansion: int = 1159 160    def __init__(161        self,162        inplanes: int,163        planes: int,164        stride: int = 1,165        downsample: Optional[nn.Module] = None,166        groups: int = 1,167        base_width: int = 64,168        dilation: int = 1,169        norm_layer: Optional[Callable[..., nn.Module]] = None170    ) -> None:171        super(BasicBlock, self).__init__()172        if norm_layer is None:173            norm_layer = nn.BatchNorm2d174        if groups != 1 or base_width != 64:175            raise ValueError('BasicBlock only supports groups=1 and base_width=64')176        if dilation > 1:177            raise NotImplementedError("Dilation > 1 not supported in BasicBlock")178        # Both self.conv1 and self.downsample layers downsample the input when stride != 1179        self.conv1 = conv3x3(inplanes, planes, stride)180        self.bn1 = norm_layer(planes)181        self.relu = nn.ReLU(inplace=True)182        self.conv2 = conv3x3(planes, planes)183        self.bn2 = norm_layer(planes)184        self.downsample = downsample185        self.stride = stride186 187    def forward(self, x: Tensor) -> Tensor:188        identity = x189 190        out = self.conv1(x)191        out = self.bn1(out)192        out = self.relu(out)193 194        out = self.conv2(out)195        out = self.bn2(out)196 197        if self.downsample is not None:198            identity = self.downsample(x)199 200        out += identity201        out = self.relu(out)202 203        return out204 205 206class Bottleneck(nn.Module):207    # Bottleneck in torchvision places the stride for downsampling at 3x3 convolution(self.conv2)208    # while original implementation places the stride at the first 1x1 convolution(self.conv1)209    # according to "Deep residual learning for image recognition"https://arxiv.org/abs/1512.03385.210    # This variant is also known as ResNet V1.5 and improves accuracy according to211    # https://ngc.nvidia.com/catalog/model-scripts/nvidia:resnet_50_v1_5_for_pytorch.212 213    expansion: int = 4214 215    def __init__(216        self,217        inplanes: int,218        planes: int,219        stride: int = 1,220        downsample: Optional[nn.Module] = None,221        groups: int = 1,222        base_width: int = 64,223        dilation: int = 1,224        norm_layer: Optional[Callable[..., nn.Module]] = None225    ) -> None:226        super(Bottleneck, self).__init__()227        if norm_layer is None:228            norm_layer = nn.BatchNorm2d229        width = int(planes * (base_width / 64.)) * groups230        # Both self.conv2 and self.downsample layers downsample the input when stride != 1231        self.conv1 = conv1x1(inplanes, width)232        self.bn1 = norm_layer(width)233        self.conv2 = conv3x3(width, width, stride, groups, dilation)234        self.bn2 = norm_layer(width)235        self.conv3 = conv1x1(width, planes * self.expansion)236        self.bn3 = norm_layer(planes * self.expansion)237        self.relu = nn.ReLU(inplace=True)238        self.downsample = downsample239        self.stride = stride240 241    def forward(self, x: Tensor) -> Tensor:242        identity = x243 244        out = self.conv1(x)245        out = self.bn1(out)246        out = self.relu(out)247 248        out = self.conv2(out)249        out = self.bn2(out)250        out = self.relu(out)251 252        out = self.conv3(out)253        out = self.bn3(out)254 255        if self.downsample is not None:256            identity = self.downsample(x)257 258        out += identity259        out = self.relu(out)260 261        return out262 263 264class ResNet(nn.Module):265 266    def __init__(267        self,268        block: Type[Union[BasicBlock, Bottleneck]],269        layers: List[int],270        num_classes: int = 1000,271        zero_init_residual: bool = False,272        use_last_fc: bool = False,273        groups: int = 1,274        width_per_group: int = 64,275        replace_stride_with_dilation: Optional[List[bool]] = None,276        norm_layer: Optional[Callable[..., nn.Module]] = None277    ) -> None:278        super(ResNet, self).__init__()279        if norm_layer is None:280            norm_layer = nn.BatchNorm2d281        self._norm_layer = norm_layer282 283        self.inplanes = 64284        self.dilation = 1285        if replace_stride_with_dilation is None:286            # each element in the tuple indicates if we should replace287            # the 2x2 stride with a dilated convolution instead288            replace_stride_with_dilation = [False, False, False]289        if len(replace_stride_with_dilation) != 3:290            raise ValueError("replace_stride_with_dilation should be None "291                             "or a 3-element tuple, got {}".format(replace_stride_with_dilation))292        self.use_last_fc = use_last_fc293        self.groups = groups294        self.base_width = width_per_group295        self.conv1 = nn.Conv2d(3, self.inplanes, kernel_size=7, stride=2, padding=3,296                               bias=False)297        self.bn1 = norm_layer(self.inplanes)298        self.relu = nn.ReLU(inplace=True)299        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)300        self.layer1 = self._make_layer(block, 64, layers[0])301        self.layer2 = self._make_layer(block, 128, layers[1], stride=2,302                                       dilate=replace_stride_with_dilation[0])303        self.layer3 = self._make_layer(block, 256, layers[2], stride=2,304                                       dilate=replace_stride_with_dilation[1])305        self.layer4 = self._make_layer(block, 512, layers[3], stride=2,306                                       dilate=replace_stride_with_dilation[2])307        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))308        309        if self.use_last_fc:310            self.fc = nn.Linear(512 * block.expansion, num_classes)311 312        for m in self.modules():313            if isinstance(m, nn.Conv2d):314                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')315            elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):316                nn.init.constant_(m.weight, 1)317                nn.init.constant_(m.bias, 0)318 319 320 321        # Zero-initialize the last BN in each residual branch,322        # so that the residual branch starts with zeros, and each residual block behaves like an identity.323        # This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677324        if zero_init_residual:325            for m in self.modules():326                if isinstance(m, Bottleneck):327                    nn.init.constant_(m.bn3.weight, 0)  # type: ignore[arg-type]328                elif isinstance(m, BasicBlock):329                    nn.init.constant_(m.bn2.weight, 0)  # type: ignore[arg-type]330 331    def _make_layer(self, block: Type[Union[BasicBlock, Bottleneck]], planes: int, blocks: int,332                    stride: int = 1, dilate: bool = False) -> nn.Sequential:333        norm_layer = self._norm_layer334        downsample = None335        previous_dilation = self.dilation336        if dilate:337            self.dilation *= stride338            stride = 1339        if stride != 1 or self.inplanes != planes * block.expansion:340            downsample = nn.Sequential(341                conv1x1(self.inplanes, planes * block.expansion, stride),342                norm_layer(planes * block.expansion),343            )344 345        layers = []346        layers.append(block(self.inplanes, planes, stride, downsample, self.groups,347                            self.base_width, previous_dilation, norm_layer))348        self.inplanes = planes * block.expansion349        for _ in range(1, blocks):350            layers.append(block(self.inplanes, planes, groups=self.groups,351                                base_width=self.base_width, dilation=self.dilation,352                                norm_layer=norm_layer))353 354        return nn.Sequential(*layers)355 356    def _forward_impl(self, x: Tensor) -> Tensor:357        # See note [TorchScript super()]358        x = self.conv1(x)359        x = self.bn1(x)360        x = self.relu(x)361        x = self.maxpool(x)362 363        x = self.layer1(x)364        x = self.layer2(x)365        x = self.layer3(x)366        x = self.layer4(x)367 368        x = self.avgpool(x)369        if self.use_last_fc:370            x = torch.flatten(x, 1)371            x = self.fc(x)372        return x373 374    def forward(self, x: Tensor) -> Tensor:375        return self._forward_impl(x)376 377 378def _resnet(379    arch: str,380    block: Type[Union[BasicBlock, Bottleneck]],381    layers: List[int],382    pretrained: bool,383    progress: bool,384    **kwargs: Any385) -> ResNet:386    model = ResNet(block, layers, **kwargs)387    if pretrained:388        state_dict = load_state_dict_from_url(model_urls[arch],389                                              progress=progress)390        model.load_state_dict(state_dict)391    return model392 393 394def resnet18(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> ResNet:395    r"""ResNet-18 model from396    `"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_.397 398    Args:399        pretrained (bool): If True, returns a model pre-trained on ImageNet400        progress (bool): If True, displays a progress bar of the download to stderr401    """402    return _resnet('resnet18', BasicBlock, [2, 2, 2, 2], pretrained, progress,403                   **kwargs)404 405 406def resnet34(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> ResNet:407    r"""ResNet-34 model from408    `"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_.409 410    Args:411        pretrained (bool): If True, returns a model pre-trained on ImageNet412        progress (bool): If True, displays a progress bar of the download to stderr413    """414    return _resnet('resnet34', BasicBlock, [3, 4, 6, 3], pretrained, progress,415                   **kwargs)416 417 418def resnet50(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> ResNet:419    r"""ResNet-50 model from420    `"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_.421 422    Args:423        pretrained (bool): If True, returns a model pre-trained on ImageNet424        progress (bool): If True, displays a progress bar of the download to stderr425    """426    return _resnet('resnet50', Bottleneck, [3, 4, 6, 3], pretrained, progress,427                   **kwargs)428 429 430def resnet101(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> ResNet:431    r"""ResNet-101 model from432    `"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_.433 434    Args:435        pretrained (bool): If True, returns a model pre-trained on ImageNet436        progress (bool): If True, displays a progress bar of the download to stderr437    """438    return _resnet('resnet101', Bottleneck, [3, 4, 23, 3], pretrained, progress,439                   **kwargs)440 441 442def resnet152(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> ResNet:443    r"""ResNet-152 model from444    `"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_.445 446    Args:447        pretrained (bool): If True, returns a model pre-trained on ImageNet448        progress (bool): If True, displays a progress bar of the download to stderr449    """450    return _resnet('resnet152', Bottleneck, [3, 8, 36, 3], pretrained, progress,451                   **kwargs)452 453 454def resnext50_32x4d(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> ResNet:455    r"""ResNeXt-50 32x4d model from456    `"Aggregated Residual Transformation for Deep Neural Networks" <https://arxiv.org/pdf/1611.05431.pdf>`_.457 458    Args:459        pretrained (bool): If True, returns a model pre-trained on ImageNet460        progress (bool): If True, displays a progress bar of the download to stderr461    """462    kwargs['groups'] = 32463    kwargs['width_per_group'] = 4464    return _resnet('resnext50_32x4d', Bottleneck, [3, 4, 6, 3],465                   pretrained, progress, **kwargs)466 467 468def resnext101_32x8d(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> ResNet:469    r"""ResNeXt-101 32x8d model from470    `"Aggregated Residual Transformation for Deep Neural Networks" <https://arxiv.org/pdf/1611.05431.pdf>`_.471 472    Args:473        pretrained (bool): If True, returns a model pre-trained on ImageNet474        progress (bool): If True, displays a progress bar of the download to stderr475    """476    kwargs['groups'] = 32477    kwargs['width_per_group'] = 8478    return _resnet('resnext101_32x8d', Bottleneck, [3, 4, 23, 3],479                   pretrained, progress, **kwargs)480 481 482def wide_resnet50_2(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> ResNet:483    r"""Wide ResNet-50-2 model from484    `"Wide Residual Networks" <https://arxiv.org/pdf/1605.07146.pdf>`_.485 486    The model is the same as ResNet except for the bottleneck number of channels487    which is twice larger in every block. The number of channels in outer 1x1488    convolutions is the same, e.g. last block in ResNet-50 has 2048-512-2048489    channels, and in Wide ResNet-50-2 has 2048-1024-2048.490 491    Args:492        pretrained (bool): If True, returns a model pre-trained on ImageNet493        progress (bool): If True, displays a progress bar of the download to stderr494    """495    kwargs['width_per_group'] = 64 * 2496    return _resnet('wide_resnet50_2', Bottleneck, [3, 4, 6, 3],497                   pretrained, progress, **kwargs)498 499 500def wide_resnet101_2(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> ResNet:501    r"""Wide ResNet-101-2 model from502    `"Wide Residual Networks" <https://arxiv.org/pdf/1605.07146.pdf>`_.503 504    The model is the same as ResNet except for the bottleneck number of channels505    which is twice larger in every block. The number of channels in outer 1x1506    convolutions is the same, e.g. last block in ResNet-50 has 2048-512-2048507    channels, and in Wide ResNet-50-2 has 2048-1024-2048.508 509    Args:510        pretrained (bool): If True, returns a model pre-trained on ImageNet511        progress (bool): If True, displays a progress bar of the download to stderr512    """513    kwargs['width_per_group'] = 64 * 2514    return _resnet('wide_resnet101_2', Bottleneck, [3, 4, 23, 3],515                   pretrained, progress, **kwargs)516 517 518func_dict = {519    'resnet18': (resnet18, 512),520    'resnet50': (resnet50, 2048)521}522