{"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":"## Bird Species Classification with EfficientNet\n## Table of Contents\n* Overview\n* Import Libraries\n* Configuration\n* Helper Functions\n* Load data\n* Exploratory Data Analysis\n* Create TensorFlow Dataset\n* Model Development\n* Model Evaluation\n* Create submission file\n* Conclusion\n\n## Overview\nIn this notebook, I will create a Bird Species Classification Model from scratch. I will train this model using [BirdCLEF 2023 competition dataset](https://www.kaggle.com/competitions/birdclef-2023), this dataset contains 16941 audio files of 264 kinds of bird species. This is an audio classification problem, one way to solve this problem is to convert audio files to spectrogram images and build an image classifier. Here are basic steps:\n* Load and preprocess sound files using tensorflow-io Library.\n* Randomly sample 5-second sound clip files.\n* Convert sound files to spectrogram image with (256, 256, 3) shape.\n* Create training and validation TensorFlow dataset.\n* Create an image classification model using EfficientNet backbone that can accepts image with shape (n, 256, 256, 3) as input and output probabilities with shape (n, 264).\n","metadata":{}},{"cell_type":"markdown","source":"## Import Libraries","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport tensorflow_io as tfio\nfrom IPython.display import Audio\nfrom sklearn.model_selection import train_test_split\nimport matplotlib.pyplot as plt\nimport sklearn.metrics\nimport json\nimport tensorflow as tf\nimport os\nimport glob","metadata":{"execution":{"iopub.status.busy":"2023-03-21T07:47:00.526210Z","iopub.execute_input":"2023-03-21T07:47:00.526605Z","iopub.status.idle":"2023-03-21T07:47:00.533429Z","shell.execute_reply.started":"2023-03-21T07:47:00.526570Z","shell.execute_reply":"2023-03-21T07:47:00.531922Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Configuration","metadata":{}},{"cell_type":"code","source":"class CFG:\n    image_size = [256, 256]\n    is_training = False\n    epochs = 10","metadata":{"execution":{"iopub.status.busy":"2023-03-21T07:47:02.952796Z","iopub.execute_input":"2023-03-21T07:47:02.953193Z","iopub.status.idle":"2023-03-21T07:47:02.958396Z","shell.execute_reply.started":"2023-03-21T07:47:02.953158Z","shell.execute_reply":"2023-03-21T07:47:02.957129Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Helper Functions","metadata":{}},{"cell_type":"code","source":"def padded_cmap(solution, submission, padding_factor=5):\n    solution = solution.drop(['row_id'], axis=1, errors='ignore')\n    submission = submission.drop(['row_id'], axis=1, errors='ignore')\n    new_rows = []\n    for i in range(padding_factor):\n        new_rows.append([1 for i in range(len(solution.columns))])\n    new_rows = pd.DataFrame(new_rows)\n    new_rows.columns = solution.columns\n    padded_solution = pd.concat([solution, new_rows]).reset_index(drop=True).copy()\n    padded_submission = pd.concat([submission, new_rows]).reset_index(drop=True).copy()\n    score = sklearn.metrics.average_precision_score(\n        padded_solution.values,\n        padded_submission.values,\n        average='macro',\n    )\n    return score\n\n\ndef preprocess(audio_url, label):\n    audio_string = tf.io.read_file(audio_url)\n    audio = tfio.audio.decode_vorbis(audio_string)\n    audio_tensor = tf.squeeze(audio, axis=[-1])\n    diff = tf.cast(tf.shape(audio_tensor)[0] - 5 * 32000, tf.float32)\n    begin = tf.cast(tf.random.uniform(shape=()) * diff, tf.int32)\n    start_position = tf.where(diff > 0, begin, 0)\n    end_position = tf.where(diff > 0, start_position + 5 * 32000, tf.shape(audio_tensor)[0])\n    audio_tensor = audio_tensor[start_position:end_position]\n    tensor = tf.cast(audio_tensor, tf.float32) / 32768.0\n    spectrogram = tfio.audio.spectrogram(tensor, nfft=512, window=512, stride=256)\n    spectrogram = tfio.audio.dbscale(spectrogram, top_db=80)\n    spectrogram = tf.expand_dims(spectrogram, axis=-1)\n    spectrogram = tf.image.resize(spectrogram, CFG.image_size)\n    spectrogram = (spectrogram - tf.reduce_min(spectrogram)) / (tf.reduce_max(spectrogram) - tf.reduce_min(spectrogram)) * 255.0\n    return spectrogram, label\n\ndef preprocess_test(audio_tensor):\n    tensor = tf.cast(audio_tensor, tf.float32) / 32768.0\n    spectrogram = tfio.audio.spectrogram(tensor, nfft=512, window=512, stride=256)\n    spectrogram = tfio.audio.dbscale(spectrogram, top_db=80)\n    spectrogram = tf.expand_dims(spectrogram, axis=-1)\n    spectrogram = tf.image.resize(spectrogram, CFG.image_size)\n    spectrogram = (spectrogram - tf.reduce_min(spectrogram)) / (tf.reduce_max(spectrogram) - tf.reduce_min(spectrogram)) * 255.0\n    return tf.expand_dims(spectrogram, axis=0)\n\ndef make_dataset(df, batch_size=128, shuffle=True):\n    ds = tf.data.Dataset.from_tensor_slices((df[\"file_path\"], df[\"label\"]))\n    ds = ds.map(preprocess)\n    if shuffle:\n        ds = ds.shuffle(batch_size * 4)\n    ds = ds.batch(batch_size).prefetch(tf.data.AUTOTUNE)\n    return ds\n\ndef make_inference(tensor):\n    image = preprocess_test(tensor)\n    return model.predict(image)\n\ndef frame_audio(\n      audio_array: np.ndarray,\n      window_size_s: float = 5.0,\n      hop_size_s: float = 5.0,\n      sample_rate = 32000,\n      ) -> np.ndarray:\n    \n    \"\"\"Helper function for framing audio for inference.\"\"\"\n    \"\"\" using tf.signal \"\"\"\n    if window_size_s is None or window_size_s < 0:\n        return audio_array[np.newaxis, :]\n    frame_length = int(window_size_s * sample_rate)\n    hop_length = int(hop_size_s * sample_rate)\n    framed_audio = tf.signal.frame(audio_array, frame_length, hop_length, pad_end=True)\n    return framed_audio\n\ndef ensure_sample_rate(waveform, original_sample_rate,\n                       desired_sample_rate=32000):\n    \"\"\"Resample waveform if required.\"\"\"\n    if original_sample_rate != desired_sample_rate:\n        waveform = tfio.audio.resample(waveform, original_sample_rate, desired_sample_rate)\n    return desired_sample_rate, waveform\n\ndef preprocess_test(audio_tensor):\n    tensor = tf.cast(audio_tensor, tf.float32) / 32768.0\n    spectrogram = tfio.audio.spectrogram(tensor, nfft=512, window=512, stride=256)\n    spectrogram = tfio.audio.dbscale(spectrogram, top_db=80)\n    spectrogram = tf.expand_dims(spectrogram, axis=-1)\n    spectrogram = tf.image.resize(spectrogram, (256, 256))\n    spectrogram = (spectrogram - tf.reduce_min(spectrogram)) / (tf.reduce_max(spectrogram) - tf.reduce_min(spectrogram)) * 255.0\n    return spectrogram\n\ndef predict_for_sample(filename, sample_submission, frame_limit_secs=None):\n    file_id = filename.split(\".ogg\")[0].split(\"/\")[-1]\n    audio = tfio.audio.AudioIOTensor(filename)\n    sample_rate = audio.rate.numpy()\n    audio_tensor = tf.squeeze(audio[0:], axis=[-1])\n    sample_rate, wav_data = ensure_sample_rate(audio_tensor, sample_rate)\n    fixed_tm = frame_audio(wav_data)\n    frame = 5\n    all_logits = make_inference(fixed_tm[:1])\n    for window in fixed_tm[1:]:\n        if frame_limit_secs and frame > frame_limit_secs:\n            continue\n        logits = make_inference(window[np.newaxis, :])\n        all_logits = np.concatenate([all_logits, logits], axis=0)\n        frame += 5\n    frame = 5\n    all_probabilities = []\n    for frame_logits in all_logits:\n        probabilities = tf.nn.softmax(frame_logits).numpy()\n        ## set the appropriate row in the sample submission\n        sample_submission.loc[sample_submission.row_id == file_id + \"_\" + str(frame), labels] = probabilities\n        frame += 5","metadata":{"execution":{"iopub.status.busy":"2023-03-21T07:47:06.082946Z","iopub.execute_input":"2023-03-21T07:47:06.084129Z","iopub.status.idle":"2023-03-21T07:47:06.116906Z","shell.execute_reply.started":"2023-03-21T07:47:06.084086Z","shell.execute_reply":"2023-03-21T07:47:06.115538Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Load data","metadata":{}},{"cell_type":"code","source":"train = pd.read_csv(\"../input/birdclef-2023/train_metadata.csv\")\ntrain.head()","metadata":{"execution":{"iopub.status.busy":"2023-03-21T07:47:12.255525Z","iopub.execute_input":"2023-03-21T07:47:12.255928Z","iopub.status.idle":"2023-03-21T07:47:12.334255Z","shell.execute_reply.started":"2023-03-21T07:47:12.255881Z","shell.execute_reply":"2023-03-21T07:47:12.332872Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission = pd.read_csv(\"../input/birdclef-2023/sample_submission.csv\")\nsubmission.head()","metadata":{"execution":{"iopub.status.busy":"2023-03-21T06:23:48.494041Z","iopub.execute_input":"2023-03-21T06:23:48.495103Z","iopub.status.idle":"2023-03-21T06:23:48.533708Z","shell.execute_reply.started":"2023-03-21T06:23:48.495047Z","shell.execute_reply":"2023-03-21T06:23:48.532246Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels = list(submission.columns)\nlabels.remove(\"row_id\")\nprint(labels)","metadata":{"execution":{"iopub.status.busy":"2023-03-21T06:23:51.872440Z","iopub.execute_input":"2023-03-21T06:23:51.872868Z","iopub.status.idle":"2023-03-21T06:23:51.880289Z","shell.execute_reply.started":"2023-03-21T06:23:51.872828Z","shell.execute_reply":"2023-03-21T06:23:51.878868Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Exploratory Data Analysis","metadata":{}},{"cell_type":"markdown","source":"There are 264 kinds of birds. Some kind of birds only have 1 sample. It's even challenging to create a Cross Validation Strategy. Before I figure out a better CV strategy, I will start with train validation split with random seed 42.","metadata":{}},{"cell_type":"code","source":"train.primary_label.value_counts()","metadata":{"execution":{"iopub.status.busy":"2023-03-21T07:48:24.032459Z","iopub.execute_input":"2023-03-21T07:48:24.033302Z","iopub.status.idle":"2023-03-21T07:48:24.044196Z","shell.execute_reply.started":"2023-03-21T07:48:24.033257Z","shell.execute_reply":"2023-03-21T07:48:24.042836Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train.secondary_labels.value_counts()","metadata":{"execution":{"iopub.status.busy":"2023-03-21T07:48:29.184640Z","iopub.execute_input":"2023-03-21T07:48:29.185051Z","iopub.status.idle":"2023-03-21T07:48:29.196074Z","shell.execute_reply.started":"2023-03-21T07:48:29.185014Z","shell.execute_reply":"2023-03-21T07:48:29.194960Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train[\"label\"] = train[\"primary_label\"].map(lambda primary_label: labels.index(primary_label))\ntrain.head()","metadata":{"execution":{"iopub.status.busy":"2023-03-21T07:48:33.290714Z","iopub.execute_input":"2023-03-21T07:48:33.291883Z","iopub.status.idle":"2023-03-21T07:48:33.365627Z","shell.execute_reply.started":"2023-03-21T07:48:33.291839Z","shell.execute_reply":"2023-03-21T07:48:33.364364Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train[\"file_path\"] = train[\"filename\"].apply(lambda filename: os.path.join(f\"/kaggle/input/birdclef-2023/train_audio/{filename}\"))\ntrain.head()","metadata":{"execution":{"iopub.status.busy":"2023-03-21T07:48:38.107777Z","iopub.execute_input":"2023-03-21T07:48:38.108165Z","iopub.status.idle":"2023-03-21T07:48:38.154098Z","shell.execute_reply.started":"2023-03-21T07:48:38.108131Z","shell.execute_reply":"2023-03-21T07:48:38.153080Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Number Of Samples","metadata":{}},{"cell_type":"code","source":"len(train)","metadata":{"execution":{"iopub.status.busy":"2023-03-21T07:48:43.295771Z","iopub.execute_input":"2023-03-21T07:48:43.296563Z","iopub.status.idle":"2023-03-21T07:48:43.303283Z","shell.execute_reply.started":"2023-03-21T07:48:43.296517Z","shell.execute_reply":"2023-03-21T07:48:43.302380Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Create Audio Tensor\nLet's create an Audio Tensor and play the sound.","metadata":{}},{"cell_type":"code","source":"audio = tfio.audio.AudioIOTensor(\"/kaggle/input/birdclef-2023/train_audio/blakit1/XC115289.ogg\")\naudio_tensor = tf.squeeze(audio[0:], axis=[-1])\nAudio(audio_tensor.numpy(), rate=audio.rate.numpy())","metadata":{"execution":{"iopub.status.busy":"2023-03-21T07:48:46.473533Z","iopub.execute_input":"2023-03-21T07:48:46.473980Z","iopub.status.idle":"2023-03-21T07:48:46.590125Z","shell.execute_reply.started":"2023-03-21T07:48:46.473938Z","shell.execute_reply":"2023-03-21T07:48:46.588549Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Show this audio clip in graph.","metadata":{}},{"cell_type":"code","source":"tensor = tf.cast(audio_tensor, tf.float32) / 32768.0\nplt.figure()\nplt.plot(tensor.numpy())","metadata":{"execution":{"iopub.status.busy":"2023-03-21T07:48:52.878443Z","iopub.execute_input":"2023-03-21T07:48:52.878813Z","iopub.status.idle":"2023-03-21T07:48:53.343487Z","shell.execute_reply.started":"2023-03-21T07:48:52.878780Z","shell.execute_reply":"2023-03-21T07:48:53.342346Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Show this audio clip to spectrogram.","metadata":{}},{"cell_type":"code","source":"# Convert to spectrogram\ntensor = tf.cast(audio_tensor, tf.float32) \nspectrogram = tfio.audio.spectrogram(tensor, nfft=512, window=512, stride=256)\nspectrogram = tf.math.log(spectrogram)\nplt.imshow(spectrogram)","metadata":{"execution":{"iopub.status.busy":"2023-03-21T07:48:58.094120Z","iopub.execute_input":"2023-03-21T07:48:58.094520Z","iopub.status.idle":"2023-03-21T07:48:58.415070Z","shell.execute_reply.started":"2023-03-21T07:48:58.094484Z","shell.execute_reply":"2023-03-21T07:48:58.413835Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The shape of spectrogram of a 5-second audio clip will be about (625, 257), for simplicity I will use (256, 256) as shape of image classification model input.","metadata":{}},{"cell_type":"code","source":"# Convert to spectrogram\nspectrogram = tfio.audio.spectrogram(tensor[0:audio.rate * 5], nfft=512, window=512, stride=256)\nspectrogram = tfio.audio.dbscale(spectrogram, top_db=80)\n\nspectrogram = (spectrogram - tf.reduce_min(spectrogram)) / (tf.reduce_max(spectrogram) - tf.reduce_min(spectrogram)) * 255.0\nplt.figure()\nplt.imshow(spectrogram.numpy())","metadata":{"execution":{"iopub.status.busy":"2023-03-21T07:49:03.203343Z","iopub.execute_input":"2023-03-21T07:49:03.203746Z","iopub.status.idle":"2023-03-21T07:49:03.412610Z","shell.execute_reply.started":"2023-03-21T07:49:03.203713Z","shell.execute_reply":"2023-03-21T07:49:03.411358Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Create TensorFlow Dataset","metadata":{}},{"cell_type":"code","source":"train_df, valid_df = train_test_split(train, test_size=0.2, shuffle=True, random_state=42)","metadata":{"execution":{"iopub.status.busy":"2023-03-21T06:24:52.818862Z","iopub.execute_input":"2023-03-21T06:24:52.819292Z","iopub.status.idle":"2023-03-21T06:24:52.838599Z","shell.execute_reply.started":"2023-03-21T06:24:52.819255Z","shell.execute_reply":"2023-03-21T06:24:52.837690Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.head()","metadata":{"execution":{"iopub.status.busy":"2023-03-21T06:24:55.924023Z","iopub.execute_input":"2023-03-21T06:24:55.924871Z","iopub.status.idle":"2023-03-21T06:24:55.946511Z","shell.execute_reply.started":"2023-03-21T06:24:55.924809Z","shell.execute_reply":"2023-03-21T06:24:55.944981Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"valid_df.head()","metadata":{"execution":{"iopub.status.busy":"2023-03-21T06:24:58.498976Z","iopub.execute_input":"2023-03-21T06:24:58.499367Z","iopub.status.idle":"2023-03-21T06:24:58.521311Z","shell.execute_reply.started":"2023-03-21T06:24:58.499332Z","shell.execute_reply":"2023-03-21T06:24:58.520270Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"valid_ds = make_dataset(valid_df, shuffle=False)","metadata":{"execution":{"iopub.status.busy":"2023-03-21T06:25:04.310801Z","iopub.execute_input":"2023-03-21T06:25:04.311207Z","iopub.status.idle":"2023-03-21T06:25:04.838050Z","shell.execute_reply.started":"2023-03-21T06:25:04.311173Z","shell.execute_reply":"2023-03-21T06:25:04.836856Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The input shape and output shape of training data will be (n, 256, 256, 1) and (n). During training, target label will be converted to onehot tensor with 264 classes.","metadata":{}},{"cell_type":"code","source":"for X, y in valid_ds.take(1):\n    print(X.shape, y.shape)","metadata":{"execution":{"iopub.status.busy":"2023-03-21T06:25:06.873824Z","iopub.execute_input":"2023-03-21T06:25:06.875122Z","iopub.status.idle":"2023-03-21T06:25:15.027135Z","shell.execute_reply.started":"2023-03-21T06:25:06.875074Z","shell.execute_reply":"2023-03-21T06:25:15.025788Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model Development","metadata":{}},{"cell_type":"code","source":"if CFG.is_training:\n    train_ds = make_dataset(train_df)\n    def get_model():\n        inputs = tf.keras.Input(shape=(CFG.image_size[0], CFG.image_size[1], 1))\n        image_inputs = tf.concat([\n            inputs,\n            inputs,\n            inputs\n        ], axis=-1)\n        vector = efficent_net(image_inputs)\n        output = tf.keras.layers.Dense(264, activation=\"softmax\")(vector)\n        model = tf.keras.Model(inputs=inputs, outputs=output)\n        model.compile(loss=tf.keras.losses.SparseCategoricalCrossentropy(), optimizer=tf.keras.optimizers.Adam(1e-3), metrics=[\"accuracy\"])\n        return model\n    efficent_net = tf.keras.applications.EfficientNetV2S(include_top=False, pooling=\"max\")\n    efficent_net.trainable = False\n    efficent_net.summary()\n    model = get_model()\n    callbacks = [\n        tf.keras.callbacks.ModelCheckpoint(\n            \"model.h5\", \n            save_best_only=True\n        ),\n        tf.keras.callbacks.EarlyStopping(\n            min_delta=1e-4, \n            patience=10\n        ),\n        tf.keras.callbacks.ReduceLROnPlateau(\n            factor=0.3,\n            patience=2, \n            min_lr=1e-7\n        ),\n        tf.keras.callbacks.TerminateOnNaN()\n    ]\n    model.fit(train_ds, epochs=CFG.epochs, validation_data=valid_ds, callbacks=callbacks)\nelse:\n    model = tf.keras.models.load_model(\"/kaggle/input/bird-clef/model.h5\")\nmodel.summary()\ntf.keras.utils.plot_model(model, show_shapes=True)","metadata":{"execution":{"iopub.status.busy":"2023-03-21T06:26:02.323614Z","iopub.execute_input":"2023-03-21T06:26:02.324011Z","iopub.status.idle":"2023-03-21T06:26:11.721606Z","shell.execute_reply.started":"2023-03-21T06:26:02.323979Z","shell.execute_reply":"2023-03-21T06:26:11.720076Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model Evaluation","metadata":{}},{"cell_type":"code","source":"y_preds = model.predict(valid_ds)\ny_pred_labels = np.argmax(y_preds, axis=1)","metadata":{"execution":{"iopub.status.busy":"2023-03-21T06:38:59.984284Z","iopub.execute_input":"2023-03-21T06:38:59.985022Z","iopub.status.idle":"2023-03-21T06:49:22.047678Z","shell.execute_reply.started":"2023-03-21T06:38:59.984979Z","shell.execute_reply":"2023-03-21T06:49:22.046320Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df = pd.DataFrame({\"row_id\": valid_df.index}).copy()\nfor i, column in enumerate(labels):\n    submission_df[column] = y_preds[:, i]\ntrue_labels = list(valid_df[\"label\"])\nsolution_df = pd.DataFrame({\"row_id\": valid_df.index}).copy()\nfor column in labels:\n    solution_df[column] = 0\nfor i in range(len(valid_df)):\n    secondary_labels = valid_df.iloc[i][\"secondary_labels\"]\n    secondary_labels = secondary_labels.replace(\"\\'\", \"\\\"\")\n    arr = json.loads(secondary_labels)\n    solution_df.loc[i, labels[true_labels[i]]] = 1\n    if len(arr) > 0:\n        for secondary_label in arr:\n            idx = labels.index(secondary_label)\n            if idx >= 0 and idx < len(labels):\n                solution_df.loc[i, labels[true_labels[idx]]] = 1\nscore = padded_cmap(solution_df, submission_df)\nprint(f\"CV:{score}\")","metadata":{"execution":{"iopub.status.busy":"2023-03-19T08:14:57.312257Z","iopub.execute_input":"2023-03-19T08:14:57.313553Z","iopub.status.idle":"2023-03-19T08:14:58.690738Z","shell.execute_reply.started":"2023-03-19T08:14:57.313506Z","shell.execute_reply":"2023-03-19T08:14:58.689818Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Create Submission file","metadata":{}},{"cell_type":"code","source":"test_samples = list(glob.glob(\"/kaggle/input/birdclef-2023/test_soundscapes/*.ogg\"))\nsubmission = pd.read_csv(\"../input/birdclef-2023/sample_submission.csv\")\nsubmission[labels] = submission[labels].astype(np.float32)\nfor filename in test_samples:\n    predict_for_sample(filename, submission, frame_limit_secs=15)\nsubmission.to_csv(\"submission.csv\", index=False)\nsubmission.head()","metadata":{"execution":{"iopub.status.busy":"2023-03-21T06:27:05.146422Z","iopub.execute_input":"2023-03-21T06:27:05.146851Z","iopub.status.idle":"2023-03-21T06:27:05.248255Z","shell.execute_reply.started":"2023-03-21T06:27:05.146810Z","shell.execute_reply":"2023-03-21T06:27:05.247079Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Conclusion\nThis Model can achieve about 0.58 CV and 0.71 LB, a little bit lower than the [baseline notebook](https://www.kaggle.com/code/philculliton/inferring-birds-with-kaggle-models), good enough for a notebook written from scratch. There's still a lot of space to improve. For example:\n* Create a better cross validation strategy.\n* Better way to create spectrogram image.\n* Better sampling method.\n* Better Neural Architecture and better pretrained model.\n* Build a Sequence Model that can handle long spectrogram image.","metadata":{}}]}