{"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":91844,"databundleVersionId":11361821,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":11050935,"sourceType":"datasetVersion","datasetId":6884608},{"sourceId":12100666,"sourceType":"datasetVersion","datasetId":7618072}],"dockerImageVersionId":30918,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Install openvino libs","metadata":{}},{"cell_type":"code","source":"!pip install /kaggle/input/runtimes-onnx-openvino/openvino/openvino_telemetry-2025.1.0-py3-none-any.whl --no-index --find-links /kaggle/input/runtimes-onnx-openvino/openvino\n!pip install /kaggle/input/runtimes-onnx-openvino/openvino/openvino-2025.0.0-17942-cp310-cp310-manylinux2014_x86_64.whl --no-index --find-links /kaggle/input/runtimes-onnx-openvino/openvino","metadata":{"execution":{"iopub.status.busy":"2025-06-08T18:56:43.349892Z","iopub.execute_input":"2025-06-08T18:56:43.350252Z","iopub.status.idle":"2025-06-08T18:56:56.522339Z","shell.execute_reply.started":"2025-06-08T18:56:43.350212Z","shell.execute_reply":"2025-06-08T18:56:56.520644Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Imports","metadata":{}},{"cell_type":"code","source":"import os\nimport tqdm\nimport ast\nfrom glob import glob\nimport math\nimport joblib\n\nfrom scipy.ndimage import convolve1d\nimport pandas as pd\nimport numpy as np\nimport librosa\nimport timm\nimport torch\nimport torch.nn as nn\nfrom torch.nn import functional as F\nimport torchaudio.transforms as T\nfrom openvino.runtime import Core\nimport openvino as ov","metadata":{"execution":{"iopub.status.busy":"2025-06-08T18:56:58.165583Z","iopub.execute_input":"2025-06-08T18:56:58.165983Z","iopub.status.idle":"2025-06-08T18:57:12.226271Z","shell.execute_reply.started":"2025-06-08T18:56:58.165943Z","shell.execute_reply":"2025-06-08T18:57:12.224546Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# SED Model Architecture","metadata":{}},{"cell_type":"code","source":"def gem_freq(x, p=3, eps=1e-6):\n    return F.avg_pool2d(x.clamp(min=eps).pow(p), (x.size(-2), 1)).pow(1.0 / p)\n\n\nclass GeMFreq(nn.Module):\n    def __init__(self, p=3, eps=1e-6):\n        super().__init__()\n        self.p = nn.Parameter(torch.ones(1) * p)\n        self.eps = eps\n\n    def forward(self, x):\n        return gem_freq(x, p=self.p, eps=self.eps)\n\n\nclass AttHead(nn.Module):\n    \n    def __init__(self, in_chans, p=0.5, num_class=397, hidden_dim=512):\n        super().__init__()\n        \n        self.pooling = GeMFreq()\n        self.dense_layers = nn.Sequential(\n                nn.Dropout(p / 2),\n                nn.Linear(in_chans, hidden_dim),\n                nn.ReLU(),\n                nn.Dropout(p),\n            )\n        self.attention = nn.Conv1d(\n            in_channels=hidden_dim,\n            out_channels=num_class,\n            kernel_size=1,\n            stride=1,\n            padding=0,\n            bias=True,\n        )\n        self.fix_scale = nn.Conv1d(\n            in_channels=hidden_dim,\n            out_channels=num_class,\n            kernel_size=1,\n            stride=1,\n            padding=0,\n            bias=True,\n        )\n\n    def forward(self, feat):\n\n        feat = self.pooling(feat).squeeze(-2).permute(0, 2, 1)  # (bs, time, ch) \n\n        feat = self.dense_layers(feat).permute(0, 2, 1)  # (bs, 512, time)\n        \n        # time_att_logit = torch.tanh(self.attention(feat))\n\n        framewise_logit = self.fix_scale(feat)\n        framewise_prob = torch.sigmoid(framewise_logit)\n        \n        # clipwise_prob = torch.sum(\n        #     segmentwise_prob * torch.softmax(time_att_logit, dim=-1),\n        #     dim=-1,\n        # )\n        output = {\n            # \"clipwise_prob\": clipwise_prob,\n            # \"time_att_logit\": time_att_logit,\n            \"framewise_logit\": framewise_logit\n        }\n        return output\n        \n        \nclass NormalizeMelSpec(nn.Module):\n    \n    def __init__(self, norm_type=\"default\", eps=1e-6, constant=80):\n        super().__init__()\n        self.eps = eps\n        self.norm_type = norm_type\n        self.constant = constant\n\n    def forward(self, X):\n        \n        if self.norm_type == \"default\":\n            mean = X.mean((1, 2), keepdim=True)\n            std = X.std((1, 2), keepdim=True)\n            \n            Xstd = (X - mean) / (std + self.eps)\n            norm_max = torch.amax(Xstd, dim=(1, 2), keepdim=True)\n            norm_min = torch.amin(Xstd, dim=(1, 2), keepdim=True)\n            return (Xstd - norm_min) / (norm_max - norm_min + self.eps)\n            \n        elif self.norm_type == \"top_db\":\n            X = (X + 80) / 80\n            return X\n\n        elif self.norm_type == \"constant\":\n            X = X / self.constant\n            return X\n            \n        \nclass SpecFeatureExtractor(nn.Module):\n    \n    def __init__(\n        self,\n        n_fft: int,\n        hop_length: int,\n        win_length = None,\n        sample_rate=200,\n        f_max=20,\n        f_min=0.5,\n        n_mels=128,\n        top_db=120,\n        normalized=False,\n        sample_mel_normalize=None,\n        output_size=(256, 256),\n    ):\n        \n        super().__init__()\n\n        self.feature_extractor = nn.Sequential()\n        self.feature_extractor.append(\n                    T.MelSpectrogram(sample_rate=sample_rate, \n                                     normalized=normalized,\n                                     n_fft=n_fft, \n                                     hop_length=hop_length, \n                                     win_length=win_length, \n                                     f_max=f_max,\n                                     n_mels=n_mels, \n                                     f_min=f_min)\n        )\n        \n        self.feature_extractor.append(T.AmplitudeToDB(top_db=top_db))\n        \n        if sample_mel_normalize is not None:\n            self.feature_extractor.append(NormalizeMelSpec(norm_type=sample_mel_normalize))\n\n        if output_size is not None:\n            self.resize = nn.UpsamplingBilinear2d(size=output_size)\n        else:\n            self.resize = None\n\n    def norm(self, x: torch.Tensor):\n        img_norm = self.feature_extractor[-1](x)\n        return img_norm\n            \n    def forward(self, x: torch.Tensor, without_norm=False) -> torch.Tensor:\n        if without_norm:\n            img = self.feature_extractor[:-1](x)\n        else:\n            img = self.feature_extractor(x)\n        if self.resize is not None:\n            img = self.resize(img.unsqueeze(1)).squeeze(1)\n\n        return img\n    \n\nclass CLEFClassifierSED(nn.Module):\n    \n    def __init__(self, \n                 config, \n                 disable_spectr_generator=False):\n        \n        super(CLEFClassifierSED, self).__init__()\n        \n        spectrogram_config = config['spectrogram']\n        backbone_config = config['backbone']\n        head_config = config['head']\n        \n        self.mel_spectr_generator = SpecFeatureExtractor(**spectrogram_config)\n                \n        self.backbone = timm.create_model(backbone_config['backbone_name'],\n                                          pretrained=backbone_config['pretrained'],\n                                          features_only=True)\n        backbone_dim = self.backbone.feature_info.channels()[-1]\n        \n        self.head = AttHead(in_chans=backbone_dim, \n                            p=head_config['dropout'], \n                            num_class=head_config['num_classes'])\n        self.infer_duration = head_config['infer_duration']\n        self.duration = head_config['duration']\n        self.inference_type = config['inference_type']\n\n        self.multilabel_to_train_labels = config.get('multilabel_to_train_labels', \"one2one\")\n\n    def get_head_preds(self, input, tta_delta=2, sigmoid=True):\n        \n        head_output = self.head(input)\n        feat_time = input.shape[-1]\n        framewise_prob = head_output[\"framewise_logit\"].sigmoid().numpy()\n        num_segments, num_labels, frames_per_segment = framewise_prob.shape\n        \n        step = int(feat_time * (self.infer_duration / self.duration))\n        \n        pad_frames = (frames_per_segment - step) // 2\n        if (frames_per_segment - step) % 2 != 0:\n            extra_pad = 1\n        else:\n            extra_pad = 0\n        framewise_ss_len = frames_per_segment + step*(num_segments - 1)\n        \n        \n        framewise_ss_preds = np.zeros((framewise_ss_len, num_segments, num_labels), dtype=np.float16)\n        framewise_ss_preds_mask = np.zeros((framewise_ss_len, 1), dtype=np.float16)\n        \n        for segment_ind in range(num_segments):\n            framewise_ss_preds[segment_ind*step: (segment_ind*step) + frames_per_segment, segment_ind] += framewise_prob[segment_ind].T\n            framewise_ss_preds_mask[segment_ind*step: (segment_ind*step) + frames_per_segment] += 1\n        \n        if self.inference_type in [\"overlap_average_max\", \"overlap_average_max_delta\"]:\n            framewise_ss_preds = framewise_ss_preds.sum(1)\n            framewise_ss_preds = framewise_ss_preds / framewise_ss_preds_mask\n            \n        elif self.inference_type == \"overlap_max\":\n            framewise_ss_preds = framewise_ss_preds.max(1)\n\n        framewise_ss_preds = framewise_ss_preds[pad_frames:-pad_frames - extra_pad]\n        segmentwise_preds = framewise_ss_preds.reshape(num_segments, step, num_labels).max(1)\n        if self.inference_type == \"overlap_average_max_delta\": \n            segmentwise_preds *= 0.5\n            for segment_ind in range(num_segments):\n                if segment_ind == 0:\n                    segmentwise_preds[segment_ind] += framewise_ss_preds[segment_ind*step: (segment_ind+1)*step].max(0) * 0.25\n                else:\n                    segmentwise_preds[segment_ind] += framewise_ss_preds[segment_ind*step - tta_delta: (segment_ind+1)*step - tta_delta].max(0) * 0.25\n    \n                if segment_ind == (num_segments -1):\n                    segmentwise_preds[segment_ind] += framewise_ss_preds[segment_ind*step: (segment_ind+1)*step].max(0) * 0.25\n                else:\n                    segmentwise_preds[segment_ind] += framewise_ss_preds[segment_ind*step + tta_delta: (segment_ind+1)*step + tta_delta].max(0) * 0.25\n        \n        return segmentwise_preds\n            \n\n    def forward(self, \n                input, \n                labels=None,\n                train=False):\n    \n        if not train:\n            return {\"preds\": self.inference(input)}\n                \n        x = self.mel_spectr_generator(input)    \n        \n        if self.augmentations is not None and train:\n            x = self.augmentations(x)\n    \n        x = torch.stack([x, x, x], 1)\n        \n        # if train and labels is not None:\n        #     x, labels = horizontal_cutmix(x, labels)    \n        \n        x = self.backbone(x)[-1]\n        head_output = self.head(x)\n        if train and labels is not None:\n            loss = 0.5 * self.critarion(torch.logit(head_output[\"clipwise_prob\"]), labels) + 0.5 * self.critarion(head_output[\"segmentwise_logit\"].max(2)[0], labels)\n        else:\n            loss = None\n        output = {\n            \"preds\": torch.logit(head_output[\"clipwise_prob\"]),\n            \"loss\": loss,\n            \"labels\": labels\n        }\n        return output","metadata":{"execution":{"iopub.status.busy":"2025-06-08T19:00:02.542357Z","iopub.execute_input":"2025-06-08T19:00:02.542712Z","iopub.status.idle":"2025-06-08T19:00:02.575299Z","shell.execute_reply.started":"2025-06-08T19:00:02.542684Z","shell.execute_reply":"2025-06-08T19:00:02.573829Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Inference Dataset, Inference Engine","metadata":{}},{"cell_type":"code","source":"class InferenceDataset:\n    def __init__(self, all_waves, feature_extractor, full_signal=True, hop_length=1252, img_size=(224, 512), sample_rate=32000, duration_sec=5, slice_step_sec=5, num_segments_sample=12, res_type=\"kaiser_best\", separate_norm=False):\n        \n        self.all_waves = all_waves\n        self.all_filenames = list(all_waves.keys())\n        \n        self.feature_extractor = feature_extractor\n        self.sample_rate = sample_rate\n        self.duration_timestamps = sample_rate*duration_sec\n        self.slice_step_timestamps = sample_rate*slice_step_sec\n        self.num_segments_sample = num_segments_sample\n        self.res_type = res_type\n        \n        self.full_signal = full_signal\n        self.hop_length = hop_length\n        self.img_size = img_size\n        self.separate_norm = separate_norm\n\n    def __len__(self):\n        return len(self.all_filenames)\n        \n    def split_wave(self, wave):\n        if self.slice_step_timestamps != self.duration_timestamps:\n            pad_side_length = int((self.duration_timestamps - self.slice_step_timestamps) / 2)\n            wave = np.pad(wave, (pad_side_length, pad_side_length))\n            segments = [librosa.util.normalize(wave[i*self.slice_step_timestamps: i*self.slice_step_timestamps + self.duration_timestamps]) for i in range(self.num_segments_sample)]\n        else:\n            segments = [librosa.util.normalize(wave[i*self.duration_timestamps: (i+1)*self.duration_timestamps]) for i in range(self.num_segments_sample)] \n        segments = np.stack(segments, 0)\n        return segments\n\n    def prepare_mel_specs_from_segments(self, wave):\n        segments = self.split_wave(wave=wave)\n        mel_specs = self.feature_extractor(torch.tensor(segments))\n        return mel_specs\n\n    def prepare_mel_specs_from_full_wave(self, wave):\n        \n        def get_wave_len(hop_length, time_bins):\n                return (time_bins -1) * hop_length\n            \n        total_time_bins = self.img_size[1] + (self.num_segments_sample - 1)*(self.img_size[1]*self.slice_step_timestamps/self.duration_timestamps)\n        \n        if not self.separate_norm:\n            total_wave_len = get_wave_len(hop_length=self.hop_length, time_bins=total_time_bins)\n            pad_side_length = math.ceil(((total_wave_len - len(wave)) / 2))\n            wave = np.pad(wave, (pad_side_length, pad_side_length))\n            \n        mel_spec_full = self.feature_extractor(torch.tensor(wave.reshape(1, -1)), without_norm=self.separate_norm)\n        \n        if self.separate_norm:\n            pad_side_length = int((total_time_bins- mel_spec_full.shape[-1]) // 2)\n            extra_pad = 0\n            if (total_time_bins - mel_spec_full.shape[-1]) % 2 !=0:\n                extra_pad = 1\n            mel_spec_full = F.pad(mel_spec_full, pad=(pad_side_length, pad_side_length + extra_pad, 0, 0), mode='constant', value=mel_spec_full.min())\n\n        slice_step = int((self.img_size[1]*self.slice_step_timestamps/self.duration_timestamps))\n        mel_specs = []\n        for i in range(self.num_segments_sample):\n            mel_specs.append(mel_spec_full[0, :, i*slice_step: (i*slice_step) + self.img_size[1]])\n        mel_specs = torch.stack(mel_specs, 0)\n\n        if self.separate_norm:\n            mel_specs = self.feature_extractor.norm(mel_specs)\n        return mel_specs\n\n    def __getitem__(self, ind):\n\n        filename = self.all_filenames[ind]\n        wave = self.all_waves[filename]\n        \n        if self.full_signal:\n            mel_specs = self.prepare_mel_specs_from_full_wave(wave=wave)\n        else:\n            mel_specs = self.prepare_mel_specs_from_segments(wave=wave)\n            \n        return mel_specs, filename\n        \n\nclass VINOEngine:\n    \n    def __init__(self, model, example_dims=(12, 3, 256, 256), ov_model_path=None):\n        self.ov_model = self.torch2openvinio(model, example_dims=example_dims, ov_model_path=ov_model_path)\n        self.output_layer = -1\n\n    def torch2openvinio(self, model, example_dims, device_name=\"AUTO\", ov_model_path=None):\n        core = ov.Core()\n        example = torch.randn(example_dims) \n        if ov_model_path is not None:\n            ov_model_pytorch = core.read_model(model=ov_model_path)\n        else:\n            ov_model_pytorch = ov.convert_model(model, example_input=(example,))\n        compiled_model_pytorch = core.compile_model(ov_model_pytorch, device_name=device_name)\n        return compiled_model_pytorch\n\n    def __call__(self, input):\n        result = self.ov_model([input])[self.output_layer]\n        return result","metadata":{"execution":{"iopub.status.busy":"2025-06-08T18:57:18.500435Z","iopub.execute_input":"2025-06-08T18:57:18.500774Z","iopub.status.idle":"2025-06-08T18:57:18.521433Z","shell.execute_reply.started":"2025-06-08T18:57:18.500747Z","shell.execute_reply":"2025-06-08T18:57:18.520128Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Configs","metadata":{}},{"cell_type":"code","source":"def get_multilabel_to_train_labels(multilabel_label2ind, train_label2ind):\n    multilabel_to_train_labels = {ind: train_label2ind[label] for label, ind in multilabel_label2ind.items() if label in train_label2ind}\n    return multilabel_to_train_labels\n    \nclass InferenceConfig:\n    label2ind = {'grekis': 0, 'compau': 1, 'trokin': 2, 'roahaw': 3, 'banana': 4, 'whtdov': 5, 'socfly1': 6, 'yeofly1': 7, 'bobfly1': 8, 'wbwwre1': 9, 'soulap1': 10, 'sobtyr1': 11, 'trsowl': 12, 'laufal1': 13, 'strcuc1': 14, 'bbwduc': 15, 'saffin': 16, 'amekes': 17, 'tropar': 18, 'compot1': 19, 'blbgra1': 20, 'bubwre1': 21, 'strfly1': 22, 'gycwor1': 23, 'greegr': 24, 'linwoo1': 25, 'pirfly1': 26, 'littin1': 27, 'bkmtou1': 28, 'yercac1': 29, 'butsal1': 30, 'smbani': 31, 'bugtan': 32, 'chbant1': 33, 'yebela1': 34, 'rutjac1': 35, 'cotfly1': 36, 'whbman1': 37, 'yehcar1': 38, 'solsan': 39, 'rumfly1': 40, 'yecspi2': 41, 'blhpar1': 42, 'creoro1': 43, 'paltan1': 44, 'rinkin1': 45, 'orcpar': 46, 'stbwoo2': 47, 'speowl1': 48, 'yebfly1': 49, 'plbwoo1': 50, 'yebsee1': 51, 'bkcdon': 52, 'strher': 53, 'y00678': 54, 'babwar': 55, 'strowl1': 56, 'gybmar': 57, 'cocwoo1': 58, 'secfly1': 59, 'thbeup1': 60, 'pavpig2': 61, 'baymac': 62, 'rtlhum': 63, 'purgal2': 64, 'colcha1': 65, 'crcwoo1': 66, 'ywcpar': 67, 'chfmac1': 68, 'rugdov': 69, 'gohman1': 70, 'watjac1': 71, 'grnkin': 72, 'greani1': 73, 'whfant1': 74, 'cattyr': 75, 'srwswa1': 76, 'blbwre1': 77, 'mastit1': 78, 'greibi1': 79, 'snoegr': 80, '41663': 81, 'leagre': 82, 'blcjay1': 83, 'grbhaw1': 84, 'eardov1': 85, 'blcant4': 86, 'whbant1': 87, 'yectyr1': 88, 'rufmot1': 89, 'thlsch3': 90, 'cargra1': 91, 'bicwre1': 92, 'anhing': 93, 'neocor': 94, 'shtfly1': 95, 'recwoo1': 96, 'amakin1': 97, 'ragmac1': 98, 'grasal4': 99, 'gretin1': 100, '65448': 101, 'spepar1': 102, 'fotfly': 103, 'ruther1': 104, 'yehbla2': 105, 'cregua1': 106, '21211': 107, 'whttro1': 108, 'brtpar1': 109, 'rubsee1': 110, 'blkvul': 111, 'verfly': 112, 'cinbec1': 113, 'labter1': 114, 'grepot1': 115, 'palhor2': 116, 'yelori1': 117, '517119': 118, 'colara1': 119, 'crbtan1': 120, 'rebbla1': 121, 'piepuf1': 122, 'savhaw1': 123, 'blchaw1': 124, '22973': 125, 'crebob1': 126, 'whwswa1': 127, 'spbwoo1': 128, '22333': 129, 'bucmot3': 130, '22976': 131, 'tbsfin1': 132, 'cocher1': 133, 'royfly1': 134, 'bobher1': 135, 'olipic1': 136, 'plukit1': 137, 'whmtyr1': 138, 'rosspo1': 139, '52884': 140, '65373': 141, 'blctit1': 142, '50186': 143, 'ampkin1': 144, 'bafibi1': 145, 'woosto': 146, '555086': 147, 'grysee1': 148, '566513': 149, '65962': 150, '48124': 151, 'bubcur1': 152, '42007': 153, 'piwtyr1': 154, 'rutpuf1': 155, '715170': 156, '65349': 157, '65344': 158, '41970': 159, 'shghum1': 160, 'norscr1': 161, 'sahpar1': 162, '67252': 163, '24322': 164, 'turvul': 165, '135045': 166, '65547': 167, '787625': 168, '1462737': 169, 'plctan1': 170, '555142': 171, '126247': 172, '65336': 173, '1564122': 174, '24272': 175, '548639': 176, '46010': 177, '1346504': 178, '963335': 179, '476538': 180, '714022': 181, '66893': 182, '134933': 183, '1192948': 184, '868458': 185, '523060': 186, '24292': 187, '65419': 188, '1194042': 189, '1462711': 190, '81930': 191, '67082': 192, '66578': 193, '66531': 194, '66016': 195, '21038': 196, '41778': 197, '21116': 198, '64862': 199, '528041': 200, '476537': 201, '47067': 202, '42113': 203, '42087': 204, '1139490': 205}\n    class_ind2label_inds = {0: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 102, 103, 104, 105, 106, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 119, 120, 121, 122, 123, 124, 126, 127, 128, 130, 132, 133, 134, 135, 136, 137, 138, 139, 142, 144, 145, 146, 148, 152, 154, 155, 160, 161, 162, 165, 170], 1: [101, 107, 118, 125, 129, 131, 141, 147, 150, 157, 158, 163, 164, 166, 167, 168, 171, 172, 173, 175, 180, 182, 183, 187, 188, 191, 192, 193, 194, 195, 196, 198, 199, 201], 2: [81, 149, 153, 159, 177, 197, 202, 203, 204], 3: [140, 143, 151, 156, 169, 174, 176, 178, 179, 181, 184, 185, 186, 189, 190, 200, 205]}\n    sample_rate = 32000\n    num_classes = len(label2ind)\n    num_segments_sample = 12\n\n    # preprocessing\n    separate_norm = False\n    \n    # postprocessing\n    smoothing = True\n    preds_weights = np.array([0.133, 0.166, 0.133, 0.133, 0.166, 0.133, 0.133])\n    preds_power = 1\n\n\nclass ModelsGroupConfig:\n\n    full_signal_to_spectr = True\n    duration = 20\n    slice_step_sec = 5\n\n    img_size = (224, 512)\n    n_mels = img_size[0]\n    hop_length = duration * InferenceConfig.sample_rate // (img_size[1] - 1)\n    resize = False\n    n_fft = 2048*2\n    f_min_max = (0, 16000)\n    \n    example_dims = (InferenceConfig.num_segments_sample, 3, img_size[-2], img_size[-1])\n    spectrogram_params = {\n                        \"n_fft\": n_fft,\n                        \"hop_length\": hop_length,\n                        \"win_length\": n_fft,\n                        \"sample_rate\": InferenceConfig.sample_rate,\n                        \"n_mels\": n_mels,\n                        \"f_min\": f_min_max[0],\n                        \"f_max\": f_min_max[1],\n                        \"normalized\": True,\n                        \"top_db\": 80,\n                        \"sample_mel_normalize\": \"default\",\n                        \"output_size\": None if not resize else img_size\n            }\n\n    inference_type = \"overlap_average_max_delta\"\n    \n\n    model_config_1 = {\n            \"model_type\": \"sed\",\n            \"example_dims\" : example_dims,\n            \"spectrogram\": spectrogram_params,\n            \"backbone\": {\n                \"backbone_name\": 'timm/tf_efficientnet_b3.ns_jft_in1k',\n                'pretrained': False,\n                \"last_layer_hidden_dim\": None\n            },\n            \"head\": {\n                'dropout': 0.50,\n                'num_classes': InferenceConfig.num_classes,\n                \"infer_duration\": 5,\n                \"duration\": duration\n            },\n            \"inference_type\": inference_type\n    }\n\n    model_config_2 = {\n            \"model_type\": \"sed\",\n            \"example_dims\" : example_dims,\n            \"spectrogram\": spectrogram_params,\n            \"backbone\": {\n                \"backbone_name\": 'timm/regnety_016.tv2_in1k',\n                'pretrained': False,\n                \"last_layer_hidden_dim\": None\n            },\n            \"head\": {\n                'dropout': 0.50,\n                'num_classes': InferenceConfig.num_classes,\n                \"infer_duration\": 5,\n                \"duration\": duration\n            },\n            \"inference_type\": inference_type\n    }\n\n    model_config_3 = {\n            \"model_type\": \"sed\",\n            \"example_dims\": example_dims,\n            \"spectrogram\": spectrogram_params,\n            \"backbone\": {\n                \"backbone_name\": 'timm/regnety_008.pycls_in1k',\n                'pretrained': False,\n                \"last_layer_hidden_dim\": None\n            },\n            \"head\": {\n                'dropout': 0.50,\n                'num_classes': InferenceConfig.num_classes,\n                \"infer_duration\": 5,\n                \"duration\": duration\n            },\n            \"inference_type\": inference_type\n    }\n\n    label2ind_I_A = {'metrioptera_saussuriana': 0, 'bicolorana_bicolor': 1, 'ephippiger_diurnus': 2, 'eumodicogryllus_bordigalensis': 3, 'decticus_verrucivorus': 4, 'decticus_albifrons': 5, 'roeseliana_roeselii': 6, 'barbitistes_fischeri': 7, 'metaplastes_ornatus': 8, 'platycleis_affinis': 9, 'poecilimon_sanctipauli': 10, 'pseudochorthippus_parallelus': 11, 'chorthippus_biguttulus': 12, 'chorthippus_brunneus': 13, 'chorthippus_mollis': 14, 'eupholidoptera_schmidti': 15, 'gryllus_campestris': 16, 'tessellana_lagrecai': 17, 'saga_hellenica': 18, 'metrioptera_brachyptera': 19, 'oecanthus_pellucens': 20, 'platycleis_albopunctata': 21, 'phaneroptera_falcata': 22, 'canariola_emarginata': 23, 'pholidoptera_griseoaptera': 24, 'platycleis_intermedia': 25, 'pteronemobius_heydenii': 26, 'gryllus_bimaculatus': 27, 'phaneroptera_sparsa': 28, 'yersinella_raymondii': 29, 'tettigonia_viridissima': 30, 'roeseliana_azami': 31, 'tettigonia_cantans': 32, 'poecilimon_jonicus': 33, 'ruspolia_nitidula': 34, 'paragraecia_temasek': 35, 'isophya_lemnotica': 36, 'ancistrura_nigrovittata': 37, 'barbitistes_ocskayi': 38, 'leptophyes_punctatissima': 39, 'tessellana_tessellata': 40, 'pholidoptera_femorata': 41, 'conocephalus_fuscus': 42, 'poecilimon_affinis': 43, 'phaneroptera_nana': 44, 'poecilimon_ornatus': 45, 'tylopsis_lilifolia': 46, 'antaxius_spinibrachius': 47, 'cyrtaspis_scutata': 48, 'uromenus_rugosicollis': 49, 'sepiana_sepium': 50, 'teleogryllus_mitratus': 51, 'nemobius_sylvestris': 52, 'poecilimon_artedentatus': 53, 'neophisis_montealegrei': 54, 'chorthippus_vagans': 55, 'rhacocleis_germanica': 56, '65448': 57, 'chorthippus_jacobsi': 58, 'eupholidoptera_latens': 59, 'gryllotalpa_gryllotalpa': 60, 'pterolepis_spoliata': 61, 'barbitistes_serricauda': 62, '21211': 63, 'platycleis_grisea': 64, 'antaxius_pedestris': 65, 'eugryllodes_pipiens': 66, 'platycleis_sabulosa': 67, 'viriacca_modesta': 68, 'poecilimon_mytilenensis': 69, 'poecilimon_zwicki': 70, 'barbitistes_yersini': 71, 'poecilimon_veluchianus': 72, 'isophya_rhodopensis': 73, 'thyreonotus_corsicus': 74, 'stenobothrus_stigmaticus': 75, 'lluciapomaresius_stalii': 76, 'chorthippus_saulcyi': 77, 'poecilimon_werneri': 78, 'pholidoptera_littoralis': 79, '517119': 80, 'melanogryllus_desertus': 81, 'leptophyes_laticauda': 82, 'sonus_naturalis': 83, 'poecilimon_nobilis': 84, 'stauroderus_scalaris': 85, 'svercus_palmetorum': 86, 'poecilimon_zimmeri': 87, '22973': 88, 'platycleis_falx': 89, 'zeuneriana_abbreviata': 90, 'poecilimon_hamatus': 91, 'ornebius_pullus': 92, 'pelophylax_ridibundus': 93, 'isophya_modestior': 94, 'polysarcus_denticauda': 95, 'chorthippus_apricarius': 96, 'asiophlugis_temasek': 97, 'sorapagus_catalaunicus': 98, 'antaxius_hispanicus': 99, 'pholidoptera_aptera': 100, 'oecanthus_dulcisonans': 101, 'omocestus_viridulus': 102, 'loxoblemmus_parabolicus': 103, '22333': 104, 'stenobothrus_lineatus': 105, 'gryllotalpa_africana': 106, 'leptophyes_albovittata': 107, 'parasteropleurus_perezii': 108, 'andreiniimon_nuptialis': 109, 'poecilimon_hoelzeli': 110, 'barbitistes_obtusus': 111, '22976': 112, 'pholidoptera_fallax': 113, 'acheta_domesticus': 114, 'gryllodes_sigillatus': 115, 'omocestus_raymondi': 116, 'conocephalus_dorsalis': 117, 'isophya_kraussii': 118, 'leptophyes_boscii': 119, 'poecilimon_chopardi': 120, 'casigneta_sp.2': 121, 'asiophlugis_longiuncus': 122, 'varitrella_bakeri': 123, 'chrysochraon_dispar': 124, 'asiophlugis_thaumasia': 125, 'poecilimon_tessellatus': 126, 'loxoblemmus_arietulus': 127, 'gampsocleis_glabra': 128, 'uromenus_brevicollis': 129, 'axylus_philippinus': 130, 'metaplastes_pulchripennis': 131, 'antaxius_difformis': 132, 'isophya_obtusa': 133, 'hyla_arborea': 134, 'alloteratura_lamella': 135, 'borneopsis_cryptosticta': 136, 'eupholidoptera_megastyla': 137, 'platycleis_concii': 138, 'rhacocleis_corsicana': 139, 'tettigonia_caudata': 140, 'poecilimon_ebneri': 141, 'rhacocleis_annulata': 142, 'phaneroptera_brevis': 143, 'isophya_rectipennis': 144, 'natula_averni': 145, 'chorthippus_binotatus': 146, 'peracca_macritchiensis': 147, 'pholidoptera_stankoi': 148, 'ephippiger_ephippiger': 149, 'poecilimon_amissus': 150, 'gryllotalpa_vineae': 151, 'antaxius_kraussii': 152, 'holochlora_bilobata': 153, 'hyla_meridionalis': 154, 'conocephalus_kisi': 155, 'gomphocerippus_rufus': 156, 'parasteropleurus_martorellii': 157, 'omocestus_antigai': 158, 'omocestus_bolivari': 159, 'omocestus_rufipes': 160, 'euchorthippus_declivus': 161, 'platycleis_escalerai': 162, 'salomona_borneensis': 163, 'neocallicrania_selligera': 164, 'chorthippus_dorsatus': 165, 'poecilimon_macedonicus': 166, 'eupholidoptera_smyrnensis': 167, 'stenobothrus_nigromaculatus': 168, 'poecilimon_fussii': 169, 'stethophyma_grossum': 170, 'incertana_incerta': 171, 'zvenella_geniculata': 172, 'poecilimon_laevissimus': 173, 'conocephalus_conocephalus': 174, 'pycnogaster_jugicola': 175, 'isophya_pyrenaea': 176, '52884': 177, 'isophya_tosevski': 178, 'ectadia_fulva': 179, 'ephippiger_terrestris': 180, 'poecilimon_orbelicus': 181, 'parasongella_ornaticeps': 182, '65373': 183, 'albarracinia_zapaterii': 184, 'cycloptiloides_timah': 185, 'myrmeleotettix_maculatus': 186, 'baetica_ustulata': 187, 'oecanthus_indicus': 188, 'steropleurus_andalusius': 189, '50186': 190, 'epidalea_calamita': 191, 'velarifictorus_aspersus': 192, 'vichetia_oblongicollis': 193, 'velarifictorus_tenepalpus': 194, 'yersinella_beybienkoi': 195, 'xenogryllus_transversus': 196, 'thyreonotus_bidens': 197, 'isophya_hospodar': 198, 'bufotes_viridis': 199, 'pelophylax_lessonae': 200, 'ornebius_tampines': 201, 'poecilimon_propinquus': 202, 'chorthippus_eisentrauti': 203, 'asiophlugis_trusmadi': 204, 'omocestus_haemorrhoidalis': 205, 'trellius_helverseni': 206, 'metrioptera_buyssoni': 207, 'poecilimon_superbus': 208, 'steropleurus_flavovittatus': 209, 'amedegnatiana_vicheti': 210, 'poecilimon_schmidtii': 211, 'odontura_stenoxypha': 212, 'polionemobius_taprobanense': 213, 'arcyptera_fusca': 214, 'bufo_bufo': 215, 'callicrania_ramburii': 216, 'leptophyes_lisae': 217, 'acrometopa_macropoda': 218, 'poecilimon_sureyanus': 219, 'poecilimon_gracilis': 220, 'pterolepis_cordubensis': 221, 'lluciapomaresius_asturiensis': 222, 'rhacocleis_distinguenda': 223, 'oecanthus_allardi': 224, 'poecilimon_obesus': 225, 'phyllomimus_inversus': 226, 'lucasinova_nigromarginata': 227, 'modestana_ebneri': 228, 'conocephalus_semivittatus': 229, 'ornebius_aureus': 230, 'poecilimon_thoracicus': 231, 'acrometopa_italica': 232, 'poecilimon_thessalicus': 233, 'ephippigerida_areolaria': 234, 'poecilimon_elegans': 235, 'ducetia_japonica': 236, 'odontura_macphersoni': 237, 'synephippius_obvius': 238, 'stenobothrus_rubicundulus': 239, 'eupholidoptera_chabrieri': 240, 'ornebius_insculptus': 241, '555086': 242, 'bombina_bombina': 243, 'chorthippus_corsicus': 244, 'gymnogryllus_angustus': 245, 'tettigonia_hispanica': 246, 'ctenodecticus_thymi': 247, 'gomphocerus_sibiricus': 248, 'odontura_aspericauda': 249, 'bradyporus_dasypus': 250, 'metaplastes_oertzeni': 251, 'pholidoptera_macedonica': 252, 'teleogryllus_oceanicus': 253, 'pachytrachis_gracilis': 254, 'poecilimon_mariannae': 255, 'ornebius_rufonigrus': 256, 'salomona_maculifrons': 257, 'isophya_plevnensis': 258, 'roeseliana_oporina': 259, 'parnassiana_tymphrestos': 260, 'platycleis_iberica': 261, 'antaxius_chopardi': 262, 'corsteropleurus_chopardi': 263, 'pteronemobius_lineolatus': 264, 'ectatoderus_argentatus': 265, 'noctitrella_plurilingua': 266, 'tettigonia_longispina': 267, \"pelophylax_'esculentus'\": 268, 'gryllus_assimilis': 269, 'stenobothrus_festivus': 270, 'alytes_obstetricans': 271, '65962': 272, 'pseudacris_crucifer': 273, 'euthystira_brachyptera': 274, 'rana_arvalis': 275, 'poecilimon_marmaraensis': 276, 'homoeoxipha_lycoides': 277, 'rhacocleis_bonfilsi': 278, 'rhacocleis_edentata': 279, 'anonconotus_ghilianii': 280, '48124': 281, 'leptophyes_sicula': 282, 'arcyptera_tornosi': 283, 'scudderia_furcata': 284, 'anonconotus_ligustinus': 285, 'eupholidoptera_tyrrhenica': 286, 'chorthippus_albomarginatus': 287, 'odontura_glabricauda': 288, 'gampsocleis_abbreviata': 289, 'neocallicrania_miegii': 290, 'eugryllodes_escalerae': 291, 'poecilimon_paros': 292, 'anomaloglossus_baeobatrachus': 293, 'anonconotus_mercantouri': 294, 'modicogryllus_algirius': 295, 'isophya_longicaudata': 296, 'anonconotus_alpinus': 297, 'poecilimon_turcicus': 298, 'uromenus_elegans': 299, 'eumodicogryllus_theryi': 300, 'viriacca_insularis': 301, 'isophya_modesta': 302, 'conocephalus_albescens': 303, 'holochlora_nigrotympana': 304, 'broughtonia_domogledi': 305, 'tartarogryllus_tartarus': 306, 'zeuneriana_burriana': 307, 'tessellana_orina': 308, 'ephippiger_ruffoi': 309, 'sciobia_caliendra': 310, 'pelophylax_perezi': 311, 'lipotactes_maculatus': 312, 'poecilimon_luschani': 313, 'pelodytes_punctatus': 314, '715170': 315, 'euanisous_teuthroides': 316, 'poecilimon_antalyaensis': 317, 'rana_temporaria': 318, 'helicocercus_triguttatus': 319, 'ornebius_bioculatus': 320, 'ectatoderus_angusticollis': 321, 'psophus_stridulus': 322, 'psyrana_tigrina': 323, 'anonconotus_baracunensis': 324, 'kuzicus_denticulatus': 325, 'saga_natoliae': 326, 'eupholidoptera_garganica': 327, 'lluciapomaresius_ortegai': 328, 'stenobothrus_fischeri': 329, 'sabaterpia_taeniata': 330, 'casigneta_sp.1': 331, 'poecilimon_pergamicus': 332, 'conocephalus_cognatus': 333, 'hexacentrus_unicolor': 334, '65344': 335, '65349': 336, 'eupholidoptera_cypria': 337, 'lipotactes_kabili': 338, 'eupholidoptera_giuliae': 339, 'arnobia_ocellata': 340, 'teleogryllus_occipitalis': 341, 'aulacobothrus_taeniatus': 342, 'tarbinskiellus_portentosus': 343, 'rhacocleis_poneli': 344, 'antaxius_sorrezensis': 345, 'pelophylax_kurtmuelleri': 346, 'gampsocleis_sedakovii': 347, 'bicolorana_kraussi': 348, 'stictophaula_armata': 349, 'acrometopa_servillea': 350, 'chorthippus_dubius': 351, 'mecopoda_elongata': 352, 'platystolus_martinezii': 353, 'parnassiana_chelmos': 354, 'montana_stricta': 355, 'poecilimon_ampliatus': 356, 'polysarcus_scutatus': 357, 'asiophlugis_philippina': 358, 'saga_campbelli': 359, 'eupholidoptera_mariannae': 360, 'antaxius_florezi': 361, 'tettigonia_silana': 362, 'chorthippus_yersini': 363, 'mitius_blennus': 364, 'oecanthus_nigricornis': 365, 'hylarana_leptoglossa': 366, 'paratrigonidium_venustulum': 367, '67252': 368, 'euchorthippus_elegantulus': 369, 'amphiestris_baetica': 370, 'euchorthippus_chopardi': 371, 'oedipoda_caerulescens': 372, 'ephippiger_melisi': 373, '24322': 374, 'uromenus_agarenus': 375, 'poecilimon_pindos': 376, 'lithobates_palustris': 377, 'isophya_savignyi': 378, 'anadrymadusa_brevipennis': 379, 'neoconocephalus_triops': 380, 'chorthippus_bornhalmi': 381, 'isophya_pavelii': 382, 'ephippiger_persicarius': 383, 'epacromius_pulverulentus': 384, 'pterolepis_grallata': 385, 'ctenodecticus_ramburi': 386, 'lipotactes_alienus': 387, 'poecilimon_erimanthos': 388, 'modicogryllus_frontalis': 389, 'conocephalus_exemptus': 390, 'parnassiana_tenuis': 391, 'isophya_mavromoustakisi': 392, 'pantecphylus_cerambycinus': 393, 'calliphona_koenigi': 394, 'metrioptera_ambigua': 395, 'ramburiella_hispanica': 396, 'acrometopa_syriaca': 397, 'incertana_decorata': 398, 'ctenodecticus_granatensis': 399, 'isophya_costata': 400, 'parnassiana_parnassica': 401, 'dociostaurus_maroccanus': 402, 'neophisis_siamensis': 403, 'bufo_spinosus': 404, 'isophya_speciosa': 405, 'hyla_sarda': 406, 'leptophyes_discoidalis': 407, 'rana_dalmatina': 408, 'zvenella_transversa': 409, 'drymadusa_dorsalis': 410, 'pachytrachis_striolatus': 411, 'conocephalus_cinereus': 412, 'pycnogaster_inermis': 413, 'conocephalus_concolor': 414, 'isophya_major': 415, 'arcyptera_microptera': 416, 'callicrania_plaxicauda': 417, 'bucephaloptera_bucephala': 418, 'bryodemella_holdereri': 419, 'bradyporus_oniscus': 420, 'eupholidoptera_forcipata': 421, 'pseudacris_feriarum': 422, 'angaracris_barabensis': 423, 'anadrymadusa_ornatipennis': 424, 'ctenodecticus_major': 425, 'isophya_pienensis': 426, 'velarifictorus_micado': 427, 'pholidoptera_frivaldszkyi': 428, 'parnassiana_dirphys': 429, 'oecanthus_quadripunctatus': 430, 'varitrella_glabra': 431, 'orchelimum_nigripes': 432, 'chiasmocleis_haddadi': 433, 'gampsocleis_gratiosa': 434, 'truljalia_versicolor': 435, 'liara_magna': 436, 'turanogryllus_lateralis': 437, 'otophryne_pyburni': 438, 'xiphidiopsis_dicera': 439, 'zvenella_yunnana': 440, 'metrioptera_maritima': 441, 'tympanophyllum_arcufolium': 442, 'rhammatocerus_cyanipes': 443, 'chorthippus_nevadensis': 444, 'fritziana_goeldii': 445, '135045': 446, 'rhacocleis_graeca': 447, 'poecilimon_unispinosus': 448, 'barbitistes_constrictus': 449, 'poecilimon_lodosi': 450, 'stenobothrus_grammicus': 451, 'poecilimon_brunneri': 452, 'poecilimon_miramae': 453, 'chorthippus_apicalis': 454, 'rhacocleis_baccettii': 455, 'eupholidoptera_astyla': 456, 'dociostaurus_jagoi': 457, 'psorodonotus_illyricus': 458, 'parnassiana_tymphiensis': 459, 'bombina_variegata': 460, 'acrometopa_cretensis': 461, 'lipotactes_virescens': 462, 'velarifictorus_acutilobus': 463, 'parnassiana_fusca': 464, 'scambophyllum_sanguinolentum': 465, 'pristimantis_espedeus': 466, 'hyla_molleri': 467, 'arcyptera_kheili': 468, 'hyla_orientalis': 469, 'poecilimon_roseoviridis': 470, 'steropleurus_brunnerii': 471, 'omocestus_uhagonii': 472, 'incertana_drepanensis': 473, 'poecilimon_martinae': 474, 'antaxius_bouvieri': 475, 'neophisis_longipennis': 476, 'dryophytes_versicolor': 477, 'omocestus_panteli': 478, 'ameerega_hahneli': 479, 'chorthippus_rubratibialis': 480, 'metrioptera_caprai': 481, 'ephippiger_provincialis': 482, 'promeca_sumatrana': 483, 'chorthippus_reissingeri': 484, 'chorthippus_macrocerus': 485, 'chorthippus_maritimus': 486, 'phonochorion_uvarovi': 487, 'gryllodinus_kerkennensis': 488, 'rhacocleis_thyrrhenica': 489, 'natula_longipennis': 490, 'psorodonotus_macedonicus': 491, 'psorodonotus_fieberi': 492, 'zeuneriana_amplipennis': 493, 'isophya_camptoxypha': 494, 'isophya_brunneri': 495, 'chorthippus_jucundus': 496, 'micrornebius_inopinatus': 497, 'parnassiana_gionica': 498, 'osteocephalus_oophagus': 499, 'rhacocleis_werneri': 500, 'rhacocleis_lithoscirtetes': 501, 'isophya_rizeensis': 502, 'metrioptera_prenjica': 503, 'anelytra_curvata': 504, 'loxoblemmus_jacobsoni': 505, 'anterastes_serbicus': 506, 'macroxiphus_sumatranus': 507, 'micrornebius_maninjau': 508, 'hyla_intermedia': 509, 'ornebius_samudra': 510, 'decticus_aprutianus': 511, 'acheta_hispanicus': 512, 'pholidoptera_lucasi': 513, 'dryophytes_cinereus': 514, 'omocestus_petraeus': 515, 'aquarana_clamitans': 516, 'hyla_savignyi': 517, 'parnassiana_parnon': 518, 'eugaster_guyoni': 519, 'rhacocleis_neglecta': 520, 'glyphonotus_coniciplicus': 521, 'rhinella_marina': 522, 'adenomera_heyeri': 523, 'anaxyrus_americanus': 524, 'stenobothrus_bolivarii': 525, 'poecilimon_heroicus': 526, 'pelodytes_ibericus': 527, 'prionotropis_appula': 528, 'aeropedellus_variegatus': 529, '65547': 530, 'arcyptera_brevipennis': 531, 'pycnogaster_sanchezgomezi': 532, 'trachycephalus_hadroceps': 533, 'eupholidoptera_magnifica': 534, 'eugaster_spinulosa': 535, '1462737': 536, 'anonconotus_occidentalis': 537, 'duolandrevus_firmus': 538, 'pholidoptera_dalmatica': 539, 'ephippiger_discoidalis': 540, '787625': 541, 'pholidoptera_transsylvanica': 542, 'leptodactylus_mystaceus': 543, '65336': 544, 'isophya_posthumoidalis': 545, 'sardoplatycleis_galvagnii': 546, 'rhinella_ornata': 547, 'anelytra_fastigata': 548, 'anelytra_tristellata': 549, 'barbitistes_kaltenbachi': 550, 'bradyporus_avanos': 551, 'montana_tomini': 552, 'eleutherodactylus_planirostris': 553, 'bradyporus_macrogaster': 554, 'omocestus_minutissimus': 555, 'tettigonia_armeniaca': 556, 'xenogryllus_ululiu': 557, 'meconematini_sp.': 558, 'velarifictorus_brevifrons': 559, 'pseudacris_regilla': 560, 'isophya_salmani': 561, 'phyllomimus_truncatus': 562, '1564122': 563, 'microhyla_ornata': 564, '24272': 565, 'poecilimon_celebi': 566, 'decticus_loudoni': 567, 'italohippus_monticola': 568, 'pseudacris_collinsorum': 569, 'rhacocleis_derrai': 570, 'rhacocleis_crypta': 571, 'aquarana_grylio': 572, 'chorthippus_dichrous': 573, 'pseudacris_maculata': 574, '555142': 575, 'loxoblemmus_doenitzi': 576, 'phaulula_malayica': 577, 'pholidoptera_rhodopensis': 578, 'phaneroptera_spinosa': 579, 'parnassiana_coracis': 580, 'velarifictorus_horridus': 581, 'isophya_straubei': 582, 'truxalis_siamensis': 583, 'atelopus_flavescens': 584, 'chorthippus_acroleucus': 585, 'tessellana_nigrosignata': 586, 'montana_barretii': 587, '126247': 588, 'parnassiana_menalon': 589, 'loxoblemmus_equestris': 590, 'leurophyllum_brevicauda': 591, 'acris_gryllus': 592, '548639': 593, 'smilisca_baudinii': 594, '66893': 595, '714022': 596, 'hylarana_guentheri': 597, 'odontura_maroccana': 598, '1346504': 599, 'pithecopus_rohdei': 600, '476538': 601, 'arcyptera_albogeniculata': 602, 'rhaebo_guttatus': 603, 'montana_macedonica': 604, 'pseudochorthippus_montanus': 605, 'uromenus_maroccanus': 606, 'hyla_chinensis': 607, 'promeca_perakana': 608, 'montana_armeniaca': 609, 'microcentrum_angustatum': 610, 'chorthippus_pullus': 611, 'sphingonotus_corsicus': 612, 'rhacocleis_insularis': 613, 'pterophylla_camellifolia': 614, 'psorodonotus_specularis': 615, 'oecanthus_longicauda': 616, 'adenomera_andreae': 617, 'oecanthus_euryelytra': 618, 'poecilimon_pseudornatus': 619, 'uromenus_siculus': 620, 'omocestus_africanus': 621, 'platycleis_romana': 622, 'pycnogaster_gaditana': 623, 'anaxipha_exigua': 624, 'meconema_meridionale': 625, 'lithoxenus_heptapotamica': 626, 'isophya_yaraligozi': 627, 'poecilimon_isopterus': 628, '963335': 629, 'pelophylax_nigromaculatus': 630, 'omocestus_minutus': 631, 'deracantha_onos': 632, 'aquarana_catesbeiana': 633, 'myrmeleotettix_palpalis': 634, 'ololygon_perpusilla': 635, 'hyperolius_spinigularis': 636, 'fritziana_mitus': 637, 'scinax_nebulosus': 638, 'leptodactylus_stenodema': 639, '134933': 640, '523060': 641, '1192948': 642, 'microhyla_heymonsi': 643, 'pseudacris_ocularis': 644, 'fejervarya_multistriata': 645, 'dryophytes_chrysoscelis': 646, 'velarifictorus_sulcifrons': 647, 'litoria_fallax': 648, 'boana_punctata': 649, 'dendropsophus_minusculus': 650, '868458': 651, 'pelobates_fuscus': 652, 'euphlyctis_cyanophlyctis': 653, '65419': 654, 'lithobates_sphenocephalus': 655, 'dryophytes_squirellus': 656, 'hyperolius_concolor': 657, 'phasmahyla_guttata': 658, 'brachycephalus_hermogenesi': 659, '24292': 660, 'leptodactylus_wagneri': 661, 'scinax_boesemani': 662, 'boana_cinerascens': 663, '1462711': 664, '1194042': 665, 'pelophylax_bedriagae': 666, 'dendropsophus_elegans': 667, 'scinax_imbegue': 668, 'alytes_almogavarii': 669, 'gastrophryne_carolinensis': 670, '66531': 671, '66016': 672, '21116': 673, '66578': 674, '67082': 675, 'dendropsophus_gaucheri': 676, '81930': 677, 'boana_raniceps': 678, 'boana_albomarginata': 679, 'trachycephalus_resinifictrix': 680, 'duttaphrynus_melanostictus': 681, 'boana_faber': 682, '21038': 683, '1139490': 684, '528041': 685, 'ischnocnema_bolbodactyla': 686, '64862': 687, 'sphaenorhynchus_lacteus': 688, '476537': 689, 'epipedobates_anthonyi': 690, 'rhinella_granulosa': 691, 'hylodes_phyllodes': 692, 'alytes_dickhilleni': 693, 'boana_multifasciata': 694, 'ololygon_littoralis': 695, 'scinax_hayii': 696, 'alytes_cisternasii': 697, 'pristimantis_zeuctotylus': 698, 'dendropsophus_minutus': 699}\n    model_config_4 = {\n            \"model_type\": \"sed\",\n            \"example_dims\": example_dims,\n            \"spectrogram\": spectrogram_params,\n            \"backbone\": {\n                \"backbone_name\": 'tf_efficientnet_b0.ns_jft_in1k',\n                'pretrained': False,\n                \"last_layer_hidden_dim\": None\n            },\n            \"head\": {\n                'dropout': 0.50,\n                'num_classes': len(label2ind_I_A),\n                \"infer_duration\": 5,\n                \"duration\": duration\n            },\n            \"inference_type\": inference_type,\n            \"multilabel_to_train_labels\": get_multilabel_to_train_labels(multilabel_label2ind=label2ind_I_A, train_label2ind=InferenceConfig.label2ind)\n    }\n\n    model_config_5 = {\n            \"model_type\": \"sed\",\n            \"example_dims\": example_dims,\n            \"spectrogram\": spectrogram_params,\n            \"backbone\": {\n                \"backbone_name\": 'timm/eca_nfnet_l0.ra2_in1k',\n                'pretrained': False,\n                \"last_layer_hidden_dim\": None\n            },\n            \"head\": {\n                'dropout': 0.50,\n                'num_classes': InferenceConfig.num_classes,\n                \"infer_duration\": 5,\n                \"duration\": duration\n            },\n            \"inference_type\": inference_type\n    }\n\n    model_config_6 = {\n            \"model_type\": \"sed\",\n            \"example_dims\": example_dims,\n            \"spectrogram\": spectrogram_params,\n            \"backbone\": {\n                \"backbone_name\": 'tf_efficientnet_b4.ns_jft_in1k',\n                'pretrained': False,\n                \"last_layer_hidden_dim\": None\n            },\n            \"head\": {\n                'dropout': 0.50,\n                'num_classes': InferenceConfig.num_classes,\n                \"infer_duration\": 5,\n                \"duration\": duration\n            },\n            \"inference_type\": inference_type\n    }","metadata":{"execution":{"iopub.status.busy":"2025-06-08T18:57:20.589209Z","iopub.execute_input":"2025-06-08T18:57:20.589689Z","iopub.status.idle":"2025-06-08T18:57:20.664379Z","shell.execute_reply.started":"2025-06-08T18:57:20.589651Z","shell.execute_reply":"2025-06-08T18:57:20.662606Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data Loading","metadata":{}},{"cell_type":"code","source":"DATA_PATH = \"/kaggle/input/birdclef-2025\"\nTEST_DIR =  \"test_soundscapes\"\nTRAIN_DIR =  \"train_soundscapes\"\n\ntest_paths = glob(os.path.join(DATA_PATH, TEST_DIR, '*ogg'))\nif len(test_paths) == 0:\n    test_paths = glob(os.path.join(DATA_PATH, TRAIN_DIR, '*ogg'))[:3]\ntest_df = pd.DataFrame(test_paths, columns=['filepath'])\ntest_df['filename'] = test_df.filepath.map(lambda x: x.split('/')[-1].replace('.ogg',''))\ntest_df.head()","metadata":{"execution":{"iopub.status.busy":"2025-06-08T18:57:22.706312Z","iopub.execute_input":"2025-06-08T18:57:22.706633Z","iopub.status.idle":"2025-06-08T18:57:22.922063Z","shell.execute_reply.started":"2025-06-08T18:57:22.706609Z","shell.execute_reply":"2025-06-08T18:57:22.920884Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_all_samples(test_df):\n    \n    def load_sample(filepath, sample_rate=32_000, res_type=\"kaiser_best\"):\n        wave, _ = librosa.load(filepath, sr=sample_rate, res_type=res_type)\n        return wave\n    \n    waves = joblib.Parallel(n_jobs=os.cpu_count())(\n            joblib.delayed(load_sample)(\n                filepath,\n            )\n            for filepath in tqdm.notebook.tqdm(test_df['filepath'].values)\n        )\n    all_waves = dict(zip(test_df['filename'].values, waves))\n    \n    return all_waves\n    \nall_waves = load_all_samples(test_df)","metadata":{"execution":{"iopub.status.busy":"2025-06-08T19:26:53.108121Z","iopub.execute_input":"2025-06-08T19:26:53.108548Z","iopub.status.idle":"2025-06-08T19:26:53.183253Z","shell.execute_reply.started":"2025-06-08T19:26:53.108477Z","shell.execute_reply":"2025-06-08T19:26:53.181917Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Models building","metadata":{}},{"cell_type":"code","source":"def prepare_models(models_group, models_dirs, ov_models_dir=None):\n    models = []\n    ov_engines = []\n    for weights_name, model_config  in tqdm.notebook.tqdm(models_group):\n        \n        model = CLEFClassifierSED(config=model_config)\n        pt_path = os.path.join(models_dirs, weights_name)\n        model.load_state_dict(torch.load(pt_path, weights_only=True, map_location=torch.device('cpu')))\n        model.eval()\n\n        if ov_models_dir is not None:\n            backbone_ov_path = os.path.join(ov_models_dir, weights_name.replace(\".pt\", \".xml\"))\n            if not os.path.exists(backbone_ov_path):\n                backbone_ov_path = None\n        else:\n            backbone_ov_path = None\n            \n        ov_engine = VINOEngine(model.backbone, \n                               example_dims=model_config[\"example_dims\"],\n                               ov_model_path=backbone_ov_path)\n        models.append(model)\n        ov_engines.append(ov_engine)\n\n    models = {\n        \"models\": models,\n        \"ov_engines\": ov_engines\n    }\n    return models\n    \n\nMODELS_PATH = \"/kaggle/input/birdclef2025-1st-place-ensemble\"\nOV_MODELS_PATH = \"/kaggle/input/birdclef2025-1st-place-ensemble\"\n\nMODELS_GROUP_META_1 = [\n    \n    # self-learning models\n    (\"tf_efficientnet_b4.ns_jft_in1k_sampler_maxsum_iteration_3_v1_temp_0.55_64_bs_0.15_drop_path_rate_1_mixup_ratio_pseudo_data_20_duration_sed_type_0.5_mixup_p_(224, 512)_size_ce_4096_n_fft_0_fold_22_seed_25_epoch.pt\", ModelsGroupConfig.model_config_6),\n    (\"tf_efficientnet_b3.ns_jft_in1k_sampler_maxsum_iteration_3_v1_temp_0.55_54_bs_0.15_drop_path_rate_1_mixup_ratio_pseudo_data_20_duration_sed_type_0.5_mixup_p_(224, 512)_size_ce_4096_n_fft_1_fold_25_epoch.pt\", ModelsGroupConfig.model_config_1),\n    (\"regnety_016.tv2_in1k_sampler_maxsum_iteration_4_v1_temp_0.6_64_bs_0.15_drop_path_rate_1_mixup_ratio_pseudo_data_20_duration_sed_type_0.5_mixup_p_(224, 512)_size_ce_4096_n_fft_2_fold_25_epoch.pt\", ModelsGroupConfig.model_config_2),\n    (\"regnety_016.tv2_in1k_sampler_maxsum_iteration_4_v1_framewise_temp_0.6_64_bs_0.15_drop_path_rate_1_mixup_ratio_pseudo_data_20_duration_sed_type_0.5_mixup_p_(224, 512)_size_ce_4096_n_fft_3_fold_25_epoch.pt\", ModelsGroupConfig.model_config_2),\n    (\"eca_nfnet_l0.ra2_in1k_sampler_maxsum_iteration_3_v1_temp_0.55_128_bs_0.15_drop_path_rate_1_mixup_ratio_pseudo_data_20_duration_sed_type_0.5_mixup_p_(224, 512)_size_ce_4096_n_fft_additional_data_full_data_22_seed_15_epoch.pt\", ModelsGroupConfig.model_config_5),\n    \n    # supervised learning models\n    (\"regnety_008.pycls_in1k_20_duration_sed_mixup_(224, 512)_size_ce_4096_n_fft_2_fold_15_epoch.pt\", ModelsGroupConfig.model_config_3),\n    (\"tf_efficientnet_b0.ns_jft_in1k_incest_amphibia_128_bs_0.0_drop_path_rate_20_duration_sed_type_0.5_mixup_p_(224, 512)_size_ce_4096_n_fft_full_data_22_seed_40_epoch.pt\", ModelsGroupConfig.model_config_4)\n]\nmodels_group_1 = prepare_models(models_group=MODELS_GROUP_META_1, models_dirs=MODELS_PATH, ov_models_dir=OV_MODELS_PATH)\nmodels_group_1[\"dataset\"] =  InferenceDataset(all_waves=all_waves, \n                                            feature_extractor=models_group_1[\"models\"][0].mel_spectr_generator, \n                                            \n                                            full_signal=ModelsGroupConfig.full_signal_to_spectr,\n                                            hop_length=ModelsGroupConfig.hop_length, \n                                            img_size=ModelsGroupConfig.img_size,\n                                            duration_sec=ModelsGroupConfig.duration, \n                                            slice_step_sec=ModelsGroupConfig.slice_step_sec,\n                                            \n                                            num_segments_sample=InferenceConfig.num_segments_sample, \n                                            separate_norm=InferenceConfig.separate_norm)\n\nmodel_groups = [models_group_1]","metadata":{"execution":{"iopub.status.busy":"2025-06-08T19:13:15.250450Z","iopub.execute_input":"2025-06-08T19:13:15.250920Z","iopub.status.idle":"2025-06-08T19:13:28.765897Z","shell.execute_reply.started":"2025-06-08T19:13:15.250883Z","shell.execute_reply":"2025-06-08T19:13:28.764631Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Inference","metadata":{}},{"cell_type":"code","source":"def gauss_convolve(arr):\n    weights = np.array([0.1, 0.2, 0.4, 0.2, 0.1])\n    result = convolve1d(arr, weights, axis=0, mode='nearest')\n    return result\n\n\ndef ensemble_preds(preds, weights, power=1):\n    preds = preds**power\n    preds = preds * weights.reshape(-1, 1, 1)\n    preds = preds.sum(0)\n    return preds\n    \n\ndef multilabel_pred_to_train_preds(preds, multilabel_to_train_labels):\n    if isinstance(multilabel_to_train_labels, dict):\n        y = np.zeros((InferenceConfig.num_segments_sample, len(InferenceConfig.label2ind)), dtype=np.float32)\n        for multilabel_ind, train_ind in multilabel_to_train_labels.items():\n            y[:, train_ind] = preds[:, multilabel_ind]\n        return y\n    else:\n        return preds\n    \n\ndef inference_model(mel_spec, model, ov_engine):\n    fts = ov_engine(mel_spec)\n    fts = torch.from_numpy(fts)\n    with torch.no_grad():\n        with torch.autocast(device_type=\"cpu\", dtype=torch.float16):\n            preds = model.get_head_preds(fts) \n    preds = preds.astype(np.float32)\n    preds = multilabel_pred_to_train_preds(preds, multilabel_to_train_labels=model.multilabel_to_train_labels)\n    return preds\n\n\ndef inference_sample(sample_ind, model_groups):\n\n    sample_probs = []\n    for model_group in model_groups:\n        mel_spec, filename = model_group['dataset'][sample_ind]\n        if len(mel_spec.shape) == 3:\n            mel_spec = mel_spec.unsqueeze(1).expand(-1,3,-1,-1)\n        for model, ov_engine in zip(model_group[\"models\"], model_group[\"ov_engines\"]):\n            probs = inference_model(mel_spec, model=model, ov_engine=ov_engine)\n            sample_probs.append(probs)\n\n    sample_probs = ensemble_preds(preds=np.array(sample_probs),\n                                  weights=InferenceConfig.preds_weights,\n                                  power=InferenceConfig.preds_power)\n    if InferenceConfig.smoothing:\n        sample_probs = gauss_convolve(sample_probs)\n    \n    rec_ids = [f'{filename}_{(frame_id+1)*5}' for frame_id in range(InferenceConfig.num_segments_sample)]\n\n    return sample_probs, rec_ids\n    \n\npreds_all = []\nids = []\nfor sample_ind in tqdm.tqdm(range(len(all_waves))):\n    sample_probs, rec_ids = inference_sample(sample_ind=sample_ind, model_groups=model_groups)\n    preds_all.append(sample_probs)\n    ids.extend(rec_ids)\n\nif len(preds_all) > 0:\n    preds_all = np.concatenate(preds_all, 0)\n    pred_df = pd.DataFrame(ids, columns=['row_id'])\n    pred_df.loc[:, list(InferenceConfig.label2ind.keys())] = preds_all\nelse:\n    pred_df = pd.DataFrame(columns=['row_id']+list(InferenceConfig.label2ind.keys()))\n    \npred_df.to_csv('submission.csv',index=False)\npred_df.head()","metadata":{"execution":{"iopub.status.busy":"2025-06-08T19:13:28.770234Z","iopub.execute_input":"2025-06-08T19:13:28.770746Z","iopub.status.idle":"2025-06-08T19:14:02.330011Z","shell.execute_reply.started":"2025-06-08T19:13:28.770704Z","shell.execute_reply":"2025-06-08T19:14:02.328766Z"},"trusted":true},"outputs":[],"execution_count":null}]}