{"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":"markdown","source":"# A simple baseline of the pytorch version is built based on resnet34 of the timm library. Training and inference code is provided.\n\n\n\n# For the training code we use an online transformation, so the training is very slow. Pre-processing before training can speed up model training.\n\n\n# Many thanks to the notebooks below for the reference:\nhttps://www.kaggle.com/tattaka/birdclef2022-submission-baseline\n\nhttps://www.kaggle.com/myso1987/birdclef2022-pytorch-resnet34-starter-lb-0-50\n\netc.\n\n# *The inference code is not easy to understand and will be improved in the future.*\n","metadata":{}},{"cell_type":"code","source":"!pip install ../input/timm-package/timm-0.4.12-py3-none-any.whl","metadata":{"execution":{"iopub.status.busy":"2022-03-17T02:24:55.112593Z","iopub.execute_input":"2022-03-17T02:24:55.112876Z","iopub.status.idle":"2022-03-17T02:25:24.517945Z","shell.execute_reply.started":"2022-03-17T02:24:55.112833Z","shell.execute_reply":"2022-03-17T02:25:24.516750Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport json\nimport tqdm\nimport random\nimport shutil\nimport pandas as pd\nimport numpy as np\nimport torch\nimport torch.optim as optim\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nimport torchaudio\nimport torchaudio.transforms as T\nfrom sklearn.model_selection import train_test_split, GroupKFold, StratifiedKFold, KFold\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nimport matplotlib.pyplot as plt\nfrom sklearn.metrics import f1_score\nimport timm\nfrom torchaudio.transforms import MelSpectrogram, Resample\nimport re\nimport torch.nn.functional as F\nimport soundfile as sf\nimport glob\n\n\nclass Config:\n    seed = 2022 \n    num_classes = 152 \n    epochs = 21\n    batch_size = 48\n    n_fold = 5 \n    learning_rate = 3e-4 \n    img_size = 128 \n    print_freq = 100 \n    model_save_dir = './' \n    pretrained = False\n\nCFG = Config()\n\ndef seed_everything(seed: int):\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\n\n\ndef extract_call(data, call = 'call'):\n    try:\n        if re.search(data, call):\n            return \"True\"\n        else:\n            return \"False\"\n    except:\n        return \"False\"\n\ndef get_train_transforms():\n    return A.Compose([\n            A.Resize(CFG.img_size, CFG.img_size),\n            A.Normalize(mean=[0.485], std=[0.229], max_pixel_value=255.0, p=1.0), \n            ToTensorV2(p=1.0),\n        ], p=1.)\n        \ndef get_val_transforms():\n    return A.Compose([\n            A.Resize(CFG.img_size, CFG.img_size),\n            A.Normalize(mean=[0.485], std=[0.229], max_pixel_value=255.0, p=1.0),\n            ToTensorV2(p=1.0),\n        ], p=1.)\n\n    \nclass MyDataset(Dataset):\n    def __init__(self, image_paths=None, label_paths=None, transforms=None, mode='train'):\n        self.image_paths = image_paths\n        self.label_paths = label_paths\n        self.transforms = transforms\n        self.mode = mode\n        self.len = len(label_paths)\n        self.target_sample_rate = 32000\n        self.num_samples = 32000*5\n        self.mel_spectrogram = T.MelSpectrogram(sample_rate=self.target_sample_rate, n_fft=2048, win_length=None, hop_length=1024, center=True,\n                                       pad_mode=\"reflect\", power=2.0, norm='slaney', onesided=True, n_mels=128,\n                                       mel_scale=\"htk\", )\n        \n    def __getitem__(self, idx):\n        if self.mode == 'train':\n            audio, sample_rate = torchaudio.load(self.image_paths[idx])\n            audio = self.to_mono(audio)\n            if sample_rate != self.target_sample_rate:\n                resample = Resample(sample_rate, self.target_sample_rate)\n                audio = resample(audio)\n            if audio.shape[0] > self.num_samples:\n                audio = self.crop_audio(audio)\n            else:    \n                audio = self.pad_audio(audio)\n            mel =self.mel_spectrogram(audio)\n            mel = torchaudio.transforms.AmplitudeToDB()(mel)\n            mel = np.array(mel)\n            mel = self.scale_minmax(mel, 0, 255)\n            inputs = mel[:, :, np.newaxis] #[m,n]->[m,n,1]\n            labels = torch.tensor(self.label_paths[idx], dtype=torch.long)\n            augments = self.transforms(image=inputs)\n            inputs = augments['image']\n            return inputs, labels\n\n        elif self.mode == 'test':\n            SR = self.target_sample_rate\n            audio, sample_rate = torchaudio.load(self.image_paths)\n            audio = self.to_mono(audio)\n            sample = self.label_paths.loc[idx, :]  #test, is no label, only clip\n            row_id = sample.row_id \n            end_seconds = int(sample.seconds)\n            start_seconds = int(end_seconds - 5)\n            end_index = int(SR * (end_seconds + (60 - 5) / 2) + len(audio) // 3)\n            start_index = int(SR * (start_seconds - (60 - 5) / 2) + len(audio) // 3)\n            \n            audio = audio[start_index:end_index]\n            if sample_rate != self.target_sample_rate:\n                resample = Resample(sample_rate, self.target_sample_rate)\n                audio = resample(audio)\n            if audio.shape[0] > self.num_samples:\n                audio = self.crop_audio(audio)\n            else:    \n                audio = self.pad_audio(audio)\n            mel =self.mel_spectrogram(audio)\n            mel = torchaudio.transforms.AmplitudeToDB()(mel)\n            mel = np.array(mel)\n            mel = self.scale_minmax(mel, 0, 255)\n            inputs = mel[:, :, np.newaxis] \n            augments = self.transforms(image=inputs)\n            inputs = augments['image']\n            return inputs, row_id \n\n    def pad_audio(self, audio):\n        pad_length = self.num_samples - audio.shape[0]\n        last_dim_padding = (0, pad_length)\n        audio = F.pad(audio, last_dim_padding)\n        return audio\n        \n    def crop_audio(self, audio):\n        return audio[:self.num_samples]\n    \n    def to_mono(self, audio):\n        return torch.mean(audio, axis=0)\n    \n    def scale_minmax(self, X, min=0.0, max=1.0):\n        X_std = (X - X.min()) / (X.max() - X.min())\n        X_scaled = X_std * (max - min) + min\n        return X_scaled\n    \n    def __len__(self):\n        return self.len\n\n\n# =============================== model ========================\nclass MyModel(nn.Module):\n    def __init__(self, num_classes=2, pretrained=True):\n        super().__init__()\n        self.model = timm.create_model('resnet34', in_chans=1, pretrained=pretrained)\n        n_features = self.model.fc.in_features\n        self.model.fc = nn.Linear(n_features, num_classes)\n\n    def forward(self, x):\n        x = self.model(x)\n        return x\n\ndef prediction_for_clip(audio_path, test_df, models,  threshold=0.05):\n\n    test_dataset = MyDataset(audio_path, test_df, transforms=get_val_transforms(), mode='test')\n    test_loader = DataLoader(test_dataset, batch_size=1, shuffle=False)\n\n    prediction_dict = {}\n    for inputs, row_id in test_loader:\n#         print(row_id)\n        inputs = inputs.to(device)\n        with torch.no_grad():\n            probas = []\n            for model in models:\n                model.eval()\n                output = model(inputs)\n                output = torch.sigmoid(output)\n                probas.append(output.detach().cpu().numpy().reshape(-1))\n            probas = np.array(probas)\n            events = probas.mean(0) >= threshold\n            events2 = probas.mean(0)\n        labels = np.argwhere(events).reshape(-1).tolist()\n        labels2 = np.argmax(events2).reshape(-1).tolist()\n#         print(events)\n        print(labels)\n        if len(labels) == 0:\n            prediction_dict[str(row_id)] = \"nocall\"\n        else:\n            labels_str_list = list(map(lambda x: class_dict[x], labels2))\n            label_string = \" \".join(labels_str_list)\n            prediction_dict[str(row_id)] = label_string\n        print(prediction_dict[str(row_id)])\n    return prediction_dict\n\nif __name__ == \"__main__\":\n    device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n    seed_everything(CFG.seed)\n    \n    root_path = \"../input/birdclef-2022/\"\n    input_path = root_path + '/train_audio/'\n    train_meta = pd.read_csv(root_path + 'train_metadata.csv')\n    for  i in range(len(train_meta)):\n        train_meta.loc[i, \"filename\"] = input_path + train_meta.loc[i, \"filename\"]\n\n    print(\"Length of data before call extraction : {}\".format(len(train_meta)))\n    train_meta[\"type\"] = train_meta[\"type\"].apply(extract_call)\n    train_meta = train_meta[train_meta[\"type\"] == \"True\"].reset_index(drop=True)\n    train_meta.drop(\"type\", 1, inplace = True)\n    print(\"Length of data after call extraction : {}\".format(len(train_meta)))\n\n    class_dict = dict()\n    for index, label in enumerate(train_meta.primary_label.unique()):\n        class_dict[index] = label\n        train_meta[\"primary_label\"].replace(label, index, inplace = True)\n    print(class_dict)\n    #================================inference==========================\n    test_audios = list(glob.glob(\"../input/birdclef-2022/test_soundscapes/*.ogg\"))\n    sample_submission = pd.read_csv('../input/birdclef-2022/sample_submission.csv')\n    \n    threshold = 0.2 \n\n    model = MyModel(num_classes=CFG.num_classes, pretrained=CFG.pretrained).to(device)\n    model.load_state_dict(torch.load('../input/train-bird-pytorch-baseline/fold_1_best.pth'))\n    model.to(device)\n    models = [model]\n    \n    prediction_dicts = {}\n    for audio_path in test_audios:\n        print(audio_path)\n        seconds = []\n        row_ids = []\n        for second in range(5, 65, 5):\n            row_id = audio_path.split(\"/\")[-1][:-4] + f\"_{second}\"\n            seconds.append(second)\n            row_ids.append(row_id)\n        print(row_ids)\n        \n        test_df = pd.DataFrame({ \"row_id\": row_ids, \"seconds\": seconds })\n        prediction_dict = prediction_for_clip(audio_path, test_df, models=models, threshold=threshold)\n        prediction_dicts.update(prediction_dict)\n\n    for i in range(len(sample_submission)):\n        sample = sample_submission.row_id[i]\n        key = sample.split(\"_\")[0] + \"_\" + sample.split(\"_\")[1] + \"_\" + sample.split(\"_\")[3]\n        target_bird = sample.split(\"_\")[2]\n        print(key, target_bird)\n        if key in prediction_dicts:\n            sample_submission.iat[i, 1] = (target_bird in prediction_dicts[key])\n    sample_submission.to_csv(\"submission.csv\", index=False)\n    print(sample_submission)","metadata":{"execution":{"iopub.status.busy":"2022-03-17T02:25:24.522935Z","iopub.execute_input":"2022-03-17T02:25:24.523287Z","iopub.status.idle":"2022-03-17T02:25:47.016226Z","shell.execute_reply.started":"2022-03-17T02:25:24.523234Z","shell.execute_reply":"2022-03-17T02:25:47.015386Z"},"trusted":true},"execution_count":null,"outputs":[]}]}