{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":70203,"databundleVersionId":8068726,"sourceType":"competition"},{"sourceId":60007,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":39842}],"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"from collections import deque\nimport concurrent\nimport math\nimport os\nimport torch\nfrom torch import nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader\nimport torchaudio\nimport numpy as np\nimport pandas as pd\nfrom scipy.signal import butter, filtfilt","metadata":{"execution":{"iopub.status.busy":"2024-06-02T03:55:05.901272Z","iopub.execute_input":"2024-06-02T03:55:05.901795Z","iopub.status.idle":"2024-06-02T03:55:09.118911Z","shell.execute_reply.started":"2024-06-02T03:55:05.901750Z","shell.execute_reply":"2024-06-02T03:55:09.117513Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_audio_dir = '../input/birdclef-2024/test_soundscapes/'\n\nfile_list = [f for f in sorted(os.listdir(test_audio_dir))]\nfile_list = [file.split('.')[0] for file in file_list if file.endswith('.ogg')]\n\nprint('Number of test soundscapes:', len(file_list))","metadata":{"execution":{"iopub.status.busy":"2024-06-02T03:55:09.121092Z","iopub.execute_input":"2024-06-02T03:55:09.121595Z","iopub.status.idle":"2024-06-02T03:55:09.130959Z","shell.execute_reply.started":"2024-06-02T03:55:09.121563Z","shell.execute_reply":"2024-06-02T03:55:09.129570Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# The model","metadata":{}},{"cell_type":"code","source":"class SedCrnnBirdSoundRecognition(nn.Module):\n    '''A model to detect bird species presence in audio track'''\n\n    def __init__(self):\n        super(SedCrnnBirdSoundRecognition, self).__init__()\n        self.conv1 = nn.Sequential(\n            nn.Conv2d(\n                in_channels=1,\n                out_channels=64,\n                kernel_size=3,\n                stride=1,\n                padding=2,\n            ),\n            nn.BatchNorm2d(64),\n            nn.LeakyReLU(),\n            nn.Dropout2d(p=0.2),\n            nn.MaxPool2d(\n                kernel_size=(8, 1),\n            ),\n        )\n        self.conv2 = nn.Sequential(\n            nn.Conv2d(\n                in_channels=64,\n                out_channels=64,\n                kernel_size=3,\n                stride=1,\n                padding=2,\n            ),\n            nn.BatchNorm2d(64),\n            nn.LeakyReLU(),\n            nn.Dropout2d(p=0.2),\n            nn.MaxPool2d(\n                kernel_size=(2, 1),\n            ),\n        )\n        self.conv3 = nn.Sequential(\n            nn.Conv2d(\n                in_channels=64,\n                out_channels=64,\n                kernel_size=3,\n                stride=1,\n                padding=2,\n            ),\n            nn.BatchNorm2d(64),\n            nn.LeakyReLU(),\n            nn.Dropout2d(p=0.2),\n            nn.MaxPool2d(\n                kernel_size=(2, 1),\n            ),\n        )\n        self.flatten_conv = nn.Flatten(1, 2)\n        self.gru = nn.GRU(320, 32, 2, dropout=0.2, batch_first=True, bidirectional=True)\n        self.flatten_gru = nn.Flatten()\n        self.dropout = nn.Dropout(0.4)\n        self.fc = nn.Linear(128, 182)\n\n    def forward(self, value):\n        '''One step of computation'''\n        output = self.conv1(value)\n        output = self.conv2(output)\n        output = self.conv3(output)\n        output = self.flatten_conv(output)\n        output = torch.permute(output, (0, 2, 1))\n        output, state = self.gru(output)\n        state = torch.permute(state, (1, 0, 2))\n        state = self.flatten_gru(state)\n        state = self.dropout(state)\n        result = self.fc(state)\n        return result","metadata":{"execution":{"iopub.status.busy":"2024-06-02T03:55:09.133197Z","iopub.execute_input":"2024-06-02T03:55:09.134024Z","iopub.status.idle":"2024-06-02T03:55:09.151741Z","shell.execute_reply.started":"2024-06-02T03:55:09.133983Z","shell.execute_reply":"2024-06-02T03:55:09.149692Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Loading the model","metadata":{}},{"cell_type":"code","source":"def load_model(model_class, model_path):\n    checkpoint = torch.load(model_path, map_location=torch.device('cpu'))\n    model = model_class()\n    model.load_state_dict(checkpoint['state_dict'])\n    model = torch.jit.optimize_for_inference(torch.jit.script(model.eval()))\n    return model","metadata":{"execution":{"iopub.status.busy":"2024-06-02T03:55:09.153658Z","iopub.execute_input":"2024-06-02T03:55:09.154091Z","iopub.status.idle":"2024-06-02T03:55:09.169012Z","shell.execute_reply.started":"2024-06-02T03:55:09.154056Z","shell.execute_reply":"2024-06-02T03:55:09.167537Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Loading the data","metadata":{}},{"cell_type":"code","source":"def get_labels():\n    metadata = pd.read_csv(\n        '../input/birdclef-2024/train_metadata.csv',\n        usecols=['primary_label']\n    )\n    return sorted(metadata.primary_label.unique().tolist())","metadata":{"execution":{"iopub.status.busy":"2024-06-02T03:55:09.174175Z","iopub.execute_input":"2024-06-02T03:55:09.175894Z","iopub.status.idle":"2024-06-02T03:55:09.182225Z","shell.execute_reply.started":"2024-06-02T03:55:09.175823Z","shell.execute_reply":"2024-06-02T03:55:09.181049Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def normalize(spectrogram):\n    return (spectrogram - torch.min(spectrogram)) / (torch.max(spectrogram) - torch.min(spectrogram))\n\ndef butter_lowpass_filter(data, cutoff_freq, sample_rate, order=4):\n    nyquist = 0.5 * sample_rate\n    normal_cutoff = cutoff_freq / nyquist\n    b, a = butter(order, normal_cutoff, btype='low', analog=False)\n    filtered_data = filtfilt(b, a, data[0].numpy())\n    return torch.unsqueeze(torch.from_numpy(filtered_data.copy()), 0).float()\n\nclass SingleFileDataset(torch.utils.data.Dataset):\n    N_FFT = 1024\n    N_MELS = 128\n    SAMPLING_RATE = 32000\n    CLIP_LENGTH_S = 5\n    CLIP_LENGTH = CLIP_LENGTH_S * SAMPLING_RATE\n\n    def __init__(self, file):\n        self.filename_no_ext = file.split(\"/\")[-1].split(\".\")[0]\n        self.spectrogram_transform = torchaudio.transforms.MelSpectrogram(\n            sample_rate=self.SAMPLING_RATE,\n            n_fft=self.N_FFT,\n            n_mels=self.N_MELS,\n        )\n        self.db_transform = torchaudio.transforms.AmplitudeToDB(stype='power')\n        waveform, _ = torchaudio.load(file)\n        self.waveform = waveform\n\n    def __len__(self):\n        return self.waveform.shape[1] // self.CLIP_LENGTH\n\n    def __getitem__(self, index):\n        w = self.waveform[:,index*self.CLIP_LENGTH:(index+1)*self.CLIP_LENGTH]\n        spectrogram = self.spectrogram_transform(w)\n        spectrogram = self.db_transform(spectrogram)\n        spectrogram = normalize(spectrogram)\n        clip_id = f'{self.filename_no_ext}_{(index+1)*5}'\n        return spectrogram, clip_id","metadata":{"execution":{"iopub.status.busy":"2024-06-02T03:55:09.183542Z","iopub.execute_input":"2024-06-02T03:55:09.184472Z","iopub.status.idle":"2024-06-02T03:55:09.199146Z","shell.execute_reply.started":"2024-06-02T03:55:09.184435Z","shell.execute_reply":"2024-06-02T03:55:09.197653Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Predicting","metadata":{}},{"cell_type":"code","source":"labels = get_labels()\n\nmodel = load_model(\n    SedCrnnBirdSoundRecognition,\n    '../input/sedcrnnbirdsrecognition_182-species/pytorch/v1/9/SedCrnnBirdSoundRecognition-135epoches_focalbce.pt',\n)","metadata":{"execution":{"iopub.status.busy":"2024-06-02T03:55:09.200711Z","iopub.execute_input":"2024-06-02T03:55:09.201188Z","iopub.status.idle":"2024-06-02T03:55:09.798525Z","shell.execute_reply.started":"2024-06-02T03:55:09.201153Z","shell.execute_reply":"2024-06-02T03:55:09.797441Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def predict_for_file(filepath):\n    result = {\n        'clips': [],\n        'predictions': [],\n    }\n    dataset = SingleFileDataset(filepath)\n    predict_loader = DataLoader(\n        dataset,\n        batch_size=4,\n        shuffle=False,\n        drop_last=False,\n        pin_memory=True,\n        num_workers=os.cpu_count(),\n    )\n    \n    for x, clip_ids in predict_loader:\n        predictions = F.sigmoid(model(x))\n        result['clips'].append(clip_ids)\n        result['predictions'].append(predictions)\n\n    return result","metadata":{"execution":{"iopub.status.busy":"2024-06-02T03:55:09.800069Z","iopub.execute_input":"2024-06-02T03:55:09.800420Z","iopub.status.idle":"2024-06-02T03:55:09.808371Z","shell.execute_reply.started":"2024-06-02T03:55:09.800389Z","shell.execute_reply":"2024-06-02T03:55:09.807217Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"files = []\nfor dirname, _, filenames in os.walk('../input/birdclef-2024/test_soundscapes'):\n# counter = 0\n# for dirname, _, filenames in os.walk('../input/birdclef-2024/unlabeled_soundscapes'):\n    for filename in filenames:\n        if filename == 'readme.txt':\n            continue\n        filepath = os.path.join(dirname, filename)\n        files.append(filepath)\n#         counter += 1\n#         if counter >= 8:\n#             break\n\nwith concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:\n    dicts = list(executor.map(predict_for_file, files))\n\nprint(\"TEST: number of files\", len(dicts))\nresult = None\nfor d in dicts:\n    for n in range(len(d['clips'])):\n        df_pred = pd.DataFrame(d['predictions'][n].detach().numpy(), columns=labels)\n        df_id = pd.DataFrame({'row_id': d['clips'][n]})\n        df = pd.concat([df_id, df_pred], axis=1)\n        if result is None:\n            result = df\n        else:\n            result = pd.concat([result, df])\n\nif result is None:\n    result = pd.DataFrame([], columns=['row_id'] + labels)\n\nprint(\"TEST: number of result rows\", result.size / 183)\nresult.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2024-06-02T03:55:09.810002Z","iopub.execute_input":"2024-06-02T03:55:09.810407Z","iopub.status.idle":"2024-06-02T03:55:09.832276Z","shell.execute_reply.started":"2024-06-02T03:55:09.810373Z","shell.execute_reply":"2024-06-02T03:55:09.831219Z"},"trusted":true},"execution_count":null,"outputs":[]}]}