{"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":"nvidiaTeslaT4","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 os\nINPUT_DIR = '/kaggle/input/birdclef-2025/'  # Kaggle's input directory\nOUTPUT_DIR = '/kaggle/working'             # Kaggle's output directory\n\n# Verify files in input directory\nfor dirname, _, filenames in os.walk(INPUT_DIR):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# --- Imports ---\nimport numpy as np\nimport pandas as pd\nimport librosa\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nfrom tqdm import tqdm\nfrom sklearn.model_selection import train_test_split\n\n# --- Load Competition Data ---\ntry:\n    train_df = pd.read_csv(f\"{INPUT_DIR}/train.csv\")\n    sample_submission = pd.read_csv(f\"{INPUT_DIR}/sample_submission.csv\")\nexcept:\n    # Fallback for local testing\n    train_df = pd.read_csv(\"train.csv\")\n    sample_submission = pd.read_csv(\"sample_submission.csv\")\n\n# --- Create Label Map ---\nall_labels = sorted(train_df[\"primary_label\"].unique())\nlabel_map = {label: i for i, label in enumerate(all_labels)}\nnum_classes = len(label_map)\n\n# --- Feature Extraction ---\ndef extract_melspectrogram(file_path, sr=32000, n_mels=64, duration=5, hop_length=512):\n    try:\n        y, _ = librosa.load(file_path, sr=sr, duration=duration)\n        if len(y) < duration * sr:\n            y = np.pad(y, (0, duration * sr - len(y)))\n        mel = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=n_mels, hop_length=hop_length)\n        mel_db = librosa.power_to_db(mel, ref=np.max)\n        mel_db = (mel_db - mel_db.min()) / (mel_db.max() - mel_db.min() + 1e-8)\n        return mel_db\n    except:\n        return np.zeros((n_mels, int(sr * duration / hop_length) + 1))\n\n# --- Dataset Class ---\nclass BirdDataset(Dataset):\n    def __init__(self, df, audio_dir, label_map, duration=5):\n        self.df = df\n        self.audio_dir = audio_dir\n        self.label_map = label_map\n        self.duration = duration\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        label = self.label_map[row[\"primary_label\"]]\n        file_path = os.path.join(self.audio_dir, row[\"filename\"])\n        mel = extract_melspectrogram(file_path, duration=self.duration)\n        mel_tensor = torch.tensor(mel).unsqueeze(0).float()\n        return mel_tensor, label\n\n# --- Model Definition ---\nclass ConvBlock(nn.Module):\n    def __init__(self, in_channels, out_channels):\n        super(ConvBlock, self).__init__()\n        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1, bias=False)\n        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1, bias=False)\n        self.bn1 = nn.BatchNorm2d(out_channels)\n        self.bn2 = nn.BatchNorm2d(out_channels)\n        self.relu = nn.ReLU()\n        \n    def forward(self, x):\n        x = self.relu(self.bn1(self.conv1(x)))\n        x = self.relu(self.bn2(self.conv2(x)))\n        return x\n\nclass CNN14(nn.Module):\n    def __init__(self, num_classes):\n        super(CNN14, self).__init__()\n        self.conv_block1 = ConvBlock(1, 64)\n        self.conv_block2 = ConvBlock(64, 128)\n        self.conv_block3 = ConvBlock(128, 256)\n        self.conv_block4 = ConvBlock(256, 512)\n        self.conv_block5 = ConvBlock(512, 1024)\n        self.conv_block6 = ConvBlock(1024, 2048)\n        \n        self.pool = nn.AvgPool2d(2)\n        self.final_pool = nn.AdaptiveAvgPool2d(1)\n        self.fc = nn.Linear(2048, num_classes)\n        self.sigmoid = nn.Sigmoid()\n        \n    def forward(self, x):\n        x = self.pool(self.conv_block1(x))\n        x = self.pool(self.conv_block2(x))\n        x = self.pool(self.conv_block3(x))\n        x = self.pool(self.conv_block4(x))\n        x = self.conv_block5(x)\n        x = self.conv_block6(x)\n        x = self.final_pool(x)\n        x = x.view(x.size(0), -1)\n        return self.sigmoid(self.fc(x))\n\n# --- Prediction Function ---\ndef predict(model, test_dir, sample_submission):\n    model.eval()\n    device = next(model.parameters()).device\n    results = []\n    \n    for row_id in tqdm(sample_submission[\"row_id\"], desc=\"Predicting\"):\n        file_name = row_id.split(\"_\")[0] + \"_\" + row_id.split(\"_\")[1] + \".ogg\"\n        path = os.path.join(test_dir, file_name)\n        \n        mel = extract_melspectrogram(path)\n        input_tensor = torch.tensor(mel).unsqueeze(0).unsqueeze(0).float().to(device)\n        \n        with torch.no_grad():\n            pred = model(input_tensor).cpu().numpy().flatten()\n        \n        results.append(pred)\n    \n    return np.stack(results)\n\n# --- Main Execution ---\ndef main():\n    # Prepare data\n    train_data, val_data = train_test_split(train_df, test_size=0.2, random_state=42)\n    train_dataset = BirdDataset(train_data, f\"{INPUT_DIR}/train_audio\", label_map)\n    train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)\n    \n    # Initialize model\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    model = CNN14(num_classes).to(device)\n    \n    # Train (simplified for Kaggle - use pre-trained in final submission)\n    optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)\n    criterion = nn.BCELoss()\n    \n    print(\"Starting training...\")\n    for epoch in range(3):  # Reduced epochs for Kaggle demo\n        model.train()\n        for x, y in train_loader:\n            x, y = x.to(device), F.one_hot(y, num_classes).float().to(device)\n            optimizer.zero_grad()\n            outputs = model(x)\n            loss = criterion(outputs, y)\n            loss.backward()\n            optimizer.step()\n        print(f\"Epoch {epoch+1} Loss: {loss.item():.4f}\")\n    \n    # Generate predictions\n    print(\"Generating predictions...\")\n    test_predictions = predict(model, f\"{INPUT_DIR}/test_soundscapes\", sample_submission)\n    \n    # Create submission\n    submission_df = sample_submission.copy()\n    submission_df.iloc[:, 1:] = test_predictions\n    submission_df.to_csv(f\"{OUTPUT_DIR}/submission.csv\", index=False)\n    print(\"Submission saved to /kaggle/working/submission.csv\")\n\nif __name__ == \"__main__\":\n    main()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-04-30T22:43:25.004750Z","iopub.execute_input":"2025-04-30T22:43:25.005409Z","iopub.status.idle":"2025-04-30T23:07:05.770388Z","shell.execute_reply.started":"2025-04-30T22:43:25.005382Z","shell.execute_reply":"2025-04-30T23:07:05.769721Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}