{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceType":"competition","sourceId":4104,"databundleVersionId":46661},{"sourceType":"datasetVersion","sourceId":7869237,"datasetId":4617269,"databundleVersionId":7974639},{"sourceType":"datasetVersion","sourceId":7866129,"datasetId":4614938,"databundleVersionId":7971356}],"dockerImageVersionId":30673,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"#Import the necessary libraries\nimport os\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport cv2\nfrom glob import glob\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.utils.class_weight import compute_class_weight\nimport tensorflow as tf\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.applications.resnet50 import preprocess_input\nfrom tensorflow.keras import layers, models\nfrom tensorflow.keras.callbacks import EarlyStopping,ReduceLROnPlateau, ModelCheckpoint\nfrom tensorflow.keras.applications import ResNet50\nfrom sklearn.metrics import confusion_matrix, classification_report\nimport seaborn as sns\nfrom sklearn.metrics import cohen_kappa_score\nfrom tensorflow.keras.applications import VGG16\nfrom tensorflow.keras.applications.vgg16 import preprocess_input as vgg16_preprocess_input","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T01:23:36.603668Z","iopub.execute_input":"2026-05-03T01:23:36.604052Z","iopub.status.idle":"2026-05-03T01:23:36.611831Z","shell.execute_reply.started":"2026-05-03T01:23:36.604026Z","shell.execute_reply":"2026-05-03T01:23:36.610148Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Visualizing & reading the data**","metadata":{}},{"cell_type":"code","source":"# Extracting file paths\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    print(dirname, len(filenames))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T00:22:58.728931Z","iopub.execute_input":"2026-05-02T00:22:58.729435Z","iopub.status.idle":"2026-05-02T00:24:25.153067Z","shell.execute_reply.started":"2026-05-02T00:22:58.729414Z","shell.execute_reply":"2026-05-02T00:24:25.152190Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Define directories\nlbl_path=\"/kaggle/input/diabetic-retinopathy-detection/trainLabels.csv.zip\"\ntrain_img_dir=\"/kaggle/input/diabetic-retinopathy-train-unzipped/train\"\ntest_img_dir=\"/kaggle/input/diabetic-retinopathy-test-unzipped/test\"\noutput_dir = \"/kaggle/working/models\" #I use this to save models so I don't have to run them again\nos.makedirs(output_dir, exist_ok=True)\n# Load directories for training data\ndf_full=pd.read_csv(lbl_path,sep=',')\ndf_full.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T01:22:30.503534Z","iopub.execute_input":"2026-05-03T01:22:30.503928Z","iopub.status.idle":"2026-05-03T01:22:30.544693Z","shell.execute_reply.started":"2026-05-03T01:22:30.503891Z","shell.execute_reply":"2026-05-03T01:22:30.543251Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Additional check: see that for every label we have a corresponding image\ndf_full[\"filename\"] = df_full[\"image\"].astype(str) + \".jpeg\"\ndf_full[\"filepath\"] = df_full[\"filename\"].apply(lambda x: os.path.join(train_img_dir, x))\ndf_full[\"level\"] = df_full[\"level\"].astype(str)   # needed for categorical ImageDataGenerator\ndf_full[\"exists\"] = df_full[\"filepath\"].apply(os.path.exists)\nprint(df_full[\"exists\"].value_counts())\ndf_full = df_full[df_full[\"exists\"]].copy()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T01:22:33.676818Z","iopub.execute_input":"2026-05-03T01:22:33.677214Z","iopub.status.idle":"2026-05-03T01:23:09.563661Z","shell.execute_reply.started":"2026-05-03T01:22:33.677188Z","shell.execute_reply":"2026-05-03T01:23:09.562446Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Check if we are able to visualize training set images\npaths_train= glob('/kaggle/input/diabetic-retinopathy-train-unzipped/train/*.jpeg')\nimage_train=cv2.imread(paths_train[0])\nplt.imshow(image_train)\n\n# Check if we are able to visualize test set images\npaths_test = glob('/kaggle/input/diabetic-retinopathy-test-unzipped/test/*.jpeg')\nimage_test=cv2.imread(paths_test[0])\nplt.imshow(image_test)","metadata":{"execution":{"iopub.status.busy":"2026-05-03T01:23:48.300256Z","iopub.execute_input":"2026-05-03T01:23:48.301060Z","iopub.status.idle":"2026-05-03T01:23:54.858385Z","shell.execute_reply.started":"2026-05-03T01:23:48.301029Z","shell.execute_reply":"2026-05-03T01:23:54.857181Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Let's check the class imbalance in the traibning set\nclass_counts = df_full[\"level\"].value_counts().sort_index()\nprint(class_counts)\n\n# Let's vizualise\nclass_counts.plot(kind=\"bar\")\nplt.title(\"Class distribution\")\nplt.xlabel(\"Retinopathy level\")\nplt.ylabel(\"Number of images\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T01:23:54.860107Z","iopub.execute_input":"2026-05-03T01:23:54.860430Z","iopub.status.idle":"2026-05-03T01:23:55.104908Z","shell.execute_reply.started":"2026-05-03T01:23:54.860403Z","shell.execute_reply":"2026-05-03T01:23:55.103618Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"We have an overwhelmingly dominating category (class 0). We will handle it by stratifying train and validation split, by using image aumentation and class weights during training.","metadata":{}},{"cell_type":"markdown","source":"**Train/validation split**","metadata":{}},{"cell_type":"code","source":"# Let's split the data set for training and validation\ndf_train,df_val=train_test_split(df_full,train_size=0.7,random_state=20,stratify=df_full[\"level\"])\nprint(df_train.shape)\nprint(df_val.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T01:23:55.106514Z","iopub.execute_input":"2026-05-03T01:23:55.106930Z","iopub.status.idle":"2026-05-03T01:23:55.163728Z","shell.execute_reply.started":"2026-05-03T01:23:55.106892Z","shell.execute_reply":"2026-05-03T01:23:55.162314Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Preprocessing the data**","metadata":{}},{"cell_type":"code","source":"# Pre-processing the images for CNN\nimg_size=224\nbatch_size=32\nnb_class=5 # from 0 to 4\n\n# Rescaling the images for training data set\ncnn_train_datagen = ImageDataGenerator(rescale=1./255,rotation_range=20,zoom_range=0.15,width_shift_range=0.1,height_shift_range=0.1, horizontal_flip=True,fill_mode=\"nearest\")\ncnn_train_gen = cnn_train_datagen.flow_from_dataframe(dataframe=df_train, x_col=\"filepath\",y_col=\"level\",target_size=(img_size, img_size),batch_size=batch_size,class_mode=\"categorical\",shuffle=True)\n\n# Rescaling the images for validation data set\ncnn_val_datagen = ImageDataGenerator(rescale=1./255)\ncnn_val_gen = cnn_val_datagen.flow_from_dataframe(dataframe=df_val,x_col=\"filepath\",y_col=\"level\",target_size=(img_size, img_size),batch_size=batch_size, class_mode=\"categorical\",shuffle=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T01:23:57.103851Z","iopub.execute_input":"2026-05-03T01:23:57.104257Z","iopub.status.idle":"2026-05-03T01:24:28.915751Z","shell.execute_reply.started":"2026-05-03T01:23:57.104229Z","shell.execute_reply":"2026-05-03T01:24:28.914702Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Pre processing for VGG16\n# Rescaling the images for training data set\nvgg16_train_datagen = ImageDataGenerator(preprocessing_function=vgg16_preprocess_input,rotation_range=20,zoom_range=0.15,width_shift_range=0.1,height_shift_range=0.1, horizontal_flip=True,fill_mode=\"nearest\")\nvgg16_train_gen = vgg16_train_datagen.flow_from_dataframe(dataframe=df_train,x_col=\"filepath\",y_col=\"level\",target_size=(img_size, img_size),batch_size=batch_size,class_mode=\"categorical\",shuffle=True)\n\n# Rescaling the images for validation data set\nvgg16_val_datagen = ImageDataGenerator( preprocessing_function=vgg16_preprocess_input)\nvgg16_val_gen = vgg16_val_datagen.flow_from_dataframe(dataframe=df_val,x_col=\"filepath\",y_col=\"level\",target_size=(img_size, img_size),batch_size=batch_size,class_mode=\"categorical\",shuffle=False)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Pre-processing the images for ResNet\n# Image and batch size and the number of classes are all the same\n# Rescaling the images for training data set\nresnet_train_datagen = ImageDataGenerator(preprocessing_function=preprocess_input,rotation_range=20,zoom_range=0.15,width_shift_range=0.1,height_shift_range=0.1, horizontal_flip=True,fill_mode=\"nearest\")\nresnet_train_gen = resnet_train_datagen.flow_from_dataframe(dataframe=df_train, x_col=\"filepath\",y_col=\"level\",target_size=(img_size, img_size),batch_size=batch_size,class_mode=\"categorical\",shuffle=True)\n\n# Rescaling the images for validation data set\nresnet_val_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)\nresnet_val_gen = resnet_val_datagen.flow_from_dataframe(dataframe=df_val,x_col=\"filepath\",y_col=\"level\",target_size=(img_size, img_size),batch_size=batch_size, class_mode=\"categorical\",shuffle=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T08:57:19.853210Z","iopub.execute_input":"2026-05-02T08:57:19.853582Z","iopub.status.idle":"2026-05-02T08:57:56.737078Z","shell.execute_reply.started":"2026-05-02T08:57:19.853552Z","shell.execute_reply":"2026-05-02T08:57:56.736444Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Let's compute the class weights\nclass_labels = list(cnn_train_gen.class_indices.keys())\nclass_weights_array = compute_class_weight(class_weight=\"balanced\",classes=np.array(class_labels),y=df_train[\"level\"])\n\nclass_weights = {\n    cnn_train_gen.class_indices[label]: weight\n    for label, weight in zip(class_labels, class_weights_array)\n}\nprint(cnn_train_gen.class_indices)\nprint(class_weights)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T01:24:28.917409Z","iopub.execute_input":"2026-05-03T01:24:28.917710Z","iopub.status.idle":"2026-05-03T01:24:28.938753Z","shell.execute_reply.started":"2026-05-03T01:24:28.917686Z","shell.execute_reply":"2026-05-03T01:24:28.937398Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Pre training CNN for better performance**","metadata":{}},{"cell_type":"code","source":"# During the first run of CNN, results were not promising, let's add a pre-trainer\ndf_train_pretrain = df_train.copy()\ndf_val_pretrain = df_val.copy()\ndf_train_pretrain[\"binary_level\"] = df_train_pretrain[\"level\"].apply( lambda x: \"0\" if str(x) == \"0\" else \"1\")\ndf_val_pretrain[\"binary_level\"] = df_val_pretrain[\"level\"].apply(lambda x: \"0\" if str(x) == \"0\" else \"1\")\n\n# QUick check\nprint(df_train_pretrain[\"binary_level\"].value_counts())\nprint(df_val_pretrain[\"binary_level\"].value_counts())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T01:24:28.940493Z","iopub.execute_input":"2026-05-03T01:24:28.940972Z","iopub.status.idle":"2026-05-03T01:24:28.980220Z","shell.execute_reply.started":"2026-05-03T01:24:28.940934Z","shell.execute_reply":"2026-05-03T01:24:28.978975Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Let's create binary generators\nbinary_train_gen = cnn_train_datagen.flow_from_dataframe(dataframe=df_train_pretrain,x_col=\"filepath\",y_col=\"binary_level\",target_size=(img_size, img_size),batch_size=batch_size,class_mode=\"categorical\",shuffle=True)\nbinary_val_gen = cnn_val_datagen.flow_from_dataframe(dataframe=df_val_pretrain,x_col=\"filepath\",y_col=\"binary_level\", target_size=(img_size, img_size), batch_size=batch_size, class_mode=\"categorical\",shuffle=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T01:24:28.983016Z","iopub.execute_input":"2026-05-03T01:24:28.983506Z","iopub.status.idle":"2026-05-03T01:24:44.204619Z","shell.execute_reply.started":"2026-05-03T01:24:28.983468Z","shell.execute_reply":"2026-05-03T01:24:44.203619Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Modeling and Training the data**","metadata":{}},{"cell_type":"code","source":"# Modeling & training\n#Set a seed for reproducibility\ntf.random.set_seed(30)\nnp.random.seed(30)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T01:24:44.205836Z","iopub.execute_input":"2026-05-03T01:24:44.206148Z","iopub.status.idle":"2026-05-03T01:24:44.211495Z","shell.execute_reply.started":"2026-05-03T01:24:44.206118Z","shell.execute_reply":"2026-05-03T01:24:44.210515Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# The model took too long to run, we can add the following funtions to control it better during the training phase\nearly_stop = EarlyStopping(monitor=\"val_loss\",patience=4,restore_best_weights=True) #If validation loss does not improve for 4 epochs in a row stop training & go back to the model weights from the best validation loss epoch\nreduce_lr = ReduceLROnPlateau(monitor=\"val_loss\",factor=0.75,patience=4,min_lr=1e-7,verbose=1) #If validation loss does not improve for 4 epochs in a row reduce the learning rate and multiply it by 0.75","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T01:24:44.213541Z","iopub.execute_input":"2026-05-03T01:24:44.214052Z","iopub.status.idle":"2026-05-03T01:24:44.227039Z","shell.execute_reply.started":"2026-05-03T01:24:44.214003Z","shell.execute_reply":"2026-05-03T01:24:44.225635Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Define basic CNN: we can start with the easiest model\nfrom tensorflow.keras import layers, models\n\ndef basic_cnn(input_shape, nb_class):\n    model_1 = models.Sequential()\n    model_1.add(layers.Input(shape=input_shape))\n    model_1.add(layers.Conv2D(32, (3, 3), activation='relu'))\n    model_1.add(layers.MaxPooling2D((2, 2)))\n    model_1.add(layers.Conv2D(64, (3, 3), activation='relu'))\n    model_1.add(layers.MaxPooling2D((2, 2)))\n    model_1.add(layers.Dropout(0.5))\n    model_1.add(layers.Flatten())\n    model_1.add(layers.Dense(128, activation='relu'))\n    model_1.add(layers.Dense(nb_class, activation='softmax'))\n    model_1.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),loss='categorical_crossentropy', metrics=['accuracy'])\n    return model_1","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T12:19:16.858313Z","iopub.execute_input":"2026-05-01T12:19:16.858758Z","iopub.status.idle":"2026-05-01T12:19:16.875822Z","shell.execute_reply.started":"2026-05-01T12:19:16.858724Z","shell.execute_reply":"2026-05-01T12:19:16.874488Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Let's add pre training\nbasic_cnn_pretrained = basic_cnn(input_shape=(img_size, img_size, 3),nb_class=2)\nbasic_cnn_pretrained.summary()\nhistory_basic_pretrain = basic_cnn_pretrained.fit(binary_train_gen,validation_data=binary_val_gen,epochs=5, callbacks=[early_stop, reduce_lr])\n\n# Save results\nbasic_cnn_pretrained.save(f\"{output_dir}/basic_cnn_binary_pretrained.keras\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T12:19:16.877364Z","iopub.execute_input":"2026-05-01T12:19:16.877694Z","iopub.status.idle":"2026-05-01T17:08:47.588919Z","shell.execute_reply.started":"2026-05-01T12:19:16.877671Z","shell.execute_reply":"2026-05-01T17:08:47.579242Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Build the model\nmodel_1=basic_cnn(input_shape=(img_size, img_size, 3),nb_class=nb_class)\n\n# Transfer weights with pre training layer\nfor layer_new, layer_old in zip(model_1.layers[:-1], basic_cnn_pretrained.layers[:-1]):\n    if layer_new.get_weights(): layer_new.set_weights(layer_old.get_weights())\n\n# View built model\nmodel_1.summary()\n\n# Save model\ncheckpoint_basic = tf.keras.callbacks.ModelCheckpoint(filepath=f\"{output_dir}/basic_cnn_best.keras\",monitor=\"val_loss\",save_best_only=True,verbose=1) # This was suggested by AI as being safer\n\n# Fitting the mocdel\nhistory_basic_cnn = model_1.fit(cnn_train_gen,validation_data=cnn_val_gen,epochs=5,class_weight=class_weights,callbacks=[early_stop, reduce_lr, checkpoint_basic])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T17:08:47.596782Z","iopub.execute_input":"2026-05-01T17:08:47.597565Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Evaluating accuracy and loss for basic cnn**","metadata":{}},{"cell_type":"code","source":"# Function defined for plotting (found in old ML code provided by teacher in first semester)\ndef plot_history(history, title):\n    plt.figure(figsize=(12, 4))\n\n    plt.subplot(1, 2, 1)\n    plt.plot(history.history[\"accuracy\"], label=\"Train accuracy\")\n    plt.plot(history.history[\"val_accuracy\"], label=\"Validation accuracy\")\n    plt.title(title + \" - Accuracy\")\n    plt.xlabel(\"Epoch\")\n    plt.ylabel(\"Accuracy\")\n    plt.legend()\n\n    plt.subplot(1, 2, 2)\n    plt.plot(history.history[\"loss\"], label=\"Train loss\")\n    plt.plot(history.history[\"val_loss\"], label=\"Validation loss\")\n    plt.title(title + \" - Loss\")\n    plt.xlabel(\"Epoch\")\n    plt.ylabel(\"Loss\")\n    plt.legend()\n\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T00:22:55.261989Z","iopub.execute_input":"2026-05-03T00:22:55.262374Z","iopub.status.idle":"2026-05-03T00:22:55.269560Z","shell.execute_reply.started":"2026-05-03T00:22:55.262345Z","shell.execute_reply":"2026-05-03T00:22:55.268446Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Kappa score function\ndef compute_kappa(model, generator, model_name):\n    generator.reset()\n\n    pred_probs = model.predict(generator)\n    y_pred = np.argmax(pred_probs, axis=1)\n    y_true = generator.classes\n\n    kappa = cohen_kappa_score(y_true, y_pred, weights=\"quadratic\")\n\n    print(f\"{model_name} quadratic weighted kappa: {kappa:.4f}\")\n\n    return kappa","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T00:22:55.271132Z","iopub.execute_input":"2026-05-03T00:22:55.271996Z","iopub.status.idle":"2026-05-03T00:22:55.281401Z","shell.execute_reply.started":"2026-05-03T00:22:55.271957Z","shell.execute_reply":"2026-05-03T00:22:55.280360Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plotting\nplot_history(history_basic_cnn, \"Basic CNN\")\n\n#Evaluating performance\nbasic_val_loss, basic_val_acc = model_1.evaluate(cnn_val_gen)\nbasic_kappa = compute_kappa(model_1, cnn_val_gen, \"Pretrained Basic CNN\")\n\n#Save model\nmodel_1.save(f\"{output_dir}/basic_cnn_pretrained_5class.keras\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T22:44:41.750841Z","iopub.execute_input":"2026-05-01T22:44:41.751275Z","iopub.status.idle":"2026-05-01T23:06:58.451036Z","shell.execute_reply.started":"2026-05-01T22:44:41.751243Z","shell.execute_reply":"2026-05-01T23:06:58.447864Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Deep CNN**","metadata":{}},{"cell_type":"code","source":"# Define deep cnn\ndef deep_cnn(input_shape=(224,224, 3), nb_class=5):\n    model_2 = models.Sequential()\n    model_2.add(layers.Input(shape=input_shape))\n    model_2.add(layers.Conv2D(32, (3, 3), padding=\"same\", activation=\"relu\"))\n    model_2.add(layers.BatchNormalization())\n    model_2.add(layers.Conv2D(32, (3, 3), padding=\"same\", activation=\"relu\"))\n    model_2.add(layers.BatchNormalization())\n    model_2.add(layers.MaxPooling2D((2, 2)))\n    model_2.add(layers.Dropout(0.25))\n    model_2.add(layers.Conv2D(64, (3, 3), padding=\"same\", activation=\"relu\"))\n    model_2.add(layers.BatchNormalization())\n    model_2.add(layers.Conv2D(64, (3, 3), padding=\"same\", activation=\"relu\"))\n    model_2.add(layers.BatchNormalization())\n    model_2.add(layers.MaxPooling2D((2, 2)))\n    model_2.add(layers.Dropout(0.25))\n    model_2.add(layers.Conv2D(128, (3, 3), padding=\"same\", activation=\"relu\"))\n    model_2.add(layers.BatchNormalization())\n    model_2.add(layers.Conv2D(128, (3, 3), padding=\"same\", activation=\"relu\"))\n    model_2.add(layers.BatchNormalization())\n    model_2.add(layers.MaxPooling2D((2, 2)))\n    model_2.add(layers.Dropout(0.35))\n    model_2.add(layers.Conv2D(256, (3, 3), padding=\"same\", activation=\"relu\"))\n    model_2.add(layers.BatchNormalization())\n    model_2.add(layers.MaxPooling2D((2, 2)))\n    model_2.add(layers.Dropout(0.4))\n    model_2.add(layers.GlobalAveragePooling2D())\n    model_2.add(layers.Dense(256, activation=\"relu\"))\n    model_2.add(layers.BatchNormalization())\n    model_2.add(layers.Dropout(0.5))\n    model_2.add(layers.Dense(nb_class, activation=\"softmax\"))\n    model_2.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),loss='categorical_crossentropy', metrics=['accuracy'])\n    return model_2","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T01:32:22.201519Z","iopub.execute_input":"2026-05-03T01:32:22.201891Z","iopub.status.idle":"2026-05-03T01:32:22.214853Z","shell.execute_reply.started":"2026-05-03T01:32:22.201865Z","shell.execute_reply":"2026-05-03T01:32:22.213204Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# We can pretrain the deep CNN as well\ndeep_cnn_pretrained = deep_cnn(input_shape=(img_size, img_size, 3),nb_class=2)\ndeep_cnn_pretrained.summary()\nhistory_deep_pretrain = deep_cnn_pretrained.fit(binary_train_gen,validation_data=binary_val_gen,epochs=5,callbacks=[early_stop, reduce_lr])\ndeep_cnn_pretrained.save(f\"{output_dir}/deep_cnn_binary_pretrained.keras\") #save model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T16:49:39.555921Z","iopub.execute_input":"2026-05-02T16:49:39.556512Z","iopub.status.idle":"2026-05-02T20:00:09.760185Z","shell.execute_reply.started":"2026-05-02T16:49:39.556483Z","shell.execute_reply":"2026-05-02T20:00:09.749210Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Build the model\nmodel_2=deep_cnn(input_shape=(img_size, img_size, 3),nb_class=nb_class)\n\n# Add pre training weights\nfor layer_new, layer_old in zip(model_2.layers[:-1], deep_cnn_pretrained.layers[:-1]):\n    if layer_new.get_weights(): layer_new.set_weights(layer_old.get_weights())\n\n# View built model\nmodel_2.summary()\n\n# Save model \ncheckpoint_deep = tf.keras.callbacks.ModelCheckpoint(filepath=f\"{output_dir}/deep_cnn_best.keras\",monitor=\"val_loss\",save_best_only=True,verbose=1)\n\n# Fitting the mocdel\nhistory_deep_cnn = model_2.fit(cnn_train_gen,validation_data=cnn_val_gen,epochs=5,class_weight=class_weights,callbacks=[early_stop, reduce_lr, checkpoint_deep])\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plotting\nplot_history(history_deep_cnn, \"Deep CNN\")\n\n#Evaluating performance\ndeep_val_loss, deep_val_acc = model_2.evaluate(cnn_val_gen)\ndeep_kappa = compute_kappa(model_2, cnn_val_gen, \"Pretrained Deep CNN\")\n\nmodel_2.save(f\"{output_dir}/deep_cnn_pretrained_5class.keras\") # Save model","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**VGG16**","metadata":{}},{"cell_type":"code","source":"\n# Build VGG16 model\nbase_model_vgg16 = VGG16(weights=\"imagenet\", include_top=False, input_shape=(img_size, img_size, 3))\nbase_model_vgg16.trainable = False\n\ninputs = layers.Input(shape=(img_size, img_size, 3))\nx = base_model_vgg16(inputs, training=False)\nx = layers.GlobalAveragePooling2D()(x)\nx = layers.Dense(256, activation=\"relu\")(x)\nx = layers.BatchNormalization()(x)\nx = layers.Dropout(0.5)(x)\noutputs = layers.Dense(nb_class, activation=\"softmax\")(x)\nmodel_vgg16 = models.Model(inputs, outputs, name=\"vgg16_retinopathy\")\n\n# Compile\nmodel_vgg16.compile( optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),loss=\"categorical_crossentropy\",metrics=[\"accuracy\"])\n\n# View\nmodel_vgg16.summary()\n\n# Save\ncheckpoint_vgg16 = tf.keras.callbacks.ModelCheckpoint(filepath=f\"{output_dir}/vgg16_best.keras\", monitor=\"val_loss\", save_best_only=True,verbose=1)\n\n# Now we can fit the model\nhistory_vgg16 = model_vgg16.fit(vgg16_train_gen,validation_data=vgg16_val_gen,epochs=5,class_weight=class_weights,callbacks=[early_stop, reduce_lr, checkpoint_vgg16])","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plotting\nplot_history(history_vgg16, \"VGG16\")\n\n# Evaluatung the performance\nvgg16_val_loss, vgg16_val_acc = model_vgg16.evaluate(vgg16_val_gen)\nvgg16_kappa = compute_kappa(model_vgg16,vgg16_val_gen, \"VGG16\")\nprint(\"VGG16 validation loss:\", vgg16_val_loss)\nprint(\"VGG16 validation accuracy:\", vgg16_val_acc)\n\n# Saving (again)\nmodel_vgg16.save(f\"{output_dir}/vgg16_retinopathy.keras\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Resnet 50**","metadata":{}},{"cell_type":"code","source":"# Define Resnet: we can try a more complex model\ndef resnet_model(input_shape=(224,224, 3), nb_class=5):\n    base_model_resnet = ResNet50(weights=\"imagenet\",include_top=False,input_shape=input_shape)\n    base_model_resnet.trainable = False\n    inputs = layers.Input(shape=input_shape)\n    x = base_model_resnet(inputs, training=False)\n    x = layers.GlobalAveragePooling2D()(x)\n    x = layers.Dense(256, activation=\"relu\")(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Dropout(0.5)(x)\n    outputs = layers.Dense(nb_class, activation=\"softmax\")(x)\n    model_3 = models.Model(inputs, outputs)\n    model_3.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),loss=\"categorical_crossentropy\",metrics=[\"accuracy\"])\n    return model_3\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T08:57:56.800596Z","iopub.execute_input":"2026-05-02T08:57:56.801060Z","iopub.status.idle":"2026-05-02T08:57:56.815190Z","shell.execute_reply.started":"2026-05-02T08:57:56.801041Z","shell.execute_reply":"2026-05-02T08:57:56.814569Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Build the model\nmodel_3=resnet_model(input_shape=(img_size, img_size, 3),nb_class=nb_class)\n\n# View built model\nmodel_3.summary()\n\n# Save model\ncheckpoint_resnet = tf.keras.callbacks.ModelCheckpoint(filepath=f\"{output_dir}/resnet50_best.keras\",monitor=\"val_loss\",save_best_only=True,verbose=1)\n\n# Fitting the mocdel\nhistory_resnet= model_3.fit(resnet_train_gen,validation_data=resnet_val_gen,epochs=5,class_weight=class_weights,callbacks=[early_stop, reduce_lr, checkpoint_resnet])\nmodel_3.save(f\"{output_dir}/resnet50_retinopathy.keras\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T08:57:56.816081Z","iopub.execute_input":"2026-05-02T08:57:56.816605Z","iopub.status.idle":"2026-05-02T12:08:54.355991Z","shell.execute_reply.started":"2026-05-02T08:57:56.816587Z","shell.execute_reply":"2026-05-02T12:08:54.344514Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model_3 = tf.keras.models.load_model(\"/kaggle/working/models/resnet50_best.keras\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T13:01:19.670130Z","iopub.execute_input":"2026-05-02T13:01:19.671012Z","iopub.status.idle":"2026-05-02T13:01:25.769085Z","shell.execute_reply.started":"2026-05-02T13:01:19.670981Z","shell.execute_reply":"2026-05-02T13:01:25.767981Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plotting\nplot_history(history_resnet, \"Basic Resnet\")\n\n#Evaluating performance\nresnet_val_loss, resnet_val_acc = model_3.evaluate(resnet_val_gen)\nresnet_kappa = compute_kappa(model_3, resnet_val_gen, \"ResNet50\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T13:01:25.770938Z","iopub.execute_input":"2026-05-02T13:01:25.772077Z","iopub.status.idle":"2026-05-02T13:19:20.841181Z","shell.execute_reply.started":"2026-05-02T13:01:25.772053Z","shell.execute_reply":"2026-05-02T13:19:20.840220Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**How about adjusting Resnet for a better performance?**","metadata":{}},{"cell_type":"code","source":"base_model_resnet = model_3.layers[1]\n\nbase_model_resnet.trainable = True\nfor layer in base_model_resnet.layers[:-30]:\n    layer.trainable = False\nmodel_3.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-5),loss=\"categorical_crossentropy\",metrics=[\"accuracy\"])\n\n\n# Save model\ncheckpoint_resnet_fine_tuned = tf.keras.callbacks.ModelCheckpoint(filepath=f\"{output_dir}/resnet50_fine_tuned_best.keras\",monitor=\"val_loss\",save_best_only=True,verbose=1)\n\n# Fitting the mocdel\nhistory_resnet_fine_tuned = model_3.fit(resnet_train_gen,validation_data=resnet_val_gen,epochs=5,class_weight=class_weights,callbacks=[early_stop, reduce_lr, checkpoint_resnet_fine_tuned])\nmodel_3.save(f\"{output_dir}/resnet50_fine_tuned_retinopathy.keras\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T13:26:01.736396Z","iopub.execute_input":"2026-05-02T13:26:01.737215Z","iopub.status.idle":"2026-05-02T16:00:24.553170Z","shell.execute_reply.started":"2026-05-02T13:26:01.737185Z","shell.execute_reply":"2026-05-02T16:00:24.541108Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plotting \nplot_history(history_resnet_fine_tuned, \"ResNet50 Fine Tuned\")\n\n# Evaluating\nresnet_fine_tuned_val_loss, resnet_fine_tuned_val_acc = model_3.evaluate(resnet_val_gen)\nresnet_fine_tuned_kappa = compute_kappa(model_3, resnet_val_gen, \"ResNet50 Fine Tuned\")\nprint(\"ResNet50 fine-tuned validation loss:\", resnet_fine_tuned_val_loss)\nprint(\"ResNet50 fine-tuned validation accuracy:\", resnet_fine_tuned_val_acc)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-02T16:00:24.562838Z","iopub.execute_input":"2026-05-02T16:00:24.564334Z","iopub.status.idle":"2026-05-02T16:17:16.025750Z","shell.execute_reply.started":"2026-05-02T16:00:24.564257Z","shell.execute_reply":"2026-05-02T16:17:16.024851Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Let's compare all three models**","metadata":{}},{"cell_type":"code","source":"# Saving results in the same dataframe\nresults = pd.DataFrame({\n    \"Model\": [\n        \"Pretrained Basic CNN\",\n        \"Pretrained Deep CNN\",\n        \"ResNet50\",\n        \"ResNet50 Fine Tuned\"\n    ],\n    \"Validation Loss\": [\n        basic_val_loss,\n        deep_val_loss,\n        resnet_val_loss,\n        resnet_fine_tuned_val_loss\n    ],\n    \"Validation Accuracy\": [\n        basic_val_acc,\n        deep_val_acc,\n        resnet_val_acc,\n        resnet_fine_tuned_val_acc\n    ],\n    \"Quadratic Weighted Kappa\": [\n        basic_kappa,\n        deep_kappa,\n        resnet_kappa,\n        resnet_fine_tuned_kappa\n    ]\n})\n\nresults.sort_values(by=\"Quadratic Weighted Kappa\", ascending=False)\nprint(results)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plotting the results\nplt.figure(figsize=(8, 5))\nplt.bar(results[\"Model\"], results[\"Quadratic Weighted Kappa\"])\nplt.title(\"Model Comparison - Quadratic Weighted Kappa\")\nplt.ylabel(\"Quadratic Weighted Kappa\")\nplt.ylim(-1, 1)\nplt.xticks(rotation=20)\nplt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#file_sub=\"/kaggle/input/diabetic-retinopathy-detection/sampleSubmission.csv.zip\"\n#df_submission=pd.read_csv(file_sub,sep=',')\n#df_submission.loc[0, 'level']=1\n#df_submission\n#df_submission.to_csv('submission.csv', index=False)","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}