{"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":"# Check GPU","metadata":{}},{"cell_type":"code","source":"!nvidia-smi -L","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-10-26T03:56:06.770072Z","iopub.execute_input":"2021-10-26T03:56:06.770493Z","iopub.status.idle":"2021-10-26T03:56:07.451538Z","shell.execute_reply.started":"2021-10-26T03:56:06.770375Z","shell.execute_reply":"2021-10-26T03:56:07.450723Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Extraction","metadata":{}},{"cell_type":"code","source":"!unzip ../input/freesound-audio-tagging-2019/train_curated.zip -d /kaggle/working/train_curated | sh &> /dev/null\n!unzip ../input/freesound-audio-tagging-2019/test.zip -d /kaggle/working/test | sh &> /dev/null","metadata":{"execution":{"iopub.status.busy":"2021-10-26T03:56:07.453665Z","iopub.execute_input":"2021-10-26T03:56:07.453891Z","iopub.status.idle":"2021-10-26T03:57:37.577257Z","shell.execute_reply.started":"2021-10-26T03:56:07.453865Z","shell.execute_reply":"2021-10-26T03:57:37.576279Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Libraries","metadata":{}},{"cell_type":"code","source":"import os\nimport IPython\nimport librosa\nimport warnings\nimport librosa.display\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nfrom skmultilearn.model_selection import iterative_train_test_split\n\nplt.style.use(\"ggplot\")\nwarnings.filterwarnings(\"ignore\", category=FutureWarning)\nos.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'\nFULL_TRAINING = False","metadata":{"execution":{"iopub.status.busy":"2021-10-26T03:57:37.578968Z","iopub.execute_input":"2021-10-26T03:57:37.579569Z","iopub.status.idle":"2021-10-26T03:57:43.911867Z","shell.execute_reply.started":"2021-10-26T03:57:37.579527Z","shell.execute_reply":"2021-10-26T03:57:43.911106Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Competition Metrics","metadata":{}},{"cell_type":"code","source":"class LWLRAP(tf.keras.metrics.Metric):\n    def __init__(self, num_classes, name=\"weighted_label_ranking_average_precision\"):\n        super().__init__(name=name)\n\n        self._precisions = self.add_weight(\n            name=\"per_class_cumulative_precision\",\n            shape=[num_classes],\n            initializer=\"zeros\",\n        )\n\n        self._counts = self.add_weight(\n            name=\"per_class_cumulative_count\",\n            shape=[num_classes],\n            initializer=\"zeros\",\n        )\n\n    @staticmethod\n    def _one_sample_positive_class_precisions(example):\n        y_true, y_pred = example\n\n        retrieved_classes = tf.argsort(y_pred, direction=\"DESCENDING\")\n        class_rankings = tf.argsort(retrieved_classes)\n        retrieved_class_true = tf.gather(y_true, retrieved_classes)\n        retrieved_cumulative_hits = tf.math.cumsum(\n            tf.cast(retrieved_class_true, tf.float32)\n        )\n\n        idx = tf.where(y_true)[:, 0]\n        i = tf.boolean_mask(class_rankings, y_true)\n        r = tf.gather(retrieved_cumulative_hits, i)\n        c = 1 + tf.cast(i, tf.float32)\n        precisions = r / c\n\n        dense = tf.scatter_nd(idx[:, None], precisions, [y_pred.shape[0]])\n        return dense\n\n    def update_state(self, y_true, y_pred, sample_weight=None):\n        precisions = tf.map_fn(\n            fn=self._one_sample_positive_class_precisions,\n            elems=(y_true, y_pred),\n            fn_output_signature=(tf.float32),\n        )\n\n        increments = tf.cast(precisions > 0, tf.float32)\n        total_increments = tf.reduce_sum(increments, axis=0)\n        total_precisions = tf.reduce_sum(precisions, axis=0)\n\n        self._precisions.assign_add(total_precisions)\n        self._counts.assign_add(total_increments)\n\n    def result(self):\n        per_class_lwlrap = self._precisions / tf.maximum(self._counts, 1.0)\n        per_class_weight = self._counts / tf.reduce_sum(self._counts)\n        overall_lwlrap = tf.reduce_sum(per_class_lwlrap * per_class_weight)\n        return overall_lwlrap\n\n    def reset_state(self):\n        self._precisions.assign(self._precisions * 0)\n        self._counts.assign(self._counts * 0)","metadata":{"execution":{"iopub.status.busy":"2021-10-26T03:57:43.913291Z","iopub.execute_input":"2021-10-26T03:57:43.913537Z","iopub.status.idle":"2021-10-26T03:57:45.289262Z","shell.execute_reply.started":"2021-10-26T03:57:43.913504Z","shell.execute_reply":"2021-10-26T03:57:45.288529Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Preparation","metadata":{}},{"cell_type":"markdown","source":"## Load Dataset CSV","metadata":{}},{"cell_type":"code","source":"df_train_curated = pd.read_csv('/kaggle/input/freesound-audio-tagging-2019/train_curated.csv')\ndf_train_noisy = pd.read_csv('/kaggle/input/freesound-audio-tagging-2019/train_noisy.csv')\ndf_sample_submission = pd.read_csv('/kaggle/input/freesound-audio-tagging-2019/sample_submission.csv')","metadata":{"execution":{"iopub.status.busy":"2021-10-26T03:57:45.291682Z","iopub.execute_input":"2021-10-26T03:57:45.291959Z","iopub.status.idle":"2021-10-26T03:57:45.383003Z","shell.execute_reply.started":"2021-10-26T03:57:45.291922Z","shell.execute_reply":"2021-10-26T03:57:45.382175Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Add Directory Column","metadata":{}},{"cell_type":"code","source":"df_train_curated['dir'] = df_train_curated.fname.apply(lambda x: f'/kaggle/working/train_curated/{x}')\ndf_train_noisy['dir'] = df_train_noisy.fname.apply(lambda x: f'/kaggle/working/train_noisy/{x}')\ndf_sample_submission['dir'] = df_sample_submission.fname.apply(lambda x: f'/kaggle/working/test/{x}')","metadata":{"execution":{"iopub.status.busy":"2021-10-26T03:57:45.384392Z","iopub.execute_input":"2021-10-26T03:57:45.384658Z","iopub.status.idle":"2021-10-26T03:57:46.300783Z","shell.execute_reply.started":"2021-10-26T03:57:45.384622Z","shell.execute_reply":"2021-10-26T03:57:46.299913Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if FULL_TRAINING:\n    df_train = pd.concat([df_train_curated, df_train_noisy], axis=0).reset_index(drop=True)\nelse:\n    df_train = df_train_curated\ndf_train","metadata":{"execution":{"iopub.status.busy":"2021-10-26T03:57:46.304779Z","iopub.execute_input":"2021-10-26T03:57:46.305052Z","iopub.status.idle":"2021-10-26T03:57:46.327785Z","shell.execute_reply.started":"2021-10-26T03:57:46.305024Z","shell.execute_reply":"2021-10-26T03:57:46.32717Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Label Encoding","metadata":{}},{"cell_type":"code","source":"LABELS = list(df_train.labels.unique())\nlabel_idx = {label: i for i, label in enumerate(LABELS)}\ndf_train[\"labels_idx\"] = df_train.labels.apply(lambda x: label_idx[x])","metadata":{"execution":{"iopub.status.busy":"2021-10-26T03:57:46.32897Z","iopub.execute_input":"2021-10-26T03:57:46.329224Z","iopub.status.idle":"2021-10-26T03:57:46.34124Z","shell.execute_reply.started":"2021-10-26T03:57:46.329187Z","shell.execute_reply":"2021-10-26T03:57:46.340453Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels = list(df_sample_submission.iloc[:, 1:].columns)[:-1]\ny_train = np.zeros((len(df_train), len(labels)), dtype=np.uint8)\n\nfor i, y in enumerate(df_train.labels.tolist()):\n    for y_i in y.split(','):\n        j = labels.index(y_i)\n        y_train[i, j] = 1\n\ny_train = pd.DataFrame(y_train, columns=labels)","metadata":{"execution":{"iopub.status.busy":"2021-10-26T03:57:46.342516Z","iopub.execute_input":"2021-10-26T03:57:46.342873Z","iopub.status.idle":"2021-10-26T03:57:46.364447Z","shell.execute_reply.started":"2021-10-26T03:57:46.342834Z","shell.execute_reply":"2021-10-26T03:57:46.363836Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.concat([df_train, y_train], axis=1)\ndf","metadata":{"execution":{"iopub.status.busy":"2021-10-26T03:57:46.365479Z","iopub.execute_input":"2021-10-26T03:57:46.365747Z","iopub.status.idle":"2021-10-26T03:57:46.390679Z","shell.execute_reply.started":"2021-10-26T03:57:46.365713Z","shell.execute_reply":"2021-10-26T03:57:46.389851Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data Generator","metadata":{}},{"cell_type":"code","source":"class AudioDataset(tf.keras.utils.Sequence):\n    sampling_rate = 44100\n    duration = 1\n    hop_length = 347*duration\n    fmin = 20\n    fmax = sampling_rate // 2\n    n_mels = 128\n    n_fft = n_mels * 20\n    samples = sampling_rate * duration\n    use_three_channels = True\n    size = (n_mels, n_mels)\n    \n    def __init__(self, dirs, labels, batch_size=32):\n        self.dirs = dirs\n        self.labels = labels\n        self.batch_size = batch_size\n\n    def __len__(self):\n        length = len(self.dirs) / self.batch_size\n        length = np.ceil(length)\n        length = length.astype(np.int)\n        return length\n\n    @classmethod\n    def get_audio(cls, dir, do_trim=True):\n        audio, sr = librosa.load(dir, sr=cls.sampling_rate)\n        if 0 < len(audio):\n            audio, _ = librosa.effects.trim(audio)\n        if len(audio) > cls.samples and do_trim:\n            audio = audio[:cls.samples]\n        else:\n            padding = cls.samples - len(audio)\n            offset = padding // 2\n            audio = np.pad(\n                audio, \n                (offset, cls.samples - len(audio) - offset), \n                'constant'\n            )\n        return audio.astype(np.float32), sr\n        \n    @classmethod\n    def audio_to_spectrogram(cls, audio, sr):\n        audio /= 32768\n        spectrogram = librosa.feature.melspectrogram(\n            audio,\n            sr=sr or cls.sampling_rate,\n            n_mels=cls.hop_length,\n            n_fft=cls.n_fft,\n            fmin=cls.fmin,\n            fmax=cls.fmax\n        )\n        spectrogram = librosa.power_to_db(spectrogram)\n        spectrogram = spectrogram.astype(np.float32)\n        return spectrogram\n        \n    @staticmethod\n    def mono_to_color(x, mean=None, std=None, norm_max=None, norm_min=None, eps=1e-6):\n        x = np.stack([x, x, x], -1)\n        mean = mean or x.mean()\n        std = std or x.std()\n        if not (mean is None and std is None):\n            mean = np.array(mean)\n            std = np.array(std)\n        x_std = (x - mean) / (std + eps)\n        x_std_min = x_std.min()\n        x_std_max = x_std.max()\n        norm_min = norm_min or x_std_min\n        norm_max = norm_max or x_std_max\n        \n        if (x_std_max - x_std_min) > eps:\n            v = x_std\n            v[v < norm_min] = norm_min\n            v[v > norm_max] = norm_max\n            v = 255 * (v - norm_min) / (norm_max - norm_min)\n        else:\n            v = np.zeros_like(x_std, dtype=np.uint8)\n        return v\n        \n\n    def __getitem__(self, idx):\n        dirs = self.dirs[idx * self.batch_size : (idx + 1) * self.batch_size]\n        if self.labels is None:\n            labels = None\n        else:\n            labels = self.labels[idx * self.batch_size : (idx + 1) * self.batch_size]\n        audios = [self.get_audio(dir) for dir in dirs]\n        audios = [self.audio_to_spectrogram(audio, sr) for audio, sr in audios]\n        if self.use_three_channels:\n            audios = [self.mono_to_color(x, [0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) for x in audios]\n        audios = [tf.image.resize(x, self.size) for x in audios]\n        audios = np.stack(audios) / 255\n        return audios, labels","metadata":{"execution":{"iopub.status.busy":"2021-10-26T03:57:46.392015Z","iopub.execute_input":"2021-10-26T03:57:46.392416Z","iopub.status.idle":"2021-10-26T03:57:46.413702Z","shell.execute_reply.started":"2021-10-26T03:57:46.392379Z","shell.execute_reply":"2021-10-26T03:57:46.412994Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"x = np.expand_dims(df.dir.to_numpy(), -1)\ny = df.drop([\"fname\", \"labels\", \"dir\", \"labels_idx\"], axis=1).to_numpy()\nx_train, y_train, x_test, y_test = iterative_train_test_split(x, y, test_size=0.2)\n\nx_train, y_train, x_val, y_val = iterative_train_test_split(\n    x_train, y_train, test_size=0.2\n)\n\nx_train = x_train.reshape(-1)\nx_val = x_val.reshape(-1)\nx_test = x_test.reshape(-1)","metadata":{"execution":{"iopub.status.busy":"2021-10-26T03:57:46.416396Z","iopub.execute_input":"2021-10-26T03:57:46.417055Z","iopub.status.idle":"2021-10-26T03:57:46.698277Z","shell.execute_reply.started":"2021-10-26T03:57:46.417017Z","shell.execute_reply":"2021-10-26T03:57:46.6976Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataset = AudioDataset(x_train, y_train)\nval_dataset = AudioDataset(x_val, y_val)\ntest_dataset = AudioDataset(x_test, y_test)","metadata":{"execution":{"iopub.status.busy":"2021-10-26T03:57:46.699505Z","iopub.execute_input":"2021-10-26T03:57:46.699745Z","iopub.status.idle":"2021-10-26T03:57:46.703671Z","shell.execute_reply.started":"2021-10-26T03:57:46.699712Z","shell.execute_reply":"2021-10-26T03:57:46.702868Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model","metadata":{}},{"cell_type":"markdown","source":"## Definition","metadata":{}},{"cell_type":"code","source":"resnet = tf.keras.applications.ResNet50V2(\n    include_top=False,\n    weights=None,\n    input_shape=(*AudioDataset.size, 3),\n)\n\nclassifier = tf.keras.Sequential(\n    [tf.keras.layers.Flatten(name='flatten'),\n     tf.keras.layers.Dense(1024, activation='elu', name='dense_1'),\n     tf.keras.layers.Dropout(0.25, name='dropout_1'),\n     tf.keras.layers.Dense(1024, activation='elu', name='dense_2'),\n     tf.keras.layers.Dropout(0.25, name='dropout_2'),\n     tf.keras.layers.Dense(80, activation='sigmoid')],\n    name='classifier'\n)(resnet.layers[-1].output)\n\nmodel = tf.keras.Model(inputs=resnet.inputs, outputs=classifier, name='resnet')\nmodel.compile(\n    loss=\"binary_crossentropy\", \n    optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4, decay=1e-4/500), \n    metrics=[LWLRAP(80)]\n)\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2021-10-26T03:57:46.707206Z","iopub.execute_input":"2021-10-26T03:57:46.707672Z","iopub.status.idle":"2021-10-26T03:57:50.266558Z","shell.execute_reply.started":"2021-10-26T03:57:46.707636Z","shell.execute_reply":"2021-10-26T03:57:50.265781Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Training","metadata":{}},{"cell_type":"code","source":"es = tf.keras.callbacks.EarlyStopping(patience=5, restore_best_weights=True)\nlr_sched = tf.keras.callbacks.ReduceLROnPlateau(patience=2)\n\ntrain_history = model.fit(\n    train_dataset, \n    validation_data=val_dataset, \n    epochs=500, \n    callbacks=[es, lr_sched]\n).history\n\ntest_history = model.evaluate(test_dataset)","metadata":{"execution":{"iopub.status.busy":"2021-10-26T03:57:50.267957Z","iopub.execute_input":"2021-10-26T03:57:50.268669Z","iopub.status.idle":"2021-10-26T04:25:52.736511Z","shell.execute_reply.started":"2021-10-26T03:57:50.268625Z","shell.execute_reply":"2021-10-26T04:25:52.735851Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Export Prediction to CSV","metadata":{}},{"cell_type":"code","source":"prediction = model.predict(AudioDataset(df_sample_submission.dir, None))\ndf_sample_submission = df_sample_submission.drop('dir', axis=1)\ndf_sample_submission.iloc[:, 1:] = prediction\ndf_sample_submission.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2021-10-26T04:25:52.738242Z","iopub.execute_input":"2021-10-26T04:25:52.738776Z","iopub.status.idle":"2021-10-26T04:27:44.038815Z","shell.execute_reply.started":"2021-10-26T04:25:52.738738Z","shell.execute_reply":"2021-10-26T04:27:44.038009Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training Plot","metadata":{}},{"cell_type":"code","source":"loss = train_history['loss']\nval_loss = train_history['val_loss']\n\nlwrap = train_history['weighted_label_ranking_average_precision']\nval_lwrap = train_history['val_weighted_label_ranking_average_precision']\n\nlr = train_history['lr']","metadata":{"execution":{"iopub.status.busy":"2021-10-26T04:38:29.251068Z","iopub.execute_input":"2021-10-26T04:38:29.251517Z","iopub.status.idle":"2021-10-26T04:38:29.255461Z","shell.execute_reply.started":"2021-10-26T04:38:29.25148Z","shell.execute_reply":"2021-10-26T04:38:29.254776Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Loss","metadata":{}},{"cell_type":"code","source":"fig, ax = plt.subplots(figsize=(16, 4))\nax.set_title('BCE Loss')\nax.plot(range(1, len(loss) + 1), loss, label='Training')\nax.plot(range(1, len(loss) + 1), val_loss, 'o--', label='Validation')\nax.set_xlim(1, len(loss))\nax.set_xlabel(\"Epoch\")\nax.set_ylabel(\"Loss\")\nax.legend()\nfig.savefig('loss.png')","metadata":{"execution":{"iopub.status.busy":"2021-10-26T04:37:05.143832Z","iopub.execute_input":"2021-10-26T04:37:05.144506Z","iopub.status.idle":"2021-10-26T04:37:05.395449Z","shell.execute_reply.started":"2021-10-26T04:37:05.144469Z","shell.execute_reply":"2021-10-26T04:37:05.394682Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Weighted Label Ranking Average Precision","metadata":{}},{"cell_type":"code","source":"fig, ax = plt.subplots(figsize=(16, 4))\nax.set_title('Weighted Label Ranking Average Precision')\nax.plot(range(1, len(loss) + 1), lwrap, label='Training')\nax.plot(range(1, len(loss) + 1), val_lwrap, 'o--', label='Validation')\nax.set_xlim(1, len(loss))\nax.set_xlabel(\"Epoch\")\nax.set_ylabel(\"LWRAP\")\nax.legend()\nfig.savefig('lwrap.png')","metadata":{"execution":{"iopub.status.busy":"2021-10-26T04:36:36.85764Z","iopub.execute_input":"2021-10-26T04:36:36.858371Z","iopub.status.idle":"2021-10-26T04:36:37.092162Z","shell.execute_reply.started":"2021-10-26T04:36:36.858332Z","shell.execute_reply":"2021-10-26T04:36:37.091455Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Scheduler Learning Rate","metadata":{}},{"cell_type":"code","source":"fig, ax = plt.subplots(figsize=(16, 4))\nax.set_title('Scheduler Learning Rate')\nax.plot(range(1, len(lr) + 1), lr, 'o--')\nax.set_xlim(1, len(lr))\nax.set_xlabel(\"Epoch\")\nax.set_ylabel(\"Learning Rate\")\nfig.savefig('lr.png')","metadata":{"execution":{"iopub.status.busy":"2021-10-26T04:38:30.560489Z","iopub.execute_input":"2021-10-26T04:38:30.560765Z","iopub.status.idle":"2021-10-26T04:38:30.773419Z","shell.execute_reply.started":"2021-10-26T04:38:30.560734Z","shell.execute_reply":"2021-10-26T04:38:30.77268Z"},"trusted":true},"execution_count":null,"outputs":[]}]}