{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":91844,"databundleVersionId":11361821,"sourceType":"competition"},{"sourceId":11006697,"sourceType":"datasetVersion","datasetId":6852193},{"sourceId":11985557,"sourceType":"datasetVersion","datasetId":7538391},{"sourceId":167220511,"sourceType":"kernelVersion"},{"sourceId":189366517,"sourceType":"kernelVersion"},{"sourceId":227215575,"sourceType":"kernelVersion"},{"sourceId":244240657,"sourceType":"kernelVersion"}],"dockerImageVersionId":30919,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Training Notebook Event Detection","metadata":{}},{"cell_type":"markdown","source":"* Inference Notebook [here.]()\n* Dataset Creation [here](https://www.kaggle.com/code/loekie/data-preparation-event-detection)","metadata":{}},{"cell_type":"code","source":"import sys\nsys.path.append('/kaggle/usr/lib/kaggle_metric_utilities')\nsys.path.append('/kaggle/usr/lib/birdclef-roc-auc')\nimport os\nimport random\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom sklearn.model_selection import train_test_split\nfrom tqdm import tqdm\n\nfrom sklearn.metrics import roc_auc_score\nimport torch\nimport torch.optim as optim\nimport torch.nn as nn\nfrom torch.utils.data import DataLoader, Dataset\nimport torchaudio\nimport torchaudio.transforms as AT\nfrom torchvision import models\n\nfrom metric import score\n\nimport csv\nimport os","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T15:17:58.303065Z","iopub.execute_input":"2025-06-07T15:17:58.303452Z","iopub.status.idle":"2025-06-07T15:17:58.310455Z","shell.execute_reply.started":"2025-06-07T15:17:58.303404Z","shell.execute_reply":"2025-06-07T15:17:58.309577Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## BirdClef dataset","metadata":{}},{"cell_type":"code","source":"input_path_bird_clef  = \"/kaggle/input/birdclef-2025-train-data/train_raw5/\"\n\ntrain_meta_bird_clef_original = pd.read_csv( \"../input/birdclef-2025/train.csv\")\n\ntrain_meta_bird_clef = pd.DataFrame()\ntrain_meta_bird_clef['id'] = pd.DataFrame(train_meta_bird_clef_original['filename'])\ntrain_meta_bird_clef['id'] = input_path_bird_clef + train_meta_bird_clef['id'].astype(str) \ntrain_meta_bird_clef['label'] = 1","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T15:17:58.311564Z","iopub.execute_input":"2025-06-07T15:17:58.311845Z","iopub.status.idle":"2025-06-07T15:17:58.464365Z","shell.execute_reply.started":"2025-06-07T15:17:58.311823Z","shell.execute_reply":"2025-06-07T15:17:58.463395Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Event detection dataset","metadata":{}},{"cell_type":"code","source":"input_path_detection = '/kaggle/input/data-preparation-event-detection/train_raw5/'\ntrain_meta_detection = pd.read_csv('/kaggle/input/expanded-labels/metadata_expanded.csv')\ntrain_meta_detection['id'] = input_path_detection + train_meta_detection['id'].astype(str) + \".wav\" \ntrain_meta_detection = train_meta_detection[train_meta_detection['label'] == 0]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T15:17:58.466510Z","iopub.execute_input":"2025-06-07T15:17:58.466881Z","iopub.status.idle":"2025-06-07T15:17:58.495122Z","shell.execute_reply.started":"2025-06-07T15:17:58.466846Z","shell.execute_reply":"2025-06-07T15:17:58.494422Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Combined dataset","metadata":{}},{"cell_type":"code","source":"train_meta = pd.concat([train_meta_detection, train_meta_bird_clef])\ntrain_meta.reset_index().drop(columns= ['index'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T15:17:58.496397Z","iopub.execute_input":"2025-06-07T15:17:58.496775Z","iopub.status.idle":"2025-06-07T15:17:58.513588Z","shell.execute_reply.started":"2025-06-07T15:17:58.496720Z","shell.execute_reply":"2025-06-07T15:17:58.512912Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Configuration","metadata":{}},{"cell_type":"code","source":"wav_sec = 5\nsample_rate = 32000\nmin_segment = sample_rate*wav_sec\n\nepochs = 10\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nmel_configs = [\n    {\"n_fft\": 1280, \"hop_length\": 512,  \"n_mels\": 64, \"f_max\": 16000}, ]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T15:17:58.514468Z","iopub.execute_input":"2025-06-07T15:17:58.514797Z","iopub.status.idle":"2025-06-07T15:17:58.519182Z","shell.execute_reply.started":"2025-06-07T15:17:58.514765Z","shell.execute_reply":"2025-06-07T15:17:58.518362Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Evaluation and logging functions","metadata":{}},{"cell_type":"code","source":"# Adapted cal score for a binary classification problem\ndef cal_score(label, pred):\n    label = np.concatenate(label)       \n    pred = np.concatenate(pred)          \n\n    label = label.reshape(-1).astype(int)\n    pred = pred.reshape(-1, 2)\n\n    pred_class1_probs = pred[:, 1]\n\n    return roc_auc_score(label, pred_class1_probs)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T15:17:58.520001Z","iopub.execute_input":"2025-06-07T15:17:58.520257Z","iopub.status.idle":"2025-06-07T15:17:58.532139Z","shell.execute_reply.started":"2025-06-07T15:17:58.520237Z","shell.execute_reply":"2025-06-07T15:17:58.531332Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def log_metrics_to_csv(csv_path, name, epoch, auc, auc_val, loss, loss_val):\n    # Create the file with header if it doesn't exist\n    file_exists = os.path.isfile(csv_path)\n    \n    with open(csv_path, mode='a', newline='') as file:\n        writer = csv.writer(file)\n        \n        if not file_exists:\n            writer.writerow(['name', 'epoch', 'auc', 'auc_val', 'loss', 'loss_val'])\n        \n        writer.writerow([name, epoch, auc, auc_val, loss, loss_val])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T15:17:58.533000Z","iopub.execute_input":"2025-06-07T15:17:58.533280Z","iopub.status.idle":"2025-06-07T15:17:58.543043Z","shell.execute_reply.started":"2025-06-07T15:17:58.533252Z","shell.execute_reply":"2025-06-07T15:17:58.542396Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Adapted version of BirdclefDataset for binary classification problem","metadata":{}},{"cell_type":"code","source":"class BirdclefDataset(Dataset):\n    def __init__(self, df, mel_transform, mode='train'):\n        self.df = df\n        self.mode = mode\n        self.mel_transform = mel_transform\n\n    def normalize_std(self, spec, eps=1e-23):\n        mean = torch.mean(spec)\n        std = torch.std(spec)\n        return torch.where(std == 0, spec-mean, (spec - mean) / (std+eps))\n                \n    def __getitem__(self, index):\n        try:\n            sig, _ = torchaudio.load(uri=self.df.iloc[index].id,backend=\"soundfile\")\n        except:\n            sig, _ = torchaudio.load(uri=self.df.iloc[index].id,backend=\"ffmpeg\")\n        sig = sig / torch.max(torch.abs(sig))\n        sig = sig + 1.5849e-05*(torch.rand(1, min_segment)-0.5) \n        melspec = self.mel_transform(sig)\n        melspec = torch.log(melspec + 1e-6)\n        melspec = self.normalize_std(melspec)\n\n        y = self.df.iloc[index].label\n        \n        return melspec, y\n    \n    def __len__(self):\n        return len(self.df)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T15:17:58.544011Z","iopub.execute_input":"2025-06-07T15:17:58.544298Z","iopub.status.idle":"2025-06-07T15:17:58.554486Z","shell.execute_reply.started":"2025-06-07T15:17:58.544267Z","shell.execute_reply":"2025-06-07T15:17:58.553796Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## ResNet model","metadata":{}},{"cell_type":"code","source":"class Model_resnet34(nn.Module):\n    def __init__(self, pretrained=False):\n        super().__init__()\n        model = models.resnet34(pretrained=pretrained)\n        num_ftrs = model.fc.in_features\n        model.fc = nn.Linear(num_ftrs, 2)\n        self.model = model\n\n    def forward(self, x):\n        x = torch.cat((x,x,x),1)\n        x = self.model(x)\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T15:17:58.556517Z","iopub.execute_input":"2025-06-07T15:17:58.556766Z","iopub.status.idle":"2025-06-07T15:17:58.569324Z","shell.execute_reply.started":"2025-06-07T15:17:58.556746Z","shell.execute_reply":"2025-06-07T15:17:58.568494Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Training loop","metadata":{}},{"cell_type":"code","source":"train_df, val_df = train_test_split(train_meta, test_size=0.2, random_state=42)\nprint(train_df.iloc[0].id)\nfor i, cfg in enumerate(mel_configs):\n    print(f\"\\nTraining model {i+1}/{len(mel_configs)} with config: {cfg}\")\n\n    mel_transform = AT.MelSpectrogram(\n        sample_rate=sample_rate,\n        n_fft=cfg[\"n_fft\"],\n        win_length=cfg[\"n_fft\"],\n        hop_length=cfg[\"hop_length\"],\n        center=True,\n        f_min=20,\n        f_max=cfg[\"f_max\"],\n        pad_mode=\"reflect\",\n        power=2.0,\n        norm='slaney',\n        n_mels=cfg[\"n_mels\"],\n        mel_scale=\"htk\",\n    )\n\n    # Datasets\n    train_dataset = BirdclefDataset(train_df, mel_transform, mode='train')\n    val_dataset = BirdclefDataset(val_df, mel_transform, mode='val')\n    \n    train_loader = DataLoader(train_dataset, batch_size=24, shuffle=True, num_workers=2, drop_last=True)\n    val_loader = DataLoader(val_dataset, batch_size=24, shuffle=False, num_workers=1, drop_last=True)\n   \n    # Model and optimizer\n    model = Model_resnet34(pretrained=True).to(device)\n    criterion = nn.CrossEntropyLoss()\n    optimizer = optim.Adam(model.parameters(), lr=0.001)\n    \n    # Training loop\n    for epoch in range(epochs):\n        model.train()\n        pred_train = []\n        label_train = []\n        running_loss = 0.0\n        for melspecs, labels in tqdm(train_loader):\n            melspecs, labels = melspecs.to(device), labels.to(device)\n            optimizer.zero_grad()\n            outputs = model(melspecs)\n            loss = criterion(outputs, labels)\n            loss.backward()\n            optimizer.step()\n            running_loss += loss.item()\n            pred_train.append(torch.softmax(outputs,dim=1).detach().cpu().numpy())\n            label_train.append(labels.detach().cpu().numpy())\n    \n        pred_val = []\n        label_val = []\n        running_loss_val = 0.0\n        model.eval()\n        with torch.no_grad():\n            for melspecs, labels in val_loader:\n                melspecs, labels = melspecs.to(device), labels.to(device)\n                outputs = model(melspecs)\n                loss = criterion(outputs, labels)\n                running_loss_val += loss.item()\n                pred_val.append(torch.softmax(outputs,dim=1).detach().cpu().numpy())\n                label_val.append(labels.detach().cpu().numpy())\n\n        csv_path = '/kaggle/working/results.csv'\n       \n        auc_train_val = cal_score(label_train, pred_train)\n        auc_val = cal_score(label_val, pred_val)\n        print(f\"Epoch {epoch+1}/{epochs}, Loss: {running_loss/len(train_loader):.4f}, Loss_val: {running_loss_val/len(train_loader):.4f}\")\n        print(f\"Auc: {auc_train_val:.2f}% Auc_val: {auc_val:.2f}%\")\n    \n        # Log metrics\n        log_metrics_to_csv(\n            csv_path=csv_path,\n            name=f\"cfg_{i}_fft{cfg['n_fft']}_hop{cfg['hop_length']}_mels{cfg['n_mels']}\",\n            epoch=epoch + 1,\n            auc=auc_train_val,\n            auc_val=auc_val,\n            loss=running_loss / len(train_loader),\n            loss_val=running_loss_val / len(val_loader)\n        )\n\n    # Save model \n    torch.save(model.state_dict(), f\"model_cfg_{i}.pth\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T15:17:58.570272Z","iopub.execute_input":"2025-06-07T15:17:58.570516Z","iopub.status.idle":"2025-06-07T15:18:11.544569Z","shell.execute_reply.started":"2025-06-07T15:17:58.570497Z","shell.execute_reply":"2025-06-07T15:18:11.543016Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}