{"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":19596,"databundleVersionId":1292430,"sourceType":"competition"},{"sourceId":1264575,"sourceType":"datasetVersion","datasetId":725893},{"sourceId":1262046,"sourceType":"datasetVersion","datasetId":726424}],"dockerImageVersionId":30626,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\nimport cv2\nimport audioread\nimport logging\nimport os\nimport random\nimport time\nimport warnings\n\nimport librosa\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport soundfile as sf\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.utils.data as data\n\nfrom contextlib import contextmanager\nfrom pathlib import Path\nfrom typing import Optional\n\nfrom fastprogress import progress_bar\nfrom sklearn.metrics import f1_score\nfrom torchvision import models\n\n# import os\n# for dirname, _, filenames in os.walk('/kaggle/input'):\n#     for filename in filenames:\n#         print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-01-11T13:37:44.792349Z","iopub.execute_input":"2024-01-11T13:37:44.793050Z","iopub.status.idle":"2024-01-11T13:37:51.066025Z","shell.execute_reply.started":"2024-01-11T13:37:44.793000Z","shell.execute_reply":"2024-01-11T13:37:51.064826Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def set_seed(seed: int = 42):\n    random.seed(seed)\n    np.random.seed(seed)\n    os.environ[\"PYTHONHASHSEED\"] = str(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = True\n\ndef get_logger(out_file=None):\n    logger= logging.getLogger()\n    formatter = logging.Formatter(\"%(asctime)s - %(levelname)s - %(message)s\")\n    logger.handlers=[]\n    logger.setLevel(logging.INFO)\n    \n    handler = logging.StreamHandler()\n    handler.setFormatter(formatter)\n    handler.setLevel(logging.INFO)\n    logger.addHandler(handler)\n    \n    if out_file is not None:\n        fh = logging.FileHandler(out_file)\n        fh.setFormatter(formatter)\n        fh.setLevel(logging.INFO)\n        logger.addHandler(fh)\n    logger.info(\"logger set up\")\n    return logger\n\n@contextmanager\ndef timer(name: str, logger: Optional[logging.Logger] = None):\n    t0 = time.time()\n    msg = f\"[{name}] start\"\n    if logger is None:\n        print(msg)\n    else:\n        logger.info(msg)\n    yield\n    \n    msg = f\"[{name}] done in {time.time() - t0:.2f} s\"\n    if logger is None:\n        print(msg)\n    else:\n        logger.info(msg)  ","metadata":{"execution":{"iopub.status.busy":"2024-01-11T13:38:03.162774Z","iopub.execute_input":"2024-01-11T13:38:03.164362Z","iopub.status.idle":"2024-01-11T13:38:03.176566Z","shell.execute_reply.started":"2024-01-11T13:38:03.164220Z","shell.execute_reply":"2024-01-11T13:38:03.175043Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"logger = get_logger('main.log')\nset_seed(1213)","metadata":{"execution":{"iopub.status.busy":"2024-01-11T13:38:08.994515Z","iopub.execute_input":"2024-01-11T13:38:08.995194Z","iopub.status.idle":"2024-01-11T13:38:09.009380Z","shell.execute_reply.started":"2024-01-11T13:38:08.995157Z","shell.execute_reply":"2024-01-11T13:38:09.008084Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"TARGET_SR = 32000","metadata":{"execution":{"iopub.status.busy":"2024-01-11T13:38:13.398723Z","iopub.execute_input":"2024-01-11T13:38:13.399858Z","iopub.status.idle":"2024-01-11T13:38:13.406280Z","shell.execute_reply.started":"2024-01-11T13:38:13.399803Z","shell.execute_reply":"2024-01-11T13:38:13.404196Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test = pd.read_csv('/kaggle/input/birdcall-check/test.csv')\ntest_audio = \"/kaggle/input/birdcall-check/test_audio\"\ntest.head(5)","metadata":{"execution":{"iopub.status.busy":"2024-01-11T13:38:16.529876Z","iopub.execute_input":"2024-01-11T13:38:16.530280Z","iopub.status.idle":"2024-01-11T13:38:16.583662Z","shell.execute_reply.started":"2024-01-11T13:38:16.530249Z","shell.execute_reply":"2024-01-11T13:38:16.582085Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class ResNet(nn.Module):\n    def __init__(self, base_model_name: str,pretrained = False,num_classes = 264):\n        super().__init__()\n        base_model = models.__getattribute__(base_model_name)(pretrained = pretrained)\n        layers = list(base_model.children())[:-2]\n        layers.append(nn.AdaptiveMaxPool2d(1))\n        self.encoder = nn.Sequential(*layers)\n        \n        in_features = base_model.fc.in_features\n        \n        self.classifier = nn.Sequential(\n        nn.Linear(in_features,1024), nn.ReLU(), nn.Dropout(p=0.2),\n        nn.Linear(1024,1024), nn.ReLU(), nn.Dropout(p=0.2),\n        nn.Linear(1024, num_classes))\n        \n    def forward(self, x):\n        batch_size = x.size(0)\n        x = self.encoder(x).view(batch_size,-1)\n        x = self.classifier(x)\n        multiclass_proba = F.softmax(x,dim=1)\n        multilabel_proba = F.sigmoid(x)\n        return{\n            \"logits\" : x,\n            \"multiclass_proba\": multiclass_proba,\n            \"multilabel_proba\": multilabel_proba\n        }","metadata":{"execution":{"iopub.status.busy":"2024-01-11T13:38:19.722405Z","iopub.execute_input":"2024-01-11T13:38:19.723004Z","iopub.status.idle":"2024-01-11T13:38:19.732237Z","shell.execute_reply.started":"2024-01-11T13:38:19.722822Z","shell.execute_reply":"2024-01-11T13:38:19.730868Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_config ={\n    \"base_model_name\": \"resnet50\",\n    \"pretrained\": False,\n    \"num_classes\": 264\n}\n\nmelspectrogram_parameters = {\n    \"n_mels\": 128,\n    \"fmin\": 20,\n    \"fmax\": 16000\n}\n\nweights_path = \"/kaggle/input/birdcall-resnet50-init-weights/best.pth\"","metadata":{"execution":{"iopub.status.busy":"2024-01-11T13:38:27.414151Z","iopub.execute_input":"2024-01-11T13:38:27.414827Z","iopub.status.idle":"2024-01-11T13:38:27.424412Z","shell.execute_reply.started":"2024-01-11T13:38:27.414730Z","shell.execute_reply":"2024-01-11T13:38:27.422209Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\ndf = pd.read_csv(\"/kaggle/input/birdsong-recognition/train.csv\")\n\nunique_bird_names = df[\"ebird_code\"].unique()\nlabel_encoder = LabelEncoder()\nencoded_labels = label_encoder.fit_transform(unique_bird_names)\nBIRD_CODE = dict(zip(unique_bird_names,encoded_labels))\nINV_BIRD_CODE = {v: k for k, v in BIRD_CODE.items()}","metadata":{"execution":{"iopub.status.busy":"2024-01-11T13:38:30.702835Z","iopub.execute_input":"2024-01-11T13:38:30.703364Z","iopub.status.idle":"2024-01-11T13:38:31.329885Z","shell.execute_reply.started":"2024-01-11T13:38:30.703307Z","shell.execute_reply":"2024-01-11T13:38:31.328835Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def mono_to_color(X: np.ndarray,\n                  mean=None,\n                  std=None,\n                  norm_max=None,\n                  norm_min=None,\n                  eps=1e-6):\n    \n    X = np.stack([X, X, X], axis=-1)\n\n \n    mean = mean or X.mean()\n    X = X - mean\n    std = std or X.std()\n    Xstd = X / (std + eps)\n    _min, _max = Xstd.min(), Xstd.max()\n    norm_max = norm_max or _max\n    norm_min = norm_min or _min\n    if (_max - _min) > eps:\n        # Normalize to [0, 255]\n        V = Xstd\n        V[V < norm_min] = norm_min\n        V[V > norm_max] = norm_max\n        V = 255 * (V - norm_min) / (norm_max - norm_min)\n        V = V.astype(np.uint8)\n    else:\n        # Just zero\n        V = np.zeros_like(Xstd, dtype=np.uint8)\n    return V\n\n\nclass TestDataset(data.Dataset):\n    def __init__(self, df: pd.DataFrame, clip: np.ndarray,\n                 img_size=224, melspectrogram_parameters={}):\n        self.df = df\n        self.clip = clip\n        self.img_size = img_size\n        self.melspectrogram_parameters = melspectrogram_parameters\n        \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, idx: int):\n        SR = 32000\n        sample = self.df.loc[idx, :]\n        site = sample.site\n        row_id = sample.row_id\n        \n        if site == \"site_3\":\n            y = self.clip.astype(np.float32)\n            len_y = len(y)\n            start = 0\n            end = SR * 5\n            images = []\n            while len_y > start:\n                y_batch = y[start:end].astype(np.float32)\n                if len(y_batch) != (SR * 5):\n                    break\n                start = end\n                end = end + SR * 5\n                \n                melspec = librosa.feature.melspectrogram(y=y_batch,\n                                                         sr=SR,\n                                                         **self.melspectrogram_parameters)\n                melspec = librosa.power_to_db(melspec).astype(np.float32)\n                image = mono_to_color(melspec)\n                height, width, _ = image.shape\n                image = cv2.resize(image, (int(width * self.img_size / height), self.img_size))\n                image = np.moveaxis(image, 2, 0)\n                image = (image / 255.0).astype(np.float32)\n                images.append(image)\n            images = np.asarray(images)\n            return images, row_id, site\n        else:\n            end_seconds = int(sample.seconds)\n            start_seconds = int(end_seconds - 5)\n            \n            start_index = SR * start_seconds\n            end_index = SR * end_seconds\n            \n            y = self.clip[start_index:end_index].astype(np.float32)\n\n            melspec = librosa.feature.melspectrogram(y=y, sr=SR, **self.melspectrogram_parameters)\n            melspec = librosa.power_to_db(melspec).astype(np.float32)\n\n            image = mono_to_color(melspec)\n            height, width, _ = image.shape\n            image = cv2.resize(image, (int(width * self.img_size / height), self.img_size))\n            image = np.moveaxis(image, 2, 0)\n            image = (image / 255.0).astype(np.float32)\n\n            return image, row_id, site","metadata":{"execution":{"iopub.status.busy":"2024-01-11T13:38:34.299875Z","iopub.execute_input":"2024-01-11T13:38:34.300546Z","iopub.status.idle":"2024-01-11T13:38:34.319482Z","shell.execute_reply.started":"2024-01-11T13:38:34.300509Z","shell.execute_reply":"2024-01-11T13:38:34.317545Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_model(config: dict, weights_path: str):\n    model = ResNet(**config)\n    checkpoint = torch.load(weights_path, map_location=torch.device('cpu'))\n    model.load_state_dict(checkpoint[\"model_state_dict\"])\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    model.to(device)\n    model.eval()\n    return model","metadata":{"execution":{"iopub.status.busy":"2024-01-11T13:38:38.941882Z","iopub.execute_input":"2024-01-11T13:38:38.942595Z","iopub.status.idle":"2024-01-11T13:38:38.952549Z","shell.execute_reply.started":"2024-01-11T13:38:38.942545Z","shell.execute_reply":"2024-01-11T13:38:38.950423Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def prediction_for_clip(test_df: pd.DataFrame, \n                        clip: np.ndarray, \n                        model: ResNet, \n                        mel_params: dict, \n                        threshold=0.5):\n\n    dataset = TestDataset(df=test_df, \n                          clip=clip,\n                          img_size=224,\n                          melspectrogram_parameters=mel_params)\n    loader = data.DataLoader(dataset, batch_size=1, shuffle=False)\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    \n    model.eval()\n    prediction_dict = {}\n    for image, row_id, site in progress_bar(loader):\n        site = site[0]\n        row_id = row_id[0]\n        if site in {\"site_1\", \"site_2\"}:\n            image = image.to(device)\n\n            with torch.no_grad():\n                prediction = model(image)\n                proba = prediction[\"multilabel_proba\"].detach().cpu().numpy().reshape(-1)\n\n            events = proba >= threshold\n            labels = np.argwhere(events).reshape(-1).tolist()\n\n        else:\n            # to avoid prediction on large batch\n            image = image.squeeze(0)\n            batch_size = 16\n            whole_size = image.size(0)\n            if whole_size % batch_size == 0:\n                n_iter = whole_size // batch_size\n            else:\n                n_iter = whole_size // batch_size + 1\n                \n            all_events = set()\n            for batch_i in range(n_iter):\n                batch = image[batch_i * batch_size:(batch_i + 1) * batch_size]\n                if batch.ndim == 3:\n                    batch = batch.unsqueeze(0)\n\n                batch = batch.to(device)\n                with torch.no_grad():\n                    prediction = model(batch)\n                    proba = prediction[\"multilabel_proba\"].detach().cpu().numpy()\n                    \n                events = proba >= threshold\n                for i in range(len(events)):\n                    event = events[i, :]\n                    labels = np.argwhere(event).reshape(-1).tolist()\n                    for label in labels:\n                        all_events.add(label)\n                        \n            labels = list(all_events)\n        if len(labels) == 0:\n            prediction_dict[row_id] = \"nocall\"\n        else:\n            labels_str_list = list(map(lambda x: INV_BIRD_CODE[x], labels))\n            label_string = \" \".join(labels_str_list)\n            prediction_dict[row_id] = label_string\n    return prediction_dict","metadata":{"execution":{"iopub.status.busy":"2024-01-11T13:38:42.467890Z","iopub.execute_input":"2024-01-11T13:38:42.468720Z","iopub.status.idle":"2024-01-11T13:38:42.490036Z","shell.execute_reply.started":"2024-01-11T13:38:42.468643Z","shell.execute_reply":"2024-01-11T13:38:42.487654Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\n\n\ndef prediction(test_df: pd.DataFrame,\n               test_audio: Path,\n               model_config: dict,\n               mel_params: dict,\n               weights_path: str,\n               threshold=0.5):\n    model = get_model(model_config, weights_path)\n    unique_audio_id = test_df.audio_id.unique()\n\n    warnings.filterwarnings(\"ignore\")\n    prediction_dfs = []\n    for audio_id in unique_audio_id:\n        with timer(f\"Loading {audio_id}\", logger):\n            clip, _ = librosa.load(test_audio+\"/\"+(audio_id+\".mp3\"),\n                                  sr=TARGET_SR,\n                                  mono=True,\n                                  res_type=\"kaiser_fast\")\n            test_df_for_audio_id = test_df.query(\n            f\"audio_id == '{audio_id}'\").reset_index(drop=True)\n        with timer(f\"Prediction on {audio_id}\", logger):\n            prediction_dict = prediction_for_clip(test_df_for_audio_id,\n                                                  clip=clip,\n                                                  model=model,\n                                                  mel_params=mel_params,\n                                                  threshold=threshold)\n        row_id = list(prediction_dict.keys())\n        birds = list(prediction_dict.values())\n        prediction_df = pd.DataFrame({\n            \"row_id\": row_id,\n            \"birds\": birds\n        })\n        prediction_dfs.append(prediction_df)\n    \n    prediction_df = pd.concat(prediction_dfs, axis=0, sort=False).reset_index(drop=True)\n    return prediction_df","metadata":{"execution":{"iopub.status.busy":"2024-01-11T13:38:46.544326Z","iopub.execute_input":"2024-01-11T13:38:46.544794Z","iopub.status.idle":"2024-01-11T13:38:46.557815Z","shell.execute_reply.started":"2024-01-11T13:38:46.544760Z","shell.execute_reply":"2024-01-11T13:38:46.556417Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pip install resampy","metadata":{"execution":{"iopub.status.busy":"2024-01-11T13:38:50.452198Z","iopub.execute_input":"2024-01-11T13:38:50.452869Z","iopub.status.idle":"2024-01-11T13:39:05.386654Z","shell.execute_reply.started":"2024-01-11T13:38:50.452821Z","shell.execute_reply":"2024-01-11T13:39:05.384649Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission = prediction(test_df=test,\n                        test_audio=test_audio,\n                        model_config=model_config,\n                        mel_params=melspectrogram_parameters,\n                        weights_path=weights_path,\n                        threshold=0.8)\nsubmission.to_csv(\"submission.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2024-01-11T13:58:36.320581Z","iopub.execute_input":"2024-01-11T13:58:36.321016Z","iopub.status.idle":"2024-01-11T13:59:31.456433Z","shell.execute_reply.started":"2024-01-11T13:58:36.320985Z","shell.execute_reply":"2024-01-11T13:59:31.455440Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission","metadata":{"execution":{"iopub.status.busy":"2024-01-11T13:59:53.406138Z","iopub.execute_input":"2024-01-11T13:59:53.407264Z","iopub.status.idle":"2024-01-11T13:59:53.420581Z","shell.execute_reply.started":"2024-01-11T13:59:53.407216Z","shell.execute_reply":"2024-01-11T13:59:53.419365Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission['short_row_id'] = submission['row_id'].str[:39]\nsubmission","metadata":{"execution":{"iopub.status.busy":"2024-01-11T13:59:58.540547Z","iopub.execute_input":"2024-01-11T13:59:58.540940Z","iopub.status.idle":"2024-01-11T13:59:58.556548Z","shell.execute_reply.started":"2024-01-11T13:59:58.540909Z","shell.execute_reply":"2024-01-11T13:59:58.555130Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission.drop_duplicates(subset=\"short_row_id\", \n                     keep=\"first\", inplace=True) \nsubmission['row_id'] = submission['short_row_id']\nsubmission.drop('short_row_id',axis=1,inplace=True)\n  ","metadata":{"execution":{"iopub.status.busy":"2024-01-11T14:00:12.169048Z","iopub.execute_input":"2024-01-11T14:00:12.169512Z","iopub.status.idle":"2024-01-11T14:00:12.180638Z","shell.execute_reply.started":"2024-01-11T14:00:12.169476Z","shell.execute_reply":"2024-01-11T14:00:12.179404Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission","metadata":{"execution":{"iopub.status.busy":"2024-01-11T14:00:17.869091Z","iopub.execute_input":"2024-01-11T14:00:17.869620Z","iopub.status.idle":"2024-01-11T14:00:17.881993Z","shell.execute_reply.started":"2024-01-11T14:00:17.869579Z","shell.execute_reply":"2024-01-11T14:00:17.880783Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}