{"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,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":373097,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":308693,"modelId":329101},{"sourceId":373118,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":308709,"modelId":329117}],"dockerImageVersionId":31012,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport librosa\nimport torch\nimport timm\nimport cv2\nfrom sklearn.svm import SVC\nfrom sklearn.preprocessing import LabelEncoder\nimport joblib\nfrom tqdm import tqdm\n\n# Parameters\nSR = 32000\nDURATION = 5\nN_MELS = 128\nHOP_LENGTH = 512\nIMG_SIZE = 224\n\n# Load the model and label encoder\nmodel_path = \"/kaggle/input/svm-from-efficient-b0/scikitlearn/default/1/svm_model.pkl\"\nencoder_path = \"/kaggle/input/svm-from-efficient-b0/scikitlearn/default/1/label_encoder.pkl\"\nclf = joblib.load(model_path)\nle = joblib.load(encoder_path)\n\n# Load EfficientNet feature extractor\nclass CNNFeatureExtractor(torch.nn.Module):\n    def __init__(self, model_path=None):\n        super().__init__()\n        model = timm.create_model(\"efficientnet_b0\", pretrained=False)\n        if model_path:\n            state_dict = torch.load(model_path, weights_only=True)\n            model.load_state_dict(state_dict)\n        model.classifier = torch.nn.Identity()\n        self.model = model\n\n    def forward(self, x):\n        return self.model(x)\n\nextractor = CNNFeatureExtractor(model_path=\"/kaggle/input/efficientnet-b0/other/default/1/efficientnet_b0.pth\").eval()\ndevice = torch.device(\"cpu\")\nextractor.to(device)\n\n# Helper functions\ndef load_audio(filepath, sr=SR):\n    try:\n        y, _ = librosa.load(filepath, sr=sr, duration=DURATION)\n        if len(y) < sr * DURATION:\n            y = np.pad(y, (0, sr * DURATION - len(y)))\n        return y\n    except:\n        return None\n\ndef compute_pcen(y, sr=SR):\n    mel = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=N_MELS, hop_length=HOP_LENGTH)\n    pcen = librosa.pcen(mel * (2**31))  # Amplify to avoid zeros\n    return pcen\n\ndef pcen_to_rgb(pcen):\n    pcen = (pcen - pcen.min()) / (pcen.max() - pcen.min())\n    pcen = (pcen * 255).astype(np.uint8)\n    pcen_rgb = np.stack([pcen] * 3, axis=-1)\n    return cv2.resize(pcen_rgb, (IMG_SIZE, IMG_SIZE))\n\ndef extract_feature(pcen_rgb):\n    img_tensor = torch.tensor(pcen_rgb).float().permute(2, 0, 1) / 255.0\n    img_tensor = img_tensor.unsqueeze(0).to(device)\n    with torch.no_grad():\n        feature = extractor(img_tensor)\n    return feature.cpu().numpy().flatten()\n\n# Load class labels\ntrain_audio_path = \"/kaggle/input/birdclef-2025/train_audio/\"\nclass_labels = sorted(os.listdir(train_audio_path))\n\n# Load submission template\nsample_df = pd.read_csv(\"/kaggle/input/birdclef-2025/sample_submission.csv\")\ntests_path = \"/kaggle/input/birdclef-2025/test_soundscapes\"\nsoundscape_files = [os.path.join(tests_path, afile) for afile in sorted(os.listdir(tests_path)) if afile.endswith('.ogg')]\n\n# fallback if no files found\nif len(soundscape_files) == 0:\n    tests_path = '/kaggle/input/birdclef-2025/train_soundscapes'\n    soundscape_files = [os.path.join(tests_path, f) for f in sorted(os.listdir(tests_path)) if f.endswith('.ogg')][:3]\n\nsubmission_rows = []\n\n# Process each soundscape file\nfor soundscape_file in tqdm(soundscape_files, desc=\"Processing soundscapes\"):\n    path = soundscape_file  # full path already\n\n    try:\n        y, _ = librosa.load(path, sr=SR)\n        duration = int(len(y) / SR)\n\n        for t in range(0, duration - DURATION + 1, DURATION):\n            segment = y[t * SR : (t + DURATION) * SR]\n            if len(segment) < SR * DURATION:\n                segment = np.pad(segment, (0, SR * DURATION - len(segment)))\n\n            pcen = compute_pcen(segment)\n            pcen_rgb = pcen_to_rgb(pcen)\n            feature = extract_feature(pcen_rgb)\n\n            # Predict probabilities using SVM\n            probs = clf.predict_proba([feature])[0]\n\n            # Format prediction row\n            soundscape_id = os.path.basename(soundscape_file).split('.')[0]\n            row = {\"row_id\": f\"{soundscape_id}_{(t + DURATION)}\"}\n            pred_row = dict(zip(class_labels, probs))\n            output = [pred_row.get(lbl, 0.0) for lbl in class_labels]\n            row.update(dict(zip(map(str, class_labels), output)))\n            submission_rows.append(row)\n\n    except Exception as e:\n        print(f\"Error processing {soundscape_file}: {e}\")\n\n# Save to CSV\nsubmission_df = pd.DataFrame(submission_rows)\nsubmission_df = submission_df[[\"row_id\"] + class_labels]\nsubmission_df.to_csv(\"submission.csv\", index=False)\nprint(\"✅ submission.csv saved.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T07:12:53.129975Z","iopub.execute_input":"2025-05-05T07:12:53.130168Z","iopub.status.idle":"2025-05-05T07:13:36.498829Z","shell.execute_reply.started":"2025-05-05T07:12:53.130151Z","shell.execute_reply":"2025-05-05T07:13:36.497909Z"}},"outputs":[],"execution_count":null}]}