{"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,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":648057,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":488818,"modelId":504239},{"sourceId":648065,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":488824,"modelId":504245},{"sourceId":648221,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":488942,"modelId":504363}],"dockerImageVersionId":31192,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport librosa as lb\nimport soundfile as sf\nimport pandas as pd\nfrom pathlib import Path\nimport torch\nfrom torch import nn\nfrom  torch.utils.data import Dataset\nimport torchvision.models as models\nimport os\nfrom tqdm import tqdm","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-11-17T02:59:10.476666Z","iopub.execute_input":"2025-11-17T02:59:10.477298Z","iopub.status.idle":"2025-11-17T02:59:20.761858Z","shell.execute_reply.started":"2025-11-17T02:59:10.477255Z","shell.execute_reply":"2025-11-17T02:59:20.761174Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"THRESH = 0.25\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(\"DEVICE:\", device)\ntest_audio_path = Path(\"../input/birdclef-2021/test_soundscapes\")\nsample_path = \"../input/birdclef-2021/sample_submission.csv\"\ntrain_soundscapes_path = None\nif not len(list(test_audio_path.glob(\"*.ogg\"))):\n    test_audio_path = Path(\"../input/birdclef-2021/train_soundscapes\")\n    sample_path = None\n    train_soundscapes_path = Path(\"../input/birdclef-2021/train_soundscape_labels.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T03:03:00.778050Z","iopub.execute_input":"2025-11-17T03:03:00.778675Z","iopub.status.idle":"2025-11-17T03:03:00.784579Z","shell.execute_reply.started":"2025-11-17T03:03:00.778649Z","shell.execute_reply":"2025-11-17T03:03:00.783976Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class MelSpecComputer:\n    def __init__(self, sr, n_mels, fmin, fmax, **kwargs):\n        self.sr = sr\n        self.n_mels = n_mels\n        self.fmin = fmin\n        self.fmax = fmax\n        kwargs[\"n_fft\"] = kwargs.get(\"n_fft\", self.sr//10)\n        kwargs[\"hop_length\"] = kwargs.get(\"hop_length\", self.sr//(10*4))\n        self.kwargs = kwargs\n\n    def __call__(self, y):\n\n        melspec = lb.feature.melspectrogram(\n            y=y, sr=self.sr, n_mels=self.n_mels, fmin=self.fmin, fmax=self.fmax, **self.kwargs,\n        )\n\n        melspec = lb.power_to_db(melspec).astype(np.float32)\n        return melspec","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T02:59:25.139928Z","iopub.execute_input":"2025-11-17T02:59:25.140244Z","iopub.status.idle":"2025-11-17T02:59:25.145486Z","shell.execute_reply.started":"2025-11-17T02:59:25.140219Z","shell.execute_reply":"2025-11-17T02:59:25.144842Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def mono_to_color(X, eps=1e-6, mean=None, std=None):\n    mean = mean or X.mean()\n    std = std or X.std()\n    X = (X - mean) / (std + eps)\n    \n    _min, _max = X.min(), X.max()\n\n    if (_max - _min) > eps:\n        V = np.clip(X, _min, _max)\n        V = 255 * (V - _min) / (_max - _min)\n        V = V.astype(np.uint8)\n    else:\n        V = np.zeros_like(X, dtype=np.uint8)\n\n    return V\n\ndef crop_or_pad(y, length):\n    if len(y) < length:\n        y = np.concatenate([y, length - np.zeros(len(y))])\n    elif len(y) > length:\n        y = y[:length]\n    return y","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T02:59:27.549516Z","iopub.execute_input":"2025-11-17T02:59:27.549786Z","iopub.status.idle":"2025-11-17T02:59:27.557743Z","shell.execute_reply.started":"2025-11-17T02:59:27.549764Z","shell.execute_reply":"2025-11-17T02:59:27.556706Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class BirdCLEFDataset(Dataset):\n    def __init__(self, data, sr=32000, n_mels=128, fmin=0, fmax=None, duration=5, step=None, res_type=\"kaiser_fast\", resample=True):\n        \n        self.data = data\n        \n        self.sr = sr\n        self.n_mels = n_mels\n        self.fmin = fmin\n        self.fmax = fmax or self.sr//2\n\n        self.duration = duration\n        self.audio_length = self.duration*self.sr\n        self.step = step or self.audio_length\n        \n        self.res_type = res_type\n        self.resample = resample\n\n        self.mel_spec_computer = MelSpecComputer(sr=self.sr, n_mels=self.n_mels, fmin=self.fmin,\n                                                 fmax=self.fmax)\n    def __len__(self):\n        return len(self.data)\n    \n    @staticmethod\n    def normalize(image):\n        image = image.astype(\"float32\", copy=False) / 255.0\n        image = np.stack([image, image, image])\n        return image\n    \n    def audio_to_image(self, audio):\n        melspec = self.mel_spec_computer(audio) \n        image = mono_to_color(melspec)\n        image = self.normalize(image)\n        return image\n\n    def read_file(self, filepath):\n        audio, orig_sr = sf.read(filepath, dtype=\"float32\")\n\n        if self.resample and orig_sr != self.sr:\n            audio = lb.resample(y=audio, orig_sr=orig_sr, target_sr=self.sr, res_type=self.res_type)\n          \n        audios = []\n        for i in range(self.audio_length, len(audio) + self.step, self.step):\n            start = max(0, i - self.audio_length)\n            end = start + self.audio_length\n            audios.append(audio[start:end])\n            \n        if len(audios[-1]) < self.audio_length:\n            audios = audios[:-1]\n            \n        images = [self.audio_to_image(audio) for audio in audios]\n        images = np.stack(images)\n        \n        return images\n    \n        \n    def __getitem__(self, idx):\n        return self.read_file(self.data.loc[idx, \"filepath\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T02:59:29.271651Z","iopub.execute_input":"2025-11-17T02:59:29.272387Z","iopub.status.idle":"2025-11-17T02:59:29.280267Z","shell.execute_reply.started":"2025-11-17T02:59:29.272352Z","shell.execute_reply":"2025-11-17T02:59:29.279677Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_net_universal(checkpoint_path, num_classes=397):\n    net = models.efficientnet_b2(weights=None)\n    net.classifier = nn.Linear(net.classifier[1].in_features, num_classes)\n    \n    dummy_device = torch.device(\"cpu\")\n    checkpoint = torch.load(checkpoint_path, map_location=dummy_device)\n    \n    filename = Path(checkpoint_path).name\n    \n    if 'model_state_dict' in checkpoint:\n        state_dict = checkpoint['model_state_dict']\n        print(f\"Loading new format: {filename}\")\n        print(f\"   Epoch: {checkpoint.get('epoch', 'N/A')}, Metric: {checkpoint.get('metric', 'N/A')}\")\n    else:\n        state_dict = checkpoint\n        print(f\"Loading old format: {filename}\")\n    \n    state_dict = {k.replace('model.', ''): v for k, v in state_dict.items()}\n    \n    net.load_state_dict(state_dict)\n    net = net.to(device)\n    net = net.eval()\n    \n    return net","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"@torch.no_grad()\ndef get_thresh_preds(out, thresh=None):\n    thresh = thresh or THRESH\n    o = (-out).argsort(1)\n    npreds = (out > thresh).sum(1)\n    preds = []\n    for oo, npred in zip(o, npreds):\n        preds.append(oo[:npred].cpu().numpy().tolist())\n    return preds\n\ndef get_bird_names(preds):\n    bird_names = []\n    for pred in preds:\n        if not pred:\n            bird_names.append(\"nocall\")\n        else:\n            bird_names.append(\" \".join([inv_label_ids[bird_id] for bird_id in pred]))\n    return bird_names\n\ndef predict(nets, test_data, names=True):\n    preds = []\n    with torch.no_grad():\n        for idx in  tqdm(list(range(len(test_data)))):\n            xb = torch.from_numpy(test_data[idx]).to(device)\n            pred = 0.\n            for net in nets:\n                o = net(xb)\n                o = torch.sigmoid(o)\n\n                pred += o\n\n            pred /= len(nets)\n            \n            if names:\n                pred = get_bird_names(get_thresh_preds(pred))\n\n            preds.append(pred)\n    return preds","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def preds_as_df(data, preds):\n    sub = {\n        \"row_id\": [],\n        \"birds\": [],\n    }\n    \n    for row, pred in zip(data.itertuples(False), preds):\n        row_id = [f\"{row.id}_{row.site}_{5*i}\" for i in range(1, len(pred)+1)]\n        sub[\"birds\"] += pred\n        sub[\"row_id\"] += row_id\n        \n    sub = pd.DataFrame(sub)\n    \n    if sample_path:\n        sample_sub = pd.read_csv(sample_path, usecols=[\"row_id\"])\n        sub = sample_sub.merge(sub, on=\"row_id\", how=\"left\")\n        sub[\"birds\"] = sub[\"birds\"].fillna(\"nocall\")\n    return sub","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data = pd.DataFrame(\n     [(path.stem, *path.stem.split(\"_\"), path) for path in Path(test_audio_path).glob(\"*.ogg\")],\n    columns = [\"filename\", \"id\", \"site\", \"date\", \"filepath\"]\n)\nprint(data.shape)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_train = pd.read_csv(\"../input/birdclef-2021/train_metadata.csv\")\n\nlabel_ids = {label: label_id for label_id,label in enumerate(sorted(df_train[\"primary_label\"].unique()))}\ninv_label_ids = {val: key for key,val in label_ids.items()}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T02:59:34.479473Z","iopub.execute_input":"2025-11-17T02:59:34.480010Z","iopub.status.idle":"2025-11-17T02:59:34.916230Z","shell.execute_reply.started":"2025-11-17T02:59:34.479983Z","shell.execute_reply":"2025-11-17T02:59:34.915428Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_data = BirdCLEFDataset(data=data)\nlen(test_data), test_data[0].shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T02:59:35.897588Z","iopub.execute_input":"2025-11-17T02:59:35.897859Z","iopub.status.idle":"2025-11-17T02:59:51.470635Z","shell.execute_reply.started":"2025-11-17T02:59:35.897837Z","shell.execute_reply":"2025-11-17T02:59:51.469871Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ансамбль моделей, обученных на разных папках с данными\ncheckpoint_paths = [\n    Path('/kaggle/input/bird-model-fold3-epoch23/pytorch/default/1/birdclef_efficientnet_fold3_epoch23_0.7138.pth'),\n    Path('/kaggle/input/modelele/pytorch/default/1/birdclef_efficientnet_fold0_epoch_24_f1_val_07082_20251116210814.pth')\n]\n\n\nnets = [\n        load_net_universal(checkpoint_path.as_posix()) for checkpoint_path in checkpoint_paths\n]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T02:59:59.970637Z","iopub.execute_input":"2025-11-17T02:59:59.970907Z","iopub.status.idle":"2025-11-17T03:00:02.292620Z","shell.execute_reply.started":"2025-11-17T02:59:59.970887Z","shell.execute_reply":"2025-11-17T03:00:02.291805Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pred_probas = predict(nets, test_data, names=False)\nprint(len(pred_probas))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T03:00:14.421233Z","iopub.execute_input":"2025-11-17T03:00:14.421489Z","iopub.status.idle":"2025-11-17T03:00:59.885272Z","shell.execute_reply.started":"2025-11-17T03:00:14.421470Z","shell.execute_reply":"2025-11-17T03:00:59.884282Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"preds = [get_bird_names(get_thresh_preds(pred, thresh=THRESH)) for pred in pred_probas]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T03:03:09.474189Z","iopub.execute_input":"2025-11-17T03:03:09.474906Z","iopub.status.idle":"2025-11-17T03:03:09.552347Z","shell.execute_reply.started":"2025-11-17T03:03:09.474879Z","shell.execute_reply":"2025-11-17T03:03:09.551770Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# if train_soundscapes_path and Path(train_soundscapes_path).exists():\n#     true_labels_df = pd.read_csv(train_soundscapes_path)\n    \n#     print(\"Columns in true_labels_df:\", true_labels_df.columns.tolist())\n#     print(\"Columns in data:\", data.columns.tolist())\n    \n#     print(\"\\n=== DEBUG INFO ===\")\n#     print(\"First 5 filenames in data:\")\n#     for i in range(min(5, len(data))):\n#         print(f\"  {data.loc[i, 'filename']}\")\n    \n#     print(\"\\nFirst 5 audio_id in true_labels_df:\")\n#     for audio_id in true_labels_df['audio_id'].unique()[:5]:\n#         print(f\"  {audio_id}\")\n    \n#     correct = 0\n#     total = 0\n#     matched_files = 0\n    \n#     for idx, pred_birds_list in enumerate(preds):\n#         filename = data.loc[idx, \"filename\"]\n#         audio_id = int(filename.split('_')[0])\n        \n#         if idx < 3:\n#             print(f\"\\nChecking file {idx}: {filename} -> audio_id: {audio_id}\")\n        \n#         true_labels = true_labels_df[true_labels_df['audio_id'] == audio_id]\n        \n#         if len(true_labels) == 0:\n#             if idx < 3:\n#                 print(f\"  No true labels found for audio_id: {audio_id}\")\n#             continue\n#         else:\n#             matched_files += 1\n#             if idx < 3:\n#                 print(f\"  Found {len(true_labels)} true labels\")\n#                 print(f\"  First few true labels:\")\n#                 print(true_labels.head(3)[['audio_id', 'seconds', 'birds']])\n        \n#         # для каждого 5-секундного сегмента\n#         for seg_idx, pred_birds in enumerate(pred_birds_list):\n#             seconds = seg_idx * 5\n            \n#             true_row = true_labels[true_labels['seconds'] == seconds]\n            \n#             if len(true_row) > 0:\n#                 true_birds = true_row['birds'].values[0]\n                \n#                 pred_set = set(pred_birds.split()) if pred_birds != \"nocall\" else set()\n#                 true_set = set(true_birds.split()) if true_birds != \"nocall\" else set()\n                \n#                 if idx < 3 and seg_idx < 3:  # DEBUG первые сегменты\n#                     print(f\"  Segment {seconds}s: Pred={pred_set}, True={true_set}, Match={pred_set == true_set}\")\n                \n#                 if pred_set == true_set:\n#                     correct += 1\n#                 total += 1\n    \n#     print(f\"\\n=== FINAL RESULTS ===\")\n#     print(f\"Files matched: {matched_files}/{len(data)}\")\n#     print(f\"Segments matched: {correct}/{total}\")\n#     if total > 0:\n#         print(f\"Accuracy: {correct/total:.4f}\")\n#     else:\n#         print(\"No segments matched - check audio_id conversion!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-17T03:03:16.436657Z","iopub.execute_input":"2025-11-17T03:03:16.437259Z","iopub.status.idle":"2025-11-17T03:03:17.027455Z","shell.execute_reply.started":"2025-11-17T03:03:16.437232Z","shell.execute_reply":"2025-11-17T03:03:17.026847Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sub = preds_as_df(data, preds)\nprint(sub.shape)\nsub","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sub.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}