{"cells":[{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5"},"cell_type":"markdown","source":"# Cornell Birdcall Identification\n\n<img src=\"https://images.unsplash.com/photo-1512658740823-0ebb97b3b86e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80\" width=\"700\" height=\"300\" /><br>\n\nThe goal of this notebook is to give competitors a quick overview of the Cornell Birdcall Identification challenge. After reading it you should have a good idea of the objective you are trying to solve, the data provided and the metrics you will be scored on.\n\nSome tl;dr items to note:\n- We need to classify sounds, to understand which bird is singing.\n- The competition metric is row-wise micro averaged F1 score.\n- Train and test datasets have different structure\n\n\n<font size=3 color=\"red\">Please upvote this kernel if you like it. It motivates me to produce more quality content :)</font>\n\nTo get started, here is an excellent video about bird sound classification.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"from IPython.display import HTML\nHTML('<iframe width=\"600\" height=\"400\" src=\"https://www.youtube.com/embed/pzmdOETnhI0\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Acknowledgements\n\n1. [M5 Competition : EDA + Models](https://www.kaggle.com/tarunpaparaju/m5-competition-eda-models) ~ by Tarun Paparaju\n2. [Cornell Birdcall: EDA Basemap Geo](https://www.kaggle.com/muhakabartay/cornell-birdcall-eda-basemap-geo) ~ by Mukharbek Organokov","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"# Contents\n\n* [<font size=4>The dataset</font>](#1)\n\n* [<font size=4>EDA</font>](#2)\n    * [Make imports](#2.1)\n    * [Load the data](#2.2)\n    * [Listen to the birds](#2.3)\n    * [Sample durations histogram](#2.4)\n    * [Where do the birds come from?](#2.5)\n    * [Some wrangling on the bird songs data](#2.6)\n    * [Denoising bird sounds](#2.7)\n\n* [<font size=4>Baseline submission</font>](#3)\n\n* [<font size=4>Takeaways</font>](#4)\n\n* [<font size=4>Relevant Competitions Collection</font>](#5)\n\n* [<font size=4>Ending Note</font>](#6)","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"# The dataset <a id=\"1\"></a>\n\nThe dataset consists of .csv files and some sound recordings in the folder structure.\n\n* <code>example_test_audio/</code> - Contains examples of how the recordings look like on the notebook inference\n\n* <code>train_audio/</code> - Folder structure containing multiple recordings for many bird species\n\n* <code>train.csv</code> - Contains metadata for the train set\n\n* <code>example_test_audio_summary.csv, example_test_audio_metadata.csv, test.csv</code> - Examples of the files which will be provided to the notebook on the hidden test set run.\n\nOnce again, in this competition we are trying to classify bird sounds on multiple classes (bird species)","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"## EDA","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"### Make imports <a id=\"2.1\">","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport os\nimport plotly.express as px\nimport librosa\n\nimport pywt\nfrom statsmodels.robust import mad\n\nimport plotly.express as px\nimport plotly.graph_objects as go\nimport plotly.figure_factory as ff\nfrom plotly.subplots import make_subplots\n\n\nPATH = '../input/birdsong-recognition/'\nos.listdir(PATH)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Load the data <a id=\"2.2\">\n\nI will firstly print all the tables, so we can understand the data sources","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\n\ntrain = pd.read_csv(os.path.join(PATH, 'train.csv'))\ntest = pd.read_csv(os.path.join(PATH, 'test.csv'))\nsubmission = pd.read_csv(os.path.join(PATH, 'sample_submission.csv'))\nexample_test_audio_summary = pd.read_csv(os.path.join(PATH, 'example_test_audio_summary.csv'))\nexample_test_audio_metadata = pd.read_csv(os.path.join(PATH, 'example_test_audio_metadata.csv'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"example_test_audio_summary.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"example_test_audio_metadata.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Listen to the birds <a id=\"2.2\">\nAs can be seen below, there are 264 bird species in total. I will make an audio samples of birds' singing, to get the idea of how the training data looks like.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"birds = np.array(os.listdir(os.path.join(PATH, 'train_audio')))\nprint(f'All bird species {len(birds)}:\\n')\nprint(*birds, sep = '\\n')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import IPython.display as ipd  \nsample_birds = birds[np.random.randint(0, len(birds), 3)]\nsongs = []\nfor bird in sample_birds:\n    bird_path = np.array(os.listdir(os.path.join(os.path.join(PATH, 'train_audio', bird))))\n    bird_song = bird_path[np.random.randint(0, len(bird_path), 1)][0]\n    songs.append(os.path.join(os.path.join(PATH, 'train_audio', bird, bird_song)))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(f'{sample_birds[0]} is singing')\nipd.Audio(songs[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(f'{sample_birds[1]} is singing')\nipd.Audio(songs[1])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(f'{sample_birds[2]} is singing')\nipd.Audio(songs[2])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Sample durations histogram <a id=\"2.3\">","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = px.histogram(x=train['duration'].values, title = 'Duration of bird songs in seconds')\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Where do the birds come from? <a id=\"2.4\">\nAs you can see, most of the bird songs originate from the United States","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = px.histogram(x=train['country'].values, title = 'Countries of the birds')\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### When were these recordings made? <a id=\"2.5\">","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# you can notice a way to fill NaNs, as well as some really strange dates\ndf = train.groupby('date').apply(len)\nprint(df)\nfig = px.line(x=df.index[4:], y=df.values[4:], title='Bird song recordings wrt date')\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Some wrangling on the bird songs data <a id=\"2.6\">","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"from warnings import filterwarnings; filterwarnings('ignore')\nwaves = []\nfor i in range(3):\n    y, sr = librosa.load(songs[i])\n    wave = y[::100]\n    waves.append(y)\n    fig = px.line(x=np.arange(len(wave)), y=wave, title=f'{sample_birds[i]} song wave')\n    fig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Denoising bird sounds <a id=\"2.7\">\n* Credits go to [this excellent kernel](https://www.kaggle.com/tarunpaparaju/m5-competition-eda-models)","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"#### Wavelet transform","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def maddest(d, axis=None):\n    return np.mean(np.absolute(d - np.mean(d, axis)), axis)\n\ndef denoise_signal(x, wavelet='db4', level=1):\n    coeff = pywt.wavedec(x, wavelet, mode=\"per\")\n    sigma = (1/0.6745) * maddest(coeff[-level])\n\n    uthresh = sigma * np.sqrt(2*np.log(len(x)))\n    coeff[1:] = (pywt.threshold(i, value=uthresh, mode='hard') for i in coeff[1:])\n\n    return pywt.waverec(coeff, wavelet, mode='per')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"x_1, x_2, x_3 = waves[0][::100], waves[1][::100], waves[2][::100]\n\ny_w1 = denoise_signal(x_1)\ny_w2 = denoise_signal(x_2)\ny_w3 = denoise_signal(x_3)\n\n\nfig = make_subplots(rows=3, cols=1)\n\nfig.add_trace(\n    go.Scatter(x=np.arange(len(x_1)), mode='lines+markers', y=x_1, marker=dict(color=\"mediumaquamarine\"), showlegend=False,\n               name=\"Original signal\"),\n    row=1, col=1\n)\n\nfig.add_trace(\n    go.Scatter(x=np.arange(len(x_1)), y=y_w1, mode='lines', marker=dict(color=\"darkgreen\"), showlegend=False,\n               name=\"Denoised signal\"),\n    row=1, col=1\n)\n\nfig.add_trace(\n    go.Scatter(x=np.arange(len(x_2)), mode='lines+markers', y=x_2, marker=dict(color=\"thistle\"), showlegend=False),\n    row=2, col=1\n)\n\nfig.add_trace(\n    go.Scatter(x=np.arange(len(x_2)), y=y_w2, mode='lines', marker=dict(color=\"purple\"), showlegend=False),\n    row=2, col=1\n)\n\nfig.add_trace(\n    go.Scatter(x=np.arange(len(x_3)), mode='lines+markers', y=x_3, marker=dict(color=\"lightskyblue\"), showlegend=False),\n    row=3, col=1\n)\n\nfig.add_trace(\n    go.Scatter(x=np.arange(len(x_3)), y=y_w3, mode='lines', marker=dict(color=\"navy\"), showlegend=False),\n    row=3, col=1\n)\n\nfig.update_layout(height=1200, width=800, title_text=\"Original (pale) vs. Denoised (dark) bird sounds\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Average smoothing","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def average_smoothing(signal, kernel_size=3, stride=1):\n    sample = []\n    start = 0\n    end = kernel_size\n    while end <= len(signal):\n        start = start + stride\n        end = end + stride\n        sample.extend(np.ones(end - start)*np.mean(signal[start:end]))\n    return np.array(sample)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y_w1 = average_smoothing(x_1)\ny_w2 = average_smoothing(x_2)\ny_w3 = average_smoothing(x_3)\n\n\nfig = make_subplots(rows=3, cols=1)\n\nfig.add_trace(\n    go.Scatter(x=np.arange(len(x_1)), mode='lines+markers', y=x_1, marker=dict(color=\"mediumaquamarine\"), showlegend=False,\n               name=\"Original signal\"),\n    row=1, col=1\n)\n\nfig.add_trace(\n    go.Scatter(x=np.arange(len(x_1)), y=y_w1, mode='lines', marker=dict(color=\"darkgreen\"), showlegend=False,\n               name=\"Denoised signal\"),\n    row=1, col=1\n)\n\nfig.add_trace(\n    go.Scatter(x=np.arange(len(x_2)), mode='lines+markers', y=x_2, marker=dict(color=\"thistle\"), showlegend=False),\n    row=2, col=1\n)\n\nfig.add_trace(\n    go.Scatter(x=np.arange(len(x_2)), y=y_w2, mode='lines', marker=dict(color=\"purple\"), showlegend=False),\n    row=2, col=1\n)\n\nfig.add_trace(\n    go.Scatter(x=np.arange(len(x_3)), mode='lines+markers', y=x_3, marker=dict(color=\"lightskyblue\"), showlegend=False),\n    row=3, col=1\n)\n\nfig.add_trace(\n    go.Scatter(x=np.arange(len(x_3)), y=y_w3, mode='lines', marker=dict(color=\"navy\"), showlegend=False),\n    row=3, col=1\n)\n\nfig.update_layout(height=1200, width=800, title_text=\"Original (pale) vs. Denoised (dark) bird sounds\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Baseline model <a id=\"3\">","execution_count":null},{"metadata":{"trusted":false},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport os\nimport plotly.express as px\nimport librosa\n\nimport pywt\nfrom statsmodels.robust import mad\nfrom warnings import filterwarnings; filterwarnings('ignore')\n\nimport plotly.express as px\nimport plotly.graph_objects as go\nimport plotly.figure_factory as ff\nfrom plotly.subplots import make_subplots\nfrom scipy.io import wavfile\nimport subprocess\n\nfrom tqdm.notebook import tqdm\nimport librosa\n\nPATH = '../input/birdsong-recognition/'\nTEST_FOLDER = '../input/birdsong-recognition/test_audio/'\nos.listdir(PATH)\nRANDOM_SEED = 4444","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def load_test_clip(path, start_time, duration=5):\n    return librosa.load(path, offset=start_time, duration=duration)[0]\n\ndef make_prediction(y, le, model):\n    feats = np.array([np.min(y), np.max(y), np.mean(y), np.std(y)]).reshape(1, -1)\n    return le.inverse_transform(model.predict(feats))[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train = pd.DataFrame(columns = ['min', 'max', 'mean', 'std', 'target'])\nnRows = 0\nfor bird in tqdm(os.listdir(os.path.join(PATH, 'train_audio'))):\n    for audio in  os.listdir(os.path.join(PATH, 'train_audio', bird)):\n        path = os.path.join(PATH, 'train_audio', bird, audio)\n        subprocess.call(['ffmpeg', '-y', '-i', f'{path}', f'/kaggle/working/temp.wav'])\n        _, y = wavfile.read('/kaggle/working/temp.wav')\n        train.loc[nRows] = [np.min(y), np.max(y), np.mean(y), np.std(y), bird]\n        nRows += 1","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\nle = LabelEncoder()\ntrain['target'] = le.fit_transform(train['target'].values)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.ensemble import RandomForestClassifier\nmodel = RandomForestClassifier(max_depth = 6, random_state = RANDOM_SEED)\nmodel.fit(train.drop(columns = 'target'), train['target'].values)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"try:\n    preds = []\n    for index, row in test.iterrows():\n        # Get test row information\n        site = row['site']\n        start_time = row['seconds'] - 5\n        row_id = row['row_id']\n        audio_id = row['audio_id']\n\n        # Get the test sound clip\n        if site == 'site_1' or site == 'site_2':\n            y = load_test_clip(TEST_FOLDER + audio_id + '.mp3', start_time)\n        else:\n            y = load_test_clip(TEST_FOLDER + audio_id + '.mp3', 0, duration=None)\n\n        # Make the prediction\n        pred = make_prediction(y, le, model)\n\n        # Store prediction\n        preds.append([row_id, pred])\nexcept:\n     preds = pd.read_csv('../input/birdsong-recognition/sample_submission.csv')\npreds = pd.DataFrame(preds, columns=['row_id', 'birds'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"preds.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Takeaways <a id=\"4\">\n\n* Train and test have different structure\n* Most of the bird songs originate from the United States\n* There is a really small portion of strange dates in the data\n* From the sample bird songs waves, it seems that they can be pretty well separated","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"## Relevant Competitions Collection  <a id=\"5\">\n    \n* [Don't call me turkey](https://www.kaggle.com/c/dont-call-me-turkey)\n* [Freesound audio tagging](https://www.kaggle.com/c/freesound-audio-tagging-2019)\n* [Multi-label Bird Species Classification](https://www.kaggle.com/c/multilabel-bird-species-classification-nips2013)\n\n ","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"## Ending note <a id = \"6\">\n  \n<font color=\"red\" size=4>This concludes my kernel. This motivates a lot :)</font>","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}