{"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":"# Import Party!","metadata":{}},{"cell_type":"code","source":"import os,json,re,time,pdb,ast\nimport random\nimport numpy as np \nimport pandas as pd \nfrom glob import glob\nimport matplotlib.pyplot as plt\n%matplotlib inline\nimport seaborn as sns\nfrom tqdm import tqdm\nfrom collections import Counter\nfrom IPython.display import Audio, display\nfrom sklearn.model_selection import train_test_split\n\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# Pytorch imports for neural networks and tensor manipulations\nimport torch, torchaudio\nimport torch.nn as nn\nfrom torch.optim import Adam\nfrom torch.nn import CrossEntropyLoss\nfrom torchvision.transforms import Resize\nfrom torchaudio.transforms import MelSpectrogram\nfrom torch.utils.data import Dataset, DataLoader, random_split\n\nimport pytorch_lightning as pl\n\n\n# Libraries for visualization\n!pip install torchsummary -q\nimport torchsummary\nfrom termcolor import cprint\n\n\n","metadata":{"execution":{"iopub.status.busy":"2022-05-05T07:02:11.627389Z","iopub.execute_input":"2022-05-05T07:02:11.628184Z","iopub.status.idle":"2022-05-05T07:02:33.327571Z","shell.execute_reply.started":"2022-05-05T07:02:11.628064Z","shell.execute_reply":"2022-05-05T07:02:33.326495Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class config:\n    seed=2022\n    num_fold = 5\n    sample_rate= 32_000\n    n_fft=1024\n    hop_length=512\n    n_mels=64\n    duration=7\n    num_classes = 152\n    train_batch_size = 32\n    valid_batch_size = 64\n    model_name = 'resnet50'\n    epochs = 2\n    device = 'cuda' if torch.cuda.is_available() else 'cpu'\n    learning_rate = 1e-4","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def seed_everything(seed):\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","metadata":{"execution":{"iopub.status.busy":"2022-05-05T07:02:33.394071Z","iopub.execute_input":"2022-05-05T07:02:33.394674Z","iopub.status.idle":"2022-05-05T07:02:33.401611Z","shell.execute_reply.started":"2022-05-05T07:02:33.394626Z","shell.execute_reply":"2022-05-05T07:02:33.400295Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def display_(path,base_dir,disp_=False):\n    df=pd.read_csv(path)\n    df['path_filename']=df.filename.map(lambda x: base_dir+'/'+x)\n\n    class_dict = dict()\n\n    for index, label in enumerate(df.primary_label.unique()):\n        class_dict[index] = label\n        df[\"primary_label\"].replace(label, index, inplace = True)\n        \n    json.dump(class_dict, open(\"class_dict.json\", \"w\"))\n    \n    if disp_:\n        display(df)\n        print(df.info())\n        print(df.describe())\n        \n    return df\n    ","metadata":{"execution":{"iopub.status.busy":"2022-05-05T07:02:33.403855Z","iopub.execute_input":"2022-05-05T07:02:33.404304Z","iopub.status.idle":"2022-05-05T07:02:33.41621Z","shell.execute_reply.started":"2022-05-05T07:02:33.404262Z","shell.execute_reply":"2022-05-05T07:02:33.414819Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot(df):\n    plt.figure(figsize=(20, 6))\n    sns.countplot(df['primary_label'])\n    plt.xticks(rotation=90)\n    plt.title(\"Distribution of Primary Labels\", fontsize=20)\n    plt.show()\n    \n    plt.figure(figsize=(20, 6))\n    sns.countplot(df['rating'])\n    plt.title(\"Distribution of Ratings\", fontsize=20)\n    plt.show()\n    \n    df['type'] = df['type'].apply(lambda x : ast.literal_eval(x))\n    top = Counter([typ.lower() for lst in df['type'] for typ in lst])\n    top = dict(top.most_common(10))\n    plt.figure(figsize=(20, 6))\n    sns.barplot(x=list(top.keys()), y=list(top.values()), palette='hls')\n    plt.title(\"Top 10 song types\")\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-05-05T07:02:33.418378Z","iopub.execute_input":"2022-05-05T07:02:33.419205Z","iopub.status.idle":"2022-05-05T07:02:33.433751Z","shell.execute_reply.started":"2022-05-05T07:02:33.419159Z","shell.execute_reply":"2022-05-05T07:02:33.432834Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Basic Info display","metadata":{}},{"cell_type":"code","source":"def main():\n    seed_everything(config.seed)\n    path_trainmeta='../input/birdclef-2022/train_metadata.csv'\n    base_train_dir='../input/birdclef-2022/train_audio'\n    df=display_(path_trainmeta,base_train_dir,True)\n    print('*'*100)\n    print('SHORT EDA From base NB consolidated')\n    plot(df)\n    \nmain()","metadata":{"execution":{"iopub.status.busy":"2022-05-05T07:02:33.437363Z","iopub.execute_input":"2022-05-05T07:02:33.437593Z","iopub.status.idle":"2022-05-05T07:02:36.940334Z","shell.execute_reply.started":"2022-05-05T07:02:33.437566Z","shell.execute_reply":"2022-05-05T07:02:36.939528Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Basic Audio files display","metadata":{}},{"cell_type":"code","source":"# from torchaudio tutorial docs\n\ndef print_stats(waveform, sample_rate=None, src=None):\n    if src:\n        print(\"-\" * 10)\n        print(\"Source:\", src)\n        print(\"-\" * 10)\n    if sample_rate:\n        print(\"Sample Rate:\", sample_rate)\n    print(\"Shape:\", tuple(waveform.shape))\n    print(\"Dtype:\", waveform.dtype)\n    print(f\" - Max:     {waveform.max().item():6.3f}\")\n    print(f\" - Min:     {waveform.min().item():6.3f}\")\n    print(f\" - Mean:    {waveform.mean().item():6.3f}\")\n    print(f\" - Std Dev: {waveform.std().item():6.3f}\")\n    print()\n    print(waveform)\n    print()\n\n\ndef plot_waveform(waveform, sample_rate, title=\"Waveform\", xlim=None, ylim=None):\n    waveform = waveform.numpy()\n\n    num_channels, num_frames = waveform.shape\n    time_axis = torch.arange(0, num_frames) / sample_rate\n\n    figure, axes = plt.subplots(num_channels, 1)\n    if num_channels == 1:\n        axes = [axes]\n    for c in range(num_channels):\n        axes[c].plot(time_axis, waveform[c], linewidth=1)\n        axes[c].grid(True)\n        if num_channels > 1:\n            axes[c].set_ylabel(f\"Channel {c+1}\")\n        if xlim:\n            axes[c].set_xlim(xlim)\n        if ylim:\n            axes[c].set_ylim(ylim)\n    figure.suptitle(title)\n    plt.show(block=False)\n\n\ndef plot_specgram(waveform, sample_rate, title=\"Spectrogram\", xlim=None):\n    waveform = waveform.numpy()\n\n    num_channels, num_frames = waveform.shape\n\n    figure, axes = plt.subplots(num_channels, 1)\n    if num_channels == 1:\n        axes = [axes]\n    for c in range(num_channels):\n        axes[c].specgram(waveform[c], Fs=sample_rate)\n        if num_channels > 1:\n            axes[c].set_ylabel(f\"Channel {c+1}\")\n        if xlim:\n            axes[c].set_xlim(xlim)\n    figure.suptitle(title)\n    plt.show(block=False)\n\n\ndef play_audio(waveform, sample_rate):\n    waveform = waveform.numpy()\n\n    num_channels, num_frames = waveform.shape\n    if num_channels == 1:\n        display(Audio(waveform[0], rate=sample_rate))\n    elif num_channels == 2:\n        display(Audio((waveform[0], waveform[1]), rate=sample_rate))\n    else:\n        raise ValueError(\"Waveform with more than 2 channels are not supported.\")\n\n\ndef _get_sample(path, resample=None):\n    effects = [[\"remix\", \"1\"]]\n    if resample:\n        effects.extend(\n            [\n                [\"lowpass\", f\"{resample // 2}\"],\n                [\"rate\", f\"{resample}\"],\n            ]\n        )\n    return torchaudio.sox_effects.apply_effects_file(path, effects=effects)\n\n\ndef get_sample(*, resample=None):\n    return _get_sample(SAMPLE_WAV_PATH, resample=resample)\n\n\ndef inspect_file(path):\n    print(\"-\" * 10)\n    print(\"Source:\", path)\n    print(\"-\" * 10)\n    print(f\" - File size: {os.path.getsize(path)} bytes\")\n    print(f\" - {torchaudio.info(path)}\")","metadata":{"execution":{"iopub.status.busy":"2022-05-05T07:02:36.942088Z","iopub.execute_input":"2022-05-05T07:02:36.942374Z","iopub.status.idle":"2022-05-05T07:02:36.962786Z","shell.execute_reply.started":"2022-05-05T07:02:36.942333Z","shell.execute_reply":"2022-05-05T07:02:36.961789Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def display_audiodata(df,idx):\n    \n    metadata = torchaudio.info(df.path_filename[idx])\n    print(metadata)\n    # output:\n    #AudioMetaData(sample_rate=32000, num_frames=1504653, num_channels=1, bits_per_sample=0, encoding=VORBIS)\n    waveform, sample_rate=torchaudio.load(df.path_filename[idx])\n    print_stats(waveform, sample_rate=sample_rate)\n    plot_waveform(waveform, sample_rate)\n    plot_specgram(waveform, sample_rate)\n    play_audio(waveform, sample_rate)\n","metadata":{"execution":{"iopub.status.busy":"2022-05-05T07:02:36.965023Z","iopub.execute_input":"2022-05-05T07:02:36.965349Z","iopub.status.idle":"2022-05-05T07:02:36.975096Z","shell.execute_reply.started":"2022-05-05T07:02:36.965286Z","shell.execute_reply":"2022-05-05T07:02:36.974283Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def main():\n    path_trainmeta='../input/birdclef-2022/train_metadata.csv'\n    base_train_dir='../input/birdclef-2022/train_audio'\n    df=display_(path_trainmeta,base_train_dir)\n    \n    for n in range(5):\n        display_audiodata(df,n)\n    \nmain()","metadata":{"execution":{"iopub.status.busy":"2022-05-05T07:02:36.978854Z","iopub.execute_input":"2022-05-05T07:02:36.979121Z","iopub.status.idle":"2022-05-05T07:04:02.923671Z","shell.execute_reply.started":"2022-05-05T07:02:36.979081Z","shell.execute_reply":"2022-05-05T07:04:02.922338Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Input Pipeline","metadata":{}},{"cell_type":"code","source":"class base_pipe(Dataset):\n    \n    def __init__(self,\n                 df,\n                 size = 640,\n                 transform = [\n        MelSpectrogram(n_mels = 128),\n        Resize((128, 128))\n        ]\n    ):\n        super().__init__()\n        self.metadata = df\n        self.size = size\n        self.transform = transform\n        \n    def __len__(self):\n        return len(self.metadata)\n    \n    def __getitem__(self, index):\n        path = self.metadata.loc[index, \"path_filename\"]\n        label = self.metadata.loc[index, \"primary_label\"]\n        mono_audio = self.load_audio(path)\n        mono_audio = mono_audio.unsqueeze(dim=0)\n        return mono_audio, label\n    \n    \n    def load_audio(self, path):\n        audio, _ = torchaudio.load(path)\n        if self.transform != None:\n            for aug in self.transform:\n                audio = aug(audio)\n                \n        return audio[0,:]\n    \nclass pl_pipeline(pl.LightningDataModule):\n    \n    def __init__(\n        self,\n        ds,\n        df,\n        bs,\n    \n    ):\n        super().__init__()\n        self.train_df,self.val_df=train_test_split(df,test_size=0.25,)\n        self.train_df,self.val_df=self.train_df.reset_index(),self.val_df.reset_index()\n        self.ds=ds\n        self.bs=bs\n        \n    def train_dataloader(self):\n        train_ds=self.ds(self.train_df)\n        train_loader=DataLoader(train_ds,batch_size=self.bs)\n        return train_loader\n    \n    def val_dataloader(self):\n        val_ds=self.ds(self.val_df)\n        val_loader=DataLoader(val_ds,batch_size=self.bs)\n        return val_loader\n    \n    \n    ","metadata":{"execution":{"iopub.status.busy":"2022-05-05T07:04:02.927127Z","iopub.execute_input":"2022-05-05T07:04:02.928145Z","iopub.status.idle":"2022-05-05T07:04:03.00644Z","shell.execute_reply.started":"2022-05-05T07:04:02.928098Z","shell.execute_reply":"2022-05-05T07:04:03.005413Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model","metadata":{}},{"cell_type":"code","source":"# Convolution shape updating function\ndef conv_shape(shape, kernel_size, stride, padding):\n    H, W = shape[0], shape[1]\n    H = ((H - kernel_size + 2*padding) // stride) + 1\n    W = ((W - kernel_size + 2*padding) // stride) + 1\n    return H, W\n\nclass Conv(pl.LightningModule):\n    \n    def __init__(self, \n                   in_channels,\n                   out_channels,\n                   kernel_size,\n                   stride=(1,1),\n                   padding=(0,0),\n                   momentum=0.15):\n        super().__init__()\n        self.conv_block = nn.Sequential(\n            nn.BatchNorm2d(in_channels, momentum = momentum),\n            nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding),\n            nn.ReLU()\n        )\n        \n    def forward(self, x):\n        return self.conv_block(x)\n\n\nclass CLEFNetwork(pl.LightningModule):\n    \n    def __init__(self,\n                 num_classes,\n                 in_channels = 1,\n                 H = 128,\n                 W = 128,\n                 num_downs = 3):\n        super().__init__()\n        \n        self.num_C = num_classes\n        self.num_downs = num_downs\n        self.in_channels = in_channels\n        self.C = 8\n        self.H, self.W = self.calc_HW(H, W)\n        self.in_conv_block = Conv(self.in_channels, self.C, 7, (2, 2))\n        self.conv_block = nn.ModuleList(\n                [\n                    Conv(self.C * 2**i, self.C * 2**(i+1), 3, (2, 2))\n                    for i in range(self.num_downs-1)\n                ]\n        )\n        self.fc_block = nn.Sequential(\n                nn.Linear(self.H * self.W * self.C * 2**(self.num_downs - 1), 1024),\n                nn.Linear(1024, 1024),\n                nn.Linear(1024, self.num_C)\n        )\n        \n    def calc_HW(self, H, W):\n        H, W = conv_shape((H, W), 7, 2, 0)\n        for num_down in range(self.num_downs - 1):\n            H, W = conv_shape((H, W), 3, 2, 0)\n        return H, W\n        \n        \n    def forward(self, x):\n        x = self.in_conv_block(x)\n        for block in self.conv_block:\n            x = block(x)\n        x = x.view(x.shape[0], -1)\n        x = self.fc_block(x)\n        return x\n  ","metadata":{"execution":{"iopub.status.busy":"2022-05-05T07:04:03.011728Z","iopub.execute_input":"2022-05-05T07:04:03.014008Z","iopub.status.idle":"2022-05-05T07:04:03.040486Z","shell.execute_reply.started":"2022-05-05T07:04:03.013961Z","shell.execute_reply":"2022-05-05T07:04:03.039596Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def main():\n    class_labels_path = \"./class_dict.json\"\n    seed_everything(config.seed)\n    path_trainmeta='../input/birdclef-2022/train_metadata.csv'\n    base_train_dir='../input/birdclef-2022/train_audio'\n    df=display_(path_trainmeta,base_train_dir)\n    \n    class_labels_path = \"./class_dict.json\"\n    class_labels = json.load(open(class_labels_path, \"r\"))\n    num_classes = len(class_labels.keys())\n    print(\"Number of class : {}\".format(num_classes))\n    \n    model = CLEFNetwork(num_classes)\n    rand_data = torch.rand(5, 1, 128, 128)\n    print(model(rand_data).shape)\n    \n    for name, param in model.named_parameters():\n        print(f\"{name} : {param.shape}, requires_grad : {param.requires_grad}\")\n        \n    torchsummary.summary(model, (1, 128, 128), device = \"cpu\")\n\n    \n    \nmain()","metadata":{"execution":{"iopub.status.busy":"2022-05-05T07:04:03.046035Z","iopub.execute_input":"2022-05-05T07:04:03.046745Z","iopub.status.idle":"2022-05-05T07:04:03.937346Z","shell.execute_reply.started":"2022-05-05T07:04:03.0467Z","shell.execute_reply":"2022-05-05T07:04:03.936464Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Lightning Classifier","metadata":{}},{"cell_type":"code","source":"class clssifier(pl.LightningModule):\n    \n    def __init__(\n        self,\n        model,\n        loss=CrossEntropyLoss(),\n        lr=1e-4\n    ):\n        super().__init__()\n        self.model=model\n        self.loss=loss\n        self.lr=lr\n        \n    def accuracy_func(self,pred, true):\n        pred = torch.argmax(pred, dim = 1)\n        acc = sum(true == pred)\n        return acc\n    \n    def configure_optimizers(self):\n        opt=torch.optim.Adam(self.model.parameters(),lr=self.lr)\n        return opt\n    \n    def training_step(self,batch,batch_idx):\n        patch,label=batch\n        output=self.model(patch)\n        loss = self.loss(output, label)\n        acc = self.accuracy_func(output, label)\n        self.log('train_loss',loss)\n        return loss\n    \n    def validation_step(self,batch,batch_idx):\n        patch,label=batch\n        output=self.model(patch)\n        val_loss = self.loss(output, label)\n        acc = self.accuracy_func(output, label)\n        self.log('val_loss',val_loss)\n        return val_loss","metadata":{"execution":{"iopub.status.busy":"2022-05-05T07:04:03.938881Z","iopub.execute_input":"2022-05-05T07:04:03.939151Z","iopub.status.idle":"2022-05-05T07:04:03.952673Z","shell.execute_reply.started":"2022-05-05T07:04:03.939112Z","shell.execute_reply":"2022-05-05T07:04:03.951691Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Lets get the party started!","metadata":{}},{"cell_type":"code","source":"def main():\n    class_labels_path = \"./class_dict.json\"\n    seed_everything(config.seed)\n    path_trainmeta='../input/birdclef-2022/train_metadata.csv'\n    base_train_dir='../input/birdclef-2022/train_audio'\n    df=display_(path_trainmeta,base_train_dir)\n    \n    data_module=pl_pipeline(base_pipe,df,128)\n    class_labels_path = \"./class_dict.json\"\n    class_labels = json.load(open(class_labels_path, \"r\"))\n    num_classes = len(class_labels.keys())\n    \n    model = CLEFNetwork(num_classes)\n    rand_data = torch.rand(5, 1, 128, 128)\n    pl_classifier=clssifier(model)\n    trainer=pl.Trainer(accelerator='gpu',max_epochs=1)\n    trainer.fit(pl_classifier,data_module)\n\n    \nmain()","metadata":{"execution":{"iopub.status.busy":"2022-05-05T07:12:02.340427Z","iopub.execute_input":"2022-05-05T07:12:02.340727Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Todo \n* Add data augmentation block (GPU compatible pipeline inclusion)\n* Different model arch\n* read disscussion forum\n* other pytorch implementation analysis\n* wandb logging\n","metadata":{}},{"cell_type":"markdown","source":"# Reference\n\nhttps://www.kaggle.com/code/utcarshagrawal/birdclef-audio-pytorch-tutorial\n\nhttps://www.kaggle.com/code/sagnik1511/birdclef-2022-model-building-and-training#Neural-Network-Model\n\n[pytorch audio tutorial](https://pytorch.org/audio/stable/tutorials/audio_io_tutorial.html)","metadata":{}},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}