{"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":"import os\nimport warnings\nwarnings.filterwarnings(action='ignore')\nimport pandas as pd\nimport librosa\nimport numpy as np\n\nfrom sklearn.utils import shuffle\nfrom PIL import Image\nfrom tqdm import tqdm\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\nimport librosa.display\n\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.cluster import DBSCAN\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom sklearn.cluster import KMeans\nimport matplotlib.pyplot as pp\n\n# Global vars\nRANDOM_SEED = 1337\nSAMPLE_RATE = 32000\nSIGNAL_LENGTH = 5 # seconds\nSPEC_SHAPE = (48, 128) # height x width\nFMIN = 500\nFMAX = 12500\nMAX_AUDIO_FILES = 1500","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-06-15T18:22:34.761277Z","iopub.execute_input":"2021-06-15T18:22:34.761614Z","iopub.status.idle":"2021-06-15T18:22:34.768889Z","shell.execute_reply.started":"2021-06-15T18:22:34.761584Z","shell.execute_reply":"2021-06-15T18:22:34.767896Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# data loading","metadata":{}},{"cell_type":"code","source":"train = pd.read_csv('../input/birdclef-2021/train_metadata.csv',)\nlist(train.columns)\n# Code adapted from: \n# https://www.kaggle.com/frlemarchand/bird-song-classification-using-an-efficientnet\n# Make sure to check out the entire notebook.\n# Limit the number of training samples and classes\n# First, only use high quality samples\ntrain = train.query('rating>=4')\n# Second, assume that birds with the most training samples are also the most common\n# A species needs at least 150 recordings with a rating above 4 to be considered common\nbirds_count = {}\nfor bird_species, count in zip(train.primary_label.unique(), \n                               train.groupby('primary_label')['primary_label'].count().values):\n    birds_count[bird_species] = count\nmost_represented_birds = [key for key,value in birds_count.items() if value >= 200]\n\nTRAIN = train.query('primary_label in @most_represented_birds')\nLABELS = sorted(TRAIN.primary_label.unique())\n# # Let's see how many species and samples we have left\nprint('NUMBER OF SPECIES IN TRAIN DATA:', len(LABELS))\nprint('NUMBER OF SAMPLES IN TRAIN DATA:', len(TRAIN))\nprint('LABELS:', most_represented_birds)","metadata":{"execution":{"iopub.status.busy":"2021-06-15T18:22:38.424561Z","iopub.execute_input":"2021-06-15T18:22:38.424962Z","iopub.status.idle":"2021-06-15T18:22:38.889462Z","shell.execute_reply.started":"2021-06-15T18:22:38.424926Z","shell.execute_reply":"2021-06-15T18:22:38.887064Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# data visualization","metadata":{}},{"cell_type":"code","source":"# # visualize the dataset after cleaning to determine the number of clusters\n# fig,ax=plt.subplots(figsize=(15,10))\n# sns.scatterplot('longitude', 'latitude', data=TRAIN, hue='primary_label',ax=ax)\n# TRAIN_pos = TRAIN[['latitude','longitude']]\n# TRAIN_pos_norm = StandardScaler().fit_transform(TRAIN_pos)\n# print(\"The shape of TRAIN_pos_norm is\",TRAIN_pos_norm.shape)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"# clustering","metadata":{}},{"cell_type":"code","source":"# # perform DBSCAN\n# db = DBSCAN(eps = 0.6,\n#              min_samples = 20,\n#              algorithm = 'ball_tree',\n#              metric = 'euclidean').fit(TRAIN_pos_norm)\n# cluster_labels = db.labels_\n# num_clusters = len(set(cluster_labels))\n# print(\"The number of cluster is: \", num_clusters)\n# TRAIN['DBSCAN_labels'] = cluster_labels\n# TRAIN['DBSCAN_labels'].value_counts().sort_index()\n# # visualize the cluster\n# fig,ax=plt.subplots(figsize=(15,10))\n# sns.scatterplot('longitude', 'latitude', data=TRAIN, hue='DBSCAN_labels',ax=ax)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # k-means\n# model = KMeans(n_clusters=2)\n# model.fit(TRAIN_pos_norm)\n# kmeans_cluster_labels = model.labels_\n# TRAIN['Kmeans_labels'] = kmeans_cluster_labels\n# print(\"Number of clusters and its count: \\n\", TRAIN['Kmeans_labels'].value_counts().sort_index())\n# # visualize the cluster\n# fig,ax=plt.subplots(figsize=(15,10))\n# x = [-75.85,-84.51,-119.95,-76.45]\n# y = [5.57,10.12,38.49,42.47]\n# location = [\"Jardín, Departamento de Antioquia Colombia\", \"Alajuela, San Ramón Costa Rica\", \"Sierra Nevada, California USA\", \"Ithaca, New York USA\"]\n# ax.scatter(x, y)\n# # train.plot(kind=\"scatter\", x=\"longitude\", y=\"latitude\", alpha=0.4,ax=ax)\n# sns.scatterplot('longitude', 'latitude', data=TRAIN, hue='Kmeans_labels',ax=ax)\n\n# plt.scatter(x, y, color=\"red\")\n# for i, txt in enumerate(location):\n#     ax.annotate(txt, (x[i], y[i]))\n\n# plt.legend([],[], frameon=False)\n# plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# fig,ax=plt.subplots(figsize=(15,10))\n# sns.scatterplot('longitude', 'latitude', hue=\"Kmeans_labels\",data=TRAIN,ax=ax)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# TRAIN_ = TRAIN[TRAIN.Kmeans_labels == 1]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# TRAIN_ = TRAIN[TRAIN.Kmeans_labels == 0]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# extract audio features","metadata":{}},{"cell_type":"code","source":"# features = np.empty((0, 60))\n# #sig, rate = librosa.load(\"../input/birdclef-2021/train_short_audio/acafly/XC11209.ogg\", sr=SAMPLE_RATE, offset=None)\n# sig, rate = librosa.load(\"../input/birdclef-2021/train_short_audio/acafly/XC109605.ogg\",sr=SAMPLE_RATE, offset=None)\n# chunk = sig\n# hop_length = int(SIGNAL_LENGTH * SAMPLE_RATE / (SPEC_SHAPE[1] - 1))\n        \n# # zero crossing rate\n# zero_crossing_rate = librosa.feature.zero_crossing_rate(y=chunk, frame_length=2048, hop_length=hop_length)\n# print(\"zero crossing rate shape: \")\n# print(zero_crossing_rate.shape)\n        \n    \n# # librosa.feature.spectral_flatness\n# spec_flat = librosa.feature.spectral_flatness(y=chunk, S=None,n_fft=1024, hop_length=hop_length, win_length=1024)\n# print(\"spectral flatness shape: \")\n# print(spec_flat.shape)\n        \n# # spectral centroids\n# spectral_centroids = librosa.feature.spectral_centroid(y=chunk, sr=SAMPLE_RATE,n_fft=1024,hop_length=hop_length)\n# print(\"spectral centroids shape: \")\n# print(spectral_centroids.shape)\n        \n# # spectral rolloff\n# spectral_rolloff = librosa.feature.spectral_rolloff(y=chunk, sr=SAMPLE_RATE,n_fft=1024,hop_length=hop_length)\n# print(\"spectral rolloff shape: \")\n# print(spectral_rolloff.shape)\n\n# # spectral contrast\n# spectral_contrast = librosa.feature.spectral_contrast(y=chunk, sr=SAMPLE_RATE,n_fft=1024, hop_length=hop_length)\n# print(\"spectral contrast shape: \")\n# print(spectral_contrast.shape)\n        \n# # melspectralgram\n# mel_spec = librosa.feature.melspectrogram(y=chunk,sr=SAMPLE_RATE, n_fft=1024, hop_length=hop_length, n_mels=SPEC_SHAPE[0],fmin=FMIN, fmax=FMAX)\n\n# mel_spec = librosa.power_to_db(mel_spec, ref=np.max) \n# # Normalize\n# mel_spec -= mel_spec.min()\n# mel_spec /= mel_spec.max()\n# print(\"melspectralgram shape: \")\n# print(mel_spec.shape)\n\n# chroma_stft = librosa.feature.chroma_stft(y=chunk, \n#                                           sr=SAMPLE_RATE,\n#                                           n_fft=1024, \n#                                           hop_length=hop_length)\n# # print(\"chroma_stft shape: \")\n# # print(chroma_stft.shape)\n# labels = np.full((1,mel_spec.shape[1]), \"label\")\n# print(\"labels\")\n# print(labels.shape)\n# f=np.concatenate((np.transpose(zero_crossing_rate),\n#                   np.transpose(spec_flat),\n#                   np.transpose(spectral_centroids),\n#                   np.transpose(spectral_rolloff),\n#                   np.transpose(spectral_contrast),\n#                   np.transpose(mel_spec),\n#                   np.transpose(labels)),\n#                   axis=1)\n# print(\"f shape: \")\n# print(f.shape)    \n# features = np.concatenate((features,f),axis=0)\n# print(\"features shape: \")\n# print(features.shape)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"get_features(\"../input/birdclef-2021/train_short_audio/acafly/XC109605.ogg\", \"xz\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# get spectral features\ndef get_features(filepath, primary_label):\n    features = np.empty((0, 60))\n    # Open the file with librosa\n    sig, rate = librosa.load(filepath, sr=SAMPLE_RATE, offset=None,duration=15)\n    \n    # Split signal into five second chunks\n    sig_splits = []\n    for i in range(0, len(sig), int(SIGNAL_LENGTH * SAMPLE_RATE)):\n        split = sig[i:i + int(SIGNAL_LENGTH * SAMPLE_RATE)]\n\n        # End of signal?\n        if len(split) < int(SIGNAL_LENGTH * SAMPLE_RATE):\n            break\n        \n        sig_splits.append(split)\n        \n \n    for chunk in sig_splits:\n        hop_length = int(SIGNAL_LENGTH * SAMPLE_RATE / (SPEC_SHAPE[1] - 1))\n        \n        # zero crossing rate\n        zero_crossing_rate = librosa.feature.zero_crossing_rate(y=chunk, frame_length=2048, hop_length=hop_length)\n#         print(zero_crossing_rate.shape)\n        # librosa.feature.spectral_flatness\n        spec_flat = librosa.feature.spectral_flatness(y=chunk, S=None,n_fft=1024, hop_length=hop_length, win_length=1024)\n        \n        # spectral centroids\n        spectral_centroids = librosa.feature.spectral_centroid(y=chunk, sr=SAMPLE_RATE,n_fft=1024,hop_length=hop_length)\n        \n        # spectral rolloff\n        spectral_rolloff = librosa.feature.spectral_rolloff(y=chunk, sr=SAMPLE_RATE,n_fft=1024,hop_length=hop_length)\n        \n        # spectral contrast\n        spectral_contrast = librosa.feature.spectral_contrast(y=chunk, sr=SAMPLE_RATE,n_fft=1024, hop_length=hop_length)\n        \n        # melspectralgram\n        mel_spec = librosa.feature.melspectrogram(y=chunk, \n                                                  sr=SAMPLE_RATE, \n                                                  n_fft=1024, \n                                                  hop_length=hop_length, \n                                                  n_mels=SPEC_SHAPE[0], \n                                                  fmin=FMIN, \n                                                  fmax=FMAX)\n    \n        mel_spec = librosa.power_to_db(mel_spec, ref=np.max) \n        # Normalize\n        mel_spec -= mel_spec.min()\n        mel_spec /= mel_spec.max()\n        \n        labels = np.full((1,mel_spec.shape[1]), primary_label)\n        \n        f=np.concatenate((np.transpose(zero_crossing_rate),\n                  np.transpose(spec_flat),\n                  np.transpose(spectral_centroids),\n                  np.transpose(spectral_rolloff),\n                  np.transpose(spectral_contrast),\n                  np.transpose(mel_spec),\n                  np.transpose(labels)),\n                  axis=1)\n        features = np.concatenate((features,f),axis=0)\n#         print(features.shape)\n    return(features)","metadata":{"execution":{"iopub.status.busy":"2021-06-15T18:22:53.322312Z","iopub.execute_input":"2021-06-15T18:22:53.322650Z","iopub.status.idle":"2021-06-15T18:22:53.335236Z","shell.execute_reply.started":"2021-06-15T18:22:53.322621Z","shell.execute_reply":"2021-06-15T18:22:53.334252Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # Define a function that splits an audio file, \n# # extracts spectrograms and saves them in a working directory\n# def get_spectrograms(filepath, primary_label, output_dir):\n    \n#     # Open the file with librosa (limited to the first 60 seconds)\n#     sig, rate = librosa.load(filepath, sr=SAMPLE_RATE, offset=None)\n    \n#     # Split signal into five second chunks\n#     sig_splits = []\n#     for i in range(0, len(sig), int(SIGNAL_LENGTH * SAMPLE_RATE)):\n#         split = sig[i:i + int(SIGNAL_LENGTH * SAMPLE_RATE)]\n\n#         # End of signal?\n#         if len(split) < int(SIGNAL_LENGTH * SAMPLE_RATE):\n#             break\n        \n#         sig_splits.append(split)\n        \n#     # Extract mel spectrograms for each audio chunk\n#     s_cnt = 0\n#     saved_samples = []\n#     for chunk in sig_splits:\n#         hop_length = int(SIGNAL_LENGTH * SAMPLE_RATE / (SPEC_SHAPE[1] - 1))\n#         # librosa.feature.spectral_flatness\n        \n# #         spec_flat = librosa.feature.spectral_flatness(y=chunk, \n# #                                                       S=None, \n# #                                                       n_fft=1024, \n# #                                                       hop_length=hop_length, \n# #                                                       win_length=1024, \n# #                                                       window='hann', \n# #                                                       center=True, \n# #                                                       pad_mode='reflect', \n# #                                                       amin=1e-10, \n# #                                                       power=2.0)\n        \n        \n# #         spec_flat_mean = np.mean(spec_flat)\n# #         a = spec_flat[0]\n# #         #sns.scatterplot(a, list(range(len(a))))\n# #         spec_flat_bag.append(spec_flat[0])\n        \n#         mel_spec = librosa.feature.melspectrogram(y=chunk, \n#                                                   sr=SAMPLE_RATE, \n#                                                   n_fft=1024, \n#                                                   hop_length=hop_length, \n#                                                   n_mels=SPEC_SHAPE[0], \n#                                                   fmin=FMIN, \n#                                                   fmax=FMAX)\n    \n#         mel_spec = librosa.power_to_db(mel_spec, ref=np.max) \n        \n#         # Normalize\n#         mel_spec -= mel_spec.min()\n#         mel_spec /= mel_spec.max()\n            \n#         # display spectrum\n# #         plt.figure(figsize=(15, 5))\n# #         librosa.display.specshow(mel_spec, \n# #                         sr=32000, \n# #                         hop_length=hop_length, \n# #                         x_axis='time', \n# #                         y_axis='mel',\n# #                         fmin=FMIN, \n# #                         fmax=FMAX, \n# #                         cmap=plt.get_cmap('viridis'))\n         \n            \n#         # Save as image file\n#         save_dir = os.path.join(output_dir, primary_label)\n#         if not os.path.exists(save_dir):\n#             os.makedirs(save_dir)\n#         save_path = os.path.join(save_dir, filepath.rsplit(os.sep, 1)[-1].rsplit('.', 1)[0] + \n#                                 '_' + str(s_cnt) + '.png')\n#         im = Image.fromarray(mel_spec * 255.0).convert(\"L\")\n#         im.save(save_path)\n        \n#         saved_samples.append(save_path)\n#         s_cnt += 1\n        \n# #         save_path_bag.append(save_path)\n        \n#     return saved_samples\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"TRAIN_1 = TRAIN\nTRAIN_1 = shuffle(TRAIN_1, random_state=RANDOM_SEED)\n# Parse audio files and extract training samples\ninput_dir = '../input/birdclef-2021/train_short_audio/'\n# output_dir = '../working/melspectrogram_dataset/cluster0/'","metadata":{"execution":{"iopub.status.busy":"2021-06-15T18:22:58.624308Z","iopub.execute_input":"2021-06-15T18:22:58.624644Z","iopub.status.idle":"2021-06-15T18:22:58.633964Z","shell.execute_reply.started":"2021-06-15T18:22:58.624612Z","shell.execute_reply":"2021-06-15T18:22:58.632994Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nos.remove(\"../working/features.csv\")","metadata":{"execution":{"iopub.status.busy":"2021-06-15T18:42:15.765778Z","iopub.execute_input":"2021-06-15T18:42:15.766120Z","iopub.status.idle":"2021-06-15T18:42:15.773609Z","shell.execute_reply.started":"2021-06-15T18:42:15.766092Z","shell.execute_reply":"2021-06-15T18:42:15.772612Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np        \nf=open('asd.dat','ab')\nfor iind in range(4):\n    a=np.random.rand(10,10)\n    np.savetxt(f,a)\nf.close()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import csv\nf = open('../working/features.csv','ab')\nfor idx, row in TRAIN_1.iterrows():\n    audio_file_path = os.path.join(input_dir, row.primary_label, row.filename)\n    features = get_features(audio_file_path,row.primary_label)\n    np.savetxt(f,features,delimiter=\",\", fmt='%s')\nf.close","metadata":{"execution":{"iopub.status.busy":"2021-06-15T18:42:41.731694Z","iopub.execute_input":"2021-06-15T18:42:41.732155Z","iopub.status.idle":"2021-06-15T18:42:50.105452Z","shell.execute_reply.started":"2021-06-15T18:42:41.732120Z","shell.execute_reply":"2021-06-15T18:42:50.103571Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pd_df.shape","metadata":{"execution":{"iopub.status.busy":"2021-06-15T18:27:49.246028Z","iopub.execute_input":"2021-06-15T18:27:49.246391Z","iopub.status.idle":"2021-06-15T18:27:49.252278Z","shell.execute_reply.started":"2021-06-15T18:27:49.246358Z","shell.execute_reply":"2021-06-15T18:27:49.251130Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"chunk = pd.read_csv('../working/features.csv',chunksize=1000000)\npd_df = pd.concat(chunk)","metadata":{"execution":{"iopub.status.busy":"2021-06-15T18:23:23.570296Z","iopub.execute_input":"2021-06-15T18:23:23.570628Z","iopub.status.idle":"2021-06-15T18:23:23.819625Z","shell.execute_reply.started":"2021-06-15T18:23:23.570598Z","shell.execute_reply":"2021-06-15T18:23:23.818809Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pd_df.iloc[1,:]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from dask import dataframe as dd\nstart = time.time()\ndask_df = dd.read_csv('huge_data.csv')\nend = time.time()\nprint(\"Read csv with dask: \",(end-start),\"sec\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"features = np.empty((0, 60))\nwith tqdm(total=len(TRAIN_1)) as pbar:\n    for idx, row in TRAIN_1.iterrows():\n        pbar.update(1)\n        audio_file_path = os.path.join(input_dir, row.primary_label, row.filename)\n        features = np.concatenate((features,get_features(audio_file_path,row.primary_label)),axis=0)\n            \n# TRAIN_SPECS_1 = shuffle(samples_1, random_state=RANDOM_SEED)\n# print('SUCCESSFULLY EXTRACTED {} SPECTROGRAMS'.format(len(TRAIN_SPECS_1)))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(features.shape)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\ny = features[:, -1] # for last column\nX = features[:, :-1] # for all but last column\nX_train, X_test, Y_train_label, Y_test_label = train_test_split(X, y, test_size=0.25, random_state=RANDOM_SEED)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Transforming non numerical labels into numerical labels\nfrom sklearn import preprocessing\nencoder = preprocessing.LabelEncoder()\n\n# encoding train labels \nencoder.fit(Y_train_label)\nY_train = encoder.transform(Y_train_label)\n\n# encoding test labels \nencoder.fit(Y_test_label)\nY_test = encoder.transform(Y_test_label)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Scaling the Train and Test feature set \nfrom sklearn.preprocessing import StandardScaler\nscaler = StandardScaler()\nX_train_scaled = scaler.fit_transform(X_train)\nX_test_scaled = scaler.transform(X_test)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Libraries to Build Ensemble Model : Random Forest Classifier \n# Create the parameter grid based on the results of random search \nparams_grid = [{'kernel': ['rbf'], 'gamma': [1e-3, 1e-4],\n                     'C': [1, 10, 100, 1000]},\n                    {'kernel': ['linear'], 'C': [1, 10, 100, 1000]}]\n# Performing CV to tune parameters for best SVM fit \nsvm_model = GridSearchCV(SVC(), params_grid, cv=5)\nsvm_model.fit(X_train_scaled, Y_train)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# View the accuracy score\nprint('Best score for training data:', svm_model.best_score_,\"\\n\") \n\n# View the best parameters for the model found using grid search\nprint('Best C:',svm_model.best_estimator_.C,\"\\n\") \nprint('Best Kernel:',svm_model.best_estimator_.kernel,\"\\n\")\nprint('Best Gamma:',svm_model.best_estimator_.gamma,\"\\n\")\n\nfinal_model = svm_model.best_estimator_\nY_pred = final_model.predict(X_test_scaled)\nY_pred_label = list(encoder.inverse_transform(Y_pred))","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Making the Confusion Matrix\n#print(pd.crosstab(Y_test_label, Y_pred_label, rownames=['Actual Activity'], colnames=['Predicted Activity']))\nprint(confusion_matrix(Y_test_label,Y_pred_label))\nprint(\"\\n\")\nprint(classification_report(Y_test_label,Y_pred_label))\n\nprint(\"Training set score for SVM: %f\" % final_model.score(X_train_scaled , Y_train))\nprint(\"Testing  set score for SVM: %f\" % final_model.score(X_test_scaled  , Y_test ))\n\nsvm_model.score","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# samples_1 = []\n# with tqdm(total=len(TRAIN_1)) as pbar:\n#     for idx, row in TRAIN_1.iterrows():\n#         pbar.update(1)\n#         audio_file_path = os.path.join(input_dir, row.primary_label, row.filename)\n#         samples_1 += get_spectrograms(audio_file_path, row.primary_label, output_dir)\n            \n# TRAIN_SPECS_1 = shuffle(samples_1, random_state=RANDOM_SEED)\n# print('SUCCESSFULLY EXTRACTED {} SPECTROGRAMS'.format(len(TRAIN_SPECS_1)))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Nice! These are good samples. Notice how some of them only contain a fraction of a bird call? That's an issue we won't deal with in this tutorial. We will simply ignore the fact that samples might not contain any bird sounds.\n\n# 4. Load training samples\n\nFor now, our spectrograms reside in a working directory. If we want to train a model, we have to load them into memory. Yet, with potentially hundreds of thousands of extracted spectrograms, an in-memory dataset is not a good idea. But for now, loading samples from disk and combining them into a large NumPy array is fine. It’s the easiest way to use these data for training with Keras.","metadata":{}},{"cell_type":"code","source":"# Parse all samples and add spectrograms into train data, primary_labels into label data\ntrain_specs_1, train_labels_1 = [], []\nwith tqdm(total=len(TRAIN_SPECS_1)) as pbar:\n    for path in TRAIN_SPECS_1:\n        pbar.update(1)\n        spec = Image.open(path) # Open image\n        spec = np.array(spec, dtype='float32') # Convert to numpy array\n        spec -= spec.min()  # Normalize between 0.0 and 1.0 and exclude samples with nan \n        spec /= spec.max()\n        if not spec.max() == 1.0 or not spec.min() == 0.0:\n            continue\n        spec = np.expand_dims(spec, -1) # Add channel axis to 2D array\n        spec = np.expand_dims(spec, 0)  # Add new dimension for batch size\n        if len(train_specs_1) == 0: #  # Add to train data\n            train_specs_1 = spec\n        else:\n            train_specs_1 = np.vstack((train_specs_1, spec))\n        target = np.zeros((len(LABELS)), dtype='float32') # Add to label data\n        bird = path.split(os.sep)[-2]\n        target[LABELS.index(bird)] = 1.0\n        if len(train_labels_1) == 0:\n            train_labels_1 = target\n        else:\n            train_labels_1 = np.vstack((train_labels_1, target))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"train_specs_1.shape","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 5. Build a simple model\n\nAlright, our dataset is ready, now we need to define a model architecture. In this tutorial, we’ll use a very simplistic, AlexNet-like design with four convolutional layers and three dense layers. It might make sense to choose an off-the-shelve TF model that was pre-trained on audio data, but we would need to adjust the inputs (i.e., the resolution of our spectrograms) to fit the external model. So we keep it simple and build our own model.","metadata":{}},{"cell_type":"code","source":"# Make sure your experiments are reproducible\ntf.random.set_seed(RANDOM_SEED)\n\n# Build a simple model as a sequence of  convolutional blocks.\n# Each block has the sequence CONV --> RELU --> BNORM --> MAXPOOL.\n# Finally, perform global average pooling and add 2 dense layers.\n# The last layer is our classification layer and is softmax activated.\n# (Well it's a multi-label task so sigmoid might actually be a better choice)\nmodel = tf.keras.Sequential([\n    \n    # First conv block\n    tf.keras.layers.Conv2D(16, (3, 3), activation='relu', \n                           input_shape=(SPEC_SHAPE[0], SPEC_SHAPE[1], 1)),\n    tf.keras.layers.BatchNormalization(),\n    tf.keras.layers.MaxPooling2D((2, 2)),\n    \n    # Second conv block\n    tf.keras.layers.Conv2D(32, (3, 3), activation='relu'),\n    tf.keras.layers.BatchNormalization(),\n    tf.keras.layers.MaxPooling2D((2, 2)), \n    \n    # Third conv block\n    tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),\n    tf.keras.layers.BatchNormalization(),\n    tf.keras.layers.MaxPooling2D((2, 2)), \n    \n    # Fourth conv block\n    tf.keras.layers.Conv2D(128, (3, 3), activation='relu'),\n    tf.keras.layers.BatchNormalization(),\n    tf.keras.layers.MaxPooling2D((2, 2)),\n    \n    # Global pooling instead of flatten()\n    tf.keras.layers.GlobalAveragePooling2D(), \n    \n    # Dense block\n    tf.keras.layers.Dense(256, activation='relu'),   \n    tf.keras.layers.Dropout(0.5),  \n    tf.keras.layers.Dense(256, activation='relu'),   \n    tf.keras.layers.Dropout(0.5),\n    \n    # Classification layer\n    tf.keras.layers.Dense(len(LABELS), activation='softmax')\n])\nprint('MODEL HAS {} PARAMETERS.'.format(model.count_params()))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This is not a huge CNN, it only has ~200,000 parameters. Yet, we also only have a very small dataset with just 27 classes.\n\nNext, we need to specify an optimzer, initial learning rate, a loss function and a metric.","metadata":{}},{"cell_type":"code","source":"# Compile the model and specify optimizer, loss and metric\nmodel.compile(optimizer=tf.keras.optimizers.Adam(lr=0.001),\n              loss=tf.keras.losses.CategoricalCrossentropy(label_smoothing=0.01),\n              metrics=['accuracy'])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Callbacks make our life easier, the three that we're adding will take care of saving the best checkpoint, they will reduce the learning rate whenever the training process stalls, and they will stop the training if the model is overfitting.","metadata":{}},{"cell_type":"code","source":"# Add callbacks to reduce the learning rate if needed, early stopping, and checkpoint saving\ncallbacks = [tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', \n                                                  patience=2, \n                                                  verbose=1, \n                                                  factor=0.5),\n             tf.keras.callbacks.EarlyStopping(monitor='val_loss', \n                                              verbose=1,\n                                              patience=5),\n             tf.keras.callbacks.ModelCheckpoint(filepath='best_model.h5', \n                                                monitor='val_loss',\n                                                verbose=0,\n                                                save_best_only=True)]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Here we go, everything is in place, let's train a model. We'll use 20% of our training data for validation and we'll stop after 25 epochs.","metadata":{}},{"cell_type":"code","source":"# Let's train the model for a few epochs\nmodel.fit(train_specs_1, \n          train_labels_1,\n          batch_size=32,\n          validation_split=0.2,\n          callbacks=callbacks,\n          epochs=50)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\n# 6. Soundscape analysis\n\nIn this tutorial, we will simply pick a soundscape from the training data, but the overall process can easily be automated and then applied to all soundscape files. And again, we have to load a file with Librosa, extract spectrograms for 5-second chunks, pass each chunk through the model and eventually assign a label to the 5-second audio chunk.\n\nLet's use a soundscape that actually contains some of the species that we trained our model for. The file \"28933_SSW_20170408.ogg\" seems to contain a lot of Song Sparrow (sonspa) vocalizations, let's try this one then.","metadata":{}},{"cell_type":"code","source":"# Load the best checkpoint\nmodel = tf.keras.models.load_model('best_model.h5')\n\n# Pick a soundscape\nsoundscape_path = '../input/birdclef-2021/train_soundscapes/28933_SSW_20170408.ogg'\n#soundscape_path = '../input/birdclef-2021/train_soundscapes/11254_COR_20190904.ogg'\n\n# Open it with librosa\nsig, rate = librosa.load(soundscape_path, sr=SAMPLE_RATE)\n\n# Store results so that we can analyze them later\ndata = {'row_id': [], 'prediction': [], 'score': []}\n\n# Split signal into 5-second chunks\n# Just like we did before (well, this could actually be a seperate function)\nsig_splits = []\nfor i in range(0, len(sig), int(SIGNAL_LENGTH * SAMPLE_RATE)):\n    split = sig[i:i + int(SIGNAL_LENGTH * SAMPLE_RATE)]\n\n    # End of signal?\n    if len(split) < int(SIGNAL_LENGTH * SAMPLE_RATE):\n        break\n\n    sig_splits.append(split)\n    \n# Get the spectrograms and run inference on each of them\n# This should be the exact same process as we used to\n# generate training samples!\nseconds, scnt = 0, 0\nfor chunk in sig_splits:\n    \n    # Keep track of the end time of each chunk\n    seconds += 5\n        \n    # Get the spectrogram\n    hop_length = int(SIGNAL_LENGTH * SAMPLE_RATE / (SPEC_SHAPE[1] - 1))\n    mel_spec = librosa.feature.melspectrogram(y=chunk, \n                                              sr=SAMPLE_RATE, \n                                              n_fft=1024, \n                                              hop_length=hop_length, \n                                              n_mels=SPEC_SHAPE[0], \n                                              fmin=FMIN, \n                                              fmax=FMAX)\n\n    mel_spec = librosa.power_to_db(mel_spec, ref=np.max) \n\n    # Normalize to match the value range we used during training.\n    # That's something you should always double check!\n    mel_spec -= mel_spec.min()\n    mel_spec /= mel_spec.max()\n    \n    # Add channel axis to 2D array\n    mel_spec = np.expand_dims(mel_spec, -1)\n\n    # Add new dimension for batch size\n    mel_spec = np.expand_dims(mel_spec, 0)\n    \n    # Predict\n    p = model.predict(mel_spec)[0]\n    \n    # Get highest scoring species\n    idx = p.argmax()\n    species = LABELS[idx]\n    score = p[idx]\n    \n    # Prepare submission entry\n    data['row_id'].append(soundscape_path.split(os.sep)[-1].rsplit('_', 1)[0] + \n                          '_' + str(seconds))    \n    \n    # Decide if it's a \"nocall\" or a species by applying a threshold\n    if score > 0.25:\n        data['prediction'].append(species)\n        scnt += 1\n    else:\n        data['prediction'].append('nocall')\n        \n    # Add the confidence score as well\n    data['score'].append(score)\n        \nprint('SOUNSCAPE ANALYSIS DONE. FOUND {} BIRDS.'.format(scnt))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Ok, we found a few bird species with a score above the threshold. Let's look at the results and see how well we're actually doing.","metadata":{}},{"cell_type":"code","source":"# Make a new data frame\nresults = pd.DataFrame(data, columns = ['row_id', 'prediction', 'score'])\n\n# Merge with ground truth so we can inspect\ngt = pd.read_csv('../input/birdclef-2021/train_soundscape_labels.csv',)\nresults = pd.merge(gt, results, on='row_id')\n\n# Let's look at the first 50 entries\nresults.head(50)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Ok, that's not too bad. We actually got some of these Song Sparrow (sonspa) vocalizations. Well, and we missed others... We also didn't detect the Northern Cardinal (norcar) and Red-winged Blackbird (rewbla) even though we had them in our training data.\n\nThis is a good example for the difficulties we're facing when analyzing soundscapes. Focal recordings as training data can be misleading and soundscapes have much higher noise levels (and also contain very faint bird calls).\n\nNow it's your turn to find better strategies to cope with this shift in acoustic domains. Please don't hesitate to leave a comment or start a new forum thread if you have any questions.","metadata":{}}]}