{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":25954,"databundleVersionId":2091745,"sourceType":"competition"},{"sourceId":1297722,"sourceType":"datasetVersion","datasetId":750498},{"sourceId":2130303,"sourceType":"datasetVersion","datasetId":1278322}],"dockerImageVersionId":31192,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"**Установка / импорт модуля resnet**","metadata":{}},{"cell_type":"code","source":"import shutil\nimport os\n\nshutil.copytree('../input/resnest50-fast-package/resnest-0.0.6b20200701/resnest', 'resnet', dirs_exist_ok=True)\nos.system('pip install \"./resnet\" --no-deps')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-16T10:57:51.895830Z","iopub.execute_input":"2025-11-16T10:57:51.896156Z","iopub.status.idle":"2025-11-16T10:57:58.076923Z","shell.execute_reply.started":"2025-11-16T10:57:51.896131Z","shell.execute_reply":"2025-11-16T10:57:58.076228Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Импорт библиотек**","metadata":{}},{"cell_type":"code","source":"import cv2\nimport torch\n\nimport numpy as np\nimport pandas as pd\n\nimport librosa as lb\nimport soundfile as sf\n\nimport re\nimport time\n\nfrom pathlib import Path\n\nfrom torch import nn\nfrom tqdm.notebook import tqdm\nfrom torch.utils.data import Dataset, DataLoader\nfrom resnest.torch import resnest50","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-16T10:58:01.687305Z","iopub.execute_input":"2025-11-16T10:58:01.687615Z","iopub.status.idle":"2025-11-16T10:58:07.316936Z","shell.execute_reply.started":"2025-11-16T10:58:01.687592Z","shell.execute_reply":"2025-11-16T10:58:07.316265Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Устройство для вычислений**","metadata":{}},{"cell_type":"code","source":"device = 'cuda:0' if torch.cuda.is_available() else 'cpu'\nprint('Доступное устройство: {}'.format(device))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-16T10:58:10.038604Z","iopub.execute_input":"2025-11-16T10:58:10.039098Z","iopub.status.idle":"2025-11-16T10:58:10.048218Z","shell.execute_reply.started":"2025-11-16T10:58:10.039069Z","shell.execute_reply":"2025-11-16T10:58:10.046955Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Переменные и пути**","metadata":{}},{"cell_type":"code","source":"NUM_CLASSES = 397\nSR = 32_000\nDURATION = 5\nTHRESH = 0.25\n\nTEST_AUDIO_ROOT = Path(\"../input/birdclef-2021/test_soundscapes\")\nSAMPLE_SUB_PATH = \"../input/birdclef-2021/sample_submission.csv\"\nTARGET_PATH = None\n\nif not len(list(TEST_AUDIO_ROOT.glob(\"*.ogg\"))):\n    TEST_AUDIO_ROOT = Path(\"../input/birdclef-2021/train_soundscapes\")\n    SAMPLE_SUB_PATH = None\n    TARGET_PATH = Path(\"../input/birdclef-2021/train_soundscape_labels.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-16T10:58:12.751202Z","iopub.execute_input":"2025-11-16T10:58:12.751615Z","iopub.status.idle":"2025-11-16T10:58:12.763698Z","shell.execute_reply.started":"2025-11-16T10:58:12.751581Z","shell.execute_reply":"2025-11-16T10:58:12.762948Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Класс для вычисления мел-спектрограммы аудиодорожки**","metadata":{}},{"cell_type":"code","source":"class MelSpecComputer:\n\n    def __init__(self, sr, n_mels, fmin, fmax, **kwargs):\n        self.sr = sr\n        self.n_mels = n_mels\n        self.fmin = fmin\n        self.fmax = fmax\n\n        kwargs[\"n_fft\"] = kwargs.get(\"n_fft\", self.sr//10)\n        kwargs[\"hop_length\"] = kwargs.get(\"hop_length\", self.sr//(10*4))\n\n        self.kwargs = kwargs\n\n    def __call__(self, y):\n        melspec = lb.feature.melspectrogram(y=y, sr=self.sr, n_mels=self.n_mels, fmin=self.fmin, fmax=self.fmax, **self.kwargs)\n        melspec = lb.power_to_db(melspec).astype(np.float32)\n\n        return melspec","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-16T10:58:15.872154Z","iopub.execute_input":"2025-11-16T10:58:15.872444Z","iopub.status.idle":"2025-11-16T10:58:15.880561Z","shell.execute_reply.started":"2025-11-16T10:58:15.872423Z","shell.execute_reply":"2025-11-16T10:58:15.879564Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Функция преобразования монохромного изображения в цветное**","metadata":{}},{"cell_type":"code","source":"def mono_to_color(X, eps=1e-6, mean=None, std=None):\n    mean = mean or X.mean()\n    std = std or X.std()\n    X = (X - mean) / (std + eps)\n\n    _min, _max = X.min(), X.max()\n\n    if (_max - _min) > eps:\n        V = np.clip(X, _min, _max)\n        V = 255 * (V - _min) / (_max - _min)\n        V = V.astype(np.uint8)\n    else:\n        V = np.zeros_like(X, dtype=np.uint8)\n\n    return V","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-16T10:58:18.008897Z","iopub.execute_input":"2025-11-16T10:58:18.009769Z","iopub.status.idle":"2025-11-16T10:58:18.015978Z","shell.execute_reply.started":"2025-11-16T10:58:18.009739Z","shell.execute_reply":"2025-11-16T10:58:18.015187Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Функция подгонки аудиодорожки до заданной длины**","metadata":{}},{"cell_type":"code","source":"def crop_or_pad(y, length):\n    if len(y) < length:\n        y = np.concatenate([y, length - np.zeros(len(y))])\n    elif len(y) > length:\n        y = y[:length]\n    return y","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-16T10:58:20.055097Z","iopub.execute_input":"2025-11-16T10:58:20.055939Z","iopub.status.idle":"2025-11-16T10:58:20.060379Z","shell.execute_reply.started":"2025-11-16T10:58:20.055894Z","shell.execute_reply":"2025-11-16T10:58:20.059465Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Класс датасета**","metadata":{}},{"cell_type":"code","source":"class BirdCLEFDataset(Dataset):\n\n    def __init__(self, data, sr=SR, n_mels=128, fmin=0, fmax=None, duration=DURATION, step=None, res_type=\"kaiser_fast\", resample=True):\n        self.data = data\n\n        self.sr = sr\n        self.n_mels = n_mels\n        self.fmin = fmin\n        self.fmax = fmax or self.sr//2\n\n        self.duration = duration\n        self.audio_length = self.duration*self.sr\n        self.step = step or self.audio_length\n\n        self.res_type = res_type\n        self.resample = resample\n\n        self.mel_spec_computer = MelSpecComputer(sr=self.sr, n_mels=self.n_mels, fmin=self.fmin, fmax=self.fmax)\n\n    def __len__(self):\n        return len(self.data)\n\n    @staticmethod\n    def normalize(image):\n        image = image.astype(\"float32\", copy=False) / 255.0\n        image = np.stack([image, image, image])\n\n        return image\n\n    def audio_to_image(self, audio):\n        melspec = self.mel_spec_computer(audio) \n        image = mono_to_color(melspec)\n        image = self.normalize(image)\n\n        return image\n\n    def read_file(self, filepath):\n        audio, orig_sr = sf.read(filepath, dtype=\"float32\")\n\n        if self.resample and orig_sr != self.sr:\n            audio = lb.resample(audio, orig_sr, self.sr, res_type=self.res_type)\n\n        audios = []\n        for i in range(self.audio_length, len(audio) + self.step, self.step):\n            start = max(0, i - self.audio_length)\n            end = start + self.audio_length\n            audios.append(audio[start:end])\n\n        if len(audios[-1]) < self.audio_length:\n            audios = audios[:-1]\n\n        images = [self.audio_to_image(audio) for audio in audios]\n        images = np.stack(images)\n\n        return images\n\n    def __getitem__(self, idx):\n        return self.read_file(self.data.loc[idx, \"filepath\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-16T10:58:22.217282Z","iopub.execute_input":"2025-11-16T10:58:22.217609Z","iopub.status.idle":"2025-11-16T10:58:22.229121Z","shell.execute_reply.started":"2025-11-16T10:58:22.217585Z","shell.execute_reply":"2025-11-16T10:58:22.228334Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Формирование датафрейма**","metadata":{}},{"cell_type":"code","source":"data = pd.DataFrame(\n     [(path.stem, *path.stem.split(\"_\"), path) for path in Path(TEST_AUDIO_ROOT).glob(\"*.ogg\")],\n    columns = [\"filename\", \"id\", \"site\", \"date\", \"filepath\"]\n)\ndata.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-16T10:58:25.376107Z","iopub.execute_input":"2025-11-16T10:58:25.376896Z","iopub.status.idle":"2025-11-16T10:58:25.410308Z","shell.execute_reply.started":"2025-11-16T10:58:25.376862Z","shell.execute_reply":"2025-11-16T10:58:25.409409Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Обучающие данные**","metadata":{}},{"cell_type":"code","source":"df_train = pd.read_csv(\"../input/birdclef-2021/train_metadata.csv\")\n\nLABEL_IDS = {label: label_id for label_id,label in enumerate(sorted(df_train[\"primary_label\"].unique()))}\nINV_LABEL_IDS = {val: key for key,val in LABEL_IDS.items()}\n\ntest_data = BirdCLEFDataset(data=data)\nlen(test_data), test_data[0].shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-16T10:58:30.279694Z","iopub.execute_input":"2025-11-16T10:58:30.280530Z","iopub.status.idle":"2025-11-16T10:58:50.294901Z","shell.execute_reply.started":"2025-11-16T10:58:30.280491Z","shell.execute_reply":"2025-11-16T10:58:50.294141Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Функция загрузки модели**","metadata":{}},{"cell_type":"code","source":"def load_net(checkpoint_path, num_classes=NUM_CLASSES):\n    net = resnest50(pretrained=False)\n    net.fc = nn.Linear(net.fc.in_features, num_classes)\n\n    dummy_device = torch.device(\"cpu\")\n    d = torch.load(checkpoint_path, map_location=dummy_device)\n\n    for key in list(d.keys()):\n        d[key.replace(\"model.\", \"\")] = d.pop(key)\n\n    net.load_state_dict(d)\n    net = net.to(device)\n    net = net.eval()\n\n    return net","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-16T10:58:53.921419Z","iopub.execute_input":"2025-11-16T10:58:53.921883Z","iopub.status.idle":"2025-11-16T10:58:53.927656Z","shell.execute_reply.started":"2025-11-16T10:58:53.921862Z","shell.execute_reply":"2025-11-16T10:58:53.926639Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Функции для предсказания результатов**","metadata":{}},{"cell_type":"code","source":"@torch.no_grad()\ndef get_thresh_preds(out, thresh=None):\n    thresh = thresh or THRESH\n    o = (-out).argsort(1)\n    npreds = (out > thresh).sum(1)\n    preds = []\n\n    for oo, npred in zip(o, npreds):\n        preds.append(oo[:npred].cpu().numpy().tolist())\n\n    return preds\n\ndef get_bird_names(preds):\n    bird_names = []\n\n    for pred in preds:\n        if not pred:\n            bird_names.append(\"nocall\")\n        else:\n            bird_names.append(\" \".join([INV_LABEL_IDS[bird_id] for bird_id in pred]))\n\n    return bird_names\n\ndef predict(nets, test_data, names=True):\n    preds = []\n\n    with torch.no_grad():\n        for idx in  tqdm(list(range(len(test_data)))):\n            xb = torch.from_numpy(test_data[idx]).to(device)\n            pred = 0.\n\n            for net in nets:\n                o = net(xb)\n                o = torch.sigmoid(o)\n                pred += o\n\n            pred /= len(nets)\n\n            if names:\n                pred = get_bird_names(get_thresh_preds(pred))\n\n            preds.append(pred)\n\n    return preds","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-16T10:58:56.635534Z","iopub.execute_input":"2025-11-16T10:58:56.635882Z","iopub.status.idle":"2025-11-16T10:58:56.643914Z","shell.execute_reply.started":"2025-11-16T10:58:56.635858Z","shell.execute_reply":"2025-11-16T10:58:56.642901Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Пути моделей**","metadata":{}},{"cell_type":"code","source":"checkpoint_paths = [Path(\"../input/kkiller-birdclef-models-public/birdclef_resnest50_fold0_epoch_10_f1_val_06471_20210417161101.pth\")]\n\nnets = [load_net(checkpoint_path.as_posix()) for checkpoint_path in checkpoint_paths]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-16T10:59:00.074263Z","iopub.execute_input":"2025-11-16T10:59:00.074937Z","iopub.status.idle":"2025-11-16T10:59:01.899450Z","shell.execute_reply.started":"2025-11-16T10:59:00.074907Z","shell.execute_reply":"2025-11-16T10:59:01.898509Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Результаты**","metadata":{}},{"cell_type":"code","source":"pred_probas = predict(nets, test_data, names=False)\npreds = [get_bird_names(get_thresh_preds(pred, thresh=THRESH)) for pred in pred_probas]\n\ndef preds_as_df(data, preds):\n    sub = {\n        \"row_id\": [],\n        \"birds\": []\n    }\n\n    for row, pred in zip(data.itertuples(False), preds):\n        row_id = [f\"{row.id}_{row.site}_{5*i}\" for i in range(1, len(pred)+1)]\n        sub[\"birds\"] += pred\n        sub[\"row_id\"] += row_id\n\n    sub = pd.DataFrame(sub)\n\n    if SAMPLE_SUB_PATH:\n        sample_sub = pd.read_csv(SAMPLE_SUB_PATH, usecols=[\"row_id\"])\n        sub = sample_sub.merge(sub, on=\"row_id\", how=\"left\")\n        sub[\"birds\"] = sub[\"birds\"].fillna(\"nocall\")\n\n    return sub\n\nsub = preds_as_df(data, preds)\nsub","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-16T10:59:08.219367Z","iopub.execute_input":"2025-11-16T10:59:08.220148Z","iopub.status.idle":"2025-11-16T11:16:50.545126Z","shell.execute_reply.started":"2025-11-16T10:59:08.220121Z","shell.execute_reply":"2025-11-16T11:16:50.544119Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Сохранение и отправка результатов**","metadata":{}},{"cell_type":"code","source":"sub.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-16T11:17:07.471800Z","iopub.execute_input":"2025-11-16T11:17:07.472809Z","iopub.status.idle":"2025-11-16T11:17:07.488507Z","shell.execute_reply.started":"2025-11-16T11:17:07.472775Z","shell.execute_reply":"2025-11-16T11:17:07.487604Z"}},"outputs":[],"execution_count":null}]}