{"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":11308299,"sourceType":"datasetVersion","datasetId":7072251},{"sourceId":11386406,"sourceType":"datasetVersion","datasetId":7130031},{"sourceId":11387272,"sourceType":"datasetVersion","datasetId":7130705},{"sourceId":11387590,"sourceType":"datasetVersion","datasetId":7130947},{"sourceId":11387702,"sourceType":"datasetVersion","datasetId":7131048},{"sourceId":11399578,"sourceType":"datasetVersion","datasetId":7139656},{"sourceId":11400184,"sourceType":"datasetVersion","datasetId":7140123},{"sourceId":11400615,"sourceType":"datasetVersion","datasetId":7140450},{"sourceId":11401644,"sourceType":"datasetVersion","datasetId":7141254},{"sourceId":11416206,"sourceType":"datasetVersion","datasetId":7149908},{"sourceId":11445847,"sourceType":"datasetVersion","datasetId":7170646},{"sourceId":11476681,"sourceType":"datasetVersion","datasetId":7192890},{"sourceId":11527221,"sourceType":"datasetVersion","datasetId":7229199},{"sourceId":184735460,"sourceType":"kernelVersion"},{"sourceId":3729,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":2656,"modelId":312},{"sourceId":3732,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":2659,"modelId":312}],"dockerImageVersionId":30919,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import torch\nimport librosa\nimport numpy as np\nimport pandas as pd\nfrom tqdm import tqdm\nimport os\nimport time\nfrom torch.utils.data import Dataset, DataLoader\nimport torch.nn.functional as F\nimport torch.nn as nn\nfrom torchvision import models, transforms  \nimport timm\n\nclass InferenceConfig:\n    sampling_rate = 32000\n    num_classes = 206\n    n_mels = 64\n    fmin = 40\n    fmax = 15000\n    n_fft = 1024\n    hop_length = 320\n    segment_duration = 5  # seconds for each segment\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    train_soundscapes = \"/kaggle/input/birdclef-2025/train_soundscapes\"\n    test_soundscapes = \"/kaggle/input/birdclef-2025/test_soundscapes\"\n    pretrained_model_path = \"/kaggle/input/tf-efficientnet/pytorch/tf-efficientnet-b3/1/tf_efficientnet_b3_aa-84b4657e.pth\"\n    batch_size = 16\n    max_dev_files = 2\n    time_limit = 540\n    \ndef load_audio_file(file_path, config):\n    try:\n        y, _ = librosa.load(file_path, sr=config.sampling_rate)\n        return y\n    except Exception as e:\n        print(f\"Error loading audio file {file_path}: {str(e)}\")\n        return None\n\ndef get_audio_segments(audio, config, num_segments=12):\n    segments = []\n    segment_length = config.segment_duration * config.sampling_rate\n    \n    for i in range(num_segments):\n        start_idx = i * segment_length\n        end_idx = start_idx + segment_length\n        \n        if end_idx <= len(audio):\n            segment = audio[start_idx:end_idx]\n        else:\n            segment = np.zeros(segment_length)\n            segment[:len(audio)-start_idx] = audio[start_idx:]\n        \n        segment = np.concatenate([segment, segment])\n        segments.append(segment)\n    \n    return segments\n\ndef get_model(config):\n    model = models.efficientnet_b3(weights=None)\n    \n    if os.path.exists(config.pretrained_model_path):\n        print(f\"Loading pre-trained weights from: {config.pretrained_model_path}\")\n        pretrained_dict = torch.load(config.pretrained_model_path, map_location=config.device)\n        \n        model_dict = model.state_dict()\n        pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict}\n        model_dict.update(pretrained_dict)\n        model.load_state_dict(model_dict, strict=False)\n        print(f\"Successfully loaded {len(pretrained_dict)} layers from pre-trained model\")\n    else:\n        print(f\"Warning: Pre-trained model not found at {config.pretrained_model_path}\")\n        print(\"Initializing model with random weights\")\n    \n    model.classifier[1] = nn.Linear(model.classifier[1].in_features, config.num_classes)\n    return model\n\ndef audio_to_melspec_batch(audio_batch, config):\n    melspec_batch = []\n    \n    for audio in audio_batch:\n        melspec = librosa.feature.melspectrogram(\n            y=audio,\n            sr=config.sampling_rate,\n            n_fft=config.n_fft,\n            hop_length=config.hop_length,\n            n_mels=config.n_mels,\n            fmin=config.fmin,\n            fmax=config.fmax\n        )\n        melspec_db = librosa.power_to_db(melspec, ref=np.max)\n        melspec_3channel = np.stack([melspec_db, melspec_db, melspec_db])\n        melspec_batch.append(melspec_3channel)\n    \n    melspec_tensor = torch.FloatTensor(np.array(melspec_batch))\n    transform = transforms.Normalize(mean=[0.485, 0.485, 0.485], std=[0.229, 0.229, 0.229])\n    melspec_tensor = torch.stack([transform(m) for m in melspec_tensor])\n    \n    return melspec_tensor\n\ndef create_submission(model_path, config, class_names):\n    start_time = time.time()\n    \n    test_dir = config.test_soundscapes\n    train_dir = config.train_soundscapes\n    \n    is_submission_mode = any(f.endswith('.ogg') for f in os.listdir(test_dir)) if os.path.exists(test_dir) else False\n    \n    audio_dir = test_dir if is_submission_mode else train_dir\n    \n    print(f\"Running in {'submission' if is_submission_mode else 'development'} mode\")\n    print(f\"Using audio directory: {audio_dir}\")\n    print(f\"Using device: {config.device}\")\n    \n    model = get_model(config)\n    \n    print(f\"Loading fine-tuned model from: {model_path}\")\n    try:\n        checkpoint = torch.load(model_path, map_location=config.device)\n        \n        if isinstance(checkpoint, dict) and \"state_dict\" in checkpoint:\n            model.load_state_dict(checkpoint[\"state_dict\"], strict=False)\n        else:\n            model.load_state_dict(checkpoint, strict=False)\n            \n        print(\"Successfully loaded fine-tuned model weights\")\n    except Exception as e:\n        print(f\"Error loading model weights: {str(e)}\")\n        raise\n        \n    model = model.to(config.device)\n    model.eval()\n\n    predictions = []\n\n    audio_files = sorted([f for f in os.listdir(audio_dir) if f.endswith('.ogg')])\n    \n    if not is_submission_mode:\n        audio_files = audio_files[:config.max_dev_files]\n        print(f\"Development mode: Using only first {config.max_dev_files} files for testing\")\n\n    for audio_file in tqdm(audio_files):\n        elapsed_time = time.time() - start_time\n        if is_submission_mode and elapsed_time > config.time_limit:\n            print(f\"WARNING: Approaching time limit ({elapsed_time:.1f}s/{config.time_limit}s). Stopping processing.\")\n            break\n            \n        file_path = os.path.join(audio_dir, audio_file)\n        base_name = os.path.splitext(audio_file)[0]\n        \n        try:\n            audio = load_audio_file(file_path, config)\n            if audio is None:\n                continue\n                \n            segments = get_audio_segments(audio, config)\n            \n            for batch_start in range(0, len(segments), config.batch_size):\n                batch_end = min(batch_start + config.batch_size, len(segments))\n                batch_segments = segments[batch_start:batch_end]\n                batch_indices = list(range(batch_start, batch_end))\n                \n                batch_melspec = audio_to_melspec_batch(batch_segments, config)\n                batch_melspec = batch_melspec.to(config.device)\n                \n                with torch.no_grad():\n                    batch_outputs = model(batch_melspec)\n                    batch_probs = torch.sigmoid(batch_outputs).cpu().numpy()\n                \n                for i, segment_idx in enumerate(batch_indices):\n                    probs = batch_probs[i]\n                    \n                    formatted_probs = [f\"{prob:.10f}\" for prob in probs]\n                    \n                    row_id = f\"{base_name}_{(segment_idx + 1) * 5}\"\n                    pred_dict = {'row_id': row_id}\n                    pred_dict.update({class_name: formatted_prob for class_name, formatted_prob in zip(class_names, formatted_probs)})\n                    predictions.append(pred_dict)\n                    \n        except Exception as e:\n            print(f\"Error processing {audio_file}: {str(e)}\")\n            continue\n\n    submission_df = pd.DataFrame(predictions)\n    \n    if 'row_id' not in submission_df.columns:\n        print(\"WARNING: No predictions were generated!\")\n        submission_df = pd.DataFrame(columns=['row_id'] + class_names)\n    else:\n        submission_df = submission_df[['row_id'] + class_names]\n    \n    for col in class_names:\n        if col in submission_df.columns:\n            submission_df[col] = submission_df[col].astype(float)\n    \n    end_time = time.time()\n    total_time = end_time - start_time\n    print(f\"Total processing time: {total_time:.2f} seconds\")\n    print(f\"Generated {len(submission_df)} predictions\")\n    \n    return submission_df\n\n\nif __name__ == \"__main__\":\n    config = InferenceConfig()\n    model_path = \"/kaggle/input/efficientnet-b3-fold4/best_fold4.pth\"  \n    \n    train_df = pd.read_csv(\"/kaggle/input/birdclef-2025/train.csv\")\n    class_names = sorted(train_df['primary_label'].unique())\n    \n    submission_df = create_submission(model_path, config, class_names)\n    \n    submission_df.to_csv(\"/kaggle/working/submission.csv\", index=False)\n    print(\"Submission saved to /kaggle/working/submission.csv\")","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}