{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import time\nstart = time.time()\n\nimport os\nimport math\nimport glob\nimport random\nimport numpy as np \nimport pandas as pd \nfrom collections import Counter, deque\nimport matplotlib.pyplot as plt\n\nimport warnings\nwarnings.simplefilter(action='ignore')\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nimport torchaudio\n\ndef timeSince(since):\n    now = time.time()\n    s = now - since\n    m = math.floor(s / 60)\n    s -= m * 60\n    return '%dm %2ds' % (m, s)\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(device)\n\ndef audio_padding(waveform, sample_rate, clip_seconds = 5):\n    audio_length = waveform.size(1)\n    clip_length = sample_rate * clip_seconds\n    \n    max_offset = clip_length - audio_length\n    offset = np.random.randint(max_offset)\n    clip = F.pad(waveform, (offset, clip_length - audio_length - offset), \"constant\")\n    return clip\n\ndef clip_cut(waveform, sample_rate = 44100, clip_seconds = 5):\n    ## cut audio into 5 seconds clips \n    audio_length = waveform.size(1)\n    clip_length = sample_rate * clip_seconds\n    num_clips = audio_length//clip_length\n    \n    if num_clips == 0:\n        clips = [audio_padding(waveform, sample_rate, clip_seconds)]\n    else:\n        onset = 0\n        clips = []\n        for i in range(1, num_clips + 1):\n            offset = i * clip_length\n            clips.append(waveform[:,onset:offset])\n            onset = offset\n        \n        #final_clip = audio_padding(waveform[:,onset:], sample_rate, clip_seconds)\n        #clips.append(final_clip)\n            \n    return torch.stack(clips)\n\nsample_rate = 44100","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"BASE_TEST_DIR = '../input/birdsong-recognition' if os.path.exists('../input/birdsong-recognition/test_audio') else '../input/birdcall-check'\nTEST_FOLDER = f'{BASE_TEST_DIR}/test_audio'\nprint(TEST_FOLDER)\n\ntest_df = pd.read_csv(f'{BASE_TEST_DIR}/test.csv')\nfile_df = test_df[['site','audio_id']].drop_duplicates()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## class dict\nclasses = ['aldfly', 'ameavo', 'amebit', 'amecro', 'amegfi', 'amekes',\n       'amepip', 'amered', 'amerob', 'amewig', 'amewoo', 'amtspa',\n       'annhum', 'astfly', 'baisan', 'baleag', 'balori', 'banswa',\n       'barswa', 'bawwar', 'belkin1', 'belspa2', 'bewwre', 'bkbcuc',\n       'bkbmag1', 'bkbwar', 'bkcchi', 'bkchum', 'bkhgro', 'bkpwar',\n       'bktspa', 'blkpho', 'blugrb1', 'blujay', 'bnhcow', 'boboli',\n       'bongul', 'brdowl', 'brebla', 'brespa', 'brncre', 'brnthr',\n       'brthum', 'brwhaw', 'btbwar', 'btnwar', 'btywar', 'buffle',\n       'buggna', 'buhvir', 'bulori', 'bushti', 'buwtea', 'buwwar',\n       'cacwre', 'calgul', 'calqua', 'camwar', 'cangoo', 'canwar',\n       'canwre', 'carwre', 'casfin', 'caster1', 'casvir', 'cedwax',\n       'chispa', 'chiswi', 'chswar', 'chukar', 'clanut', 'cliswa',\n       'comgol', 'comgra', 'comloo', 'commer', 'comnig', 'comrav',\n       'comred', 'comter', 'comyel', 'coohaw', 'coshum', 'cowscj1',\n       'daejun', 'doccor', 'dowwoo', 'dusfly', 'eargre', 'easblu',\n       'easkin', 'easmea', 'easpho', 'eastow', 'eawpew', 'eucdov',\n       'eursta', 'evegro', 'fiespa', 'fiscro', 'foxspa', 'gadwal',\n       'gcrfin', 'gnttow', 'gnwtea', 'gockin', 'gocspa', 'goleag',\n       'grbher3', 'grcfly', 'greegr', 'greroa', 'greyel', 'grhowl',\n       'grnher', 'grtgra', 'grycat', 'gryfly', 'haiwoo', 'hamfly',\n       'hergul', 'herthr', 'hoomer', 'hoowar', 'horgre', 'horlar',\n       'houfin', 'houspa', 'houwre', 'indbun', 'juntit1', 'killde',\n       'labwoo', 'larspa', 'lazbun', 'leabit', 'leafly', 'leasan',\n       'lecthr', 'lesgol', 'lesnig', 'lesyel', 'lewwoo', 'linspa',\n       'lobcur', 'lobdow', 'logshr', 'lotduc', 'louwat', 'macwar',\n       'magwar', 'mallar3', 'marwre', 'merlin', 'moublu', 'mouchi',\n       'moudov', 'norcar', 'norfli', 'norhar2', 'normoc', 'norpar',\n       'norpin', 'norsho', 'norwat', 'nrwswa', 'nutwoo', 'olsfly',\n       'orcwar', 'osprey', 'ovenbi1', 'palwar', 'pasfly', 'pecsan',\n       'perfal', 'phaino', 'pibgre', 'pilwoo', 'pingro', 'pinjay',\n       'pinsis', 'pinwar', 'plsvir', 'prawar', 'purfin', 'pygnut',\n       'rebmer', 'rebnut', 'rebsap', 'rebwoo', 'redcro', 'redhea',\n       'reevir1', 'renpha', 'reshaw', 'rethaw', 'rewbla', 'ribgul',\n       'rinduc', 'robgro', 'rocpig', 'rocwre', 'rthhum', 'ruckin',\n       'rudduc', 'rufgro', 'rufhum', 'rusbla', 'sagspa1', 'sagthr',\n       'savspa', 'saypho', 'scatan', 'scoori', 'semplo', 'semsan',\n       'sheowl', 'shshaw', 'snobun', 'snogoo', 'solsan', 'sonspa', 'sora',\n       'sposan', 'spotow', 'stejay', 'swahaw', 'swaspa', 'swathr',\n       'treswa', 'truswa', 'tuftit', 'tunswa', 'veery', 'vesspa',\n       'vigswa', 'warvir', 'wesblu', 'wesgre', 'weskin', 'wesmea',\n       'wessan', 'westan', 'wewpew', 'whbnut', 'whcspa', 'whfibi',\n       'whtspa', 'whtswi', 'wilfly', 'wilsni1', 'wiltur', 'winwre3',\n       'wlswar', 'wooduc', 'wooscj2', 'woothr', 'y00475', 'yebfly',\n       'yebsap', 'yehbla', 'yelwar', 'yerwar', 'yetvir']\n\nlabel2class = { l:c for l, c in enumerate(classes)}\nclass2label = { c:l for l, c in enumerate(classes)}\n\nlen(classes)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def init_layer(layer):\n    nn.init.xavier_uniform_(layer.weight)\n\n    if hasattr(layer, \"bias\"):\n        if layer.bias is not None:\n            layer.bias.data.fill_(0.)\n\ndef init_bn(bn):\n    bn.bias.data.fill_(0.)\n    bn.weight.data.fill_(1.0)\n\nclass ConvBlock(nn.Module):\n    def __init__(self, in_channels: int, out_channels: int):\n        super().__init__()\n\n        self.conv1 = nn.Conv2d(in_channels, out_channels, \n                               kernel_size=(3, 3),\n                               stride=(1, 1),\n                               padding=(1, 1),\n                               bias=False)\n\n        self.conv2 = nn.Conv2d(out_channels, out_channels,\n                               kernel_size=(3, 3),\n                               stride=(1, 1),\n                               padding=(1, 1),\n                               bias=False)\n\n        self.bn1 = nn.BatchNorm2d(out_channels)\n        self.bn2 = nn.BatchNorm2d(out_channels)\n\n        self.init_weight()\n\n    def init_weight(self):\n        init_layer(self.conv1)\n        init_layer(self.conv2)\n        init_bn(self.bn1)\n        init_bn(self.bn2)\n\n    def forward(self, input, pool_size=(2, 2), pool_type='avg'):\n        x = input\n        x = F.relu_(self.bn1(self.conv1(x)))\n        x = F.relu_(self.bn2(self.conv2(x)))\n        if pool_type == 'max':\n            x = F.max_pool2d(x, kernel_size=pool_size)\n        elif pool_type == 'avg':\n            x = F.avg_pool2d(x, kernel_size=pool_size)\n        elif pool_type == 'avg+max':\n            x1 = F.avg_pool2d(x, kernel_size=pool_size)\n            x2 = F.max_pool2d(x, kernel_size=pool_size)\n            x = x1 + x2\n        else:\n            raise Exception('Incorrect argument!')\n\n        return x\n\n\nclass AttBlock(nn.Module):\n    def __init__(self, in_channels: int, out_channels: int):\n        super().__init__()\n\n        self.att = nn.Conv1d(in_channels, out_channels, kernel_size=1)\n        self.cla = nn.Conv1d(in_channels, out_channels, kernel_size=1)\n        self.init_weights()\n\n    def init_weights(self):\n        init_layer(self.att)\n        init_layer(self.cla)\n\n    def forward(self, x):\n        # x: (batch, features, time)\n        \n        # attention-based pooling weights\n        pooling_weights = torch.softmax(torch.clamp(self.att(x), -10, 10), dim = -1) \n        \n        # frame level prediction\n        framewise = torch.sigmoid(self.cla(x)) \n        \n        # clip level prediction\n        clipwise = torch.sum(pooling_weights * framewise, dim = -1) # (batch, feature)\n        \n        return framewise, clipwise","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class TALNet(nn.Module):\n    def __init__(self, classes_num = 264):\n        super().__init__()\n\n        self.preprocess = nn.Sequential(\n            torchaudio.transforms.MelSpectrogram(sample_rate = 44100, win_length = 1024, hop_length = 320, \n                                                 n_fft=2048, f_min=50, f_max=14000, n_mels=64),\n            torchaudio.transforms.AmplitudeToDB(top_db = 80)\n        )\n        \n        self.bn0 = nn.BatchNorm2d(64)\n        \n        self.conv_block1 = ConvBlock(in_channels=1, out_channels=32)\n        self.conv_block2 = ConvBlock(in_channels=32, out_channels=64)\n        self.conv_block3 = ConvBlock(in_channels=64, out_channels=128)\n        self.conv_block4 = ConvBlock(in_channels=128, out_channels=256)\n        self.conv_block5 = ConvBlock(in_channels=256, out_channels=512)\n        \n        self.biGRU = nn.GRU(1024, 512, num_layers = 2, batch_first = True, dropout = 0.2, bidirectional = True)\n        \n        self.att_block = AttBlock(1024, classes_num)\n        \n    def cnn_feature_extractor(self, x):\n        x = self.conv_block1(x, pool_size=(2, 2), pool_type='max')\n        x = F.dropout(x, p=0.2, training=self.training)\n        x = self.conv_block2(x, pool_size=(2, 2), pool_type='max')\n        x = F.dropout(x, p=0.2, training=self.training)\n        x = self.conv_block3(x, pool_size=(2, 2), pool_type='max')\n        x = F.dropout(x, p=0.2, training=self.training)\n        x = self.conv_block4(x, pool_size=(2, 1), pool_type='max')\n        x = F.dropout(x, p=0.2, training=self.training)\n        x = self.conv_block5(x, pool_size=(2, 1), pool_type='max')\n        x = F.dropout(x, p=0.2, training=self.training)\n        return x\n\n\n    def forward(self, input):\n        \"\"\"\n        Input: (batch_size, data_length)\"\"\"\n        x= self.preprocess(input) # (batch, 1, freq, time)\n        #frames_num = x.size(-1)\n        \n        x = x.transpose(1, 2)\n        x = self.bn0(x)\n        x = x.transpose(1, 2)\n        \n        x = self.cnn_feature_extractor(x)\n        # Flatten in channel and frequency axis\n        x = torch.flatten(x, start_dim = 1, end_dim = 2) # (batch, feature, time)\n        \n        x = F.dropout(x, p=0.5, training=self.training)\n        x = x.transpose(1, 2) # (batch, time, feature)\n        x, hn = self.biGRU(x)\n        x = x.transpose(1, 2) # (batch, feature, time)\n        x = F.dropout(x, p=0.5, training=self.training)\n        \n        (framewise, clipwise) = self.att_block(x)\n\n        return framewise, clipwise\n    \nmodel = TALNet().to(device)\nmodel.load_state_dict(torch.load('/kaggle/input/birdcall-identification-talnet-training3/birdcall_TALNet_model.pth'))\nmodel.eval()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def make_prediction(clips):\n    birds = []\n    for idx, clip in enumerate(clips):\n        input_tensor = clip.unsqueeze(0)\n        _,output = model(input_tensor.to(device))\n        \n        pred = torch.where(output>=0.6, output, torch.zeros_like(output)).cpu()\n        pred_label = torch.nonzero(pred.squeeze()).numpy().ravel()\n        \n        pred_class = [label2class[l] for l in pred_label] if len(pred_label)> 0 else ['nocall']\n        \n        #pred_class = label2class[pred.argmax().item()] if len(pred_label)> 0 else 'nocall'\n        \n        birds.append(pred_class)\n        \n    return birds","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"row_id, birds = [], []\n    \nfor i, row in file_df.iterrows():\n\n    audio_path = f'{TEST_FOLDER}/{row.audio_id}.mp3'\n    waveform, orig_freq = torchaudio.load(audio_path)\n    \n    if orig_freq != sample_rate:\n        waveform = torchaudio.transforms.Resample(orig_freq, sample_rate)(waveform)\n\n    waveform = waveform.mean(0).unsqueeze(0) if waveform.size(0) == 2 else waveform\n    clips = clip_cut(waveform)\n    preds = make_prediction(clips)\n\n    if row.site in ['site_1','site_2']:\n        for s, pred in enumerate(preds):\n            birds.append(' '.join(pred))\n            row_id.append(f'{row.site}_{row.audio_id}_{str((s+1)*5)}')\n    else:\n        flat_preds = [p for pred in preds for p in pred if p != 'nocall']\n        #bird = 'nocall' if len(flat_preds) == 0 else Counter(flat_preds).most_common(1)[0][0]\n        bird = 'nocall' if len(flat_preds) == 0 else ' '.join(set(flat_preds))\n        birds.append(bird)\n        row_id.append(f'{row.site}_{row.audio_id}')\n        \nsub_df = pd.DataFrame(data={'row_id': row_id, 'birds': birds})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub_df = test_df.merge(sub_df, on='row_id', how = 'left')\nsub_df[sub_df.birds.isna()]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub_df[['row_id','birds']]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub_df[['row_id','birds']].to_csv(\"submission.csv\", index=False)","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}