{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":70203,"databundleVersionId":8068726,"sourceType":"competition"},{"sourceId":8662715,"sourceType":"datasetVersion","datasetId":5190454},{"sourceId":181293291,"sourceType":"kernelVersion"}],"dockerImageVersionId":30732,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install /kaggle/input/kapenon-birdclef2024/openvino-2024.1.0-15008-cp310-cp310-manylinux2014_x86_64.whl --no-index --find-links /kaggle/input/kapenon-birdclef2024","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-06-11T07:50:05.494006Z","iopub.execute_input":"2024-06-11T07:50:05.494440Z","iopub.status.idle":"2024-06-11T07:50:24.423608Z","shell.execute_reply.started":"2024-06-11T07:50:05.494400Z","shell.execute_reply":"2024-06-11T07:50:24.422024Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# -----------------------------\n# IMPORTS\n# -----------------------------\nfrom openvino.runtime import Core\nimport pandas as pd\nfrom tqdm import tqdm\nimport numpy as np\nimport re\nimport soundfile as sf\nimport timm\nfrom pathlib import Path\nimport torch\nimport torch.nn as nn\nfrom torch.nn import functional as F\nfrom torchaudio.transforms import MelSpectrogram\nimport time\nimport librosa\n\ntorch.set_flush_denormal(True)\n\n\nclass Timer:\n    def __init__(self, txt=\"\"):\n        self.txt = txt\n\n    def __enter__(self):\n        self.start_time = time.time()\n        return self\n\n    def __exit__(self, exc_type, exc_value, traceback):\n        self.end_time = time.time()\n        elapsed_time = self.end_time - self.start_time\n        print(f\"{self.txt}: {elapsed_time:.5f} seconds\")\n\n\nSR = 32_000\nN_ALL_TARGETS = 182\n\n\nn_inference = 10\n\nupload_dir = \"/kaggle/input/bc2024pub\"\ntest_files = list(Path(\"/kaggle/input/birdclef-2024/test_soundscapes\").glob(\"*.ogg\"))\nif len(test_files) == 0:\n    test_files = list(\n        Path(\"/kaggle/input/birdclef-2024/unlabeled_soundscapes\").glob(\"*.ogg\")\n    )[:n_inference]\n\n# -----------------------------\n# MODELS\n# -----------------------------\n_ckpts = [\n    dict(\n        arch=\"eca_nfnet_l0\",\n        pth=f\"{upload_dir}/063_latest_fALL.pth\",\n        classes=np.load(f\"{upload_dir}/063_labels.npy\", allow_pickle=True),\n        sed=True,\n    ),\n    dict(\n        arch=\"eca_nfnet_l0\",\n        pth=f\"{upload_dir}/059_full_latest_fALL_e14.pth\",\n        classes=np.load(f\"{upload_dir}/059_full_labels.npy\", allow_pickle=True),\n        sed=False,\n    ),\n    dict(\n        arch=\"tf_efficientnet_b0.ns_jft_in1k\",\n        pth=f\"{upload_dir}/057_latest_fALL.pth\",\n        classes=np.load(f\"{upload_dir}/057_labels.npy\", allow_pickle=True),\n        sed=True,\n    ),\n]\nckpts = dict()\nfor ckpt in _ckpts:\n    ckpt[\"model_ir\"] = None\n    ckpt[\"preds\"] = []\n    ckpt[\"onnx_preds\"] = []\n    ckpt[\"pth_preds\"] = []\n    ckpts[Path(ckpt[\"pth\"]).stem] = ckpt\n\n\n# -----------------------------\n# FEATURE EXTRACTOR\n# -----------------------------\nclass NormalizeMelSpec(nn.Module):\n    def __init__(self, eps=1e-6):\n        super().__init__()\n        self.eps = eps\n\n    def forward(self, X):\n        mean = X.mean((1, 2), keepdim=True)\n        std = X.std((1, 2), keepdim=True)\n        Xstd = (X - mean) / (std + self.eps)\n        norm_min, norm_max = Xstd.min(-1)[0].min(-1)[0], Xstd.max(-1)[0].max(-1)[0]\n        fix_ind = (norm_max - norm_min) > self.eps * torch.ones_like(\n            (norm_max - norm_min)\n        )\n        V = torch.zeros_like(Xstd)\n        if fix_ind.sum():\n            V_fix = Xstd[fix_ind]\n            norm_max_fix = norm_max[fix_ind, None, None]\n            norm_min_fix = norm_min[fix_ind, None, None]\n            V_fix = torch.max(\n                torch.min(V_fix, norm_max_fix),\n                norm_min_fix,\n            )\n            V_fix = (V_fix - norm_min_fix) / (norm_max_fix - norm_min_fix)\n            V[fix_ind] = V_fix\n        return V\n\n\nclass LogMelSpecExtractor(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.ms = MelSpectrogram(\n            sample_rate=SR,\n            n_fft=1095,\n            hop_length=500,\n            f_min=40,\n            f_max=15000,\n            n_mels=128,\n        )\n        self.norm = NormalizeMelSpec()\n\n    def forward(self, x):\n        x = self.ms(x)\n        x = torch.log10(x + 1e-20)\n        x = self.norm(x)\n        return x\n\n\n# -----------------------------\n# RUNTIME\n# -----------------------------\nfor ckpt_name, ckpt in ckpts.items():\n    print(\"prepare runtime for...\", ckpt_name)\n    core = Core()\n    model_ir = core.read_model(model=str(ckpt[\"pth\"]).replace(\".pth\", \".xml\"))\n    model_ir = core.compile_model(model_ir, device_name=\"CPU\")\n    model_ir = model_ir.create_infer_request()\n    ckpt[\"model_ir\"] = model_ir\n\n\n# -----------------------------\n# INFERENCE\n# -----------------------------\nrow_ids = []\nfront = LogMelSpecExtractor()\n\nfor i, pi in tqdm(enumerate(test_files)):\n    row_id = re.search(r\"/([^/]+)\\.ogg$\", str(pi)).group(1)\n    with Timer(\"load\"):\n        wave, sr = sf.read(pi)\n        if sr != SR:\n            wave = librosa.resample(wave, orig_sr=sr, target_sr=SR)\n\n    with Timer(\"preprocess\"):\n        len_sub = SR * 5 * 48\n        if len(wave) >= len_sub:\n            N = 48\n            wave = wave[:len_sub]\n        else:\n            N = (len(wave) - 1) // (SR * 5) + 1\n            pad = np.zeros(N * SR * 5 - len(wave))\n            wave = np.concatenate([wave, pad])\n            \n        wave = wave.reshape(N, SR * 5)\n        wave = torch.tensor(wave, dtype=torch.float32)\n\n    with Timer(\"front\"):\n        x = front(wave)\n\n    for ckpt_name, ckpt in ckpts.items():\n        tmp = x.unsqueeze(1).expand(x.shape[0], 3, x.shape[1], x.shape[2])\n\n        with Timer(f\"{ckpt_name.split('_')[0]}_openvino\"):\n            preds = ckpt[\"model_ir\"].infer(inputs=tmp.numpy())[\"y\"]\n            ckpt[\"preds\"].append(preds)\n\n    row_ids.extend([f\"{row_id}_{(i+1)*5}\" for i in range(min(48, len(preds)))])\n\n# -----------------------------\n# PREDS AS DF\n# -----------------------------\nfor ckpt_name, ckpt in ckpts.items():\n    res = np.concatenate(ckpt[\"preds\"])\n    df = pd.DataFrame(res, columns=ckpt[\"classes\"])\n    df[\"row_id\"] = row_ids\n    ckpt[\"df\"] = df","metadata":{"execution":{"iopub.status.busy":"2024-06-11T07:50:24.426359Z","iopub.execute_input":"2024-06-11T07:50:24.426785Z","iopub.status.idle":"2024-06-11T07:52:03.174040Z","shell.execute_reply.started":"2024-06-11T07:50:24.426738Z","shell.execute_reply":"2024-06-11T07:52:03.172847Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"names = list(ckpts.keys())\n\nsample_df = pd.read_csv(\"/kaggle/input/birdclef-2024/sample_submission.csv\")\nsub_df = ckpts[names[0]][\"df\"][sample_df.columns]\n\n# logits ensemble\nbird_cols = [c for c in sample_df.columns if c != \"row_id\"]\nsub_df[bird_cols] = (\n    ckpts[names[0]]['df'][bird_cols].values * 2 +\n    ckpts[names[1]]['df'][bird_cols].values * 2 +\n    ckpts[names[2]]['df'][bird_cols].values * 1\n)\n\nsub_df.to_csv(\"submission.csv\", index=False)\nsub_df.head(5)","metadata":{"execution":{"iopub.status.busy":"2024-06-11T07:53:07.186927Z","iopub.execute_input":"2024-06-11T07:53:07.187390Z","iopub.status.idle":"2024-06-11T07:53:07.426868Z","shell.execute_reply.started":"2024-06-11T07:53:07.187355Z","shell.execute_reply":"2024-06-11T07:53:07.425486Z"},"trusted":true},"execution_count":null,"outputs":[]}]}