{"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":11601994,"sourceType":"datasetVersion","datasetId":7263239},{"sourceId":407549,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":333005,"modelId":353934}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nfrom pathlib import Path\n\ninput_path = Path('/kaggle/input')\nbirdclef_path = input_path / 'birdclef-2025'\nmeta_path = input_path / 'birdclef-2025-train-meta-extra-updated-1' / 'train_meta_extra_updated_2.csv'\ntaxonomy_path = birdclef_path / 'taxonomy.csv'\ntrain_path = birdclef_path / 'train_audio'\ntrain_soundscapes_path = birdclef_path / 'train_soundscapes'\ntest_soundscapes_path = birdclef_path / 'test_soundscapes'\ncheckpoint_path = input_path / 'birdclef_2025_efficientnet_b0' / 'pytorch' / 'default' / '1' / 'efficientnet_b0_0000.ckpt'","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-05-29T18:53:43.048141Z","iopub.execute_input":"2025-05-29T18:53:43.048504Z","iopub.status.idle":"2025-05-29T18:53:43.681517Z","shell.execute_reply.started":"2025-05-29T18:53:43.048466Z","shell.execute_reply":"2025-05-29T18:53:43.680533Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import random\n\nconfig = {\n    'experiment_name': 'efficientnet_0000',\n    'seed': 42,\n    'spectrogram_input': True,\n    'spectrogram_params': {\n        'n_mels': 256,\n        'fmin': 20,\n        'fmax': 15_000\n    },\n    'validation_size': 0.2,\n    'batch_size': 128,\n    'cw_len': 5000,\n    'pretrained_model': True,\n    'early_stopping_patience': 3,\n    'N_batches': 1.0,\n    'N_epochs': 100,\n    'sample': False,\n    'sample_fraction': 1.0,\n    'oversample': True,\n    'oversample_factor': 1.0,\n    'model': 'EfficientNet'\n}\n\nseed = config['seed']\nrandom.seed(seed)\nrng = np.random.default_rng(seed=seed)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-29T18:53:43.682541Z","iopub.execute_input":"2025-05-29T18:53:43.683248Z","iopub.status.idle":"2025-05-29T18:53:43.690714Z","shell.execute_reply.started":"2025-05-29T18:53:43.683220Z","shell.execute_reply":"2025-05-29T18:53:43.689691Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport timm\n\nclass EfficientNet(nn.Module):\n    def __init__(self, num_classes: int, pretrained: bool):\n        super().__init__()\n\n        self.model = timm.create_model(\n            'efficientnet_b0',\n            in_chans=1,\n            num_classes=0,\n            pretrained=False\n        )\n        pool_out_shape = 1280\n        self.classifier = nn.Linear(pool_out_shape, num_classes)\n\n    def forward(self, x):\n        features = self.model(x)\n        output = self.classifier(features)\n        return output","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-29T18:53:43.691745Z","iopub.execute_input":"2025-05-29T18:53:43.692024Z","iopub.status.idle":"2025-05-29T18:53:53.724194Z","shell.execute_reply.started":"2025-05-29T18:53:43.691970Z","shell.execute_reply":"2025-05-29T18:53:53.723151Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"meta_df = pd.read_csv(meta_path)\nall_species = sorted(meta_df['primary_label'].unique().tolist())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-29T18:56:10.503012Z","iopub.execute_input":"2025-05-29T18:56:10.503411Z","iopub.status.idle":"2025-05-29T18:56:10.812567Z","shell.execute_reply.started":"2025-05-29T18:56:10.503376Z","shell.execute_reply":"2025-05-29T18:56:10.811381Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"match config['model']:\n    case 'EfficientNet':\n        model = EfficientNet(len(all_species), config['pretrained_model'])\n    case _:\n        raise ValueError(f\"Unknown model: {config['model']}\")\n\ncheckpoint = torch.load(checkpoint_path, weights_only=True, map_location=torch.device('cpu'))['state_dict']\nstate_dict = {k.partition('model.')[2]: v for k,v in checkpoint.items()}\nmodel.load_state_dict(state_dict)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-29T18:56:11.359273Z","iopub.execute_input":"2025-05-29T18:56:11.359591Z","iopub.status.idle":"2025-05-29T18:56:12.199977Z","shell.execute_reply.started":"2025-05-29T18:56:11.359567Z","shell.execute_reply":"2025-05-29T18:56:12.199030Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if len(os.listdir(test_soundscapes_path)) == 1:\n    debug = True\n    soundscapes_path = train_soundscapes_path\nelse:\n    soundscapes_path = test_soundscapes_path\n\nsoundscape_files = [afile for afile in sorted(os.listdir(soundscapes_path)) if afile.endswith('.ogg')]\nif debug:\n    soundscape_files = soundscape_files[:700]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-29T18:56:15.436588Z","iopub.execute_input":"2025-05-29T18:56:15.436918Z","iopub.status.idle":"2025-05-29T18:56:15.542894Z","shell.execute_reply.started":"2025-05-29T18:56:15.436892Z","shell.execute_reply":"2025-05-29T18:56:15.541844Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tqdm import tqdm\nimport librosa\nimport cv2\nfrom itertools import islice\nimport torch.nn.functional as F\n\ndef batched(iterable, n, *, strict=False):\n    if n < 1:\n        raise ValueError('n must be at least one')\n    iterator = iter(iterable)\n    while batch := tuple(islice(iterator, n)):\n        if strict and len(batch) != n:\n            raise ValueError('batched(): incomplete batch')\n        yield batch\n\nmodel.eval()\nclass_labels = all_species\npredict_batch_size = 16\n\nfs = 32_000\n\ndef get_mel_spec(audio: np.ndarray, spectrogram_params: dict) -> np.ndarray:\n    mel_spec = librosa.feature.melspectrogram(\n        y=audio,\n        sr=fs,\n        power=2.0,\n        **spectrogram_params\n    )\n    mel_spec = librosa.power_to_db(mel_spec, ref=np.max)\n    mel_spec = (mel_spec - mel_spec.min()) / (mel_spec.max() - mel_spec.min() + np.finfo(np.float32).eps)\n    return np.expand_dims(cv2.resize(mel_spec, (256, 256), interpolation=cv2.INTER_LINEAR), 0)\n\ndef get_soundscape(soundscape):\n    # Load audio\n    sig, rate = librosa.load(path=soundscapes_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(get_mel_spec(chunk, config['spectrogram_params']))\n\n    row_id_prefix = os.path.basename(soundscape).split('.')[0]\n    row_ids = [row_id_prefix + f'_{i * 5 + 5}' for i in range(len(chunks))]\n    return chunks, row_ids\n\n# Open each soundscape and make predictions for 5-second segments\n# Use pandas df with 'row_id' plus class labels as columns\npredictions = pd.DataFrame(columns=['row_id'] + class_labels)\nfor soundscapes in tqdm(batched(soundscape_files, predict_batch_size)):\n    chunks = []\n    all_row_ids = []\n    for soundscape in soundscapes:\n        chunk, row_ids = get_soundscape(soundscape)\n        chunks.extend(chunk)\n        all_row_ids.extend(row_ids)\n\n    all_row_ids = pd.Series(all_row_ids)\n    chunks = torch.tensor(np.array(chunks))\n    with torch.no_grad():\n        scores = pd.DataFrame(F.sigmoid(model(chunks)))\n    \n    new_rows = pd.concat([all_row_ids, scores], axis=1)\n    new_rows.columns = ['row_id'] + class_labels\n\n    predictions = pd.concat([predictions, new_rows], axis=0, ignore_index=True)\n    \n# Save prediction as csv\npredictions.to_csv('submission.csv', index=False)\npredictions.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-29T18:57:17.907725Z","iopub.execute_input":"2025-05-29T18:57:17.908111Z","execution_failed":"2025-05-29T18:58:07.091Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}