{"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":8108072,"sourceType":"datasetVersion","datasetId":4789213},{"sourceId":8625476,"sourceType":"datasetVersion","datasetId":5163904}],"dockerImageVersionId":30684,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install /kaggle/input/onnxruntime/humanfriendly-10.0-py2.py3-none-any.whl --no-index --find-links /kaggle/input/onnxruntime\n!pip install /kaggle/input/onnxruntime/coloredlogs-15.0.1-py2.py3-none-any.whl --no-index --find-links /kaggle/input/onnxruntime\n!pip install /kaggle/input/onnxruntime/onnxruntime-1.17.3-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl --no-index --find-links /kaggle/input/onnxruntime","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:11:02.597767Z","iopub.execute_input":"2024-05-28T07:11:02.598204Z","iopub.status.idle":"2024-05-28T07:11:50.613006Z","shell.execute_reply.started":"2024-05-28T07:11:02.598159Z","shell.execute_reply":"2024-05-28T07:11:50.611712Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nfrom pathlib import Path\nfrom types import SimpleNamespace\n\ncfg = SimpleNamespace(**{})\ncfg.num_folds = 5\n\ncfg.fname = 'bird_003'\ncfg.seed = 2029\n\ncfg.input_path = Path('../input')\ncfg.comp_data_path = cfg.input_path / 'birdclef-2024'\ncfg.save_path = Path('../checkpoints')\ncfg.soundscape_path = cfg.comp_data_path / 'unlabeled_soundscapes'\ncfg.test_path = cfg.comp_data_path / 'test_soundscapes'\ncfg.checkpoint_path = Path('/kaggle/input/cpmp-birdclef-237')\n\ncfg.logger_file = False\n\n# image size\ncfg.image_height = 224\ncfg.image_width = 224\n\n# audio\ncfg.duration = 5\ncfg.sr = 32000\ncfg.fmin = 90\ncfg.fmax = 14000\ncfg.n_fft = 1536\ncfg.n_mels = cfg.image_height\ncfg.win_length = 1024\ncfg.hop_length = int((cfg.duration * cfg.sr - cfg.win_length + cfg.n_fft) / (cfg.image_width)) + 1 \n\n# training HP\ncfg.num_epochs = 3\ncfg.train_batch_size = 1\ncfg.valid_batch_size = 64\ncfg.onnx_batch_size = 48\ncfg.workers = 0\ncfg.grad_norm = 0.0\ncfg.grad_norm_type = 2\ncfg.device = \"cpu\"\ncfg.accumulate = 1\n\n# optimizer\ncfg.lr = 7e-5\ncfg.decay = 0.01\ncfg.opt_beta1 = 0.9\ncfg.opt_beta2 = 0.999\ncfg.opt_eps = 1e-8\ncfg.optimizer = 'AdamW'\ncfg.no_decay = False\n\n# scheduler\ncfg.pct_start = 0.1\ncfg.max_lr = 3e-3\ncfg.final_div_factor = 100\n\n# augmentations\ncfg.resample_train = 10\n\n# logging\ncfg.local_rank = 0\ncfg.verbose=True\n\n# model\ncfg.backbone = 'efficientvit_b0.r224_in1k'\ncfg.gem_pooling = False\ncfg.bce = True\n\n# tasks hp\ncfg.train_model = True\ncfg.pl = None\ncfg.pretrained_path = None \n\ncfg.num_rows = 48","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-05-28T07:11:50.615758Z","iopub.execute_input":"2024-05-28T07:11:50.616257Z","iopub.status.idle":"2024-05-28T07:11:50.634653Z","shell.execute_reply.started":"2024-05-28T07:11:50.616218Z","shell.execute_reply":"2024-05-28T07:11:50.633257Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport random\nfrom tqdm import tqdm\nfrom logging import getLogger, INFO, StreamHandler, FileHandler, Formatter\nimport gc\nimport pickle as pkl\n\nimport librosa\n\nfrom torch.utils.data import DataLoader, Dataset\nimport torchaudio\nimport torchaudio.transforms as T\n#import torch_audiomentations as tA\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.cuda.amp import autocast, GradScaler\nfrom torch.optim import lr_scheduler, Adam, AdamW\n\nimport timm\n\nfrom glob import glob\nfrom sklearn.model_selection import KFold, GroupKFold\nfrom sklearn.metrics import roc_auc_score\nfrom scipy.special import logit, expit\nimport re\n\nimport joblib","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:11:50.636084Z","iopub.execute_input":"2024-05-28T07:11:50.636537Z","iopub.status.idle":"2024-05-28T07:12:03.488366Z","shell.execute_reply.started":"2024-05-28T07:11:50.636506Z","shell.execute_reply":"2024-05-28T07:12:03.487127Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_logger(cfg):\n    logger = getLogger(cfg.fname)\n    logger.setLevel(INFO)\n    handler1 = StreamHandler()\n    handler1.setFormatter(Formatter(\"%(message)s\"))\n    logger.addHandler(handler1)\n    if cfg.logger_file and cfg.train_model:\n        filename = 'run.log'\n        handler2 = FileHandler(filename=filename)\n        handler2.setFormatter(Formatter(\"%(message)s\"))\n        logger.addHandler(handler2)\n    return logger\n\ndef seed_torch(seed_value):\n    random.seed(seed_value) # Python\n    np.random.seed(seed_value) # cpu vars\n    torch.manual_seed(seed_value) # cpu  vars    \n    if torch.cuda.is_available(): \n        torch.cuda.manual_seed(seed_value)\n        torch.cuda.manual_seed_all(seed_value) # gpu vars\n    if torch.backends.cudnn.is_available:\n        torch.backends.cudnn.deterministic = True\n        torch.backends.cudnn.benchmark = False","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:12:03.489694Z","iopub.execute_input":"2024-05-28T07:12:03.490045Z","iopub.status.idle":"2024-05-28T07:12:03.501808Z","shell.execute_reply.started":"2024-05-28T07:12:03.490014Z","shell.execute_reply":"2024-05-28T07:12:03.500239Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = pd.read_csv(cfg.comp_data_path / 'train_metadata.csv')\ntrain['species'] = [filename.split('/')[0] for filename in train.filename]","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:12:03.505041Z","iopub.execute_input":"2024-05-28T07:12:03.505452Z","iopub.status.idle":"2024-05-28T07:12:03.722375Z","shell.execute_reply.started":"2024-05-28T07:12:03.505418Z","shell.execute_reply":"2024-05-28T07:12:03.721264Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cfg.labels = np.array(sorted(train.species.unique()))\ncfg.num_labels = len(cfg.labels)\ncfg.targets = {v : i for i,v in enumerate(cfg.labels)}\n\ncfg.logger = get_logger(cfg)\nseed_torch(cfg.seed)","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:12:03.723771Z","iopub.execute_input":"2024-05-28T07:12:03.724098Z","iopub.status.idle":"2024-05-28T07:12:03.741083Z","shell.execute_reply.started":"2024-05-28T07:12:03.724071Z","shell.execute_reply":"2024-05-28T07:12:03.73975Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def batch_to_device(batch, device):\n    return batch\n\ndef get_data_loader(dataset, istrain, cfg):\n    if istrain:\n        batch_size = cfg.train_batch_size\n    else:\n        batch_size = cfg.valid_batch_size\n    data_loader = DataLoader(\n        dataset,\n        batch_size=batch_size,\n        num_workers=cfg.workers,\n        shuffle=istrain,\n        pin_memory=False,\n        #collate_fn=collate_pad,\n        drop_last = istrain,\n    )\n    return data_loader ","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:12:03.742539Z","iopub.execute_input":"2024-05-28T07:12:03.742967Z","iopub.status.idle":"2024-05-28T07:12:03.751436Z","shell.execute_reply.started":"2024-05-28T07:12:03.742926Z","shell.execute_reply":"2024-05-28T07:12:03.750027Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def gem(x, p=3, eps=1e-6):\n    return F.avg_pool2d(x.clamp(min=eps).pow(p), (x.size(-2), x.size(-1))).pow(1./p)\n\nclass GeM(nn.Module):\n    def __init__(self, p=3, eps=1e-6, p_trainable=True):\n        super(GeM,self).__init__()\n        if p_trainable:\n            self.p = Parameter(torch.ones(1)*p)\n        else:\n            self.p = p\n        self.eps = eps\n\n    def forward(self, x):\n        return gem(x, p=self.p, eps=self.eps)       \n    def __repr__(self):\n        return self.__class__.__name__ + '(' + 'p=' + '{:.4f}'.format(self.p.data.tolist()[0]) + ', ' + 'eps=' + str(self.eps) + ')'\n\nclass BirdModel(nn.Module):\n    def __init__(self, cfg, pretrained: bool = True):\n        super(BirdModel, self).__init__()\n        self.cfg = cfg\n        self.mel = T.MelSpectrogram(\n            sample_rate=cfg.sr, n_fft=cfg.n_fft, win_length=cfg.win_length, \n            hop_length= cfg.hop_length, f_min=cfg.fmin, f_max=cfg.fmax, \n            n_mels=cfg.n_mels, mel_scale='htk', power=2.0)\n        self.A2DB = T.AmplitudeToDB(stype=\"power\")\n        self.backbone = timm.create_model(\n            cfg.backbone,\n            pretrained=pretrained,\n            drop_rate = 0.1,\n            #drop_path_rate = 0.2,\n            num_classes=cfg.num_labels, \n            #global_pool=''\n        )\n        #if cfg.gem_pooling == \"gem\":\n        #    self.backbone.head.global_pool = GeM(p_trainable=args.p_trainable)\n         \n    def forward(self, x):\n        x = self.get_image(x)\n        x = self.backbone(x)\n        return x\n    \n    def get_image(self, x):\n        with torch.no_grad():\n            x = x / torch.std(x, 1, keepdim=True)\n            x = self.mel(x)\n            x = self.A2DB(x)\n            x = (x - 40) / 80\n            x = x.unsqueeze(1)\n            pos = torch.linspace(0., 1., x.size(2)).to(x.device)\n            pos = pos.unsqueeze(0).unsqueeze(0).unsqueeze(-1)\n            pos = pos.expand(x.size(0), 1, x.size(2), x.size(3))\n            x = torch.cat([x, x, pos], 1)\n        return x\n        \n    ","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:12:03.753087Z","iopub.execute_input":"2024-05-28T07:12:03.753495Z","iopub.status.idle":"2024-05-28T07:12:03.773977Z","shell.execute_reply.started":"2024-05-28T07:12:03.753458Z","shell.execute_reply":"2024-05-28T07:12:03.772445Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class SoundScapeDataset():\n    def __init__(self, soundscape, istrain, cfg):\n        self.cfg = cfg\n        self.istrain = istrain\n        self.soundscape = soundscape\n        self.duration = int(np.round(soundscape.shape[0] / cfg.sr))\n        self.starts = np.arange(0, self.duration, cfg.duration)\n        self.ends = self.starts + cfg.duration\n        self.starts = cfg.sr * self.starts\n        self.ends = cfg.sr * self.ends\n        \n    def __len__(self):\n        return len(self.starts)\n    \n    def __getitem__(self, idx):\n        audio = self.soundscape[self.starts[idx] : self.ends[idx]]\n        duration = self.cfg.sr * self.cfg.duration\n        if len(audio) < duration:\n            pad_length = (duration - len(audio)) // 2\n            audio = np.pad(audio, \n                           ((pad_length, duration - len(audio) - pad_length),), \n                           mode='constant')\n        out = {\n            'audio' : torch.from_numpy(audio),\n        }\n        return out\n    \ndef predict(loader, models, ort_sessions, cfg):\n    if cfg.verbose:\n        bar = tqdm(range(len(loader)))\n    else:\n        bar = range(len(loader))\n    load_iter = iter(loader)\n    preds_l = []\n    targets_l = []\n    with torch.no_grad():\n        for i, batch in zip(bar, load_iter):      \n            input_dict = batch\n            with autocast(enabled=False):\n                preds = []\n                x = models[0].get_image(input_dict['audio'])\n                for model, ort_session in zip(models, ort_sessions):\n                    pred = infer_onnx(model, ort_session, x, cfg)\n                    preds.append(pred)\n                preds_max = np.max(preds, 0)\n                preds = [(pred + preds_max + pred.mean() - preds_max.mean()) / 2.0 for pred in preds]\n                preds = np.mean(preds, 0)\n        \n            preds_l.append(preds)\n    preds = np.concatenate(preds_l)\n    return preds\n\ndef load_soundscape(pathname, cfg=cfg):\n    audio = librosa.load(pathname, sr=32000)[0].astype(np.float32)\n    return audio\n\ndef predict_soundscape(waveform, models, ort_sessions, cfg):\n    dataset = SoundScapeDataset(waveform, False, cfg)\n    dataloader = get_data_loader(dataset, False, cfg)\n    preds = predict(dataloader, models, ort_sessions, cfg)\n    return preds\n\ndef load_checkpoint(fold, seed, cfg):\n    if cfg.pretrained_path:\n        checkpoint_path = cfg.pretrained_path\n    else:\n        checkpoint_path = cfg.checkpoint_path\n    save_path = checkpoint_path / ('%s_%d_%d.pt' % (cfg.fname, fold, seed, ))\n    cfg.logger.info('loading %s ...' % save_path)\n    checkpoint = torch.load(save_path, map_location='cpu')\n    model = BirdModel(cfg, pretrained=False).to(cfg.device)\n    model.load_state_dict(checkpoint['model'], strict=True)\n    model.eval()\n    cfg.logger.info('done')\n    return model\n","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:12:03.775726Z","iopub.execute_input":"2024-05-28T07:12:03.776076Z","iopub.status.idle":"2024-05-28T07:12:03.800804Z","shell.execute_reply.started":"2024-05-28T07:12:03.776047Z","shell.execute_reply":"2024-05-28T07:12:03.799555Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"models = [load_checkpoint(fold, cfg.seed + fold, cfg) for fold in range(cfg.num_folds)]\nfor model in models:\n    model.eval()\n    model.zero_grad()\n    model.half().float()","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:12:03.802687Z","iopub.execute_input":"2024-05-28T07:12:03.803081Z","iopub.status.idle":"2024-05-28T07:12:05.197294Z","shell.execute_reply.started":"2024-05-28T07:12:03.80305Z","shell.execute_reply":"2024-05-28T07:12:05.196199Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"input_tensor = torch.randn(cfg.onnx_batch_size, 3, cfg.image_height, cfg.image_width)  # input shape\ncfg.input_names = ['x']\ncfg.output_names = ['output']","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:12:05.199057Z","iopub.execute_input":"2024-05-28T07:12:05.200096Z","iopub.status.idle":"2024-05-28T07:12:05.288934Z","shell.execute_reply.started":"2024-05-28T07:12:05.200056Z","shell.execute_reply":"2024-05-28T07:12:05.287672Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"onnx_ckpt_list = list()\nfor (i, model) in enumerate(models):\n    model.eval()\n    \n    # == convert to onnx ==\n    torch.onnx.export(model.backbone, input_tensor, f\"model_{i}.onnx\", verbose=False, input_names=cfg.input_names, output_names=cfg.output_names)\n    onnx_ckpt_list.append(f\"model_{i}.onnx\")","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:12:05.290548Z","iopub.execute_input":"2024-05-28T07:12:05.290951Z","iopub.status.idle":"2024-05-28T07:12:16.121306Z","shell.execute_reply.started":"2024-05-28T07:12:05.290918Z","shell.execute_reply":"2024-05-28T07:12:16.119934Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import onnx\nimport onnxruntime as ort\n\nort_sessions = []\nfor i in range(len(models)):\n    onnx_model = onnx.load(f\"model_{i}.onnx\")\n    onnx.checker.check_model(onnx_model)\n    ort_session = ort.InferenceSession(f\"model_{i}.onnx\")\n    ort_sessions.append(ort_session)","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:12:16.124334Z","iopub.execute_input":"2024-05-28T07:12:16.12479Z","iopub.status.idle":"2024-05-28T07:12:16.676059Z","shell.execute_reply.started":"2024-05-28T07:12:16.124749Z","shell.execute_reply":"2024-05-28T07:12:16.67473Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ort_session = ort_sessions[0]\nout = ort_session.run(cfg.output_names, {cfg.input_names[0] : input_tensor.numpy()})","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:12:16.680084Z","iopub.execute_input":"2024-05-28T07:12:16.680539Z","iopub.status.idle":"2024-05-28T07:12:17.145409Z","shell.execute_reply.started":"2024-05-28T07:12:16.680495Z","shell.execute_reply":"2024-05-28T07:12:17.144181Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"out[0].shape","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:12:17.147198Z","iopub.execute_input":"2024-05-28T07:12:17.147591Z","iopub.status.idle":"2024-05-28T07:12:17.156251Z","shell.execute_reply.started":"2024-05-28T07:12:17.147549Z","shell.execute_reply":"2024-05-28T07:12:17.154894Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def infer_onnx(model, ort_session, x, cfg):\n    x = ort_session.run(cfg.output_names, {cfg.input_names[0] : x.numpy()})[0]\n    return x\n\n","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:12:17.158325Z","iopub.execute_input":"2024-05-28T07:12:17.158709Z","iopub.status.idle":"2024-05-28T07:12:17.168064Z","shell.execute_reply.started":"2024-05-28T07:12:17.158675Z","shell.execute_reply":"2024-05-28T07:12:17.166678Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hidden test files\ntest_files = glob(str(cfg.test_path / '*.ogg'))\nif len(test_files) == 0:\n    test_files = sorted(glob(str(cfg.soundscape_path / '*.ogg')))[:10]","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:12:17.170038Z","iopub.execute_input":"2024-05-28T07:12:17.170599Z","iopub.status.idle":"2024-05-28T07:12:17.426138Z","shell.execute_reply.started":"2024-05-28T07:12:17.170558Z","shell.execute_reply":"2024-05-28T07:12:17.424516Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\ntest_files = np.array(test_files)","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:12:17.427563Z","iopub.execute_input":"2024-05-28T07:12:17.42792Z","iopub.status.idle":"2024-05-28T07:12:17.433514Z","shell.execute_reply.started":"2024-05-28T07:12:17.427889Z","shell.execute_reply":"2024-05-28T07:12:17.432093Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"inference_batch_size = 100\ncfg.verbose = False\ninference_rows = []\nfor i in tqdm(range(0, len(test_files), inference_batch_size)):\n    batch_files = test_files[i : i + inference_batch_size]\n    soundscapes = joblib.Parallel(n_jobs=4)(  # , backend='loky'\n        joblib.delayed(load_soundscape)(path) for path in batch_files\n    )\n    for waveform, soundscape_path in zip(soundscapes, batch_files):\n        row_id = re.search(r'/([^/]+)\\.ogg$', soundscape_path).group(1)\n        #waveform = load_soundscape(soundscape_path, cfg)\n        preds = predict_soundscape(waveform, models, ort_sessions, cfg)\n        max_preds = preds.max(0, keepdims=True)\n        max_preds = max_preds + (preds.mean() - max_preds.mean())\n        preds = preds + max_preds\n        preds = expit(preds)\n        for i in range(preds.shape[1]):\n            p = np.pad(preds[:, i], (2, 2), mode=\"edge\")\n            preds[:, i] = np.convolve(p, np.array([0.1, 0.2, 0.4, 0.2, 0.1]), mode=\"valid\")\n        for t, pred in zip(range(cfg.num_rows), preds):\n            predictions = dict([ (l,p) for l, p in zip(cfg.labels, pred) ])\n            inference_rows.append(\n                { 'row_id': f'{row_id}_{(t+1)*5}' } | predictions\n            )\n    del soundscapes\n    gc.collect()\nsubmission = pd.DataFrame(inference_rows)\n\nsubmission.to_csv('submission.csv', index=False)\n\nsubmission","metadata":{"execution":{"iopub.status.busy":"2024-05-28T07:23:36.842314Z","iopub.execute_input":"2024-05-28T07:23:36.842781Z","iopub.status.idle":"2024-05-28T07:24:01.415972Z","shell.execute_reply.started":"2024-05-28T07:23:36.842741Z","shell.execute_reply":"2024-05-28T07:24:01.414814Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}