{"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":"# AML Challenge 2 Baseline Solution\n\nThis notebook is almost based on the Github [respository](https://github.com/daisukelab/dcase2020_task2_variants). \n\nWe replicate the (Variational Auto-Encoder) VAE method for this AML challenge 2 which is a small scale of the official DCASE challenge [task 2](https://dcase.community/challenge2020/task-unsupervised-detection-of-anomalous-sounds). ","metadata":{}},{"cell_type":"markdown","source":"# Required Python Packages\n\nLet's install some python packages used in this notebook.","metadata":{}},{"cell_type":"code","source":"!pip install torchsummary\n!pip install dl-cliche","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:14:29.162691Z","iopub.execute_input":"2022-05-25T17:14:29.16308Z","iopub.status.idle":"2022-05-25T17:14:51.14739Z","shell.execute_reply.started":"2022-05-25T17:14:29.162988Z","shell.execute_reply":"2022-05-25T17:14:51.146517Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from shutil import copyfile\n#copyfile(src = \"../input/model123/model.py\", dst = \"../working/model.py\")\n\n# copy our file into the working directory (make sure it has .py suffix)\ncopyfile(src = \"../input/dcase2020-task2-baseline/pytorch_common.py\", dst = \"../working/pytorch_common.py\")\ncopyfile(src = \"../input/dcase2020-task2-baseline/common.py\", dst = \"../working/common.py\")\n\n# import all our functions\nimport common as com\nimport pytorch_common\nfrom pytorch_common import *\n\n# public modules\nimport torch\nimport torch.nn.functional as F\nimport torchsummary\nimport pytorch_lightning as pl\nimport random\nimport common as com\nimport librosa\nimport os\nimport glob\nimport csv\nimport re\nimport itertools\nimport sys\nimport path\nimport numpy\nimport seaborn as sns\nfrom torch import nn\nfrom tqdm import tqdm\nfrom sklearn import metrics\nfrom dlcliche.utils import *\nfrom dlcliche.notebook import *\nfrom dlcliche.utils import (\n    sys, random, Path, np, plt, EasyDict,\n    ensure_folder, deterministic_everything,\n)\n","metadata":{"scrolled":true,"execution":{"iopub.status.busy":"2022-05-25T17:14:51.150443Z","iopub.execute_input":"2022-05-25T17:14:51.1507Z","iopub.status.idle":"2022-05-25T17:15:02.010305Z","shell.execute_reply.started":"2022-05-25T17:14:51.150673Z","shell.execute_reply":"2022-05-25T17:15:02.009529Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Exploration\n\nHow many samples included?\n\nHow are the raw wave files?\n\nHow are the log mel spectrogram of the wave files?","metadata":{}},{"cell_type":"code","source":"# development dataset\nDATA_ROOT=Path(\"../input/eurecom-aml-2022-challenge-2/dev_data/dev_data\")\ntypes = [t.name for t in sorted(DATA_ROOT.glob('*')) if t.is_dir()]\nprint('Machine types:', types)\n\ndf = pd.DataFrame()\ndf['file'] = sorted(DATA_ROOT.glob('*/*/*.wav'))\ndf['type'] = df.file.map(lambda f: f.parent.parent.name)\ndf['split'] = df.file.map(lambda f: f.parent.name)\ndf['id'] = df.file.map(lambda s: str(s).split('/')[-1].split('_')[-2])\n\nagg = df.groupby(['id', 'split']).agg('count')\nfig = plt.figure(figsize=(12.0, 6.0))\ng = sns.barplot(x=\"id\", y=\"file\", hue=\"split\", data=agg.reset_index())\nplt.xlabel(\"machine id\")\nplt.ylabel(\"number of files\")\nplt.show()\nagg.transpose()","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:15:02.011763Z","iopub.execute_input":"2022-05-25T17:15:02.012054Z","iopub.status.idle":"2022-05-25T17:15:02.742106Z","shell.execute_reply.started":"2022-05-25T17:15:02.012019Z","shell.execute_reply":"2022-05-25T17:15:02.74145Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df.shape","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:15:02.744052Z","iopub.execute_input":"2022-05-25T17:15:02.744473Z","iopub.status.idle":"2022-05-25T17:15:02.817164Z","shell.execute_reply.started":"2022-05-25T17:15:02.744432Z","shell.execute_reply":"2022-05-25T17:15:02.816343Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df.head()","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:15:02.818489Z","iopub.execute_input":"2022-05-25T17:15:02.818913Z","iopub.status.idle":"2022-05-25T17:15:02.896437Z","shell.execute_reply.started":"2022-05-25T17:15:02.818858Z","shell.execute_reply":"2022-05-25T17:15:02.895558Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train=df[df.split=='train']\ndf_test=df[df.split=='test']","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:15:02.898018Z","iopub.execute_input":"2022-05-25T17:15:02.898315Z","iopub.status.idle":"2022-05-25T17:15:02.971471Z","shell.execute_reply.started":"2022-05-25T17:15:02.898267Z","shell.execute_reply":"2022-05-25T17:15:02.970507Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"agg = df[['file', 'type', 'split']].groupby(['type', 'split']).agg('count')\nfig = plt.figure(figsize=(12.0, 6.0))\ng = sns.barplot(x=\"type\", y=\"file\", hue=\"split\", data=agg.reset_index())\nplt.ylabel(\"machine type\")\nplt.ylabel(\"number of files\")\nplt.show()\nagg.transpose()","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:15:02.973049Z","iopub.execute_input":"2022-05-25T17:15:02.973351Z","iopub.status.idle":"2022-05-25T17:15:03.230397Z","shell.execute_reply.started":"2022-05-25T17:15:02.973313Z","shell.execute_reply":"2022-05-25T17:15:03.229703Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# evaluation dataset\nDATA_ROOT=Path(\"../input/eurecom-aml-2022-challenge-2/eval_data/eval_data\")\ntypes = [t.name for t in sorted(DATA_ROOT.glob('*')) if t.is_dir()]\nprint('Machine types:', types)\n\ndf_eval = pd.DataFrame()\ndf_eval['file'] = sorted(DATA_ROOT.glob('*/*/*.wav'))\ndf_eval['type'] = df_eval.file.map(lambda f: f.parent.parent.name)\ndf_eval['split'] = df_eval.file.map(lambda f: f.parent.name)\ndf_eval['id'] = df_eval.file.map(lambda s: str(s).split('/')[-1].split('_')[-2])\n\nagg = df_eval.groupby(['id', 'split']).agg('count')\nfig = plt.figure(figsize=(12.0, 6.0))\ng = sns.barplot(x=\"id\", y=\"file\", hue=\"split\", data=agg.reset_index())\nplt.xlabel(\"machine id\")\nplt.ylabel(\"number of files\")\nplt.show()\nagg.transpose()","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:15:03.231723Z","iopub.execute_input":"2022-05-25T17:15:03.232059Z","iopub.status.idle":"2022-05-25T17:15:03.722956Z","shell.execute_reply.started":"2022-05-25T17:15:03.232021Z","shell.execute_reply":"2022-05-25T17:15:03.722289Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_log_mel_spectrogram(filename, n_mels=64,\n                        n_fft=1024,\n                        hop_length=512,\n                        power=2.0):\n    wav, sampling_rate = com.file_load(filename)\n    mel_spectrogram = librosa.feature.melspectrogram(y=wav,\n                                                     sr=sampling_rate,\n                                                     n_fft=n_fft,\n                                                     hop_length=hop_length,\n                                                     n_mels=n_mels,\n                                                     power=power)\n    log_mel_spectrogram = 20.0 / power * np.log10(mel_spectrogram + sys.float_info.epsilon)\n    return log_mel_spectrogram, wav\n\nfor t in types:\n    for split in ['train', 'test']:\n        type_df = df[df['type'] == t][df.split == split].reset_index()\n        R = 2\n        fig, ax = plt.subplots(R, 2, figsize = (15, 1.5*R))\n        print(f'=== Machine type [{t}], {split} set ===')\n        for i in range(R * 2):\n            file_index = i//2\n            file_path = Path(type_df.file[file_index])\n            mels, wav = get_log_mel_spectrogram(file_path)\n            if i%2==0:\n                ax[i//2, i%2].set_title(file_path.name)\n                ax[i//2, i%2].imshow(mels)\n                ax[i//2, i%2].axis('off')\n            else:\n                ax[i//2, i%2].set_title(file_path.name)\n                ax[i//2, i%2].plot(wav)\n                ax[i//2, i%2].get_xaxis().set_ticks([])\n        plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:15:03.724229Z","iopub.execute_input":"2022-05-25T17:15:03.724666Z","iopub.status.idle":"2022-05-25T17:15:04.698406Z","shell.execute_reply.started":"2022-05-25T17:15:03.724627Z","shell.execute_reply":"2022-05-25T17:15:04.697744Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Network","metadata":{}},{"cell_type":"code","source":"class CNNAE(nn.Module):\n    \"\"\"\n    This code is mostly taken from the suggested CNN baseline https://github.com/daisukelab/dcase2020_task2_variants\n    \"\"\"\n\n    def  __init__(self, z_dim=40, skip_connections=False):\n        super().__init__()\n        self.skip_connections = skip_connections\n        # encoder\n        self.conv1 = nn.Sequential(nn.ZeroPad2d((1,2,1,2)), # left right top botttom\n                                   nn.Conv2d(1, 32, kernel_size=5, stride=2),\n                                  nn.ReLU())\n\n        self.conv2 = nn.Sequential(nn.ZeroPad2d((1,2,1,2)),\n                                  nn.Conv2d(32, 64, kernel_size=5, stride=2),\n                                  nn.ReLU(),\n                                  nn.Dropout(0.2))\n\n        self.conv3 = nn.Sequential(nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=0),\n                                   nn.ReLU(),\n                                   nn.Dropout(0.3))\n\n        self.conv4 = nn.Sequential(nn.Conv2d(128, 256, kernel_size=3, stride=2, padding=0),\n                                  nn.ReLU(),\n                                  nn.Dropout(0.3))\n\n        self.fc1 = nn.Conv2d(256, z_dim, kernel_size=3)\n        \n        if self.skip_connections:\n            # decoder\n            self.fc2 = nn.Sequential(nn.ConvTranspose2d(z_dim, 256, kernel_size=3),\n                                     nn.ReLU(),\n                                     nn.Dropout(0.3))\n\n            self.conv4d = nn.Sequential(nn.Conv2d(512, 256, kernel_size=1, stride=1, padding=0),\n                                        nn.ConvTranspose2d(256, 128, kernel_size=3, stride=2, padding=0),\n                                        nn.ReLU(),\n                                        nn.Dropout(0.3))\n\n            self.conv3d = nn.Sequential(nn.Conv2d(256, 128, kernel_size=1, stride=1, padding=0),\n                                        nn.ConvTranspose2d(128, 64, kernel_size=3, stride=2, padding=0),\n                                        nn.ReLU(),\n                                        nn.Dropout(0.2))\n\n            self.conv2d = nn.Sequential(nn.Conv2d(128, 64, kernel_size=1, stride=1, padding=0),\n                                        nn.ConvTranspose2d(64, 32, kernel_size=5, stride=2),\n                                        nn.ReLU())\n\n            self.conv1d = nn.Sequential(nn.Conv2d(64, 32, kernel_size=1, stride=1, padding=0),\n                                        nn.ConvTranspose2d(32, 1, kernel_size=5, stride=2))\n        else:\n            # no skip connections\n            self.fc2 = nn.Sequential(nn.ConvTranspose2d(z_dim, 256, kernel_size=3),\n                                     nn.ReLU(),\n                                     nn.Dropout(0.3))\n\n            self.conv4d = nn.Sequential(nn.ConvTranspose2d(256, 128, kernel_size=3, stride=2, padding=0),\n                                        nn.ReLU(),\n                                        nn.Dropout(0.3))\n\n            self.conv3d = nn.Sequential(nn.ConvTranspose2d(128, 64, kernel_size=3, stride=2, padding=0),\n                                        nn.ReLU(),\n                                        nn.Dropout(0.2))\n\n            self.conv2d = nn.Sequential(nn.ConvTranspose2d(64, 32, kernel_size=5, stride=2),\n                                        nn.ReLU())\n\n            self.conv1d = nn.ConvTranspose2d(32, 1, kernel_size=5, stride=2)\n\n    def forward(self, x):\n        if self.skip_connections:\n            # x.shape (batch, 1, n_mels, n_mels )\n            # encoder\n            encoded = skip_1 = self.conv1(x)\n            encoded = skip_2 = self.conv2(encoded)\n            \n            encoded = skip_3 = self.conv3(encoded)\n            encoded = skip_4 = self.conv4(encoded)\n            \n            encoded = self.fc1(encoded)\n            \n            # decoder\n            decoded = self.fc2(encoded)\n            \n            decoded = torch.cat([skip_4, decoded], dim=1)\n            decoded = self.conv4d(decoded)\n            \n            decoded = torch.cat([skip_3, decoded], dim=1)\n            decoded = self.conv3d(decoded)\n           \n            decoded = torch.cat([skip_2[:, :, :-1, :-1], decoded], dim=1)\n            decoded = self.conv2d(decoded)            \n            \n            decoded = torch.cat([skip_1[:, :, :-1,:-1], decoded[:, :, 1:-1,1:-1]], dim=1)\n            decoded = self.conv1d(decoded)[:,:,0:-1,0:-1]\n            \n            decoded = nn.Sigmoid()(decoded)\n        else:\n            # x.shape (batch, 1, n_mels, n_mels )\n            encoded = self.conv1(x)\n            encoded = self.conv2(encoded)\n            encoded = self.conv3(encoded)\n            encoded = self.conv4(encoded)\n            encoded = self.fc1(encoded)\n\n            decoded = self.fc2(encoded)\n            decoded = self.conv4d(decoded)\n            decoded = self.conv3d(decoded)\n            decoded = self.conv2d(decoded)[:,:,1:-1,1:-1]\n            decoded = self.conv1d(decoded)[:,:,0:-1,0:-1]\n\n            decoded = nn.Sigmoid()(decoded)\n\n        return decoded","metadata":{"execution":{"iopub.status.busy":"2022-05-25T19:59:32.304463Z","iopub.execute_input":"2022-05-25T19:59:32.304836Z","iopub.status.idle":"2022-05-25T19:59:32.430682Z","shell.execute_reply.started":"2022-05-25T19:59:32.304747Z","shell.execute_reply":"2022-05-25T19:59:32.429467Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Parameters","metadata":{"execution":{"iopub.status.busy":"2022-05-02T08:38:36.395212Z","iopub.execute_input":"2022-05-02T08:38:36.395652Z","iopub.status.idle":"2022-05-02T08:38:36.422764Z","shell.execute_reply.started":"2022-05-02T08:38:36.395533Z","shell.execute_reply":"2022-05-02T08:38:36.422138Z"}}},{"cell_type":"code","source":"params_cnn = {\n# inout directory\n\"dev_directory\" : \"../input/eurecom-aml-2022-challenge-2/dev_data/dev_data/\",\n\"eval_directory\" : \"../input/eurecom-aml-2022-challenge-2/eval_data/eval_data/\",\n\"model_directory\": \"./model\",\n\"result_directory\": \"./result\",\n\"result_file\": \"result.csv\",\n\"max_fpr\" : 0.1,\n\"mode\" : True, # mode=True for development dataset, mode=False for evaluation dataset\n\"skip_connection\": False,\n\n# preprocessing for mel-spectrogram\n\"feature\": {\n\"n_mels\": 128, #64\n\"frames\" : 5,\n\"n_fft\": 1024,\n\"hop_length\": 512,\n\"power\": 2.0,\n\"random\": True,  # apply random crop\n\"augment_tfm\": None,  # Augument function\n\"augx\": False, # augument input\n\"augy\": False, # augument target\n\"debug\": False, # print a couple of image after creation of dataset\n\"normalize\": True  # Apply normalization\n}, #2.0\n    \n# training\n\"fit\":{\n\"lr\": 0.001,\n\"b1\": 0.9,\n\"b2\": 0.99,\n\"weight_decay\": 0.0,\n\"epochs\" : 100,\n\"batch_size\" : 64, #256\n\"shuffle\" : True,\n\"validation_split\" : 0.1,\n\"verbose\" : 1,\n\"nu\": None} # set to None if not SVDD\n    \n}\n\nparams_cnn = EasyDict(params_cnn)\n\n# create working directory\nensure_folder(params_cnn.model_directory)\n\n\ntypes = ['slider']\n\ndirs = com.select_dirs(param=params_cnn, mode=params_cnn.mode)\n\n\n# data_files = sorted(Path(params_cnn.dev_directory).glob('*.wav'))\n\n# data = {t:[f for f in data_files if t in str(f)] for t in types}\n\n\n\n# # fix random seeds\ndeterministic_everything(2020, pytorch=True)\n\n\n\n# PyTorch device\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\n# data, types\nslider_dir = dirs[0]\nslider_dir","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:18:54.341993Z","iopub.execute_input":"2022-05-25T17:18:54.342599Z","iopub.status.idle":"2022-05-25T17:18:54.48726Z","shell.execute_reply.started":"2022-05-25T17:18:54.342561Z","shell.execute_reply":"2022-05-25T17:18:54.486475Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"markdown","source":"## Dataset","metadata":{}},{"cell_type":"code","source":"def list_to_vector_array_CNN(file_list,\n                             n_mels=64,\n                            n_fft=1024,\n                            hop_length=512,\n                            power=2.0\n                            ):\n    \n    for idx, file in enumerate(file_list):\n        log_mel_spect, _ = get_log_mel_spectrogram(file,\n                                                n_mels=n_mels,\n                                                n_fft=n_fft,\n                                                hop_length=hop_length,\n                                                power=power)\n\n        if idx == 0:  # first file\n            dataset = np.zeros(\n                (len(file_list), \n                log_mel_spect.shape[0],\n                log_mel_spect.shape[1])\n            )\n            com.logger.info(f'Created the  dataset shape ({dataset.shape})')\n            dataset[idx, ...] = log_mel_spect\n        else:\n            dataset[idx, ...], _ = get_log_mel_spectrogram(file,\n                                            n_mels=n_mels,\n                                            n_fft=n_fft,\n                                            hop_length=hop_length,\n                                            power=power)\n    return dataset\n\n","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:18:56.938818Z","iopub.execute_input":"2022-05-25T17:18:56.93971Z","iopub.status.idle":"2022-05-25T17:18:57.045229Z","shell.execute_reply.started":"2022-05-25T17:18:56.939665Z","shell.execute_reply":"2022-05-25T17:18:57.044471Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## DATASET CNN","metadata":{}},{"cell_type":"code","source":"class DatasetCNN(torch.utils.data.Dataset):\n    \"\"\"\n    most of the code is taken from the suggested baseline https://github.com/daisukelab/dcase2020_task2_variants\n\n    This dataset is used to load the data for the CNN Auto Encoder\n\n    For every output, this dataset class crop square image from this original data.\n\n    Number of total samples is `n_sampling` times number of _live data_.\n    _Live data_ is all the original data by default, and filtered by splitting functions.\n\n    - For training use, set `random=True`. This will yield randomly cropped square\n      image (64x64 for example) from the original sample (64x313 for 10s sample).\n      \n    - For validation use, set `random=False`. Image will be cropped from fixed position.\n\n    Augmentation can be flexibly applied to either the output `x` only or `y` only, or both `x` and `y`.\n    `aug_x` and `aug_y` control this behavor.\n\n    Data split for training/validation can be done by using:\n        `get_index_by_pct()`: generate list of training index.\n        `train_split(train_index)`: set live data as original samples listed on `train_index`.\n        `val_split(train_index)`: set live data as original samples NOT listed on `train_index`.\n    Yields:\n        x: square image expected to be used as source.\n        y: square image expected to be used as reference for evaluating reconstructed image by training model.\n    \"\"\"\n\n    def __init__(self,  files, n_mels, n_fft, hop_length, power,\n                 n_sampling=10, transform=None, augment_tfm=None, normalize=True, \n                 random=True, aug_x=True, aug_y=False, debug=True):\n        \n        self.n_sampling = n_sampling\n        self.files = files\n        self.n_mels, self.n_fft = n_mels, n_fft\n        self.hop_length, self.power = hop_length, power\n        \n        self.transform, self.augment_tfm = transform, augment_tfm\n        self.random, self.aug_x, self.aug_y = random, aug_x, aug_y\n\n        self.X = list_to_vector_array_CNN(self.files,\n                                        n_mels=self.n_mels,\n                                        n_fft=self.n_fft,\n                                        hop_length=self.hop_length,\n                                        power=self.power\n                                        )\n        \n        if normalize:\n            self.X = normalize_0to1(self.X)\n\n        if debug:\n            from dlcliche.utils import display\n            from dlcliche.math import np_describe\n            display(np_describe(self.X[0].cpu().numpy()))\n\n        self.orgX = self.X\n  \n    def get_index_by_pct(self, split_pct=0.1):\n        n = len(self.orgX)\n        return random.sample(range(n), k=(n - int(n * split_pct)))\n\n    def train_split(self, train_index):\n        self.train_index = train_index\n        self.X = self.orgX[train_index]\n    \n    def val_split(self, train_index):\n        n = len(self.orgX)\n        self.val_index = [i for i in range(n) if i not in train_index]\n        self.X = self.orgX[self.val_index]\n\n    def __len__(self):\n        return len(self.X) * self.n_sampling\n\n    def __getitem__(self, index):\n        file_index = index // self.n_sampling\n        part_index = index % self.n_sampling\n        x = self.X[file_index]\n        dim, length = x.shape\n\n        # crop square part of sample\n        if self.random:\n            # random crop\n            start = random.randint(0, length - dim)\n        else:\n            # crop with fixed position\n            start = (length // self.n_sampling) * part_index\n        start = min(start, length - dim)\n        x = x[:, start:start+dim]\n\n        # augmentation transform\n        y = x\n        if self.augment_tfm is not None:\n            tfm_x = self.augment_tfm(x)\n            if self.aug_x: x = tfm_x\n            if self.aug_y: y = tfm_x\n\n        # transform (convert to tensor here)\n        if self.transform is not None:\n            x = self.transform(x)\n            y = self.transform(y)\n        return x, y","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:18:58.497262Z","iopub.execute_input":"2022-05-25T17:18:58.497532Z","iopub.status.idle":"2022-05-25T17:18:58.81467Z","shell.execute_reply.started":"2022-05-25T17:18:58.497502Z","shell.execute_reply":"2022-05-25T17:18:58.81377Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## CNN training class","metadata":{}},{"cell_type":"code","source":"def get_radius(dist: torch.Tensor, nu: float):\n    \"\"\"Optimally solve for radius R via the (1-nu)-quantile of distances.\"\"\"\n    return np.quantile(np.sqrt(dist.clone().data.cpu().numpy()), 1 - nu)\n\ndef SVDD_loss_function(outputs, loss_objective, c, R, nu=0.1):\n    \"\"\"\n    Params:\n        recon_x: output of the VAE\n        c: Hypersphere center\n        loss_objective: 'one-class', 'soft-boundary'\n        R: radius of the sphere\n        \n    \"\"\"\n\n    dist = torch.sum( (outputs - c) ** 2, dim=1)\n\n    if loss_objective == 'soft-boundary':\n        scores = dist - R ** 2\n        loss = R ** 2 + (1 / nu) * torch.mean(\n            torch.max( torch.zeros_like(scores),scores)\n            )\n    else:\n        loss = torch.mean(dist)\n    \n    # Update R\n    if (loss_objective == 'soft-boundary'):\n        R = torch.tensor(get_radius(dist, nu), device=device) # Device set global \n\n    return loss, R","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:19:00.258258Z","iopub.execute_input":"2022-05-25T17:19:00.259085Z","iopub.status.idle":"2022-05-25T17:19:00.3355Z","shell.execute_reply.started":"2022-05-25T17:19:00.25905Z","shell.execute_reply":"2022-05-25T17:19:00.334521Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"total_loss = []\ntotal_val_loss = []\n\n# CNN VA Training class\nclass CNNTraining(pl.LightningModule):\n    \"\"\"Task2 PyTorch Lightning class, for training only.\"\"\"\n\n    def __init__(self, device, model, params, files, normalize=False):\n        super().__init__()\n        self.nu = None\n        self.params = params\n        self.lr = params.fit.lr\n        self.normalize = normalize\n        self.model = model\n        self.mseloss = torch.nn.MSELoss()\n        \n        # split data files\n        if files is not None:\n            n_val = int(params.fit.validation_split * len(files))\n            self.val_files = random.sample(files, n_val)\n            self.train_files = [f for f in files if f not in self.val_files]\n        \n        if params.fit.nu is not None:\n            self.R = torch.tensor(0.0, device=device)\n            self.nu = params.fit.nu\n            \n            # GET CENTER\n            dl = self.train_dataloader()\n            self.c = self.init_center_c(dl, model, device)\n    \n    \n    def init_center_c(self,\n                      train_loader, \n                  net: nn.Module,\n                  device, \n                  eps: float=0.1):\n        \"\"\"Initialize hypersphere center c as the mean from an initial forward pass on the data.\"\"\"\n\n        n_samples = 0\n        # print('C: ', c.device)\n        # print('self.device: ', self.device)\n        net.eval()\n        with torch.no_grad():\n            for i, data in enumerate(train_loader):\n                # get the inputs of the batch\n                inputs, _ = data \n                # print(inputs.device)\n                inputs = inputs.to(device)\n                # print(inputs.device)\n                outputs = net(inputs)\n                outputs = outputs.to(device)\n                # print(outputs.device, c.device)\n                n_samples += outputs.shape[0]\n                if i == 0:\n                    c = torch.zeros(outputs.shape[1:], device=device)\n                c += torch.sum(outputs, dim=0)\n\n        c /= n_samples\n\n        # If c_i is too close to 0, set to +-eps. Reason: a zero unit can be trivially matched with zero weights.\n        c[(abs(c) < eps) & (c < 0)] = -eps\n        c[(abs(c) < eps) & (c > 0)] = eps\n        return c\n       \n\n    def forward(self, x):\n        return self.model(x)\n    \n    def training_step(self, batch, batch_nb):\n        x, y = batch\n        y_hat = self.forward(x)\n        if self.nu is None:\n            loss = self.mseloss(y_hat, y)\n        else:\n            loss, self.R = SVDD_loss_function(\n                outputs = y_hat,\n                loss_objective = 'soft-boundary',\n                R = self.R,\n                c = self.c,\n                nu = self.nu\n           )\n        \n        total_loss.append(float(loss))\n        # step every N epochs\n#         if self.trainer.is_last_batch and (self.trainer.current_epoch + 1) % 5 == 0:\n#             self.lr = self.lr * 0.1\n#             print(\"CHANGEDEEEE \", self.lr)\n        tensorboard_logs = {'train_loss': loss}\n        return {'loss': loss, 'log': tensorboard_logs}\n\n    def validation_step(self, batch, batch_nb):\n        x, y = batch\n        y_hat = self.forward(x)\n        loss =  self.mseloss(y_hat, y)\n        \n        total_val_loss.append(float(loss))\n        self.log(\"val_loss\", float(loss))\n        \n        return {'val_loss': self.mseloss(y_hat, y)}\n\n    def validation_epoch_end(self, outputs):\n        avg_loss = torch.stack([x['val_loss'] for x in outputs]).mean()\n        tensorboard_logs = {'val_loss': avg_loss}\n        \n        return {'avg_val_loss': avg_loss, 'log': tensorboard_logs}\n\n    def configure_optimizers(self):\n        return torch.optim.Adam(self.parameters(),\n                                lr=self.params.fit.lr,\n                                betas=(self.params.fit.b1, self.params.fit.b2),\n                                weight_decay=self.params.fit.weight_decay)\n\n    def train_dataloader(self):\n        com.logger.info('Creating Train')\n        data_loader = self._get_dl('train')\n        com.logger.info('End Train')\n        return data_loader\n\n    def val_dataloader(self):\n        com.logger.info('Creating Val')\n        data_loader = self._get_dl('val')\n        com.logger.info('End Val')\n        return data_loader\n    \n    \n    def _get_dl(self, for_what):\n        files = self.train_files if for_what == 'train' else self.val_files\n        \n        ds = DatasetCNN(files,\n                        n_mels=self.params.feature.n_mels,\n                        n_fft=self.params.feature.n_fft,\n                        hop_length=self.params.feature.hop_length,\n                        power=self.params.feature.power,\n                        transform=ToTensor1ch(device=device, image=True),\n                        random=self.params.feature.random,\n                        augment_tfm=self.params.feature.augment_tfm,\n                        aug_x=self.params.feature.augx,\n                        aug_y=self.params.feature.augy,\n                        debug=self.params.feature.debug,\n                        normalize=self.params.feature.normalize)\n    \n        return torch.utils.data.DataLoader(ds,\n                                           batch_size=self.params.fit.batch_size,\n                                              shuffle=(self.params.fit.shuffle if for_what == 'train' else False)\n                                          )\n\n    ","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:19:01.398919Z","iopub.execute_input":"2022-05-25T17:19:01.399188Z","iopub.status.idle":"2022-05-25T17:19:01.491349Z","shell.execute_reply.started":"2022-05-25T17:19:01.399159Z","shell.execute_reply":"2022-05-25T17:19:01.490528Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from pytorch_lightning.callbacks.early_stopping import EarlyStopping\n\ntotal_loss = []\ntotal_val_loss = []\nparams_cnn.skip_connection = False # Add skip connections\nparams_cnn.feature.n_mels = 128 # Define size input (1, n_mels, n_mels)\nparams_cnn.fit.nu = None # None no SVDD, otherwise coefficient of the loss\nparams_cnn.fit.epochs = 100\nparams_cnn.fit.lr = 0.001\nparams_cnn.fit.batch_size = 64 \n\ndef train_cnnae():\n\n    target='slider'\n    \n    print(f'==== Start training [{target}] with {torch.cuda.device_count()} GPU(s). ====')\n    \n    files = sorted(glob.glob(f'{slider_dir}/train/*.wav'))\n    \n    model = CNNAE(skip_connections = params_cnn.skip_connection).to(device)\n    summary(device, model, input_size=(1, params_cnn.feature.n_mels, params_cnn.feature.n_mels))\n    \n    trainer = pl.Trainer(max_epochs=params_cnn.fit.epochs,\n                         gpus=torch.cuda.device_count(),\n                        callbacks=[EarlyStopping(monitor=\"val_loss\", mode=\"min\")])\n    \n    task2 = CNNTraining(device, model, params_cnn, files, normalize = True)\n    trainer.fit(task2)\n\n    model_file = f'{params_cnn.model_directory}/model_{target}.pth'\n    torch.save(task2.model.state_dict(), model_file)\n    print(f'saved {model_file}.\\n')\n    return task2\n        \ntask2 = train_cnnae()","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:19:19.060032Z","iopub.execute_input":"2022-05-25T17:19:19.060367Z","iopub.status.idle":"2022-05-25T17:19:19.174685Z","shell.execute_reply.started":"2022-05-25T17:19:19.06033Z","shell.execute_reply":"2022-05-25T17:19:19.173587Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def show_some_predictions(dl, model, start_index, n_samples, image=False):\n    shape = (-1, params_cnn.feature.n_mels, params_cnn.feature.n_mels) if image else (-1, 640)\n    \n    x, y = next(iter(dl))\n    \n    with torch.no_grad():\n        yhat = model(x)\n    \n    x = x.cpu().numpy().reshape(shape)\n    yhat = yhat.cpu().numpy().reshape(shape)\n    print(x.shape, yhat.shape)\n    \n    for sample_idx in range(start_index, start_index + n_samples):\n        fig, axs = plt.subplots(1, 2, figsize=(10, 5))\n        if image:\n            axs[0].imshow(x[sample_idx])\n            axs[1].imshow(yhat[sample_idx])\n        else:\n            axs[0].plot(x[sample_idx])\n            axs[1].plot(yhat[sample_idx])\n\nshow_some_predictions(task2.train_dataloader(), task2.model.to(device), 0, 3, image=True)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Testing on Development dataset","metadata":{}},{"cell_type":"code","source":"class DatasetCNNTest(DatasetCNN):\n    def get_test_batch_x(self, file_index, n_mels=params_cnn.feature.n_mels, steps=20):\n        log_mel_spectrogram = self.X[file_index]\n        vector_array_size = (log_mel_spectrogram.shape[1] - n_mels + 1) // steps\n        vector_array = np.zeros((vector_array_size, n_mels, n_mels))\n        for t in range(vector_array_size):\n            vector_array[t] = log_mel_spectrogram[:, t*steps:t*steps+n_mels]\n        return vector_array","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:19:32.594694Z","iopub.execute_input":"2022-05-25T17:19:32.594981Z","iopub.status.idle":"2022-05-25T17:19:32.668539Z","shell.execute_reply.started":"2022-05-25T17:19:32.59495Z","shell.execute_reply":"2022-05-25T17:19:32.667636Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def file_to_vector_array_2d(file_name,\n                         n_mels=128,\n                         steps=20,\n                         n_fft=1024,\n                         hop_length=512,\n                         power=2.0):\n    \"\"\"\n    convert file_name to a 2d vector array.\n    file_name : str\n        target .wav file\n    return : np.array( np.array( float ) )\n        vector array\n        * dataset.shape = (dataset_size, feature_vector_length)\n    \"\"\"\n    # 02 generate melspectrogram using librosa\n    y, sr = com.file_load(file_name)\n    mel_spectrogram = librosa.feature.melspectrogram(y=y,\n                                                     sr=sr,\n                                                     n_fft=n_fft,\n                                                     hop_length=hop_length,\n                                                     n_mels=n_mels,\n                                                     power=power)\n\n    # 03 convert melspectrogram to log mel energy\n    log_mel_spectrogram = 20.0 / power * np.log10(mel_spectrogram + sys.float_info.epsilon)\n\n    # 04 calculate total vector size\n    vector_array_size = (log_mel_spectrogram.shape[1] - n_mels + 1) // steps\n\n    # 06 generate feature vectors by concatenating multiframes\n    vector_array = np.zeros((vector_array_size, n_mels, n_mels))\n    for t in range(vector_array_size):\n        vector_array[t] = log_mel_spectrogram[:, t*steps:t*steps+n_mels]\n\n    return vector_array","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:19:35.518994Z","iopub.execute_input":"2022-05-25T17:19:35.519739Z","iopub.status.idle":"2022-05-25T17:19:35.591668Z","shell.execute_reply.started":"2022-05-25T17:19:35.519703Z","shell.execute_reply":"2022-05-25T17:19:35.590819Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def test_cnn():\n    # make output result directory\n    os.makedirs(params_cnn.result_directory, exist_ok=True)\n\n    # load base directory\n    dirs = com.select_dirs(param=params_cnn, mode=params_cnn.mode)\n    \n    # initialize lines in csv for AUC and pAUC\n    csv_lines = []\n\n    # PyTorch version specific...\n    to_tensor = pytorch_common.ToTensor1ch(image=True)\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\n    # loop of the base directory\n    for idx, target_dir in enumerate(dirs):\n        # idx = 0\n        # target_dir = '/kaggle/input/eurecom-aml-2022-challenge-2/eval_data/eval_data/slider'\n        print(\"\\n===========================\")\n        # it indicates how many machine type we have [1/1]\n        print(\"[{idx}/{total}] {dirname}\".format(dirname=target_dir, idx=idx+1, total=len(dirs)))\n        machine_type = os.path.split(target_dir)[1] # slider\n\n        print(\"============== MODEL LOAD ==============\")\n        # set model path\n        model_file = \"{model}/model_{machine_type}.pth\".format(model=params_cnn.model_directory,\n                                                               machine_type=machine_type)\n\n        # load model file\n        if not os.path.exists(model_file):\n            com.logger.error(\"{} model not found \".format(machine_type))\n            sys.exit(-1)\n            \n        com.logger.info(\"loading model: {}\".format(model_file))\n        \n        model = CNNAE(skip_connections = params_cnn.skip_connection).to(device)\n        \n        pytorch_common.load_weights(model, model_file)\n        \n        model.eval()\n        if params_cnn.mode: # if True, development !\n            # results by type\n            csv_lines.append([machine_type])\n            csv_lines.append([\"id\", \"AUC\", \"pAUC\"])\n            performance = []\n        \n        # contain the available id\n        # train ['id_00', 'id_02', 'id_04']\n        # test ['id_01', 'id_03', 'id_05']\n        machine_id_list = get_machine_id_list_for_test(target_dir)\n        print(machine_id_list)\n        for id_str in machine_id_list:\n            # load test file\n            test_files, y_true = test_file_list_generator(target_dir, id_str, mode=params_cnn.mode)\n            \n            # setup anomaly score file path\n            anomaly_score_csv = \"{result}/anomaly_score_{machine_type}_{id_str}.csv\".format(\n                                                                                     result=params_cnn.result_directory,\n                                                                                     machine_type=machine_type,\n                                                                                     id_str=id_str)\n            anomaly_score_list = []\n\n            print(\"\\n============== BEGIN TEST FOR A MACHINE ID ==============\")\n            y_pred = [0. for k in test_files]\n            for file_idx, file_path in tqdm(enumerate(test_files), total=len(test_files)):\n                try:\n                    data_ = file_to_vector_array_2d(file_path,\n                                                    n_mels=params_cnn.feature.n_mels,\n                                                    n_fft=params_cnn.feature.n_fft,\n                                                    hop_length=params_cnn.feature.hop_length,\n                                                    power=params_cnn.feature.power)\n                    data_ = pytorch_common.normalize_0to1(data_)\n                    \n                    with torch.no_grad():\n                        yhat = model(to_tensor(data_)).cpu().detach().numpy().reshape(data_.shape)\n                        errors = np.mean(np.square(data_ - yhat), axis=1)\n#                         if file_idx in [0, 500]:\n#                             for i in range(2):\n#                                 fig, axs = plt.subplots(1, 2)\n#                                 axs[0].imshow(data_)\n#                                 axs[1].imshow(yhat[i])\n#                                 plt.show()\n                    y_pred[file_idx] = np.mean(errors)\n                    \n                    anomaly_score_list.append([os.path.basename(file_path), y_pred[file_idx]])\n                except:\n                    com.logger.error(\"file broken!!: {}\".format(file_path))\n                    sys.exit(-1)\n\n            # save anomaly score\n            save_csv(save_file_path=anomaly_score_csv, save_data=anomaly_score_list)\n            com.logger.info(\"anomaly score result ->  {}\".format(anomaly_score_csv))\n\n            if params_cnn.mode:\n                # append AUC and pAUC to lists\n                auc = metrics.roc_auc_score(y_true, y_pred)\n                p_auc = metrics.roc_auc_score(y_true, y_pred, max_fpr=params_cnn.max_fpr)\n                csv_lines.append([id_str.split(\"_\", 1)[1], auc, p_auc])\n                performance.append([auc, p_auc])\n                com.logger.info(\"AUC : {}\".format(auc))\n                com.logger.info(\"pAUC : {}\".format(p_auc))\n\n            print(\"\\n============ END OF TEST FOR A MACHINE ID ============\")\n\n        if params_cnn.mode:\n            # calculate averages for AUCs and pAUCs\n            averaged_performance = numpy.mean(numpy.array(performance, dtype=float), axis=0)\n            csv_lines.append([\"Average\"] + list(averaged_performance))\n            csv_lines.append([])\n\n    if params_cnn.mode:\n        # output results\n        result_path = \"{result}/{file_name}\".format(result=params_cnn.result_directory, file_name=params_cnn.result_file)\n        com.logger.info(\"AUC and pAUC results -> {}\".format(result_path))\n        save_csv(save_file_path=result_path, save_data=csv_lines)\n\ntest_cnn()","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:19:48.401513Z","iopub.execute_input":"2022-05-25T17:19:48.402239Z","iopub.status.idle":"2022-05-25T17:19:48.492214Z","shell.execute_reply.started":"2022-05-25T17:19:48.402197Z","shell.execute_reply":"2022-05-25T17:19:48.491341Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def upto_6digits(cell):\n    if not cell[0].isdigit(): return cell\n    return f'{float(cell):.6f}'\n\nif params_cnn.mode:\n    with open('./result/result.csv') as f:\n        for l in f.readlines():\n            l = l.strip() #replace('\\n', '')\n            if ',' not in l:\n                print(l)\n                continue\n            ls = l.split(',')\n            print(f'{ls[0]}\\t\\t{upto_6digits(ls[1])}\\t\\t{upto_6digits(ls[2])}')","metadata":{"execution":{"iopub.status.busy":"2022-05-25T14:40:32.698935Z","iopub.execute_input":"2022-05-25T14:40:32.701447Z","iopub.status.idle":"2022-05-25T14:40:32.812264Z","shell.execute_reply.started":"2022-05-25T14:40:32.701389Z","shell.execute_reply":"2022-05-25T14:40:32.811579Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Testing on the Evaluation dataset and generating submission file","metadata":{}},{"cell_type":"code","source":"# re-training on evaluation dataset\nparams_cnn.mode = False\n\ntask2 = train_cnnae() # due to the memory restriction, restart the kernel and train on only evaluation dataset","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:28:30.206436Z","iopub.execute_input":"2022-05-25T17:28:30.207411Z","iopub.status.idle":"2022-05-25T17:34:08.485473Z","shell.execute_reply.started":"2022-05-25T17:28:30.207351Z","shell.execute_reply":"2022-05-25T17:34:08.484658Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#load_weights(task2.model, 'model/model_slider.pth')\ndef show_some_predictions(dl, model, start_index, n_samples, image=False):\n    shape = (-1, params_cnn.feature.n_mels, params_cnn.feature.n_mels) if image else (-1, 640)\n    \n    x, y = next(iter(dl))\n    \n    with torch.no_grad():\n        yhat = model(x)\n    \n    x = x.cpu().numpy().reshape(shape)\n    yhat = yhat.cpu().numpy().reshape(shape)\n    print(x.shape, yhat.shape)\n    \n    for sample_idx in range(start_index, start_index + n_samples):\n        fig, axs = plt.subplots(1, 2, figsize=(10, 5))\n        if image:\n            axs[0].imshow(x[sample_idx])\n            axs[1].imshow(yhat[sample_idx])\n        else:\n            axs[0].plot(x[sample_idx])\n            axs[1].plot(yhat[sample_idx])\n            \nshow_some_predictions(task2.train_dataloader(), task2.model.to(device), 0, 3, image=True)","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:34:08.487289Z","iopub.execute_input":"2022-05-25T17:34:08.48778Z","iopub.status.idle":"2022-05-25T17:35:12.290308Z","shell.execute_reply.started":"2022-05-25T17:34:08.487737Z","shell.execute_reply":"2022-05-25T17:35:12.289646Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_cnn()","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:35:12.291449Z","iopub.execute_input":"2022-05-25T17:35:12.292159Z","iopub.status.idle":"2022-05-25T17:35:30.725296Z","shell.execute_reply.started":"2022-05-25T17:35:12.292119Z","shell.execute_reply":"2022-05-25T17:35:30.724543Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\n\ndf1 = pd.read_csv('./result/anomaly_score_slider_id_01.csv', delimiter=',', header=None)\ndf3 = pd.read_csv('./result/anomaly_score_slider_id_03.csv', delimiter=',', header=None)\ndf5 = pd.read_csv('./result/anomaly_score_slider_id_05.csv', delimiter=',', header=None)\n\ndfs = [df1, df3, df5]\neval_df = pd.concat(dfs)\neval_df = eval_df.rename(columns={0: \"file_name\", 1: \"anomaly_score\"})\n\neval_df.to_csv('/kaggle/working/submission.csv',index = False)\n","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:35:30.730106Z","iopub.execute_input":"2022-05-25T17:35:30.730737Z","iopub.status.idle":"2022-05-25T17:35:30.859575Z","shell.execute_reply.started":"2022-05-25T17:35:30.730693Z","shell.execute_reply":"2022-05-25T17:35:30.858857Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"eval_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-05-25T17:35:48.166319Z","iopub.execute_input":"2022-05-25T17:35:48.166571Z","iopub.status.idle":"2022-05-25T17:35:48.242693Z","shell.execute_reply.started":"2022-05-25T17:35:48.166545Z","shell.execute_reply":"2022-05-25T17:35:48.241552Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}