{"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 json \nimport librosa\nimport numpy as np\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split\nimport tensorflow as tf\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Dense, GlobalAveragePooling2D\nfrom tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras import optimizers\nfrom tensorflow.keras.utils import plot_model, Sequence\nfrom tensorflow.keras.utils import to_categorical\nfrom tensorflow.keras.applications import MobileNetV2\nfrom tensorflow.keras.layers import Input, Concatenate\nfrom tqdm import tqdm\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-06-03T08:07:43.688448Z","iopub.execute_input":"2022-06-03T08:07:43.688804Z","iopub.status.idle":"2022-06-03T08:07:51.260354Z","shell.execute_reply.started":"2022-06-03T08:07:43.688720Z","shell.execute_reply":"2022-06-03T08:07:51.259391Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_meta = pd.read_csv(\"/kaggle/input/birdclef-2022/train_metadata.csv\")\ntrain_meta","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:07:51.262162Z","iopub.execute_input":"2022-06-03T08:07:51.262485Z","iopub.status.idle":"2022-06-03T08:07:51.400937Z","shell.execute_reply.started":"2022-06-03T08:07:51.262440Z","shell.execute_reply":"2022-06-03T08:07:51.400168Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels = list(train_meta['primary_label'].unique())","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:07:51.407811Z","iopub.execute_input":"2022-06-03T08:07:51.408075Z","iopub.status.idle":"2022-06-03T08:07:51.418976Z","shell.execute_reply.started":"2022-06-03T08:07:51.408040Z","shell.execute_reply":"2022-06-03T08:07:51.417800Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(labels)","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:07:51.420721Z","iopub.execute_input":"2022-06-03T08:07:51.420993Z","iopub.status.idle":"2022-06-03T08:07:51.430604Z","shell.execute_reply.started":"2022-06-03T08:07:51.420960Z","shell.execute_reply":"2022-06-03T08:07:51.429607Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Split audio into 5 seconds chunks","metadata":{}},{"cell_type":"code","source":"import soundfile as sf\nimport os\n\ndef cutAudio(file_path, is_save):\n    # First load the file\n    filename = file_path.replace(\"/\", \"_\")\n    file_path = \"/kaggle/input/birdclef-2022/train_audio/\" + file_path\n    audio, sr = librosa.load(file_path)\n\n    # Get number of samples for 5 seconds; replace 5 by any number\n    buffer = 5 * sr\n\n    samples_total = len(audio)\n    samples_wrote = 0\n    counter = 1\n\n    audio_split = []\n    audio_filenames = []\n    while samples_wrote < samples_total:\n        #check if the buffer is not exceeding total samples \n        if buffer > (samples_total - samples_wrote):\n            buffer = samples_total - samples_wrote\n\n        block = audio[samples_wrote : (samples_wrote + buffer)]\n        audio_split.append(block)\n\n        # Write 5 second segment\n        if is_save == True:\n            out_filename = \"/kaggle/working/each5s/split_\" + str(counter) + \"_\" + filename\n            audio_filenames.append(out_filename)\n            sf.write(out_filename, block, sr)\n        counter += 1\n        samples_wrote += buffer\n    return audio_split, sr, audio_filenames","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:07:51.432282Z","iopub.execute_input":"2022-06-03T08:07:51.432706Z","iopub.status.idle":"2022-06-03T08:07:51.445438Z","shell.execute_reply.started":"2022-06-03T08:07:51.432669Z","shell.execute_reply":"2022-06-03T08:07:51.444663Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def splitTrainAudio(_df):\n    data = []\n    for index, row in tqdm(_df.iterrows()):\n        cutAudio(row[\"filename\"], True)\n        audio_lst, sr, filenames = cutAudio(row[\"filename\"], True)\n        for idx, y in enumerate(audio_lst):\n            data.append([row[\"primary_label\"], row[\"filename\"], filenames[idx]])\n\n    data_df = pd.DataFrame(data, columns=['primary_label', 'original_filename', 'filename'])\n    data_df.to_csv(\"/kaggle/working/data_df.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:07:51.446647Z","iopub.execute_input":"2022-06-03T08:07:51.447354Z","iopub.status.idle":"2022-06-03T08:07:51.455158Z","shell.execute_reply.started":"2022-06-03T08:07:51.447315Z","shell.execute_reply":"2022-06-03T08:07:51.454460Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Sample Data\ndata_frames = []\nfor label in labels:\n    tmp_df = train_meta[train_meta[\"primary_label\"] == label].sample(n=1, replace=True).reset_index(drop=True)\n    data_frames.append(tmp_df)\nsample_df = pd.concat(data_frames).reset_index(drop=True)\nsample_df","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:07:51.456599Z","iopub.execute_input":"2022-06-03T08:07:51.457334Z","iopub.status.idle":"2022-06-03T08:07:51.997300Z","shell.execute_reply.started":"2022-06-03T08:07:51.457297Z","shell.execute_reply":"2022-06-03T08:07:51.996602Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!mkdir -p \"/kaggle/working/each5s\"\nsplitTrainAudio(sample_df)","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:07:51.998671Z","iopub.execute_input":"2022-06-03T08:07:51.999310Z","iopub.status.idle":"2022-06-03T08:17:10.656631Z","shell.execute_reply.started":"2022-06-03T08:07:51.999271Z","shell.execute_reply":"2022-06-03T08:17:10.655775Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_df = pd.read_csv(\"/kaggle/working/data_df.csv\")\ndata_df","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:17:10.659804Z","iopub.execute_input":"2022-06-03T08:17:10.660017Z","iopub.status.idle":"2022-06-03T08:17:10.676267Z","shell.execute_reply.started":"2022-06-03T08:17:10.659992Z","shell.execute_reply":"2022-06-03T08:17:10.675477Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Training","metadata":{}},{"cell_type":"code","source":"num_rows = 216\nnum_columns = 216\nnum_channels = 1\nn_mels = 512\n\ndef extractFeatures(y, sr):\n    feat = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=num_rows, n_mels=n_mels)\n    if feat.shape[1] <= num_columns:\n        pad_width = num_columns - feat.shape[1]\n        feat = np.pad(feat, pad_width=((0,0),(0,pad_width)), mode='constant')\n    return feat","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:17:10.677370Z","iopub.execute_input":"2022-06-03T08:17:10.678010Z","iopub.status.idle":"2022-06-03T08:17:10.684558Z","shell.execute_reply.started":"2022-06-03T08:17:10.677974Z","shell.execute_reply":"2022-06-03T08:17:10.683829Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class DataGenerator(Sequence):\n    def __init__(self,\n                _X,\n                batch_size=32,\n                n_channels=1,\n                n_columns=470,\n                n_rows=120,\n                shuffle=True):\n        self.batch_size = batch_size\n        self.X = _X\n        self.n_channels = n_channels\n        self.n_columns = n_columns\n        self.n_rows = n_rows\n        self.shuffle = shuffle\n        self.img_indexes = np.arange(len(self.X))\n        self.on_epoch_end()\n        \n    def __len__(self):\n        'Denotes the number of batches per epoch'\n        return int(np.floor(len(self.img_indexes) / self.batch_size))\n\n    def __getitem__(self, index):\n        'Generate one batch of data'\n        # Generate indexes of the batch\n        indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size]\n        # Find list of IDs\n        list_IDs_temps = [self.img_indexes[k] for k in indexes]\n\n        # Generate data\n        X, y = self.__data_generation(list_IDs_temps)\n        return X, y\n\n    def on_epoch_end(self):\n        'Updates indexes after each epoch'\n        self.indexes = np.arange(len(self.X))\n        if self.shuffle == True:\n            np.random.shuffle(self.indexes)\n\n    def __data_generation(self, list_IDs_temps):\n        X = np.empty((self.batch_size, self.n_rows, self.n_columns))\n        y = np.empty((self.batch_size), dtype=int)\n        for i, ID in enumerate(list_IDs_temps):\n            file_path = self.X.iloc[ID][\"filename\"]\n            audio, sr = librosa.load(file_path)\n            feat = extractFeatures(audio, sr)\n            x_features = feat.tolist()\n            label = self.X.iloc[ID][\"target\"]\n            X[i] = np.array(x_features)\n            y[i] = label\n        X = X.reshape(X.shape[0], self.n_rows, self.n_columns, self.n_channels)\n        \n        return X, to_categorical(y, num_classes=len(labels))","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:17:10.685911Z","iopub.execute_input":"2022-06-03T08:17:10.686253Z","iopub.status.idle":"2022-06-03T08:17:10.702003Z","shell.execute_reply.started":"2022-06-03T08:17:10.686174Z","shell.execute_reply":"2022-06-03T08:17:10.701259Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Paramaters for the data generator\n\nparams = dict(\n    batch_size=128,\n    n_rows=num_rows,\n    n_columns=num_columns,\n    n_channels=num_channels,\n)\nparams_train = dict(\n    shuffle=True,\n    **params\n)\nparams_valid = dict(\n    shuffle=False,\n    **params\n)","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:17:10.703321Z","iopub.execute_input":"2022-06-03T08:17:10.703578Z","iopub.status.idle":"2022-06-03T08:17:10.710893Z","shell.execute_reply.started":"2022-06-03T08:17:10.703530Z","shell.execute_reply":"2022-06-03T08:17:10.710239Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\ndef plot_his(history):\n    '''Plots the data from the fitting process'''\n    \n    plt.figure(1, figsize = (15,8))\n    plt.subplot(221)\n    plt.plot(history.history['accuracy'])\n    plt.plot(history.history['val_accuracy'])\n    plt.title('model accuracy')\n    plt.ylabel('accuracy')\n    plt.xlabel('epoch')\n    plt.legend(['train', 'valid'])\n    plt.subplot(222)\n    plt.plot(history.history['loss'])\n    plt.plot(history.history['val_loss'])\n    plt.title('model loss')\n    plt.ylabel('loss')\n    plt.xlabel('epoch')\n    plt.legend(['train', 'valid'])\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:17:10.712623Z","iopub.execute_input":"2022-06-03T08:17:10.712943Z","iopub.status.idle":"2022-06-03T08:17:10.724119Z","shell.execute_reply.started":"2022-06-03T08:17:10.712909Z","shell.execute_reply":"2022-06-03T08:17:10.723401Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def make_model(input_shape, num_classes):\n    '''Returns a CNN model'''\n    \n    \n    \n    inputs = keras.Input(shape=input_shape)\n    # Image augmentation block\n    x = (inputs)\n\n    # Entry block\n#     x = layers.Rescaling(1.0 / 255)(x)\n    x = layers.Conv2D(32, 3, strides=2, padding=\"same\")(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Activation(\"relu\")(x)\n\n    x = layers.Conv2D(64, 3, padding=\"same\")(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Activation(\"relu\")(x)\n\n    previous_block_activation = x  # Set aside residual\n\n    for size in [128, 256, 512, 728]:\n        x = layers.Activation(\"relu\")(x)\n        x = layers.SeparableConv2D(size, 3, padding=\"same\")(x)\n        x = layers.BatchNormalization()(x)\n\n        x = layers.Activation(\"relu\")(x)\n        x = layers.SeparableConv2D(size, 3, padding=\"same\")(x)\n        x = layers.BatchNormalization()(x)\n\n        x = layers.MaxPooling2D(3, strides=2, padding=\"same\")(x)\n\n        # Project residual\n        residual = layers.Conv2D(size, 1, strides=2, padding=\"same\")(\n            previous_block_activation\n        )\n        x = layers.add([x, residual])  # Add back residual\n        previous_block_activation = x  # Set aside next residual\n\n    x = layers.SeparableConv2D(1024, 3, padding=\"same\")(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Activation(\"relu\")(x)\n\n    x = layers.GlobalAveragePooling2D()(x)\n    if num_classes == 2:\n        activation = \"sigmoid\"\n        units = 1\n    else:\n        activation = \"softmax\"\n        units = num_classes\n\n    x = layers.Dropout(0.5)(x)\n    outputs = layers.Dense(units, activation=activation)(x)\n    return keras.Model(inputs, outputs)\n\n\nmodel = make_model(input_shape=(num_rows, num_columns, num_channels), num_classes=2)\n# Create model of the model\nkeras.utils.plot_model(model, show_shapes=True)","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:17:10.725450Z","iopub.execute_input":"2022-06-03T08:17:10.725763Z","iopub.status.idle":"2022-06-03T08:17:15.268441Z","shell.execute_reply.started":"2022-06-03T08:17:10.725709Z","shell.execute_reply":"2022-06-03T08:17:15.267571Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_df['target'] = data_df['primary_label'].apply(lambda x: labels.index(x))\ndata_df","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:36:08.948749Z","iopub.execute_input":"2022-06-03T08:36:08.949209Z","iopub.status.idle":"2022-06-03T08:36:08.969765Z","shell.execute_reply.started":"2022-06-03T08:36:08.949177Z","shell.execute_reply":"2022-06-03T08:36:08.968985Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create the dataset for training and validation\nX_train, X_valid, _, _ = train_test_split(data_df, data_df[\"target\"], test_size=0.2, random_state=42)\ntrain_generator = DataGenerator(X_train, **params_train)\nvalid_generator = DataGenerator(X_valid, **params_valid)","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:36:10.049331Z","iopub.execute_input":"2022-06-03T08:36:10.049605Z","iopub.status.idle":"2022-06-03T08:36:10.060540Z","shell.execute_reply.started":"2022-06-03T08:36:10.049575Z","shell.execute_reply":"2022-06-03T08:36:10.059512Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Train the model and plot the results\nepochs = 10\n\ncallbacks = [\n    keras.callbacks.ModelCheckpoint(\"save_at_{epoch}.h5\"),\n]\nmodel.compile(\n    optimizer=keras.optimizers.Adam(1e-3),\n    loss=\"binary_crossentropy\",\n    metrics=[\"accuracy\"],\n)\nhistory = model.fit(\n    train_generator, epochs=epochs, callbacks=callbacks, validation_data=valid_generator,\n)\n\nplot_his(history)","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:36:12.689191Z","iopub.execute_input":"2022-06-03T08:36:12.689455Z","iopub.status.idle":"2022-06-03T08:50:23.208514Z","shell.execute_reply.started":"2022-06-03T08:36:12.689426Z","shell.execute_reply":"2022-06-03T08:50:23.207803Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Submission","metadata":{}},{"cell_type":"code","source":"data_df = pd.read_csv(\"/kaggle/working/data_df.csv\")\nlabels = list(data_df['primary_label'].unique())","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:50:23.210170Z","iopub.execute_input":"2022-06-03T08:50:23.210579Z","iopub.status.idle":"2022-06-03T08:50:23.221452Z","shell.execute_reply.started":"2022-06-03T08:50:23.210542Z","shell.execute_reply":"2022-06-03T08:50:23.220781Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_path = \"/kaggle/input/birdclef-2022/test_soundscapes/\"\nfiles = [f.split('.')[0] for f in sorted(os.listdir(test_path))]\n\nbirds_path = \"/kaggle/input/birdclef-2022/scored_birds.json\"\nwith open(birds_path) as bf:\n    birds = json.load(bf)\n\ndata = []\nfor f in files:\n    file_path = test_path + f + '.ogg'\n    audio, sr = librosa.load(file_path)\n    # Get number of samples for 5 seconds; replace 5 by any number\n    buffer = 5 * sr\n    samples_total = len(audio)\n    samples_wrote = 0\n    counter = 1\n\n    while samples_wrote < samples_total:\n        #check if the buffer is not exceeding total samples \n        if buffer > (samples_total - samples_wrote):\n            buffer = samples_total - samples_wrote\n\n        block = audio[samples_wrote : (samples_wrote + buffer)]\n        feat = extractFeatures(block, sr)\n        x = feat.reshape(1, num_rows, num_columns, num_channels)\n        pred = model.predict(x)\n        label_index = np.argmax(pred,axis=1)[0]\n        \n        for b in birds:\n            segment_end = counter * 5   \n            row_id = f + '_' + b + '_' + str(segment_end)\n            target = False\n            if labels[label_index] == b:\n                target = True\n            data.append([row_id, target])\n        counter += 1\n        samples_wrote += buffer\n        \nsubmission_df = pd.DataFrame(data, columns=['row_id', 'target'])\nsubmission_df","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:50:23.222831Z","iopub.execute_input":"2022-06-03T08:50:23.223089Z","iopub.status.idle":"2022-06-03T08:50:26.361764Z","shell.execute_reply.started":"2022-06-03T08:50:23.223054Z","shell.execute_reply":"2022-06-03T08:50:26.360801Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df.to_csv(\"/kaggle/working/submission.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:50:26.363803Z","iopub.execute_input":"2022-06-03T08:50:26.364127Z","iopub.status.idle":"2022-06-03T08:50:26.373123Z","shell.execute_reply.started":"2022-06-03T08:50:26.364086Z","shell.execute_reply":"2022-06-03T08:50:26.371801Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import shutil\nshutil.rmtree(\"/kaggle/working/each5s\")","metadata":{"execution":{"iopub.status.busy":"2022-06-03T08:17:15.677368Z","iopub.status.idle":"2022-06-03T08:17:15.677928Z","shell.execute_reply.started":"2022-06-03T08:17:15.677674Z","shell.execute_reply":"2022-06-03T08:17:15.677699Z"},"trusted":true},"execution_count":null,"outputs":[]}]}