{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport json\nimport pandas as pd\nimport numpy as np\nfrom matplotlib import pyplot as plt\nimport torch\nfrom torch import optim\nfrom torch import nn\nfrom torch.utils.data import DataLoader\nimport torchaudio\nfrom torchaudio import transforms\nfrom torchvision.models.resnet import ResNet, BasicBlock\n\ndevice = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nprint(f\"Using {device}\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-05-05T10:56:04.653424Z","iopub.execute_input":"2022-05-05T10:56:04.653778Z","iopub.status.idle":"2022-05-05T10:56:06.264969Z","shell.execute_reply.started":"2022-05-05T10:56:04.653697Z","shell.execute_reply":"2022-05-05T10:56:06.264163Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"out_path = \"./train/\"\ntry:\n    os.mkdir(out_path)\nexcept FileExistsError:\n    pass\n\nwith open(\"../input/birdclef-2022/scored_birds.json\") as f:\n    scored_birds = json.load(f)\n\ntest_df = pd.read_csv(\"../input/birdclef-2022/test.csv\")\ntrain_metadata_df = pd.read_csv(\"../input/birdclef-2022/train_metadata.csv\")","metadata":{"execution":{"iopub.status.busy":"2022-05-05T10:56:08.126492Z","iopub.execute_input":"2022-05-05T10:56:08.127123Z","iopub.status.idle":"2022-05-05T10:56:08.331425Z","shell.execute_reply.started":"2022-05-05T10:56:08.127082Z","shell.execute_reply":"2022-05-05T10:56:08.330664Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"filter_df = train_metadata_df[train_metadata_df[\"primary_label\"].isin(scored_birds) | train_metadata_df[\"secondary_labels\"].apply(lambda x: set(eval(x)).intersection(set(scored_birds))).apply(bool)]\nbird_label = train_metadata_df[\"primary_label\"].unique()\nprint(bird_label)\n\ntrain_rest_df = train_metadata_df[~train_metadata_df.index.isin(filter_df.index.to_list())]\ntrain_metadata_df = filter_df.append(train_rest_df.sample(len(train_rest_df) // 5))","metadata":{"execution":{"iopub.status.busy":"2022-05-05T10:56:11.557985Z","iopub.execute_input":"2022-05-05T10:56:11.558258Z","iopub.status.idle":"2022-05-05T10:56:11.59236Z","shell.execute_reply.started":"2022-05-05T10:56:11.558219Z","shell.execute_reply":"2022-05-05T10:56:11.591651Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_rate = 32000\nn_fft = 1024\nhop_length = 512\nn_mels = 256\nmin_sec_proc = sample_rate * 5\nf_min = 250\n\nmel_spectrogram = transforms.MelSpectrogram(\n    sample_rate = sample_rate,\n    n_fft = n_fft,\n    hop_length = hop_length,\n    center = True,\n    f_min = f_min,\n    pad_mode = \"reflect\",\n    power = 2.0,\n    norm = \"slaney\",\n    onesided = True,\n    n_mels = n_mels,\n    mel_scale = \"htk\"\n)","metadata":{"execution":{"iopub.status.busy":"2022-04-26T07:39:58.344541Z","iopub.execute_input":"2022-04-26T07:39:58.345079Z","iopub.status.idle":"2022-04-26T07:39:58.460975Z","shell.execute_reply.started":"2022-04-26T07:39:58.345033Z","shell.execute_reply":"2022-04-26T07:39:58.460224Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# !pip download noisereduce==2.0.0 -d /kaggle/working/noisereduce/","metadata":{"execution":{"iopub.status.busy":"2022-04-29T02:30:42.207248Z","iopub.execute_input":"2022-04-29T02:30:42.207509Z","iopub.status.idle":"2022-04-29T02:30:52.328482Z","shell.execute_reply.started":"2022-04-29T02:30:42.20748Z","shell.execute_reply":"2022-04-29T02:30:52.324345Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install ../input/noisereduce-2-0-0/noisereduce-2.0.0-py3-none-any.whl","metadata":{"execution":{"iopub.status.busy":"2022-04-29T02:32:09.145289Z","iopub.execute_input":"2022-04-29T02:32:09.145628Z","iopub.status.idle":"2022-04-29T02:32:16.379154Z","shell.execute_reply.started":"2022-04-29T02:32:09.145591Z","shell.execute_reply":"2022-04-29T02:32:16.378256Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import noisereduce as nr\ndef normalize_std(spec):\n    return (spec - torch.mean(spec)) / torch.std(spec)\n\ndef audio_to_mel_label_train(filepath, min_sec_proc, reduce_noise = False, data_index = 0, label_list = [], bird_label = [], label_file = []):\n    label_file_all = np.zeros(bird_label.shape)\n    for label_file_temp in label_file:\n        label_file_all += (label_file_temp == bird_label)\n    label_file_all = np.clip(label_file_all, 0, 1)\n\n    waveform, _ = torchaudio.load(filepath = filepath)\n    if reduce_noise:\n        waveform = torch.tensor(nr.reduce_noise(y = waveform, sr = sample_rate, win_length = mel_spectrogram.win_length, use_tqdm = False, n_fft = mel_spectrogram.n_fft, n_jobs = -1))\n    len_wave = waveform.shape[1]\n    waveform = waveform[0, :].reshape(1, len_wave)\n\n    if len_wave < min_sec_proc: # Fill with recurrent sound samples until sample length \n        for i in range(int(min_sec_proc / len_wave)):\n            waveform = torch.cat((waveform, waveform[:, 0:len_wave]), dim = 1)\n        len_wave = min_sec_proc\n        waveform = waveform[:, 0:len_wave]\n\n    for i in range(int(len_wave / min_sec_proc)):\n        log_melspec = torch.log10(mel_spectrogram(waveform[0, i * min_sec_proc:(i + 1) * min_sec_proc]).unsqueeze(0) + 1e-10)\n        log_melspec = normalize_std(log_melspec)\n\n        torch.save(log_melspec, out_path + str(data_index) + \".pt\")\n        label_list.append(label_file_all)\n        data_index += 1\n\n    return data_index\n    \ndef audio_to_mel_label_test(filepath, min_sec_proc, reduce_noise = False, mel_list = []):\n    waveform, _ = torchaudio.load(filepath = filepath)\n    if reduce_noise:\n        waveform = torch.tensor(nr.reduce_noise(y = waveform, sr = sample_rate, win_length = mel_spectrogram.win_length, use_tqdm = False, n_fft = mel_spectrogram.n_fft, n_jobs = -1))\n    len_wave = waveform.shape[1]\n    waveform = waveform[0, :].reshape(1, len_wave)\n    \n    if len_wave >= min_sec_proc * 12: # Curtail sound samples that are too long (> 12 * min_sec_proc (1 min))\n        waveform = torch.cat((waveform, waveform[:, 0:len_wave]), dim = 1)\n        len_wave = min_sec_proc * 12\n        waveform = waveform[:, 0:len_wave]\n\n    for i in range(int(len_wave / min_sec_proc)):\n        log_melspec = torch.log10(mel_spectrogram(waveform[0, i * min_sec_proc:(i + 1) * min_sec_proc]).unsqueeze(0) + 1e-10)\n        log_melspec = normalize_std(log_melspec)\n        mel_list.append(log_melspec)\n\n        return mel_list\n\ndef load_tensor(path, filename):\n    return torch.load(path + str(filename) + \".pt\")\n\ndef get_X_y(path, idx, label_list):\n    batch_X = torch.stack([load_tensor(path, i.item()) for i in idx])\n    batch_y = torch.stack([label_list[i.item()] for i in idx])\n    return batch_X, batch_y\n\ndef plot_history(history):\n    plt.figure(figsize = (10, 10))\n    plt.plot(history[:, 0], history[:, 1], label = \"loss\")\n    plt.plot(history[:, 0], history[:, 2], label = \"val_loss\")\n    plt.xlabel(\"epoch\")\n    plt.ylabel(\"loss\")\n    plt.legend()\n","metadata":{"execution":{"iopub.status.busy":"2022-04-26T07:50:26.829115Z","iopub.execute_input":"2022-04-26T07:50:26.829387Z","iopub.status.idle":"2022-04-26T07:50:26.848721Z","shell.execute_reply.started":"2022-04-26T07:50:26.829359Z","shell.execute_reply":"2022-04-26T07:50:26.84783Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Save spectrogram\ndata_index = 0\nlabel_list = []\nfor primary_label, secondary_label, filename in zip(train_metadata_df[\"primary_label\"], train_metadata_df[\"secondary_labels\"], train_metadata_df[\"filename\"]):\n    data_index = audio_to_mel_label_train(\"../input/birdclef-2022/train_audio/\" + filename, min_sec_proc, False, data_index, label_list, bird_label, [primary_label] + eval(secondary_label))\n\ntorch.save(np.stack(label_list), out_path + \"label_list.pt\")\nlabel_list = torch.from_numpy(np.stack(label_list)).clone()\n# label_list = torch.from_numpy(torch.load(out_path + \"label_list.pt\"))","metadata":{"execution":{"iopub.status.busy":"2022-04-26T07:50:30.772005Z","iopub.execute_input":"2022-04-26T07:50:30.772269Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Build model\nn_output = len(bird_label)\nout_sigmoid = nn.Sigmoid()\n\n\"\"\"class BirdResNet(ResNet):\n    def __init__(self):\n        super().__init__(BasicBlock, [3, 4, 6, 3], num_classes = n_output)\n        self.conv1 = nn.Conv2d(1, 64, kernel_size = 7, stride = 1, padding = 3, bias = False)\"\"\"\n\nimport torchvision.models as models\nnet = models.resnet18()\nnet.conv1 = torch.nn.Conv2d(1, 64, kernel_size = 7, stride = 2, padding = 3, bias = False)\nnet.fc = torch.nn.Linear(512, n_output)\nnet = net.to(device)","metadata":{"execution":{"iopub.status.busy":"2022-04-24T07:23:03.55144Z","iopub.execute_input":"2022-04-24T07:23:03.551841Z","iopub.status.idle":"2022-04-24T07:23:06.571387Z","shell.execute_reply.started":"2022-04-24T07:23:03.551806Z","shell.execute_reply":"2022-04-24T07:23:06.570655Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Data split\ndata_len = label_list.shape[0]\ntrain_idx, val_idx = torch.utils.data.random_split(np.arange(0, data_len), [int(0.8 * data_len), data_len - int(0.8 * data_len)])","metadata":{"execution":{"iopub.status.busy":"2022-04-24T07:23:06.572657Z","iopub.execute_input":"2022-04-24T07:23:06.572909Z","iopub.status.idle":"2022-04-24T07:23:06.580628Z","shell.execute_reply.started":"2022-04-24T07:23:06.572876Z","shell.execute_reply":"2022-04-24T07:23:06.579909Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Train loop\nnum_epochs = 20\nlr = 0.0005\nbatch_size = 48\n\ncriterion = nn.BCEWithLogitsLoss()\noptimizer = optim.Adam(net.parameters(), lr = lr)\nhistory = np.zeros((0, 3))\n\ntrain_loader = DataLoader(train_idx, batch_size = batch_size, shuffle = True)\nval_loader = DataLoader(val_idx, batch_size = batch_size, shuffle = True)\n\nfor epoch in range(num_epochs):\n    train_loss, val_loss = 0, 0\n    train_acc, val_acc = 0, 0\n    n_train, n_val = 0, 0\n\n    net.train()\n    for idx in train_loader:\n        inputs, labels = get_X_y(out_path, idx, label_list)\n        n_train += len(labels)\n        inputs = inputs.to(device)\n        labels = labels.to(device)\n\n        optimizer.zero_grad()\n        outputs = net(inputs)\n\n        out_labels = out_sigmoid(outputs) > 0.1\n        train_acc += len(labels) - (torch.eq(out_labels, 1 - labels).sum(axis = 1) > 0).sum().item()\n\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        train_loss += loss.item()\n\n    net.eval()\n    with torch.no_grad():\n        for idx in val_loader:\n            inputs_val, labels_val = get_X_y(out_path, idx, label_list)\n            n_val += len(labels_val)\n            inputs_val = inputs_val.to(device)\n            labels_val = labels_val.to(device)\n\n            outputs_val = net(inputs_val)\n\n            out_labels_val = out_sigmoid(outputs_val) > 0.1\n            val_acc += len(labels_val) - (torch.eq(out_labels_val, 1 - labels_val).sum(axis = 1) > 0).sum().item()\n\n            loss_val = criterion(outputs_val, labels_val)\n            val_loss += loss_val.item()\n\n    train_loss = train_loss * batch_size / n_train\n    val_loss = val_loss * batch_size / n_val\n    train_acc /= n_train\n    val_acc /= n_val\n    print(f\"Epoch [{epoch + 1} / {num_epochs}], Train loss: {train_loss:.5f}, Train accuracy: {train_acc:.5f}, Val loss: {val_loss:.5f}, Val accuracy: {val_acc:.5f}\")\n    item = np.array([epoch + 1, train_loss, val_loss])\n    history = np.vstack((history, item))\n    \n    if (epoch + 1) % 5 == 0:\n        lr = lr * 0.7\n\ntorch.save(net.state_dict(), \"model.pt\")\nplot_history(history)","metadata":{"execution":{"iopub.status.busy":"2022-04-24T07:23:06.581933Z","iopub.execute_input":"2022-04-24T07:23:06.582268Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_audio_dir = \"../input/birdclef-2022/test_soundscapes/\"\ntest_list = [f.split(\".\")[0] for f in sorted(os.listdir(test_audio_dir))]\nprint(f\"Number of test soundscapes: {len(test_list)}\")","metadata":{"execution":{"iopub.status.busy":"2022-04-24T08:37:47.370545Z","iopub.execute_input":"2022-04-24T08:37:47.370883Z","iopub.status.idle":"2022-04-24T08:37:47.449383Z","shell.execute_reply.started":"2022-04-24T08:37:47.370803Z","shell.execute_reply":"2022-04-24T08:37:47.448304Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Evaluate and submission\npred = {\"row_id\": [], \"target\": []}\nbinary_th = 0.1\nnet.eval()\n\ntest_df = pd.read_csv(\"../input/birdclef-2022/test.csv\")\n\nfor testfile in test_list:\n    path = \"../input/birdclef-2022/test_soundscapes/\" + testfile + \".ogg\"\n    \n    chunks = [[] for i in range(12)]\n\n    mel_list_test = []\n    mel_list_test = audio_to_mel_label_test(path, min_sec_proc, mel_list = mel_list_test)\n    # n_chunks = len(mel_list_test)\n    mel_list_test = torch.stack(mel_list_test).to(device)\n\n    outputs = net(mel_list_test)\n    outputs_test = out_sigmoid(outputs)\n\n    for i in range(len(chunks)):\n        chunk_end_time = (i + 1) * 5\n        for bird in scored_birds:\n            try:\n                score = outputs_test[i][np.where(bird_label == bird)]\n            except IndexError:\n                score = 0\n            \n            row_id = testfile + \"_\" + bird + \"_\" + str(chunk_end_time)\n\n            pred[\"row_id\"].append(row_id)\n            pred[\"target\"].append(bool(score > binary_th))\n            \nresults = pd.DataFrame(pred, columns = [\"row_id\", \"target\"])\nprint(results[\"target\"])\nresults.to_csv(\"submission.csv\", index = False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}