{"cells":[{"metadata":{"_uuid":"ea7416e286f6eff57c64fb51c1786061e1d877b2"},"cell_type":"markdown","source":"https://www.kaggle.com/c/freesound-audio-tagging"},{"metadata":{"_uuid":"9ab0061c8dccccc7fc2cbfd6da257d3bb2d053a0"},"cell_type":"markdown","source":"# Clasificación de Audio"},{"metadata":{"trusted":true,"_uuid":"1c30c159b4af116b669743b4f0c4161e088e9ccc"},"cell_type":"code","source":"!du -sh ../input/*","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"import math\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n%matplotlib inline","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"144031dab66c74e6ea3f944a0ce310c0ea8d3527"},"cell_type":"code","source":"df = pd.read_csv('../input/train.csv')\ndf.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"1806a93b788f5b9ee935be63607b071608826d98"},"cell_type":"markdown","source":"# Preprocesando audio"},{"metadata":{"trusted":true,"_uuid":"b1a0b314059aef40a925912ca0834008a342510b"},"cell_type":"code","source":"from IPython.display import Audio\nfile = '786ee883.wav'\npath = '../input/audio_train/audio_train/'\nAudio(filename=path+file)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a341fb2b8d34ffd2b59dbe877e277b1811d32e30"},"cell_type":"code","source":"# Vamos a definir una funcion para extraer la duracion de un audio en segundos\nimport wave\n\ndef get_length(file):\n    audio = wave.open(path+file)\n    return audio.getnframes() / audio.getframerate()\n\nget_length(file)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"4adfba60bf394bd900a6c091799bbf149fde8f11"},"cell_type":"code","source":"# Vamos a procesar en paralelo la funcion en todos los archivos\nfrom joblib import Parallel, delayed\n\nwith Parallel(n_jobs=10, prefer='threads', verbose=1) as ex:\n    lengths = ex(delayed(get_length)(e) for e in df.fname)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"05a949b16f7d739a9e2a689a470df3e3e1256950"},"cell_type":"code","source":"df['length'] = lengths\ndf.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2c85c3a466eb74a27bca22a3a55f958982874c50"},"cell_type":"markdown","source":"Vamos a filtrar solo los audios que duran 6 segundos o menos."},{"metadata":{"trusted":true,"_uuid":"197fc5e1db38adeac05a42316980a3c75628a679"},"cell_type":"code","source":"df = df.query('length <= 6').reset_index(drop=True)\nprint(df.shape)\ndf.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"c84f80a8aba22d08d3df37a411769d843f4b322d"},"cell_type":"markdown","source":"# MFCC (Mel-Frequency Cepstral Coefficients)"},{"metadata":{"_uuid":"c523bf8ba11fd68806c0d874424c36aa6d561c7d"},"cell_type":"markdown","source":"Detalles:\n- http://www.speech.cs.cmu.edu/15-492/slides/03_mfcc.pdf\n- http://practicalcryptography.com/miscellaneous/machine-learning/guide-mel-frequency-cepstral-coefficients-mfccs\n\nVamos a usar la libreria librosa: https://librosa.github.io/librosa"},{"metadata":{"trusted":true,"_uuid":"5b670dab819c392a5ddf2ee8b75af386f2c74f62"},"cell_type":"code","source":"import librosa\n\ny, sr = librosa.load(path+file)\n# y : audio data\n# sr: sample rate\n\nplt.plot(y)\nplt.title(f'Sample rate = {sr}', size=18);","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"4f112af3b44ec947453ea63bcd12a523b606bdd6"},"cell_type":"code","source":"# Ahora obtengamos la representacion MFCC\nmfcc = librosa.feature.mfcc(y, sr, n_mfcc=40)\nprint(mfcc.shape)\n\nplt.figure(figsize=(10,5))\nplt.imshow(mfcc, cmap='hot');","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"fc54667fda305eaeb74b8e9c536609a1822ed52d"},"cell_type":"code","source":"# Definimos una funcion para obtener los features\ndef obtain_mfcc(file, features=40):\n    y, sr = librosa.load(path+file, res_type='kaiser_fast')\n    return librosa.feature.mfcc(y, sr, n_mfcc=features)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3edd3e25b818ca6f8e4c93a20ffe336a776d945c"},"cell_type":"code","source":"obtain_mfcc(file).shape","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2fe71dc81994e64fe8e1c3bb196813f1d8ee51de"},"cell_type":"markdown","source":"La segunda tamaño de la dimension (160) depende de la duracion del audio. Para poder tener los resultados del mismo tamaño, vamos a añadir un offset a la funcion."},{"metadata":{"trusted":true,"_uuid":"383a53d30ac77d6b6bdb59a0cb53e6ae6dd56fdb"},"cell_type":"code","source":"mfcc.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"9d24158880e91abc359acd15eb8504a65e5c7289"},"cell_type":"code","source":"def get_mfcc(file, n_mfcc=40, padding=None):\n    y, sr = librosa.load(path+file, res_type='kaiser_fast')\n    mfcc = librosa.feature.mfcc(y, sr, n_mfcc=n_mfcc)\n    if padding: mfcc = np.pad(mfcc, ((0, 0), (0, max(0, padding-mfcc.shape[1]))), 'constant')\n    return mfcc.astype(np.float32)\n\nmfcc = get_mfcc(file, padding=200)\nprint(mfcc.shape)\nplt.figure(figsize=(12,5))\nplt.imshow(mfcc, cmap='hot');","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3c04fb7a5eba136034b131a5d9e55020356dfc82"},"cell_type":"code","source":"# Veamos cuanto padding necesitamos para el archivo de mayor duracion\nprint(get_mfcc(df.sort_values('length').fname.iloc[-1]).shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"296da8f33df1732b3ab13a5794070dc2970ec0d1"},"cell_type":"code","source":"from functools import partial\n\nn_mfcc = 40\npadding = 259\nfun = partial(get_mfcc, n_mfcc=n_mfcc, padding=padding)\n\nwith Parallel(n_jobs=10, prefer='threads', verbose=1) as ex:\n    mfcc_data = ex(delayed(partial(fun))(e) for e in df.fname)\n    \n# Juntamos la data en un solo array y agregamos una dimension\nmfcc_data = np.stack(mfcc_data)[..., None]\nmfcc_data.shape","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e9fb0d1b432bcd68a152201ec8b078bd722a7348"},"cell_type":"markdown","source":"Necesitamos la matriz en esta forma (5843, 40, 259, 1), dado que vamos a utilizar convoluciones 2D, como si fuera una imagen."},{"metadata":{"_uuid":"5ae3e64b9261b21dd180d9480faa46963a6ed764"},"cell_type":"markdown","source":"# Veamos las categorias"},{"metadata":{"trusted":true,"scrolled":true,"_uuid":"50857ec50d18cd7dd209ab93d26d649fd0dac1e5"},"cell_type":"code","source":"lbl2idx = {lbl:idx for idx,lbl in enumerate(df.label.unique())}\nidx2lbl = {idx:lbl for lbl,idx in lbl2idx.items()}\nn_categories = len(lbl2idx)\nlbl2idx","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"2417a9f26986daba14d63df4db7dd26af3b0dce2"},"cell_type":"code","source":"n_categories = len(lbl2idx)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"018a6a4b1898b69d2bca4ad0a3bbbb1dac659600"},"cell_type":"code","source":"df['y'] = df.label.map(lbl2idx)\ndf.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"a4796f17d6d204ebd72d7bd960563a612d4aaf28"},"cell_type":"markdown","source":"# Train validation split"},{"metadata":{"trusted":true,"_uuid":"7079bc2b9733c97f3a7b1c28b7355f3ff87f7ff0"},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\nx_train, x_val, y_train, y_val = train_test_split(mfcc_data, df.y, test_size=0.2, random_state=42)\nx_train.shape, x_val.shape","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b574ca1c994346cf635ef52792fe01633cac9f62"},"cell_type":"markdown","source":"# Modelo"},{"metadata":{"trusted":true,"_uuid":"0ff4c35b532ac036ee12558fd31e2bca99bafcb9"},"cell_type":"code","source":"from keras.models import Model\nfrom keras.layers import Dense, Conv2D, BatchNormalization, Dropout, Input, GlobalAvgPool2D, GlobalMaxPool2D, concatenate\nfrom keras.optimizers import Adam, SGD\nimport keras.backend as K","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"e2033a28f3e6455529d7bbf4cc3f0b6b26802b6a"},"cell_type":"code","source":"bs = 128\nlr = 0.003\n\nm_in = Input([n_mfcc, padding, 1])\nx = BatchNormalization()(m_in)\n\nlayers = [10, 20, 50, 100]\nfor i,l in enumerate(layers):\n    strides = 1 if i == 0 else (2,2)\n    x = Conv2D(l, 3, strides=strides, activation='relu', padding='same',\n               use_bias=False, kernel_initializer='he_uniform')(x)\n    x = BatchNormalization()(x)\n    x = Dropout(0.02)(x)\n\nx_avg = GlobalAvgPool2D()(x)\nx_max = GlobalMaxPool2D()(x)\n\nx = concatenate([x_avg, x_max])\nx = Dense(1000, activation='relu', use_bias=False, kernel_initializer='he_uniform')(x)\nx = Dropout(0.2)(x)\nm_out = Dense(n_categories, activation='softmax')(x)\n\nmodel = Model(m_in, m_out)\nmodel.compile(Adam(lr), loss='sparse_categorical_crossentropy', metrics=['accuracy'])\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"773b1931049f0750890a1833c8b92f6ad2e04afa"},"cell_type":"markdown","source":"# Training"},{"metadata":{"trusted":true,"_uuid":"92cc84772ecd81a503b3189355d2bbbc33d2e402"},"cell_type":"code","source":"log1 = model.fit(x_train, y_train, bs, 15, validation_data=[x_val, y_val])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"68c3a967d0b60b81e50f6b3aafeddb9b818d68d9"},"cell_type":"code","source":"K.eval(model.optimizer.lr.assign(lr/10))\nlog2 = model.fit(x_train, y_train, bs, 10, validation_data=[x_val, y_val])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"f256f35bf35c7ed0d1da465ca8f3f8e716c5af8e"},"cell_type":"code","source":"def show_results(*logs):\n    trn_loss, val_loss, trn_acc, val_acc = [], [], [], []\n    \n    for log in logs:\n        trn_loss += log.history['loss']\n        val_loss += log.history['val_loss']\n        trn_acc += log.history['acc']\n        val_acc += log.history['val_acc']\n    \n    fig, axes = plt.subplots(1, 2, figsize=(14,4))\n    ax1, ax2 = axes\n    ax1.plot(trn_loss, label='train')\n    ax1.plot(val_loss, label='validation')\n    ax1.set_xlabel('epoch'); ax1.set_ylabel('loss')\n    ax2.plot(trn_acc, label='train')\n    ax2.plot(val_acc, label='validation')\n    ax2.set_xlabel('epoch'); ax2.set_ylabel('accuracy')\n    for ax,title in zip(axes, ['Train', 'Accuracy']):\n        ax.set_title(title, size=14)\n        ax.legend()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"13344a6cfb3414df193650831c1120c3fcbc9adc"},"cell_type":"code","source":"show_results(log1, log2)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3c6ab3a91cd4a3ca603ccb2492026819cc732ac5"},"cell_type":"markdown","source":"# Results"},{"metadata":{"trusted":true,"_uuid":"480af5422b695e17bcc6d3bcd224f4e7f9ad9972"},"cell_type":"code","source":"sample = df.sample()\nsample_file = sample.fname.iloc[0]\nsample_label = sample.label.iloc[0]\n\nmfcc = get_mfcc(sample_file, n_mfcc, padding)[None, ..., None]\ny_ = model.predict(mfcc)\npred = idx2lbl[np.argmax(y_)]\n\nprint(f'True       = {sample_label}')\nprint(f'Prediction = {pred}')\nAudio(path + sample_file)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}