{"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":"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\nimport 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-03-22T09:45:46.485775Z","iopub.execute_input":"2022-03-22T09:45:46.486365Z","iopub.status.idle":"2022-03-22T09:45:46.491411Z","shell.execute_reply.started":"2022-03-22T09:45:46.486324Z","shell.execute_reply":"2022-03-22T09:45:46.49063Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 1. Perform simple EDA.\n\nNOTE: In this example, I use wav files (16k, mono) from this link.https://www.kaggle.com/harveenchadha/pogmusicclassification","metadata":{}},{"cell_type":"code","source":"DATASET_PATH = '/kaggle/input/kaggle-pog-series-s01e02'\ntrain_csv = os.path.join(DATASET_PATH, 'train.csv')\n\nfor directory in os.listdir(DATASET_PATH):\n    print(directory)","metadata":{"execution":{"iopub.status.busy":"2022-03-22T09:45:46.539983Z","iopub.execute_input":"2022-03-22T09:45:46.540271Z","iopub.status.idle":"2022-03-22T09:45:46.548385Z","shell.execute_reply.started":"2022-03-22T09:45:46.540243Z","shell.execute_reply":"2022-03-22T09:45:46.547605Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Explore metadata in csv file.\n# Read csv file with pandas\nimport pandas as pd\n\ntrain_df = pd.read_csv(train_csv)\ntrain_df.head()\nprint(\"Total number of samples: \", len(train_df))","metadata":{"execution":{"iopub.status.busy":"2022-03-22T09:45:46.573814Z","iopub.execute_input":"2022-03-22T09:45:46.574278Z","iopub.status.idle":"2022-03-22T09:45:46.616498Z","shell.execute_reply.started":"2022-03-22T09:45:46.574251Z","shell.execute_reply":"2022-03-22T09:45:46.615707Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Explore files in each genres\nimport seaborn as sns\ngenre_group = train_df.groupby('genre_id')\nnum_genres = len(genre_group)\nprint(\"Total number of genres: \", num_genres)\nfor g in range(num_genres):\n    group = genre_group.get_group(g)\n    print(group['genre'].iloc[0] + ' : {}'.format(len(group)))\n\n# Using countplot in seaborn\nsns.countplot(y = 'genre', data=train_df)","metadata":{"execution":{"iopub.status.busy":"2022-03-22T09:45:46.617866Z","iopub.execute_input":"2022-03-22T09:45:46.618106Z","iopub.status.idle":"2022-03-22T09:45:47.788549Z","shell.execute_reply.started":"2022-03-22T09:45:46.618076Z","shell.execute_reply":"2022-03-22T09:45:47.787762Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Some minor genre like 'Easy Listening' just has 13 file in total ~20k trong in training set.\n# Shuffle the train data before spliting train/val\nfrom sklearn.utils import shuffle\ntrain_df = shuffle(train_df, random_state=14)","metadata":{"execution":{"iopub.status.busy":"2022-03-22T09:45:47.790262Z","iopub.execute_input":"2022-03-22T09:45:47.790508Z","iopub.status.idle":"2022-03-22T09:45:47.8771Z","shell.execute_reply.started":"2022-03-22T09:45:47.790474Z","shell.execute_reply":"2022-03-22T09:45:47.876422Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2. Prepare dataset for training\nNOTE: Some of unusual data points is refer from this [post](https://www.kaggle.com/c/kaggle-pog-series-s01e02/discussion/312842).\n1. Create csv file for wav files\n2. Split training data into train set and validation set\n3. Filter out unusual data points and not found find in csv.","metadata":{}},{"cell_type":"code","source":"train_dict = {\n    \"song_id\": [],\n    \"filepath\": [],\n    \"genre_id\": []\n}\nval_dict = {\n    \"song_id\": [],\n    \"filepath\": [],\n    \"genre_id\": []\n}\n\nNUM_FILE_PER_GENRE = 400 # get small amount of files for training\nval_portion = 0.2\nval_portion_majority = 0.1\n# ID taken from discussion.\nunusual_file = [10449, 5589, 4921, 19511, 13375, 24247, 24156, 20004, 18390,1975,8114,13437]\n# For equal division for each genres, process data in group of genre\nfor g in range(num_genres):\n    group = genre_group.get_group(g)\n    files_per_group = len(group)\n    val_num = NUM_FILE_PER_GENRE * val_portion_majority if files_per_group > NUM_FILE_PER_GENRE else files_per_group * val_portion\n    group_iter = group.iterrows()\n    for i in range(NUM_FILE_PER_GENRE):\n        try:\n            item = next(group_iter)[1]\n        except StopIteration:\n            break\n        full_filepath = os.path.join(DATASET_PATH ,item[\"filepath\"])\n        song_id = item[\"song_id\"]\n        if song_id in unusual_file:\n            print(\"Unusual file. Ignore!! \", song_id)\n            continue\n        wav_filename = \"resampled_train/\" + os.path.basename(item[\"filepath\"])[:-4] + \"_16k.wav\"\n        if i > val_num:\n            if os.path.isfile(full_filepath):\n                train_dict[\"song_id\"].append(item[\"song_id\"])\n                train_dict[\"filepath\"].append(wav_filename)\n                train_dict[\"genre_id\"].append(item[\"genre_id\"])\n            else:\n                print(\"File not found: \", full_filepath)\n                print(item[\"genre\"])\n        else:\n            if os.path.isfile(full_filepath):\n                val_dict[\"song_id\"].append(item[\"song_id\"])\n                val_dict[\"filepath\"].append(wav_filename)\n                val_dict[\"genre_id\"].append(item[\"genre_id\"])\n            else:\n                print(\"File not found: \", full_filepath)\n                print(item[\"genre\"])","metadata":{"execution":{"iopub.status.busy":"2022-03-22T09:45:47.880118Z","iopub.execute_input":"2022-03-22T09:45:47.880307Z","iopub.status.idle":"2022-03-22T09:46:06.033124Z","shell.execute_reply.started":"2022-03-22T09:45:47.880283Z","shell.execute_reply":"2022-03-22T09:46:06.032352Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Save new csv files\nfor key in train_dict.keys():\n    print(\"{} : {}\".format(key, len(train_dict[key])))\n\nfor key in val_dict.keys():\n    print(\"{} : {}\".format(key, len(train_dict[key])))\n\nout_df = pd.DataFrame.from_dict(train_dict)\nout_df = shuffle(out_df, random_state=14)\nprint(out_df.head())\nout_df.to_csv('my_train.csv')\n\nout_df = pd.DataFrame.from_dict(val_dict)\nout_df = shuffle(out_df, random_state=14)\nprint(out_df.head())\nout_df.to_csv('my_val.csv')","metadata":{"execution":{"iopub.status.busy":"2022-03-22T09:46:06.036606Z","iopub.execute_input":"2022-03-22T09:46:06.036813Z","iopub.status.idle":"2022-03-22T09:46:06.079244Z","shell.execute_reply.started":"2022-03-22T09:46:06.036787Z","shell.execute_reply":"2022-03-22T09:46:06.078445Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 3. Audio features\n## Feature include:\n1. waveform\n2. Spectrogram\n3. Melgrams","metadata":{}},{"cell_type":"code","source":"WAV_DATASET_PATH = '/kaggle/input/pogmusicclassification/'\nCSV_TRAIN = './my_train.csv'\nCSV_VAL = './my_val.csv'\n\ntrain_set = pd.read_csv(CSV_TRAIN)\nval_set = pd.read_csv(CSV_VAL)\nprint(train_set.head())\nprint(\"Total number of training samples: \", len(train_df))","metadata":{"execution":{"iopub.status.busy":"2022-03-22T09:46:06.080616Z","iopub.execute_input":"2022-03-22T09:46:06.080872Z","iopub.status.idle":"2022-03-22T09:46:06.099692Z","shell.execute_reply.started":"2022-03-22T09:46:06.080839Z","shell.execute_reply":"2022-03-22T09:46:06.099035Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import tensorflow as tf\nimport tensorflow_io as tfio\nimport matplotlib.pyplot as plt\n\nprint(WAV_DATASET_PATH + train_set[\"filepath\"].iloc[0])\ntest_file = tf.io.read_file(WAV_DATASET_PATH + train_set[\"filepath\"].iloc[0])\ntest_audio, _ = tf.audio.decode_wav(contents=test_file)\ntest_audio.shape","metadata":{"execution":{"iopub.status.busy":"2022-03-22T09:46:06.100726Z","iopub.execute_input":"2022-03-22T09:46:06.101098Z","iopub.status.idle":"2022-03-22T09:46:13.319151Z","shell.execute_reply.started":"2022-03-22T09:46:06.10106Z","shell.execute_reply":"2022-03-22T09:46:13.318333Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 3.1 Get waveform and label with tf dataset","metadata":{}},{"cell_type":"code","source":"AUTOTUNE = tf.data.AUTOTUNE\n# Define a function to preprocess the dataset's raw wav\ndef decode_audio(audio_binary):\n    audio, _ = tf.audio.decode_wav(contents=audio_binary)\n    return tf.squeeze(audio, axis=-1)\n\n# def get_label(index):\n#     return train_set[\"genre_id\"]\n\ndef get_waveform_and_label(filepath, label):\n    full_filepath = WAV_DATASET_PATH + filepath\n    audio_binary = tf.io.read_file(full_filepath)\n    waveform = decode_audio(audio_binary)\n    return waveform, label\n\nfiles_ds = tf.data.Dataset.from_tensor_slices(\n    (\n        train_set[\"filepath\"].values.tolist(),\n        train_set[\"genre_id\"].values.tolist()\n    )\n)\n\nwaveform_ds = files_ds.map(\n    map_func=get_waveform_and_label,\n    num_parallel_calls=AUTOTUNE\n)","metadata":{"execution":{"iopub.status.busy":"2022-03-22T09:46:13.320544Z","iopub.execute_input":"2022-03-22T09:46:13.320801Z","iopub.status.idle":"2022-03-22T09:46:13.476307Z","shell.execute_reply.started":"2022-03-22T09:46:13.320766Z","shell.execute_reply":"2022-03-22T09:46:13.475616Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plot a few audio waveforms\nrows = 3\ncols = 3\nn = rows * cols\nfig, axes = plt.subplots(rows, cols, figsize=(10,12))\n\nfor i, (audio, label) in enumerate(waveform_ds.take(n)):\n    r = i // cols\n    c = i % cols\n    ax = axes[r][c]\n    ax.plot(audio.numpy())\n    ax.set_yticks(np.arange(-1.2, 1.2, 0.2))\n    label = label.numpy()\n    ax.set_title(label)\n# plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-03-22T09:46:13.477808Z","iopub.execute_input":"2022-03-22T09:46:13.478058Z","iopub.status.idle":"2022-03-22T09:46:15.520086Z","shell.execute_reply.started":"2022-03-22T09:46:13.478024Z","shell.execute_reply":"2022-03-22T09:46:15.519403Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 3.2  Spectrogram","metadata":{}},{"cell_type":"code","source":"# Get spectogram\ndef get_spectrogram(waveform):\n    input_len = 16000 \n    waveform = waveform[:input_len]\n    waveform = tf.cast(waveform, dtype=tf.float32)\n    spectrogram = tf.signal.stft(waveform, frame_length=255, frame_step=128)\n    spectrogram = tf.abs(spectrogram)\n\n    spectrogram = spectrogram[..., tf.newaxis]\n    return spectrogram\n\nfor waveform, label in waveform_ds.take(1):\n    label = label.numpy()\n    spectrogram = get_spectrogram(waveform)\n\n    print('Label:', label)\n    print('Waveform shape:', waveform.shape)\n    print('Spectrogram shape:', spectrogram.shape)\n\ndef plot_spectrogram(spectrogram, ax):\n    if len(spectrogram.shape) > 2:\n        assert len(spectrogram.shape) == 3\n        spectrogram = np.squeeze(spectrogram, axis=-1)\n    # Convert the frequencies to log scale and transpose, so that the time is\n    # represented on the x-axis (columns).\n    # Add an epsilon to avoid taking a log of zero.\n    log_spec = np.log(spectrogram.T + np.finfo(float).eps)\n    height = log_spec.shape[0]\n    width = log_spec.shape[1]\n    X = np.linspace(0, np.size(spectrogram), num=width, dtype=int)\n    Y = range(height)\n    ax.pcolormesh(X, Y, log_spec)\n\nfig, axes = plt.subplots(2, figsize=(12, 8))\ntimescale = np.arange(waveform.shape[0])\naxes[0].plot(timescale, waveform.numpy())\naxes[0].set_title('Waveform')\naxes[0].set_xlim([0, 16000])\n\nplot_spectrogram(spectrogram.numpy(), axes[1])\naxes[1].set_title('Spectrogram')\n# plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-03-22T09:46:15.5212Z","iopub.execute_input":"2022-03-22T09:46:15.52148Z","iopub.status.idle":"2022-03-22T09:46:16.6724Z","shell.execute_reply.started":"2022-03-22T09:46:15.521443Z","shell.execute_reply":"2022-03-22T09:46:16.671742Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_spectrogram_and_label_id(audio, label):\n    spectrogram = get_spectrogram(audio)\n    spectrogram = tf.convert_to_tensor(spectrogram, dtype=tf.float32)\n    label_id = tf.convert_to_tensor(label, dtype=tf.int32)\n    return spectrogram, label_id\n\nspectrogram_ds = waveform_ds.map(\n    map_func=get_spectrogram_and_label_id,\n    num_parallel_calls=AUTOTUNE\n)\n\nrows = 3\ncols = 3\nn = rows*cols\nfig, axes = plt.subplots(rows, cols, figsize=(10, 10))\n\nfor i, (spectrogram, label_id) in enumerate(spectrogram_ds.take(n)):\n    r = i // cols\n    c = i % cols\n    ax = axes[r][c]\n    plot_spectrogram(spectrogram.numpy(), ax)\n    ax.set_title(str(label_id.numpy()))\n    ax.axis('off')\n\n# plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-03-22T09:46:16.67508Z","iopub.execute_input":"2022-03-22T09:46:16.675822Z","iopub.status.idle":"2022-03-22T09:46:17.566264Z","shell.execute_reply.started":"2022-03-22T09:46:16.675784Z","shell.execute_reply":"2022-03-22T09:46:17.564815Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 4. Define input pipeline Tensorflow for performance\nFrom Tensorflow tutorial. Create pipeline input for better performance.","metadata":{}},{"cell_type":"code","source":"# Replace spectrogram with melgrams as features.\nSR = 16000\nduration = 29.12\nN_MELS =  96\ndef get_melgrams(waveform):\n    input_len = int(duration * SR)\n    waveform = tf.cast(waveform, dtype=tf.float32) / 32768.0\n    if len(waveform) < input_len:\n        print(\"Short input\")\n        zero_padding = tf.zeros([input_len] - tf.shape(waveform), dtype=tf.float32)\n        waveform = tf.concat([waveform, zero_padding], 0)\n    else:\n        waveform = waveform[:input_len]\n    spectrogram = tfio.audio.spectrogram(waveform, nfft=512, window=512, stride=256)\n    mel_spectrogram = tfio.audio.melscale(spectrogram, rate=SR, mels=N_MELS, fmin=0, fmax=8000)\n    dbscale_mel_spectrogram = tfio.audio.dbscale(mel_spectrogram, top_db=80)\n    melgrams = dbscale_mel_spectrogram[..., tf.newaxis]\n    melgrams = tf.transpose(melgrams, [1,0,2])\n    return melgrams\n\ndef get_spectrogram_and_label_id(audio, label):\n    spectrogram = get_melgrams(audio)\n    spectrogram = tf.convert_to_tensor(spectrogram, dtype=tf.float32)\n    label_id = tf.convert_to_tensor(label, dtype=tf.int32)\n    return spectrogram, label_id\n\ndef preprocess_dataset(df):\n    filepath_list = df[\"filepath\"].values.tolist()\n    genre_id_list = df[\"genre_id\"].values.tolist()\n    files_ds = tf.data.Dataset.from_tensor_slices(\n        (filepath_list, genre_id_list)\n    )\n    output_ds = files_ds.map(\n        map_func=get_waveform_and_label,\n        num_parallel_calls=AUTOTUNE\n    )\n    output_ds = output_ds.map(\n        map_func=get_spectrogram_and_label_id,\n        num_parallel_calls=AUTOTUNE\n    )\n    return output_ds","metadata":{"execution":{"iopub.status.busy":"2022-03-22T09:46:17.567711Z","iopub.execute_input":"2022-03-22T09:46:17.56817Z","iopub.status.idle":"2022-03-22T09:46:17.582488Z","shell.execute_reply.started":"2022-03-22T09:46:17.568135Z","shell.execute_reply":"2022-03-22T09:46:17.581791Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Train size: \", len(train_set))\nprint(\"Val size: \", len(val_set))\n\ntrain_ds = preprocess_dataset(train_set)\nval_ds = preprocess_dataset(val_set)\n\nbatch_size = 32\ntrain_ds = train_ds.batch(batch_size)\nval_ds = val_ds.batch(batch_size)\n\ntrain_ds = train_ds.cache().prefetch(AUTOTUNE)\nval_ds = val_ds.cache().prefetch(AUTOTUNE)\n\nfor spectrogram, _ in train_ds.take(1):\n    input_shape = spectrogram.shape\nprint('Input shape: ', input_shape)","metadata":{"execution":{"iopub.status.busy":"2022-03-22T09:46:17.583882Z","iopub.execute_input":"2022-03-22T09:46:17.584362Z","iopub.status.idle":"2022-03-22T09:46:20.64901Z","shell.execute_reply.started":"2022-03-22T09:46:17.584325Z","shell.execute_reply":"2022-03-22T09:46:20.648291Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 5. Define the CRNN model\nThe model structure is prefer from this paper: https://arxiv.org/pdf/1609.04243v3.pdf","metadata":{}},{"cell_type":"code","source":"# Refer github and CRNN papers (https://arxiv.org/abs/1609.04243).\nimport tensorflow.keras.backend as K\nfrom tensorflow.keras import backend as K\nfrom tensorflow.keras.layers import Input, Dense, Dropout, Flatten, Conv2D, Permute, Reshape, \\\n                                    MaxPooling2D, ZeroPadding2D, BatchNormalization, GRU\nfrom tensorflow.keras.layers import ELU\nfrom tensorflow.keras.models import Model\n\nTIME_STEPS = 1820\ndef MusicTaggerCRNN(input_tensor=None):\n    print(\"Channel mode: \", K.image_data_format())\n\n    if K.image_data_format() == 'channels_first':\n        input_shape = (1, N_MELS, TIME_STEPS)\n    else:\n        input_shape = (N_MELS, TIME_STEPS, 1)\n    \n    if input_tensor is None:\n        melgram_input = Input(shape=input_shape)\n    else: \n        melgram_input = Input(shape=input_tensor)\n    \n    # Determine input axis\n    if K.image_data_format() == 'channels_first':\n        channel_axis = 1\n        freq_axis = 2\n        time_axis = 3\n    else:\n        channel_axis = 3\n        freq_axis = 1\n        time_axis = 2\n    dropout_para = 0.1\n\n    # Input block\n    x = BatchNormalization(axis=time_axis)(melgram_input)\n\n    # Conv block 1\n    x = Conv2D(32,(3,3) , padding='same')(x) # 64\n    x = BatchNormalization(axis=channel_axis)(x)\n    x = ELU()(x)\n    x = MaxPooling2D(pool_size=(2, 2), strides=(2, 2))(x)\n    x = Dropout(dropout_para)(x)\n\n    # Conv block 2\n    x = Conv2D(64, (3,3) , padding='same')(x) # 128\n    x = BatchNormalization(axis=channel_axis)(x)\n    x = ELU()(x)\n    x = MaxPooling2D(pool_size=(3, 3), strides=(3, 3))(x)\n    x = Dropout(dropout_para)(x)\n\n    # Conv block 3\n    x = Conv2D(128, (3,3), padding='same')(x) #128\n    x = BatchNormalization(axis=channel_axis)(x)\n    x = ELU()(x)\n    x = MaxPooling2D(pool_size=(4, 4), strides=(4, 4))(x)\n    x = Dropout(dropout_para)(x)\n\n    # Conv block 4\n    x = Conv2D(128, (3,3), padding='same')(x) # 128\n    x = BatchNormalization(axis=channel_axis)(x)\n    x = ELU()(x)\n    x = MaxPooling2D(pool_size=(4, 4), strides=(4, 4))(x)\n    x = Dropout(dropout_para)(x)\n\n    # reshaping\n    if K.image_data_format() == 'channels_first':\n        x = Permute((3, 1, 2))(x)\n    x = Reshape((-1, 128))(x)\n\n    # GRU block 1, 2, output\n    x = GRU(24, return_sequences=True)(x)\n    x = GRU(24, return_sequences=False)(x)\n    x = Dropout(0.3)(x)\n\n    # Create model\n    x = Dense(num_genres, activation='softmax')(x)\n    model = Model(melgram_input, x)\n    return model\n\nx = tf.random.uniform((1, 96, 1820, 1))\nmodel = MusicTaggerCRNN(input_tensor=None)\ny = model(x)\nprint(y.shape)","metadata":{"execution":{"iopub.status.busy":"2022-03-22T09:46:20.651895Z","iopub.execute_input":"2022-03-22T09:46:20.652113Z","iopub.status.idle":"2022-03-22T09:46:27.473551Z","shell.execute_reply.started":"2022-03-22T09:46:20.652088Z","shell.execute_reply":"2022-03-22T09:46:27.472603Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 5.1 Compile model","metadata":{}},{"cell_type":"code","source":"model = MusicTaggerCRNN(input_tensor=(N_MELS, TIME_STEPS, 1))\n\nmodel.compile(loss='sparse_categorical_crossentropy',\n                optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),\n                metrics=['accuracy'])\n\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2022-03-22T09:46:27.474801Z","iopub.execute_input":"2022-03-22T09:46:27.475155Z","iopub.status.idle":"2022-03-22T09:46:27.984446Z","shell.execute_reply.started":"2022-03-22T09:46:27.475117Z","shell.execute_reply":"2022-03-22T09:46:27.983614Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"checkpoint_filepath = './model-ckpt.h5'\ncheckpoint_callbacks = tf.keras.callbacks.ModelCheckpoint(filepath = checkpoint_filepath,save_best_only=True,\\\n                                                            monitor='val_accuracy', mode='max')\n# tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir='logs-crnn')\nEPOCHS= 40\nhistory = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=EPOCHS,\n    callbacks=[checkpoint_callbacks]\n)\nmodel.save('last_model.h5')","metadata":{"execution":{"iopub.status.busy":"2022-03-22T09:46:27.985678Z","iopub.execute_input":"2022-03-22T09:46:27.986065Z","iopub.status.idle":"2022-03-22T10:07:05.087355Z","shell.execute_reply.started":"2022-03-22T09:46:27.986027Z","shell.execute_reply":"2022-03-22T10:07:05.086656Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"metrics = history.history\nplt.plot(history.epoch, metrics['accuracy'], metrics['val_accuracy'])\nplt.legend(['accuracy', 'val_accuracy'])\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-03-22T10:07:05.088511Z","iopub.execute_input":"2022-03-22T10:07:05.088748Z","iopub.status.idle":"2022-03-22T10:07:05.283486Z","shell.execute_reply.started":"2022-03-22T10:07:05.088714Z","shell.execute_reply":"2022-03-22T10:07:05.282756Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 6. Test the model with test set.","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport os\ntest_csv = os.path.join(DATASET_PATH, 'test.csv')\ntest_df = pd.read_csv(test_csv)\n\nprint(test_df.head())\n\ntest_dict = {\n    \"song_id\": [],\n    \"filepath\": []\n}\n\nnum_tests = len(test_df)\nprint(\"Number of test data: \", num_tests)\ntest_iter = test_df.iterrows()\nfor i in range(num_tests):\n    _, item = next(test_iter)\n    full_filepath = os.path.join(DATASET_PATH, item[\"filepath\"])\n    wav_filename = \"resampled_test/\" + os.path.basename(item[\"filepath\"])[:-4] + \"_16k.wav\"\n    if not os.path.isfile(full_filepath):\n        print(\"File not found: \", full_filepath)\n        test_dict[\"song_id\"].append(item[\"song_id\"])\n        test_dict[\"filepath\"].append(\"resampled_test/007072_16k.wav\")\n    else:\n        test_dict[\"song_id\"].append(item[\"song_id\"])\n        test_dict[\"filepath\"].append(wav_filename)\n\nout_df = pd.DataFrame.from_dict(test_dict)\nprint(out_df.head())\nprint(\"Number of test file after convert: \", len(out_df))\nout_df.to_csv('my_test.csv')","metadata":{"execution":{"iopub.status.busy":"2022-03-22T10:17:09.413368Z","iopub.execute_input":"2022-03-22T10:17:09.414057Z","iopub.status.idle":"2022-03-22T10:17:12.014454Z","shell.execute_reply.started":"2022-03-22T10:17:09.414019Z","shell.execute_reply":"2022-03-22T10:17:12.013647Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Read my_test.csv and run prediction\nCSV_TEST = './my_test.csv'\ntest_df = pd.read_csv(CSV_TEST)\nprint(test_df.head())\nprint(\"Total number of samples:  \", len(test_df))","metadata":{"execution":{"iopub.status.busy":"2022-03-22T10:17:15.47801Z","iopub.execute_input":"2022-03-22T10:17:15.478575Z","iopub.status.idle":"2022-03-22T10:17:15.492686Z","shell.execute_reply.started":"2022-03-22T10:17:15.478535Z","shell.execute_reply":"2022-03-22T10:17:15.491963Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define input pipeline\n# Define a function to preprocess the dataset's raw wav\ndef decode_audio(audio_binary):\n    audio, _ = tf.audio.decode_wav(contents=audio_binary)\n    return tf.squeeze(audio, axis=-1)\n\ndef get_waveform_and_label(filepath, song_id):\n    full_filepath = WAV_DATASET_PATH + filepath\n    audio_binary = tf.io.read_file(full_filepath)\n    waveform = decode_audio(audio_binary)\n    return waveform, song_id\n\nSR = 16000\nduration = 29.12\n\ndef get_melgrams(waveform):\n    input_len = int(duration * SR)\n    waveform = waveform[:input_len]\n    waveform = tf.cast(waveform, dtype=tf.float32) / 32768.0\n    spectrogram = tfio.audio.spectrogram(waveform, nfft=512, window=512, stride=256)\n    mel_spectrogram = tfio.audio.melscale(spectrogram, rate=SR, mels=N_MELS, fmin=0, fmax=8000)\n    dbscale_mel_spectrogram = tfio.audio.dbscale(mel_spectrogram, top_db=80)\n    melgrams = dbscale_mel_spectrogram[..., tf.newaxis]\n    melgrams = tf.transpose(melgrams, [1,0,2])\n    return melgrams\n\ndef get_spectrogram_and_label_id(audio, song_id):\n    spectrogram = get_melgrams(audio)\n    spectrogram = tf.convert_to_tensor(spectrogram, dtype=tf.float32)\n    return spectrogram, song_id\n\ndef preprocess_dataset(df):\n    filepath_list = df[\"filepath\"].values.tolist()\n    song_id_list = df[\"song_id\"].values.tolist()\n    # print(\"len  filepath\", len(filepath_list))\n\n    files_ds = tf.data.Dataset.from_tensor_slices(\n        (\n            filepath_list,\n            song_id_list\n        )\n    )\n    output_ds = files_ds.map(\n        map_func=get_waveform_and_label,\n        num_parallel_calls=AUTOTUNE\n    )\n    output_ds = output_ds.map(\n        map_func=get_spectrogram_and_label_id,\n        num_parallel_calls=AUTOTUNE\n    )\n    return output_ds","metadata":{"execution":{"iopub.status.busy":"2022-03-22T10:17:17.916973Z","iopub.execute_input":"2022-03-22T10:17:17.917755Z","iopub.status.idle":"2022-03-22T10:17:17.929253Z","shell.execute_reply.started":"2022-03-22T10:17:17.917718Z","shell.execute_reply":"2022-03-22T10:17:17.928522Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_ds = preprocess_dataset(test_df)\n\n# batch_size = 64\ntest_ds = test_ds.batch(1)\n\ntest_ds = test_ds.cache().prefetch(AUTOTUNE)\n\nfor spectrogram, song_id in test_ds.take(1):\n    input_shape = spectrogram.shape\n    print('Input shape: ', input_shape)\n    print('Song ID: ', song_id)","metadata":{"execution":{"iopub.status.busy":"2022-03-22T10:17:23.944068Z","iopub.execute_input":"2022-03-22T10:17:23.94483Z","iopub.status.idle":"2022-03-22T10:17:24.732962Z","shell.execute_reply.started":"2022-03-22T10:17:23.944777Z","shell.execute_reply":"2022-03-22T10:17:24.732192Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Load the model\nmodel = tf.keras.models.load_model('model-ckpt.h5')\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2022-03-22T10:21:52.515894Z","iopub.execute_input":"2022-03-22T10:21:52.516186Z","iopub.status.idle":"2022-03-22T10:21:53.083886Z","shell.execute_reply.started":"2022-03-22T10:21:52.516156Z","shell.execute_reply":"2022-03-22T10:21:53.083061Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Run prediction for every files\nresult_dict = {\n    \"song_id\": [],\n    \"genre_id\": []\n}\n\nfor melgrams, song_id in test_ds:\n    # print(melgrams.shape)\n    # Follow discussion on: https://www.kaggle.com/c/kaggle-pog-series-s01e02/discussion/312486\n    if song_id == 22612:\n        print(song_id)\n        result_dict[\"song_id\"].append(song_id.numpy()[0])\n        result_dict[\"genre_id\"].append(1) \n        continue\n    elif song_id == 24013:\n        print(song_id)\n        result_dict[\"song_id\"].append(song_id.numpy()[0])\n        result_dict[\"genre_id\"].append(0) \n        continue\n\n    # melgrams = tf.expand_dims(melgrams, axis=0)\n    pred = model(melgrams) # predict shape (1, 19)\n    pred_id = np.argmax(pred, axis=1)[0]\n    result_dict[\"song_id\"].append(song_id.numpy()[0])\n    result_dict[\"genre_id\"].append(pred_id)","metadata":{"execution":{"iopub.status.busy":"2022-03-22T10:21:59.202428Z","iopub.execute_input":"2022-03-22T10:21:59.202688Z","iopub.status.idle":"2022-03-22T10:28:52.595813Z","shell.execute_reply.started":"2022-03-22T10:21:59.202657Z","shell.execute_reply":"2022-03-22T10:28:52.594148Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Output submission file\nsubmission = pd.DataFrame.from_dict(result_dict)\nprint(submission.head())\nprint(len(submission))\nsubmission.to_csv(\"submission.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2022-03-22T10:28:52.624798Z","iopub.execute_input":"2022-03-22T10:28:52.625123Z","iopub.status.idle":"2022-03-22T10:28:52.647969Z","shell.execute_reply.started":"2022-03-22T10:28:52.625089Z","shell.execute_reply":"2022-03-22T10:28:52.647319Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## COMMENT:\nModel is overfiting, maybe because of some reasons:\n1. Model is  complicated, expected to work better with more training data.\n2. Training data is not large enough.\n3. Problem when random split train and val.\n...\n\nContinued...\n\nIF anyone find this useful,or any problems please comment below for further discussion.","metadata":{}}]}