{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","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":[{"sourceType":"competition","sourceId":91844,"databundleVersionId":11361821},{"sourceType":"datasetVersion","sourceId":11060723,"datasetId":6891568,"databundleVersionId":11447259},{"sourceType":"datasetVersion","sourceId":11694685,"datasetId":7293662,"databundleVersionId":12169808},{"sourceType":"kernelVersion","sourceId":238601016}],"dockerImageVersionId":31012,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false},"papermill":{"default_parameters":{},"duration":30.256259,"end_time":"2025-05-02T14:06:51.666412","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2025-05-02T14:06:21.410153","version":"2.6.0"},"widgets":{"application/vnd.jupyter.widget-state+json":{"state":{"0d4c7796211447679b76fd7055b51557":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_95188e77c3834197ac8f3f802f57a0a7","placeholder":"​","style":"IPY_MODEL_9e687877fcc249459bea9860e9a3db6e","tabbable":null,"tooltip":null,"value":" 0/0 [00:00&lt;?, ?it/s]"}},"1f7090d328db4084b80bf699aaf6516c":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_a6169a3f1055413fa179eab359d93fa7","placeholder":"​","style":"IPY_MODEL_cff11d4c06404421aaecc98a3a9e34ec","tabbable":null,"tooltip":null,"value":""}},"6144dc8346b54e03bb814a9e7a3fed27":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"82ad6bd288af45719adaa8873334aea2":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"FloatProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"ProgressView","bar_style":"success","description":"","description_allow_html":false,"layout":"IPY_MODEL_ed790e5c9f81477eaedad2829def4f84","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_6144dc8346b54e03bb814a9e7a3fed27","tabbable":null,"tooltip":null,"value":0}},"95188e77c3834197ac8f3f802f57a0a7":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"9e687877fcc249459bea9860e9a3db6e":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"a6169a3f1055413fa179eab359d93fa7":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"b2331f2e4c0a4ee5962811332469ea3a":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_1f7090d328db4084b80bf699aaf6516c","IPY_MODEL_82ad6bd288af45719adaa8873334aea2","IPY_MODEL_0d4c7796211447679b76fd7055b51557"],"layout":"IPY_MODEL_c36ca83537d8426fa92499e13a073019","tabbable":null,"tooltip":null}},"c36ca83537d8426fa92499e13a073019":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"cff11d4c06404421aaecc98a3a9e34ec":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"ed790e5c9f81477eaedad2829def4f84":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":"20px"}}},"version_major":2,"version_minor":0}}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"9745aa20","cell_type":"markdown","source":"# **BirdCLEF 2025 Inference Notebook**\nThis notebook is a copy of https://www.kaggle.com/code/kadircandrisolu/efficientnet-b0-pytorch-inference-birdclef-25 \n\n**Features**\n- Audio Preprocessing\n- Test-Time Augmentation (TTA)","metadata":{"papermill":{"duration":0.004423,"end_time":"2025-05-02T14:06:25.377908","exception":false,"start_time":"2025-05-02T14:06:25.373485","status":"completed"},"tags":[]}},{"id":"db555c05","cell_type":"code","source":"import os\nimport gc\nimport warnings\nimport logging\nimport time\nimport math\nimport cv2\nfrom pathlib import Path\n\nimport numpy as np\nimport pandas as pd\nimport librosa\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport timm\nfrom tqdm.auto import tqdm\n\nwarnings.filterwarnings(\"ignore\")\nlogging.basicConfig(level=logging.ERROR)","metadata":{"execution":{"iopub.status.busy":"2025-05-09T01:20:27.901012Z","iopub.execute_input":"2025-05-09T01:20:27.901223Z","iopub.status.idle":"2025-05-09T01:20:44.891958Z","shell.execute_reply.started":"2025-05-09T01:20:27.901203Z","shell.execute_reply":"2025-05-09T01:20:44.891144Z"},"papermill":{"duration":18.267627,"end_time":"2025-05-02T14:06:43.652030","exception":false,"start_time":"2025-05-02T14:06:25.384403","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"c62f24b1","cell_type":"code","source":"class CFG:\n \n    test_soundscapes = '/kaggle/input/birdclef-2025/test_soundscapes'\n    submission_csv = '/kaggle/input/birdclef-2025/sample_submission.csv'\n    taxonomy_csv = '/kaggle/input/birdclef-2025/taxonomy.csv'\n    model_path = '/kaggle/input/try-quy-code'  \n    \n    # Audio parameters\n    FS = 32000  \n    WINDOW_SIZE = 5  \n    \n    # Mel spectrogram parameters\n    TARGET_SHAPE = (224, 224) \n    \n    N_FFT = 2048\n    HOP_LENGTH = 512\n    WIN_LENGTH = 2048\n    N_MELS = 224\n    FMIN = 50\n    FMAX = 14000\n    \n    model_name = 'efficientnet_b0'\n    in_channels = 1\n    device = 'cpu'  \n    \n    # Inference parameters\n    batch_size = 16\n    use_tta = False  \n    tta_count = 3   \n    threshold = 0.5\n    \n    use_specific_folds = False  # If False, use all found models\n    folds = [0, 1]  # Used only if use_specific_folds is True\n    \n    debug = False\n    debug_count = 3\n\ncfg = CFG()","metadata":{"execution":{"iopub.status.busy":"2025-05-09T01:20:44.894018Z","iopub.execute_input":"2025-05-09T01:20:44.894324Z","iopub.status.idle":"2025-05-09T01:20:44.900690Z","shell.execute_reply.started":"2025-05-09T01:20:44.894304Z","shell.execute_reply":"2025-05-09T01:20:44.899599Z"},"papermill":{"duration":0.012856,"end_time":"2025-05-02T14:06:43.668755","exception":false,"start_time":"2025-05-02T14:06:43.655899","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"d0eab71e","cell_type":"code","source":"print(f\"Using device: {cfg.device}\")\nprint(f\"Loading taxonomy data...\")\ntaxonomy_df = pd.read_csv(cfg.taxonomy_csv)\nspecies_ids = taxonomy_df['primary_label'].tolist()\nnum_classes = len(species_ids)\nprint(f\"Number of classes: {num_classes}\")","metadata":{"execution":{"iopub.status.busy":"2025-05-09T01:20:44.901666Z","iopub.execute_input":"2025-05-09T01:20:44.901972Z","iopub.status.idle":"2025-05-09T01:20:44.950396Z","shell.execute_reply.started":"2025-05-09T01:20:44.901950Z","shell.execute_reply":"2025-05-09T01:20:44.949517Z"},"papermill":{"duration":0.035199,"end_time":"2025-05-02T14:06:43.707788","exception":false,"start_time":"2025-05-02T14:06:43.672589","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"4330b419","cell_type":"code","source":"class BirdCLEFModel(nn.Module):\n    def __init__(self, cfg, num_classes):\n        super().__init__()\n        self.cfg = cfg\n        \n        self.backbone = timm.create_model(\n            cfg.model_name,\n            pretrained=False,  \n            in_chans=cfg.in_channels,\n            drop_rate=0.0,    \n            drop_path_rate=0.0\n        )\n        \n        if 'efficientnet' in cfg.model_name:\n            backbone_out = self.backbone.classifier.in_features\n            self.backbone.classifier = nn.Identity()\n        elif 'resnet' in cfg.model_name:\n            backbone_out = self.backbone.fc.in_features\n            self.backbone.fc = nn.Identity()\n        else:\n            backbone_out = self.backbone.get_classifier().in_features\n            self.backbone.reset_classifier(0, '')\n        \n        self.pooling = nn.AdaptiveAvgPool2d(1)\n        self.feat_dim = backbone_out\n        self.classifier = nn.Linear(backbone_out, num_classes)\n        \n    def forward(self, x):\n        features = self.backbone(x)\n        \n        if isinstance(features, dict):\n            features = features['features']\n            \n        if len(features.shape) == 4:\n            features = self.pooling(features)\n            features = features.view(features.size(0), -1)\n        \n        logits = self.classifier(features)\n        return logits\n","metadata":{"execution":{"iopub.status.busy":"2025-05-09T01:20:44.951282Z","iopub.execute_input":"2025-05-09T01:20:44.951612Z","iopub.status.idle":"2025-05-09T01:20:44.960007Z","shell.execute_reply.started":"2025-05-09T01:20:44.951588Z","shell.execute_reply":"2025-05-09T01:20:44.958882Z"},"papermill":{"duration":0.01462,"end_time":"2025-05-02T14:06:43.726163","exception":false,"start_time":"2025-05-02T14:06:43.711543","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"09d7156a","cell_type":"code","source":"def audio2melspec(audio_data, cfg):\n    \"\"\"Convert audio data to mel spectrogram\"\"\"\n    if np.isnan(audio_data).any():\n        mean_signal = np.nanmean(audio_data)\n        audio_data = np.nan_to_num(audio_data, nan=mean_signal)\n\n    mel_spec = librosa.feature.melspectrogram(\n        y=audio_data,\n        sr=cfg.FS,\n        n_fft=cfg.N_FFT,\n        hop_length=cfg.HOP_LENGTH,\n        win_length=cfg.WIN_LENGTH,\n        n_mels=cfg.N_MELS,\n        fmin=cfg.FMIN,\n        fmax=cfg.FMAX,\n        power=2.0,\n        center=True,\n        pad_mode=\"reflect\",\n        norm='slaney',\n        htk=True,\n    )\n\n    mel_spec_db = librosa.power_to_db(mel_spec, ref=np.max)\n    mel_spec_norm = (mel_spec_db - mel_spec_db.min()) / (mel_spec_db.max() - mel_spec_db.min() + 1e-8)\n    \n    return mel_spec_norm\n\ndef process_audio_segment(audio_data, cfg):\n    \"\"\"Process audio segment to get mel spectrogram\"\"\"\n    if len(audio_data) < cfg.FS * cfg.WINDOW_SIZE:\n        audio_data = np.pad(audio_data, \n                          (0, cfg.FS * cfg.WINDOW_SIZE - len(audio_data)), \n                          mode='constant')\n    \n    mel_spec = audio2melspec(audio_data, cfg)\n    \n    # Resize if needed\n    if mel_spec.shape != cfg.TARGET_SHAPE:\n        mel_spec = cv2.resize(mel_spec, cfg.TARGET_SHAPE, interpolation=cv2.INTER_LINEAR)\n        \n    return mel_spec.astype(np.float32)","metadata":{"execution":{"iopub.status.busy":"2025-05-09T01:20:44.961141Z","iopub.execute_input":"2025-05-09T01:20:44.961508Z","iopub.status.idle":"2025-05-09T01:20:44.983849Z","shell.execute_reply.started":"2025-05-09T01:20:44.961484Z","shell.execute_reply":"2025-05-09T01:20:44.982718Z"},"papermill":{"duration":0.015194,"end_time":"2025-05-02T14:06:43.745102","exception":false,"start_time":"2025-05-02T14:06:43.729908","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"630c40cb","cell_type":"code","source":"def find_model_files(cfg):\n    \"\"\"\n    Find all .pth model files in the specified model directory\n    \"\"\"\n    model_files = []\n    \n    model_dir = Path(cfg.model_path)\n    \n    for path in model_dir.glob('**/*.pth'):\n        model_files.append(str(path))\n    \n    return model_files\n\ndef load_models(cfg, num_classes):\n    \"\"\"\n    Load all found model files and prepare them for ensemble\n    \"\"\"\n    models = []\n    \n    model_files = find_model_files(cfg)\n    \n    if not model_files:\n        print(f\"Warning: No model files found under {cfg.model_path}!\")\n        return models\n    \n    print(f\"Found a total of {len(model_files)} model files.\")\n    \n    if cfg.use_specific_folds:\n        filtered_files = []\n        for fold in cfg.folds:\n            fold_files = [f for f in model_files if f\"fold{fold}\" in f]\n            filtered_files.extend(fold_files)\n        model_files = filtered_files\n        print(f\"Using {len(model_files)} model files for the specified folds ({cfg.folds}).\")\n    \n    for model_path in model_files:\n        try:\n            print(f\"Loading model: {model_path}\")\n            checkpoint = torch.load(model_path, map_location=torch.device(cfg.device))\n            \n            model = BirdCLEFModel(cfg, num_classes)\n            model.load_state_dict(checkpoint['model_state_dict'])\n            model = model.to(cfg.device)\n            model.eval()\n            \n            models.append(model)\n        except Exception as e:\n            print(f\"Error loading model {model_path}: {e}\")\n    \n    return models\n\ndef predict_on_spectrogram(audio_path, models, cfg, species_ids):\n    \"\"\"Process a single audio file and predict species presence for each 5-second segment\"\"\"\n    predictions = []\n    row_ids = []\n    soundscape_id = Path(audio_path).stem\n    \n    try:\n        print(f\"Processing {soundscape_id}\")\n        audio_data, _ = librosa.load(audio_path, sr=cfg.FS)\n        \n        total_segments = int(len(audio_data) / (cfg.FS * cfg.WINDOW_SIZE))\n        \n        for segment_idx in range(total_segments):\n            start_sample = segment_idx * cfg.FS * cfg.WINDOW_SIZE\n            end_sample = start_sample + cfg.FS * cfg.WINDOW_SIZE\n            segment_audio = audio_data[start_sample:end_sample]\n            \n            end_time_sec = (segment_idx + 1) * cfg.WINDOW_SIZE\n            row_id = f\"{soundscape_id}_{end_time_sec}\"\n            row_ids.append(row_id)\n\n            if cfg.use_tta:\n                all_preds = []\n                \n                for tta_idx in range(cfg.tta_count):\n                    mel_spec = process_audio_segment(segment_audio, cfg)\n                    mel_spec = apply_tta(mel_spec, tta_idx)\n\n                    mel_spec = torch.tensor(mel_spec, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n                    mel_spec = mel_spec.to(cfg.device)\n\n                    if len(models) == 1:\n                        with torch.no_grad():\n                            outputs = models[0](mel_spec)\n                            probs = torch.sigmoid(outputs).cpu().numpy().squeeze()\n                            all_preds.append(probs)\n                    else:\n                        segment_preds = []\n                        for model in models:\n                            with torch.no_grad():\n                                outputs = model(mel_spec)\n                                probs = torch.sigmoid(outputs).cpu().numpy().squeeze()\n                                segment_preds.append(probs)\n                        \n                        avg_preds = np.mean(segment_preds, axis=0)\n                        all_preds.append(avg_preds)\n\n                final_preds = np.mean(all_preds, axis=0)\n            else:\n                mel_spec = process_audio_segment(segment_audio, cfg)\n                \n                mel_spec = torch.tensor(mel_spec, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n                mel_spec = mel_spec.to(cfg.device)\n                \n                if len(models) == 1:\n                    with torch.no_grad():\n                        outputs = models[0](mel_spec)\n                        final_preds = torch.sigmoid(outputs).cpu().numpy().squeeze()\n                else:\n                    segment_preds = []\n                    for model in models:\n                        with torch.no_grad():\n                            outputs = model(mel_spec)\n                            probs = torch.sigmoid(outputs).cpu().numpy().squeeze()\n                            segment_preds.append(probs)\n\n                    final_preds = np.mean(segment_preds, axis=0)\n                    \n            predictions.append(final_preds)\n            \n    except Exception as e:\n        print(f\"Error processing {audio_path}: {e}\")\n    \n    return row_ids, predictions","metadata":{"execution":{"iopub.status.busy":"2025-05-09T01:20:44.984844Z","iopub.execute_input":"2025-05-09T01:20:44.985154Z","iopub.status.idle":"2025-05-09T01:20:45.008270Z","shell.execute_reply.started":"2025-05-09T01:20:44.985127Z","shell.execute_reply":"2025-05-09T01:20:45.007283Z"},"papermill":{"duration":0.025024,"end_time":"2025-05-02T14:06:43.774324","exception":false,"start_time":"2025-05-02T14:06:43.749300","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"20707b50","cell_type":"code","source":"def apply_tta(spec, tta_idx):\n    \"\"\"Apply test-time augmentation\"\"\"\n    if tta_idx == 0:\n        # Original spectrogram\n        return spec\n    elif tta_idx == 1:\n        # Time shift (horizontal flip)\n        return np.flip(spec, axis=1)\n    elif tta_idx == 2:\n        # Frequency shift (vertical flip)\n        return np.flip(spec, axis=0)\n    else:\n        return spec\n\ndef run_inference(cfg, models, species_ids):\n    \"\"\"Run inference on all test soundscapes\"\"\"\n    test_files = list(Path(cfg.test_soundscapes).glob('*.ogg'))\n    \n    if cfg.debug:\n        print(f\"Debug mode enabled, using only {cfg.debug_count} files\")\n        test_files = test_files[:cfg.debug_count]\n    \n    print(f\"Found {len(test_files)} test soundscapes\")\n\n    all_row_ids = []\n    all_predictions = []\n\n    for audio_path in tqdm(test_files):\n        row_ids, predictions = predict_on_spectrogram(str(audio_path), models, cfg, species_ids)\n        all_row_ids.extend(row_ids)\n        all_predictions.extend(predictions)\n    \n    return all_row_ids, all_predictions\n\ndef create_submission(row_ids, predictions, species_ids, cfg):\n    \"\"\"Create submission dataframe\"\"\"\n    print(\"Creating submission dataframe...\")\n\n    submission_dict = {'row_id': row_ids}\n    \n    for i, species in enumerate(species_ids):\n        submission_dict[species] = [pred[i] for pred in predictions]\n\n    submission_df = pd.DataFrame(submission_dict)\n\n    submission_df.set_index('row_id', inplace=True)\n\n    sample_sub = pd.read_csv(cfg.submission_csv, index_col='row_id')\n\n    missing_cols = set(sample_sub.columns) - set(submission_df.columns)\n    if missing_cols:\n        print(f\"Warning: Missing {len(missing_cols)} species columns in submission\")\n        for col in missing_cols:\n            submission_df[col] = 0.0\n\n    submission_df = submission_df[sample_sub.columns]\n\n    submission_df = submission_df.reset_index()\n    \n    return submission_df\n","metadata":{"execution":{"iopub.status.busy":"2025-05-09T01:20:45.010883Z","iopub.execute_input":"2025-05-09T01:20:45.011180Z","iopub.status.idle":"2025-05-09T01:20:45.030717Z","shell.execute_reply.started":"2025-05-09T01:20:45.011156Z","shell.execute_reply":"2025-05-09T01:20:45.029827Z"},"papermill":{"duration":0.01675,"end_time":"2025-05-02T14:06:43.794737","exception":false,"start_time":"2025-05-02T14:06:43.777987","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"e632990d","cell_type":"code","source":"def main():\n    start_time = time.time()\n    print(\"Starting BirdCLEF-2025 inference...\")\n    print(f\"TTA enabled: {cfg.use_tta} (variations: {cfg.tta_count if cfg.use_tta else 0})\")\n\n    models = load_models(cfg, num_classes)\n    \n    if not models:\n        print(\"No models found! Please check model paths.\")\n        return\n    \n    print(f\"Model usage: {'Single model' if len(models) == 1 else f'Ensemble of {len(models)} models'}\")\n\n    row_ids, predictions = run_inference(cfg, models, species_ids)\n\n    submission_df = create_submission(row_ids, predictions, species_ids, cfg)\n\n    submission_path = 'submission.csv'\n    submission_df.to_csv(submission_path, index=False)\n    print(f\"Submission saved to {submission_path}\")\n    \n    end_time = time.time()\n    print(f\"Inference completed in {(end_time - start_time)/60:.2f} minutes\")","metadata":{"execution":{"iopub.status.busy":"2025-05-09T01:20:45.031640Z","iopub.execute_input":"2025-05-09T01:20:45.032006Z","iopub.status.idle":"2025-05-09T01:20:45.046266Z","shell.execute_reply.started":"2025-05-09T01:20:45.031976Z","shell.execute_reply":"2025-05-09T01:20:45.045287Z"},"papermill":{"duration":0.012785,"end_time":"2025-05-02T14:06:43.811514","exception":false,"start_time":"2025-05-02T14:06:43.798729","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"8f50158a","cell_type":"code","source":"if __name__ == \"__main__\":\n    main()","metadata":{"execution":{"iopub.status.busy":"2025-05-09T01:20:45.047268Z","iopub.execute_input":"2025-05-09T01:20:45.047563Z","iopub.status.idle":"2025-05-09T01:20:49.576996Z","shell.execute_reply.started":"2025-05-09T01:20:45.047542Z","shell.execute_reply":"2025-05-09T01:20:49.575952Z"},"papermill":{"duration":5.049285,"end_time":"2025-05-02T14:06:48.864442","exception":false,"start_time":"2025-05-02T14:06:43.815157","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"37bc0842","cell_type":"code","source":"","metadata":{"papermill":{"duration":0.004124,"end_time":"2025-05-02T14:06:48.873526","exception":false,"start_time":"2025-05-02T14:06:48.869402","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null}]}