{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":25954,"databundleVersionId":2091745,"sourceType":"competition"},{"sourceId":1297722,"sourceType":"datasetVersion","datasetId":750498},{"sourceId":2130303,"sourceType":"datasetVersion","datasetId":1278322},{"sourceId":286427604,"sourceType":"kernelVersion"}],"dockerImageVersionId":31193,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import shutil\nimport os\nimport numpy as np\nimport pandas as pd\nimport librosa\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom sklearn.preprocessing import LabelEncoder\nfrom tqdm import tqdm\n\nRESNEST_SOURCE_DIR = '../input/resnest50-fast-package/resnest-0.0.6b20200701'\nRESNEST_DEST_DIR = 'resnest_pkg'\n\nprint(\"Attempting to install ResNeSt using directory copy...\")\n\ntry:\n    if os.path.exists(RESNEST_DEST_DIR):\n        print(f\"Cleaning up existing directory: {RESNEST_DEST_DIR}\")\n        shutil.rmtree(RESNEST_DEST_DIR)\n        \n    shutil.copytree(os.path.join(RESNEST_SOURCE_DIR, 'resnest'), RESNEST_DEST_DIR)\n    \n    os.system(f'pip install \"./{RESNEST_DEST_DIR}\" --no-deps')\n    print(\"ResNeSt installed successfully.\")\n\nexcept Exception as e:\n    print(f\"Failed to install ResNeSt. Final check on the dataset structure is needed if this fails again. Error: {e}\")\n\nfrom resnest.torch import resnest50\n\nSR = 32000\nDURATION = 5\nTHRESHOLD = 0.28 \nBATCH_SIZE = 64\n\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nDATA_ROOT = \"../input/birdclef-2021\"\nTEST_AUDIO = os.path.join(DATA_ROOT, \"test_soundscapes\")\nWEIGHTS = \"../input/kkiller-birdclef-models-public/birdclef_resnest50_fold0_epoch_10_f1_val_06471_20210417161101.pth\"\n\nprint(f\"Device: {DEVICE}\")\nprint(f\"Threshold: {THRESHOLD}\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:09:57.678318Z","iopub.execute_input":"2025-12-16T01:09:57.678579Z","iopub.status.idle":"2025-12-16T01:10:07.767579Z","shell.execute_reply.started":"2025-12-16T01:09:57.678561Z","shell.execute_reply":"2025-12-16T01:10:07.766776Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_meta = pd.read_csv(os.path.join(DATA_ROOT, \"train_metadata.csv\"))\nspecies = sorted(train_meta[\"primary_label\"].unique())\nencoder = LabelEncoder().fit(species)\nNUM_CLASSES = len(species)\n\nprint(f\"Species count: {NUM_CLASSES}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:10:07.769292Z","iopub.execute_input":"2025-12-16T01:10:07.769632Z","iopub.status.idle":"2025-12-16T01:10:08.096626Z","shell.execute_reply.started":"2025-12-16T01:10:07.769615Z","shell.execute_reply":"2025-12-16T01:10:08.096001Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def audio_to_melspec(audio, sr=SR):\n    mel = librosa.feature.melspectrogram(\n        y=audio, sr=sr, n_mels=128, fmin=300, fmax=sr//2,\n        n_fft=sr//10, hop_length=sr//40\n    )\n    log_mel = librosa.power_to_db(mel, ref=np.max)\n    \n    mean, std = log_mel.mean(), log_mel.std()\n    normalized = (log_mel - mean) / (std + 1e-8)\n    \n    vmin, vmax = normalized.min(), normalized.max()\n    if vmax - vmin > 1e-6:\n        scaled = 255 * (normalized - vmin) / (vmax - vmin)\n    else:\n        scaled = np.zeros_like(normalized)\n    \n    return scaled.astype(np.uint8)\n\ndef to_rgb(spec):\n    return np.stack([spec] * 3, axis=0).astype(np.float32) / 255.0","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:10:08.097381Z","iopub.execute_input":"2025-12-16T01:10:08.097646Z","iopub.status.idle":"2025-12-16T01:10:08.103457Z","shell.execute_reply.started":"2025-12-16T01:10:08.097622Z","shell.execute_reply":"2025-12-16T01:10:08.102744Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class SoundscapeDataset(Dataset):\n    def __init__(self, df, audio_dir, sr=SR, duration=DURATION):\n        self.df = df.reset_index(drop=True)\n        self.audio_dir = audio_dir\n        self.sr = sr\n        self.duration = duration\n        self.cache = {}\n    \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        row_id = row[\"row_id\"]\n        \n        file_id = \"_\".join(row_id.split(\"_\")[:2])\n        end_time = int(row_id.split(\"_\")[-1])\n        \n        try:\n            if file_id not in self.cache:\n                filename = next(f for f in os.listdir(self.audio_dir) if f.startswith(file_id))\n                audio, orig_sr = librosa.load(\n                    os.path.join(self.audio_dir, filename), \n                    sr=None, res_type='kaiser_fast'\n                )\n                if orig_sr != self.sr:\n                    audio = librosa.resample(audio, orig_sr=orig_sr, target_sr=self.sr)\n                self.cache[file_id] = audio\n            \n            audio = self.cache[file_id]\n            \n            start = max(0, (end_time - self.duration) * self.sr)\n            end = min(len(audio), end_time * self.sr)\n            segment = audio[start:end]\n            \n            if len(segment) < self.duration * self.sr:\n                segment = np.pad(segment, (0, self.duration * self.sr - len(segment)))\n            \n            spec = audio_to_melspec(segment, self.sr)\n            return to_rgb(spec)\n        \n        except:\n            return np.zeros((3, 128, 313), dtype=np.float32)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:10:08.104844Z","iopub.execute_input":"2025-12-16T01:10:08.105088Z","iopub.status.idle":"2025-12-16T01:10:08.120665Z","shell.execute_reply.started":"2025-12-16T01:10:08.105071Z","shell.execute_reply":"2025-12-16T01:10:08.119773Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_model(weights_path, num_classes):\n    model = resnest50(pretrained=False)\n    model.fc = torch.nn.Linear(model.fc.in_features, num_classes)\n    \n    state = torch.load(weights_path, map_location=\"cpu\")\n    state = {k.replace(\"model.\", \"\"): v for k, v in state.items()}\n    model.load_state_dict(state)\n    \n    model.to(DEVICE)\n    model.eval()\n    return model\n\nmodel = load_model(WEIGHTS, NUM_CLASSES)\nprint(\"Model loaded\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:10:08.121430Z","iopub.execute_input":"2025-12-16T01:10:08.121699Z","iopub.status.idle":"2025-12-16T01:10:10.383949Z","shell.execute_reply.started":"2025-12-16T01:10:08.121675Z","shell.execute_reply":"2025-12-16T01:10:10.383077Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"@torch.no_grad()\ndef predict_batch(batch, model, threshold=THRESHOLD):\n    inputs = torch.from_numpy(batch).to(DEVICE)\n    logits = model(inputs)\n    probs = torch.sigmoid(logits).cpu().numpy()\n    \n    predictions = []\n    for prob in probs:\n        indices = np.where(prob > threshold)[0]\n        if len(indices) == 0:\n            predictions.append(\"nocall\")\n        else:\n            labels = encoder.inverse_transform(indices)\n            predictions.append(\" \".join(sorted(labels)))\n    \n    return predictions","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:10:10.384775Z","iopub.execute_input":"2025-12-16T01:10:10.385111Z","iopub.status.idle":"2025-12-16T01:10:10.390590Z","shell.execute_reply.started":"2025-12-16T01:10:10.385091Z","shell.execute_reply":"2025-12-16T01:10:10.389777Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df = pd.read_csv(os.path.join(DATA_ROOT, \"test.csv\"))\n\nif len(test_df) < 10:\n    print(\"Using train_soundscapes for testing\")\n    test_df = pd.read_csv(os.path.join(DATA_ROOT, \"train_soundscape_labels.csv\"))\n    audio_dir = os.path.join(DATA_ROOT, \"train_soundscapes\")\nelse:\n    audio_dir = TEST_AUDIO\n\nprint(f\"Segments to process: {len(test_df)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:10:10.392493Z","iopub.execute_input":"2025-12-16T01:10:10.392971Z","iopub.status.idle":"2025-12-16T01:10:10.416785Z","shell.execute_reply.started":"2025-12-16T01:10:10.392953Z","shell.execute_reply":"2025-12-16T01:10:10.416247Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dataset = SoundscapeDataset(test_df, audio_dir)\nloader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=0)\n\nall_predictions = []\n\nprint(\"Starting inference...\")\nfor batch in tqdm(loader, desc=\"Processing\"):\n    batch_array = np.stack([b.numpy() for b in batch])\n    preds = predict_batch(batch_array, model, THRESHOLD)\n    all_predictions.extend(preds)\n\nprint(\"Inference complete\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:10:10.417473Z","iopub.execute_input":"2025-12-16T01:10:10.417839Z","iopub.status.idle":"2025-12-16T01:11:17.127129Z","shell.execute_reply.started":"2025-12-16T01:10:10.417814Z","shell.execute_reply":"2025-12-16T01:11:17.126258Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = pd.DataFrame({\n    \"row_id\": test_df[\"row_id\"],\n    \"birds\": all_predictions\n})\n\nsubmission.to_csv(\"submission.csv\", index=False)\n\nprint(\"Saved to submission.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:11:17.127950Z","iopub.execute_input":"2025-12-16T01:11:17.128360Z","iopub.status.idle":"2025-12-16T01:11:17.141458Z","shell.execute_reply.started":"2025-12-16T01:11:17.128342Z","shell.execute_reply":"2025-12-16T01:11:17.140747Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if os.path.exists('resnest_pkg'):\n    shutil.rmtree('resnest_pkg')\n    print(\"Cleaned up resnest_pkg directory\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-16T01:11:17.142274Z","iopub.execute_input":"2025-12-16T01:11:17.142584Z","iopub.status.idle":"2025-12-16T01:11:17.150964Z","shell.execute_reply.started":"2025-12-16T01:11:17.142559Z","shell.execute_reply":"2025-12-16T01:11:17.150070Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}