{"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":"none","dataSources":[{"sourceId":91844,"databundleVersionId":11361821,"sourceType":"competition"},{"sourceId":11591275,"sourceType":"datasetVersion","datasetId":7260258},{"sourceId":233608968,"sourceType":"kernelVersion"},{"sourceId":234997499,"sourceType":"kernelVersion"}],"dockerImageVersionId":31012,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import timm\nimport torch.nn as nn\nimport torch\nimport librosa\nimport numpy as np\nimport os\nfrom pathlib import Path\nimport pandas as pd\nimport glob","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-15T08:32:27.250213Z","iopub.execute_input":"2025-04-15T08:32:27.250566Z","iopub.status.idle":"2025-04-15T08:32:41.335902Z","shell.execute_reply.started":"2025-04-15T08:32:27.250541Z","shell.execute_reply":"2025-04-15T08:32:41.334701Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nprint(os.listdir(\"/kaggle/input/birdclef-2025/test_soundscapes\")[:5])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-15T08:32:41.337502Z","iopub.execute_input":"2025-04-15T08:32:41.338166Z","iopub.status.idle":"2025-04-15T08:32:41.346797Z","shell.execute_reply.started":"2025-04-15T08:32:41.338142Z","shell.execute_reply":"2025-04-15T08:32:41.345292Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class BirdCLEFNet(nn.Module):\n    def __init__(self, num_classes):\n        super().__init__()\n        self.backbone = timm.create_model(\"tf_efficientnetv2_s\", pretrained=False, in_chans=1)\n        self.backbone.global_pool = nn.Identity()\n        self.pooling = nn.AdaptiveAvgPool2d(1)\n        self.classifier = nn.Linear(self.backbone.num_features, num_classes)\n    \n    def forward(self, x):\n        x = self.backbone.forward_features(x)  # [B, C, H, W]\n        x = self.pooling(x).squeeze(-1).squeeze(-1)  # [B, C]\n        x = self.classifier(x)  # [B, num_classes]\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-15T08:32:41.348228Z","iopub.execute_input":"2025-04-15T08:32:41.348742Z","iopub.status.idle":"2025-04-15T08:32:41.420047Z","shell.execute_reply.started":"2025-04-15T08:32:41.348708Z","shell.execute_reply":"2025-04-15T08:32:41.418901Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ckpt_path = \"/kaggle/input/gba-filtered-checkpoint-random-5-sec-sample/checkpoints/baseline_v1_gba_clean_softsec_epoch5_auc0.96300.pth\"\nnum_classes = 206\nmodel = BirdCLEFNet(num_classes)\nmodel.load_state_dict(torch.load(ckpt_path, map_location=\"cpu\"))\nmodel.eval()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-15T08:32:41.422459Z","iopub.execute_input":"2025-04-15T08:32:41.422809Z","iopub.status.idle":"2025-04-15T08:32:43.752881Z","shell.execute_reply.started":"2025-04-15T08:32:41.422785Z","shell.execute_reply":"2025-04-15T08:32:43.752046Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"SR = 32000\nN_MELS = 128\nTARGET_T = 309\n\ndef audio_to_logmelspec_segment(audio, sr=SR, n_mels=N_MELS, fmin=20, fmax=16000):\n    mel = librosa.feature.melspectrogram(\n        y=audio,\n        sr=sr,\n        n_fft=2048,\n        hop_length=512,\n        n_mels=n_mels,\n        fmin=fmin,\n        fmax=fmax,\n    )\n    logmel = librosa.power_to_db(mel).astype(np.float32)\n    return logmel\n    \n\ndef pad_or_crop(logmel, target_len=TARGET_T):\n    _, t = logmel.shape\n    if t < target_len:\n        pad_width = target_len - t\n        logmel = np.pad(logmel, ((0, 0), (0, pad_width)), mode='constant')\n    else:\n        logmel = logmel[:, :target_len]\n    return logmel\n\n\ndef prepare_input_for_model(audio_segment, sr=SR):\n    if len(audio_segment) < sr * 5:\n        pad_width = sr * 5 - len(audio_segment)\n        audio_segment = np.pad(audio_segment, (0, pad_width), mode='constant')\n    \n    logmel = audio_to_logmelspec_segment(audio_segment, sr=sr)\n    logmel = pad_or_crop(logmel)\n    tensor = torch.tensor(logmel, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n    return tensor  # [1, 1, 128, 313]\n\n\n\ndef predict_for_soundscape(audio_path: str, model, device=\"cpu\"):\n    model.eval()\n    y, _ = librosa.load(audio_path, sr=SR, mono=True)\n    duration = librosa.get_duration(y=y, sr=SR)\n\n    predictions = []\n    row_ids = []\n\n    filename = Path(audio_path).stem  # already 'soundscape_123456'\n\n    for t_start in range(0, int(duration), 5):\n        t_end = t_start + 5\n        segment = y[t_start * SR : t_end * SR]\n\n        x = prepare_input_for_model(segment).to(device)\n\n        with torch.no_grad():\n            probs = torch.sigmoid(model(x)).cpu().numpy().flatten()\n        \n        row_id = f\"{filename}_{t_end}\"\n        \n        predictions.append(probs)\n        row_ids.append(row_id)\n\n    return row_ids, predictions","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-15T08:32:43.754049Z","iopub.execute_input":"2025-04-15T08:32:43.754302Z","iopub.status.idle":"2025-04-15T08:32:43.765397Z","shell.execute_reply.started":"2025-04-15T08:32:43.754283Z","shell.execute_reply":"2025-04-15T08:32:43.764362Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\nimport pandas as pd\n\ndef get_audio_files(\n    test_dir=\"/kaggle/input/birdclef-2025/test_soundscapes\",\n    fallback_dir=\"/kaggle/input/birdclef-2025/train_soundscapes\",\n    fallback_limit=8\n):\n    test_files = glob.glob(f\"{test_dir}/*.ogg\")\n    \n    if len(test_files) > 0:\n        print(\"🚀 Using real test data from test_soundscapes/\")\n        return test_files\n    else:\n        print(\"🧪 Test data not available — falling back to train_soundscapes/ for debugging.\")\n        fallback_files = sorted(glob.glob(f\"{fallback_dir}/*.ogg\"))[:fallback_limit]\n        return fallback_files","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-18T15:20:37.030152Z","iopub.execute_input":"2025-04-18T15:20:37.030455Z","iopub.status.idle":"2025-04-18T15:20:39.129355Z","shell.execute_reply.started":"2025-04-18T15:20:37.030425Z","shell.execute_reply":"2025-04-18T15:20:39.128430Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def generate_submission(\n    model, \n    audio_files, \n    taxonomy_csv: str, \n    device=\"cpu\", \n    output_path=\"submission.csv\"\n):\n    # Загрузка списка видов\n    taxonomy = pd.read_csv(taxonomy_csv)\n    species_ids = taxonomy[\"primary_label\"].tolist()\n\n    all_row_ids = []\n    all_probs = []\n\n    for audio_path in audio_files:\n        row_ids, predictions = predict_for_soundscape(str(audio_path), model, device=device)\n        all_row_ids.extend(row_ids)\n        all_probs.extend(predictions)\n\n    # Создание submission DataFrame\n    submission_df = pd.DataFrame(all_probs, columns=species_ids)\n    submission_df.insert(0, \"row_id\", all_row_ids)\n\n    submission_df.to_csv(output_path, index=False)\n    print(f\"✅ Submission saved to {output_path} with shape {submission_df.shape}\")\n    return submission_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-15T08:32:43.788472Z","iopub.execute_input":"2025-04-15T08:32:43.788775Z","iopub.status.idle":"2025-04-15T08:32:43.800597Z","shell.execute_reply.started":"2025-04-15T08:32:43.788755Z","shell.execute_reply":"2025-04-15T08:32:43.799612Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TAXONOMY_CSV = \"/kaggle/input/birdclef-2025/taxonomy.csv\"\nDEVICE = \"cpu\"\n\naudio_files = get_audio_files()\nsubmission_df = generate_submission(model, audio_files=audio_files, taxonomy_csv=TAXONOMY_CSV, device=DEVICE)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nsubmission_df = pd.read_csv(\"submission.csv\")\nsubmission_df.head()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nsample_df = pd.read_csv(\"/kaggle/input/birdclef-2025/sample_submission.csv\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample_df","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"assert submission_df.shape[1] == sample_df.shape[1], f\"❌ Submission must have {sample_df.shape[1]} columns (got {submission_df.shape[1]})\"\nassert submission_df.columns.tolist() == sample_df.columns.tolist(), \"❌ Submission columns must match sample_submission.csv exactly\"\nassert submission_df[\"row_id\"].is_unique, \"❌ row_id must be unique\"\nassert submission_df.isnull().sum().sum() == 0, \"❌ No NaNs allowed\"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}