{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Imports and Setups"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"!nvidia-smi","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"SEED = 666\n\nimport tensorflow as tf\nfrom tensorflow.keras.layers import *\nfrom tensorflow.keras.models import *\ntf.random.set_seed(SEED)\n\nfrom tensorflow import keras\nfrom tensorflow.keras.datasets import cifar10","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nimport csv\nos.environ[\"TF_DETERMINISTIC_OPS\"] = \"1\"\n\nimport numpy as np\nnp.random.seed(SEED)\n\nimport pandas as pd\nfrom pathlib import Path\nimport matplotlib.pyplot as plt\n\n%matplotlib inline\n\nfrom mpl_toolkits.axes_grid1 import ImageGrid\nfrom PIL import Image\n\nfrom sklearn.metrics import classification_report\nfrom sklearn.model_selection import train_test_split\n\nimport librosa as lb \nimport librosa.display\nimport matplotlib.pyplot as plt\nimport IPython.display as ipd\n\nfrom skimage.transform import resize\nfrom scipy import stats","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import wandb\nfrom wandb.keras import WandbCallback\n\nwandb.login()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Download Dataset from W&B Artifacts and Prepare"},{"metadata":{"trusted":true},"cell_type":"code","source":"run = wandb.init(project='rainforest', job_type='download_dataset')\n\nartifact = run.use_artifact('wandb/rainforest/spectrogram-dataset_nfft_2024_hop_512:v0', type='dataset')\nartifact_dir = artifact.download()\n\nrun.join()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"IMG_DIR = Path(artifact_dir+'/')\nIMG_PATH = list(map(str, list(IMG_DIR.glob('*.bmp'))))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Train-test Split"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_path, valid_path = train_test_split(IMG_PATH, test_size=0.20, shuffle=True, random_state=42)\nlen(IMG_PATH), len(train_path), len(valid_path)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Dataloader"},{"metadata":{"trusted":true},"cell_type":"code","source":"AUTO = tf.data.experimental.AUTOTUNE\nBATCH_SIZE = 32\nIMG_WIDTH = 400\nIMG_HEIGHT = 224\nCHANNELS = 3\nNUM_CLASSES = 24","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"@tf.function\ndef parse_data(image_path):\n    # parse image\n    image = tf.io.read_file(image_path)\n    image = tf.image.decode_image(image)\n    image = tf.image.convert_image_dtype(image, tf.float32)\n    # normalize image\n    image = tf.image.per_image_standardization(image)\n    \n    # parse data\n    label = tf.strings.split(image_path, sep='_')[-2]\n    label = tf.strings.to_number(label, out_type=tf.int32)\n    label = tf.one_hot(label, NUM_CLASSES) \n    \n    return image, label\n\ntrainloader = tf.data.Dataset.list_files((train_path))\ntestloader = tf.data.Dataset.list_files((valid_path))\n\ntrainloader = (\n    trainloader\n    .shuffle(1024)\n    .map(parse_data, num_parallel_calls=AUTO)\n    .batch(BATCH_SIZE)\n    .prefetch(AUTO)\n)\n\ntestloader = (\n    testloader\n    .map(parse_data, num_parallel_calls=AUTO)\n    .batch(BATCH_SIZE)\n    .prefetch(AUTO)\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Visualize"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Ref: https://www.tensorflow.org/tutorials/load_data/images\ndef show_batch(image_batch, label_batch):\n  plt.figure(figsize=(10,10))\n  for n in range(25):\n      ax = plt.subplot(5,5,n+1)\n      plt.imshow(image_batch[n])\n      plt.title(np.argmax(label_batch[n]))\n      plt.axis('off')\n        \nimage_batch, label_batch = next(iter(trainloader))\nshow_batch(image_batch, label_batch)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_resnet_model():\n  base_model = tf.keras.applications.ResNet50(include_top=False, weights=\"imagenet\", input_shape=(224, 224, 3))\n  base_model.trainabe = True\n\n  inputs = Input((IMG_HEIGHT, IMG_WIDTH, 3))\n  resize = experimental.preprocessing.Resizing(224,224)(inputs) \n  x = base_model(resize, training=True)\n  x = GlobalAveragePooling2D()(x)\n  x = Dropout(0.5)(x)  \n  outputs = Dense(NUM_CLASSES, activation='sigmoid')(x)\n\n  return Model(inputs, outputs)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"keras.backend.clear_session()\nmodel = get_resnet_model()\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Callbacks"},{"metadata":{"trusted":true},"cell_type":"code","source":"earlystoper = tf.keras.callbacks.EarlyStopping(\n    monitor='val_loss', patience=5, verbose=0, mode='auto',\n    restore_best_weights=True\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Train"},{"metadata":{"trusted":true},"cell_type":"code","source":"EPOCHS = 70\n\nkeras.backend.clear_session()\n\n# initialize model\nmodel = get_resnet_model()\n\n# compile model\nopt = keras.optimizers.Adam(learning_rate=0.001)\nmodel.compile(opt, 'binary_crossentropy', metrics=['acc'])\n\n# initialize W&B run\nrun = wandb.init(project='rainforest', job_type='train')\n\n# declare the artifact we are using\ndata_artifact = run.use_artifact('wandb/rainforest/spectrogram-dataset_nfft_2024_hop_512:v0')\n\n# train model \n_ = model.fit(trainloader,\n          epochs=EPOCHS,\n          validation_data=testloader,\n          callbacks=[WandbCallback(),\n                     earlystoper])\n\n# save model\nmodel.save('model.h5')\n\n# initialize a new artifact to save the model\nmodel_artifact =  wandb.Artifact(\"trained-model\", \n                                 type=\"model\", \n                                 description=\"Simple model trained with spectrogram dataset formed with nfft 2024 and hop length of 512\",\n                                 metadata={'optimizer': 'Adam',\n                                          'Loss': 'Binary Cross Entropy',\n                                          'Learning Rate': 0.001})\n\nmodel_artifact.add_file('model.h5')\nrun.log_artifact(model_artifact)\n\nrun.join()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Submit predictions"},{"metadata":{"trusted":true},"cell_type":"code","source":"N_FFT = int(artifact_dir.split('_')[-3])\nHOP_LENGTH = int(artifact_dir.split('_')[-1].split(':')[0])\nSR = 48000 # high sr for less rounding errors this way\nLENGTH = 10 * SR #length of slice\n\nIMG_WIDTH = 400\nIMG_HEIGHT = 224\n\nSAVE_DIR = 'kaggle/working/'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def load_test_file(f):\n    wav, sr = librosa.load('/kaggle/input/rfcx-species-audio-detection/test/' + f, sr=None)\n\n    # Split for enough segments to not miss anything\n    segments = len(wav) / LENGTH\n    segments = int(np.ceil(segments))\n    \n    spect_array = []\n    \n    for i in range(0, segments):\n        # Last segment going from the end\n        if (i + 1) * LENGTH > len(wav):\n            wav_slice = wav[len(wav) - LENGTH:len(wav)]\n        else:\n            wav_slice = wav[i * LENGTH:(i + 1) * LENGTH]\n            \n        # spectrogram\n        stft = lb.core.stft(wav_slice, hop_length=HOP_LENGTH, n_fft=N_FFT)\n        spectrogram = np.abs(stft)\n        spectrogram = resize(spectrogram, (IMG_HEIGHT, IMG_WIDTH))\n\n        # log_spectrogram\n        log_spectrogram = lb.amplitude_to_db(spectrogram)\n        log_spectrogram = resize(log_spectrogram, (IMG_HEIGHT, IMG_WIDTH))\n\n        # mel_spectrogram\n        mel_spectrogram = lb.feature.melspectrogram(wav_slice, n_fft=N_FFT, hop_length=HOP_LENGTH, sr=sr)\n        log_mel_spectrogram = lb.amplitude_to_db(mel_spectrogram)\n        log_mel_spectrogram = resize(log_mel_spectrogram, (IMG_HEIGHT, IMG_WIDTH))\n\n        # generate image by stacking three transforms \n        img = np.stack((spectrogram, log_spectrogram, log_mel_spectrogram), axis=-1)\n\n        # normalize image\n        norm_img = stats.zscore(img)\n\n        spect_array.append(norm_img)\n    \n    return np.array(spect_array)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# for f in os.listdir('/kaggle/working/'):\n#     os.remove('/kaggle/working/' + f)\n    \n# Prediction loop\nprint('Starting prediction loop')\nwith open('submission.csv', 'w', newline='') as csvfile:\n    submission_writer = csv.writer(csvfile, delimiter=',')\n    submission_writer.writerow(['recording_id','s0','s1','s2','s3','s4','s5','s6','s7','s8','s9','s10','s11',\n                               's12','s13','s14','s15','s16','s17','s18','s19','s20','s21','s22','s23'])\n    \n    test_files = os.listdir('/kaggle/input/rfcx-species-audio-detection/test/')\n    print(len(test_files))\n    \n    # Every test file is split on several chunks and prediction is made for each chunk\n    for i in range(0, len(test_files)):\n        data = load_test_file(test_files[i])\n\n        output = model.predict(data)\n\n        # Taking max prediction from all slices per bird species\n        # Usually you want Sigmoid layer here to convert output to probabilities\n        # In this competition only relative ranking matters, and not the exact value of prediction, so we can use it directly\n        maxed_output = np.max(output, axis=0)\n        \n        file_id = str.split(test_files[i], '.')[0]\n        write_array = [file_id]\n        \n        for out in maxed_output:\n            write_array.append(out)\n    \n        submission_writer.writerow(write_array)\n        \n        if i % 100 == 0 and i > 0:\n            print('Predicted for ' + str(i) + ' of ' + str(len(test_files) + 1) + ' files')\n\nprint('Submission generated')","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}