{"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\nimport shutil\nwarnings.filterwarnings(action='ignore')\n\nimport math\nimport pandas as pd\nimport librosa\nimport librosa.display\nimport numpy as np\nimport seaborn as sns; sns.set(style='whitegrid')\nimport matplotlib.pyplot as plt\n\nfrom sklearn.utils import shuffle\nfrom PIL import Image\nfrom tqdm import tqdm,tnrange,tqdm_notebook\nimport tensorflow as tf\nfrom tqdm.keras import TqdmCallback\nfrom keras.callbacks import ReduceLROnPlateau,EarlyStopping,ModelCheckpoint\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator \nfrom tensorflow.keras import applications as app\nfrom tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten,AveragePooling2D\nfrom tensorflow.keras.layers import Dense,BatchNormalization,Dropout\nfrom tensorflow.keras.models import Sequential \nfrom tensorflow.keras.applications import EfficientNetB4, ResNet50,ResNet101, VGG16, MobileNet, InceptionV3\nimport pickle","metadata":{"execution":{"iopub.status.busy":"2022-04-06T04:50:28.595626Z","iopub.execute_input":"2022-04-06T04:50:28.596565Z","iopub.status.idle":"2022-04-06T04:50:35.780383Z","shell.execute_reply.started":"2022-04-06T04:50:28.596443Z","shell.execute_reply":"2022-04-06T04:50:35.779623Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Global vars\nRANDOM_SEED = 1337\nSAMPLE_RATE = 32000\nSIGNAL_LENGTH = 5 # seconds\nSPEC_SHAPE = (224, 224) # height x width\nFMIN = 500\nFMAX = 12500\n# MAX_AUDIO_FILES = 10000\nEPOCHS=50","metadata":{"execution":{"iopub.status.busy":"2022-04-06T06:10:51.225056Z","iopub.execute_input":"2022-04-06T06:10:51.225493Z","iopub.status.idle":"2022-04-06T06:10:51.230524Z","shell.execute_reply.started":"2022-04-06T06:10:51.225456Z","shell.execute_reply":"2022-04-06T06:10:51.229575Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 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\n# Load metadata file\ntrain = pd.read_csv('../input/birdclef-2022/train_metadata.csv',)\n\n# Limit the number of training samples and classes\n# First, only use high quality samples\ntrain = train.query('rating>=4')\n\n# Second, assume that birds with the most training samples are also the most common\n# A species needs at least 200 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 >= 175] \n\nTRAIN = train.query('primary_label in @most_represented_birds')\nLABELS = sorted(TRAIN.primary_label.unique())\n\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":"2022-04-06T04:51:13.804992Z","iopub.execute_input":"2022-04-06T04:51:13.805251Z","iopub.status.idle":"2022-04-06T04:51:13.935915Z","shell.execute_reply.started":"2022-04-06T04:51:13.805221Z","shell.execute_reply":"2022-04-06T04:51:13.935178Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# saving labels \nwith open('LABELS.pkl','wb') as f:\n    pickle.dump(LABELS,f)","metadata":{"execution":{"iopub.status.busy":"2022-04-06T04:51:30.852823Z","iopub.execute_input":"2022-04-06T04:51:30.853492Z","iopub.status.idle":"2022-04-06T04:51:30.859330Z","shell.execute_reply.started":"2022-04-06T04:51:30.853455Z","shell.execute_reply":"2022-04-06T04:51:30.858167Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Shuffle the training data and limit the number of audio files to MAX_AUDIO_FILES\nTRAIN = shuffle(TRAIN, random_state=RANDOM_SEED)\n\n# Define a function that splits an audio file, \n# extracts spectrograms and saves them in a working directory\ndef get_spectrograms(filepath, primary_label, output_dir):\n    \n    # Open the file with librosa (limited to the first 15 seconds)\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    # Extract mel spectrograms for each audio chunk\n    s_cnt = 0\n    saved_samples = []\n    for chunk in sig_splits:\n        \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\n        mel_spec -= mel_spec.min()\n        mel_spec /= mel_spec.max()\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        \n    return saved_samples\n\nprint('FINAL NUMBER OF AUDIO FILES IN TRAINING DATA:', len(TRAIN))","metadata":{"execution":{"iopub.status.busy":"2022-04-06T04:51:36.569847Z","iopub.execute_input":"2022-04-06T04:51:36.570107Z","iopub.status.idle":"2022-04-06T04:51:36.587662Z","shell.execute_reply.started":"2022-04-06T04:51:36.570079Z","shell.execute_reply":"2022-04-06T04:51:36.586804Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Parse audio files and extract training samples\ninput_dir = '../input/birdclef-2022/train_audio'\noutput_dir = '../working/melspectrogram_dataset/'\nsamples = []\nwith tqdm(total=len(TRAIN)) as pbar:\n    for idx, row in TRAIN.iterrows():\n        pbar.update(1)\n        \n        if row.primary_label in most_represented_birds:\n            audio_file_path = os.path.join(input_dir, row.filename)\n            samples += get_spectrograms(audio_file_path, row.primary_label, output_dir)\n            \nTRAIN_SPECS = shuffle(samples, random_state=RANDOM_SEED)\nprint('SUCCESSFULLY EXTRACTED {} SPECTROGRAMS'.format(len(TRAIN_SPECS)))","metadata":{"execution":{"iopub.status.busy":"2022-04-06T04:52:14.808147Z","iopub.execute_input":"2022-04-06T04:52:14.808873Z","iopub.status.idle":"2022-04-06T05:05:16.744170Z","shell.execute_reply.started":"2022-04-06T04:52:14.808831Z","shell.execute_reply":"2022-04-06T05:05:16.743424Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.applications import DenseNet201, EfficientNetB0\nfrom tensorflow.keras.layers import GlobalAveragePooling2D, MaxPooling2D\nfrom tensorflow.keras.layers import Dropout\nfrom tensorflow.keras.layers import Flatten\nfrom tensorflow.keras.layers import Dense\nfrom tensorflow.keras.layers import Input\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.applications.efficientnet import preprocess_input\nfrom tensorflow.keras.callbacks import EarlyStopping\nfrom tensorflow.keras.callbacks import ModelCheckpoint\nfrom sklearn.metrics import classification_report\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport argparse\nimport os","metadata":{"execution":{"iopub.status.busy":"2022-04-06T05:05:30.055465Z","iopub.execute_input":"2022-04-06T05:05:30.056326Z","iopub.status.idle":"2022-04-06T05:05:30.065578Z","shell.execute_reply.started":"2022-04-06T05:05:30.056288Z","shell.execute_reply":"2022-04-06T05:05:30.064855Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\ntrain_folder = './melspectrogram_dataset'\n# valid_datagen = ImageDataGenerator(rescale=1./255, validation_split=0.2)\ndatagen = ImageDataGenerator(\n    rotation_range=20,\n    zoom_range=0.15,\n    width_shift_range=0.2,\n    height_shift_range=0.2,\n    shear_range=0.15,\n    horizontal_flip=True,\n    fill_mode=\"nearest\",\n    preprocessing_function=preprocess_input,\n    validation_split=0.2)\n\ntrain_generator = datagen.flow_from_directory(train_folder, \n#                         target_size=(coefs.sshape[0],coefs.sshape[1]),  # target size\n                        target_size=(224,224),\n                        batch_size=64, \n                        seed=2022,\n                        shuffle=True,\n                        subset = \"training\",\n                        class_mode='categorical')    # batch size\nvalidation_generator = datagen.flow_from_directory(train_folder, \n#                         target_size=(coefs.sshape[0],coefs.sshape[1]),  # target size\n                        target_size=(224,224),\n                        batch_size=64,\n                        seed=2022,\n                        shuffle=True,\n                        subset = \"validation\",\n                        class_mode='categorical')    # batch size","metadata":{"execution":{"iopub.status.busy":"2022-04-06T05:06:02.531408Z","iopub.execute_input":"2022-04-06T05:06:02.532102Z","iopub.status.idle":"2022-04-06T05:06:03.179419Z","shell.execute_reply.started":"2022-04-06T05:06:02.532068Z","shell.execute_reply":"2022-04-06T05:06:03.178776Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import cv2\nimg = cv2.imread('./melspectrogram_dataset/brnowl/XC635289_2.png')\n\nimg.shape","metadata":{"execution":{"iopub.status.busy":"2022-04-06T05:06:16.500829Z","iopub.execute_input":"2022-04-06T05:06:16.501100Z","iopub.status.idle":"2022-04-06T05:06:16.522884Z","shell.execute_reply.started":"2022-04-06T05:06:16.501072Z","shell.execute_reply":"2022-04-06T05:06:16.522071Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"LEARNING_RATE = 0.001\nnum_epochs = 80\nBATCH_SIZE = 64\nIMG_SIZE = 224","metadata":{"execution":{"iopub.status.busy":"2022-04-06T05:06:20.457429Z","iopub.execute_input":"2022-04-06T05:06:20.457996Z","iopub.status.idle":"2022-04-06T05:06:20.462530Z","shell.execute_reply.started":"2022-04-06T05:06:20.457959Z","shell.execute_reply":"2022-04-06T05:06:20.461545Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from datetime import datetime, timedelta\n\nstart_time = datetime.now()\nprint('Time now is', start_time)\nend_training_by_tdelta = timedelta(seconds=8400)\nthis_run_file_prefix = start_time.strftime('%Y%m%d_%H%M_')\n\nnum_epochs = 80\n# EPOCHS = 25\n# STEPS_PER_EPOCH = NUM_TRAINING_IMAGES // BATCH_SIZE\n\n\n\n# Learning Rate Schedule for Fine Tuning #\n# Learning rate schedule for TPU, GPU and CPU.\n# Using an LR ramp up because fine-tuning a pre-trained model.\n# Starting with a high LR would break the pre-trained weights.\n\nLR_START = 0.00001\nLR_MAX = 0.00005 * 8\nLR_MIN = 0.00001\nLR_RAMPUP_EPOCHS = 5\nLR_SUSTAIN_EPOCHS = 0\nLR_EXP_DECAY = .8\n\ndef lrfn(epoch):\n    if epoch < LR_RAMPUP_EPOCHS:\n        lr = LR_START + (epoch * (LR_MAX - LR_START) / LR_RAMPUP_EPOCHS)\n    elif epoch < LR_RAMPUP_EPOCHS + LR_SUSTAIN_EPOCHS:\n        lr = LR_MAX\n    else:\n        lr = (LR_MAX - LR_MIN) * LR_EXP_DECAY**(epoch - LR_RAMPUP_EPOCHS - LR_SUSTAIN_EPOCHS) + LR_MIN\n    return lr\n    \nlr_callback = tf.keras.callbacks.LearningRateScheduler(lrfn, verbose = True)\nrng = [i for i in range(num_epochs)]\ny = [lrfn(x) for x in rng]\nplt.plot(rng, y)\nprint(\"Learning rate schedule: {:.3g} to {:.3g} to {:.3g}\".format(y[0], max(y), y[-1]))","metadata":{"execution":{"iopub.status.busy":"2022-04-06T05:06:23.197093Z","iopub.execute_input":"2022-04-06T05:06:23.197343Z","iopub.status.idle":"2022-04-06T05:06:23.484223Z","shell.execute_reply.started":"2022-04-06T05:06:23.197314Z","shell.execute_reply":"2022-04-06T05:06:23.483525Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":" \nbase_model = EfficientNetB0(weights='imagenet',include_top=False, input_shape=(224,224,3))\n\n# Construct the head of the model that will be placed on top of the base model\nhead_model = base_model.output\nhead_model = GlobalAveragePooling2D()(head_model)\nhead_model = Flatten(name=\"flatten\")(head_model)\nhead_model = Dense(16, activation=\"softmax\")(head_model)\nmodel = Model(inputs=base_model.input, outputs=head_model)\n\nfor layer in base_model.layers:\n    layer.trainable = True\nmodel.summary()\nmodel.compile(loss=\"categorical_crossentropy\", optimizer='adam', metrics=[\"accuracy\"])\n\nearly_stopping = EarlyStopping(\n    monitor='val_accuracy',\n    min_delta=0.0001,\n    patience=5,\n    verbose=1,\n    mode='max',\n    baseline=None,\n    restore_best_weights=True)\nH = model.fit(\n    train_generator,\n    steps_per_epoch=train_generator.samples // train_generator.batch_size,\n    callbacks=[early_stopping, lr_callback],\n    validation_data=validation_generator,\n    validation_steps=validation_generator.samples // validation_generator.batch_size,\n    epochs=num_epochs)","metadata":{"execution":{"iopub.status.busy":"2022-04-06T05:07:21.059181Z","iopub.execute_input":"2022-04-06T05:07:21.059456Z","iopub.status.idle":"2022-04-06T06:06:50.772844Z","shell.execute_reply.started":"2022-04-06T05:07:21.059426Z","shell.execute_reply":"2022-04-06T06:06:50.772048Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.style.use(\"ggplot\")\nplt.figure()\nplt.plot(np.arange(0, len(H.history[\"loss\"])), H.history[\"loss\"], label=\"train_loss\")\nplt.plot(np.arange(0, len(H.history[\"val_loss\"])), H.history[\"val_loss\"], label=\"val_loss\")\nplt.plot(np.arange(0, len(H.history[\"accuracy\"])), H.history[\"accuracy\"], label=\"train_acc\")\nplt.plot(np.arange(0, len(H.history[\"val_accuracy\"])), H.history[\"val_accuracy\"], label=\"val_acc\")\nplt.title(\"Training Loss and Accuracy\")\nplt.xlabel(\"Epoch #\")\nplt.ylabel(\"Loss/Accuracy\")\nplt.legend(loc=\"lower left\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-04-06T06:08:36.648350Z","iopub.execute_input":"2022-04-06T06:08:36.648637Z","iopub.status.idle":"2022-04-06T06:08:36.917076Z","shell.execute_reply.started":"2022-04-06T06:08:36.648608Z","shell.execute_reply":"2022-04-06T06:08:36.916381Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.save('bird_model.h5')","metadata":{"execution":{"iopub.status.busy":"2022-04-06T06:08:54.904034Z","iopub.execute_input":"2022-04-06T06:08:54.904293Z","iopub.status.idle":"2022-04-06T06:08:55.642237Z","shell.execute_reply.started":"2022-04-06T06:08:54.904263Z","shell.execute_reply":"2022-04-06T06:08:55.641402Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_audio_dir = '../input/birdclef-2022/test_soundscapes/'\nfile_list = [f.split('.')[0] for f in sorted(os.listdir(test_audio_dir))]\n\nprint('Number of test soundscapes:', len(file_list))","metadata":{"execution":{"iopub.status.busy":"2022-04-06T06:09:04.577686Z","iopub.execute_input":"2022-04-06T06:09:04.578216Z","iopub.status.idle":"2022-04-06T06:09:04.592674Z","shell.execute_reply.started":"2022-04-06T06:09:04.578178Z","shell.execute_reply":"2022-04-06T06:09:04.591817Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import json\nwith open('../input/birdclef-2022/scored_birds.json') as sbfile:\n    scored_birds = json.load(sbfile)","metadata":{"execution":{"iopub.status.busy":"2022-04-06T06:09:21.777444Z","iopub.execute_input":"2022-04-06T06:09:21.777735Z","iopub.status.idle":"2022-04-06T06:09:21.784369Z","shell.execute_reply.started":"2022-04-06T06:09:21.777703Z","shell.execute_reply":"2022-04-06T06:09:21.783343Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def predict(threshold):\n#     row_id = []\n    pred = {'row_id': [], 'target': []}\n    scnt = 0\n    for afile in file_list:\n        # Open it with librosa\n        path = test_audio_dir + afile + '.ogg'\n        sig, rate = librosa.load(path, sr=SAMPLE_RATE)\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        seconds= 0\n        for 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 = 0.5*model.predict(mel_spec)[0] \n\n            # Get highest scoring species\n            idx = p.argmax()\n            species = LABELS[idx]\n            score = p[idx]\n            chunks = [[] for i in range(12)]\n            for idx,i in enumerate(range(len(chunks))):        \n                for bird in scored_birds:\n                    chunk_end_time = (i + 1) * 5\n                # Prepare submission entry\n                    row_id = afile + '_' + bird + '_' + str(chunk_end_time)\n#                     row_id.append(afile.split(os.sep)[-1].rsplit('_', 1)[0] + \n#                                   '_' + str(seconds))  \n                    # Decide if it's a \"nocall\" or a species by applying a threshold\n#                     pred['row_id'].append(row_id)\n#                     pred['target'].append(True if score > threshold else False)\n            \n                    pred['row_id'].append(row_id)\n                    pred['target'].append(True if score > threshold else False)\n                       \n\n    result = pd.DataFrame(pred, columns = ['row_id', 'target'])\n    return result","metadata":{"execution":{"iopub.status.busy":"2022-04-06T06:37:12.546453Z","iopub.execute_input":"2022-04-06T06:37:12.546804Z","iopub.status.idle":"2022-04-06T06:37:12.560053Z","shell.execute_reply.started":"2022-04-06T06:37:12.546772Z","shell.execute_reply":"2022-04-06T06:37:12.558922Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"result=predict(0.3)\nresult","metadata":{"execution":{"iopub.status.busy":"2022-04-06T06:37:14.745893Z","iopub.execute_input":"2022-04-06T06:37:14.746301Z","iopub.status.idle":"2022-04-06T06:37:16.251297Z","shell.execute_reply.started":"2022-04-06T06:37:14.746262Z","shell.execute_reply":"2022-04-06T06:37:16.250612Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"result\nresults = result.to_csv('submission_bird.csv',index=False)","metadata":{"execution":{"iopub.status.busy":"2022-04-06T06:12:09.585532Z","iopub.execute_input":"2022-04-06T06:12:09.585911Z","iopub.status.idle":"2022-04-06T06:12:09.619209Z","shell.execute_reply.started":"2022-04-06T06:12:09.585863Z","shell.execute_reply":"2022-04-06T06:12:09.618073Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"results = pd.read_csv('./submission_bird.csv')\nresults","metadata":{"execution":{"iopub.status.busy":"2022-04-06T06:12:16.320482Z","iopub.execute_input":"2022-04-06T06:12:16.320941Z","iopub.status.idle":"2022-04-06T06:12:16.338084Z","shell.execute_reply.started":"2022-04-06T06:12:16.320906Z","shell.execute_reply":"2022-04-06T06:12:16.337424Z"},"trusted":true},"execution_count":null,"outputs":[]}]}