{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":59093,"databundleVersionId":7469972,"sourceType":"competition"},{"sourceId":8195538,"sourceType":"datasetVersion","datasetId":4854331},{"sourceId":6127,"sourceType":"modelInstanceVersion","modelInstanceId":4598,"modelId":2797}],"dockerImageVersionId":31090,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"I have used help from a starter notebook and then I have made my own adjustments and additions. The plan is to use their model, try to improve it, and then use some other models and compare them.\n\nI will do my best to label everything that I have borrowed from other authors or generated with ChatGPT 5.","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\n\n#Installing the libraries\n!pip install -q /kaggle/input/kerasv3-lib-ds/keras_cv-0.8.2-py3-none-any.whl --no-deps\n!pip install -q /kaggle/input/kerasv3-lib-ds/tensorflow-2.15.0.post1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl --no-deps\n!pip install -q /kaggle/input/kerasv3-lib-ds/keras-3.0.4-py3-none-any.whl --no-deps\n\n\n#Importing the libraries\nimport os\nos.environ[\"KERAS_BACKEND\"] = \"jax\" # you can also use tensorflow or torch\n\nimport keras_cv\nimport keras\nfrom keras import ops\nimport tensorflow as tf\n\nimport cv2\nimport pandas as pd\nimport numpy as np\nfrom glob import glob\nfrom tqdm.notebook import tqdm\nimport joblib\n\nimport matplotlib.pyplot as plt \n\n\n#Printing the library versions\nprint(\"TensorFlow:\", tf.__version__)\nprint(\"Keras:\", keras.__version__)\nprint(\"KerasCV:\", keras_cv.__version__)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:16:38.872485Z","iopub.execute_input":"2025-10-09T10:16:38.872717Z","iopub.status.idle":"2025-10-09T10:17:07.126251Z","shell.execute_reply.started":"2025-10-09T10:16:38.872696Z","shell.execute_reply":"2025-10-09T10:17:07.125418Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Now comes the configuration which also is provided by the \"HMS-HBAC: KerasCV Starter Notebook\".","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\nclass CFG:\n    verbose = 1  # Verbosity\n    seed = 42  # Random seed\n    preset = \"efficientnetv2_b2_imagenet\"  # Name of pretrained classifier\n    image_size = [400, 300]  # Input image size\n    epochs = 13 # Training epochs\n    batch_size = 64  # Batch size\n    lr_mode = \"cos\" # LR scheduler mode from one of \"cos\", \"step\", \"exp\"\n    drop_remainder = True  # Drop incomplete batches\n    num_classes = 6 # Number of classes in the dataset\n    fold = 0 # Which fold to set as validation data\n    class_names = ['Seizure', 'LPD', 'GPD', 'LRDA','GRDA', 'Other']\n    label2name = dict(enumerate(class_names))\n    name2label = {v:k for k, v in label2name.items()}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:17:07.126907Z","iopub.execute_input":"2025-10-09T10:17:07.127388Z","iopub.status.idle":"2025-10-09T10:17:07.133224Z","shell.execute_reply.started":"2025-10-09T10:17:07.127362Z","shell.execute_reply":"2025-10-09T10:17:07.132253Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Setting value for random seed to reduce variability between runs","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\nkeras.utils.set_random_seed(CFG.seed)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:17:07.135358Z","iopub.execute_input":"2025-10-09T10:17:07.135634Z","iopub.status.idle":"2025-10-09T10:17:07.162140Z","shell.execute_reply.started":"2025-10-09T10:17:07.135616Z","shell.execute_reply":"2025-10-09T10:17:07.161264Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"The base dataset path","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\nBASE_PATH = \"/kaggle/input/hms-harmful-brain-activity-classification\"\n\nSPEC_DIR = \"/tmp/dataset/hms-hbac\"\nos.makedirs(SPEC_DIR+'/train_spectrograms', exist_ok=True)\nos.makedirs(SPEC_DIR+'/test_spectrograms', exist_ok=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:17:07.163085Z","iopub.execute_input":"2025-10-09T10:17:07.163339Z","iopub.status.idle":"2025-10-09T10:17:07.183405Z","shell.execute_reply.started":"2025-10-09T10:17:07.163320Z","shell.execute_reply":"2025-10-09T10:17:07.182649Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Then there is Meta Data which is the data about the data, not the raw data.\nIt makes it easier to access the correct data while training and testing.","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\n# Train + Valid\ndf = pd.read_csv(f'{BASE_PATH}/train.csv')\ndf['eeg_path'] = f'{BASE_PATH}/train_eegs/'+df['eeg_id'].astype(str)+'.parquet'\ndf['spec_path'] = f'{BASE_PATH}/train_spectrograms/'+df['spectrogram_id'].astype(str)+'.parquet'\ndf['spec2_path'] = f'{SPEC_DIR}/train_spectrograms/'+df['spectrogram_id'].astype(str)+'.npy'\ndf['class_name'] = df.expert_consensus.copy()\ndf['class_label'] = df.expert_consensus.map(CFG.name2label)\ndisplay(df.head(2))\n\n# Test\ntest_df = pd.read_csv(f'{BASE_PATH}/test.csv')\ntest_df['eeg_path'] = f'{BASE_PATH}/test_eegs/'+test_df['eeg_id'].astype(str)+'.parquet'\ntest_df['spec_path'] = f'{BASE_PATH}/test_spectrograms/'+test_df['spectrogram_id'].astype(str)+'.parquet'\ntest_df['spec2_path'] = f'{SPEC_DIR}/test_spectrograms/'+test_df['spectrogram_id'].astype(str)+'.npy'\ndisplay(test_df.head(2))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:17:07.184457Z","iopub.execute_input":"2025-10-09T10:17:07.184783Z","iopub.status.idle":"2025-10-09T10:17:07.652550Z","shell.execute_reply.started":"2025-10-09T10:17:07.184751Z","shell.execute_reply":"2025-10-09T10:17:07.651840Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Now we change the file format from the large and slow parquet files into fast and efficient npy files. We use all cpu cores in parallel to do it faster and then save them into npy files.","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\n# Define a function to process a single eeg_id\ndef process_spec(spec_id, split=\"train\"):\n    spec_path = f\"{BASE_PATH}/{split}_spectrograms/{spec_id}.parquet\"\n    spec = pd.read_parquet(spec_path)\n    spec = spec.fillna(0).values[:, 1:].T # fill NaN values with 0, transpose for (Time, Freq) -> (Freq, Time)\n    spec = spec.astype(\"float32\")\n    np.save(f\"{SPEC_DIR}/{split}_spectrograms/{spec_id}.npy\", spec)\n\n# Get unique spec_ids of train and valid data\nspec_ids = df[\"spectrogram_id\"].unique()\n\n# Parallelize the processing using joblib for training data\n_ = joblib.Parallel(n_jobs=-1, backend=\"loky\")(\n    joblib.delayed(process_spec)(spec_id, \"train\")\n    for spec_id in tqdm(spec_ids, total=len(spec_ids))\n)\n\n# Get unique spec_ids of test data\ntest_spec_ids = test_df[\"spectrogram_id\"].unique()\n\n# Parallelize the processing using joblib for test data\n_ = joblib.Parallel(n_jobs=-1, backend=\"loky\")(\n    joblib.delayed(process_spec)(spec_id, \"test\")\n    for spec_id in tqdm(test_spec_ids, total=len(test_spec_ids))\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:17:07.653392Z","iopub.execute_input":"2025-10-09T10:17:07.654182Z","iopub.status.idle":"2025-10-09T10:20:16.373566Z","shell.execute_reply.started":"2025-10-09T10:17:07.654161Z","shell.execute_reply":"2025-10-09T10:20:16.372861Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"This following code block is about preparing the spectrogram data for training with augmenters, decoders, and dataset formation.","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\n\n#This code uses some tricks to make the to-be model more robust to missing features\n#such as missing frequencies, missing times, and mixups of two images.\ndef build_augmenter(dim=CFG.image_size):\n    augmenters = [\n        keras_cv.layers.MixUp(alpha=2.0),\n        keras_cv.layers.RandomCutout(height_factor=(1.0, 1.0),\n                                     width_factor=(0.06, 0.1)), # freq-masking\n        keras_cv.layers.RandomCutout(height_factor=(0.06, 0.1),\n                                     width_factor=(1.0, 1.0)), # time-masking\n    ]\n    \n    def augment(img, label):\n        data = {\"images\":img, \"labels\":label}\n        for augmenter in augmenters:\n            if tf.random.uniform([]) < 0.5:\n                data = augmenter(data, training=True)\n        return data[\"images\"], data[\"labels\"]\n    \n    return augment\n\n\ndef build_decoder(with_labels=True, target_size=CFG.image_size, dtype=32):\n    def decode_signal(path, offset=None):\n        # Read .npy files and process the signal\n        file_bytes = tf.io.read_file(path)\n        sig = tf.io.decode_raw(file_bytes, tf.float32)\n        sig = sig[1024//dtype:]  # Remove header tag\n        sig = tf.reshape(sig, [400, -1])\n        \n        # Extract labeled subsample from full spectrogram using \"offset\"\n        if offset is not None: \n            offset = offset // 2  # Only odd values are given\n            sig = sig[:, offset:offset+300]\n            \n            # Pad spectrogram to ensure the same input shape of [400, 300]\n            pad_size = tf.math.maximum(0, 300 - tf.shape(sig)[1])\n            sig = tf.pad(sig, [[0, 0], [0, pad_size]])\n            sig = tf.reshape(sig, [400, 300])\n        \n        # Log spectrogram \n        sig = tf.clip_by_value(sig, tf.math.exp(-4.0), tf.math.exp(8.0)) # avoid 0 in log\n        sig = tf.math.log(sig)\n        \n        # Normalize spectrogram\n        sig -= tf.math.reduce_mean(sig)\n        sig /= tf.math.reduce_std(sig) + 1e-6\n        \n        # Mono channel to 3 channels to use \"ImageNet\" weights\n        sig = tf.tile(sig[..., None], [1, 1, 3])\n        return sig\n    \n    def decode_label(label):\n        label = tf.one_hot(label, CFG.num_classes)\n        label = tf.cast(label, tf.float32)\n        label = tf.reshape(label, [CFG.num_classes])\n        return label\n    \n    def decode_with_labels(path, offset=None, label=None):\n        sig = decode_signal(path, offset)\n        label = decode_label(label)\n        return (sig, label)\n    \n    return decode_with_labels if with_labels else decode_signal\n\n\ndef build_dataset(paths, offsets=None, labels=None, batch_size=32, cache=True,\n                  decode_fn=None, augment_fn=None,\n                  augment=False, repeat=True, shuffle=1024, \n                  cache_dir=\"\", drop_remainder=False):\n    if cache_dir != \"\" and cache is True:\n        os.makedirs(cache_dir, exist_ok=True)\n    \n    if decode_fn is None:\n        decode_fn = build_decoder(labels is not None)\n    \n    if augment_fn is None:\n        augment_fn = build_augmenter()\n    \n    AUTO = tf.data.experimental.AUTOTUNE\n    slices = (paths, offsets) if labels is None else (paths, offsets, labels)\n    \n    ds = tf.data.Dataset.from_tensor_slices(slices)\n    ds = ds.map(decode_fn, num_parallel_calls=AUTO)\n    ds = ds.cache(cache_dir) if cache else ds\n    ds = ds.repeat() if repeat else ds\n    if shuffle: \n        ds = ds.shuffle(shuffle, seed=CFG.seed)\n        opt = tf.data.Options()\n        opt.experimental_deterministic = False\n        ds = ds.with_options(opt)\n    ds = ds.batch(batch_size, drop_remainder=drop_remainder)\n    ds = ds.map(augment_fn, num_parallel_calls=AUTO) if augment else ds\n    ds = ds.prefetch(AUTO)\n    return ds","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:20:16.374517Z","iopub.execute_input":"2025-10-09T10:20:16.374819Z","iopub.status.idle":"2025-10-09T10:20:16.390273Z","shell.execute_reply.started":"2025-10-09T10:20:16.374788Z","shell.execute_reply":"2025-10-09T10:20:16.389403Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"The data is split into five train/validation folds to increase the confidence in the accuracy results.","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\nfrom sklearn.model_selection import StratifiedGroupKFold\n\nsgkf = StratifiedGroupKFold(n_splits=5, shuffle=True, random_state=CFG.seed)\n\ndf[\"fold\"] = -1\ndf.reset_index(drop=True, inplace=True)\nfor fold, (train_idx, valid_idx) in enumerate(\n    sgkf.split(df, y=df[\"class_label\"], groups=df[\"patient_id\"])\n):\n    df.loc[valid_idx, \"fold\"] = fold\ndf.groupby([\"fold\", \"class_name\"])[[\"eeg_id\"]].count().T","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:20:16.391237Z","iopub.execute_input":"2025-10-09T10:20:16.391515Z","iopub.status.idle":"2025-10-09T10:20:22.491850Z","shell.execute_reply.started":"2025-10-09T10:20:16.391482Z","shell.execute_reply":"2025-10-09T10:20:22.491124Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Select which spectrograms are used for validation and which are for training. Then train and validate them.","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n# Sample from full data\nsample_df = df.groupby(\"spectrogram_id\").head(1).reset_index(drop=True)\ntrain_df = sample_df[sample_df.fold != CFG.fold]\nvalid_df = sample_df[sample_df.fold == CFG.fold]\nprint(f\"# Num Train: {len(train_df)} | Num Valid: {len(valid_df)}\")\n\n# Train\ntrain_paths = train_df.spec2_path.values\ntrain_offsets = train_df.spectrogram_label_offset_seconds.values.astype(int)\ntrain_labels = train_df.class_label.values\ntrain_ds = build_dataset(train_paths, train_offsets, train_labels, batch_size=CFG.batch_size,\n                         repeat=True, shuffle=True, augment=True, cache=True)\n\n# Valid\nvalid_paths = valid_df.spec2_path.values\nvalid_offsets = valid_df.spectrogram_label_offset_seconds.values.astype(int)\nvalid_labels = valid_df.class_label.values\nvalid_ds = build_dataset(valid_paths, valid_offsets, valid_labels, batch_size=CFG.batch_size,\n                         repeat=False, shuffle=False, augment=False, cache=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:20:22.494776Z","iopub.execute_input":"2025-10-09T10:20:22.495914Z","iopub.status.idle":"2025-10-09T10:20:25.842266Z","shell.execute_reply.started":"2025-10-09T10:20:22.495881Z","shell.execute_reply":"2025-10-09T10:20:25.841521Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Show what the spectrogram images look like and their corresponding labels.","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\nimgs, tars = next(iter(train_ds))\n\nnum_imgs = 8\nplt.figure(figsize=(4*4, num_imgs//4*5))\nfor i in range(num_imgs):\n    plt.subplot(num_imgs//4, 4, i + 1)\n    img = imgs[i].numpy()[...,0]  # Adjust as per your image data format\n    img -= img.min()\n    img /= img.max() + 1e-4\n    tar = CFG.label2name[np.argmax(tars[i].numpy())]\n    plt.imshow(img)\n    plt.title(f\"Target: {tar}\")\n    plt.axis('off')\n    \nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:20:25.843029Z","iopub.execute_input":"2025-10-09T10:20:25.843273Z","iopub.status.idle":"2025-10-09T10:20:28.841806Z","shell.execute_reply.started":"2025-10-09T10:20:25.843254Z","shell.execute_reply":"2025-10-09T10:20:28.840863Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Chosen loss metric is KLDivergence","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\nLOSS = keras.losses.KLDivergence()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:20:28.843202Z","iopub.execute_input":"2025-10-09T10:20:28.843670Z","iopub.status.idle":"2025-10-09T10:20:28.848480Z","shell.execute_reply.started":"2025-10-09T10:20:28.843636Z","shell.execute_reply":"2025-10-09T10:20:28.847719Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"The selection of the model is made and the learning rate and optimizer are also chosen.","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\n# Build Classifier\nmodel = keras_cv.models.ImageClassifier.from_preset(\n    CFG.preset, num_classes=CFG.num_classes\n)\n\n# Compile the model  \nmodel.compile(optimizer=keras.optimizers.Adam(learning_rate=1e-4),\n              loss=LOSS)\n\n# Model Sumamry\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:20:28.849437Z","iopub.execute_input":"2025-10-09T10:20:28.849647Z","iopub.status.idle":"2025-10-09T10:20:55.450184Z","shell.execute_reply.started":"2025-10-09T10:20:28.849630Z","shell.execute_reply":"2025-10-09T10:20:55.449535Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Creation of the learning schedule function, to alter the learning rate over the epochs of training.","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\nimport math\n\ndef get_lr_callback(batch_size=8, mode='cos', epochs=10, plot=False):\n    lr_start, lr_max, lr_min = 5e-5, 6e-6 * batch_size, 1e-5\n    lr_ramp_ep, lr_sus_ep, lr_decay = 3, 0, 0.75\n\n    def lrfn(epoch):  # Learning rate update function\n        if epoch < lr_ramp_ep: lr = (lr_max - lr_start) / lr_ramp_ep * epoch + lr_start\n        elif epoch < lr_ramp_ep + lr_sus_ep: lr = lr_max\n        elif mode == 'exp': lr = (lr_max - lr_min) * lr_decay**(epoch - lr_ramp_ep - lr_sus_ep) + lr_min\n        elif mode == 'step': lr = lr_max * lr_decay**((epoch - lr_ramp_ep - lr_sus_ep) // 2)\n        elif mode == 'cos':\n            decay_total_epochs, decay_epoch_index = epochs - lr_ramp_ep - lr_sus_ep + 3, epoch - lr_ramp_ep - lr_sus_ep\n            phase = math.pi * decay_epoch_index / decay_total_epochs\n            lr = (lr_max - lr_min) * 0.5 * (1 + math.cos(phase)) + lr_min\n        return lr\n\n    if plot:  # Plot lr curve if plot is True\n        plt.figure(figsize=(10, 5))\n        plt.plot(np.arange(epochs), [lrfn(epoch) for epoch in np.arange(epochs)], marker='o')\n        plt.xlabel('epoch'); plt.ylabel('lr')\n        plt.title('LR Scheduler')\n        plt.show()\n\n    return keras.callbacks.LearningRateScheduler(lrfn, verbose=False)  # Create lr callback\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:20:55.451043Z","iopub.execute_input":"2025-10-09T10:20:55.451311Z","iopub.status.idle":"2025-10-09T10:20:55.458950Z","shell.execute_reply.started":"2025-10-09T10:20:55.451293Z","shell.execute_reply":"2025-10-09T10:20:55.458166Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Then the learning rate schedule function is used","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\nlr_cb = get_lr_callback(CFG.batch_size, mode=CFG.lr_mode, plot=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:20:55.459886Z","iopub.execute_input":"2025-10-09T10:20:55.460610Z","iopub.status.idle":"2025-10-09T10:20:55.670745Z","shell.execute_reply.started":"2025-10-09T10:20:55.460589Z","shell.execute_reply":"2025-10-09T10:20:55.669897Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Saving models that have minimal loss to keep the best models.","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\nckpt_cb = keras.callbacks.ModelCheckpoint(\"best_model.keras\",\n                                         monitor='val_loss',\n                                         save_best_only=True,\n                                         save_weights_only=False,\n                                         mode='min')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:20:55.671660Z","iopub.execute_input":"2025-10-09T10:20:55.671869Z","iopub.status.idle":"2025-10-09T10:20:55.676529Z","shell.execute_reply.started":"2025-10-09T10:20:55.671853Z","shell.execute_reply":"2025-10-09T10:20:55.675477Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Training of the model with the specified dtatsets and settings for learning rate and checkpoints, etc..","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\nhistory = model.fit(\n    train_ds, \n    epochs=CFG.epochs,\n    callbacks=[lr_cb, ckpt_cb], \n    steps_per_epoch=len(train_df)//CFG.batch_size,\n    validation_data=valid_ds, \n    verbose=CFG.verbose\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:20:55.677401Z","iopub.execute_input":"2025-10-09T10:20:55.677590Z","iopub.status.idle":"2025-10-09T10:39:00.274056Z","shell.execute_reply.started":"2025-10-09T10:20:55.677575Z","shell.execute_reply":"2025-10-09T10:39:00.273342Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Prediction","metadata":{}},{"cell_type":"markdown","source":"Loading the best model after the training.","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\nmodel.load_weights(\"best_model.keras\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:39:00.275961Z","iopub.execute_input":"2025-10-09T10:39:00.276279Z","iopub.status.idle":"2025-10-09T10:39:10.844774Z","shell.execute_reply.started":"2025-10-09T10:39:00.276258Z","shell.execute_reply":"2025-10-09T10:39:10.844099Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"The test dataset is built as a set amount of spectrogram files to be classified.","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\ntest_paths = test_df.spec2_path.values\ntest_ds = build_dataset(test_paths, batch_size=min(CFG.batch_size, len(test_df)),\n                         repeat=False, shuffle=False, cache=False, augment=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:39:10.847522Z","iopub.execute_input":"2025-10-09T10:39:10.847755Z","iopub.status.idle":"2025-10-09T10:39:10.907570Z","shell.execute_reply.started":"2025-10-09T10:39:10.847737Z","shell.execute_reply":"2025-10-09T10:39:10.906913Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"The model processes the test dataset and the predictions it made as to the classification of the spectrograms are stored in a variable.","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\npreds = model.predict(test_ds)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:39:10.908325Z","iopub.execute_input":"2025-10-09T10:39:10.908550Z","iopub.status.idle":"2025-10-09T10:39:32.375323Z","shell.execute_reply.started":"2025-10-09T10:39:10.908534Z","shell.execute_reply":"2025-10-09T10:39:32.374636Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Reformatting to fit the expected submission format of Kaggle.","metadata":{}},{"cell_type":"code","source":"#This code block has been borrowed from the \"HMS-HBAC: KerasCV Starter Notebook\"\n#by authors Awsaf, fchollet, Phil Culliton, Martin Görner, and Gusthema.\n#found from the link https://www.kaggle.com/code/awsaf49/hms-hbac-kerascv-starter-notebook\n\npred_df = test_df[[\"eeg_id\"]].copy()\ntarget_cols = [x.lower()+'_vote' for x in CFG.class_names]\npred_df[target_cols] = preds.tolist()\n\nsub_df = pd.read_csv(f'{BASE_PATH}/sample_submission.csv')\nsub_df = sub_df[[\"eeg_id\"]].copy()\nsub_df = sub_df.merge(pred_df, on=\"eeg_id\", how=\"left\")\nsub_df.to_csv(\"submission.csv\", index=False)\nsub_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-09T10:39:32.377118Z","iopub.execute_input":"2025-10-09T10:39:32.377346Z","iopub.status.idle":"2025-10-09T10:39:32.426964Z","shell.execute_reply.started":"2025-10-09T10:39:32.377329Z","shell.execute_reply":"2025-10-09T10:39:32.426240Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}