{"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":"First audio feature extraction practice     \nrefer to\n  \nhttps://www.kaggle.com/code/utcarshagrawal/birdclef-audio-pytorch-tutorial     \n\nnote(korean)    \nhttps://goodfingers.notion.site/BirdCLEF-Audio-PyTorch-Tutorial-5dc97cf9d05042ac8efedccd5ad5d560\n","metadata":{}},{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\n# import os\n# for dirname, _, filenames in os.walk('/kaggle/input'):\n#     for filename in filenames:\n#         print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-05-09T13:32:35.481137Z","iopub.execute_input":"2022-05-09T13:32:35.481499Z","iopub.status.idle":"2022-05-09T13:32:35.508194Z","shell.execute_reply.started":"2022-05-09T13:32:35.481413Z","shell.execute_reply":"2022-05-09T13:32:35.507505Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Import","metadata":{}},{"cell_type":"code","source":"import os\nimport gc\nimport ast\nimport random\nimport numpy as np \nimport pandas as pd \nimport matplotlib.pyplot as plt\n%matplotlib inline\nimport seaborn as sns\nfrom tqdm import tqdm\nimport torchaudio\nimport IPython.display as ipd\nfrom collections import Counter\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.metrics import f1_score\n\nimport torch\nimport torch.nn as nn\nfrom torch.optim import Adam\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import models\nfrom typing import List\n\nimport warnings\nwarnings.filterwarnings('ignore')","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:32:35.773837Z","iopub.execute_input":"2022-05-09T13:32:35.774761Z","iopub.status.idle":"2022-05-09T13:32:38.567456Z","shell.execute_reply.started":"2022-05-09T13:32:35.774699Z","shell.execute_reply":"2022-05-09T13:32:38.566448Z"},"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":{"iopub.status.busy":"2022-05-09T13:32:38.569496Z","iopub.execute_input":"2022-05-09T13:32:38.570009Z","iopub.status.idle":"2022-05-09T13:32:38.576107Z","shell.execute_reply.started":"2022-05-09T13:32:38.569917Z","shell.execute_reply":"2022-05-09T13:32:38.575428Z"},"trusted":true},"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\nseed_everything(config.seed)","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:32:38.577328Z","iopub.execute_input":"2022-05-09T13:32:38.577602Z","iopub.status.idle":"2022-05-09T13:32:38.592212Z","shell.execute_reply.started":"2022-05-09T13:32:38.577551Z","shell.execute_reply":"2022-05-09T13:32:38.591481Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ROOTDIR = \"../input/birdclef-2022\"","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:32:38.593932Z","iopub.execute_input":"2022-05-09T13:32:38.594289Z","iopub.status.idle":"2022-05-09T13:32:38.606524Z","shell.execute_reply.started":"2022-05-09T13:32:38.594252Z","shell.execute_reply":"2022-05-09T13:32:38.605827Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = pd.read_csv(f'{ROOTDIR}/train_metadata.csv')\ntrain_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:32:38.607818Z","iopub.execute_input":"2022-05-09T13:32:38.608148Z","iopub.status.idle":"2022-05-09T13:32:38.756567Z","shell.execute_reply.started":"2022-05-09T13:32:38.60812Z","shell.execute_reply":"2022-05-09T13:32:38.755906Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.info()","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:32:38.757655Z","iopub.execute_input":"2022-05-09T13:32:38.758226Z","iopub.status.idle":"2022-05-09T13:32:38.798481Z","shell.execute_reply.started":"2022-05-09T13:32:38.758194Z","shell.execute_reply":"2022-05-09T13:32:38.797782Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.describe()","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:32:38.799609Z","iopub.execute_input":"2022-05-09T13:32:38.799952Z","iopub.status.idle":"2022-05-09T13:32:38.826305Z","shell.execute_reply.started":"2022-05-09T13:32:38.799923Z","shell.execute_reply":"2022-05-09T13:32:38.82558Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Quick EDA","metadata":{}},{"cell_type":"markdown","source":"### Checking data distribution","metadata":{}},{"cell_type":"markdown","source":"#### make class for visualization","metadata":{}},{"cell_type":"code","source":"class Dataviz():\n    def __init__(self, root_dir):\n        self.root_dir = root_dir\n        self.meta = pd.read_csv(fr\"{self.root_dir}/train_metadata.csv\")\n        self.meta['type'] = self.meta['type'].apply(lambda x : ast.literal_eval(x))\n    \n    \n    def feature_distribution(self, feature: str):\n        plt.figure(figsize=(20, 6))\n\n        sns.countplot(self.meta[feature])\n        plt.xticks(rotation=90)\n        plt.title(f\"Distribution of {feature} Labels\", fontsize=20)\n\n        plt.show()\n        \n    \n    def most_common(self, feature: str, k: int):\n        \n        if isinstance(self.meta[feature].iloc[0], list):\n    \n            top = Counter([typ.lower() for lst in self.meta[feature] \n                                           for typ in lst])\n\n            top = dict(top.most_common(k))\n\n            plt.figure(figsize=(20, 6))\n\n            sns.barplot(x=list(top.keys()), y=list(top.values()), palette='hls')\n            plt.title(f\"Top {k} song {feature}\")\n\n            \n        else:\n            top = Counter([f.lower() for f in self.meta[feature]])\n\n            top = dict(top.most_common(k))\n\n            plt.figure(figsize=(20, 6))\n\n            sns.barplot(x=list(top.keys()), y=list(top.values()), palette='hls')\n            plt.title(f\"Top {k} song {feature}\")\n\n        plt.show()\n            ","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:32:38.827661Z","iopub.execute_input":"2022-05-09T13:32:38.827868Z","iopub.status.idle":"2022-05-09T13:32:38.843335Z","shell.execute_reply.started":"2022-05-09T13:32:38.827843Z","shell.execute_reply":"2022-05-09T13:32:38.842318Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_viz = Dataviz('../input/birdclef-2022/')","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:32:38.844491Z","iopub.execute_input":"2022-05-09T13:32:38.845076Z","iopub.status.idle":"2022-05-09T13:32:39.078918Z","shell.execute_reply.started":"2022-05-09T13:32:38.845046Z","shell.execute_reply":"2022-05-09T13:32:39.077961Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_viz.feature_distribution(\"primary_label\")","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:32:39.081971Z","iopub.execute_input":"2022-05-09T13:32:39.082319Z","iopub.status.idle":"2022-05-09T13:32:40.908044Z","shell.execute_reply.started":"2022-05-09T13:32:39.082273Z","shell.execute_reply":"2022-05-09T13:32:40.907161Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_viz.feature_distribution(\"rating\")","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:32:40.90955Z","iopub.execute_input":"2022-05-09T13:32:40.90988Z","iopub.status.idle":"2022-05-09T13:32:41.155635Z","shell.execute_reply.started":"2022-05-09T13:32:40.909839Z","shell.execute_reply":"2022-05-09T13:32:41.154759Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_viz.most_common(\"type\", 10)","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:32:41.15685Z","iopub.execute_input":"2022-05-09T13:32:41.157095Z","iopub.status.idle":"2022-05-09T13:32:41.408373Z","shell.execute_reply.started":"2022-05-09T13:32:41.157065Z","shell.execute_reply":"2022-05-09T13:32:41.407506Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Analysis audio files","metadata":{}},{"cell_type":"markdown","source":"## Play a few samples","metadata":{}},{"cell_type":"code","source":"filename_1 = train_df[\"filename\"].values[0] # first training example\nipd.Audio(f\"../input/birdclef-2022/train_audio/{filename_1}\")","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:32:41.41Z","iopub.execute_input":"2022-05-09T13:32:41.410419Z","iopub.status.idle":"2022-05-09T13:32:41.433117Z","shell.execute_reply.started":"2022-05-09T13:32:41.410373Z","shell.execute_reply":"2022-05-09T13:32:41.432313Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"filename_2 = train_df[\"filename\"].values[-1] # last training example\nipd.Audio(f\"../input/birdclef-2022/train_audio/{filename_2}\")","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:32:41.434475Z","iopub.execute_input":"2022-05-09T13:32:41.435162Z","iopub.status.idle":"2022-05-09T13:32:41.483937Z","shell.execute_reply.started":"2022-05-09T13:32:41.43512Z","shell.execute_reply":"2022-05-09T13:32:41.483168Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### waveform","metadata":{}},{"cell_type":"markdown","source":"#### add a method for visualizing waveform to DataViz class ","metadata":{"execution":{"iopub.status.busy":"2022-05-09T05:11:39.015615Z","iopub.execute_input":"2022-05-09T05:11:39.01594Z","iopub.status.idle":"2022-05-09T05:11:39.019828Z","shell.execute_reply.started":"2022-05-09T05:11:39.015905Z","shell.execute_reply":"2022-05-09T05:11:39.019048Z"}}},{"cell_type":"code","source":"class Dataviz():\n    def __init__(self, root_dir):\n        self.root_dir = root_dir\n        self.meta = pd.read_csv(fr\"{self.root_dir}/train_metadata.csv\")\n        self.meta['type'] = self.meta['type'].apply(lambda x : ast.literal_eval(x))\n    \n    \n    def feature_distribution(self, feature: str):\n        plt.figure(figsize=(20, 6))\n\n        sns.countplot(self.meta[feature])\n        plt.xticks(rotation=90)\n        plt.title(f\"Distribution of {feature} Labels\", fontsize=20)\n\n        plt.show()\n        \n    \n    def most_common(self, feature: str, k: int):\n        \n        if isinstance(self.meta[feature].iloc[0], list):\n    \n            top = Counter([typ.lower() for lst in self.meta[feature] \n                                           for typ in lst])\n\n            top = dict(top.most_common(k))\n\n            plt.figure(figsize=(20, 6))\n\n            sns.barplot(x=list(top.keys()), y=list(top.values()), palette='hls')\n            plt.title(f\"Top {k} song {feature}\")\n            \n        else:\n            top = Counter([f.lower() for f in self.meta[feature]])\n\n            top = dict(top.most_common(k))\n\n            plt.figure(figsize=(20, 6))\n\n            sns.barplot(x=list(top.keys()), y=list(top.values()), palette='hls')\n            plt.title(f\"Top {k} song {feature}\")\n\n        plt.show()\n            \n    \n    \n    def waveform(self, indices: List[str]):\n        if isinstance(indices, int):\n            indices = [indices]\n        n = len(indices)\n                                  \n        if n == 1:    \n            idx = indices[0]\n            fig, ax = plt.subplots(1, 1, figsize=(20, 5))\n            fig.suptitle(\"Sound Waves\", fontsize=15)\n            \n            signal_1, sample_rate = torchaudio.load(f\"{self.root_dir}/train_audio/{self.meta.filename.iloc[idx]}\")\n            # The audio data consist of two things-\n            # Sound: sequence of vibrations in varying pressure strengths (y)\n            # Sample Rate: (sample_rate) is the number of samples of audio carried per second, measured in Hz or kHz\n\n            sns.lineplot(x=np.arange(len(signal_1[0,:].detach().numpy())), y=signal_1[0,:].detach().numpy(), ax=ax, color='#4400FF')\n            ax.set_title(f\"Audio {idx}\")\n        \n        elif n > 1:\n            fig, ax = plt.subplots(n, 1, figsize=(20, 5 * n))\n            fig.suptitle(\"Sound Waves\", fontsize=15)\n            \n            for i, idx in enumerate(indices):\n\n                signal, sample_rate = torchaudio.load(f\"{self.root_dir}/train_audio/{self.meta.filename.iloc[idx]}\")\n                # The audio data consist of two things-\n                # Sound: sequence of vibrations in varying pressure strengths (y)\n                # Sample Rate: (sample_rate) is the number of samples of audio carried per second, measured in Hz or kHz\n\n                sns.lineplot(x=np.arange(len(signal[0,:].detach().numpy())), y=signal[0,:].detach().numpy(), ax=ax[i], color='#4400FF')\n                ax[i].set_title(f\"Audio {idx}\")\n\n        else:\n            print(\"n should be more than or equal to 1\")\n            assert n >= 1\n            \n        plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:32:41.485362Z","iopub.execute_input":"2022-05-09T13:32:41.485843Z","iopub.status.idle":"2022-05-09T13:32:41.511237Z","shell.execute_reply.started":"2022-05-09T13:32:41.485801Z","shell.execute_reply":"2022-05-09T13:32:41.510542Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_viz = Dataviz('../input/birdclef-2022/')","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:32:41.512172Z","iopub.execute_input":"2022-05-09T13:32:41.51282Z","iopub.status.idle":"2022-05-09T13:32:41.9263Z","shell.execute_reply.started":"2022-05-09T13:32:41.512785Z","shell.execute_reply":"2022-05-09T13:32:41.925504Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_viz.waveform([0, -1])","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:32:41.928845Z","iopub.execute_input":"2022-05-09T13:32:41.929098Z","iopub.status.idle":"2022-05-09T13:33:36.683418Z","shell.execute_reply.started":"2022-05-09T13:32:41.92907Z","shell.execute_reply":"2022-05-09T13:33:36.682604Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Dataset preprocessing","metadata":{}},{"cell_type":"markdown","source":"## Convert target which is in string format to integer using LabelEncoder","metadata":{}},{"cell_type":"code","source":"encoder = LabelEncoder()\ntrain_df['primary_label_encoded'] = encoder.fit_transform(train_df['primary_label'])","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:33:36.684497Z","iopub.execute_input":"2022-05-09T13:33:36.68473Z","iopub.status.idle":"2022-05-09T13:33:36.698844Z","shell.execute_reply.started":"2022-05-09T13:33:36.684703Z","shell.execute_reply":"2022-05-09T13:33:36.697256Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"skf = StratifiedKFold(n_splits=config.num_fold)\nfor k, (_, val_ind) in enumerate(skf.split(X=train_df, y=train_df['primary_label_encoded'])):\n    train_df.loc[val_ind, 'fold'] = k","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:33:36.701711Z","iopub.execute_input":"2022-05-09T13:33:36.702631Z","iopub.status.idle":"2022-05-09T13:33:36.720725Z","shell.execute_reply.started":"2022-05-09T13:33:36.702543Z","shell.execute_reply":"2022-05-09T13:33:36.719726Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## feature extraction","metadata":{}},{"cell_type":"code","source":"class Dataviz():\n    def __init__(self, sample_rate, n_fft, hop_length, n_mels):\n        self.root_dir = ROOTDIR\n        self.meta = pd.read_csv(fr\"{self.root_dir}/train_metadata.csv\")\n        self.meta['type'] = self.meta['type'].apply(lambda x : ast.literal_eval(x))\n        self.sample_rate = sample_rate\n        self.n_fft = n_fft\n        self.hop_length = hop_length\n        self.n_mels = n_mels\n        \n        self.mel_converter = torchaudio.transforms.MelSpectrogram(sample_rate=self.sample_rate, \n                                                              n_fft=self.n_fft, \n                                                              hop_length=self.hop_length, \n                                                              n_mels=self.n_mels)\n    \n    \n    def feature_distribution(self, feature: str):\n        plt.figure(figsize=(20, 6))\n\n        sns.countplot(self.meta[feature])\n        plt.xticks(rotation=90)\n        plt.title(f\"Distribution of {feature} Labels\", fontsize=20)\n\n        plt.show()\n        \n    \n    def most_common(self, feature: str, k: int):\n        \n        if isinstance(self.meta[feature].iloc[0], list):\n    \n            top = Counter([typ.lower() for lst in self.meta[feature] \n                                           for typ in lst])\n\n            top = dict(top.most_common(k))\n\n            plt.figure(figsize=(20, 6))\n\n            sns.barplot(x=list(top.keys()), y=list(top.values()), palette='hls')\n            plt.title(f\"Top {k} song {feature}\")\n            \n        else:\n            top = Counter([f.lower() for f in self.meta[feature]])\n\n            top = dict(top.most_common(k))\n\n            plt.figure(figsize=(20, 6))\n\n            sns.barplot(x=list(top.keys()), y=list(top.values()), palette='hls')\n            plt.title(f\"Top {k} song {feature}\")\n\n        plt.show()\n            \n    \n    def waveform(self, indices: List[str]):\n        if isinstance(indices, int):\n            indices = [indices]\n        n = len(indices)\n                                  \n        if n == 1:    \n            idx = indices[0]\n            fig, ax = plt.subplots(1, 1, figsize=(20, 5))\n            fig.suptitle(\"Sound Waves\", fontsize=15)\n            \n            signal_1, sample_rate = torchaudio.load(f\"{self.root_dir}/train_audio/{self.meta.filename.iloc[idx]}\")\n            # The audio data consist of two things-\n            # Sound: sequence of vibrations in varying pressure strengths (y)\n            # Sample Rate: (sample_rate) is the number of samples of audio carried per second, measured in Hz or kHz\n\n            sns.lineplot(x=np.arange(len(signal_1[0,:].detach().numpy())), y=signal_1[0,:].detach().numpy(), ax=ax, color='#4400FF')\n            ax.set_title(f\"Audio {idx}\")\n        \n        elif n > 1:\n            fig, ax = plt.subplots(n, 1, figsize=(20, 5 * n))\n            fig.suptitle(\"Sound Waves\", fontsize=15)\n            \n            for i, idx in enumerate(indices):\n\n                signal, sample_rate = torchaudio.load(f\"{self.root_dir}/train_audio/{self.meta.filename.iloc[idx]}\")\n                # The audio data consist of two things-\n                # Sound: sequence of vibrations in varying pressure strengths (y)\n                # Sample Rate: (sample_rate) is the number of samples of audio carried per second, measured in Hz or kHz\n\n                sns.lineplot(x=np.arange(len(signal[0,:].detach().numpy())), y=signal[0,:].detach().numpy(), ax=ax[i], color='#4400FF')\n                ax[i].set_title(f\"Audio {idx}\")\n\n        else:\n            print(\"n should be more than or equal to 1\")\n            assert n >= 1\n            \n        plt.show()\n        \n    \n    def mel_spectrogram(self, indices: List[str]):\n        \n        if isinstance(indices, int):\n            indices = [indices]\n            \n        n = len(indices)\n                                  \n        if n == 1:    \n            idx = indices[0]\n            fig, ax = plt.subplots(1, 1, figsize=(10, 7))\n            fig.suptitle(\"Mel Spectrogram\", fontsize=15)\n            \n            signal, sample_rate = torchaudio.load(f\"{self.root_dir}/train_audio/{self.meta.filename.iloc[idx]}\")\n            # The audio data consist of two things-\n            # Sound: sequence of vibrations in varying pressure strengths (y)\n            # Sample Rate: (sample_rate) is the number of samples of audio carried per second, measured in Hz or kHz\n            \n            mel = self.mel_converter(signal)\n\n            ax.imshow(mel.log2()[0,:,:].detach().numpy(), aspect='auto', cmap='cool')\n            ax.set_title(f\"Audio {idx}\")\n                    \n        elif n > 1:\n            fig, ax = plt.subplots(n, 1, figsize=(10,  7 * n))\n            fig.suptitle(\"Mel Spectrogram\", fontsize=15)\n            \n            for i, idx in enumerate(indices):\n\n                signal, sample_rate = torchaudio.load(f\"{self.root_dir}/train_audio/{self.meta.filename.iloc[idx]}\")\n                # The audio data consist of two things-\n                # Sound: sequence of vibrations in varying pressure strengths (y)\n                # Sample Rate: (sample_rate) is the number of samples of audio carried per second, measured in Hz or kHz\n\n                mel = self.mel_converter(signal)\n                ax[i].imshow(mel.log2()[0,:,:].detach().numpy(), aspect='auto', cmap='cool')\n                ax[i].set_title(f\"Audio {idx}\")\n\n        else:\n            print(\"n should be more than or equal to 1\")\n            assert n >= 1\n            \n        plt.show()\n            ","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:33:36.722399Z","iopub.execute_input":"2022-05-09T13:33:36.722643Z","iopub.status.idle":"2022-05-09T13:33:36.7602Z","shell.execute_reply.started":"2022-05-09T13:33:36.722606Z","shell.execute_reply":"2022-05-09T13:33:36.759434Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_viz = Dataviz(sample_rate=config.sample_rate, n_fft=config.n_fft, hop_length=config.hop_length, n_mels=config.n_mels)\ntrain_viz.mel_spectrogram([0, -1])","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:33:36.761743Z","iopub.execute_input":"2022-05-09T13:33:36.762064Z","iopub.status.idle":"2022-05-09T13:33:37.774011Z","shell.execute_reply.started":"2022-05-09T13:33:36.762023Z","shell.execute_reply":"2022-05-09T13:33:37.773401Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"- all data need to have same sample rate => resample the data so that all the data have same sample rate     \n- if we talk about the sound => the dimension of sound is (n_channels, n_samples)      \n  if we talk about the channels => each audio can have different channel, so we will ensure that they are mono(n_channels == 1)     \n- each audio signal have different time durations which lead to difference in number of samples.    \n  => ensure same number of samples by applying padding if it less than the desired samples or by truncating if it is more than the desired samples","metadata":{}},{"cell_type":"code","source":"class BirdClefDataset(Dataset, Dataviz):\n    \n    def __init__(self, root_dir, sample_rate, n_fft, hop_length, n_mels, duration):\n        self.root_dir = ROOTDIR\n        self.meta = pd.read_csv(fr\"{self.root_dir}/train_metadata.csv\")\n        \n        self.meta['type'] = self.meta['type'].apply(lambda x : ast.literal_eval(x))\n        encoder = LabelEncoder()\n        self.meta['primary_label_encoded'] = encoder.fit_transform(self.meta['primary_label'])\n        \n        self.audio_paths = self.root_dir + \"/\" + \"train_audio\" + \"/\" + self.meta.filename.values\n        self.labels = self.meta.primary_label_encoded.values\n        \n        self.sample_rate = sample_rate\n        self.n_fft = n_fft\n        self.hop_length = hop_length\n        self.n_mels = n_mels\n        self.num_samples = sample_rate * duration\n        \n        self.mel_converter = torchaudio.transforms.MelSpectrogram(sample_rate=self.sample_rate, \n                                                              n_fft=self.n_fft, \n                                                              hop_length=self.hop_length, \n                                                              n_mels=self.n_mels)\n        \n    def __len__(self):\n        return len(self.audio_paths)\n    \n    \n    def __getitem__(self, idx):\n        audio_path = self.audio_paths[idx]\n        signal, sample_rate = torchaudio.load(audio_path) # loaded the audio\n        \n        # Now we first checked if the sample rate is same as TARGET_SAMPLE_RATE and if it not equal we perform resampling\n        if sample_rate != self.sample_rate:\n            resampler = torchaudio.transforms.Resample(sample_rate, self.sample_rate)\n            signal = resampler(signal)\n        \n        # Next we check the number of channels of the signal\n        #signal -> (num_channels, num_samples) - Eg.-(2, 14000) -> (1, 14000)\n        if signal.shape[0]>1:\n            signal = torch.mean(signal, axis=0, keepdim=True)\n        \n        # Lastly we check the number of samples of the signal\n        #signal -> (num_channels, num_samples) - Eg.-(1, 14000) -> (1, self.num_samples)\n        # If it is more than the required number of samples, we truncate the signal\n        if signal.shape[1] > self.num_samples:\n            signal = signal[:, :self.num_samples]\n        \n        # If it is less than the required number of samples, we pad the signal\n        if signal.shape[1] < self.num_samples:\n            num_missing_samples = self.num_samples - signal.shape[1]\n            last_dim_padding = (0, num_missing_samples)\n            signal = F.pad(signal, last_dim_padding)\n        \n        # Finally all the process has been done and now we will extract mel spectrogram from the signal\n        mel = self.mel_converter(signal)\n        \n        # For pretrained models, we need 3 channel image, so for that we concatenate the extracted mel\n        image = torch.cat([mel, mel, mel])\n        \n        # Normalized the image\n        max_val = torch.abs(image).max()\n        image = image / max_val\n        \n        label = torch.tensor(self.labels[idx])\n        \n        return image, label","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:33:37.775024Z","iopub.execute_input":"2022-05-09T13:33:37.775685Z","iopub.status.idle":"2022-05-09T13:33:37.795801Z","shell.execute_reply.started":"2022-05-09T13:33:37.775645Z","shell.execute_reply":"2022-05-09T13:33:37.794919Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Function to get data according to the folds\ndef get_data(df, fold):\n    train_df = df[df['fold'] != fold].reset_index(drop=True)\n    valid_df = df[df['fold'] == fold].reset_index(drop=True)\n    train_dataset = BirdClefDataset(train_df, sample_rate=config.sample_rate, n_fft=config.n_fft, hop_length=config.hop_length, n_mels=config.n_mels, duration=config.duration)\n    valid_dataset = BirdClefDataset(valid_df, sample_rate=config.sample_rate, n_fft=config.n_fft, hop_length=config.hop_length, n_mels=config.n_mels, duration=config.duration)\n    \n    train_loader = DataLoader(train_dataset, batch_size=config.train_batch_size, shuffle=True)\n    valid_loader = DataLoader(valid_dataset, batch_size=config.valid_batch_size, shuffle=False)\n    \n    return train_loader, valid_loader","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:33:37.796853Z","iopub.execute_input":"2022-05-09T13:33:37.797065Z","iopub.status.idle":"2022-05-09T13:33:37.812636Z","shell.execute_reply.started":"2022-05-09T13:33:37.79704Z","shell.execute_reply":"2022-05-09T13:33:37.811906Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model","metadata":{}},{"cell_type":"markdown","source":"## Simple cnn","metadata":{}},{"cell_type":"code","source":"class BirdClefModel(nn.Module):\n    def __init__(self):\n        super(BirdClefModel, self).__init__()\n        self.conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, stride=1, padding=1)\n        self.conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, stride=1, padding=1)\n        self.conv3 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1)\n        self.pool = nn.MaxPool2d(kernel_size=2, stride=2)\n        self.fc1 = nn.Linear(128*8*54, 64)\n        self.fc2 = nn.Linear(64, config.num_classes)\n        \n    def forward(self, x):\n        x = F.relu(self.conv1(x))\n        x = self.pool(x)\n        x = F.relu(self.conv2(x))\n        x = self.pool(x)\n        x = F.relu(self.conv3(x))\n        x = self.pool(x)\n        x = x.view(x.size(0), -1)\n        x = F.relu(self.fc1(x))\n        x = self.fc2(x)\n        \n        return x","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:33:37.813796Z","iopub.execute_input":"2022-05-09T13:33:37.814449Z","iopub.status.idle":"2022-05-09T13:33:37.825679Z","shell.execute_reply.started":"2022-05-09T13:33:37.814412Z","shell.execute_reply":"2022-05-09T13:33:37.824965Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## pretrained resnet","metadata":{}},{"cell_type":"code","source":"class BirdCLEFResnet(nn.Module):\n    def __init__(self):\n        super(BirdCLEFResnet, self).__init__()\n        self.base_model = models.__getattribute__(config.model_name)(pretrained=True)\n        for param in self.base_model.parameters():\n            param.requires_grad = False\n            \n        in_features = self.base_model.fc.in_features\n        \n        self.base_model.fc = nn.Sequential(\n            nn.Linear(in_features, 1024), \n            nn.ReLU(), \n            nn.Dropout(p=0.2),\n            nn.Linear(1024, 512), \n            nn.ReLU(), \n            nn.Dropout(p=0.2),\n            nn.Linear(512, config.num_classes))\n        \n    def forward(self, x):\n        x = self.base_model(x)\n        return x","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:33:37.826784Z","iopub.execute_input":"2022-05-09T13:33:37.827395Z","iopub.status.idle":"2022-05-09T13:33:37.849414Z","shell.execute_reply.started":"2022-05-09T13:33:37.827363Z","shell.execute_reply":"2022-05-09T13:33:37.848621Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Utility Functions","metadata":{}},{"cell_type":"code","source":"def loss_fn(outputs, labels):\n    return nn.CrossEntropyLoss()(outputs, labels)\n\ndef train(model, data_loader, optimizer, scheduler, device, epoch):\n    model.train()\n    \n    running_loss = 0\n    loop = tqdm(data_loader, position=0)\n    for i, (mels, labels) in enumerate(loop):\n        mels = mels.to(device)\n        labels = labels.to(device)\n        \n        outputs = model(mels)\n        _, preds = torch.max(outputs, 1)\n        \n        loss = loss_fn(outputs, labels)\n        \n        loss.backward()\n        optimizer.step()\n        optimizer.zero_grad()\n        \n        if scheduler is not None:\n            scheduler.step()\n            \n        running_loss += loss.item()\n        \n        loop.set_description(f\"Epoch [{epoch+1}/{config.epochs}]\")\n        loop.set_postfix(loss=loss.item())\n\n    return running_loss/len(data_loader)","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:33:37.850809Z","iopub.execute_input":"2022-05-09T13:33:37.851077Z","iopub.status.idle":"2022-05-09T13:33:37.860817Z","shell.execute_reply.started":"2022-05-09T13:33:37.851046Z","shell.execute_reply":"2022-05-09T13:33:37.860062Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def valid(model, data_loader, device, epoch):\n    model.eval()\n    \n    running_loss = 0\n    pred = []\n    label = []\n    \n    loop = tqdm(data_loader, position=0)\n    for mels, labels in loop:\n        mels = mels.to(device)\n        labels = labels.to(device)\n        \n        outputs = model(mels)\n        _, preds = torch.max(outputs, 1)\n        \n        loss = loss_fn(outputs, labels)\n            \n        running_loss += loss.item()\n        \n        pred.extend(preds.view(-1).cpu().detach().numpy())\n        label.extend(labels.view(-1).cpu().detach().numpy())\n        \n        loop.set_description(f\"Epoch [{epoch+1}/{config.epochs}]\")\n        loop.set_postfix(loss=loss.item())\n        \n    valid_f1 = f1_score(label, pred, average='macro')\n    \n    return running_loss/len(data_loader), valid_f1","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:33:37.862076Z","iopub.execute_input":"2022-05-09T13:33:37.862313Z","iopub.status.idle":"2022-05-09T13:33:37.877143Z","shell.execute_reply.started":"2022-05-09T13:33:37.862276Z","shell.execute_reply":"2022-05-09T13:33:37.876419Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def run(fold):\n    train_loader, valid_loader = get_data(train_df, fold)\n    \n    #model = BirdClefModel().to(config.device) # check version 3 for this\n    model = BirdCLEFResnet().to(config.device)\n    \n    optimizer = Adam(model.parameters(), lr=config.learning_rate)\n    \n    scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, eta_min=1e-5, T_max=10)\n    \n    best_valid_f1 = 0\n    for epoch in range(config.epochs):\n        train_loss = train(model, train_loader, optimizer, scheduler, config.device, epoch)\n        valid_loss, valid_f1 = valid(model, valid_loader, config.device, epoch)\n        if valid_f1 > best_valid_f1:\n            print(f\"Validation F1 Improved - {best_valid_f1} ---> {valid_f1}\")\n            torch.save(model.state_dict(), f'./model_{fold}.bin')\n            print(f\"Saved model checkpoint at ./model_{fold}.bin\")\n            best_valid_f1 = valid_f1\n            \n    return best_valid_f1","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:33:37.878293Z","iopub.execute_input":"2022-05-09T13:33:37.878883Z","iopub.status.idle":"2022-05-09T13:33:37.888917Z","shell.execute_reply.started":"2022-05-09T13:33:37.878851Z","shell.execute_reply":"2022-05-09T13:33:37.888248Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for fold in range(config.num_fold):\n    print(\"=\" * 30)\n    print(\"Training Fold - \", fold)\n    print(\"=\" * 30)\n    best_valid_f1 = run(fold)\n    print(f'Best F1 Score: {best_valid_f1:.5f}')\n    \n    gc.collect()\n    torch.cuda.empty_cache()    \n    break # To run for all the folds, just remove this break ","metadata":{"execution":{"iopub.status.busy":"2022-05-09T13:33:37.889992Z","iopub.execute_input":"2022-05-09T13:33:37.890699Z","iopub.status.idle":"2022-05-09T16:40:10.005755Z","shell.execute_reply.started":"2022-05-09T13:33:37.890637Z","shell.execute_reply":"2022-05-09T16:40:10.002799Z"},"trusted":true},"execution_count":null,"outputs":[]}]}