{"cells":[{"metadata":{},"cell_type":"markdown","source":"## RainForest Species Audio Detection Pytorch Starter"},{"metadata":{"trusted":true},"cell_type":"code","source":"tez_path = '../input/tez-modified-tqdm/'\neffnet_path = '../input/pytorch-efficientnet'\nimport sys\nsys.path.append(tez_path)\nsys.path.append(effnet_path)\nsys.path.append('../input/multistartifiedkfold')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Special Thanks to [Abhishek Thakur](https://www.kaggle.com/abhishek) for tez Library which makes traing faster and Still pretty close to raw pytorch code**"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import albumentations\nimport pandas as pd\nimport plotly.express as px\nimport seaborn as sns\nimport tez\nimport torch\nfrom tqdm.notebook import tqdm\nimport torch.nn as nn\nfrom torch.nn import functional as F\nimport torchaudio\nimport librosa\nimport random\nimport tez\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport audioread\nimport cv2\nfrom sklearn.metrics import label_ranking_average_precision_score\nfrom sklearn.model_selection import train_test_split\nimport torch.nn as nn\nfrom efficientnet_pytorch import EfficientNet\nimport soundfile\nfrom pathlib import Path\nfrom iterstrat.ml_stratifiers import MultilabelStratifiedKFold","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"path = Path('../input/rfcx-species-audio-detection')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.read_csv(path/'train_tp.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df['recording_id'] = df['recording_id']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"files = df.recording_id.tolist()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(df.shape)\ndf.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Processing csv files"},{"metadata":{"trusted":true},"cell_type":"code","source":"fnames = df.recording_id.unique().tolist()\ndf_gr = df.groupby(['recording_id'])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Converting Labels to OneHotEncoded targets"},{"metadata":{"trusted":true},"cell_type":"code","source":"bird_dict = {}\nfor fn in tqdm(fnames):\n    lbls = np.zeros(24)\n    temp = df_gr.get_group(fn)\n    sps = temp.species_id.unique()\n    for ss in sps:\n        lbls[ss] = 1\n    bird_dict[fn] = lbls","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"bird_df = pd.DataFrame.from_dict(bird_dict,orient='index').reset_index()\nbird_df.columns = ['recording_id'] + ['species_id_'+str(x) for x in range(24)]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"bird_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_agg = df.groupby(['recording_id']).agg({'t_min':lambda x :min(x),'t_max':lambda x :max(x)}).reset_index()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_agg['duration'] = df_agg['t_max'] - df_agg['t_min']\ndf_agg['duration'] = df_agg['duration'].apply(lambda x: x+abs(x-10) if x<=3 else x)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"trn_df = bird_df.merge(df_agg,on='recording_id',how='left')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"trn_df['recording_id'] = '../input/rfcx-species-audio-detection/train/' +trn_df['recording_id'] + '.flac'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"trn_df.sample(n=10)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"tar_cols = ['species_id_'+str(x) for x in range(24)]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Returns spectogram"},{"metadata":{"trusted":true},"cell_type":"code","source":"import librosa\nimport cv2,os\n#from https://www.kaggle.com/daisukelab/creating-fat2019-preprocessed-data\ndef mono_to_color(X, mean=None, std=None, norm_max=None, norm_min=None, eps=1e-6):\n    # Stack X as [X,X,X]\n#     X = np.stack([X, X, X], axis=-1)\n\n    # Standardize\n    mean = mean or X.mean()\n    X = X - mean\n    std = std or X.std()\n    Xstd = X / (std + eps)\n    _min, _max = Xstd.min(), Xstd.max()\n    norm_max = norm_max or _max\n    norm_min = norm_min or _min\n    if (_max - _min) > eps:\n        # Normalize to [0, 255]\n        V = Xstd\n        V[V < norm_min] = norm_min\n        V[V > norm_max] = norm_max\n        V = 255 * (V - norm_min) / (norm_max - norm_min)\n        V = V.astype(np.uint8)\n    else:\n        # Just zero\n        V = np.zeros_like(Xstd, dtype=np.uint8)\n    return V\n\ndef build_spectrogram(path,offset,duration=15):\n    y, sr = librosa.load(path,offset=np.floor(offset),duration=np.ceil(duration))\n    total_secs = y.shape[0] / sr\n    M = librosa.feature.melspectrogram(y=y, sr=sr)\n    M = librosa.power_to_db(M)\n    #print(M.shape)\n    M = mono_to_color(M)\n    if M.shape[1]<600:\n        new_img = np.zeros((128,600))\n        new_img[:M.shape[0],:M.shape[1]] = M\n        M = new_img\n    else:\n        M = M\n    return M[:,:600]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"n = random.randint(0,400)\nimg = build_spectrogram(trn_df.iloc[n]['recording_id'],offset=int(trn_df.iloc[n]['t_min']),duration=15);img.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(1,figsize=(10,6))\nplt.imshow(img,cmap='inferno');","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## MultiStratifiedKfold"},{"metadata":{"trusted":true},"cell_type":"code","source":"trn_df = trn_df.sample(frac=1.,random_state = 2020)\ntrn_df['kfold'] = -1\ny = trn_df[tar_cols].values\nkf = MultilabelStratifiedKFold(n_splits=5,random_state = 2020,shuffle = True)\nfor fold ,(trn_,val_ )in enumerate(kf.split(X=trn_df,y=y)):\n    trn_df.loc[val_,'kfold'] = fold","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"trn_df.to_csv('rain_forest_train_kfold.csv',index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"px.violin(data_frame=trn_df,x='duration',title='duration',box=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"IMAGE_SIZE = 128\ntrain_aug = albumentations.Compose(\n    [\n        #albumentations.Resize(128, 600,p=1.0),\n        albumentations.Normalize(\n            mean=[0.485],\n            std=[0.229],\n            max_pixel_value=255.0,\n            p=1.0,\n        ),\n    ],\n    p=1.0,\n)\n\nvalid_aug = albumentations.Compose(\n    [\n        #albumentations.Resize(128, 600, p=1.0),\n        albumentations.Normalize(\n            mean=[0.485],\n            std=[0.229],\n            max_pixel_value=255.0,\n            p=1.0,\n        ),\n    ],\n    p=1.0,\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Audio Dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"class AudioDataset:\n    def __init__(self,audio_paths,targets,offset,duration,augmentations=None,channel_first=False,grayscale=True):\n        self.audio_paths = audio_paths\n        self.targets = targets\n        self.offset = offset\n        self.duration = duration\n        self.augmentations = augmentations\n        \n    def __len__(self):\n        return len(self.audio_paths)\n    \n    def __getitem__(self,item):\n        targets = self.targets[item]\n        image = build_spectrogram(self.audio_paths[item],self.offset[item],10)\n        image = np.array(image)\n        #print(image.shape)\n        if self.augmentations is not None:\n            augmented = self.augmentations(image=image)\n            image = augmented[\"image\"]\n        image = np.nan_to_num(image)\n        image_tensor = torch.tensor(image)\n        image_tensor = image_tensor.unsqueeze(0)\n        return {\"image\": image_tensor,\n                \"targets\": torch.tensor(targets,dtype=torch.float)}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"FOLD = 2","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train,df_valid = trn_df[trn_df.kfold!=FOLD],trn_df[trn_df.kfold==FOLD]\ndf_train = df_train.reset_index(drop=True)\ndf_valid = df_valid.reset_index(drop=True)\ntrain_targets = df_train[tar_cols].values\nvalid_targets = df_valid[tar_cols].values","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_dataset = AudioDataset(df_train.recording_id,\n                             train_targets,\n                             offset=df_train['t_min'].values,\n                             duration=df_train['duration'].values,\n                             augmentations=train_aug)\nvalid_dataset = AudioDataset(df_valid.recording_id,\n                             valid_targets,\n                             offset=df_valid['t_min'].values,\n                             duration=df_valid.duration.values,\n                             augmentations=valid_aug)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Let's check a valid_dataset sample:"},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(1,figsize=(10,6))\nplt.imshow(valid_dataset[5]['image'].numpy()[0,:,:],cmap='inferno');","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"wp3 = '../input/efficientnet-pytorch/efficientnet-b3-c8376fa2.pth'","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Species Audio Detection Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"class SpeciesModel(tez.Model):\n    def __init__(self):\n        super().__init__()\n\n        self.effnet = EfficientNet.from_pretrained(\"efficientnet-b3\",weights_path=wp3)\n\n        self.effnet._conv_stem.in_channels = 1\n        weight = self.effnet._conv_stem.weight.mean(1, keepdim=True)\n        self.effnet._conv_stem.weight = torch.nn.Parameter(weight)\n\n        self.dropout = nn.Dropout(0.1)\n        self.out = nn.Linear(1536, 24)\n        self.step_scheduler_after = \"epoch\"\n        self.step_scheduler_metric = \"valid_label_rank_avg_prec_sc\"\n        \n\n    def monitor_metrics(self, outputs, targets):\n        outputs = outputs.cpu().detach().numpy()\n        targets = targets.cpu().detach().numpy()\n        \n        return {\"label_rank_avg_prec_sc\": label_ranking_average_precision_score(targets,outputs)}\n\n    def fetch_optimizer(self):\n        opt = torch.optim.Adam(self.parameters(), lr=1e-3)\n        return opt\n\n    def fetch_scheduler(self):\n        rlr = torch.optim.lr_scheduler.ReduceLROnPlateau(\n            self.optimizer,\n            verbose=True,\n            factor=0.7,\n            mode=\"max\",\n            patience=2,\n            threshold=0.01,\n        )\n        return rlr\n\n    def forward(self, image, targets=None):\n        batch_size, _, _, _ = image.shape\n\n        x = self.effnet.extract_features(image)\n        x = F.adaptive_avg_pool2d(x, 1).reshape(batch_size, -1)\n        outputs = self.out(self.dropout(x))\n        if targets is not None:\n            loss = nn.BCEWithLogitsLoss()(\n                outputs, targets.type_as(outputs)\n            )\n            metrics = self.monitor_metrics(outputs, targets)\n            return outputs, loss, metrics\n        return outputs, None, {}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = SpeciesModel()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from tez.callbacks import EarlyStopping\nes = EarlyStopping(\n    monitor=\"valid_label_rank_avg_prec_sc\", model_path=\"model.bin\", patience=5, mode=\"max\"\n    )","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.fit(\n        train_dataset,\n        valid_dataset=valid_dataset,\n        train_bs=64,\n        valid_bs=16,\n        device=\"cuda\",\n        epochs=10,\n        callbacks=[es],\n        fp16=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}