{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Inference Kernel"},{"metadata":{},"cell_type":"markdown","source":"## Intialization"},{"metadata":{},"cell_type":"markdown","source":"### Imports"},{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"import os\nimport cv2\nimport sys\nimport time\nimport math\n\nimport random\nimport librosa\nimport warnings\nimport torchaudio\nimport torchvision\nimport numpy as np\nimport pandas as pd\nimport typing as tp\nimport IPython.display as ipd\nimport matplotlib.pyplot as plt\n\nfrom pathlib import Path\nfrom collections import Counter\n\nimport torch\nimport torch.nn as nn\nimport torch.utils.data as data\nimport torch.nn.functional as F\n\nfrom torch.utils.data import DataLoader\nfrom torch.nn.modules.utils import _pair\nfrom torch.nn import Conv2d, Module, Linear, BatchNorm2d, ReLU\n\n\npd.options.display.max_rows = 500\npd.options.display.max_columns = 500","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","collapsed":true},"cell_type":"markdown","source":"### Utils"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"def load_audio(path, sr):\n    clip, _ = librosa.load(path, sr=sr, mono=True, res_type=\"kaiser_fast\")\n    return clip","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"def load_model_weights(model, weights):\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    state_dict = torch.load(weights, map_location=device)\n    model.load_state_dict(state_dict)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"def seed_everything(seed):\n    \"\"\"\n    Seeds basic parameters for reproductibility of results\n    \n    Arguments:\n        seed {int} -- Number of the seed\n    \"\"\"\n    random.seed(seed)\n    os.environ[\"PYTHONHASHSEED\"] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = True #False","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Data"},{"metadata":{"trusted":true},"cell_type":"code","source":"SEED = 1213\nseed_everything(SEED)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ROOT = Path.cwd().parent\nINPUT_ROOT = ROOT / \"input\"\nRAW_DATA = INPUT_ROOT / \"birdsong-recognition\"\nTRAIN_AUDIO_DIR = RAW_DATA / \"train_audio\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train = pd.read_csv(RAW_DATA / \"train.csv\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"TEST_AUDIO_DIR = RAW_DATA / \"test_audio\"\n\nif not TEST_AUDIO_DIR.exists():\n    TEST_AUDIO_DIR = INPUT_ROOT / \"birdcall-check\" / \"test_audio\"\n    test = pd.read_csv(INPUT_ROOT / \"birdcall-check\" / \"test.csv\")\nelse:\n    test = pd.read_csv(RAW_DATA / \"test.csv\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Parameters"},{"metadata":{"trusted":true},"cell_type":"code","source":"CLASSES = sorted(os.listdir(TRAIN_AUDIO_DIR))\nNUM_CLASSES = len(CLASSES)\nNUM_WORKERS = 4","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class AudioParams:\n    sr = 32000\n    stride = 5\n    true_kernel_size = 5\n\n    img_size = None\n    \n    # Melspectrogram\n    n_mels = 128\n    fmin = 20\n    fmax = 16000","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Dataset"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"def convert_site_3(df, clip_length, params):\n    n_samples = clip_length // (params.sr * params.true_kernel_size)  # may lose the end \n    \n    audio_id = [df['audio_id'].values[0]] * n_samples\n    site = ['site_3'] * n_samples\n    seconds = [i * params.true_kernel_size for i in range(1, n_samples + 1)]\n    row_id = [f'site_3_{audio_id[0]}_{int(s)}' for s in seconds]\n    \n    new_df = pd.DataFrame(data={'site': site,\n                                'row_id': row_id,\n                                'seconds': seconds,\n                                'audio_id': audio_id\n                               })\n    \n    return new_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"def compute_melspec(y, params):\n    melspec = librosa.feature.melspectrogram(\n        y,\n        sr=params.sr,\n        n_mels=params.n_mels,\n        fmin=params.fmin,\n        fmax=params.fmax\n    )\n    \n    melspec = librosa.power_to_db(melspec).astype(np.float32)\n    \n    return melspec","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"def mono_to_color(X, eps=1e-6, mean=None, std=None):\n    X = np.stack([X, X, X], axis=-1)\n\n    # Standardize\n    mean = mean or X.mean()\n    std = std or X.std()\n    X = (X - mean) / (std + eps)\n\n    # Normalize to [0, 255]\n    _min, _max = X.min(), X.max()\n\n    if (_max - _min) > eps:\n        V = np.clip(X, _min, _max)\n        V = 255 * (V - _min) / (_max - _min)\n        V = V.astype(np.uint8)\n    else:\n        V = np.zeros_like(X, dtype=np.uint8)\n\n    return V\n\n\ndef resize(image, size=None):\n    if size is not None:\n        h, w, _ = image.shape\n        new_w, new_h = int(w * size / h), size\n        image = cv2.resize(image, (new_w, new_h))\n\n    return image\n\n\ndef normalize(image, mean=None, std=None):\n    image = image / 255.0\n    if mean is not None and std is not None:\n        image = (image - mean) / std\n    return np.moveaxis(image, 2, 0).astype(np.float32)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"trusted":true},"cell_type":"code","source":"class TestDataset(data.Dataset):\n    def __init__(self, df, clip, params):\n        self.df = df\n        self.clip = clip\n        self.params = params\n        \n        if df['site'].values[0] == 'site_3':\n            self.df = convert_site_3(df, len(clip), params)\n        \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, idx):      \n        end_seconds = int(self.df['seconds'][idx])\n        start_seconds = int(end_seconds - 5)\n\n        start_index = self.params.sr * start_seconds\n        end_index = self.params.sr * end_seconds\n\n        y = self.clip[start_index:end_index].astype(np.float32)\n\n        melspec = compute_melspec(y, self.params)\n        \n        image = mono_to_color(melspec)\n        image = resize(image, self.params.img_size)\n        image = normalize(image, mean=None, std=None)\n        \n        return image","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Modeling"},{"metadata":{},"cell_type":"markdown","source":"\n### Resnest\n> From https://github.com/zhanghang1989/ResNeSt"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"class SplAtConv2d(Module):\n    \"\"\"Split-Attention Conv2d\n    \"\"\"\n    def __init__(self, in_channels, channels, kernel_size, stride=(1, 1), padding=(0, 0),\n                 dilation=(1, 1), groups=1, bias=True,\n                 radix=2, reduction_factor=4,\n                 rectify=False, rectify_avg=False, norm_layer=None,\n                 dropblock_prob=0.0, **kwargs):\n        super(SplAtConv2d, self).__init__()\n        padding = _pair(padding)\n        self.rectify = rectify and (padding[0] > 0 or padding[1] > 0)\n        self.rectify_avg = rectify_avg\n        inter_channels = max(in_channels*radix//reduction_factor, 32)\n        self.radix = radix\n        self.cardinality = groups\n        self.channels = channels\n        self.dropblock_prob = dropblock_prob\n        if self.rectify:\n            from rfconv import RFConv2d\n            self.conv = RFConv2d(in_channels, channels*radix, kernel_size, stride, padding, dilation,\n                                 groups=groups*radix, bias=bias, average_mode=rectify_avg, **kwargs)\n        else:\n            self.conv = Conv2d(in_channels, channels*radix, kernel_size, stride, padding, dilation,\n                               groups=groups*radix, bias=bias, **kwargs)\n        self.use_bn = norm_layer is not None\n        if self.use_bn:\n            self.bn0 = norm_layer(channels*radix)\n        self.relu = ReLU(inplace=True)\n        self.fc1 = Conv2d(channels, inter_channels, 1, groups=self.cardinality)\n        if self.use_bn:\n            self.bn1 = norm_layer(inter_channels)\n        self.fc2 = Conv2d(inter_channels, channels*radix, 1, groups=self.cardinality)\n        if dropblock_prob > 0.0:\n            self.dropblock = DropBlock2D(dropblock_prob, 3)\n        self.rsoftmax = rSoftMax(radix, groups)\n\n    def forward(self, x):\n        x = self.conv(x)\n        if self.use_bn:\n            x = self.bn0(x)\n        if self.dropblock_prob > 0.0:\n            x = self.dropblock(x)\n        x = self.relu(x)\n\n        batch, rchannel = x.shape[:2]\n        if self.radix > 1:\n            if torch.__version__ < '1.5':\n                splited = torch.split(x, int(rchannel//self.radix), dim=1)\n            else:\n                splited = torch.split(x, rchannel//self.radix, dim=1)\n            gap = sum(splited) \n        else:\n            gap = x\n        gap = F.adaptive_avg_pool2d(gap, 1)\n        gap = self.fc1(gap)\n\n        if self.use_bn:\n            gap = self.bn1(gap)\n        gap = self.relu(gap)\n\n        atten = self.fc2(gap)\n        atten = self.rsoftmax(atten).view(batch, -1, 1, 1)\n\n        if self.radix > 1:\n            if torch.__version__ < '1.5':\n                attens = torch.split(atten, int(rchannel//self.radix), dim=1)\n            else:\n                attens = torch.split(atten, rchannel//self.radix, dim=1)\n            out = sum([att*split for (att, split) in zip(attens, splited)])\n        else:\n            out = atten * x\n        return out.contiguous()\n\nclass rSoftMax(nn.Module):\n    def __init__(self, radix, cardinality):\n        super().__init__()\n        self.radix = radix\n        self.cardinality = cardinality\n\n    def forward(self, x):\n        batch = x.size(0)\n        if self.radix > 1:\n            x = x.view(batch, self.cardinality, self.radix, -1).transpose(1, 2)\n            x = F.softmax(x, dim=1)\n            x = x.reshape(batch, -1)\n        else:\n            x = torch.sigmoid(x)\n        return x","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"class DropBlock2D(object):\n    def __init__(self, *args, **kwargs):\n        raise NotImplementedError\n\nclass GlobalAvgPool2d(nn.Module):\n    def __init__(self):\n        \"\"\"Global average pooling over the input's spatial dimensions\"\"\"\n        super(GlobalAvgPool2d, self).__init__()\n\n    def forward(self, inputs):\n        return nn.functional.adaptive_avg_pool2d(inputs, 1).view(inputs.size(0), -1)\n\nclass Bottleneck(nn.Module):\n    \"\"\"ResNet Bottleneck\n    \"\"\"\n    # pylint: disable=unused-argument\n    expansion = 4\n    def __init__(self, inplanes, planes, stride=1, downsample=None,\n                 radix=1, cardinality=1, bottleneck_width=64,\n                 avd=False, avd_first=False, dilation=1, is_first=False,\n                 rectified_conv=False, rectify_avg=False,\n                 norm_layer=None, dropblock_prob=0.0, last_gamma=False):\n        super(Bottleneck, self).__init__()\n        group_width = int(planes * (bottleneck_width / 64.)) * cardinality\n        self.conv1 = nn.Conv2d(inplanes, group_width, kernel_size=1, bias=False)\n        self.bn1 = norm_layer(group_width)\n        self.dropblock_prob = dropblock_prob\n        self.radix = radix\n        self.avd = avd and (stride > 1 or is_first)\n        self.avd_first = avd_first\n\n        if self.avd:\n            self.avd_layer = nn.AvgPool2d(3, stride, padding=1)\n            stride = 1\n\n        if dropblock_prob > 0.0:\n            self.dropblock1 = DropBlock2D(dropblock_prob, 3)\n            if radix == 1:\n                self.dropblock2 = DropBlock2D(dropblock_prob, 3)\n            self.dropblock3 = DropBlock2D(dropblock_prob, 3)\n\n        if radix >= 1:\n            self.conv2 = SplAtConv2d(\n                group_width, group_width, kernel_size=3,\n                stride=stride, padding=dilation,\n                dilation=dilation, groups=cardinality, bias=False,\n                radix=radix, rectify=rectified_conv,\n                rectify_avg=rectify_avg,\n                norm_layer=norm_layer,\n                dropblock_prob=dropblock_prob)\n        elif rectified_conv:\n            from rfconv import RFConv2d\n            self.conv2 = RFConv2d(\n                group_width, group_width, kernel_size=3, stride=stride,\n                padding=dilation, dilation=dilation,\n                groups=cardinality, bias=False,\n                average_mode=rectify_avg)\n            self.bn2 = norm_layer(group_width)\n        else:\n            self.conv2 = nn.Conv2d(\n                group_width, group_width, kernel_size=3, stride=stride,\n                padding=dilation, dilation=dilation,\n                groups=cardinality, bias=False)\n            self.bn2 = norm_layer(group_width)\n\n        self.conv3 = nn.Conv2d(\n            group_width, planes * 4, kernel_size=1, bias=False)\n        self.bn3 = norm_layer(planes*4)\n\n        if last_gamma:\n            from torch.nn.init import zeros_\n            zeros_(self.bn3.weight)\n        self.relu = nn.ReLU(inplace=True)\n        self.downsample = downsample\n        self.dilation = dilation\n        self.stride = stride\n\n    def forward(self, x):\n        residual = x\n\n        out = self.conv1(x)\n        out = self.bn1(out)\n        if self.dropblock_prob > 0.0:\n            out = self.dropblock1(out)\n        out = self.relu(out)\n\n        if self.avd and self.avd_first:\n            out = self.avd_layer(out)\n\n        out = self.conv2(out)\n        if self.radix == 0:\n            out = self.bn2(out)\n            if self.dropblock_prob > 0.0:\n                out = self.dropblock2(out)\n            out = self.relu(out)\n\n        if self.avd and not self.avd_first:\n            out = self.avd_layer(out)\n\n        out = self.conv3(out)\n        out = self.bn3(out)\n        if self.dropblock_prob > 0.0:\n            out = self.dropblock3(out)\n\n        if self.downsample is not None:\n            residual = self.downsample(x)\n\n        out += residual\n        out = self.relu(out)\n\n        return out\n\nclass ResNet(nn.Module):\n    \"\"\"ResNet Variants\n    Parameters\n    ----------\n    block : Block\n        Class for the residual block. Options are BasicBlockV1, BottleneckV1.\n    layers : list of int\n        Numbers of layers in each block\n    classes : int, default 1000\n        Number of classification classes.\n    dilated : bool, default False\n        Applying dilation strategy to pretrained ResNet yielding a stride-8 model,\n        typically used in Semantic Segmentation.\n    norm_layer : object\n        Normalization layer used in backbone network (default: :class:`mxnet.gluon.nn.BatchNorm`;\n        for Synchronized Cross-GPU BachNormalization).\n    Reference:\n        - He, Kaiming, et al. \"Deep residual learning for image recognition.\" Proceedings of the IEEE conference on computer vision and pattern recognition. 2016.\n        - Yu, Fisher, and Vladlen Koltun. \"Multi-scale context aggregation by dilated convolutions.\"\n    \"\"\"\n    # pylint: disable=unused-variable\n    def __init__(self, block, layers, radix=1, groups=1, bottleneck_width=64,\n                 num_classes=1000, dilated=False, dilation=1,\n                 deep_stem=False, stem_width=64, avg_down=False,\n                 rectified_conv=False, rectify_avg=False,\n                 avd=False, avd_first=False,\n                 final_drop=0.0, dropblock_prob=0,\n                 last_gamma=False, norm_layer=nn.BatchNorm2d):\n        self.cardinality = groups\n        self.bottleneck_width = bottleneck_width\n        # ResNet-D params\n        self.inplanes = stem_width*2 if deep_stem else 64\n        self.avg_down = avg_down\n        self.last_gamma = last_gamma\n        # ResNeSt params\n        self.radix = radix\n        self.avd = avd\n        self.avd_first = avd_first\n\n        super(ResNet, self).__init__()\n        self.rectified_conv = rectified_conv\n        self.rectify_avg = rectify_avg\n        if rectified_conv:\n            from rfconv import RFConv2d\n            conv_layer = RFConv2d\n        else:\n            conv_layer = nn.Conv2d\n        conv_kwargs = {'average_mode': rectify_avg} if rectified_conv else {}\n        if deep_stem:\n            self.conv1 = nn.Sequential(\n                conv_layer(3, stem_width, kernel_size=3, stride=2, padding=1, bias=False, **conv_kwargs),\n                norm_layer(stem_width),\n                nn.ReLU(inplace=True),\n                conv_layer(stem_width, stem_width, kernel_size=3, stride=1, padding=1, bias=False, **conv_kwargs),\n                norm_layer(stem_width),\n                nn.ReLU(inplace=True),\n                conv_layer(stem_width, stem_width*2, kernel_size=3, stride=1, padding=1, bias=False, **conv_kwargs),\n            )\n        else:\n            self.conv1 = conv_layer(3, 64, kernel_size=7, stride=2, padding=3,\n                                   bias=False, **conv_kwargs)\n        self.bn1 = norm_layer(self.inplanes)\n        self.relu = nn.ReLU(inplace=True)\n        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)\n        self.layer1 = self._make_layer(block, 64, layers[0], norm_layer=norm_layer, is_first=False)\n        self.layer2 = self._make_layer(block, 128, layers[1], stride=2, norm_layer=norm_layer)\n        if dilated or dilation == 4:\n            self.layer3 = self._make_layer(block, 256, layers[2], stride=1,\n                                           dilation=2, norm_layer=norm_layer,\n                                           dropblock_prob=dropblock_prob)\n            self.layer4 = self._make_layer(block, 512, layers[3], stride=1,\n                                           dilation=4, norm_layer=norm_layer,\n                                           dropblock_prob=dropblock_prob)\n        elif dilation==2:\n            self.layer3 = self._make_layer(block, 256, layers[2], stride=2,\n                                           dilation=1, norm_layer=norm_layer,\n                                           dropblock_prob=dropblock_prob)\n            self.layer4 = self._make_layer(block, 512, layers[3], stride=1,\n                                           dilation=2, norm_layer=norm_layer,\n                                           dropblock_prob=dropblock_prob)\n        else:\n            self.layer3 = self._make_layer(block, 256, layers[2], stride=2,\n                                           norm_layer=norm_layer,\n                                           dropblock_prob=dropblock_prob)\n            self.layer4 = self._make_layer(block, 512, layers[3], stride=2,\n                                           norm_layer=norm_layer,\n                                           dropblock_prob=dropblock_prob)\n        self.avgpool = GlobalAvgPool2d()\n        self.drop = nn.Dropout(final_drop) if final_drop > 0.0 else None\n        self.fc = nn.Linear(512 * block.expansion, num_classes)\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, norm_layer):\n                m.weight.data.fill_(1)\n                m.bias.data.zero_()\n\n    def _make_layer(self, block, planes, blocks, stride=1, dilation=1, norm_layer=None,\n                    dropblock_prob=0.0, is_first=True):\n        downsample = None\n        if stride != 1 or self.inplanes != planes * block.expansion:\n            down_layers = []\n            if self.avg_down:\n                if dilation == 1:\n                    down_layers.append(nn.AvgPool2d(kernel_size=stride, stride=stride,\n                                                    ceil_mode=True, count_include_pad=False))\n                else:\n                    down_layers.append(nn.AvgPool2d(kernel_size=1, stride=1,\n                                                    ceil_mode=True, count_include_pad=False))\n                down_layers.append(nn.Conv2d(self.inplanes, planes * block.expansion,\n                                             kernel_size=1, stride=1, bias=False))\n            else:\n                down_layers.append(nn.Conv2d(self.inplanes, planes * block.expansion,\n                                             kernel_size=1, stride=stride, bias=False))\n            down_layers.append(norm_layer(planes * block.expansion))\n            downsample = nn.Sequential(*down_layers)\n\n        layers = []\n        if dilation == 1 or dilation == 2:\n            layers.append(block(self.inplanes, planes, stride, downsample=downsample,\n                                radix=self.radix, cardinality=self.cardinality,\n                                bottleneck_width=self.bottleneck_width,\n                                avd=self.avd, avd_first=self.avd_first,\n                                dilation=1, is_first=is_first, rectified_conv=self.rectified_conv,\n                                rectify_avg=self.rectify_avg,\n                                norm_layer=norm_layer, dropblock_prob=dropblock_prob,\n                                last_gamma=self.last_gamma))\n        elif dilation == 4:\n            layers.append(block(self.inplanes, planes, stride, downsample=downsample,\n                                radix=self.radix, cardinality=self.cardinality,\n                                bottleneck_width=self.bottleneck_width,\n                                avd=self.avd, avd_first=self.avd_first,\n                                dilation=2, is_first=is_first, rectified_conv=self.rectified_conv,\n                                rectify_avg=self.rectify_avg,\n                                norm_layer=norm_layer, dropblock_prob=dropblock_prob,\n                                last_gamma=self.last_gamma))\n        else:\n            raise RuntimeError(\"=> unknown dilation size: {}\".format(dilation))\n\n        self.inplanes = planes * block.expansion\n        for i in range(1, blocks):\n            layers.append(block(self.inplanes, planes,\n                                radix=self.radix, cardinality=self.cardinality,\n                                bottleneck_width=self.bottleneck_width,\n                                avd=self.avd, avd_first=self.avd_first,\n                                dilation=dilation, rectified_conv=self.rectified_conv,\n                                rectify_avg=self.rectify_avg,\n                                norm_layer=norm_layer, dropblock_prob=dropblock_prob,\n                                last_gamma=self.last_gamma))\n\n        return nn.Sequential(*layers)\n\n    def forward(self, x):\n        x = self.conv1(x)\n        x = self.bn1(x)\n        x = self.relu(x)\n        x = self.maxpool(x)\n\n        x = self.layer1(x)\n        x = self.layer2(x)\n        x = self.layer3(x)\n        x = self.layer4(x)\n\n        x = self.avgpool(x)\n        #x = x.view(x.size(0), -1)\n        x = torch.flatten(x, 1)\n        if self.drop:\n            x = self.drop(x)\n        x = self.fc(x)\n\n        return x","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"MODEL_CONFIGS = {\n    \"resnest50_fast_1s1x64d\":\n    {\n        \"num_classes\": 264,\n        \"block\": Bottleneck,\n        \"layers\": [3, 4, 6, 3],\n        \"radix\": 1,\n        \"groups\": 1,\n        \"bottleneck_width\": 64,\n        \"deep_stem\": True,\n        \"stem_width\": 32,\n        \"avg_down\": True,\n        \"avd\" : True,\n        \"avd_first\" : True\n    },\n    \"resnest101\":\n    {\n        \"num_classes\": 264,\n        \"block\": Bottleneck,\n        \"layers\": [3, 4, 23, 3],\n        \"radix\": 2,\n        \"groups\": 1,\n        \"bottleneck_width\": 64,\n        \"deep_stem\": True,\n        \"stem_width\": 64,\n        \"avg_down\": True,\n        \"avd\" : True,\n        \"avd_first\" : False\n    },\n}","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Model loader"},{"metadata":{"trusted":true,"_kg_hide-input":false},"cell_type":"code","source":"def get_model(name):\n    if \"resnest\" in name:\n        model = ResNet(**MODEL_CONFIGS[name])\n    elif \"resnext101\" in name:\n#         model = resnext101_32x8d()\n        model = torchvision.models.resnext101_32x8d(pretrained=False)\n    elif \"resnext50\" in name:\n        model = torchvision.models.resnext50_32x4d(pretrained=False)\n    else:\n        raise NotImplementedError\n\n    nb_ft = model.fc.in_features\n    del model.fc\n    model.fc = nn.Linear(nb_ft, NUM_CLASSES)\n\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    model.to(device)\n    model.eval()\n    \n    return model","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Inference"},{"metadata":{},"cell_type":"markdown","source":"### Predict"},{"metadata":{"trusted":true},"cell_type":"code","source":"def predict(model, dataset, batch_size=16):\n    model.eval()\n    preds = np.empty((0, NUM_CLASSES))\n    \n    loader = DataLoader(\n        dataset, batch_size=batch_size, shuffle=False, num_workers=NUM_WORKERS, pin_memory=True\n    )\n\n    with torch.no_grad():\n        for x in loader:\n            y_pred = model(x.cuda()).detach()\n            preds = np.concatenate([preds, torch.sigmoid(y_pred).cpu().numpy()])\n    \n    return preds","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Post-process"},{"metadata":{"trusted":true},"cell_type":"code","source":"def post_process_site_12(preds, threshold=0.5, maxpreds=3):\n    preds = preds * (preds >= threshold)   # remove preds < threshold\n\n    next_preds = np.concatenate([preds[1:], np.zeros((1, preds.shape[-1]))])   # pred corresponding to next window\n    prev_preds = np.concatenate([np.zeros((1, preds.shape[-1])), preds[:-1]])  # pred corresponding to previous window\n    \n    score = preds + 0.5 * next_preds + 0.5 * prev_preds  # Aggregating with neighbouring predictions\n    \n    n_birds = (score >= threshold).sum(-1)   # Counting birds\n    n_birds = np.clip(n_birds, 0, maxpreds)  # keep at most maxpreds birds\n    \n    labels = [np.argsort(- score[i])[:n_birds[i]] for i in range(len(preds))]  # Getting the n_birds most likely class indices\n    \n    class_labels = [\" \".join([CLASSES[l] for l in label]) for label in labels]  # Getting class names\n    \n    return class_labels","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def post_process_site_3(preds, threshold=0.5, maxpreds=3):\n    preds = preds * (preds >= threshold)     # remove preds < threshold\n\n    score = np.sum(preds, 0)                 # Aggregating all the predictions\n    \n    n_birds = (score >= threshold).sum(-1)   # Counting birds\n    n_birds = np.clip(n_birds, 0, maxpreds)  # keep at most maxpreds birds\n    \n    label = np.argsort(- score)[:n_birds]    # Getting the n_birds most likely class indices\n    \n    class_labels = \" \".join([CLASSES[l] for l in label])  # Getting class names\n    \n    return class_labels","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def max_pred_gen(site, duration):\n    if site != \"site_3\":\n        return 3\n    else:\n        rets = [(7,2), (15, 3), (30, 5), (60, 7)]\n        \n        for ref_duration,thresh in rets:\n            if ref_duration >= duration:\n                return thresh\n        return 10","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":false},"cell_type":"code","source":"def reformat_preds(preds, df, site):\n    prediction_df = pd.DataFrame({\n        \"row_id\": df['row_id'].values,\n        \"birds\": preds\n    })\n    \n    prediction_df['birds'] = prediction_df['birds'].replace([''],'nocall')\n    \n    return prediction_df","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Inference"},{"metadata":{"_kg_hide-input":false,"trusted":true},"cell_type":"code","source":"def inference(test_df, test_audio, configs, params, threshold=0.5):\n    unique_audio_id = test_df.audio_id.unique()\n    \n    models = []\n    for config in configs:\n        models_ = []\n        for weights in config[\"weights\"]:\n            model = get_model(config['name'])\n            load_model_weights(model, weights)\n            models_.append(model)\n        models.append(models_)\n        \n    print(f'\\t -> Using {len(models)} models, with {len(models[0])} weights per model.')\n        \n    pred_dfs = []\n    for audio_id in unique_audio_id :\n        \n        audio_df = test_df[test_df['audio_id'] == audio_id].reset_index(drop=True)\n        site = audio_df[\"site\"].values[0]      \n        \n        print(f'\\nMaking predictions for audio {audio_id} in {site} ')\n\n        clip = load_audio(test_audio / (audio_id + \".mp3\"), params.sr)\n        clip_duration = len(clip) // params.sr\n        \n        dataset = TestDataset(audio_df, clip, params)\n        \n        preds = []\n        for i, config in enumerate(configs):\n            for j, weights in enumerate(config[\"weights\"]):\n                pred = predict(models[i][j], dataset, batch_size=16)\n                preds.append(pred)\n        preds = np.mean(preds, 0)\n        \n        maxpreds = max_pred_gen(site, clip_duration)\n        print(f'Limiting the number of birds to {maxpreds}')\n        \n        if site == 'site_3':\n            preds_pp = post_process_site_3(preds, threshold=threshold, maxpreds=maxpreds)\n        else:\n            preds_pp = post_process_site_12(preds, threshold=threshold, maxpreds=maxpreds)\n        \n        print(\"Predicted classes :\", preds_pp)\n        \n        pred_df = reformat_preds(preds_pp, audio_df, site)\n        pred_dfs.append(pred_df)\n    \n    sub = pd.concat(pred_dfs, axis=0, sort=False).reset_index(drop=True)\n    return sub","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Inferene with voting"},{"metadata":{"trusted":true},"cell_type":"code","source":"def vote(preds, min_votes=3):\n    votes = Counter(preds)\n    return [c for c, count in votes.items() if count >= min_votes]","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":false,"trusted":true},"cell_type":"code","source":"def inference_voting(test_df, test_audio, configs, params, threshold=0.5, min_votes=3):\n    unique_audio_id = test_df.audio_id.unique()\n    \n    models = []\n    for config in configs:\n        models_ = []\n        for weights in config[\"weights\"]:\n            model = get_model(config['name'])\n            load_model_weights(model, weights)\n            models_.append(model)\n        models.append(models_)\n        \n    print(f'\\t -> Using {len(models)} models, with {len(models[0])} weights per model.')\n        \n    pred_dfs = []\n    for audio_id in unique_audio_id :\n        \n        audio_df = test_df[test_df['audio_id'] == audio_id].reset_index(drop=True)\n        site = audio_df[\"site\"].values[0]   \n        \n        print(f'\\nMaking predictions for audio {audio_id} in {site} ')\n\n        clip = load_audio(test_audio / (audio_id + \".mp3\"), params.sr)\n        clip_duration = len(clip) // params.sr\n        \n        dataset = TestDataset(audio_df, clip, params)\n        \n        \n        all_preds = []\n        for i, config in enumerate(configs):\n            \n            preds = []\n            for j, weights in enumerate(config[\"weights\"]):\n                pred = predict(models[i][j], dataset, batch_size=16)\n                preds.append(pred)\n            preds = np.mean(preds, 0)\n\n            maxpreds = max_pred_gen(site, clip_duration)\n            print(f'Limiting the number of birds to {maxpreds}')\n\n            if site == 'site_3':\n                preds_pp = post_process_site_3(preds, threshold=threshold, maxpreds=maxpreds)\n                preds_pp = [preds_pp]\n            else:\n                preds_pp = post_process_site_12(preds, threshold=threshold, maxpreds=maxpreds)\n\n            all_preds.append(preds_pp)\n            print(\"Predicted classes :\", preds_pp)\n        \n        final_preds = []\n        for i in range(len(all_preds[0])):\n            preds = []\n            for m in range(len(all_preds)):\n                preds += all_preds[m][i].split(' ')\n                \n            final_pred = vote(preds, min_votes=min_votes)\n            final_preds.append(' '.join(final_pred))\n        \n        print(\"\\n    -> Voted classes :\", final_preds)\n        \n        pred_df = reformat_preds(final_preds, audio_df, site)\n        pred_dfs.append(pred_df)\n    \n    sub = pd.concat(pred_dfs, axis=0, sort=False).reset_index(drop=True)\n    return sub","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Prediction"},{"metadata":{},"cell_type":"markdown","source":"### Used models"},{"metadata":{"trusted":true},"cell_type":"code","source":"configs = []","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"model_name = \"resnext50_32x4d\"\nweights = [f\"../input/birds-cp-2/{model_name}_extra_{i}.pt\" for i in range(5)]\n\nfor w in weights:\n    assert os.path.isfile(w), f\"Weights {w} not found\"\n    \nconfigs.append({\n    \"name\": model_name,\n    \"weights\": weights,\n})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"model_name = \"resnext101_32x8d_wsl\"\nweights = [f\"../input/birds-cp-2/{model_name}_extra_{i}.pt\" for i in range(5)]\n\nfor w in weights:\n    assert os.path.isfile(w), f\"Weights {w} not found\"\n    \nconfigs.append({\n    \"name\": model_name,\n    \"weights\": weights,\n})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"model_name = \"resnest50_fast_1s1x64d\"\nweights = [f\"../input/birds-cp-1/{model_name}_conf_{i}.pt\" for i in range(5)]\n\nfor w in weights:\n    assert os.path.isfile(w), f\"Weights {w} not found\"\n    \nconfigs.append({\n    \"name\": model_name,\n    \"weights\": weights,\n})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"# model_name = \"resnest50_fast_1s1x64d\"\n# weights = [f\"../input/birds-cp-1/{model_name}_mixup5_{i}.pt\" for i in range(5)]\n\n# for w in weights:\n#     assert os.path.isfile(w), f\"Weights {w} not found\"\n    \n# configs.append({\n#     \"name\": model_name,\n#     \"weights\": weights,\n# })","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"configs","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Running inference"},{"metadata":{"trusted":true},"cell_type":"code","source":"threshold = 0.5\nmin_votes = 2","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"warnings.filterwarnings(\"ignore\")\n\n# submission = inference(test, TEST_AUDIO_DIR, configs, AudioParams, threshold=threshold)\nsubmission = inference_voting(test, TEST_AUDIO_DIR, configs, AudioParams, threshold=threshold, min_votes=min_votes)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Submission"},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"submission.to_csv(\"submission.csv\", index=False)\n# submission","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":4}