{"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":381664,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":315203,"modelId":335668}],"dockerImageVersionId":31012,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport librosa\nimport numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport timm\nfrom collections import OrderedDict\nimport cv2\n\nclass CFG:\n    # path\n    train_audio = '/kaggle/input/birdclef-2025/train_audio'\n    train_soundscapes = '/kaggle/input/birdclef-2025/train_soundscapes'\n    test_soundscapes = '/kaggle/input/birdclef-2025/train_soundscapes/'\n    submission_csv = '/kaggle/input/birdclef-2025/sample_submission.csv'\n    taxonomy_csv = '/kaggle/input/birdclef-2025/taxonomy.csv'\n    model = '/kaggle/input/psuedo_model_01/pytorch/default/1/best_model.pth'\n\n    # audio\n    FS = 32000\n    WINDOW_SIZE = 5  # 오디오 분할 길이(초)\n    \n    # MEL_SPEC\n    N_FFT = 1024\n    HOP_LENGTH = 512\n    N_MELS = 128\n    FMIN = 50\n    FMAX = 14000\n    TARGET_SHAPE = (224, 224)\n\n    # inference\n    threshold = 0.5\n\n    # model\n    model_name = 'efficientnet_b0' # resnet도 가능\n    in_channels = 1 # submission test를 위해 임의로 1로 했다.\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n    \n    # another\n    seed = 42\n\ncfg = CFG()\ntorch.manual_seed(cfg.seed)\nnp.random.seed(cfg.seed)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-14T08:39:57.667891Z","iopub.execute_input":"2025-05-14T08:39:57.668298Z","iopub.status.idle":"2025-05-14T08:39:57.680429Z","shell.execute_reply.started":"2025-05-14T08:39:57.668271Z","shell.execute_reply":"2025-05-14T08:39:57.679280Z"}},"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.2,    \n            drop_path_rate=0.1\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","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-14T08:40:00.512424Z","iopub.execute_input":"2025-05-14T08:40:00.513598Z","iopub.status.idle":"2025-05-14T08:40:00.522682Z","shell.execute_reply.started":"2025-05-14T08:40:00.513556Z","shell.execute_reply":"2025-05-14T08:40:00.521703Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class_labels = sorted(os.listdir(cfg.train_audio))\nnum_classes = len(class_labels)\nmodel = BirdCLEFModel(cfg, num_classes)\nmodel = model.to(cfg.device)\n\nprint(\"Loading best model...\")\nckpt = torch.load(cfg.model, map_location=cfg.device, weights_only=True)\nstate = ckpt.get('model_state_dict', ckpt)\nmodel.load_state_dict(state)\nmodel.eval()\nprint(\"finished!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-14T08:40:02.060408Z","iopub.execute_input":"2025-05-14T08:40:02.060816Z","iopub.status.idle":"2025-05-14T08:40:02.367623Z","shell.execute_reply.started":"2025-05-14T08:40:02.060789Z","shell.execute_reply":"2025-05-14T08:40:02.366696Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_soundscapes = [os.path.join(cfg.test_soundscapes, afile) \n                    for afile in sorted(os.listdir(cfg.test_soundscapes))]\npredictions = pd.DataFrame(columns=['row_id'] + class_labels)\nfor soundscape in test_soundscapes:\n\n    # Load audio\n    sig, rate = librosa.load(path=soundscape, sr=None)\n\n    # Split into 5-second chunks\n    chunks = []\n    for i in range(0, len(sig), rate*5):\n        chunk = sig[i:i+rate*5]\n        chunks.append(chunk)\n        \n    # Make predictions for each chunk\n    for i, chunk in enumerate(chunks):\n        \n        # Get row id  (soundscape id + end time of 5s chunk)      \n        row_id = os.path.basename(soundscape).split('.')[0] + f'_{i * 5 + 5}'\n\n        # Audio -> Mel-spec\n        mel_spec = librosa.feature.melspectrogram(\n            y=chunk,\n            sr=rate,\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        )\n\n        # Mel-spec -> db scale\n        mel_spec_db = librosa.power_to_db(mel_spec, ref=np.max)\n        mel_spec_db_norm = (mel_spec_db - mel_spec_db.min()) / (mel_spec_db.max() - mel_spec_db.min())\n        mel_spec_resized = cv2.resize(mel_spec_db_norm, cfg.TARGET_SHAPE)\n\n        # db scaled Mel-spec -> input tensor\n        input_tensor = torch.tensor(mel_spec_resized, dtype=torch.float32).unsqueeze(0).unsqueeze(0).to(cfg.device)\n\n        # Make prediction\n        with torch.no_grad():\n            logits = model(input_tensor)\n            scores = torch.sigmoid(logits).cpu().numpy()[0]\n        \n        # Append to predictions as new row\n        new_row = pd.DataFrame([[row_id] + list(scores)], columns=['row_id'] + class_labels)\n        predictions = pd.concat([predictions, new_row], axis=0, ignore_index=True)\n        \n# Save prediction as csv\npredictions.to_csv('submission.csv', index=False)\npredictions.head()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-05-14T08:50:40.401266Z","iopub.execute_input":"2025-05-14T08:50:40.401601Z","iopub.status.idle":"2025-05-14T08:50:43.461323Z","shell.execute_reply.started":"2025-05-14T08:50:40.401577Z","shell.execute_reply":"2025-05-14T08:50:43.459729Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}