{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","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"}],"dockerImageVersionId":31011,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\n\n# Load metadata\ndf = pd.read_csv(\"/kaggle/input/birdclef-2025/train.csv\")\n\n# Sample 3% for debugging\ndf_small = df.sample(frac=0.03, random_state=42).reset_index(drop=True)\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-04-15T04:23:47.390082Z","iopub.execute_input":"2025-04-15T04:23:47.390847Z","iopub.status.idle":"2025-04-15T04:23:47.503765Z","shell.execute_reply.started":"2025-04-15T04:23:47.390823Z","shell.execute_reply":"2025-04-15T04:23:47.503190Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nfrom torch.utils.data import Dataset\nimport torchaudio\nimport torchvision.transforms as T\nimport numpy as np\nimport os\n\nclass BirdclefDataset(Dataset):\n    def __init__(self, df, audio_dir, duration=5.0, sr=32000, transform=None):\n        self.df = df\n        self.audio_dir = audio_dir\n        self.duration = duration\n        self.sr = sr\n        self.transform = transform\n        self.samples = int(duration * sr)\n\n        self.mel_transform = torchaudio.transforms.MelSpectrogram(\n            sample_rate=sr,\n            n_fft=2048,\n            hop_length=512,\n            n_mels=128,\n            f_min=50,\n            f_max=16000\n        )\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        filename = row[\"filename\"]\n        label = row[\"primary_label\"]\n\n        filepath = os.path.join(self.audio_dir, filename)\n\n        waveform, sr = torchaudio.load(filepath)\n        if waveform.shape[1] < self.samples:\n            # pad if too short\n            pad_len = self.samples - waveform.shape[1]\n            waveform = torch.nn.functional.pad(waveform, (0, pad_len))\n        else:\n            waveform = waveform[:, :self.samples]\n\n        mel = self.mel_transform(waveform).squeeze(0)  # (n_mels, time)\n\n        # Normalize and convert to image format\n        mel = (mel - mel.mean()) / (mel.std() + 1e-6)\n        mel = torch.stack([mel, mel, mel], dim=0)  # (3, H, W)\n\n        if self.transform:\n            mel = self.transform(mel)\n\n        return mel, label\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-15T04:23:51.776926Z","iopub.execute_input":"2025-04-15T04:23:51.777391Z","iopub.status.idle":"2025-04-15T04:23:51.785411Z","shell.execute_reply.started":"2025-04-15T04:23:51.777369Z","shell.execute_reply":"2025-04-15T04:23:51.784531Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transform = T.Compose([\n    T.Resize((224, 224)),\n    T.Normalize(mean=[0.485, 0.456, 0.406],\n                std=[0.229, 0.224, 0.225]),\n])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-15T04:23:56.295776Z","iopub.execute_input":"2025-04-15T04:23:56.296486Z","iopub.status.idle":"2025-04-15T04:23:56.300442Z","shell.execute_reply.started":"2025-04-15T04:23:56.296460Z","shell.execute_reply":"2025-04-15T04:23:56.299767Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import DataLoader\n\ntrain_ds = BirdclefDataset(df_small, audio_dir='/kaggle/input/birdclef-2025/train_audio', transform=transform)\ntrain_dl = DataLoader(train_ds, batch_size=16, shuffle=True, num_workers=2)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-15T04:24:04.354439Z","iopub.execute_input":"2025-04-15T04:24:04.354718Z","iopub.status.idle":"2025-04-15T04:24:04.362709Z","shell.execute_reply.started":"2025-04-15T04:24:04.354698Z","shell.execute_reply":"2025-04-15T04:24:04.362066Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for x, y in train_dl:\n    print(x.shape)  # should be [B, 3, 224, 224]\n    print(y)\n    break\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-15T04:24:06.862039Z","iopub.execute_input":"2025-04-15T04:24:06.862363Z","iopub.status.idle":"2025-04-15T04:24:08.206286Z","shell.execute_reply.started":"2025-04-15T04:24:06.862338Z","shell.execute_reply":"2025-04-15T04:24:08.205552Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torchvision.models as models\nimport torch.nn as nn\n\nNUM_CLASSES = df[\"primary_label\"].nunique()  # should be 206 for full dataset\n\n\nmodel = models.efficientnet_b0(pretrained=True)\nmodel.classifier[1] = nn.Linear(model.classifier[1].in_features, NUM_CLASSES)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-15T04:24:15.371808Z","iopub.execute_input":"2025-04-15T04:24:15.372081Z","iopub.status.idle":"2025-04-15T04:24:15.526817Z","shell.execute_reply.started":"2025-04-15T04:24:15.372061Z","shell.execute_reply":"2025-04-15T04:24:15.525910Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel = model.to(device)\n\ncriterion = nn.BCEWithLogitsLoss()  # multilabel setup\noptimizer = torch.optim.Adam(model.parameters(), lr=1e-4)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-15T04:24:19.424433Z","iopub.execute_input":"2025-04-15T04:24:19.424702Z","iopub.status.idle":"2025-04-15T04:24:19.455861Z","shell.execute_reply.started":"2025-04-15T04:24:19.424682Z","shell.execute_reply":"2025-04-15T04:24:19.455008Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"species_list = sorted(df['primary_label'].unique().tolist())\nlabel_dict = {species: idx for idx, species in enumerate(species_list)}\n\ndef encode_labels(species_names, num_classes=206):\n    # Create a one-hot encoded tensor for the labels\n    label_tensor = torch.zeros(len(species_names), num_classes)\n    for i, species in enumerate(species_names):\n        label_index = label_dict.get(species, -1)  # Get the label index from the dictionary\n        if label_index >= 0:  # If a valid species name\n            label_tensor[i, label_index] = 1  # Set the index corresponding to the species\n    return label_tensor\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-15T04:24:23.636658Z","iopub.execute_input":"2025-04-15T04:24:23.636919Z","iopub.status.idle":"2025-04-15T04:24:23.643270Z","shell.execute_reply.started":"2025-04-15T04:24:23.636900Z","shell.execute_reply":"2025-04-15T04:24:23.642388Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for batch in train_dl:\n    mel = batch[0]  # mel spectrograms\n    label_strs = batch[1]  # species names\n    \n    # Convert species names to indices\n    label_tensor = encode_labels(label_strs).to(device).float()\n\n\n    mel = mel.to(device)\n    label_tensor = label_tensor.to(device).float()\n\n    optimizer.zero_grad()\n    output = model(mel)\n    loss = criterion(output, label_tensor)\n    loss.backward()\n    optimizer.step()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-15T04:24:27.918330Z","iopub.execute_input":"2025-04-15T04:24:27.918599Z","iopub.status.idle":"2025-04-15T04:24:44.698100Z","shell.execute_reply.started":"2025-04-15T04:24:27.918581Z","shell.execute_reply":"2025-04-15T04:24:44.697332Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(batch[1])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-15T04:24:49.052725Z","iopub.execute_input":"2025-04-15T04:24:49.053389Z","iopub.status.idle":"2025-04-15T04:24:49.058113Z","shell.execute_reply.started":"2025-04-15T04:24:49.053357Z","shell.execute_reply":"2025-04-15T04:24:49.057340Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for epoch in range(2):  # just a couple of epochs for testing\n    model.train()\n    running_loss = 0.0\n    for mel, label_strs in train_dl:\n        mel = mel.to(device)\n        label_tensor = encode_labels(label_strs).to(device).float()\n\n        optimizer.zero_grad()\n        output = model(mel)\n        loss = criterion(output, label_tensor)\n        loss.backward()\n        optimizer.step()\n\n        running_loss += loss.item()\n    \n    print(f\"Epoch {epoch+1} - Loss: {running_loss / len(train_dl):.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T01:40:33.936625Z","iopub.execute_input":"2025-04-14T01:40:33.936894Z","iopub.status.idle":"2025-04-14T01:41:09.019248Z","shell.execute_reply.started":"2025-04-14T01:40:33.936872Z","shell.execute_reply":"2025-04-14T01:41:09.018488Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for epoch in range(2):  # just a couple of epochs for testing\n    model.train()\n    running_loss = 0.0\n    for mel, label_strs in train_dl:\n        mel = mel.to(device)\n        label_tensor = encode_labels(label_strs).to(device).float()\n\n        optimizer.zero_grad()\n        output = model(mel)\n        loss = criterion(output, label_tensor)\n        loss.backward()\n        optimizer.step()\n\n        running_loss += loss.item()\n    \n    print(f\"Epoch {epoch+1} - Loss: {running_loss / len(train_dl):.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T01:41:12.630508Z","iopub.execute_input":"2025-04-14T01:41:12.631336Z","iopub.status.idle":"2025-04-14T01:41:46.631197Z","shell.execute_reply.started":"2025-04-14T01:41:12.631299Z","shell.execute_reply":"2025-04-14T01:41:46.630117Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"torch.save(model.state_dict(), \"birdclef_test_model.pth\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T01:41:54.243705Z","iopub.execute_input":"2025-04-14T01:41:54.244319Z","iopub.status.idle":"2025-04-14T01:41:54.317921Z","shell.execute_reply.started":"2025-04-14T01:41:54.244296Z","shell.execute_reply":"2025-04-14T01:41:54.317328Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from IPython.display import FileLink\n\n# Just show a clickable link\nFileLink(r'./birdclef_test_model.pth')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T01:42:15.001832Z","iopub.execute_input":"2025-04-14T01:42:15.002535Z","iopub.status.idle":"2025-04-14T01:42:15.008539Z","shell.execute_reply.started":"2025-04-14T01:42:15.002513Z","shell.execute_reply":"2025-04-14T01:42:15.007987Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport torch\nimport librosa\nimport numpy as np\nimport pandas as pd\nfrom torchvision import models\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torchvision import transforms\n\n# Set seed\nnp.random.seed(42)\n\n# Load the trained model\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# Define the model (same architecture as used for training)\nmodel = models.efficientnet_b0(pretrained=False)  # Do not load pre-trained weights\nNUM_CLASSES = 206  # Update to your actual number of classes\nmodel.classifier[1] = nn.Linear(model.classifier[1].in_features, NUM_CLASSES)\n\n# Load model weights\nmodel.load_state_dict(torch.load(\"birdclef_test_model.pth\"))\nmodel = model.to(device)\nmodel.eval()\n\n# Transform to match model input\ntransform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n])\n\n# Class labels from train audio (ensure these match the labels used during training)\nclass_labels = sorted(os.listdir('/kaggle/input/birdclef-2025/train_audio/'))\n\ntest_soundscape_path = '/kaggle/input/birdclef-2025/train_soundscapes/'\ntest_soundscapes_all = [os.path.join(test_soundscape_path, afile) \n                        for afile in sorted(os.listdir(test_soundscape_path)) if afile.endswith('.ogg')]\n\n# Use only the first 3% for testing/debugging\nnum_files = max(1, int(0.01 * len(test_soundscapes_all)))  # at least 1 file\ntest_soundscapes = test_soundscapes_all[:num_files]\n\n# DataFrame for predictions\npredictions = pd.DataFrame(columns=['row_id'] + class_labels)\n\n# Helper function to process the 5-second chunks\ndef process_audio_chunk(chunk, sr=32000):\n    # Convert chunk to Mel Spectrogram (same process as during training)\n    mel_transform = torchaudio.transforms.MelSpectrogram(\n        sample_rate=sr,\n        n_fft=2048,\n        hop_length=512,\n        n_mels=128,\n        f_min=50,\n        f_max=16000\n    ).to(device)\n    waveform = torch.tensor(chunk).unsqueeze(0).float().to(device)\n    mel = mel_transform(waveform).squeeze(0)  # (n_mels, time)\n    mel = (mel - mel.mean()) / (mel.std() + 1e-6)  # Normalize\n    mel = torch.stack([mel, mel, mel], dim=0)  # Make 3-channel image (H, W, C)\n    mel = transform(mel)  # Apply resize and normalization\n    return mel.unsqueeze(0)  # Add batch dimension\n\n# Loop through each test soundscape\nfor soundscape in test_soundscapes:\n    # Load audio file\n    sig, rate = librosa.load(path=soundscape, sr=32000)\n\n    # Split into 5-second chunks\n    chunks = [sig[i:i + rate * 5] for i in range(0, len(sig), rate * 5)]\n\n    # Predict for each chunk\n    for i, chunk in enumerate(chunks):\n        # Create row_id based on soundscape and chunk number\n        row_id = os.path.basename(soundscape).split('.')[0] + f'_{i * 5 + 5}'\n\n        # Process the chunk (convert to Mel spectrogram)\n        mel = process_audio_chunk(chunk)\n\n        # Make prediction (output from model)\n        with torch.no_grad():\n            output = model(mel)\n            probs = torch.sigmoid(output)  # Apply sigmoid to get probabilities\n\n        # Apply threshold to get the labels (0.5 is commonly used)\n        threshold = 0\n        pred_labels = [class_labels[i] for i, p in enumerate(probs[0]) if p >= threshold]\n        \n        # Create a new row for the prediction\n        new_row = pd.DataFrame([[row_id] + [1 if label in pred_labels else 0 for label in class_labels]],\n                               columns=['row_id'] + class_labels)\n        predictions = pd.concat([predictions, new_row], axis=0, ignore_index=True)\n\n# Save the predictions to CSV\npredictions.to_csv('submission.csv', index=False)\npd.read_csv('submission.csv')\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 1. Check output logits range\nwith torch.no_grad():\n    output = model(mel)\nprint(\"Logits:\", output[0][:10])  # first 10 classes\n\n# 2. Check model weights\nprint(\"Weight avg:\", next(model.parameters()).abs().mean().item())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-14T01:38:33.040683Z","iopub.status.idle":"2025-04-14T01:38:33.040932Z","shell.execute_reply.started":"2025-04-14T01:38:33.040821Z","shell.execute_reply":"2025-04-14T01:38:33.040831Z"}},"outputs":[],"execution_count":null}]}