{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"from pathlib import Path\nimport os\nimport random\n\nimport numpy as np\nimport librosa\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nimport torchaudio\n\nfrom torch.utils.data import DataLoader, TensorDataset\n\nimport pandas as pd\n\nseed = 42\n\nnp.random.seed(seed)\nrandom.seed(seed)\nos.environ['PYTHONHASHSEED'] = str(seed)\n\nBASE_DIR = Path('../input')\n\nDATA_DIR = Path('../input/birdsong-recognition') if os.path.exists('../input/birdsong-recognition/test_audio') else Path('../input/birdcall-check')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class ConvBlock(nn.Module):\n    def __init__(self, in_channels, out_channels):\n        super().__init__()\n        \n        self.conv1 = nn.Sequential(\n            nn.Conv2d(in_channels, out_channels, 3, 1, 1),\n            nn.BatchNorm2d(out_channels),\n            # nn.Dropout2d(0.1),\n            nn.ReLU(),\n        )\n        \n        self.conv2 = nn.Sequential(\n            nn.Conv2d(out_channels, out_channels, 3, 1, 1),\n            nn.BatchNorm2d(out_channels),\n            # nn.Dropout2d(0.1),\n            nn.ReLU(),\n        )\n        \n        self._init_weights()\n        \n    def _init_weights(self):\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                nn.init.kaiming_normal_(m.weight)\n                if m.bias is not None:\n                    nn.init.zeros_(m.bias)\n            elif isinstance(m, nn.BatchNorm2d):\n                nn.init.constant_(m.weight, 1)\n                nn.init.zeros_(m.bias)\n    \n    def forward(self, x):\n        x = self.conv1(x)\n        x = self.conv2(x)\n        x = F.avg_pool2d(x, 2)\n        return x\n\n\nclass CNN(nn.Module):\n    def __init__(self, num_classes, sample_rate):\n        super().__init__()\n        \n        n_fft = 2048\n        hop_len = 256\n        f_min = 20\n        f_max = sample_rate / 2\n        \n        self.preprocess = nn.Sequential(\n            torchaudio.transforms.MelSpectrogram(sample_rate=sample_rate, n_fft=n_fft, hop_length=hop_len, f_min=f_min, f_max=f_max),\n            torchaudio.transforms.AmplitudeToDB(stype='magnitude'),\n        )\n\n        self.conv = nn.Sequential(\n            ConvBlock(in_channels=1, out_channels=16),\n            ConvBlock(in_channels=16, out_channels=32),\n            ConvBlock(in_channels=32, out_channels=64),\n            ConvBlock(in_channels=64, out_channels=128),\n            ConvBlock(in_channels=128, out_channels=256),\n        )\n\n        self.fc = nn.Sequential(\n            nn.Dropout(0.4),\n            nn.Linear(256, 64),\n            nn.PReLU(),\n            nn.BatchNorm1d(64),\n            nn.Dropout(0.4),\n            nn.Linear(64, num_classes),\n        )\n    \n    def forward(self, x):\n        x = self.preprocess(x)\n        x = torch.unsqueeze(x, 1)\n        x = self.conv(x)\n        x = torch.mean(x, dim=3)\n        x, _ = torch.max(x, dim=2)\n        x = self.fc(x)\n        return x","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class SNRSegmenter(object):\n\n    def __init__(self, sample_rate, segment_len_ms, hop_len_ms, noise_len_ms, min_snr):\n        self.segment_len_samples = int(sample_rate * segment_len_ms / 1000)\n        self.hop_len_samples = int(sample_rate * hop_len_ms / 1000)\n        self.noise_len_samples = int(sample_rate * noise_len_ms / 1000)\n\n        self.min_snr = min_snr\n\n    def get_noise_level(self, sample):\n        abs_max = []\n        \n        if len(sample) > self.noise_len_samples:\n            idx = 0\n            while idx + self.noise_len_samples < len(sample):\n                abs_max.append(torch.max(torch.abs(sample[idx:(idx+self.noise_len_samples)])))\n                idx += self.noise_len_samples\n        else:\n            abs_max.append(torch.max(torch.abs(sample)))\n\n        return min(abs_max)\n\n    def __call__(self, sample, noise_level):\n\n        call_segments = []\n\n        if len(sample) > self.segment_len_samples:\n            idx = 0\n            while idx + self.segment_len_samples < len(sample):\n                segment = sample[idx:(idx+self.segment_len_samples)].clone()\n                seg_abs_max = torch.max(torch.abs(segment))\n                if seg_abs_max / noise_level > self.min_snr:\n                    segment -= torch.mean(segment)\n                    segment /= seg_abs_max\n                    call_segments.append(segment)\n\n                idx += self.hop_len_samples\n        else:\n            seg_abs_max = torch.max(torch.abs(sample))\n            if seg_abs_max / noise_level > self.min_snr:\n                segment = torch.randn(self.segment_len_samples) * (0.01 * seg_abs_max)\n                segment[:len(sample)] = sample\n                segment -= torch.mean(segment)\n                segment /= seg_abs_max\n                call_segments.append(segment)\n\n        return call_segments","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_audio(fpath, sample_rate):\n    try:\n        audio, orig_sr = torchaudio.load(fpath)\n        audio = audio[0, :]\n        if orig_sr != sample_rate:\n            audio = torchaudio.transforms.Resample(orig_freq=orig_sr, new_freq=sample_rate)(audio)\n    except:\n        audio, _ = librosa.core.load(fpath, sr=sample_rate, mono=True)\n        audio = torch.from_numpy(audio)\n    return audio\n\n# def get_all_segments(audio, sample_rate, segment_len_ms, hop_len_ms):\n    \n#     segment_len_samples = int(sample_rate * segment_len_ms / 1000)\n#     hop_len_samples = int(sample_rate * hop_len_ms / 1000)\n\n#     segments = []\n#     idx = 0\n\n#     while idx + segment_len_samples < len(audio):\n#         segment = audio[int(idx):int(idx+segment_len_samples)].clone()\n        \n#         segment -= torch.mean(segment)\n#         segment /= torch.max(torch.abs(segment))\n        \n#         segments.append(segment)\n        \n#         idx += hop_len_samples\n\n#     return segments\n\ndef run_inference(model, data, batch_size):\n    test_ds = TensorDataset(data)\n    test_dl = DataLoader(test_ds, batch_size=batch_size, shuffle=False)\n\n    nn_out = []\n    for x_batch in test_dl:\n        nn_out_batch = model(x_batch[0].cuda()).detach().cpu()\n        nn_out.append(nn_out_batch)\n\n    return torch.cat(nn_out, 0)\n\ndef get_preds(test_data, test_df, model, num2label, label2num, sample_rate, segment_len_ms, hop_len_ms, noise_len_ms, min_snr):\n\n    segmenter = SNRSegmenter(sample_rate, segment_len_ms, hop_len_ms, noise_len_ms, min_snr)\n    # segment_len_samples = int(sample_rate * segment_len_ms / 1000)\n    # hop_len_samples = int(sample_rate * hop_len_ms / 1000)\n\n    model.cuda()\n    model.eval()\n\n    preds = []\n\n    unique_audio_ids = test_df.audio_id.unique()\n\n    for audio_id in unique_audio_ids:\n\n        fpath = test_data / (audio_id + '.mp3')\n\n        audio = get_audio(fpath, sample_rate)\n        \n        noise_level = segmenter.get_noise_level(audio)\n        \n        if noise_level < 1e-2:\n            noise_level = 1e-2\n\n        for index, row in test_df.iterrows():\n            if row['audio_id'] == audio_id:\n                if row['site'] in ('site_1', 'site_2'):\n\n                    start_idx = int((row['seconds'] - 5) * sample_rate)\n                    end_idx = int(row['seconds'] * sample_rate)\n                    \n                    sample_audio = audio[start_idx:end_idx].clone()\n                else:\n                    sample_audio = audio.clone()\n\n                segments = segmenter(sample_audio, noise_level)\n                # segments = get_all_segments(sample_audio, sample_rate, segment_len_ms, hop_len_ms)\n                # segments = torch.stack([sample_audio[i:i+segment_len_samples] for i in range(0, sample_audio.size(0) - segment_len_samples + 1, hop_len_samples)])\n\n                if segments:\n                    segments = torch.stack(segments)\n                    nn_out = run_inference(model, segments, batch_size=1024)\n                    probabilities = F.softmax(nn_out, dim=1).numpy()\n\n                    all_idxs = list(np.argwhere(probabilities > 0.5)[:, 1])\n                    idx_set = list(set(all_idxs))\n\n                    # if len(idx_set) > 1 and label2num['nocall'] in idx_set:\n                    #    idx_set.remove(label2num['nocall'])\n\n                    if row['site'] in ('site_1', 'site_2'):\n                        pred_idxs = [i for i in idx_set if all_idxs.count(i) > 1]\n                    else:\n                        pred_idxs = [i for i in idx_set if all_idxs.count(i) > 1]\n\n                    if len(pred_idxs) > 0:\n                        birds = [num2label[idx] for idx in pred_idxs]\n                        preds.append([row['row_id'], ' '.join(birds)])\n                    else:\n                        preds.append([row['row_id'], 'nocall'])\n                else:\n                    preds.append([row['row_id'], 'nocall'])\n\n    return preds","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df = pd.read_csv(BASE_DIR / 'birdsong-recognition' / 'train.csv')\nall_birds = list(train_df['ebird_code'].unique())\n# all_birds.append('nocall')\nall_birds = sorted(all_birds)\n\nnum2label = {idx: label for idx, label in enumerate(all_birds)}\nlabel2num = {label: idx for idx, label in enumerate(all_birds)}\n\ntest_df = pd.read_csv(DATA_DIR / 'test.csv')\ntest_data = DATA_DIR / 'test_audio'\n\nsample_rate = 22050\nsegment_len_ms = 2500\nhop_len_ms = 500\nnoise_len_ms = 500\nmin_snr = 5.\n\nmodel = CNN(num_classes=len(all_birds), sample_rate=sample_rate)\nmodel.load_state_dict(torch.load('../input/bciweights200908/cnn_weights_1.pt'))\n\npreds = get_preds(test_data, test_df, model, num2label, label2num, sample_rate, segment_len_ms, hop_len_ms, noise_len_ms, min_snr)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"preds = pd.DataFrame(preds, columns=['row_id', 'birds'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"preds.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}