{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"markdown","source":"## ['EfficientDet: Scalable and Efficient Object Detection'](https://link.zhihu.com/?target=https%3A//arxiv.org/abs/1911.09070v1)\n- Google's paper in Nov 20th\n\nno official open-source now, just find some repos.\n\nhttps://github.com/toandaominh1997/EfficientDet.Pytorch\n\nIf you think it's useful, please give me an upvote."},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","collapsed":true,"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":false},"cell_type":"markdown","source":"## wonderful result :\n![1](http://pics5.baidu.com/feed/c8177f3e6709c93d6cc3fbc78f4a2ad9d100544d.jpeg?token=989fe2e4ec45157cb9f0fa85fa9944e2&s=21B6ED3251AFF0EE1ED908C10000F0B3)"},{"metadata":{},"cell_type":"markdown","source":"# Related Work\n## 1. One-Stage Detectors :\n**In this paper, we mainly follow the one-stage detector design, and we show it is possible to achieve both better efficiency and higher accuracy with optimized network architectures.**"},{"metadata":{},"cell_type":"markdown","source":"## 2. Multi-Scale Feature Representations :\n**we first formulate the multi-scale feature fusion problem, and then introduce the two main ideas for our proposed BiFPN: efficient bidirectional cross-scale connections and weighted feature fusion.**\n\n- structure :\n![2](https://pic1.zhimg.com/80/v2-c5b7e0dcb2f455e2e4037e10922bbb50_hd.jpg)\n\n- weighted feature fusion\n![a](https://pic1.zhimg.com/80/v2-328a45d131dac4c7aefe0eddffbf0bcc_hd.jpg)"},{"metadata":{},"cell_type":"markdown","source":"## 3. Model Scaling :\n**Based on our BiFPN, we have developed a new family of detection models named EfficientDet. In this section, we will discuss the network architecture and a new compound scaling method for EfficientDet.**\n![3](https://pic3.zhimg.com/80/v2-e89c42b5c07697837b2883b0174916da_hd.jpg)"},{"metadata":{},"cell_type":"markdown","source":"## 4. Result :\n- 1\n![4](https://pic4.zhimg.com/80/v2-039b1197596fff9af09b55bb24b53e33_hd.jpg)\n\n- 2\n![5](http://pics2.baidu.com/feed/b7fd5266d016092498ca5cd7da70e7ffe4cd348f.png?token=5bf52f9beca3ddc6c420785935ee6994&s=2AAC7A22C4F1C988185DB1CB0000C0B1)\n\n- 3\n![6](http://pics4.baidu.com/feed/1c950a7b02087bf403c35afce2a4852911dfcf70.jpeg?token=36b5e37c58493aa8ee12e82cb817dce1&s=8D0EED1211CC48EA0CC9A1DA000080B2)"},{"metadata":{},"cell_type":"markdown","source":"# How to build BiFPN ?\n## 1. What's FPN?\n[**Feature Pyramid Networks for Object Detection**](https://arxiv.org/abs/1612.03144)\n![fpn](http://file.elecfans.com/web1/M00/59/BB/pIYBAFtqRd-AZlifAAAziaQTkYk948.png)\n"},{"metadata":{},"cell_type":"markdown","source":"## 2. BiFPN\n![image.png](attachment:image.png)","attachments":{"image.png":{"image/png":"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"}}},{"metadata":{},"cell_type":"markdown","source":"# EfficientNet backbone\n## 1. What's EfficientNet?\n[**EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks ICML 2019 **](https://arxiv.org/abs/1905.11946)\n\nTensorflow(official): https://github.com/tensorflow/tpu/tree/master/models/official/efficientnet\n\nPytorch: https://github.com/lukemelas/EfficientNet-PyTorch\n\n![efficientnet](https://img-blog.csdnimg.cn/20190607190809941.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly90cmVudC5ibG9nLmNzZG4ubmV0,size_16,color_FFFFFF,t_70)"},{"metadata":{},"cell_type":"markdown","source":"# Demo"},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch\nimport torch.nn as nn\nfrom torch.nn import functional as F\nfrom torch.utils import model_zoo\nfrom torchvision.ops import nms\nfrom torchvision import transforms\n\nimport cv2\nfrom PIL import Image\nimport matplotlib.pyplot as plt\nimport skimage\n\nimport re\nimport collections\nfrom functools import partial\nimport math\nimport pandas as pd\nimport numpy as np\nfrom timeit import default_timer as timer\nimport copy\nimport albumentations as albu\nfrom albumentations.pytorch.transforms import ToTensor\nfrom six.moves import map, zip","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### EfficientDet"},{"metadata":{},"cell_type":"markdown","source":"utils"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"########################################################################\n############### HELPERS FUNCTIONS FOR MODEL ARCHITECTURE ###############\n########################################################################\n\n\n# Parameters for the entire model (stem, all blocks, and head)\nGlobalParams = collections.namedtuple('GlobalParams', [\n    'batch_norm_momentum', 'batch_norm_epsilon', 'dropout_rate',\n    'num_classes', 'width_coefficient', 'depth_coefficient',\n    'depth_divisor', 'min_depth', 'drop_connect_rate', 'image_size'])\n\n# Parameters for an individual model block\nBlockArgs = collections.namedtuple('BlockArgs', [\n    'kernel_size', 'num_repeat', 'input_filters', 'output_filters',\n    'expand_ratio', 'id_skip', 'stride', 'se_ratio'])\n\n# Change namedtuple defaults\nGlobalParams.__new__.__defaults__ = (None,) * len(GlobalParams._fields)\nBlockArgs.__new__.__defaults__ = (None,) * len(BlockArgs._fields)\n\n\nclass SwishImplementation(torch.autograd.Function):\n    @staticmethod\n    def forward(ctx, i):\n        result = i * torch.sigmoid(i)\n        ctx.save_for_backward(i)\n        return result\n\n    @staticmethod\n    def backward(ctx, grad_output):\n        i = ctx.saved_variables[0]\n        sigmoid_i = torch.sigmoid(i)\n        return grad_output * (sigmoid_i * (1 + i * (1 - sigmoid_i)))\n\n\nclass MemoryEfficientSwish(nn.Module):\n    def forward(self, x):\n        return SwishImplementation.apply(x)\n\nclass Swish(nn.Module):\n    def forward(self, x):\n        return x * torch.sigmoid(x)\n\n\ndef round_filters(filters, global_params):\n    \"\"\" Calculate and round number of filters based on depth multiplier. \"\"\"\n    multiplier = global_params.width_coefficient\n    if not multiplier:\n        return filters\n    divisor = global_params.depth_divisor\n    min_depth = global_params.min_depth\n    filters *= multiplier\n    min_depth = min_depth or divisor\n    new_filters = max(min_depth, int(filters + divisor / 2) // divisor * divisor)\n    if new_filters < 0.9 * filters:  # prevent rounding by more than 10%\n        new_filters += divisor\n    return int(new_filters)\n\n\ndef round_repeats(repeats, global_params):\n    \"\"\" Round number of filters based on depth multiplier. \"\"\"\n    multiplier = global_params.depth_coefficient\n    if not multiplier:\n        return repeats\n    return int(math.ceil(multiplier * repeats))\n\n\ndef drop_connect(inputs, p, training):\n    \"\"\" Drop connect. \"\"\"\n    if not training: return inputs\n    batch_size = inputs.shape[0]\n    keep_prob = 1 - p\n    random_tensor = keep_prob\n    random_tensor += torch.rand([batch_size, 1, 1, 1], dtype=inputs.dtype, device=inputs.device)\n    binary_tensor = torch.floor(random_tensor)\n    output = inputs / keep_prob * binary_tensor\n    return output\n\n\ndef get_same_padding_conv2d(image_size=None):\n    \"\"\" Chooses static padding if you have specified an image size, and dynamic padding otherwise.\n        Static padding is necessary for ONNX exporting of models. \"\"\"\n    if image_size is None:\n        return Conv2dDynamicSamePadding\n    else:\n        return partial(Conv2dStaticSamePadding, image_size=image_size)\n\n\nclass Conv2dDynamicSamePadding(nn.Conv2d):\n    \"\"\" 2D Convolutions like TensorFlow, for a dynamic image size \"\"\"\n\n    def __init__(self, in_channels, out_channels, kernel_size, stride=1, dilation=1, groups=1, bias=True):\n        super().__init__(in_channels, out_channels, kernel_size, stride, 0, dilation, groups, bias)\n        self.stride = self.stride if len(self.stride) == 2 else [self.stride[0]] * 2\n\n    def forward(self, x):\n        ih, iw = x.size()[-2:]\n        kh, kw = self.weight.size()[-2:]\n        sh, sw = self.stride\n        oh, ow = math.ceil(ih / sh), math.ceil(iw / sw)\n        pad_h = max((oh - 1) * self.stride[0] + (kh - 1) * self.dilation[0] + 1 - ih, 0)\n        pad_w = max((ow - 1) * self.stride[1] + (kw - 1) * self.dilation[1] + 1 - iw, 0)\n        if pad_h > 0 or pad_w > 0:\n            x = F.pad(x, [pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2])\n        return F.conv2d(x, self.weight, self.bias, self.stride, self.padding, self.dilation, self.groups)\n\n\nclass Conv2dStaticSamePadding(nn.Conv2d):\n    \"\"\" 2D Convolutions like TensorFlow, for a fixed image size\"\"\"\n\n    def __init__(self, in_channels, out_channels, kernel_size, image_size=None, **kwargs):\n        super().__init__(in_channels, out_channels, kernel_size, **kwargs)\n        self.stride = self.stride if len(self.stride) == 2 else [self.stride[0]] * 2\n\n        # Calculate padding based on image size and save it\n        assert image_size is not None\n        ih, iw = image_size if type(image_size) == list else [image_size, image_size]\n        kh, kw = self.weight.size()[-2:]\n        sh, sw = self.stride\n        oh, ow = math.ceil(ih / sh), math.ceil(iw / sw)\n        pad_h = max((oh - 1) * self.stride[0] + (kh - 1) * self.dilation[0] + 1 - ih, 0)\n        pad_w = max((ow - 1) * self.stride[1] + (kw - 1) * self.dilation[1] + 1 - iw, 0)\n        if pad_h > 0 or pad_w > 0:\n            self.static_padding = nn.ZeroPad2d((pad_w // 2, pad_w - pad_w // 2, pad_h // 2, pad_h - pad_h // 2))\n        else:\n            self.static_padding = Identity()\n\n    def forward(self, x):\n        x = self.static_padding(x)\n        x = F.conv2d(x, self.weight, self.bias, self.stride, self.padding, self.dilation, self.groups)\n        return x\n\n\nclass Identity(nn.Module):\n    def __init__(self, ):\n        super(Identity, self).__init__()\n\n    def forward(self, input):\n        return input\n\n\n########################################################################\n############## HELPERS FUNCTIONS FOR LOADING MODEL PARAMS ##############\n########################################################################\n\n\ndef efficientnet_params(model_name):\n    \"\"\" Map EfficientNet model name to parameter coefficients. \"\"\"\n    params_dict = {\n        # Coefficients:   width,depth,res,dropout\n        'efficientnet-b0': (1.0, 1.0, 224, 0.2),\n        'efficientnet-b1': (1.0, 1.1, 240, 0.2),\n        'efficientnet-b2': (1.1, 1.2, 260, 0.3),\n        'efficientnet-b3': (1.2, 1.4, 300, 0.3),\n        'efficientnet-b4': (1.4, 1.8, 380, 0.4),\n        'efficientnet-b5': (1.6, 2.2, 456, 0.4),\n        'efficientnet-b6': (1.8, 2.6, 528, 0.5),\n        'efficientnet-b7': (2.0, 3.1, 600, 0.5),\n    }\n    return params_dict[model_name]\n\n\nclass BlockDecoder(object):\n    \"\"\" Block Decoder for readability, straight from the official TensorFlow repository \"\"\"\n\n    @staticmethod\n    def _decode_block_string(block_string):\n        \"\"\" Gets a block through a string notation of arguments. \"\"\"\n        assert isinstance(block_string, str)\n\n        ops = block_string.split('_')\n        options = {}\n        for op in ops:\n            splits = re.split(r'(\\d.*)', op)\n            if len(splits) >= 2:\n                key, value = splits[:2]\n                options[key] = value\n\n        # Check stride\n        assert (('s' in options and len(options['s']) == 1) or\n                (len(options['s']) == 2 and options['s'][0] == options['s'][1]))\n\n        return BlockArgs(\n            kernel_size=int(options['k']),\n            num_repeat=int(options['r']),\n            input_filters=int(options['i']),\n            output_filters=int(options['o']),\n            expand_ratio=int(options['e']),\n            id_skip=('noskip' not in block_string),\n            se_ratio=float(options['se']) if 'se' in options else None,\n            stride=[int(options['s'][0])])\n\n    @staticmethod\n    def _encode_block_string(block):\n        \"\"\"Encodes a block to a string.\"\"\"\n        args = [\n            'r%d' % block.num_repeat,\n            'k%d' % block.kernel_size,\n            's%d%d' % (block.strides[0], block.strides[1]),\n            'e%s' % block.expand_ratio,\n            'i%d' % block.input_filters,\n            'o%d' % block.output_filters\n        ]\n        if 0 < block.se_ratio <= 1:\n            args.append('se%s' % block.se_ratio)\n        if block.id_skip is False:\n            args.append('noskip')\n        return '_'.join(args)\n\n    @staticmethod\n    def decode(string_list):\n        \"\"\"\n        Decodes a list of string notations to specify blocks inside the network.\n        :param string_list: a list of strings, each string is a notation of block\n        :return: a list of BlockArgs namedtuples of block args\n        \"\"\"\n        assert isinstance(string_list, list)\n        blocks_args = []\n        for block_string in string_list:\n            blocks_args.append(BlockDecoder._decode_block_string(block_string))\n        return blocks_args\n\n    @staticmethod\n    def encode(blocks_args):\n        \"\"\"\n        Encodes a list of BlockArgs to a list of strings.\n        :param blocks_args: a list of BlockArgs namedtuples of block args\n        :return: a list of strings, each string is a notation of block\n        \"\"\"\n        block_strings = []\n        for block in blocks_args:\n            block_strings.append(BlockDecoder._encode_block_string(block))\n        return block_strings\n\n\ndef efficientnet(width_coefficient=None, depth_coefficient=None, dropout_rate=0.2,\n                 drop_connect_rate=0.2, image_size=None, num_classes=1000):\n    \"\"\" Creates a efficientnet model. \"\"\"\n\n    blocks_args = [\n        'r1_k3_s11_e1_i32_o16_se0.25', 'r2_k3_s22_e6_i16_o24_se0.25',\n        'r2_k5_s22_e6_i24_o40_se0.25', 'r3_k3_s22_e6_i40_o80_se0.25',\n        'r3_k5_s22_e6_i80_o112_se0.25', 'r4_k5_s22_e6_i112_o192_se0.25',\n        'r1_k3_s22_e6_i192_o320_se0.25',\n    ]\n    blocks_args = BlockDecoder.decode(blocks_args)\n\n    global_params = GlobalParams(\n        batch_norm_momentum=0.99,\n        batch_norm_epsilon=1e-3,\n        dropout_rate=dropout_rate,\n        drop_connect_rate=drop_connect_rate,\n        # data_format='channels_last',  # removed, this is always true in PyTorch\n        num_classes=num_classes,\n        width_coefficient=width_coefficient,\n        depth_coefficient=depth_coefficient,\n        depth_divisor=8,\n        min_depth=None,\n        image_size=image_size,\n    )\n\n    return blocks_args, global_params\n\n\ndef get_model_params(model_name, override_params):\n    \"\"\" Get the block args and global params for a given model \"\"\"\n    if model_name.startswith('efficientnet'):\n        w, d, s, p = efficientnet_params(model_name)\n        # note: all models have drop connect rate = 0.2\n        blocks_args, global_params = efficientnet(\n            width_coefficient=w, depth_coefficient=d, dropout_rate=p, image_size=s)\n    else:\n        raise NotImplementedError('model name is not pre-defined: %s' % model_name)\n    if override_params:\n        # ValueError will be raised here if override_params has fields not included in global_params.\n        global_params = global_params._replace(**override_params)\n    return blocks_args, global_params\n\n\nurl_map = {\n    'efficientnet-b0': 'http://storage.googleapis.com/public-models/efficientnet/efficientnet-b0-355c32eb.pth',\n    'efficientnet-b1': 'http://storage.googleapis.com/public-models/efficientnet/efficientnet-b1-f1951068.pth',\n    'efficientnet-b2': 'http://storage.googleapis.com/public-models/efficientnet/efficientnet-b2-8bb594d6.pth',\n    'efficientnet-b3': 'http://storage.googleapis.com/public-models/efficientnet/efficientnet-b3-5fb5a3c3.pth',\n    'efficientnet-b4': 'http://storage.googleapis.com/public-models/efficientnet/efficientnet-b4-6ed6700e.pth',\n    'efficientnet-b5': 'http://storage.googleapis.com/public-models/efficientnet/efficientnet-b5-b6417697.pth',\n    'efficientnet-b6': 'http://storage.googleapis.com/public-models/efficientnet/efficientnet-b6-c76e70fd.pth',\n    'efficientnet-b7': 'http://storage.googleapis.com/public-models/efficientnet/efficientnet-b7-dcc49843.pth',\n}\n\n\ndef load_pretrained_weights(model, model_name, load_fc=True):\n    \"\"\" Loads pretrained weights, and downloads if loading for the first time. \"\"\"\n    state_dict = model_zoo.load_url(url_map[model_name])\n    if load_fc:\n        model.load_state_dict(state_dict)\n    else:\n        state_dict.pop('_fc.weight')\n        state_dict.pop('_fc.bias')\n        res = model.load_state_dict(state_dict, strict=False)\n        assert set(res.missing_keys) == set(['_fc.weight', '_fc.bias']), 'issue loading pretrained weights'\n    print('Loaded pretrained weights for {}'.format(model_name))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"EfficientNet"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"class MBConvBlock(nn.Module):\n    \"\"\"\n    Mobile Inverted Residual Bottleneck Block\n    Args:\n        block_args (namedtuple): BlockArgs, see above\n        global_params (namedtuple): GlobalParam, see above\n    Attributes:\n        has_se (bool): Whether the block contains a Squeeze and Excitation layer.\n    \"\"\"\n\n    def __init__(self, block_args, global_params):\n        super().__init__()\n        self._block_args = block_args\n        self._bn_mom = 1 - global_params.batch_norm_momentum\n        self._bn_eps = global_params.batch_norm_epsilon\n        self.has_se = (self._block_args.se_ratio is not None) and (0 < self._block_args.se_ratio <= 1)\n        self.id_skip = block_args.id_skip  # skip connection and drop connect\n\n        # Get static or dynamic convolution depending on image size\n        Conv2d = get_same_padding_conv2d(image_size=global_params.image_size)\n\n        # Expansion phase\n        inp = self._block_args.input_filters  # number of input channels\n        oup = self._block_args.input_filters * self._block_args.expand_ratio  # number of output channels\n        if self._block_args.expand_ratio != 1:\n            self._expand_conv = Conv2d(in_channels=inp, out_channels=oup, kernel_size=1, bias=False)\n            self._bn0 = nn.BatchNorm2d(num_features=oup, momentum=self._bn_mom, eps=self._bn_eps)\n        # Depthwise convolution phase\n        k = self._block_args.kernel_size\n        s = self._block_args.stride\n        self._depthwise_conv = Conv2d(\n            in_channels=oup, out_channels=oup, groups=oup,  # groups makes it depthwise\n            kernel_size=k, stride=s, bias=False)\n        self._bn1 = nn.BatchNorm2d(num_features=oup, momentum=self._bn_mom, eps=self._bn_eps)\n\n        # Squeeze and Excitation layer, if desired\n        if self.has_se:\n            num_squeezed_channels = max(1, int(self._block_args.input_filters * self._block_args.se_ratio))\n            self._se_reduce = Conv2d(in_channels=oup, out_channels=num_squeezed_channels, kernel_size=1)\n            self._se_expand = Conv2d(in_channels=num_squeezed_channels, out_channels=oup, kernel_size=1)\n\n        # Output phase\n        final_oup = self._block_args.output_filters\n        self._project_conv = Conv2d(in_channels=oup, out_channels=final_oup, kernel_size=1, bias=False)\n        self._bn2 = nn.BatchNorm2d(num_features=final_oup, momentum=self._bn_mom, eps=self._bn_eps)\n        self._swish = MemoryEfficientSwish()\n\n    def forward(self, inputs, drop_connect_rate=None):\n        \"\"\"\n        :param inputs: input tensor\n        :param drop_connect_rate: drop connect rate (float, between 0 and 1)\n        :return: output of block\n        \"\"\"\n\n        # Expansion and Depthwise Convolution\n        x = inputs\n        if self._block_args.expand_ratio != 1:\n            x = self._swish(self._bn0(self._expand_conv(inputs)))\n        \n        x = self._swish(self._bn1(self._depthwise_conv(x)))\n\n        # Squeeze and Excitation\n        if self.has_se:\n            x_squeezed = F.adaptive_avg_pool2d(x, 1)\n            x_squeezed = self._se_expand(self._swish(self._se_reduce(x_squeezed)))\n            x = torch.sigmoid(x_squeezed) * x\n\n        x = self._bn2(self._project_conv(x))\n\n        # Skip connection and drop connect\n        input_filters, output_filters = self._block_args.input_filters, self._block_args.output_filters\n        if self.id_skip and self._block_args.stride == 1 and input_filters == output_filters:\n            if drop_connect_rate:\n                x = drop_connect(x, p=drop_connect_rate, training=self.training)\n            x = x + inputs  # skip connection\n        return x\n\n    def set_swish(self, memory_efficient=True):\n        \"\"\"Sets swish function as memory efficient (for training) or standard (for export)\"\"\"\n        self._swish = MemoryEfficientSwish() if memory_efficient else Swish()\n\n\nclass EfficientNet(nn.Module):\n    \"\"\"\n    An EfficientNet model. Most easily loaded with the .from_name or .from_pretrained methods\n    Args:\n        blocks_args (list): A list of BlockArgs to construct blocks\n        global_params (namedtuple): A set of GlobalParams shared between blocks\n    Example:\n        model = EfficientNet.from_pretrained('efficientnet-b0')\n    \"\"\"\n\n    def __init__(self, blocks_args=None, global_params=None):\n        super().__init__()\n        assert isinstance(blocks_args, list), 'blocks_args should be a list'\n        assert len(blocks_args) > 0, 'block args must be greater than 0'\n        self._global_params = global_params\n        self._blocks_args = blocks_args\n\n        # Get static or dynamic convolution depending on image size\n        Conv2d = get_same_padding_conv2d(image_size=global_params.image_size)\n\n        # Batch norm parameters\n        bn_mom = 1 - self._global_params.batch_norm_momentum\n        bn_eps = self._global_params.batch_norm_epsilon\n\n        # Stem\n        in_channels = 3  # rgb\n        out_channels = round_filters(32, self._global_params)  # number of output channels\n        self._conv_stem = Conv2d(in_channels, out_channels, kernel_size=3, stride=2, bias=False)\n        self._bn0 = nn.BatchNorm2d(num_features=out_channels, momentum=bn_mom, eps=bn_eps)\n        \n        # Build blocks\n        self._blocks = nn.ModuleList([])\n        for i in range(len(self._blocks_args)):\n            # Update block input and output filters based on depth multiplier.\n            self._blocks_args[i] = self._blocks_args[i]._replace(\n                input_filters=round_filters(self._blocks_args[i].input_filters, self._global_params),\n                output_filters=round_filters(self._blocks_args[i].output_filters, self._global_params),\n                num_repeat=round_repeats(self._blocks_args[i].num_repeat, self._global_params)\n            )\n\n            # The first block needs to take care of stride and filter size increase.\n            self._blocks.append(MBConvBlock(self._blocks_args[i], self._global_params))\n            if self._blocks_args[i].num_repeat > 1:\n                self._blocks_args[i] = self._blocks_args[i]._replace(input_filters=self._blocks_args[i].output_filters, stride=1)\n            for _ in range(self._blocks_args[i].num_repeat - 1):\n                self._blocks.append(MBConvBlock(self._blocks_args[i], self._global_params))\n\n        # Head'efficientdet-d0': 'efficientnet-b0',\n        in_channels = self._blocks_args[len(self._blocks_args)-1].output_filters  # output of final block\n        out_channels = round_filters(1280, self._global_params)\n        self._conv_head = Conv2d(in_channels, out_channels, kernel_size=1, bias=False)\n        self._bn1 = nn.BatchNorm2d(num_features=out_channels, momentum=bn_mom, eps=bn_eps)\n\n        # Final linear layer\n        self._avg_pooling = nn.AdaptiveAvgPool2d(1)\n        self._dropout = nn.Dropout(self._global_params.dropout_rate)\n        self._fc = nn.Linear(out_channels, self._global_params.num_classes)\n        self._swish = MemoryEfficientSwish()\n\n    def set_swish(self, memory_efficient=True):\n        \"\"\"Sets swish function as memory efficient (for training) or standard (for export)\"\"\"\n        self._swish = MemoryEfficientSwish() if memory_efficient else Swish()\n        for block in self._blocks:\n            block.set_swish(memory_efficient)\n\n\n    def extract_features(self, inputs):\n        \"\"\" Returns output of the final convolution layer \"\"\"\n        # Stem\n        x = self._swish(self._bn0(self._conv_stem(inputs)))\n\n        P = []\n        index = 0 \n        num_repeat = 0\n         # Blocks\n        for idx, block in enumerate(self._blocks):\n            drop_connect_rate = self._global_params.drop_connect_rate\n            if drop_connect_rate:\n                drop_connect_rate *= float(idx) / len(self._blocks)\n            x = block(x, drop_connect_rate=drop_connect_rate)\n            num_repeat = num_repeat + 1\n            if(num_repeat == self._blocks_args[index].num_repeat):\n                num_repeat = 0\n                index = index + 1\n                P.append(x)\n        return P\n\n    def forward(self, inputs):\n        \"\"\" Calls extract_features to extract features, applies final linear layer, and returns logits. \"\"\"\n        # Convolution layers\n        P = self.extract_features(inputs)\n        return P\n    \n    @classmethod\n    def from_name(cls, model_name, override_params=None):\n        cls._check_model_name_is_valid(model_name)\n        blocks_args, global_params = get_model_params(model_name, override_params)\n        return cls(blocks_args, global_params)\n\n    @classmethod\n    def from_pretrained(cls, model_name, num_classes=1000, in_channels = 3):\n        model = cls.from_name(model_name, override_params={'num_classes': num_classes})\n        load_pretrained_weights(model, model_name, load_fc=(num_classes == 1000))\n        if in_channels != 3:\n            Conv2d = get_same_padding_conv2d(image_size = model._global_params.image_size)\n            out_channels = round_filters(32, model._global_params)\n            model._conv_stem = Conv2d(in_channels, out_channels, kernel_size=3, stride=2, bias=False)\n        return model\n    \n    @classmethod\n    def from_pretrained(cls, model_name, num_classes=1000):\n        model = cls.from_name(model_name, override_params={'num_classes': num_classes})\n        load_pretrained_weights(model, model_name, load_fc=(num_classes == 1000))\n\n        return model\n\n    @classmethod\n    def get_image_size(cls, model_name):\n        cls._check_model_name_is_valid(model_name)\n        _, _, res, _ = efficientnet_params(model_name)\n        return res\n\n    @classmethod\n    def _check_model_name_is_valid(cls, model_name, also_need_pretrained_weights=False):\n        \"\"\" Validates model name. None that pretrained weights are only available for\n        the first four models (efficientnet-b{i} for i in 0,1,2,3) at the moment. \"\"\"\n        num_models = 4 if also_need_pretrained_weights else 8\n        valid_models = ['efficientnet-b'+str(i) for i in range(num_models)]\n        if model_name not in valid_models:\n            raise ValueError('model_name should be one of: ' + ', '.join(valid_models))\n    \n    def get_list_features(self):\n        list_feature = []\n        for idx in range(len(self._blocks_args)):\n            list_feature.append(self._blocks_args[idx].output_filters)\n        \n        return list_feature","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"conv_module"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"def conv_ws_2d(input,\n               weight,\n               bias=None,\n               stride=1,\n               padding=0,\n               dilation=1,\n               groups=1,\n               eps=1e-5):\n    c_in = weight.size(0)\n    weight_flat = weight.view(c_in, -1)\n    mean = weight_flat.mean(dim=1, keepdim=True).view(c_in, 1, 1, 1)\n    std = weight_flat.std(dim=1, keepdim=True).view(c_in, 1, 1, 1)\n    weight = (weight - mean) / (std + eps)\n    return F.conv2d(input, weight, bias, stride, padding, dilation, groups)\n\nclass ConvWS2d(nn.Conv2d):\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 kernel_size,\n                 stride=1,\n                 padding=0,\n                 dilation=1,\n                 groups=1,\n                 bias=True,\n                 eps=1e-5):\n        super(ConvWS2d, self).__init__(\n            in_channels,\n            out_channels,\n            kernel_size,\n            stride=stride,\n            padding=padding,\n            dilation=dilation,\n            groups=groups,\n            bias=bias)\n        self.eps = eps\n\n    def forward(self, x):\n        return conv_ws_2d(x, self.weight, self.bias, self.stride, self.padding,\n                          self.dilation, self.groups, self.eps)\nconv_cfg = {\n    'Conv': nn.Conv2d,\n    'ConvWS': ConvWS2d,\n    # TODO: octave conv\n}\n\ndef build_conv_layer(cfg, *args, **kwargs):\n    \"\"\" Build convolution layer\n    Args:\n        cfg (None or dict): cfg should contain:\n            type (str): identify conv layer type.\n            layer args: args needed to instantiate a conv layer.\n    Returns:\n        layer (nn.Module): created conv layer\n    \"\"\"\n    if cfg is None:\n        cfg_ = dict(type='Conv')\n    else:\n        assert isinstance(cfg, dict) and 'type' in cfg\n        cfg_ = cfg.copy()\n\n    layer_type = cfg_.pop('type')\n    if layer_type not in conv_cfg:\n        raise KeyError('Unrecognized norm type {}'.format(layer_type))\n    else:\n        conv_layer = conv_cfg[layer_type]\n\n    layer = conv_layer(*args, **kwargs, **cfg_)\n\n    return layer\n\nnorm_cfg = {\n    # format: layer_type: (abbreviation, module)\n    'BN': ('bn', nn.BatchNorm2d),\n    'SyncBN': ('bn', nn.SyncBatchNorm),\n    'GN': ('gn', nn.GroupNorm),\n    # and potentially 'SN'\n}\n\ndef build_norm_layer(cfg, num_features, postfix=''):\n    \"\"\" Build normalization layer\n    Args:\n        cfg (dict): cfg should contain:\n            type (str): identify norm layer type.\n            layer args: args needed to instantiate a norm layer.\n            requires_grad (bool): [optional] whether stop gradient updates\n        num_features (int): number of channels from input.\n        postfix (int, str): appended into norm abbreviation to\n            create named layer.\n    Returns:\n        name (str): abbreviation + postfix\n        layer (nn.Module): created norm layer\n    \"\"\"\n    assert isinstance(cfg, dict) and 'type' in cfg\n    cfg_ = cfg.copy()\n\n    layer_type = cfg_.pop('type')\n    if layer_type not in norm_cfg:\n        raise KeyError('Unrecognized norm type {}'.format(layer_type))\n    else:\n        abbr, norm_layer = norm_cfg[layer_type]\n        if norm_layer is None:\n            raise NotImplementedError\n\n    assert isinstance(postfix, (int, str))\n    name = abbr + str(postfix)\n\n    requires_grad = cfg_.pop('requires_grad', True)\n    cfg_.setdefault('eps', 1e-5)\n    if layer_type != 'GN':\n        layer = norm_layer(num_features, **cfg_)\n        if layer_type == 'SyncBN':\n            layer._specify_ddp_gpu_num(1)\n    else:\n        assert 'num_groups' in cfg_\n        layer = norm_layer(num_channels=num_features, **cfg_)\n\n    for param in layer.parameters():\n        param.requires_grad = requires_grad\n\n    return name, layer\n\nclass ConvModule(nn.Module):\n    \"\"\"A conv block that contains conv/norm/activation layers.\n    Args:\n        in_channels (int): Same as nn.Conv2d.\n        out_channels (int): Same as nn.Conv2d.\n        kernel_size (int or tuple[int]): Same as nn.Conv2d.\n        stride (int or tuple[int]): Same as nn.Conv2d.\n        padding (int or tuple[int]): Same as nn.Conv2d.\n        dilation (int or tuple[int]): Same as nn.Conv2d.\n        groups (int): Same as nn.Conv2d.\n        bias (bool or str): If specified as `auto`, it will be decided by the\n            norm_cfg. Bias will be set as True if norm_cfg is None, otherwise\n            False.\n        conv_cfg (dict): Config dict for convolution layer.\n        norm_cfg (dict): Config dict for normalization layer.\n        activation (str or None): Activation type, \"ReLU\" by default.\n        inplace (bool): Whether to use inplace mode for activation.\n        order (tuple[str]): The order of conv/norm/activation layers. It is a\n            sequence of \"conv\", \"norm\" and \"act\". Examples are\n            (\"conv\", \"norm\", \"act\") and (\"act\", \"conv\", \"norm\").\n    \"\"\"\n\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 kernel_size,\n                 stride=1,\n                 padding=0,\n                 dilation=1,\n                 groups=1,\n                 bias='auto',\n                 conv_cfg=None,\n                 norm_cfg=None,\n                 activation='relu',\n                 inplace=True,\n                 order=('conv', 'norm', 'act')):\n        super(ConvModule, self).__init__()\n        assert conv_cfg is None or isinstance(conv_cfg, dict)\n        assert norm_cfg is None or isinstance(norm_cfg, dict)\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        self.activation = activation\n        self.inplace = inplace\n        self.order = order\n        assert isinstance(self.order, tuple) and len(self.order) == 3\n        assert set(order) == set(['conv', 'norm', 'act'])\n\n        self.with_norm = norm_cfg is not None\n        self.with_activatation = activation is not None\n        # if the conv layer is before a norm layer, bias is unnecessary.\n        if bias == 'auto':\n            bias = False if self.with_norm else True\n        self.with_bias = bias\n\n        if self.with_norm and self.with_bias:\n            warnings.warn('ConvModule has norm and bias at the same time')\n\n        # build convolution layer\n        self.conv = build_conv_layer(\n            conv_cfg,\n            in_channels,\n            out_channels,\n            kernel_size,\n            stride=stride,\n            padding=padding,\n            dilation=dilation,\n            groups=groups,\n            bias=bias)\n        # export the attributes of self.conv to a higher level for convenience\n        self.in_channels = self.conv.in_channels\n        self.out_channels = self.conv.out_channels\n        self.kernel_size = self.conv.kernel_size\n        self.stride = self.conv.stride\n        self.padding = self.conv.padding\n        self.dilation = self.conv.dilation\n        self.transposed = self.conv.transposed\n        self.output_padding = self.conv.output_padding\n        self.groups = self.conv.groups\n\n        # build normalization layers\n        if self.with_norm:\n            # norm layer is after conv layer\n            if order.index('norm') > order.index('conv'):\n                norm_channels = out_channels\n            else:\n                norm_channels = in_channels\n            self.norm_name, norm = build_norm_layer(norm_cfg, norm_channels)\n            self.add_module(self.norm_name, norm)\n\n        # build activation layer\n        if self.with_activatation:\n            # TODO: introduce `act_cfg` and supports more activation layers\n            if self.activation not in ['relu']:\n                raise ValueError('{} is currently not supported.'.format(\n                    self.activation))\n            if self.activation == 'relu':\n                self.activate = nn.ReLU(inplace=inplace)\n    @property\n    def norm(self):\n        return getattr(self, self.norm_name)\n    def forward(self, x, activate=True, norm=True):\n        for layer in self.order:\n            if layer == 'conv':\n                x = self.conv(x)\n            elif layer == 'norm' and norm and self.with_norm:\n                x = self.norm(x)\n            elif layer == 'act' and activate and self.with_activatation:\n                x = self.activate(x)\n        return x\n\nimport numpy as np\nimport torch.nn as nn\n\n\ndef xavier_init(module, gain=1, bias=0, distribution='normal'):\n    assert distribution in ['uniform', 'normal']\n    if distribution == 'uniform':\n        nn.init.xavier_uniform_(module.weight, gain=gain)\n    else:\n        nn.init.xavier_normal_(module.weight, gain=gain)\n    if hasattr(module, 'bias'):\n        nn.init.constant_(module.bias, bias)\n\n\ndef normal_init(module, mean=0, std=1, bias=0):\n    nn.init.normal_(module.weight, mean, std)\n    if hasattr(module, 'bias'):\n        nn.init.constant_(module.bias, bias)\n\n\ndef uniform_init(module, a=0, b=1, bias=0):\n    nn.init.uniform_(module.weight, a, b)\n    if hasattr(module, 'bias'):\n        nn.init.constant_(module.bias, bias)\n\n\ndef kaiming_init(module,\n                 mode='fan_out',\n                 nonlinearity='relu',\n                 bias=0,\n                 distribution='normal'):\n    assert distribution in ['uniform', 'normal']\n    if distribution == 'uniform':\n        nn.init.kaiming_uniform_(\n            module.weight, mode=mode, nonlinearity=nonlinearity)\n    else:\n        nn.init.kaiming_normal_(\n            module.weight, mode=mode, nonlinearity=nonlinearity)\n    if hasattr(module, 'bias'):\n        nn.init.constant_(module.bias, bias)\n\n\ndef bias_init_with_prob(prior_prob):\n    \"\"\" initialize conv/fc bias value according to giving probablity\"\"\"\n    bias_init = float(-np.log((1 - prior_prob) / prior_prob))\n    return bias_init","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"BIFPN"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"class BIFPN(nn.Module):\n    def __init__(self,\n                 in_channels,\n                 out_channels,\n                 num_outs,\n                 start_level=0,\n                 end_level=-1,\n                 stack=1,\n                 add_extra_convs=False,\n                 extra_convs_on_inputs=True,\n                 relu_before_extra_convs=False,\n                 no_norm_on_lateral=False,\n                 conv_cfg=None,\n                 norm_cfg=None,\n                 activation=None):\n        super(BIFPN, self).__init__()\n        assert isinstance(in_channels, list)\n        self.in_channels = in_channels\n        self.out_channels = out_channels\n        self.num_ins = len(in_channels)\n        self.num_outs = num_outs\n        self.activation = activation\n        self.relu_before_extra_convs = relu_before_extra_convs\n        self.no_norm_on_lateral = no_norm_on_lateral\n        self.stack = stack\n\n        if end_level == -1:\n            self.backbone_end_level = self.num_ins\n            assert num_outs >= self.num_ins - start_level\n        else:\n            # if end_level < inputs, no extra level is allowed\n            self.backbone_end_level = end_level\n            assert end_level <= len(in_channels)\n            assert num_outs == end_level - start_level\n        self.start_level = start_level\n        self.end_level = end_level\n        self.add_extra_convs = add_extra_convs\n        self.extra_convs_on_inputs = extra_convs_on_inputs\n\n        self.lateral_convs = nn.ModuleList()\n        self.fpn_convs = nn.ModuleList()\n        self.stack_bifpn_convs = nn.ModuleList()\n\n        for i in range(self.start_level, self.backbone_end_level):\n            l_conv = ConvModule(\n                in_channels[i],\n                out_channels,\n                1,\n                conv_cfg=conv_cfg,\n                norm_cfg=norm_cfg if not self.no_norm_on_lateral else None,\n                activation=self.activation,\n                inplace=False)\n            self.lateral_convs.append(l_conv)\n\n        for ii in range(stack):\n            self.stack_bifpn_convs.append(BiFPNModule(channels=out_channels,\n                                                      levels=self.backbone_end_level-self.start_level,\n                                                      conv_cfg=conv_cfg,\n                                                      norm_cfg=norm_cfg,\n                                                      activation=activation))\n        # add extra conv layers (e.g., RetinaNet)\n        extra_levels = num_outs - self.backbone_end_level + self.start_level\n        if add_extra_convs and extra_levels >= 1:\n            for i in range(extra_levels):\n                if i == 0 and self.extra_convs_on_inputs:\n                    in_channels = self.in_channels[self.backbone_end_level - 1]\n                else:\n                    in_channels = out_channels\n                extra_fpn_conv = ConvModule(\n                    in_channels,\n                    out_channels,\n                    3,\n                    stride=2,\n                    padding=1,\n                    conv_cfg=conv_cfg,\n                    norm_cfg=norm_cfg,\n                    activation=self.activation,\n                    inplace=False)\n                self.fpn_convs.append(extra_fpn_conv)\n\n    # default init_weights for conv(msra) and norm in ConvModule\n    def init_weights(self):\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                xavier_init(m, distribution='uniform')\n\n    def forward(self, inputs):\n        assert len(inputs) == len(self.in_channels)\n\n        # build laterals\n        laterals = [\n            lateral_conv(inputs[i + self.start_level])\n            for i, lateral_conv in enumerate(self.lateral_convs)\n        ]\n        \n        # part 1: build top-down and down-top path with stack\n        used_backbone_levels = len(laterals)\n        for bifpn_module in self.stack_bifpn_convs:\n            laterals = bifpn_module(laterals)\n        outs = laterals\n        # part 2: add extra levels\n        if self.num_outs > len(outs):\n            # use max pool to get more levels on top of outputs\n            # (e.g., Faster R-CNN, Mask R-CNN)\n            if not self.add_extra_convs:\n                for i in range(self.num_outs - used_backbone_levels):\n                    outs.append(F.max_pool2d(outs[-1], 1, stride=2))\n            # add conv layers on top of original feature maps (RetinaNet)\n            else:\n                if self.extra_convs_on_inputs:\n                    orig = inputs[self.backbone_end_level - 1]\n                    outs.append(self.fpn_convs[0](orig))\n                else:\n                    outs.append(self.fpn_convs[0](outs[-1]))\n                for i in range(1, self.num_outs - used_backbone_levels):\n                    if self.relu_before_extra_convs:\n                        outs.append(self.fpn_convs[i](F.relu(outs[-1])))\n                    else:\n                        outs.append(self.fpn_convs[i](outs[-1]))\n        return tuple(outs)\n\n\nclass BiFPNModule(nn.Module):\n    def __init__(self,\n                 channels,\n                 levels,\n                 init=0.5,\n                 conv_cfg=None,\n                 norm_cfg=None,\n                 activation=None,\n                 eps = 0.0001):\n        super(BiFPNModule, self).__init__()\n        self.activation = activation\n        self.eps = eps \n        self.levels = levels\n        self.bifpn_convs = nn.ModuleList()\n        # weighted\n        self.w1 = nn.Parameter(torch.Tensor(2, levels).fill_(init))\n        self.relu1 = nn.ReLU()\n        self.w2 = nn.Parameter(torch.Tensor(3, levels - 2).fill_(init))\n        self.relu2 = nn.ReLU()\n        for jj in range(2):\n            for i in range(self.levels-1):  # 1,2,3\n                fpn_conv = nn.Sequential(\n                    ConvModule(\n                        channels,\n                        channels,\n                        3,\n                        padding=1,\n                        conv_cfg=conv_cfg,\n                        norm_cfg=norm_cfg,\n                        activation=self.activation,\n                        inplace=False)\n                        )\n                self.bifpn_convs.append(fpn_conv)\n\n    # default init_weights for conv(msra) and norm in ConvModule\n    def init_weights(self):\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                xavier_init(m, distribution='uniform')\n\n    def forward(self, inputs):\n        assert len(inputs) == self.levels\n        # build top-down and down-top path with stack\n        levels = self.levels\n        # w relu\n        w1 = self.relu1(self.w1)\n        w1 /= torch.sum(w1, dim=0) + self.eps  # normalize\n        w2 = self.relu2(self.w2)\n        w2 /= torch.sum(w2, dim=0) + self.eps # normalize \n        # build top-down\n        idx_bifpn = 0\n        pathtd = inputs\n        inputs_clone = []\n        for in_tensor in inputs:\n            inputs_clone.append(in_tensor.clone())\n        \n        for i in range(levels - 1, 0, -1):\n            pathtd[i - 1] = (w1[0, i-1]*pathtd[i - 1] + w1[1, i-1]*F.interpolate(pathtd[i], scale_factor=2, mode='nearest'))/(w1[0, i-1] + w1[1, i-1] + self.eps)\n            pathtd[i - 1] = self.bifpn_convs[idx_bifpn](pathtd[i - 1])\n            idx_bifpn = idx_bifpn + 1\n        # build down-top\n        for i in range(0, levels - 2, 1):\n            pathtd[i + 1] = (w2[0, i] * pathtd[i + 1] + w2[1, i] * F.max_pool2d(pathtd[i], kernel_size=2) + w2[2, i] * inputs_clone[i + 1])/(w2[0, i] + w2[1, i] + w2[2, i] + self.eps)\n            pathtd[i + 1] = self.bifpn_convs[idx_bifpn](pathtd[i + 1])\n            idx_bifpn = idx_bifpn + 1\n\n        pathtd[levels - 1] = (w1[0, levels-1] * pathtd[levels - 1] + w1[1, levels-1] * F.max_pool2d(pathtd[levels - 2], kernel_size=2))/(w1[0, levels-1] + w1[1, levels-1] + self.eps)\n        pathtd[levels - 1] = self.bifpn_convs[idx_bifpn](pathtd[levels - 1])\n        return pathtd","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"RetinaHead"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"def multi_apply(func, *args, **kwargs):\n    pfunc = partial(func, **kwargs) if kwargs else func\n    map_results = map(pfunc, *args)\n    return tuple(map(list, zip(*map_results)))\n\nclass RetinaHead(nn.Module):\n    \"\"\"\n    An anchor-based head used in [1]_.\n    The head contains two subnetworks. The first classifies anchor boxes and\n    the second regresses deltas for the anchors.\n    References:\n        .. [1]  https://arxiv.org/pdf/1708.02002.pdf\n    Example:\n        >>> import torch\n        >>> self = RetinaHead(11, 7)\n        >>> x = torch.rand(1, 7, 32, 32)\n        >>> cls_score, bbox_pred = self.forward_single(x)\n        >>> # Each anchor predicts a score for each class except background\n        >>> cls_per_anchor = cls_score.shape[1] / self.num_anchors\n        >>> box_per_anchor = bbox_pred.shape[1] / self.num_anchors\n        >>> assert cls_per_anchor == (self.num_classes - 1)\n        >>> assert box_per_anchor == 4\n    \"\"\"\n\n    def __init__(self,\n                num_classes,\n                 in_channels,\n                 feat_channels=256,\n                 anchor_scales=[8, 16, 32],\n                 anchor_ratios=[0.5, 1.0, 2.0],\n                 anchor_strides=[4, 8, 16, 32, 64],\n                 stacked_convs=4,\n                 octave_base_scale=4,\n                 scales_per_octave=3,\n                 conv_cfg=None,\n                 norm_cfg=None,\n                 **kwargs):\n        super(RetinaHead, self).__init__()\n        self.in_channels = in_channels\n        self.num_classes = num_classes\n        self.feat_channels = feat_channels\n        self.anchor_scales = anchor_scales\n        self.anchor_ratios = anchor_ratios\n        self.anchor_strides = anchor_strides\n        self.stacked_convs = stacked_convs\n        self.octave_base_scale = octave_base_scale\n        self.scales_per_octave = scales_per_octave\n        self.conv_cfg = conv_cfg\n        self.norm_cfg = norm_cfg\n        octave_scales = np.array(\n            [2**(i / scales_per_octave) for i in range(scales_per_octave)])\n        anchor_scales = octave_scales * octave_base_scale\n        self.cls_out_channels = num_classes\n        self.num_anchors = len(self.anchor_ratios) * len(self.anchor_scales)\n        self._init_layers()\n    def _init_layers(self):\n        self.relu = nn.ReLU(inplace=True)\n        self.cls_convs = nn.ModuleList()\n        self.reg_convs = nn.ModuleList()\n        for i in range(self.stacked_convs):\n            chn = self.in_channels if i == 0 else self.feat_channels\n            self.cls_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n            self.reg_convs.append(\n                ConvModule(\n                    chn,\n                    self.feat_channels,\n                    3,\n                    stride=1,\n                    padding=1,\n                    conv_cfg=self.conv_cfg,\n                    norm_cfg=self.norm_cfg))\n        self.retina_cls = nn.Conv2d(\n            self.feat_channels,\n            self.num_anchors * self.cls_out_channels,\n            3,\n            padding=1)\n        self.retina_reg = nn.Conv2d(\n            self.feat_channels, self.num_anchors * 4, 3, padding=1)\n        self.output_act = nn.Sigmoid()\n\n    def init_weights(self):\n        for m in self.cls_convs:\n            normal_init(m.conv, std=0.01)\n        for m in self.reg_convs:\n            normal_init(m.conv, std=0.01)\n        bias_cls = bias_init_with_prob(0.01)\n        normal_init(self.retina_cls, std=0.01, bias=bias_cls)\n        normal_init(self.retina_reg, std=0.01)\n\n    def forward_single(self, x):\n        cls_feat = x\n        reg_feat = x\n        for cls_conv in self.cls_convs:\n            cls_feat = cls_conv(cls_feat)\n        for reg_conv in self.reg_convs:\n            reg_feat = reg_conv(reg_feat)\n        \n        cls_score = self.retina_cls(cls_feat)\n        cls_score = self.output_act(cls_score)\n        # out is B x C x W x H, with C = n_classes + n_anchors\n        cls_score = cls_score.permute(0, 2, 3, 1)\n        batch_size, width, height, channels = cls_score.shape\n        cls_score = cls_score.view(batch_size, width, height, self.num_anchors, self.num_classes)\n        cls_score = cls_score.contiguous().view(x.size(0), -1, self.num_classes)\n\n\n        bbox_pred = self.retina_reg(reg_feat)\n        bbox_pred = bbox_pred.permute(0, 2, 3, 1)\n        bbox_pred = bbox_pred.contiguous().view(bbox_pred.size(0), -1, 4)\n        return cls_score, bbox_pred\n    def forward(self, feats):\n        return multi_apply(self.forward_single, feats)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"module"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"class BBoxTransform(nn.Module):\n    \n    def __init__(self, mean=None, std=None):\n        super(BBoxTransform, self).__init__()\n        if mean is None:\n            self.mean = torch.from_numpy(np.array([0, 0, 0, 0]).astype(np.float32))\n        else:\n            self.mean = mean\n        if std is None:\n            self.std = torch.from_numpy(np.array([0.1, 0.1, 0.2, 0.2]).astype(np.float32))\n        else:\n            self.std = std\n\n    def forward(self, boxes, deltas):\n\n        widths  = boxes[:, :, 2] - boxes[:, :, 0]\n        heights = boxes[:, :, 3] - boxes[:, :, 1]\n        ctr_x   = boxes[:, :, 0] + 0.5 * widths\n        ctr_y   = boxes[:, :, 1] + 0.5 * heights\n\n        dx = deltas[:, :, 0] * self.std[0] + self.mean[0]\n        dy = deltas[:, :, 1] * self.std[1] + self.mean[1]\n        dw = deltas[:, :, 2] * self.std[2] + self.mean[2]\n        dh = deltas[:, :, 3] * self.std[3] + self.mean[3]\n\n        pred_ctr_x = ctr_x + dx * widths\n        pred_ctr_y = ctr_y + dy * heights\n        pred_w     = torch.exp(dw) * widths\n        pred_h     = torch.exp(dh) * heights\n\n        pred_boxes_x1 = pred_ctr_x - 0.5 * pred_w\n        pred_boxes_y1 = pred_ctr_y - 0.5 * pred_h\n        pred_boxes_x2 = pred_ctr_x + 0.5 * pred_w\n        pred_boxes_y2 = pred_ctr_y + 0.5 * pred_h\n\n        pred_boxes = torch.stack([pred_boxes_x1, pred_boxes_y1, pred_boxes_x2, pred_boxes_y2], dim=2)\n\n        return pred_boxes\n\nclass ClipBoxes(nn.Module):\n\n    def __init__(self, width=None, height=None):\n        super(ClipBoxes, self).__init__()\n\n    def forward(self, boxes, img):\n\n        batch_size, num_channels, height, width = img.shape\n\n        boxes[:, :, 0] = torch.clamp(boxes[:, :, 0], min=0)\n        boxes[:, :, 1] = torch.clamp(boxes[:, :, 1], min=0)\n\n        boxes[:, :, 2] = torch.clamp(boxes[:, :, 2], max=width)\n        boxes[:, :, 3] = torch.clamp(boxes[:, :, 3], max=height)\n      \n        return boxes\n\nclass RegressionModel(nn.Module):\n    def __init__(self, num_features_in, num_anchors=9, feature_size=256):\n        super(RegressionModel, self).__init__()\n        \n        self.conv1 = nn.Conv2d(num_features_in, feature_size, kernel_size=3, padding=1)\n        self.act1 = nn.ReLU()\n        self.conv2 = nn.Conv2d(feature_size, feature_size, kernel_size=3, padding=1)\n        self.act2 = nn.ReLU()\n        self.conv3 = nn.Conv2d(feature_size, feature_size, kernel_size=3, padding=1)\n        self.act3 = nn.ReLU()\n        self.conv4 = nn.Conv2d(feature_size, feature_size, kernel_size=3, padding=1)\n        self.act4 = nn.ReLU()\n        self.output = nn.Conv2d(feature_size, num_anchors*4, kernel_size=3, padding=1)\n    def forward(self, x):\n        out = self.conv1(x)\n        out = self.act1(out)\n        out = self.conv2(out)\n        out = self.act2(out)\n        out = self.conv3(out)\n        out = self.act3(out)\n        out = self.conv4(out)\n        out = self.act4(out)\n        out = self.output(out)\n        # out is B x C x W x H, with C = 4*num_anchors\n        out = out.permute(0, 2, 3, 1)\n        return out.contiguous().view(out.shape[0], -1, 4)\n\nclass ClassificationModel(nn.Module):\n    def __init__(self, num_features_in, num_anchors=9, num_classes=80, prior=0.01, feature_size=256):\n        super(ClassificationModel, self).__init__()\n        self.num_classes = num_classes\n        self.num_anchors = num_anchors\n        \n        self.conv1 = nn.Conv2d(num_features_in, feature_size, kernel_size=3, padding=1)\n        self.act1 = nn.ReLU()\n        self.conv2 = nn.Conv2d(feature_size, feature_size, kernel_size=3, padding=1)\n        self.act2 = nn.ReLU()\n        self.conv3 = nn.Conv2d(feature_size, feature_size, kernel_size=3, padding=1)\n        self.act3 = nn.ReLU()\n        self.conv4 = nn.Conv2d(feature_size, feature_size, kernel_size=3, padding=1)\n        self.act4 = nn.ReLU()\n        self.output = nn.Conv2d(feature_size, num_anchors*num_classes, kernel_size=3, padding=1)\n        self.output_act = nn.Sigmoid()\n    def forward(self, x):\n        out = self.conv1(x)\n        out = self.act1(out)\n        out = self.conv2(out)\n        out = self.act2(out)\n        out = self.conv3(out)\n        out = self.act3(out)\n        out = self.conv4(out)\n        out = self.act4(out)\n        out = self.output(out)\n        out = self.output_act(out)\n        # out is B x C x W x H, with C = n_classes + n_anchors\n        out1 = out.permute(0, 2, 3, 1)\n        batch_size, width, height, channels = out1.shape\n        out2 = out1.view(batch_size, width, height, self.num_anchors, self.num_classes)\n        return out2.contiguous().view(x.shape[0], -1, self.num_classes)\n\nclass Anchors(nn.Module):\n    def __init__(self, pyramid_levels=None, strides=None, sizes=None, ratios=None, scales=None):\n        super(Anchors, self).__init__()\n\n        if pyramid_levels is None:\n            self.pyramid_levels = [3, 4, 5, 6, 7]\n        if strides is None:\n            self.strides = [2 ** x for x in self.pyramid_levels]\n        if sizes is None:\n            self.sizes = [2 ** (x + 2) for x in self.pyramid_levels]\n        if ratios is None:\n            self.ratios = np.array([0.5, 1, 2])\n        if scales is None:\n            self.scales = np.array([2 ** 0, 2 ** (1.0 / 3.0), 2 ** (2.0 / 3.0)])\n\n    def forward(self, image):\n        \n        image_shape = image.shape[2:]\n        image_shape = np.array(image_shape)\n        image_shapes = [(image_shape + 2 ** x - 1) // (2 ** x) for x in self.pyramid_levels]\n\n        # compute anchors over all pyramid levels\n        all_anchors = np.zeros((0, 4)).astype(np.float32)\n\n        for idx, p in enumerate(self.pyramid_levels):\n            anchors         = generate_anchors(base_size=self.sizes[idx], ratios=self.ratios, scales=self.scales)\n            shifted_anchors = shift(image_shapes[idx], self.strides[idx], anchors)\n            all_anchors     = np.append(all_anchors, shifted_anchors, axis=0)\n\n        all_anchors = np.expand_dims(all_anchors, axis=0)\n\n        return torch.from_numpy(all_anchors.astype(np.float32)).to(image.device)\n\ndef generate_anchors(base_size=16, ratios=None, scales=None):\n    \"\"\"\n    Generate anchor (reference) windows by enumerating aspect ratios X\n    scales w.r.t. a reference window.\n    \"\"\"\n\n    if ratios is None:\n        ratios = np.array([0.5, 1, 2])\n\n    if scales is None:\n        scales = np.array([2 ** 0, 2 ** (1.0 / 3.0), 2 ** (2.0 / 3.0)])\n\n    num_anchors = len(ratios) * len(scales)\n\n    # initialize output anchors\n    anchors = np.zeros((num_anchors, 4))\n\n    # scale base_size\n    anchors[:, 2:] = base_size * np.tile(scales, (2, len(ratios))).T\n\n    # compute areas of anchors\n    areas = anchors[:, 2] * anchors[:, 3]\n\n    # correct for ratios\n    anchors[:, 2] = np.sqrt(areas / np.repeat(ratios, len(scales)))\n    anchors[:, 3] = anchors[:, 2] * np.repeat(ratios, len(scales))\n\n    # transform from (x_ctr, y_ctr, w, h) -> (x1, y1, x2, y2)\n    anchors[:, 0::2] -= np.tile(anchors[:, 2] * 0.5, (2, 1)).T\n    anchors[:, 1::2] -= np.tile(anchors[:, 3] * 0.5, (2, 1)).T\n\n    return anchors\n\ndef compute_shape(image_shape, pyramid_levels):\n    \"\"\"Compute shapes based on pyramid levels.\n    :param image_shape:\n    :param pyramid_levels:\n    :return:\n    \"\"\"\n    image_shape = np.array(image_shape[:2])\n    image_shapes = [(image_shape + 2 ** x - 1) // (2 ** x) for x in pyramid_levels]\n    return image_shapes\n\ndef anchors_for_shape(\n    image_shape,\n    pyramid_levels=None,\n    ratios=None,\n    scales=None,\n    strides=None,\n    sizes=None,\n    shapes_callback=None,\n):\n\n    image_shapes = compute_shape(image_shape, pyramid_levels)\n\n    # compute anchors over all pyramid levels\n    all_anchors = np.zeros((0, 4))\n    for idx, p in enumerate(pyramid_levels):\n        anchors         = generate_anchors(base_size=sizes[idx], ratios=ratios, scales=scales)\n        shifted_anchors = shift(image_shapes[idx], strides[idx], anchors)\n        all_anchors     = np.append(all_anchors, shifted_anchors, axis=0)\n\n    return all_anchors\n\n\ndef shift(shape, stride, anchors):\n    shift_x = (np.arange(0, shape[1]) + 0.5) * stride\n    shift_y = (np.arange(0, shape[0]) + 0.5) * stride\n\n    shift_x, shift_y = np.meshgrid(shift_x, shift_y)\n\n    shifts = np.vstack((\n        shift_x.ravel(), shift_y.ravel(),\n        shift_x.ravel(), shift_y.ravel()\n    )).transpose()\n\n    # add A anchors (1, A, 4) to\n    # cell K shifts (K, 1, 4) to get\n    # shift anchors (K, A, 4)\n    # reshape to (K*A, 4) shifted anchors\n    A = anchors.shape[0]\n    K = shifts.shape[0]\n    all_anchors = (anchors.reshape((1, A, 4)) + shifts.reshape((1, K, 4)).transpose((1, 0, 2)))\n    all_anchors = all_anchors.reshape((K * A, 4))\n\n    return all_anchors","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"EfficientDet"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"MODEL_MAP = {\n    'efficientdet-d0': 'efficientnet-b0',\n    'efficientdet-d1': 'efficientnet-b1',\n    'efficientdet-d2': 'efficientnet-b2',\n    'efficientdet-d3': 'efficientnet-b3',\n    'efficientdet-d4': 'efficientnet-b4',\n    'efficientdet-d5': 'efficientnet-b5',\n    'efficientdet-d6': 'efficientnet-b6',\n    'efficientdet-d7': 'efficientnet-b6',\n}\nclass EfficientDet(nn.Module):\n    def __init__(self,\n                 num_classes,\n                 network = 'efficientdet-d0',\n                 D_bifpn=3,\n                 W_bifpn=88,\n                 D_class=3,\n                 is_training=True,\n                 threshold=0.5,\n                 iou_threshold=0.5):\n        super(EfficientDet, self).__init__()\n        self.backbone = EfficientNet.from_pretrained(MODEL_MAP[network])\n        self.is_training = is_training\n        self.neck = BIFPN(in_channels=self.backbone.get_list_features()[-5:],\n                                out_channels=W_bifpn,\n                                stack=D_bifpn,\n                                num_outs=5)\n        self.bbox_head = RetinaHead(num_classes = num_classes,\n                                    in_channels = W_bifpn)\n\n\n        self.anchors = Anchors()\n        self.regressBoxes = BBoxTransform()\n        self.clipBoxes = ClipBoxes()\n        self.threshold = threshold\n        self.iou_threshold = iou_threshold\n\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels\n                m.weight.data.normal_(0, math.sqrt(2. / n))\n            elif isinstance(m, nn.BatchNorm2d):\n                m.weight.data.fill_(1)\n                m.bias.data.zero_()\n        self.freeze_bn()\n\n    def forward(self, inputs):\n        x = self.extract_feat(inputs)\n        outs = self.bbox_head(x)\n        classification = torch.cat([out for out in outs[0]], dim=1)\n        regression = torch.cat([out for out in outs[1]], dim=1)\n        anchors = self.anchors(inputs)\n        if self.is_training:\n            return classification, regression, anchors\n        else:\n            transformed_anchors = self.regressBoxes(anchors, regression)\n            transformed_anchors = self.clipBoxes(transformed_anchors, inputs)\n            scores = torch.max(classification, dim=2, keepdim=True)[0]\n            scores_over_thresh = (scores > self.threshold)[0, :, 0]\n\n            if scores_over_thresh.sum() == 0:\n                print('No boxes to NMS')\n                # no boxes to NMS, just return\n                return [torch.zeros(0), torch.zeros(0), torch.zeros(0, 4)]\n            classification = classification[:, scores_over_thresh, :]\n            transformed_anchors = transformed_anchors[:, scores_over_thresh, :]\n            scores = scores[:, scores_over_thresh, :]\n            anchors_nms_idx = nms(transformed_anchors[0, :, :], scores[0, :, 0], iou_threshold = self.iou_threshold)\n            nms_scores, nms_class = classification[0, anchors_nms_idx, :].max(dim=1)\n            return [nms_scores, nms_class, transformed_anchors[0, anchors_nms_idx, :]]\n    def freeze_bn(self):\n        '''Freeze BatchNorm layers.'''\n        for layer in self.modules():\n            if isinstance(layer, nn.BatchNorm2d):\n                layer.eval()\n    def extract_feat(self, img):\n        \"\"\"\n            Directly extract features from the backbone+neck\n        \"\"\"\n        x = self.backbone(img)\n        x = self.neck(x[-5:])\n        return x","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Detect"},{"metadata":{},"cell_type":"markdown","source":"vis_bbox, get_augmentation"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"def get_augumentation(phase, width=512, height=512, min_area=0., min_visibility=0.):\n    list_transforms = []\n    if phase == 'train':\n        list_transforms.extend([\n            albu.augmentations.transforms.LongestMaxSize(\n                max_size=width, always_apply=True),\n            albu.PadIfNeeded(min_height=height, min_width=width,\n                             always_apply=True, border_mode=0, value=[0, 0, 0]),\n            albu.augmentations.transforms.RandomResizedCrop(\n                height=height,\n                width=width, p=0.3),\n            albu.augmentations.transforms.Flip(),\n            albu.augmentations.transforms.Transpose(),\n            albu.OneOf([\n                albu.RandomBrightnessContrast(brightness_limit=0.5,\n                                              contrast_limit=0.4),\n                albu.RandomGamma(gamma_limit=(50, 150)),\n                albu.NoOp()\n            ]),\n            albu.OneOf([\n                albu.RGBShift(r_shift_limit=20, b_shift_limit=15,\n                              g_shift_limit=15),\n                albu.HueSaturationValue(hue_shift_limit=5,\n                                        sat_shift_limit=5),\n                albu.NoOp()\n            ]),\n            albu.CLAHE(p=0.8),\n            albu.HorizontalFlip(p=0.5),\n            albu.VerticalFlip(p=0.5),\n        ])\n    if(phase == 'test'):\n        list_transforms.extend([\n            albu.Resize(height=height, width=width)\n        ])\n    list_transforms.extend([\n        albu.Normalize(mean=(0.485, 0.456, 0.406),\n                       std=(0.229, 0.224, 0.225), p=1),\n        ToTensor()\n    ])\n    if(phase == 'test'):\n        return albu.Compose(list_transforms)\n    return albu.Compose(list_transforms, bbox_params=albu.BboxParams(format='pascal_voc', min_area=min_area,\n                                                                     min_visibility=min_visibility, label_fields=['category_id']))\n\ndef vis_bbox(img, bbox, label=None, score=None,\n             instance_colors=None, alpha=1., linewidth=2., ax=None):\n    \"\"\"Visualize bounding boxes inside the image.\n    Args:\n        img (~numpy.ndarray): An array of shape :math:`(3, height, width)`.\n            This is in RGB format and the range of its value is\n            :math:`[0, 255]`. If this is :obj:`None`, no image is displayed.\n        bbox (~numpy.ndarray): An array of shape :math:`(R, 4)`, where\n            :math:`R` is the number of bounding boxes in the image.\n            Each element is organized\n            by :math:`(y_{min}, x_{min}, y_{max}, x_{max})` in the second axis.\n        label (~numpy.ndarray): An integer array of shape :math:`(R,)`.\n            The values correspond to id for label names stored in\n            :obj:`label_names`. This is optional.\n        score (~numpy.ndarray): A float array of shape :math:`(R,)`.\n             Each value indicates how confident the prediction is.\n             This is optional.\n        label_names (iterable of strings): Name of labels ordered according\n            to label ids. If this is :obj:`None`, labels will be skipped.\n        instance_colors (iterable of tuples): List of colors.\n            Each color is RGB format and the range of its values is\n            :math:`[0, 255]`. The :obj:`i`-th element is the color used\n            to visualize the :obj:`i`-th instance.\n            If :obj:`instance_colors` is :obj:`None`, the red is used for\n            all boxes.\n        alpha (float): The value which determines transparency of the\n            bounding boxes. The range of this value is :math:`[0, 1]`.\n        linewidth (float): The thickness of the edges of the bounding boxes.\n        ax (matplotlib.axes.Axis): The visualization is displayed on this\n            axis. If this is :obj:`None` (default), a new axis is created.\n    Returns:\n        ~matploblib.axes.Axes:\n        Returns the Axes object with the plot for further tweaking.\n    from: https://github.com/chainer/chainercv\n    \"\"\"\n             \n    if label is not None and not len(bbox) == len(label):\n        raise ValueError('The length of label must be same as that of bbox')\n    if score is not None and not len(bbox) == len(score):\n        raise ValueError('The length of score must be same as that of bbox')\n\n    # Returns newly instantiated matplotlib.axes.Axes object if ax is None\n    if ax is None:\n        fig = plt.figure()\n        # ax = fig.add_subplot(1, 1, 1)\n        h, w, _ = img.shape\n        w_ = w / 60.0\n        h_ = w_ * (h / w)\n        fig.set_size_inches((w_, h_))\n        ax = plt.axes([0, 0, 1, 1])\n    ax.imshow(img.astype(np.uint8))\n    ax.axis('off')\n    # If there is no bounding box to display, visualize the image and exit.\n    if len(bbox) == 0:\n        return fig, ax\n\n    if instance_colors is None:\n        # Red\n        instance_colors = np.zeros((len(bbox), 3), dtype=np.float32)\n        instance_colors[:, 0] = 51\n        instance_colors[:, 1] = 51\n        instance_colors[:, 2] = 224\n    instance_colors = np.array(instance_colors)\n\n    for i, bb in enumerate(bbox):\n        xy = (bb[0], bb[1])\n        height = bb[3] - bb[1]\n        width = bb[2] - bb[0]\n        color = instance_colors[i % len(instance_colors)] / 255\n        ax.add_patch(plt.Rectangle(\n            xy, width, height, fill=False,\n            edgecolor=color, linewidth=linewidth, alpha=alpha))\n\n        caption = []\n        caption.append(label[i])\n        if(len(score)>0):\n            sc = score[i]\n            caption.append('{}'.format(sc))\n\n        if len(caption) > 0:\n            face_color = np.array([225, 51, 123])/255\n            ax.text(bb[0], bb[1],\n                    ': '.join(caption),\n                    fontsize=12,\n                    color='black',\n                    style='italic',\n                    bbox={'facecolor': face_color, 'edgecolor': face_color, 'alpha': 1, 'pad': 0})\n    return fig, ax","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"VOC_CLASSES"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"VOC_CLASSES = (  # always index 0\n    'aeroplane', 'bicycle', 'bird', 'boat',\n    'bottle', 'bus', 'car', 'cat', 'chair',\n    'cow', 'diningtable', 'dog', 'horse',\n    'motorbike', 'person', 'pottedplant',\n    'sheep', 'sofa', 'train', 'tvmonitor')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"EFFICIENTDET"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"EFFICIENTDET = {\n    'efficientdet-d0': {'input_size': 512,\n                        'backbone': 'B0',\n                        'W_bifpn': 64,\n                        'D_bifpn': 2,\n                        'D_class': 3},\n    'efficientdet-d1': {'input_size': 640,\n                        'backbone': 'B1',\n                        'W_bifpn': 88,\n                        'D_bifpn': 3,\n                        'D_class': 3},\n    'efficientdet-d2': {'input_size': 768,\n                        'backbone': 'B2',\n                        'W_bifpn': 112,\n                        'D_bifpn': 4,\n                        'D_class': 3},\n    'efficientdet-d3': {'input_size': 896,\n                        'backbone': 'B3',\n                        'W_bifpn': 160,\n                        'D_bifpn': 5,\n                        'D_class': 4},\n    'efficientdet-d4': {'input_size': 1024,\n                        'backbone': 'B4',\n                        'W_bifpn': 224,\n                        'D_bifpn': 6,\n                        'D_class': 4},\n    'efficientdet-d5': {'input_size': 1280,\n                        'backbone': 'B5',\n                        'W_bifpn': 288,\n                        'D_bifpn': 7,\n                        'D_class': 4},\n    'efficientdet-d6': {'input_size': 1408,\n                        'backbone': 'B6',\n                        'W_bifpn': 384,\n                        'D_bifpn': 8,\n                        'D_class': 5},\n    'efficientdet-d7': {'input_size': 1636,\n                        'backbone': 'B6',\n                        'W_bifpn': 384,\n                        'D_bifpn': 8,\n                        'D_class': 5},\n}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class Detect(object):\n    \"\"\"\n        dir_name: Folder or image_file\n    \"\"\"\n\n    def __init__(self, weights, num_class=21, network='efficientdet-d1', size_image=(640, 640)):\n        super(Detect,  self).__init__()\n        self.weights = weights\n        self.size_image = size_image\n        self.device = torch.device(\n            \"cuda:0\" if torch.cuda.is_available() else 'cpu')\n        self.transform = get_augumentation(phase='test', width=self.size_image[1], height=self.size_image[0])\n        if(self.weights is not None):\n            print('Load pretrained Model')\n            checkpoint = torch.load(\n                self.weights, map_location=lambda storage, loc: storage)\n            num_class = checkpoint['num_class']\n            network = checkpoint['network']\n\n        self.model = EfficientDet(num_classes=num_class,\n                     network=network,\n                     W_bifpn=EFFICIENTDET[network]['W_bifpn'],\n                     D_bifpn=EFFICIENTDET[network]['D_bifpn'],\n                     D_class=EFFICIENTDET[network]['D_class'],\n                     is_training=False\n                     )\n\n        if(self.weights is not None):\n            state_dict = checkpoint['state_dict']\n            self.model.load_state_dict(state_dict)\n        self.model = self.model.cuda()\n        self.model.eval()\n\n    def process(self, file_name=None, img=None, show=False, score=True):\n        if file_name is not None:\n            img = cv2.imread(file_name)\n#         origin_img = copy.deepcopy(img)\n        origin_img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\n        augmentation = self.transform(image=img)\n        img = augmentation['image']\n        img = img.to(self.device)\n        img = img.unsqueeze(0)\n\n        with torch.no_grad():\n            scores, classification, transformed_anchors = self.model(img)\n            bboxes = list()\n            labels = list()\n            bbox_scores = list()\n            colors = list()\n            for j in range(scores.shape[0]):\n                bbox = transformed_anchors[[j], :][0].data.cpu().numpy()\n                x1 = int(bbox[0]*origin_img.shape[1]/self.size_image[1])\n                y1 = int(bbox[1]*origin_img.shape[0]/self.size_image[0])\n                x2 = int(bbox[2]*origin_img.shape[1]/self.size_image[1])\n                y2 = int(bbox[3]*origin_img.shape[0]/self.size_image[0])\n                bboxes.append([x1, y1, x2, y2])\n                label_name = VOC_CLASSES[int(classification[[j]])]\n                labels.append(label_name)\n\n                if score:\n                    score = np.around(scores[[j]].cpu().numpy(), decimals=2) * 100\n                    bbox_scores.append(int(score))\n                    \n            if show:\n                fig, ax = vis_bbox(img=origin_img, bbox=bboxes,\n                                   label=labels, score=bbox_scores)\n                fig.savefig('./demo.png')\n                plt.show()\n            else:\n                return origin_img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"detect = Detect(weights='../input/efficientdetd0/checkpoint_VOC_efficientdet-d1_37.pth')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test = pd.read_csv('../input/pku-autonomous-driving/sample_submission.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"n = np.random.randint(len(test))\nfile_path = '../input/pku-autonomous-driving/test_images/' + test[\"ImageId\"][n] + '.jpg'\ndetect.process(file_name=file_path, show=True)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":1}