{"metadata":{"kernelspec":{"display_name":"base","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.8"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":70203,"databundleVersionId":8068726,"sourceType":"competition"},{"sourceId":8359997,"sourceType":"datasetVersion","datasetId":4968282}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport gc\nimport cv2\nimport math\nfrom glob import glob\nimport re\nimport numpy as np\nimport pandas as pd\nfrom tqdm.notebook import tqdm\nimport matplotlib.pyplot as plt\nimport librosa\nfrom scipy import signal as sci_signal\nimport sklearn\n\nimport torch\nfrom datetime import datetime\nfrom torch import nn\n\nimport kaggle_metric_utilities\n\nimport cupy as cp\nfrom cupyx.scipy import signal as cupy_signal\n\nimport albumentations as albu","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class config:\n    SEED = 14\n    DEVICE = 'cuda'\n    MIXED_PRECISION = False\n    OUTPUT_DIR = 'out_dir'\n    \n    DATA_ROOT = '/kaggle/input/birdclef-2024'\n    PREPROCESSED_DATA_ROOT = '/kaggle/input/birdclef-preprocessed'\n    LOAD_DATA = True\n    FS = 32000\n    N_FFT = 1095\n    WIN_SIZE = 412\n    WIN_LAP = 100\n    MIN_FREQ = 40\n    MAX_FREQ = 15000 \n    \n    BATCH_SIZE = 32\n    N_WORKERS = 12\n    \n    USE_XYMASKING = True\n    \n    FOLDS = 10\n    EPOCHS = 5\n    LR = 8e-5\n    WEIGHT_DECAY = 1e-5\n    \n    VISUALIZE = True\n\ndef set_seed(seed=14):\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 = False\n    \nset_seed()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"label_list = sorted(os.listdir(os.path.join(config.DATA_ROOT, 'train_audio')))\nlabel_id_list = list(range(len(label_list)))\nlabel2id = dict(zip(label_list, label_id_list))\nid2label = dict(zip(label_id_list, label_list))","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"metadata_df = pd.read_csv(f'{config.DATA_ROOT}/train_metadata.csv')\nmetadata_df.head()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = metadata_df[['primary_label', 'rating', 'filename']].copy()\n\ntrain_df['target'] = train_df.primary_label.map(label2id)\ntrain_df['filepath'] = config.DATA_ROOT + '/train_audio/' + train_df.filename\ntrain_df['samplename'] = train_df.filename.map(lambda x: x.split('/')[0] + '-' + x.split('/')[-1].split('.')[0])\n\ntrain_df.head()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def scipy_spectro(audio_data):\n    mean_signal = np.nanmean(audio_data)\n    audio_data = np.nan_to_num(audio_data, nan=mean_signal) if np.isnan(audio_data).mean() < 1 else np.zeros_like(audio_data)\n    \n    frequencies, times, spec_data = sci_signal.spectrogram(\n        audio_data, \n        fs=config.FS, \n        nfft=config.N_FFT, \n        nperseg=config.WIN_SIZE, \n        noverlap=config.WIN_LAP, \n        window='hann'\n    )\n    \n    valid_freq = (frequencies >= config.MIN_FREQ) & (frequencies <= config.MAX_FREQ)\n    spec_data = spec_data[valid_freq, :]\n    \n    spec_data = np.log10(spec_data + 1e-20)\n    \n    spec_data = spec_data - spec_data.min()\n    spec_data = spec_data / spec_data.max()\n    \n    return spec_data","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def cupy_spectro(audio_data):    \n    audio_data = cp.array(audio_data)\n    \n    mean_signal = cp.nanmean(audio_data)\n    audio_data = cp.nan_to_num(audio_data, nan=mean_signal) if cp.isnan(audio_data).mean() < 1 else cp.zeros_like(audio_data)\n    \n    frequencies, times, spec_data = cupy_signal.spectrogram(\n        audio_data, \n        fs=config.FS, \n        nfft=config.N_FFT, \n        nperseg=config.WIN_SIZE, \n        noverlap=config.WIN_LAP, \n        window='hann'\n    )\n    \n    valid_freq = (frequencies >= config.MIN_FREQ) & (frequencies <= config.MAX_FREQ)\n    spec_data = spec_data[valid_freq, :]\n    \n    spec_data = cp.log10(spec_data + 1e-20)\n    \n    spec_data = spec_data - spec_data.min()\n    spec_data = spec_data / spec_data.max()\n    \n    return spec_data.get()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if config.LOAD_DATA:\n    all_bird_data = np.load(f'{config.PREPROCESSED_DATA_ROOT}/spec_center_5sec_256_256.npy', allow_pickle=True).item()\nelse:\n    all_bird_data = dict()\n    for i, row_metadata in tqdm(train_df.iterrows()):\n        audio_data, _ = librosa.load(row_metadata.filepath, sr=config.FS)\n\n        n_copy = math.ceil(5 * config.FS / len(audio_data))\n        if n_copy > 1: audio_data = np.concatenate([audio_data]*n_copy)\n\n        start_idx = int(len(audio_data) / 2 - 2.5 * config.FS)\n        end_idx = int(start_idx + 5.0 * config.FS)\n        input_audio = audio_data[start_idx:end_idx]\n\n        input_spec = cupy_spectro(input_audio)\n        \n        input_spec = cv2.resize(input_spec, (256, 256), interpolation=cv2.INTER_AREA)\n\n        all_bird_data[row_metadata.samplename] = input_spec.astype(np.float32)\n\n    np.save(os.path.join(config.PREPROCESSED_DATA_ROOT, f'spec_center_5sec_256_256.npy'), all_bird_data)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class BirdDataset(torch.utils.data.Dataset):\n    def __init__(\n        self,\n        metadata,\n        augmentation=None,\n        mode='train'\n    ):\n        super().__init__()\n        self.metadata = metadata\n        self.augmentation = augmentation\n        self.mode = mode\n    \n    def __len__(self):\n        return len(self.metadata)\n    \n    def __getitem__(self, index):\n        row_metadata = self.metadata.iloc[index]\n        \n        input_spec = all_bird_data[row_metadata.samplename]\n        \n        if self.augmentation is not None:\n            input_spec = self.augmentation(image=input_spec)['image']\n        \n        target = row_metadata.target\n        \n        return torch.tensor(input_spec, dtype=torch.float32).unsqueeze(0), torch.tensor(target, dtype=torch.long)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_transforms(_type):\n    if _type == 'train':\n        return albu.Compose([\n            albu.HorizontalFlip(p=0.5),\n            albu.XYMasking(\n                p=0.3,\n                num_masks_x=(1, 3),\n                num_masks_y=(1, 3),\n                mask_x_length=(1, 10),\n                mask_y_length=(1, 20),\n            ) if config.USE_XYMASKING else albu.NoOp()\n        ])\n    elif _type == 'valid' or _type == 'test':\n        return albu.Compose([])","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dummy_dataset = BirdDataset(train_df, get_transforms('train'))","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def show_batch(ds, row=3, col=3):\n    fig = plt.figure(figsize=(10, 10))\n    img_index = np.random.randint(0, len(ds)-1, row*col)\n    \n    for i in range(len(img_index)):\n        img, label = dummy_dataset[img_index[i]]\n        \n        if isinstance(img, torch.Tensor):\n            img = img.squeeze().detach().numpy()\n        \n        ax = fig.add_subplot(row, col, i + 1, xticks=[], yticks=[])\n        ax.imshow(img)\n        ax.set_title(f'ID: {img_index[i]}; Target: {label}')\n    \n    plt.tight_layout()\n    plt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# test_input, test_target = dummy_dataset[0]\n# print(test_input.detach().numpy().shape)\n\n# if config.VISUALIZE:\n#     show_batch(dummy_dataset)\n\n# del dummy_dataset\n# gc.collect()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class ConvBlock(nn.Module):\n    def __init__(\n        self, in_channels, out_channels\n    ) -> None:\n        super().__init__()\n        self.same_channels = in_channels==out_channels\n\n        self.conv1 = nn.Sequential(\n            nn.Conv2d(in_channels, out_channels, 3, 2, 1),\n            nn.BatchNorm2d(out_channels),\n            nn.GELU(),\n        )\n\n    def forward(self, x):\n        x = self.conv1(x)\n        return x","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Model(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.conv = nn.Sequential(\n            ConvBlock(1, 2),\n            ConvBlock(2, 4),\n            ConvBlock(4, 8),\n            ConvBlock(8, 8),\n            ConvBlock(8, 16),\n            ConvBlock(16, 16),\n            ConvBlock(16, 32),\n            ConvBlock(32, 182),\n        )\n\n    def forward(self, x):\n        return nn.Softmax()(self.conv(x))","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = Model()\nmodel.load_state_dict(torch.load(\"save_dir/model_save_time=2024-05-09 09:51:45.475239 | epoch: 60.npy\"))\n# model.eval()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = train_df.sample(frac=1).reset_index(drop=True)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.shape","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"split = int(train_df.shape[0] * 14/15)\n\ntrain_df_ = train_df[:split].copy()\nvalid_df = train_df[split:].copy()\ntrain_df = train_df_\n\nprint(f'Train Samples: {len(train_df)}')\nprint(f'Valid Samples: {len(valid_df)}')\n\ntrain_ds = BirdDataset(train_df, get_transforms('train'), 'train')\nval_ds = BirdDataset(valid_df, get_transforms('valid'), 'valid')\n# test_ds = BirdDataset(test_df, get_transforms('valid'), 'valid')","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dl = torch.utils.data.DataLoader(\n    train_ds,\n    batch_size=config.BATCH_SIZE,\n    shuffle=True,\n    num_workers=config.N_WORKERS,\n    pin_memory=True,\n    persistent_workers=True\n)\n\nval_dl = torch.utils.data.DataLoader(\n    val_ds,\n    batch_size=1,\n    shuffle=False,\n    num_workers=config.N_WORKERS,\n    pin_memory=True,\n    persistent_workers=True\n)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = \"cuda\"\nepochs = 150    \nloss_fn = nn.CrossEntropyLoss()\noptim = torch.optim.Adam(\n    filter(lambda p: p.requires_grad, model.parameters()),\n    lr=config.LR\n)\nscaler = torch.cuda.amp.GradScaler()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"epoch_i = 0\nfor epoch in range(epochs):\n    i = 0\n    sum = 0\n    for batch in train_dl:\n        image, target = batch\n        image = image.to(device)\n        target = target.to(device)\n        model = model.to(device)\n\n        y_pred = model(image).squeeze()\n\n        loss = loss_fn(y_pred, target)\n\n        loss.backward()\n        optim.step()\n\n        # with torch.cuda.amp.autocast():\n        #     scaler.scale(train_loss).backward()\n        #     # grad_norm = torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=10)\n        #     scaler.step(optim)\n        #     scaler.update()\n\n        i += 1\n        sum += loss\n    \n    print(\"Epoch #\" + str(epoch) + \" | Loss: \" + str((sum / i).item()))\n    epoch_i+=1\n    \n    if epoch_i % 100 == 0 and epoch_i > 0:\n        now = datetime.now()\n\n        torch.save(model.state_dict(), os.path.join(\n            config.PREPROCESSED_DATA_ROOT, f'model_save_time=' + str(now) + ' | epoch: ' + str(epoch_i) + '.npy'))","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def predict(data_loader, model):\n    model.to(config.DEVICE)\n    model.eval()\n    pred = []\n    for batch in data_loader:\n        with torch.no_grad():\n            x, _ = batch\n            x = x.to(config.DEVICE)\n            outputs = model(x)\n        pred.append(outputs.detach().cpu())\n    \n    pred = torch.cat(pred, dim=0).cpu().detach()\n    \n    return pred.numpy().astype(np.float32)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predictions = []\n\npredictions.append(predict(val_dl, model))\ngc.collect()\n\npredictions = np.mean(predictions, axis=0).squeeze()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(predictions)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def score(solution, submission):\n    if not pd.api.types.is_numeric_dtype(submission.values):\n        bad_dtypes = {x: submission[x].dtype  for x in submission.columns\n                      if not pd.api.types.is_numeric_dtype(submission[x])}\n        raise TypeError(f'Invalid submission data types found: {bad_dtypes}')\n\n    solution_sums = solution.sum(axis=0)\n    scored_columns = list(solution_sums[solution_sums > 0].index.values)\n    assert len(scored_columns) > 0\n\n    return kaggle_metric_utilities.safe_call_score( sklearn.metrics.roc_auc_score,\n                                                    solution[scored_columns].values,\n                                                    submission[scored_columns].values,\n                                                    average='macro'\n                                                  )","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"i = 0\nnum_correct_1 = 0\nnum_correct_9 = 0\nnum_correct_18 = 0\nnum_correct_91 = 0\navg_correct_pos = 0\ntarget_list = []\nfor _, target in val_dl:\n    target_list_ = [0] * 182\n    for ii in range(182):\n        if ii == target[0].item():\n            target_list_[ii] = 1\n\n        if predictions[ii].argmax() == target[0]:\n            if ii == 0:\n                num_correct_1 += 1\n\n            if ii <= 9:\n                num_correct_9 += 1\n            \n            if ii <= 18:\n                num_correct_18 += 1\n            \n            if ii <= 91:\n                num_correct_91 += 1\n\n            avg_correct_pos += ii\n            \n            break\n\n    i+=1\n    target_list.append(target_list_)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"target_list_pd = pd.DataFrame(target_list)\npredictions_pd = pd.DataFrame(predictions)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Correct: \" + str(num_correct_1/i))\nprint(\"Correct Top 5%: \" + str(num_correct_9/i))\nprint(\"Correct Top 10%: \" + str(num_correct_18/i))\nprint(\"Correct Top 50%: \" + str(num_correct_91/i))\nprint(\"Avg Correct Pos: \" + str(avg_correct_pos/i))\nprint(\"Score:\", score(target_list_pd, predictions_pd))","metadata":{},"execution_count":null,"outputs":[]}]}