{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","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,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":11060723,"sourceType":"datasetVersion","datasetId":6891568},{"sourceId":11796771,"sourceType":"datasetVersion","datasetId":7407880},{"sourceId":11811842,"sourceType":"datasetVersion","datasetId":7418660},{"sourceId":11957693,"sourceType":"datasetVersion","datasetId":7518261},{"sourceId":400757,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":327936,"modelId":348794},{"sourceId":401975,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":328842,"modelId":348795},{"sourceId":402276,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":321991,"modelId":342614}],"dockerImageVersionId":30918,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# **BirdCLEF 2025 Inference Notebook**\nThis notebook runs inference on BirdCLEF 2025 test soundscapes and generates a submission file. It supports both single model inference and ensemble inference with multiple models. You can find the pre-processing and training processes in the following notebooks:\n\nIn this notebook I implemented smoothing, and ran some with the basemodel and with the no-human data. this also contains openvino code that runs fine in the notebook, but crashed in submission.","metadata":{}},{"cell_type":"code","source":"%%capture\n!pip install -U openvino-telemetry  --no-index --find-links /kaggle/input/pip-hub-2\n!pip install -U openvino  --no-index --find-links /kaggle/input/pip-hub-2","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:38:53.471323Z","iopub.execute_input":"2025-05-26T11:38:53.471745Z","iopub.status.idle":"2025-05-26T11:39:06.501315Z","shell.execute_reply.started":"2025-05-26T11:38:53.471713Z","shell.execute_reply":"2025-05-26T11:39:06.499984Z"}},"outputs":[],"execution_count":null},{"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\nimport openvino\nwarnings.filterwarnings(\"ignore\")\nlogging.basicConfig(level=logging.ERROR)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:39:06.502527Z","iopub.execute_input":"2025-05-26T11:39:06.502895Z","iopub.status.idle":"2025-05-26T11:39:23.109802Z","shell.execute_reply.started":"2025-05-26T11:39:06.502854Z","shell.execute_reply":"2025-05-26T11:39:23.105160Z"}},"outputs":[],"execution_count":null},{"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/best-no-human'  \n    \n    # Audio parameters\n    FS = 32000  \n    WINDOW_SIZE = 5  \n    \n    # Mel spectrogram parameters\n    N_FFT = 1024\n    HOP_LENGTH = 512\n    N_MELS = 128\n    FMIN = 50\n    FMAX = 14000\n    TARGET_SHAPE = (256, 256)\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":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:39:23.112711Z","iopub.execute_input":"2025-05-26T11:39:23.113125Z","iopub.status.idle":"2025-05-26T11:39:23.124342Z","shell.execute_reply.started":"2025-05-26T11:39:23.113059Z","shell.execute_reply":"2025-05-26T11:39:23.122523Z"}},"outputs":[],"execution_count":null},{"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":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:39:23.125671Z","iopub.execute_input":"2025-05-26T11:39:23.126009Z","iopub.status.idle":"2025-05-26T11:39:23.177087Z","shell.execute_reply.started":"2025-05-26T11:39:23.125982Z","shell.execute_reply":"2025-05-26T11:39:23.175723Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CFG_model:\n    def __init__(self, name, model_path):\n        self.model_path = model_path\n        self.model_name = name\n    in_channels = 1\n    device = 'cpu'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:39:23.178200Z","iopub.execute_input":"2025-05-26T11:39:23.178585Z","iopub.status.idle":"2025-05-26T11:39:23.184552Z","shell.execute_reply.started":"2025-05-26T11:39:23.178549Z","shell.execute_reply":"2025-05-26T11:39:23.183235Z"}},"outputs":[],"execution_count":null},{"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":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:39:23.188410Z","iopub.execute_input":"2025-05-26T11:39:23.188778Z","iopub.status.idle":"2025-05-26T11:39:23.205345Z","shell.execute_reply.started":"2025-05-26T11:39:23.188749Z","shell.execute_reply":"2025-05-26T11:39:23.204151Z"}},"outputs":[],"execution_count":null},{"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        n_mels=cfg.N_MELS,\n        fmin=cfg.FMIN,\n        fmax=cfg.FMAX,\n        power=2.0\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":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:39:23.231126Z","iopub.execute_input":"2025-05-26T11:39:23.231615Z","iopub.status.idle":"2025-05-26T11:39:23.253646Z","shell.execute_reply.started":"2025-05-26T11:39:23.231572Z","shell.execute_reply":"2025-05-26T11:39:23.252538Z"}},"outputs":[],"execution_count":null},{"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\ndef convert_pytorch_to_openvino(pytorch_model_path, model_cfg, num_classes, output_dir='/kaggle/working/openvino_models'):\n    \"\"\"Converts a single PyTorch model to OpenVINO IR and saves to a writable directory.\"\"\"\n    print(f\"Converting {pytorch_model_path} to OpenVINO...\")\n    try:\n        # Instantiate and load the PyTorch model\n        pytorch_model = BirdCLEFModel(model_cfg, num_classes)\n        checkpoint = torch.load(pytorch_model_path, map_location=torch.device('cpu'))\n        pytorch_model.load_state_dict(checkpoint['model_state_dict'])\n        pytorch_model.eval()\n\n        # Create a dummy input\n        example_input = torch.randn(1, model_cfg.in_channels, cfg.TARGET_SHAPE[0], cfg.TARGET_SHAPE[1])\n\n        # Convert to OpenVINO IR\n        ov_model = openvino.convert_model(pytorch_model, example_input=example_input)\n\n        # Reshape if necessary\n        ov_model.reshape([-1, model_cfg.in_channels, cfg.TARGET_SHAPE[0], cfg.TARGET_SHAPE[1]])\n\n        # Save the OpenVINO IR to the specified output directory\n        output_dir_path = Path(output_dir)\n        output_dir_path.mkdir(parents=True, exist_ok=True) # Ensure the directory exists\n\n        model_name = Path(pytorch_model_path).stem\n        output_xml_path = output_dir_path / f\"{model_name}.xml\"\n        openvino.save_model(ov_model, output_xml_path)\n        print(f\"Converted model saved to {output_xml_path}\")\n\n    except Exception as e:\n        print(f\"Error converting {pytorch_model_path}: {e}\")\n\n\ndef load_models(num_classes, model_files_to_process, cfg, openvino_save_dir='/kaggle/working/openvino_models'):\n    \"\"\"\n    Load all specified OpenVINO model files or convert them if not found,\n    saving/loading to/from a writable directory.\n    \"\"\"\n    models = []\n    ie = openvino.Core()\n    openvino_save_dir_path = Path(openvino_save_dir)\n    openvino_save_dir_path.mkdir(parents=True, exist_ok=True) # Ensure save directory exists\n\n    for pytorch_model_path, model_cfg in model_files_to_process:\n        model_name = Path(pytorch_model_path).stem\n        model_xml_path = openvino_save_dir_path / f\"{model_name}.xml\"\n        model_bin_path = openvino_save_dir_path / f\"{model_name}.bin\"\n\n        if not model_xml_path.exists() or not model_bin_path.exists():\n             print(f\"OpenVINO model for {model_name} not found in {openvino_save_dir}. Converting from PyTorch...\")\n             convert_pytorch_to_openvino(pytorch_model_path, model_cfg, num_classes, openvino_save_dir)\n\n        if model_xml_path.exists() and model_bin_path.exists():\n            try:\n                print(f\"Loading OpenVINO model from: {model_xml_path}\")\n                ov_model = ie.read_model(model=model_xml_path)\n\n                # Compile the model for the specified device (e.g., 'CPU')\n                compiled_model  = ie.compile_model(model=ov_model, device_name=cfg.device.upper())\n                models.append(compiled_model)\n            except Exception as e:\n                print(f\"Error loading OpenVINO model {model_xml_path}: {e}\")\n        else:\n             print(f\"Skipping model {model_name} due to conversion or file loading error.\")\n\n    return models\n\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                # Process audio segment to get mel spectrogram (NumPy array)\n                mel_spec = process_audio_segment(segment_audio, cfg)\n\n                if cfg.use_tta:\n                    all_preds = []\n\n                    for tta_idx in range(cfg.tta_count):\n                        # Apply TTA to the NumPy array before converting to OpenVINO input format\n                        tta_mel_spec = apply_tta(mel_spec, tta_idx)\n\n                        # OpenVINO expects input in HWC or NCHW format depending on model.\n                        # For your model, NCHW is likely appropriate.\n                        # Add batch and channel dimensions, and ensure correct data type.\n                        input_data = np.expand_dims(np.expand_dims(tta_mel_spec, axis=0), axis=0).astype(np.float32)\n\n                        if len(models) == 1:\n                            # Run inference with the OpenVINO compiled model\n                            results = models[0](input_data)\n                            # Get the output tensor name - you might need to inspect the model XML\n                            # to find the exact output tensor name. A common default is the output layer name.\n                            # For now, we'll assume it's the first output.\n                            output_tensor = list(results.keys())[0]\n                            probs = results[output_tensor].squeeze()\n                            probs = 1 / (1 + np.exp(-probs)) # Apply sigmoid manually for probabilities\n\n                            all_preds.append(probs)\n                        else:\n                            segment_preds = []\n                            for model in models:\n                                results = model(input_data)\n                                output_tensor = list(results.keys())[0]\n                                probs = results[output_tensor].squeeze()\n                                probs = 1 / (1 + np.exp(-probs))\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                    # Add batch and channel dimensions for OpenVINO input\n                    input_data = np.expand_dims(np.expand_dims(mel_spec, axis=0), axis=0).astype(np.float32)\n\n                    if len(models) == 1:\n                        results = models[0](input_data)\n                        output_tensor = list(results.keys())[0]\n                        final_preds = results[output_tensor].squeeze()\n                        final_preds = 1 / (1 + np.exp(-final_preds))\n                    else:\n                        segment_preds = []\n                        for model in models:\n                            results = model(input_data)\n                            output_tensor = list(results.keys())[0]\n                            probs = results[output_tensor].squeeze()\n                            probs = 1 / (1 + np.exp(-probs))\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\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:39:23.254840Z","iopub.execute_input":"2025-05-26T11:39:23.255149Z","iopub.status.idle":"2025-05-26T11:39:23.282541Z","shell.execute_reply.started":"2025-05-26T11:39:23.255123Z","shell.execute_reply":"2025-05-26T11:39:23.281249Z"}},"outputs":[],"execution_count":null},{"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, model_weights):\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, model_weights)\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\ndef smooth_submission(submission_path):\n        \"\"\"\n        Post-process the submission CSV by smoothing predictions to enforce temporal consistency.\n        \n        For each soundscape (grouped by the file name part of 'row_id'), each row's predictions\n        are averaged with those of its neighbors using defined weights.\n        \n        :param submission_path: Path to the submission CSV file.\n        \"\"\"\n        print(\"Smoothing submission predictions...\")\n        sub = pd.read_csv(submission_path)\n        cols = sub.columns[1:]\n        # Extract group names by splitting row_id on the last underscore\n        groups = sub['row_id'].str.rsplit('_', n=1).str[0].values\n        unique_groups = np.unique(groups)\n        \n        for group in unique_groups:\n            # Get indices for the current group\n            idx = np.where(groups == group)[0]\n            sub_group = sub.iloc[idx].copy()\n            predictions = sub_group[cols].values\n            new_predictions = predictions.copy()\n            \n            if predictions.shape[0] > 1:\n                # Smooth the predictions using neighboring segments\n                new_predictions[0] = (predictions[0] * 0.8) + (predictions[1] * 0.2)\n                new_predictions[-1] = (predictions[-1] * 0.8) + (predictions[-2] * 0.2)\n                for i in range(1, predictions.shape[0]-1):\n                    new_predictions[i] = (predictions[i-1] * 0.2) + (predictions[i] * 0.6) + (predictions[i+1] * 0.2)\n            # Replace the smoothed values in the submission dataframe\n            sub.iloc[idx, 1:] = new_predictions\n        \n        sub.to_csv(submission_path, index=False)\n        print(f\"Smoothed submission saved to {submission_path}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:39:23.284557Z","iopub.execute_input":"2025-05-26T11:39:23.284995Z","iopub.status.idle":"2025-05-26T11:39:23.309961Z","shell.execute_reply.started":"2025-05-26T11:39:23.284958Z","shell.execute_reply":"2025-05-26T11:39:23.308675Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_ov_models(modelfiles):\n    ie = openvino.Core()\n    models = []\n    for m in modelfiles:    \n        ov_model = ie.read_model(model=m)\n        compiled_model  = ie.compile_model(model=ov_model, device_name='CPU')\n        models.append(compiled_model)\n    return models    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:55:43.392816Z","iopub.execute_input":"2025-05-26T11:55:43.393314Z","iopub.status.idle":"2025-05-26T11:55:43.399908Z","shell.execute_reply.started":"2025-05-26T11:55:43.393275Z","shell.execute_reply":"2025-05-26T11:55:43.398767Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def main():\n    cfg = CFG()\n    cfg_regnet = CFG_model(\"regnety_008\", \"/kaggle/input/regnet_rms_spect/pytorch/new/1\")\n    cfg_effnet = CFG_model(\"efficientnet_b0\", \"/kaggle/input/effnet_b0_rms_spect/pytorch/default/1\")\n    cfg_resnet = CFG_model(\"resnet34\", \"/kaggle/input/resnet34-no-human/pytorch/default/4\")\n\n\n    eff1 = \"/kaggle/input/effnet_b0_rms_spect/pytorch/default/1/model_eff_fcl_rms_fold1.pth\"\n    reg2 = \"/kaggle/input/regnet_rms_spect/pytorch/new/1/model_fcl_fold1.pth\" # This was fold1, not fold2\n    reg4 = \"/kaggle/input/regnet_rms_spect/pytorch/new/1/model_fcl_fold2.pth\"\n    res2 = \"/kaggle/input/resnet34-no-human/pytorch/default/4/model_res34__fcl_rms_fold2.pth\"\n    \n    # List of (PyTorch model path, model_cfg) for conversion and loading\n    model_files_to_process = [\n        (eff1, cfg_effnet),\n        (reg2, cfg_regnet),\n        (reg4, cfg_regnet),\n        (res2, cfg_resnet)\n    ]\n    model_weights = [0.2, 0.3, 0.3, 0.2]\n\n\n    start_time = time.time()\n    print(\"Starting BirdCLEF-2025 inference with OpenVINO...\")\n    print(f\"TTA enabled: {cfg.use_tta} (variations: {cfg.tta_count if cfg.use_tta else 0})\")\n\n    # Load OpenVINO models. Conversion happens within load_models if IR is not found in /kaggle/working.\n    # Pass the list of models to process and the desired OpenVINO save directory\n    modelfiles = ['/kaggle/input/openvino-models/model_eff_fcl_rms_fold1.xml', \n                  '/kaggle/input/openvino-models/model_fcl_fold1.xml',\n                  '/kaggle/input/openvino-models/model_fcl_fold2.xml',\n                  '/kaggle/input/openvino-models/model_res34__fcl_rms_fold2.xml']\n   \n    #models = load_models(num_classes, model_files_to_process, cfg, openvino_save_dir='/kaggle/working/openvino_models')\n    models = load_ov_models(modelfiles)\n    if not models:\n        print(\"No OpenVINO models loaded! Please check conversion and loading steps.\")\n        return\n\n    print(f\"Model usage: {'Single model' if len(models) == 1 else f'Ensemble of {len(models)} models'}\")\n\n    # Ensure the number of loaded models matches the number of weights\n    if len(models) != len(model_weights):\n        print(f\"Warning: Number of loaded models ({len(models)}) does not match number of weights ({len(model_weights)}). Proceeding without explicit weights in predict_on_spectrogram.\")\n        # If counts don't match, predict_on_spectrogram will just do a simple average\n        model_weights = None # or handle the error more explicitly\n\n    # You need to adjust the predict_on_spectrogram function signature if you pass model_weights\n    # based on whether it was set to None.\n    # For simplicity in this example, let's assume predict_on_spectrogram handles None weights\n    # or you can modify it to pass model_weights only if not None.\n    row_ids, predictions = run_inference(cfg, models, species_ids, model_weights)\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    smooth_submission(submission_path)\n    end_time = time.time()\n    print(f\"Inference completed in {(end_time - start_time)/60:.2f} minutes\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:58:15.989347Z","iopub.execute_input":"2025-05-26T11:58:15.989747Z","iopub.status.idle":"2025-05-26T11:58:16.000923Z","shell.execute_reply.started":"2025-05-26T11:58:15.989718Z","shell.execute_reply":"2025-05-26T11:58:15.999300Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if __name__ == \"__main__\":\n    main()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-26T11:58:22.370949Z","iopub.execute_input":"2025-05-26T11:58:22.371296Z","iopub.status.idle":"2025-05-26T11:58:25.104280Z","shell.execute_reply.started":"2025-05-26T11:58:22.371268Z","shell.execute_reply":"2025-05-26T11:58:25.102918Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}