{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import json\nimport math\nimport os\n\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport scipy\nimport tensorflow as tf\n\nfrom PIL import Image\nfrom tqdm import tqdm\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import (\n    cohen_kappa_score,\n    accuracy_score,\n)\n\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.applications import DenseNet121\nfrom tensorflow.keras.callbacks import (\n    Callback,\n    ModelCheckpoint,\n)\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.optimizers import Adam\n\n%matplotlib inline\n%matplotlib inline","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:04:27.954179Z","iopub.execute_input":"2026-07-23T22:04:27.955127Z","iopub.status.idle":"2026-07-23T22:04:27.963744Z","shell.execute_reply.started":"2026-07-23T22:04:27.955094Z","shell.execute_reply":"2026-07-23T22:04:27.963031Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Set random seed for reproducibility.","metadata":{}},{"cell_type":"code","source":"np.random.seed(2026)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:04:27.964993Z","iopub.execute_input":"2026-07-23T22:04:27.965853Z","iopub.status.idle":"2026-07-23T22:04:27.986941Z","shell.execute_reply.started":"2026-07-23T22:04:27.965823Z","shell.execute_reply":"2026-07-23T22:04:27.986218Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Loading & Exploration","metadata":{}},{"cell_type":"code","source":"# CONFIG\nIMAGE_SIZE = 512\nBATCH_SIZE = 16\nNUM_CLASSES = 5\nTRAIN_DIR = \"/kaggle/input/datasets/fbtresent/cv-dataset/dataset/train\"\nVAL_DIR = \"/kaggle/input/datasets/fbtresent/cv-dataset/dataset/val\"\nTEST_DIR = \"/kaggle/input/datasets/fbtresent/cv-dataset/dataset/test\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:04:27.987828Z","iopub.execute_input":"2026-07-23T22:04:27.988166Z","iopub.status.idle":"2026-07-23T22:04:28.000952Z","shell.execute_reply.started":"2026-07-23T22:04:27.988132Z","shell.execute_reply":"2026-07-23T22:04:28.000099Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.applications.densenet import preprocess_input\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n\ntrain_datagen = ImageDataGenerator(\n    preprocessing_function=preprocess_input,\n    rotation_range=5,\n    horizontal_flip=True,\n    zoom_range=0.05\n)\n\nval_datagen = ImageDataGenerator(\n    preprocessing_function=preprocess_input\n)\n\ntest_datagen = ImageDataGenerator(\n    preprocessing_function=preprocess_input\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:04:28.001872Z","iopub.execute_input":"2026-07-23T22:04:28.002677Z","iopub.status.idle":"2026-07-23T22:04:28.014356Z","shell.execute_reply.started":"2026-07-23T22:04:28.002647Z","shell.execute_reply":"2026-07-23T22:04:28.013729Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_generator = train_datagen.flow_from_directory(\n    TRAIN_DIR,\n    target_size=(IMAGE_SIZE, IMAGE_SIZE),\n    batch_size=BATCH_SIZE,\n    class_mode=\"categorical\",\n    shuffle=False\n)\n\nvalidation_generator = val_datagen.flow_from_directory(\n    VAL_DIR,\n    target_size=(IMAGE_SIZE, IMAGE_SIZE),\n    batch_size=BATCH_SIZE,\n    class_mode=\"categorical\",\n    shuffle=False\n)\n\ntest_generator = test_datagen.flow_from_directory(\n    TEST_DIR,\n    target_size=(IMAGE_SIZE, IMAGE_SIZE),\n    batch_size=BATCH_SIZE,\n    class_mode=\"categorical\",\n    shuffle=False\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:04:28.016359Z","iopub.execute_input":"2026-07-23T22:04:28.016692Z","iopub.status.idle":"2026-07-23T22:04:29.152229Z","shell.execute_reply.started":"2026-07-23T22:04:28.016671Z","shell.execute_reply":"2026-07-23T22:04:29.151690Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\n\nprint(np.bincount(train_generator.classes))\nprint(np.bincount(validation_generator.classes))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:04:29.153077Z","iopub.execute_input":"2026-07-23T22:04:29.153354Z","iopub.status.idle":"2026-07-23T22:04:29.158638Z","shell.execute_reply.started":"2026-07-23T22:04:29.153333Z","shell.execute_reply":"2026-07-23T22:04:29.157910Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"x_batch, y_batch = next(train_generator)\n\nprint(x_batch.shape)\nprint(y_batch.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:04:29.159483Z","iopub.execute_input":"2026-07-23T22:04:29.160205Z","iopub.status.idle":"2026-07-23T22:04:30.016928Z","shell.execute_reply.started":"2026-07-23T22:04:29.160183Z","shell.execute_reply":"2026-07-23T22:04:30.016205Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(train_generator.class_indices)\nprint(validation_generator.class_indices)\nprint(test_generator.class_indices)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:04:30.017806Z","iopub.execute_input":"2026-07-23T22:04:30.018094Z","iopub.status.idle":"2026-07-23T22:04:30.022692Z","shell.execute_reply.started":"2026-07-23T22:04:30.018062Z","shell.execute_reply":"2026-07-23T22:04:30.021945Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Displaying some Sample Images","metadata":{}},{"cell_type":"code","source":"# import matplotlib.pyplot as plt\n# import numpy as np\n\n# # Mapping index -> class name\n# idx_to_class = {\n#     v: k for k, v in train_generator.class_indices.items()\n# }\n\n# datasets = [\n#     (\"Train\", train_generator),\n#     (\"Validation\", validation_generator),\n#     (\"Test\", test_generator),\n# ]\n\n# num_classes = len(idx_to_class)\n\n# def get_one_image_each_class(generator):\n#     images_by_class = {}\n#     generator.reset()\n\n#     for images, labels in generator:\n#         for img, label in zip(images, labels):\n#             cls = np.argmax(label)\n#             if cls not in images_by_class:\n#                 images_by_class[cls] = img.copy()\n\n#             if len(images_by_class) == num_classes:\n#                 return images_by_class\n\n#     return images_by_class\n\n\n# samples = {}\n# for name, generator in datasets:\n#     samples[name] = get_one_image_each_class(generator)\n\n# fig, axes = plt.subplots(num_classes, len(datasets), figsize=(10, 15))\n\n# for col, (dataset_name, _) in enumerate(datasets):\n#     for row in range(num_classes):\n\n#         ax = axes[row, col]\n#         img = samples[dataset_name][row]\n\n#         # Chuyển về 0-255 để hiển thị\n#         if img.max() <= 1.0 and img.min() >= 0:\n#             img = (img * 255).astype(np.uint8)\n\n#         elif img.min() < 0:\n#             # preprocess_input của DenseNet\n#             img = img - img.min()\n#             img = img / img.max()\n#             img = (img * 255).astype(np.uint8)\n\n#         else:\n#             img = np.clip(img, 0, 255).astype(np.uint8)\n\n#         ax.imshow(img)\n\n#         if row == 0:\n#             ax.set_title(dataset_name, fontsize=14, fontweight=\"bold\")\n\n#         ax.set_xlabel(idx_to_class[row], fontsize=10)\n#         ax.set_xticks([])\n#         ax.set_yticks([])\n\n# plt.tight_layout()\n# plt.savefig(\"train_val_test_each_class.png\", dpi=300, bbox_inches=\"tight\")\n# plt.show()","metadata":{"_kg_hide-input":true,"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:04:30.024701Z","iopub.execute_input":"2026-07-23T22:04:30.025369Z","iopub.status.idle":"2026-07-23T22:07:01.241044Z","shell.execute_reply.started":"2026-07-23T22:04:30.025344Z","shell.execute_reply":"2026-07-23T22:07:01.239853Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Quadratic Weighted Kappa\n\nQuadratic Weighted Kappa (QWK, the greek letter $\\kappa$), also known as Cohen's Kappa, is the official evaluation metric. For our kernel, we will use a custom callback to monitor the score, and plot it at the end.\n\n### What is Cohen Kappa?\n\nAccording to the [wikipedia article](https://en.wikipedia.org/wiki/Cohen%27s_kappa), we have\n> The definition of $\\kappa$ is:\n> $$\\kappa \\equiv \\frac{p_o - p_e}{1 - p_e}$$\n> where $p_o$ is the relative observed agreement among raters (identical to accuracy), and $p_e$ is the hypothetical probability of chance agreement, using the observed data to calculate the probabilities of each observer randomly seeing each category.\n\n### How is it computed?\n\nLet's take the example of a binary classification problem. Say we have:","metadata":{}},{"cell_type":"code","source":"# true_labels = np.array([1, 0, 1, 1, 0, 1])\n# pred_labels = np.array([1, 0, 0, 0, 0, 1])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:07:01.294052Z","iopub.execute_input":"2026-07-23T22:07:01.294335Z","iopub.status.idle":"2026-07-23T22:07:01.305636Z","shell.execute_reply.started":"2026-07-23T22:07:01.294314Z","shell.execute_reply":"2026-07-23T22:07:01.304969Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"We can construct the following table:\n\n| true | pred | agreement      |\n|------|------|----------------|\n| 1    | 1    | true positive  |\n| 0    | 0    | true negative  |\n| 1    | 0    | false negative |\n| 1    | 0    | false negative |\n| 0    | 0    | true negative  |\n| 1    | 1    | true positive  |\n\n\nThen the \"observed proportionate agreement\" is calculated exactly the same way as accuracy:\n\n$$\np_o = acc = \\frac{tp + tn}{all} = {2 + 2}{6} = 0.66\n$$\n\nThis can be confirmed using scikit-learn:","metadata":{}},{"cell_type":"code","source":"# accuracy_score(true_labels, pred_labels)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:07:01.306543Z","iopub.execute_input":"2026-07-23T22:07:01.307070Z","iopub.status.idle":"2026-07-23T22:07:01.317919Z","shell.execute_reply.started":"2026-07-23T22:07:01.307039Z","shell.execute_reply":"2026-07-23T22:07:01.317174Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Additionally, we also need to compute `p_e`:\n\n$$p_{yes} = \\frac{tp + fp}{all} \\frac{tp + fn}{all} = \\frac{2}{6} \\frac{4}{6} = 0.222$$\n\n$$p_{no} = \\frac{fn + tn}{all} \\frac{fp + tn}{all} = \\frac{4}{6} \\frac{2}{6} = 0.222$$\n\n$$p_{e} = p_{yes} + p_{no} = 0.222 + 0.222 = 0.444$$\n\nFinally,\n\n$$\n\\kappa = \\frac{p_o - p_e}{1-p_e} = \\frac{0.666 - 0.444}{1 - 0.444} = 0.4\n$$\n\nLet's verify with scikit-learn:","metadata":{}},{"cell_type":"code","source":"# cohen_kappa_score(true_labels, pred_labels)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:07:01.319035Z","iopub.execute_input":"2026-07-23T22:07:01.319355Z","iopub.status.idle":"2026-07-23T22:07:01.331087Z","shell.execute_reply.started":"2026-07-23T22:07:01.319333Z","shell.execute_reply":"2026-07-23T22:07:01.330165Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### What is the weighted kappa?\n\nThe wikipedia page offer a very concise explanation: \n> The weighted kappa allows disagreements to be weighted differently and is especially useful when **codes are ordered**. Three matrices are involved, the matrix of observed scores, the matrix of expected scores based on chance agreement, and the weight matrix. Weight matrix cells located on the diagonal (upper-left to bottom-right) represent agreement and thus contain zeros. Off-diagonal cells contain weights indicating the seriousness of that disagreement.\n\nSimply put, if two scores disagree, then the penalty will depend on how far they are apart. That means that our score will be higher if (a) the real value is 4 but the model predicts a 3, and the score will be lower if (b) the model instead predicts a 0. This metric makes sense for this competition, since the labels 0-4 indicates how severe the illness is. Intuitively, a model that predicts a severe retinopathy (3) when it is in reality a proliferative retinopathy (4) is probably better than a model that predicts a mild retinopathy (1).","metadata":{}},{"cell_type":"markdown","source":"### Creating keras callback for QWK","metadata":{}},{"cell_type":"code","source":"import numpy as np\nfrom sklearn.metrics import cohen_kappa_score\nfrom tensorflow.keras.callbacks import Callback\n\n\nclass Metrics(Callback):\n\n    def __init__(self, valid_generator):\n        super().__init__()\n        self.valid_generator = valid_generator\n        self.val_kappas = []\n\n    def on_epoch_end(self, epoch, logs=None):\n\n        self.valid_generator.reset()\n\n        y_prob = self.model.predict(\n            self.valid_generator,\n            verbose=0\n        )\n\n        y_pred = np.argmax(y_prob, axis=1)\n        y_true = self.valid_generator.classes\n\n        kappa = cohen_kappa_score(\n            y_true,\n            y_pred,\n            weights=\"quadratic\"\n        )\n\n        self.val_kappas.append(kappa)\n\n        print(f\"\\nEpoch {epoch+1}: val_QWK = {kappa:.6f}\")\n\n        if kappa >= max(self.val_kappas):\n            print(f\"Validation QWK improved to {kappa:.6f}. Saving model.\")\n            self.model.save(\"model.keras\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:07:01.332153Z","iopub.execute_input":"2026-07-23T22:07:01.332581Z","iopub.status.idle":"2026-07-23T22:07:01.344195Z","shell.execute_reply.started":"2026-07-23T22:07:01.332532Z","shell.execute_reply":"2026-07-23T22:07:01.343529Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model: DenseNet-121","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.applications import DenseNet121\n\ndensenet = DenseNet121(\n    weights=\"imagenet\",\n    include_top=False,\n    input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3)\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:07:01.345403Z","iopub.execute_input":"2026-07-23T22:07:01.345727Z","iopub.status.idle":"2026-07-23T22:07:03.077717Z","shell.execute_reply.started":"2026-07-23T22:07:01.345692Z","shell.execute_reply":"2026-07-23T22:07:03.077015Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.models import Sequential\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.optimizers import Adam\n\ndef build_model():\n    model = Sequential([\n        densenet,\n        layers.GlobalAveragePooling2D(),\n        layers.Dropout(0.5),\n        layers.Dense(5, activation=\"softmax\")\n    ])\n\n    model.compile(\n        optimizer=Adam(learning_rate=5e-5),\n        loss=\"categorical_crossentropy\",\n        metrics=[\"accuracy\"]\n    )\n\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:07:03.078588Z","iopub.execute_input":"2026-07-23T22:07:03.078900Z","iopub.status.idle":"2026-07-23T22:07:03.083966Z","shell.execute_reply.started":"2026-07-23T22:07:03.078858Z","shell.execute_reply":"2026-07-23T22:07:03.083149Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.models import load_model\n\nmodel = build_model()\n# model = load_model(\"/kaggle/input/notebooks/fbtresent/aptos-2019-densenet-keras-starter/model.keras\")\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:07:03.087209Z","iopub.execute_input":"2026-07-23T22:07:03.087573Z","iopub.status.idle":"2026-07-23T22:07:06.971927Z","shell.execute_reply.started":"2026-07-23T22:07:03.087549Z","shell.execute_reply":"2026-07-23T22:07:06.971071Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Training & Evaluation","metadata":{}},{"cell_type":"code","source":"from sklearn.utils.class_weight import compute_class_weight\nimport numpy as np\n\nlabels = train_generator.classes\n\nclass_weights = compute_class_weight(\n    class_weight=\"balanced\",\n    classes=np.unique(labels),\n    y=labels\n)\n\nclass_weights = dict(enumerate(class_weights))\n\nprint(class_weights)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:07:06.972972Z","iopub.execute_input":"2026-07-23T22:07:06.973311Z","iopub.status.idle":"2026-07-23T22:07:06.980980Z","shell.execute_reply.started":"2026-07-23T22:07:06.973280Z","shell.execute_reply":"2026-07-23T22:07:06.979988Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"kappa_metrics = Metrics(validation_generator)\n\nhistory = model.fit(\n    train_generator,\n    validation_data=validation_generator,\n    epochs=30,\n    class_weight=class_weights,\n    callbacks=[kappa_metrics]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:07:06.981920Z","iopub.execute_input":"2026-07-23T22:07:06.982252Z","iopub.status.idle":"2026-07-23T22:07:08.676914Z","shell.execute_reply.started":"2026-07-23T22:07:06.982227Z","shell.execute_reply":"2026-07-23T22:07:08.676315Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport json\n\nOLD_HISTORY = \"/kaggle/input/notebooks/fbtresent/aptos-2019-densenet-keras-starter/history.json\"\nOUTPUT_HISTORY = \"/kaggle/working/history.json\"\n\n# History của lần train hiện tại\nnew_history = history.history\n\n# Đọc history cũ nếu có\nif os.path.exists(OLD_HISTORY):\n    with open(OLD_HISTORY, \"r\") as f:\n        all_history = json.load(f)\n\n    print(\"Loaded previous history.\")\nelse:\n    all_history = {}\n    print(\"No previous history found. Creating a new one.\")\n\n# Ghép dữ liệu\nfor key, values in new_history.items():\n    if key not in all_history:\n        all_history[key] = []\n\n    all_history[key].extend(values)\n\n# Lưu history cuối cùng\nwith open(OUTPUT_HISTORY, \"w\") as f:\n    json.dump(all_history, f, indent=4)\n\nprint(f\"Saved merged history to: {OUTPUT_HISTORY}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:07:08.677813Z","iopub.execute_input":"2026-07-23T22:07:08.677998Z","iopub.status.idle":"2026-07-23T22:07:08.687805Z","shell.execute_reply.started":"2026-07-23T22:07:08.677979Z","shell.execute_reply":"2026-07-23T22:07:08.687022Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import numpy as np\n# import matplotlib.pyplot as plt\n# from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay\n\n# def plot_confusion_matrices(model, train_generator, validation_generator, test_generator):\n#     datasets = [\n#         (\"Train\", train_generator),\n#         (\"Validation\", validation_generator),\n#         (\"Test\", test_generator)\n#     ]\n\n#     fig, axes = plt.subplots(1, 3, figsize=(18, 6))\n#     for ax, (title, generator) in zip(axes, datasets):\n\n#         # Reset generator\n#         generator.reset()\n\n#         # Predict\n#         predictions = model.predict(generator, verbose=0)\n#         # Predicted labels\n#         y_pred = np.argmax(predictions, axis=1)\n#         # Ground truth\n#         y_true = generator.classes\n\n#         # Confusion matrix\n#         cm = confusion_matrix(y_true, y_pred)\n\n#         # Plot\n#         disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=list(generator.class_indices.keys()))\n#         disp.plot(cmap=\"Blues\", values_format=\"d\", ax=ax, colorbar=False)\n#         ax.set_title(title)\n#     plt.tight_layout()\n#     plt.show()\n\n# plot_confusion_matrices(model, train_generator, validation_generator, test_generator)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:07:08.688850Z","iopub.execute_input":"2026-07-23T22:07:08.689209Z","iopub.status.idle":"2026-07-23T22:07:08.696409Z","shell.execute_reply.started":"2026-07-23T22:07:08.689178Z","shell.execute_reply":"2026-07-23T22:07:08.695708Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import pandas as pd\n# import matplotlib.pyplot as plt\n\n# history_df = pd.DataFrame(all_history)\n\n# print(history_df.head())\n# history_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:07:08.697164Z","iopub.execute_input":"2026-07-23T22:07:08.697523Z","iopub.status.idle":"2026-07-23T22:07:08.711913Z","shell.execute_reply.started":"2026-07-23T22:07:08.697479Z","shell.execute_reply":"2026-07-23T22:07:08.711142Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Find best threshold\n\nPlease Note: Although I show how to construct a threshold optimizer, **it is currently unused**. Please see notice at the top of the kernel.","metadata":{}},{"cell_type":"code","source":"# y_prob = model.predict(validation_generator, verbose=0)\n# y_pred = np.argmax(y_prob, axis=1)\n# y_true = valid_generator.classes\n\n# qwk = cohen_kappa_score(\n#     y_true,\n#     y_pred,\n#     weights=\"quadratic\"\n# )\n\n# print(qwk)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:07:08.712972Z","iopub.execute_input":"2026-07-23T22:07:08.713237Z","iopub.status.idle":"2026-07-23T22:07:08.724771Z","shell.execute_reply.started":"2026-07-23T22:07:08.713209Z","shell.execute_reply":"2026-07-23T22:07:08.723873Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Submit","metadata":{}},{"cell_type":"code","source":"# y_test = model.predict(x_test) > 0.5\n# y_test = y_test.astype(int).sum(axis=1) - 1\n\n# test_df['diagnosis'] = y_test\n# test_df.to_csv('submission.csv',index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-23T22:07:08.725713Z","iopub.execute_input":"2026-07-23T22:07:08.725984Z","iopub.status.idle":"2026-07-23T22:07:08.737157Z","shell.execute_reply.started":"2026-07-23T22:07:08.725957Z","shell.execute_reply":"2026-07-23T22:07:08.736266Z"}},"outputs":[],"execution_count":null}]}