{"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":"markdown","source":"## Libraries","metadata":{}},{"cell_type":"code","source":"!pip install -U tensorflow==2.8","metadata":{"execution":{"iopub.status.busy":"2022-05-17T15:52:17.933798Z","iopub.execute_input":"2022-05-17T15:52:17.934204Z","iopub.status.idle":"2022-05-17T15:53:59.271305Z","shell.execute_reply.started":"2022-05-17T15:52:17.934093Z","shell.execute_reply":"2022-05-17T15:53:59.270499Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport json\nfrom sklearn import preprocessing\nimport os\nfrom tqdm import tqdm\nimport librosa\nimport tensorflow as tf\nimport matplotlib.pyplot as plt","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-05-17T15:53:59.273398Z","iopub.execute_input":"2022-05-17T15:53:59.273634Z","iopub.status.idle":"2022-05-17T15:54:05.779844Z","shell.execute_reply.started":"2022-05-17T15:53:59.273605Z","shell.execute_reply":"2022-05-17T15:54:05.778659Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(tf.__version__)","metadata":{"execution":{"iopub.status.busy":"2022-05-17T15:54:05.781387Z","iopub.execute_input":"2022-05-17T15:54:05.781712Z","iopub.status.idle":"2022-05-17T15:54:05.787221Z","shell.execute_reply.started":"2022-05-17T15:54:05.781670Z","shell.execute_reply":"2022-05-17T15:54:05.786209Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Config","metadata":{}},{"cell_type":"code","source":"# Data:\nTRAIN_DIR = '../input/birdclef-2022/train_audio/'\nIMAGES_DIR = 'images/'\nSAMPLE_RATE = 32000\nVAL_SIZE = 0.2\n\n# Data processing:\nN_FFT = 2048\nHOP_LEN = 512\nWIN_FUNC = 'hann'\nN_MELS = 224\nF_MIN = 0\nF_MAX = SAMPLE_RATE / 2\n\n# Learning process:\nNAME_MODEL_0 = \"model_0_inst.h5\"\nNAME_MODEL_0_PIC = 'model_0_pic.png'\nNAME_MODEL_0_CHECKPOINT = 'model_0_cp.ckpt'\nIMAGE_HEIGHT = 256\nIMAGE_WIDTH = 256\nBATCH_SIZE = 32\nN_CHANNELS = 3\nEPOCHS = 5\nCALL_BACKS = [tf.keras.callbacks.ModelCheckpoint(\n    filepath=NAME_MODEL_0_CHECKPOINT,\n    save_weights_only=True,\n    verbose=0\n)]","metadata":{"execution":{"iopub.status.busy":"2022-05-17T15:54:05.789419Z","iopub.execute_input":"2022-05-17T15:54:05.789655Z","iopub.status.idle":"2022-05-17T15:54:05.805926Z","shell.execute_reply.started":"2022-05-17T15:54:05.789625Z","shell.execute_reply":"2022-05-17T15:54:05.804790Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Load data","metadata":{}},{"cell_type":"code","source":"train_metadata = pd.read_csv('/kaggle/input/birdclef-2022/train_metadata.csv')\ntrain_metadata.head()","metadata":{"execution":{"iopub.status.busy":"2022-05-17T15:54:05.807536Z","iopub.execute_input":"2022-05-17T15:54:05.807786Z","iopub.status.idle":"2022-05-17T15:54:05.944807Z","shell.execute_reply.started":"2022-05-17T15:54:05.807756Z","shell.execute_reply":"2022-05-17T15:54:05.943842Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Load work classes:\nwith open('/kaggle/input/birdclef-2022/scored_birds.json', 'r') as f:\n    valid_classes = json.load(f)\n\nprimary_labels = train_metadata.primary_label\n\n# Encode labels:\nencoder = preprocessing.LabelEncoder()\nlabels = encoder.fit_transform(primary_labels)\nlabels = np.uint8(labels)\n\nNUM_CLASSES = len(np.unique(labels))","metadata":{"execution":{"iopub.status.busy":"2022-05-17T15:54:05.946377Z","iopub.execute_input":"2022-05-17T15:54:05.946701Z","iopub.status.idle":"2022-05-17T15:54:05.963647Z","shell.execute_reply.started":"2022-05-17T15:54:05.946658Z","shell.execute_reply":"2022-05-17T15:54:05.963031Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Convert audio files into spectrograms","metadata":{}},{"cell_type":"code","source":"# Load audio:\ndef loadAudio(filename: str) -> np.ndarray:\n    # Load audio:\n    signal, _ = librosa.load(\n        filename,\n        sr=SAMPLE_RATE,\n        mono=True,\n        dtype=np.float32\n    )\n    \n    return signal\n\n\n# Cut the signal into frames duration 5 sec:\ndef framing(sig: np.ndarray, sample_rate: int, frame_len: int, duration_time: float) -> np.ndarray:\n    num_frames = int(np.ceil(duration_time / 5))\n    framed_sig = np.zeros((num_frames, int(frame_len * sample_rate)))\n    start_time = 0\n    end_time = frame_len * sample_rate\n    if duration_time < 5:\n        framed_sig[0][:sig.shape[0]] = sig\n    else:\n        for i in range(num_frames):\n            framed_sig[i][:end_time - start_time] = sig[start_time:end_time]\n            start_time = start_time + int(frame_len * sample_rate)\n            if i == num_frames - 2:\n                end_time = end_time + int(sig.shape[0] - start_time)\n            else:\n                end_time = end_time + int(frame_len * sample_rate)\n\n    return framed_sig\n\n\n# Convert audio frame into spectrogram:\ndef createSpectrogram(frame: np.ndarray) -> np.ndarray:\n    specgram = librosa.feature.melspectrogram(\n        y=frame,\n        sr=SAMPLE_RATE,\n        n_fft=N_FFT,\n        hop_length=HOP_LEN,\n        win_length=N_FFT,\n        window='hann',\n        center=True,\n        pad_mode='reflect',\n        power=2.0,\n        n_mels=N_MELS,\n        fmin=F_MIN,\n        fmax=F_MAX,\n        dtype=np.float32\n    )\n    specgram = librosa.amplitude_to_db(specgram, ref=np.max)\n    \n    return specgram\n\n\n# Save spectrogram as png:\ndef saveSpectogram(specgram: np.ndarray, filename: str, label: np.uint8, ind: int) -> None:\n    bird_name = filename.rsplit('/', 2)[0]\n    file_id = filename.rsplit('/', 2)[1].rsplit('.', 2)[0]\n    file_name = IMAGES_DIR + 'class_' + str(labels[i]) + '/' + bird_name + '_' + file_id + '_' + str((ind + 1) * 5) + '.png'\n    specgram = specgram + 80 # -80 dB -> Min\n    specgram = specgram.astype(np.uint8) # 0 - 255 the pixel value\n    plt.axis('off')\n    plt.imsave(file_name, specgram)\n    \n\n# Common function:\ndef convertAudio(filename: str, label: np.uint8) -> None:\n    signal = loadAudio(filename=TRAIN_DIR + filename)\n    frames = framing(\n        sig=signal,\n        sample_rate=SAMPLE_RATE,\n        frame_len=5,\n        duration_time=librosa.get_duration(\n            y=signal,\n            sr=SAMPLE_RATE\n        )\n    )\n    for i in range(frames.shape[0]):\n        specgram = createSpectrogram(frame=frames[i])\n        saveSpectogram(\n            specgram=specgram,\n            filename=filename,\n            label=label,\n            ind=i\n        )","metadata":{"execution":{"iopub.status.busy":"2022-05-17T15:54:05.966319Z","iopub.execute_input":"2022-05-17T15:54:05.967016Z","iopub.status.idle":"2022-05-17T15:54:05.983431Z","shell.execute_reply.started":"2022-05-17T15:54:05.966971Z","shell.execute_reply":"2022-05-17T15:54:05.982562Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create dirs for each class:\nos.mkdir(IMAGES_DIR)\nfor i in range(np.unique(labels).shape[0]):\n    os.mkdir(IMAGES_DIR + 'class_' + str(np.unique(labels)[i]) + '/')\n    \n# Convert audio into spectragrams:\nfor i in tqdm(range(train_metadata.shape[0])):\n    convertAudio(\n        filename=train_metadata.filename.iloc[i],\n        label=labels[i]\n    )","metadata":{"execution":{"iopub.status.busy":"2022-05-17T15:54:05.984764Z","iopub.execute_input":"2022-05-17T15:54:05.985724Z","iopub.status.idle":"2022-05-17T19:15:00.506092Z","shell.execute_reply.started":"2022-05-17T15:54:05.985678Z","shell.execute_reply":"2022-05-17T19:15:00.501772Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Preparing data","metadata":{}},{"cell_type":"code","source":"# Make a dataset containing the training spectrograms\ntrain_dataset = tf.keras.preprocessing.image_dataset_from_directory(\n    batch_size=BATCH_SIZE,\n    validation_split=VAL_SIZE,\n    directory=IMAGES_DIR,\n    shuffle=True,\n    color_mode='rgb',\n    image_size=(IMAGE_HEIGHT, IMAGE_WIDTH),\n    subset=\"training\",\n    label_mode='categorical',\n    seed=42\n)\n\n# Make a dataset containing the validation spectrogram\nvalid_dataset = tf.keras.preprocessing.image_dataset_from_directory(\n    batch_size=BATCH_SIZE,\n    validation_split=VAL_SIZE,\n    directory=IMAGES_DIR,\n    shuffle=True,\n    color_mode='rgb',\n    image_size=(IMAGE_HEIGHT, IMAGE_WIDTH),\n    subset=\"validation\",\n    label_mode='categorical',\n    seed=42\n)","metadata":{"execution":{"iopub.status.busy":"2022-05-17T19:15:00.515217Z","iopub.execute_input":"2022-05-17T19:15:00.517408Z","iopub.status.idle":"2022-05-17T19:15:15.615398Z","shell.execute_reply.started":"2022-05-17T19:15:00.517327Z","shell.execute_reply":"2022-05-17T19:15:15.614571Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Function to prepare our datasets for modelling\ndef prepare(ds, augment=False):\n    # Define our one transformation\n    rescale = tf.keras.Sequential([tf.keras.layers.experimental.preprocessing.Rescaling(1./255)])\n    flip_and_rotate = tf.keras.Sequential([\n        tf.keras.layers.experimental.preprocessing.RandomFlip(\"horizontal_and_vertical\"),\n        tf.keras.layers.experimental.preprocessing.RandomRotation(0.2)\n    ])\n    \n    # Apply rescale to both datasets and augmentation only to training\n    ds = ds.map(lambda x, y: (rescale(x, training=True), y))\n    if augment: ds = ds.map(lambda x, y: (flip_and_rotate(x, training=True), y))\n    return ds\n\ntrain_dataset = prepare(train_dataset, augment=False)\nvalid_dataset = prepare(valid_dataset, augment=False)","metadata":{"execution":{"iopub.status.busy":"2022-05-17T19:15:15.620445Z","iopub.execute_input":"2022-05-17T19:15:15.620671Z","iopub.status.idle":"2022-05-17T19:15:15.792696Z","shell.execute_reply.started":"2022-05-17T19:15:15.620644Z","shell.execute_reply":"2022-05-17T19:15:15.791945Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Build and fit the model","metadata":{}},{"cell_type":"code","source":"def getModel():\n    # Create CNN model\n    model = tf.keras.models.Sequential()\n    model.add(tf.keras.layers.Input(shape=(IMAGE_HEIGHT, IMAGE_WIDTH, N_CHANNELS)))\n    model.add(tf.keras.layers.Conv2D(32, 3, strides=2, padding='same', activation='relu'))\n    model.add(tf.keras.layers.BatchNormalization())\n    model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))\n    model.add(tf.keras.layers.BatchNormalization())\n    model.add(tf.keras.layers.Conv2D(64, 3, padding='same', activation='relu'))\n    model.add(tf.keras.layers.BatchNormalization())\n    model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))\n    model.add(tf.keras.layers.BatchNormalization())\n    model.add(tf.keras.layers.Conv2D(128, 3, padding='same', activation='relu'))\n    model.add(tf.keras.layers.BatchNormalization())\n    model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))\n    model.add(tf.keras.layers.BatchNormalization())\n    model.add(tf.keras.layers.Flatten())\n    model.add(tf.keras.layers.Dense(256, activation='relu'))\n    model.add(tf.keras.layers.BatchNormalization())\n    model.add(tf.keras.layers.Dropout(0.5))\n    model.add(tf.keras.layers.Dense(np.unique(labels).shape[0], activation='softmax'))\n\n    # Compile model\n    model.compile(\n        loss='categorical_crossentropy',\n        optimizer=tf.keras.optimizers.RMSprop(),\n        metrics=[\n            'accuracy',\n            tf.keras.metrics.Precision(),\n            tf.keras.metrics.Recall()\n        ],\n    )\n    \n    return model\n\n\ndef plotMetrics(history):\n    metrics = list()\n    for key, value in history.history.items():\n        metrics.append(key)\n        \n    for i in range(int(len(metrics) / 2)):\n        plt.figure(figsize=(24, 6))\n        plt.plot(history.history[metrics[i]], c =\"darkblue\")\n        plt.plot(history.history[metrics[i + int(len(metrics) / 2)]], c =\"crimson\")\n        plt.legend([\"Train\", \"Validation\"])\n        plt.title(\"Model\" + metrics[i])\n        plt.xlabel(\"Epoch\")\n        plt.ylabel(metrics[i])\n        plt.grid(True, alpha = 0.2)\n        plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-05-17T19:15:15.793834Z","iopub.execute_input":"2022-05-17T19:15:15.794194Z","iopub.status.idle":"2022-05-17T19:15:15.810167Z","shell.execute_reply.started":"2022-05-17T19:15:15.794164Z","shell.execute_reply":"2022-05-17T19:15:15.809227Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = getModel()\ntf.keras.utils.plot_model(model, NAME_MODEL_0_PIC, show_shapes=True)\nhistory = model.fit(\n    train_dataset,\n    epochs=EPOCHS,\n    validation_data=valid_dataset,\n    callbacks=CALL_BACKS\n)\nmodel.save(NAME_MODEL_0)","metadata":{"execution":{"iopub.status.busy":"2022-05-17T19:15:15.811483Z","iopub.execute_input":"2022-05-17T19:15:15.811809Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plotMetrics(history)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#model.save(NAME_MODEL_0)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Make prediction","metadata":{}},{"cell_type":"code","source":"\"\"\"pred = {\n    'row_id': list(),\n    'target': list()\n}\n\ntest_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    \nfor f in files:\n    p = test_path + f + '.ogg'\n    \n    sig = loadAudio(filename=p)\n    duration_sig = librosa.get_duration(y=sig, sr=SAMPLE_RATE)\n    sig_framed = framing(sig=sig, sample_rate=SAMPLE_RATE, frame_len=5, duration_time=duration_sig)\n    \n    for i in range(sig_framed.shape[0]):\n        # Get prediction:\n        specgram = createSpectrogram(frame=sig_framed[i])\n        specgram = specgram + 80 # -80 dB -> Min\n        specgram = specgram.astype(np.uint8) # 0 - 255 the pixel value\n\n        y_pred = model.predict(specgram)\n        predicted_class = np.argmax(y_pred)\n\n        for b in birds:  \n            segment_end = (i + 1) * 5\n            row_id = f + '_' + b + '_' + str(segment_end)\n            pred['row_id'].append(row_id)\n            label_inv = encoder.inverse_transform([predicted_class])\n            if label_inv[0] == b:\n                pred['target'].append(True)\n            else:\n                pred['target'].append(False)\"\"\"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#cols = ['row_id','target']\n#df_sub = pd.DataFrame(pred, columns=cols)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#df_sub.to_csv('/kaggle/working/submission.csv', index=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}