{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install /kaggle/input/timm-pytorch-image-models/pytorch-image-models-master/\n!pip install --no-deps /kaggle/input/evaluations/","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install ../input/torchlibrosa/torchlibrosa-0.0.5-py3-none-any.whl","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport cv2\nimport sys\nimport time\nimport math\n\nimport random\nimport librosa\nimport warnings\nimport torchaudio\nimport torchvision\nimport numpy as np\nimport pandas as pd\nimport typing as tp\nimport IPython.display as ipd\nimport matplotlib.pyplot as plt\n\nfrom pathlib import Path\n\nimport torch\nimport torch.nn as nn\nimport torch.utils.data as data\nimport torch.nn.functional as F\n\nfrom torch.utils.data import DataLoader\nfrom torch.nn.modules.utils import _pair\nfrom torch.nn import Conv2d, Module, Linear, BatchNorm2d, ReLU\n\n\npd.options.display.max_rows = 500\npd.options.display.max_columns = 500","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Utils","metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a"}},{"cell_type":"code","source":"def load_audio(path, sr):\n    clip, _ = librosa.load(path, sr=sr, mono=True, res_type=\"kaiser_fast\")\n    return clip","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_model_weights(model, weights):\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    state_dict = torch.load(weights, map_location=device)\n    model.load_state_dict(state_dict)","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def seed_everything(seed):\n    \"\"\"\n    Seeds basic parameters for reproductibility of results\n    \n    Arguments:\n        seed {int} -- Number of the seed\n    \"\"\"\n    random.seed(seed)\n    os.environ[\"PYTHONHASHSEED\"] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = True #False","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data","metadata":{}},{"cell_type":"code","source":"SEED = 1213\nseed_everything(SEED)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ROOT = Path.cwd().parent\nINPUT_ROOT = ROOT / \"input\"\nRAW_DATA = INPUT_ROOT / \"birdsong-recognition\"\nTRAIN_AUDIO_DIR = RAW_DATA / \"train_audio\"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = pd.read_csv(RAW_DATA / \"train.csv\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"TEST_AUDIO_DIR = RAW_DATA / \"test_audio\"\n\nif not TEST_AUDIO_DIR.exists():\n    TEST_AUDIO_DIR = INPUT_ROOT / \"birdcall-check\" / \"test_audio\"\n    test = pd.read_csv(INPUT_ROOT / \"birdcall-check\" / \"test.csv\")\nelse:\n    test = pd.read_csv(RAW_DATA / \"test.csv\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Parameters","metadata":{}},{"cell_type":"code","source":"CLASSES = sorted(os.listdir(TRAIN_AUDIO_DIR))\nNUM_CLASSES = len(CLASSES)\nNUM_WORKERS = 4","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class AudioParams:\n    sr = 32000\n    stride = 5\n    true_kernel_size = 5\n\n    img_size = None\n    \n    # Melspectrogram\n    n_mels = 128\n    fmin = 20\n    fmax = 16000","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Dataset","metadata":{}},{"cell_type":"code","source":"def convert_site_3(df, clip_length, params):\n    n_samples = clip_length // (params.sr * params.true_kernel_size)  # may lose the end \n    \n    audio_id = [df['audio_id'].values[0]] * n_samples\n    site = ['site_3'] * n_samples\n    seconds = [i * params.true_kernel_size for i in range(1, n_samples + 1)]\n    row_id = [f'site_3_{audio_id[0]}_{int(s)}' for s in seconds]\n    \n    new_df = pd.DataFrame(data={'site': site,\n                                'row_id': row_id,\n                                'seconds': seconds,\n                                'audio_id': audio_id\n                               })\n    \n    return new_df","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def compute_melspec(y, params):\n    melspec = librosa.feature.melspectrogram(\n        y,\n        sr=params.sr,\n        n_mels=params.n_mels,\n        fmin=params.fmin,\n        fmax=params.fmax\n    )\n    \n    melspec = librosa.power_to_db(melspec).astype(np.float32)\n    \n    return melspec","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class TestDataset(data.Dataset):\n    def __init__(self, df, clip, params):\n        self.df = df\n        self.clip = clip\n        self.params = params\n        \n        if df['site'].values[0] == 'site_3':\n            self.df = convert_site_3(df, len(clip), params)\n        \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, idx):\n        row_id = self.df['row_id'][idx]\n        \n        end_seconds = int(self.df['seconds'][idx])\n        start_seconds = int(end_seconds - 5)\n\n        start_index = self.params.sr * start_seconds\n        end_index = self.params.sr * end_seconds\n\n        y = self.clip[start_index:end_index].astype(np.float32)\n        \n        return y","metadata":{"_kg_hide-input":false,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import timm\nclass AudioParams:\n    sr = 32000\n    stride = 5\n    true_kernel_size = 5\n\n    img_size = None\n    \n    # Melspectrogram\n    n_mels = 128\n    fmin = 20\n    fmax = 16000","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Model loader","metadata":{}},{"cell_type":"code","source":"from torchlibrosa.stft import LogmelFilterBank, Spectrogram\nfrom torchlibrosa.augmentation import SpecAugmentation\n\nclass BirdCLEFNet(nn.Module):\n    def __init__(self, model_name,params):\n        super(BirdCLEFNet, self).__init__()\n        self.model_name = model_name\n        self.n_label = 264\n        self.params=params\n        self.spectrogram_extractor = Spectrogram(n_fft=2048, hop_length=512,\n                                                 win_length=None, window=\"hann\", center=True, pad_mode=\"reflect\",\n                                                 freeze_parameters=True)\n\n        # Logmel feature extractor\n        self.logmel_extractor = LogmelFilterBank(sr=params.sr, n_fft=2048,\n                                                 n_mels=params.n_mels, fmin=params.fmin, fmax=params.fmax, ref=1.0, amin=1e-10, top_db=80.0,\n                                                 freeze_parameters=True)\n        \n        self.spec_augmenter = SpecAugmentation(time_drop_width=8,time_stripes_num=2,\n                                               freq_drop_width=4,freq_stripes_num=2)\n        \n        self.base_model = timm.create_model(model_name, pretrained=False,num_classes=self.n_label,in_chans=3)\n\n\n    def forward(self, x):  # input x: (batch, channel, Hz, time)\n        x = self.spectrogram_extractor(x)\n        x = self.logmel_extractor(x)\n        x=(x-x.mean())/x.std()\n        x=torch.squeeze(x,dim=1)\n        x = torch.stack([x,x,x],dim=1)\n        x=self.base_model(x)\n        return x","metadata":{"_kg_hide-input":false,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Inference","metadata":{}},{"cell_type":"markdown","source":"### Predict","metadata":{}},{"cell_type":"code","source":"def predict(model, dataset, batch_size=16):\n    model.eval()\n    preds = np.empty((0, NUM_CLASSES))\n    \n    loader = DataLoader(\n        dataset, batch_size=batch_size, shuffle=False, num_workers=NUM_WORKERS\n    )\n\n    with torch.no_grad():\n        for x in loader:\n#             print(x.shape)\n            y_pred = model(x.cuda()).detach()\n            preds = np.concatenate([preds, torch.sigmoid(y_pred).cpu().numpy()])\n    \n    return preds","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Post-process","metadata":{}},{"cell_type":"code","source":"def post_process_site_12(preds, threshold=0.5, maxpreds=3):\n    preds = preds * (preds >= threshold)   # remove preds < threshold\n    \n#     next_preds = np.concatenate([preds[1:], preds[-1:]])  # pred corresponding to next window\n#     prev_preds = np.concatenate([preds[:1], preds[:-1]])  # pred corresponding to previous window\n\n    next_preds = np.concatenate([preds[1:], np.zeros((1, preds.shape[-1]))])  # pred corresponding to next window\n    prev_preds = np.concatenate([np.zeros((1, preds.shape[-1])), preds[:-1]])  # pred corresponding to previous window\n    \n    score = preds + next_preds + prev_preds  # Aggregating\n    \n    n_birds = (score >= threshold - 1e-5).sum(-1)   # threshold ?\n    n_birds = np.clip(n_birds, 0, maxpreds)  # keep at most maxpreds birds\n    \n    labels = [np.argsort(- score[i])[:n_birds[i]].tolist() for i in range(len(preds))]\n    class_labels = [\" \".join([CLASSES[l] for l in label]) for label in labels]\n    \n    return class_labels","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def post_process_site_3(preds, threshold=0.6, maxpreds=3):\n    preds = preds * (preds >= threshold)   # remove preds < threshold\n\n    score = np.sum(preds, 0)    # Aggregating\n    \n    n_birds = (score >= threshold - 1e-5).sum(-1)\n    n_birds = np.clip(n_birds, 0, maxpreds)  # keep at most maxpreds birds\n    \n    label = np.argsort(- score)[:n_birds].tolist()\n    \n    class_labels = \" \".join([CLASSES[l] for l in label])\n    return class_labels","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def max_pred_gen(site, duration):\n    if site != \"site_3\":\n        return 3\n    else:\n        rets = [(7,2), (15, 3), (30, 5), (60, 7)]\n        \n        for ref_duration,thresh in rets:\n            if ref_duration >= duration:\n                return thresh\n        return 10","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def reformat_preds(preds, df, site):\n    prediction_df = pd.DataFrame({\n        \"row_id\": df['row_id'].values,\n        \"birds\": preds\n    })\n    \n    prediction_df['birds'] = prediction_df['birds'].replace([''],'nocall')\n    \n    return prediction_df","metadata":{"_kg_hide-input":false,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Inference","metadata":{}},{"cell_type":"code","source":"def inference(test_df, test_audio,params,threshold=0.5):\n    unique_audio_id = test_df.audio_id.unique()\n    \n    models=BirdCLEFNet('resnext50_32x4d',AudioParams)\n    models.to('cuda:0')\n    models.load_state_dict(torch.load('../input/cornell-data-downloading-version1/birdclefnet_f0_best_model_resnext50_32x4dtry7_cornell(30).pth'))\n    pred_dfs = []\n    for audio_id in unique_audio_id :\n        \n        audio_df = test_df[test_df['audio_id'] == audio_id].reset_index(drop=True)\n        site = audio_df[\"site\"].values[0]\n        \n        print(f'\\nMaking predictions for audio {audio_id} in {site} ')\n\n        clip = load_audio(test_audio / (audio_id + \".mp3\"), params.sr)\n        clip_duration = len(clip) // params.sr\n        \n        dataset = TestDataset(audio_df, clip, params)\n        \n        preds = []\n        pred = predict(models, dataset)\n        preds.append(pred)\n            \n        preds = np.mean(preds, 0)\n        \n        maxpreds = max_pred_gen(site, clip_duration)\n        print(f'Limiting the number of birds to {maxpreds}')\n        \n        if site == 'site_3':\n            preds_pp = post_process_site_3(preds, threshold=threshold, maxpreds=maxpreds)\n        else:\n            preds_pp = post_process_site_12(preds, threshold=threshold, maxpreds=maxpreds)\n        \n        print(\"Predicted classes :\", preds_pp)\n        \n        pred_df = reformat_preds(preds_pp, audio_df, site)\n        pred_dfs.append(pred_df)\n    \n    sub = pd.concat(pred_dfs, axis=0, sort=False).reset_index(drop=True)\n    return sub","metadata":{"_kg_hide-input":false,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Prediction","metadata":{}},{"cell_type":"markdown","source":"### Used models","metadata":{}},{"cell_type":"code","source":"warnings.filterwarnings(\"ignore\")\nthreshold=0.3\nsubmission = inference(test, TEST_AUDIO_DIR, AudioParams,threshold=threshold)","metadata":{"_kg_hide-output":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Submission","metadata":{}},{"cell_type":"code","source":"submission.to_csv(\"submission.csv\", index=False)\nsubmission","metadata":{"_kg_hide-output":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}