{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":14774,"databundleVersionId":875431,"sourceType":"competition"},{"sourceId":187731,"sourceType":"datasetVersion","datasetId":80814},{"sourceId":418031,"sourceType":"datasetVersion","datasetId":131128},{"sourceId":1141522,"sourceType":"datasetVersion","datasetId":475232}],"dockerImageVersionId":30919,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# To have reproducible results and compare them\nnr_seed = 2019\nimport numpy as np \nnp.random.seed(nr_seed)\nimport tensorflow as tf\ntf.random.set_seed(nr_seed)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T13:54:55.055422Z","iopub.execute_input":"2025-04-08T13:54:55.055714Z","iopub.status.idle":"2025-04-08T13:54:59.744719Z","shell.execute_reply.started":"2025-04-08T13:54:55.055684Z","shell.execute_reply":"2025-04-08T13:54:59.744031Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import libraries\nimport json\nimport math\nfrom tqdm import tqdm, tqdm_notebook\nimport gc\nimport warnings\nimport os\n\nimport cv2\nfrom PIL import Image\n\nimport pandas as pd\nimport scipy\nimport matplotlib.pyplot as plt\n\nfrom keras import backend as K\nfrom keras import layers\nfrom keras.applications.efficientnet import EfficientNetB7, preprocess_input\nfrom keras.callbacks import Callback, ModelCheckpoint\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom keras.models import Sequential, Model\nfrom keras.optimizers import Adam\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import cohen_kappa_score, accuracy_score\n\nwarnings.filterwarnings(\"ignore\")\n\n%matplotlib inline","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true,"_kg_hide-input":true,"execution":{"iopub.status.busy":"2025-04-08T13:54:59.746704Z","iopub.execute_input":"2025-04-08T13:54:59.747277Z","iopub.status.idle":"2025-04-08T13:55:00.540260Z","shell.execute_reply.started":"2025-04-08T13:54:59.747245Z","shell.execute_reply":"2025-04-08T13:55:00.539360Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Image size\nim_size = 224\n# Batch size\nBATCH_SIZE = 4","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T13:55:00.541923Z","iopub.execute_input":"2025-04-08T13:55:00.542611Z","iopub.status.idle":"2025-04-08T13:55:00.546933Z","shell.execute_reply.started":"2025-04-08T13:55:00.542575Z","shell.execute_reply":"2025-04-08T13:55:00.546001Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Loading & Merging","metadata":{}},{"cell_type":"code","source":"new_train = pd.read_csv('../input/aptos2019-blindness-detection/train.csv')\nold_train = pd.read_csv('../input/diabetic-retinopathy-resized/trainLabels.csv')\nprint(new_train.shape)\nprint(old_train.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T13:55:00.547881Z","iopub.execute_input":"2025-04-08T13:55:00.548131Z","iopub.status.idle":"2025-04-08T13:55:00.594996Z","shell.execute_reply.started":"2025-04-08T13:55:00.548111Z","shell.execute_reply":"2025-04-08T13:55:00.593995Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"old_train = old_train[['image','level']]\nold_train.columns = new_train.columns\nold_train.diagnosis.value_counts()\n\n# path columns\nnew_train['id_code'] = '../input/aptos2019-blindness-detection/train_images/' + new_train['id_code'].astype(str) + '.png'\nold_train['id_code'] = '../input/diabetic-retinopathy-resized/resized_train/resized_train/' + old_train['id_code'].astype(str) + '.jpeg'\n\ntrain_df = old_train.copy()\nval_df = new_train.copy()\ntrain_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T13:55:00.596079Z","iopub.execute_input":"2025-04-08T13:55:00.596426Z","iopub.status.idle":"2025-04-08T13:55:00.625425Z","shell.execute_reply.started":"2025-04-08T13:55:00.596393Z","shell.execute_reply":"2025-04-08T13:55:00.624501Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Train - Valid split","metadata":{}},{"cell_type":"code","source":"# Not used in version 5\n#train_df, val_df = train_test_split(train_df, shuffle=True, stratify=train_df.diagnosis, test_size=0.1, random_state=2019)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T13:55:00.626524Z","iopub.execute_input":"2025-04-08T13:55:00.626852Z","iopub.status.idle":"2025-04-08T13:55:00.638642Z","shell.execute_reply.started":"2025-04-08T13:55:00.626821Z","shell.execute_reply":"2025-04-08T13:55:00.637701Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Let's shuffle the datasets\ntrain_df = train_df.sample(frac=1).reset_index(drop=True)\nval_df = val_df.sample(frac=1).reset_index(drop=True)\nprint(train_df.shape)\nprint(val_df.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T13:55:00.639488Z","iopub.execute_input":"2025-04-08T13:55:00.639781Z","iopub.status.idle":"2025-04-08T13:55:00.659221Z","shell.execute_reply.started":"2025-04-08T13:55:00.639751Z","shell.execute_reply":"2025-04-08T13:55:00.658533Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Process Images","metadata":{}},{"cell_type":"markdown","source":"Crop function: https://www.kaggle.com/ratthachat/aptos-updated-preprocessing-ben-s-cropping ","metadata":{}},{"cell_type":"code","source":"def crop_image1(img,tol=7):\n    # img is image data\n    # tol  is tolerance\n        \n    mask = img>tol\n    return img[np.ix_(mask.any(1),mask.any(0))]\n\ndef crop_image_from_gray(img,tol=7):\n    if img.ndim ==2:\n        mask = img>tol\n        return img[np.ix_(mask.any(1),mask.any(0))]\n    elif img.ndim==3:\n        gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)\n        mask = gray_img>tol\n        \n        check_shape = img[:,:,0][np.ix_(mask.any(1),mask.any(0))].shape[0]\n        if (check_shape == 0): # image is too dark so that we crop out everything,\n            return img # return original image\n        else:\n            img1=img[:,:,0][np.ix_(mask.any(1),mask.any(0))]\n            img2=img[:,:,1][np.ix_(mask.any(1),mask.any(0))]\n            img3=img[:,:,2][np.ix_(mask.any(1),mask.any(0))]\n            img = np.stack([img1,img2,img3],axis=-1)\n        return img\n\ndef preprocess_image(image_path, desired_size=im_size):\n    img = cv2.imread(image_path)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    img = crop_image_from_gray(img)\n    img = cv2.resize(img, (desired_size,desired_size))\n    img = cv2.addWeighted(img,4,cv2.GaussianBlur(img, (0,0), desired_size/30) ,-4 ,128)\n    \n    return img\n\ndef preprocess_image_old(image_path, desired_size=im_size):\n    img = cv2.imread(image_path)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    #img = crop_image_from_gray(img)\n    img = cv2.resize(img, (desired_size,desired_size))\n    img = cv2.addWeighted(img,4,cv2.GaussianBlur(img, (0,0), desired_size/40) ,-4 ,128)\n    \n    return img","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T13:55:00.661624Z","iopub.execute_input":"2025-04-08T13:55:00.661854Z","iopub.status.idle":"2025-04-08T13:55:00.671728Z","shell.execute_reply.started":"2025-04-08T13:55:00.661835Z","shell.execute_reply":"2025-04-08T13:55:00.670907Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def display_samples(df, columns=4, rows=3):\n    fig=plt.figure(figsize=(5*columns, 4*rows))\n\n    for i in range(columns*rows):\n        image_path = df.loc[i,'id_code']\n        image_id = df.loc[i,'diagnosis']\n        img = cv2.imread(f'{image_path}')\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        #img = crop_image_from_gray(img)\n        img = cv2.resize(img, (im_size,im_size))\n        img = cv2.addWeighted(img,4,cv2.GaussianBlur(img, (0,0), im_size/40) ,-4 ,128)\n        \n        fig.add_subplot(rows, columns, i+1)\n        plt.title(image_id)\n        plt.imshow(img)\n    \n    plt.tight_layout()\n\ndisplay_samples(train_df)","metadata":{"_kg_hide-input":true,"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T13:55:00.673306Z","iopub.execute_input":"2025-04-08T13:55:00.673579Z","iopub.status.idle":"2025-04-08T13:55:03.844613Z","shell.execute_reply.started":"2025-04-08T13:55:00.673545Z","shell.execute_reply":"2025-04-08T13:55:03.843668Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Processing Images","metadata":{}},{"cell_type":"markdown","source":"__UPDATE:__ Here we are reading just the validation set. In order to use 320x320 images, we are going to load one bucket at a time only when needed. This will let our code run without memory-related errors.","metadata":{}},{"cell_type":"code","source":"# Validation set\nN = val_df.shape[0]\nx_val = np.empty((N, im_size, im_size, 3), dtype=np.float32)\n\nfor i, image_id in enumerate(tqdm_notebook(val_df['id_code'])):\n    img = preprocess_image(f'{image_id}', desired_size=im_size)\n    x_val[i, :, :, :] = preprocess_input(img)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T13:55:03.845584Z","iopub.execute_input":"2025-04-08T13:55:03.845867Z","iopub.status.idle":"2025-04-08T14:05:57.173372Z","shell.execute_reply.started":"2025-04-08T13:55:03.845844Z","shell.execute_reply":"2025-04-08T14:05:57.172181Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_train = pd.get_dummies(train_df['diagnosis']).values\ny_val = pd.get_dummies(val_df['diagnosis']).values\n\nprint(y_train.shape)\nprint(x_val.shape)\nprint(y_val.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:05:57.174224Z","iopub.execute_input":"2025-04-08T14:05:57.174469Z","iopub.status.idle":"2025-04-08T14:05:57.182333Z","shell.execute_reply.started":"2025-04-08T14:05:57.174446Z","shell.execute_reply":"2025-04-08T14:05:57.181598Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Creating multilabels\n\nInstead of predicting a single label, we will change our target to be a multilabel problem; i.e., if the target is a certain class, then it encompasses all the classes before it. E.g. encoding a class 4 retinopathy would usually be `[0, 0, 0, 1]`, but in our case we will predict `[1, 1, 1, 1]`. For more details, please check out [Lex's kernel](https://www.kaggle.com/lextoumbourou/blindness-detection-resnet34-ordinal-targets).","metadata":{}},{"cell_type":"code","source":"y_train_multi = np.empty(y_train.shape, dtype=y_train.dtype)\ny_train_multi[:, 4] = y_train[:, 4]\n\nfor i in range(3, -1, -1):\n    y_train_multi[:, i] = np.logical_or(y_train[:, i], y_train_multi[:, i+1])\n\ny_val_multi = np.empty(y_val.shape, dtype=y_val.dtype)\ny_val_multi[:, 4] = y_val[:, 4]\n\nfor i in range(3, -1, -1):\n    y_val_multi[:, i] = np.logical_or(y_val[:, i], y_val_multi[:, i+1])\n\nprint(\"Y_train multi: {}\".format(y_train_multi.shape))\nprint(\"Y_val multi: {}\".format(y_val_multi.shape))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:05:57.183141Z","iopub.execute_input":"2025-04-08T14:05:57.183515Z","iopub.status.idle":"2025-04-08T14:05:57.196782Z","shell.execute_reply.started":"2025-04-08T14:05:57.183488Z","shell.execute_reply":"2025-04-08T14:05:57.196079Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_train = y_train_multi\ny_val = y_val_multi","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:05:57.197541Z","iopub.execute_input":"2025-04-08T14:05:57.197852Z","iopub.status.idle":"2025-04-08T14:05:57.211579Z","shell.execute_reply.started":"2025-04-08T14:05:57.197798Z","shell.execute_reply":"2025-04-08T14:05:57.210532Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# delete the uneeded df\ndel new_train\ndel old_train\ndel val_df\ngc.collect()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:05:57.212548Z","iopub.execute_input":"2025-04-08T14:05:57.212765Z","iopub.status.idle":"2025-04-08T14:05:57.396920Z","shell.execute_reply.started":"2025-04-08T14:05:57.212735Z","shell.execute_reply":"2025-04-08T14:05:57.396162Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Creating keras callback for QWK\n\n---\n\nI had to change this function, in order to consider the best kappa score among all the buckets.","metadata":{}},{"cell_type":"code","source":"class Metrics(Callback):\n\n    def on_epoch_end(self, epoch, logs={}):\n        X_val, y_val = self.validation_data[:2]\n        y_val = y_val.sum(axis=1) - 1\n        \n        y_pred = self.model.predict(X_val) > 0.5\n        y_pred = y_pred.astype(int).sum(axis=1) - 1\n\n        _val_kappa = cohen_kappa_score(\n            y_val,\n            y_pred, \n            weights='quadratic'\n        )\n\n        self.val_kappas.append(_val_kappa)\n\n        print(f\"val_kappa: {_val_kappa:.4f}\")\n        \n        if _val_kappa == max(self.val_kappas):\n            print(\"Validation Kappa has improved. Saving model.\")\n            self.model.save('model.h5')\n\n        return","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:05:57.397739Z","iopub.execute_input":"2025-04-08T14:05:57.398109Z","iopub.status.idle":"2025-04-08T14:05:57.415204Z","shell.execute_reply.started":"2025-04-08T14:05:57.398077Z","shell.execute_reply":"2025-04-08T14:05:57.414592Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data Generator","metadata":{}},{"cell_type":"code","source":"from keras.applications.efficientnet import preprocess_input\n\ndef create_datagen():\n    return ImageDataGenerator(\n        preprocessing_function=preprocess_input,  # ✅ Use EfficientNetB7-specific preprocessing\n        horizontal_flip=True,\n        vertical_flip=True,\n        rotation_range=360\n    )\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:05:57.415926Z","iopub.execute_input":"2025-04-08T14:05:57.416244Z","iopub.status.idle":"2025-04-08T14:05:57.444924Z","shell.execute_reply.started":"2025-04-08T14:05:57.416213Z","shell.execute_reply":"2025-04-08T14:05:57.444321Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model: EfficientNetB7","metadata":{}},{"cell_type":"code","source":"# efficientnet = EfficientNetB7(\n#     weights='imagenet',\n#     include_top=False,\n#     input_shape=(im_size, im_size, 3)\n# )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:05:57.445654Z","iopub.execute_input":"2025-04-08T14:05:57.445950Z","iopub.status.idle":"2025-04-08T14:05:57.460080Z","shell.execute_reply.started":"2025-04-08T14:05:57.445914Z","shell.execute_reply":"2025-04-08T14:05:57.459488Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# def build_model():\n#     model = Sequential()\n#     model.add(efficientnet)\n#     model.add(layers.GlobalAveragePooling2D())\n#     model.add(layers.Dropout(0.5))\n#     model.add(layers.Dense(5, activation='sigmoid'))\n\n#     model.compile(\n#         loss='binary_crossentropy',\n#         optimizer=Adam(learning_rate=1e-4, decay=1e-6),  \n#         metrics=['accuracy']\n#     )\n\n#     return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:05:57.460956Z","iopub.execute_input":"2025-04-08T14:05:57.461297Z","iopub.status.idle":"2025-04-08T14:05:57.475253Z","shell.execute_reply.started":"2025-04-08T14:05:57.461266Z","shell.execute_reply":"2025-04-08T14:05:57.474446Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def build_model(input_shape=(im_size, im_size, 3)):\n    base_model = ResNet34(\n        weights='imagenet',\n        include_top=False,\n        input_shape=input_shape\n    )\n    base_model.trainable = False\n\n    model = models.Sequential()\n    model.add(base_model)\n    model.add(layers.GlobalAveragePooling2D())\n    model.add(layers.BatchNormalization())\n    model.add(layers.Dropout(0.5))\n    model.add(layers.Dense(512, activation='relu', kernel_regularizer=l2(0.0001)))\n    model.add(layers.BatchNormalization())\n    model.add(layers.Dropout(0.5))\n    model.add(layers.Dense(5, activation='sigmoid', kernel_regularizer=l2(0.0001)))\n\n    model.compile(\n        optimizer=Adam(learning_rate=1e-4, decay=1e-6),\n        loss='binary_crossentropy',\n        metrics=['accuracy']\n    )\n    return model\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:05:57.476021Z","iopub.execute_input":"2025-04-08T14:05:57.476306Z","iopub.status.idle":"2025-04-08T14:05:57.489786Z","shell.execute_reply.started":"2025-04-08T14:05:57.476282Z","shell.execute_reply":"2025-04-08T14:05:57.489159Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# def build_model(input_shape=(im_size, im_size, 3)):\n#     efficientnet = EfficientNetB7(\n#         weights='imagenet',\n#         include_top=False,\n#         input_shape=input_shape\n#     )\n#     efficientnet.trainable = True # Or False initially, then True for fine-tuning later\n\n#     model = models.Sequential()\n#     model.add(efficientnet)\n#     model.add(layers.GlobalAveragePooling2D())\n#     model.add(layers.BatchNormalization()) # Add Batch Normalization\n#     model.add(layers.Dropout(0.5))        # Keep Dropout (or adjust)\n#     model.add(layers.Dense(5, activation='sigmoid'))\n\n#     model.compile(\n#         optimizer=Adam(learning_rate=1e-4, decay=1e-6), # Or try 1e-5\n#         loss='binary_crossentropy',\n#         metrics=['accuracy']\n#     )\n#     return model","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = build_model()\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:05:57.490511Z","iopub.execute_input":"2025-04-08T14:05:57.490717Z","iopub.status.idle":"2025-04-08T14:06:03.325734Z","shell.execute_reply.started":"2025-04-08T14:05:57.490698Z","shell.execute_reply":"2025-04-08T14:06:03.325041Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Training & Evaluation","metadata":{}},{"cell_type":"code","source":"#train_df = train_df.reset_index(drop=True)\nbucket_num = 8\ndiv = round(train_df.shape[0]/bucket_num)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:06:03.326515Z","iopub.execute_input":"2025-04-08T14:06:03.326724Z","iopub.status.idle":"2025-04-08T14:06:03.330638Z","shell.execute_reply.started":"2025-04-08T14:06:03.326706Z","shell.execute_reply":"2025-04-08T14:06:03.329677Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_init = {\n    'val_loss': [0.0],\n    'val_acc': [0.0],\n    'loss': [0.0], \n    'acc': [0.0],\n    'bucket': [0.0]\n}\nresults = pd.DataFrame(df_init)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:06:03.331534Z","iopub.execute_input":"2025-04-08T14:06:03.331829Z","iopub.status.idle":"2025-04-08T14:06:03.346085Z","shell.execute_reply.started":"2025-04-08T14:06:03.331798Z","shell.execute_reply":"2025-04-08T14:06:03.345261Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import cohen_kappa_score\nfrom keras.callbacks import Callback\n\n# Custom callback class to calculate Quadratic Kappa\nclass KappaMetrics(Callback):\n    def __init__(self, validation_data):\n        super().__init__()\n        self.validation_data = validation_data\n        self.val_kappas = []\n\n    def on_epoch_end(self, epoch, logs=None):\n        logs = logs or {}\n        x_val, y_val = self.validation_data\n\n        y_val_true = y_val.argmax(axis=1)\n        y_val_pred = self.model.predict(x_val)\n        y_val_pred = y_val_pred.argmax(axis=1)\n\n        kappa = cohen_kappa_score(y_val_true, y_val_pred, weights='quadratic')\n        self.val_kappas.append(kappa)\n\n        print(f\"\\nval_kappa: {kappa:.4f}\")\n        logs['val_kappa'] = kappa\n\n# Epochs for each bucket\nepochs = [4, 4, 8, 10, 10, 12, 12, 15]  # Reduced epochs  # Reduced epochs from the 3rd bucket onwards\n\n# Pass validation data to the callback\nkappa_metrics = KappaMetrics(validation_data=(x_val, y_val))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:06:03.346844Z","iopub.execute_input":"2025-04-08T14:06:03.347053Z","iopub.status.idle":"2025-04-08T14:06:03.360091Z","shell.execute_reply.started":"2025-04-08T14:06:03.347034Z","shell.execute_reply":"2025-04-08T14:06:03.359533Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# for i in range(0, bucket_num):\n#     if i != (bucket_num-1):\n#         print(\"Bucket Nr: {}\".format(i))\n\n#         N = train_df.iloc[i*div:(1+i)*div].shape[0]\n#         x_train = np.empty((N, im_size, im_size, 3), dtype=np.float32)  # ✅ float32 for preprocess_input\n\n#         for j, image_id in enumerate(tqdm_notebook(train_df.iloc[i*div:(1+i)*div, 0])):\n#             img = preprocess_image(f'{image_id}', desired_size=im_size)  # ✅ Use updated function\n#             x_train[j, :, :, :] = img  # ✅ Apply EfficientNetB7 preprocessing\n\n#         data_generator = create_datagen().flow(x_train, y_train[i*div:(1+i)*div, :], batch_size=BATCH_SIZE)\n\n#         history = model.fit(\n#             data_generator,\n#             steps_per_epoch=x_train.shape[0] // BATCH_SIZE,\n#             epochs=epochs[i],\n#             validation_data=(x_val, y_val),\n#             callbacks=[kappa_metrics]\n#         )\n\n#         dic = history.history\n#         df_model = pd.DataFrame(dic)\n#         df_model['bucket'] = i\n\n#     else:\n#         print(\"Bucket Nr: {}\".format(i))\n\n#         N = train_df.iloc[i*div:].shape[0]\n#         x_train = np.empty((N, im_size, im_size, 3), dtype=np.float32)\n\n#         for j, image_id in enumerate(tqdm_notebook(train_df.iloc[i*div:, 0])):\n#             img = preprocess_image(f'{image_id}', desired_size=im_size)\n#             x_train[j, :, :, :] = img\n\n#         data_generator = create_datagen().flow(x_train, y_train[i*div:, :], batch_size=BATCH_SIZE)\n\n#         history = model.fit(\n#             data_generator,\n#             steps_per_epoch=x_train.shape[0] // BATCH_SIZE,\n#             epochs=epochs[i],\n#             validation_data=(x_val, y_val),\n#             callbacks=[kappa_metrics]\n#         )\n\n#         dic = history.history\n#         df_model = pd.DataFrame(dic)\n#         df_model['bucket'] = i\n\n#     results = results.append(df_model)\n\n#     del data_generator\n#     del x_train\n#     gc.collect()\n#     K.clear_session()\n#     tf.compat.v1.reset_default_graph()\n\n#     print('-'*40)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:38:08.131775Z","iopub.status.idle":"2025-04-08T14:38:08.132031Z","shell.execute_reply":"2025-04-08T14:38:08.131926Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for i in range(bucket_num):\n    print(f\"Bucket Nr: {i}\")\n\n    # Rebuild model for each bucket – this ensures a fresh start and helps free memory\n    model = build_model()\n\n    # Reinitialize the KappaMetrics callback with current validation data\n    kappa_metrics = KappaMetrics(validation_data=(x_val, y_val))\n    \n    # Select the current bucket's training data and labels\n    if i != (bucket_num - 1):\n        current_df = train_df.iloc[i*div:(i+1)*div]\n        current_labels = y_train[i*div:(i+1)*div, :]\n    else:\n        current_df = train_df.iloc[i*div:]\n        current_labels = y_train[i*div:, :]\n\n    # Load images for the current bucket into x_train\n    N = current_df.shape[0]\n    x_train = np.empty((N, im_size, im_size, 3), dtype=np.float32)\n    for j, image_id in enumerate(tqdm_notebook(current_df['id_code'])):\n        # Load image using your custom function. It should return the resized image.\n        img = preprocess_image(f'{image_id}', desired_size=im_size)\n        # DO NOT call preprocess_input here because create_datagen() already applies it.\n        x_train[j, :, :, :] = img\n\n    # Create the data generator for this bucket; it applies preprocessing_function internally.\n    data_generator = create_datagen().flow(x_train, current_labels, batch_size=BATCH_SIZE)\n\n    # Train the model on the current bucket\n    history = model.fit(\n        data_generator,\n        steps_per_epoch = x_train.shape[0] // BATCH_SIZE,\n        epochs = epochs[i],\n        validation_data = (x_val, y_val),\n        callbacks = [kappa_metrics]\n    )\n\n    # Save training history for analysis\n    df_model = pd.DataFrame(history.history)\n    df_model['bucket'] = i\n    results = pd.concat([results, df_model], ignore_index=True)\n\n    # Cleanup to free memory\n    del model, data_generator, x_train\n    gc.collect()\n    K.clear_session()\n    tf.compat.v1.reset_default_graph()\n\n    print('-' * 40)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:06:03.360901Z","iopub.execute_input":"2025-04-08T14:06:03.361179Z","iopub.status.idle":"2025-04-08T14:38:08.131261Z","shell.execute_reply.started":"2025-04-08T14:06:03.361150Z","shell.execute_reply":"2025-04-08T14:38:08.118113Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"results = results.iloc[1:]\nresults['kappa'] = kappa_metrics.val_kappas\nresults = results.reset_index()\nresults = results.rename(columns={\"index\": \"epoch\"})  # ✅ removed deprecated `index=str`\nresults\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:38:08.132762Z","iopub.status.idle":"2025-04-08T14:38:08.133069Z","shell.execute_reply":"2025-04-08T14:38:08.132919Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"results[['loss', 'val_loss']].plot()\nresults[['acc', 'val_acc']].plot()\nresults[['kappa']].plot()\nresults.to_csv('model_results.csv',index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:38:08.133852Z","iopub.status.idle":"2025-04-08T14:38:08.134276Z","shell.execute_reply":"2025-04-08T14:38:08.134078Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Find best threshold","metadata":{}},{"cell_type":"code","source":"model.load_weights('model.h5')\ny_val_pred = model.predict(x_val)\n\ndef compute_score_inv(threshold):\n    y1 = y_val_pred > threshold\n    y1 = y1.astype(int).sum(axis=1) - 1\n    y2 = y_val.sum(axis=1) - 1\n    score = cohen_kappa_score(y1, y2, weights='quadratic')\n    \n    return 1 - score\n\nsimplex = scipy.optimize.minimize(\n    compute_score_inv, 0.5, method='nelder-mead'\n)\n\nbest_threshold = simplex['x'][0]\n\ny1 = y_val_pred > best_threshold\ny1 = y1.astype(int).sum(axis=1) - 1\ny2 = y_val.sum(axis=1) - 1\nscore = cohen_kappa_score(y1, y2, weights='quadratic')\nprint('Threshold: {}'.format(best_threshold))\nprint('Validation QWK score with best_threshold: {}'.format(score))\n\ny1 = y_val_pred > .5\ny1 = y1.astype(int).sum(axis=1) - 1\nscore = cohen_kappa_score(y1, y2, weights='quadratic')\nprint('Validation QWK score with .5 threshold: {}'.format(score))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:38:08.135321Z","iopub.status.idle":"2025-04-08T14:38:08.135725Z","shell.execute_reply":"2025-04-08T14:38:08.135546Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"","metadata":{"jp-MarkdownHeadingCollapsed":true}},{"cell_type":"markdown","source":"# Results","metadata":{}},{"cell_type":"code","source":"from sklearn.metrics import classification_report, confusion_matrix, ConfusionMatrixDisplay\n\n# Predicted and actual labels\ny_pred_final = (y_val_pred > best_threshold).astype(int).sum(axis=1) - 1\ny_true_final = y_val.sum(axis=1).astype(int) - 1\n\n# Classification report\nprint(\"\\nClassification Report:\")\nprint(classification_report(y_true_final, y_pred_final))\n\n# Confusion Matrix\ncm = confusion_matrix(y_true_final, y_pred_final)\ndisp = ConfusionMatrixDisplay(confusion_matrix=cm)\ndisp.plot(cmap=plt.cm.Blues)\nplt.title('Confusion Matrix')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:38:08.136555Z","iopub.status.idle":"2025-04-08T14:38:08.136834Z","shell.execute_reply":"2025-04-08T14:38:08.136729Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import classification_report\n\n# Convert predictions to final labels using the best threshold\ny_pred_best = (y_val_pred > best_threshold).astype(int)\n# Sum across the binary predictions (as done previously) to get class indices\ny_pred_labels = y_pred_best.sum(axis=1) - 1\ny_true = y_val.sum(axis=1) - 1\n\nprint(\"Classification Report with Best Threshold:\")\nprint(classification_report(y_true, y_pred_labels))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:38:08.137345Z","iopub.status.idle":"2025-04-08T14:38:08.137732Z","shell.execute_reply":"2025-04-08T14:38:08.137544Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\n# Compute confusion matrix\ncm = confusion_matrix(y_true, y_pred_labels)\n\n# Plot the confusion matrix\nplt.figure(figsize=(8,6))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues')\nplt.title(\"Confusion Matrix (Best Threshold)\")\nplt.ylabel(\"True Label\")\nplt.xlabel(\"Predicted Label\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:38:08.138524Z","iopub.status.idle":"2025-04-08T14:38:08.138824Z","shell.execute_reply":"2025-04-08T14:38:08.138707Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import precision_score, recall_score, f1_score\n\n# Compute weighted metrics\nprecision = precision_score(y_true, y_pred_labels, average='weighted')\nrecall = recall_score(y_true, y_pred_labels, average='weighted')\nf1 = f1_score(y_true, y_pred_labels, average='weighted')\n\nprint(\"Performance Metrics (Weighted):\")\nprint(f\"Precision: {precision:.4f}\")\nprint(f\"Recall:    {recall:.4f}\")\nprint(f\"F1 Score:  {f1:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:38:08.139596Z","iopub.status.idle":"2025-04-08T14:38:08.139875Z","shell.execute_reply":"2025-04-08T14:38:08.139765Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"results[['loss', 'val_loss']].plot(title='Loss over Epochs')\nplt.xlabel(\"Epochs\")\nplt.ylabel(\"Loss\")\nplt.show()\n\nresults[['acc', 'val_acc']].plot(title='Accuracy over Epochs')\nplt.xlabel(\"Epochs\")\nplt.ylabel(\"Accuracy\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:38:08.140763Z","iopub.status.idle":"2025-04-08T14:38:08.141157Z","shell.execute_reply":"2025-04-08T14:38:08.140985Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import roc_curve, auc\nimport numpy as np\n\nn_classes = y_val.shape[1]\nfpr = dict()\ntpr = dict()\nroc_auc = dict()\n\n# Calculate ROC curve and AUC for each class\nfor i in range(n_classes):\n    fpr[i], tpr[i], _ = roc_curve(y_val[:, i], y_val_pred[:, i])\n    roc_auc[i] = auc(fpr[i], tpr[i])\n\n# Plot all ROC curves\nplt.figure(figsize=(10,8))\nfor i in range(n_classes):\n    plt.plot(fpr[i], tpr[i], label=f'Class {i} (AUC = {roc_auc[i]:.2f})')\nplt.plot([0, 1], [0, 1], 'k--')\nplt.xlabel('False Positive Rate')\nplt.ylabel('True Positive Rate')\nplt.title('ROC Curves for Each Class')\nplt.legend(loc='lower right')\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:38:08.141837Z","iopub.status.idle":"2025-04-08T14:38:08.142118Z","shell.execute_reply":"2025-04-08T14:38:08.142005Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import seaborn as sns\n\n# Plot distribution of predicted class labels\nplt.figure(figsize=(8,6))\nsns.countplot(x=y_pred_labels)\nplt.title(\"Distribution of Predicted Class Labels (Best Threshold)\")\nplt.xlabel(\"Class Label\")\nplt.ylabel(\"Count\")\nplt.show()\n\n# Plot distribution of true class labels\nplt.figure(figsize=(8,6))\nsns.countplot(x=y_true)\nplt.title(\"Distribution of True Class Labels\")\nplt.xlabel(\"Class Label\")\nplt.ylabel(\"Count\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-08T14:38:08.142887Z","iopub.status.idle":"2025-04-08T14:38:08.143140Z","shell.execute_reply":"2025-04-08T14:38:08.143035Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}