{"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":14774,"databundleVersionId":875431,"sourceType":"competition"},{"sourceId":13903908,"sourceType":"datasetVersion","datasetId":8858400}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# ## Cell 1: Imports and Setup (Updated for Research Paper)\nimport os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\n# Sklearn for metrics and splitting\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.utils.class_weight import compute_class_weight\nfrom sklearn.metrics import classification_report, confusion_matrix, cohen_kappa_score, fbeta_score\n\n# TensorFlow and Keras\nimport tensorflow as tf\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras import layers, Model\nfrom tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau, CSVLogger\nfrom tensorflow.keras.models import load_model\n\n# CRITICAL IMPORT: All 3 architectures for comparison\nfrom tensorflow.keras.applications import EfficientNetB0, ResNet50, DenseNet121\n\nprint(f\"TensorFlow Version: {tf.__version__}\")\n\n# Check for GPU\ngpus = tf.config.list_physical_devices('GPU')\nif gpus:\n    print(f\"GPU(s) available: {len(gpus)}\")\n    # Optional: Set Mixed Precision for T4 GPUs to speed up training\n    from tensorflow.keras import mixed_precision\n    mixed_precision.set_global_policy('mixed_float16')\n    print(\"Mixed Precision enabled.\")\nelse:\n    print(\"No GPU detected.\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-11-28T05:23:55.908146Z","iopub.execute_input":"2025-11-28T05:23:55.908321Z","iopub.status.idle":"2025-11-28T05:23:55.946616Z","shell.execute_reply.started":"2025-11-28T05:23:55.908306Z","shell.execute_reply":"2025-11-28T05:23:55.945944Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ## Cell 2: Configuration\n\n# Paths to your Kaggle data\nTRAIN_CSV = '/kaggle/input/aptos2019-blindness-detection/train.csv'\nTRAIN_IMG_DIR = '/kaggle/input/aptos2019-blindness-detection/train_images'\n\n# Model and training hyperparameters\nIMG_SIZE = (384, 384)\nBATCH_SIZE = 16\nSEED = 42\nEPOCHS = 25  # Increased from 20 to allow more time for augmentation\nNUM_CLASSES = 5\n\n# Set seeds for reproducibility\nnp.random.seed(SEED)\ntf.random.set_seed(SEED)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-28T05:23:50.777440Z","iopub.execute_input":"2025-11-28T05:23:50.778192Z","iopub.status.idle":"2025-11-28T05:23:50.782258Z","shell.execute_reply.started":"2025-11-28T05:23:50.778168Z","shell.execute_reply":"2025-11-28T05:23:50.781574Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ## Cell 3: Load and Prepare Data\n\n# Load the CSV and ensure the diagnosis column is a string for the generator\ndf = pd.read_csv(TRAIN_CSV)\ndf['id_code'] = df['id_code'].astype(str) + '.png'\n\n# This line fixes the error by converting the diagnosis labels to strings\ndf['diagnosis'] = df['diagnosis'].astype(str)\n\nprint(\"Data loaded successfully.\")\nprint(f\"Total samples: {len(df)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-28T05:23:50.785704Z","iopub.execute_input":"2025-11-28T05:23:50.785996Z","iopub.status.idle":"2025-11-28T05:23:50.829074Z","shell.execute_reply.started":"2025-11-28T05:23:50.785972Z","shell.execute_reply":"2025-11-28T05:23:50.828484Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ## Cell 4: Quick EDA\n# Visualize the class distribution\nplt.figure(figsize=(10, 6))\ndf['diagnosis'].value_counts().sort_index().plot(kind='bar', color='skyblue')\nplt.title('Class Distribution of Diabetic Retinopathy')\nplt.xlabel('Diagnosis Level')\nplt.ylabel('Number of Images')\nplt.xticks(rotation=0)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-28T05:23:50.829724Z","iopub.execute_input":"2025-11-28T05:23:50.829942Z","iopub.status.idle":"2025-11-28T05:23:51.087990Z","shell.execute_reply.started":"2025-11-28T05:23:50.829924Z","shell.execute_reply":"2025-11-28T05:23:51.087243Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ## Cell 5: Train/Validation Split\n# Create a stratified split to maintain class distribution in both sets\ntrain_df, val_df = train_test_split(\n    df,\n    test_size=0.15,\n    random_state=SEED,\n    stratify=df['diagnosis']\n)\n\nprint(f\"Training samples: {len(train_df)}\")\nprint(f\"Validation samples: {len(val_df)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-28T05:23:51.088780Z","iopub.execute_input":"2025-11-28T05:23:51.089144Z","iopub.status.idle":"2025-11-28T05:23:51.099404Z","shell.execute_reply.started":"2025-11-28T05:23:51.089119Z","shell.execute_reply":"2025-11-28T05:23:51.098626Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ## Cell 6: Data Generators (with Augmentations)\n\n# Add basic augmentations to the training generator to combat overfitting\ntrain_datagen = ImageDataGenerator(\n    rescale=1./255,\n    rotation_range=15,\n    horizontal_flip=True,\n    zoom_range=0.1,\n    width_shift_range=0.1,\n    height_shift_range=0.1\n)\n\n# The validation generator should NOT have augmentations\nval_datagen = ImageDataGenerator(rescale=1./255)\n\ntrain_gen = train_datagen.flow_from_dataframe(\n    dataframe=train_df,\n    directory=TRAIN_IMG_DIR,\n    x_col='id_code',\n    y_col='diagnosis',\n    target_size=IMG_SIZE,\n    batch_size=BATCH_SIZE,\n    class_mode='sparse',\n    shuffle=True,\n    seed=SEED\n)\n\nval_gen = val_datagen.flow_from_dataframe(\n    dataframe=val_df,\n    directory=TRAIN_IMG_DIR,\n    x_col='id_code',\n    y_col='diagnosis',\n    target_size=IMG_SIZE,\n    batch_size=BATCH_SIZE,\n    class_mode='sparse',\n    shuffle=False\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-28T05:23:51.101045Z","iopub.execute_input":"2025-11-28T05:23:51.101600Z","iopub.status.idle":"2025-11-28T05:23:55.896643Z","shell.execute_reply.started":"2025-11-28T05:23:51.101581Z","shell.execute_reply":"2025-11-28T05:23:55.896000Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ## Cell 7: Calculate Class Weights\n# Correctly compute class weights on the integer labels to handle imbalance\nclasses = np.unique(train_df['diagnosis'])\nclass_weights = compute_class_weight(\n    'balanced',\n    classes=classes,\n    y=train_df['diagnosis'].values\n)\nclass_weight_dict = {c: w for c, w in zip(classes, class_weights)}\n\nprint(\"Class weights calculated:\")\nprint(class_weight_dict)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-28T05:23:55.897350Z","iopub.execute_input":"2025-11-28T05:23:55.897605Z","iopub.status.idle":"2025-11-28T05:23:55.905121Z","shell.execute_reply.started":"2025-11-28T05:23:55.897582Z","shell.execute_reply":"2025-11-28T05:23:55.904382Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ## Cell 8: Build the DenseNet121 Model (Experiment A - Part 3)\n\ndef build_densenet_model(input_shape=IMG_SIZE + (3,), n_classes=NUM_CLASSES):\n    \"\"\"Builds a DenseNet121 model for comparative analysis.\"\"\"\n    \n    # Base model: Switched to DenseNet121\n    # DenseNet connects each layer to every other layer in a feed-forward fashion.\n    # This architecture is excellent for feature reuse in medical imaging.\n    base = DenseNet121(\n        include_top=False,\n        weights='imagenet',\n        input_shape=input_shape\n    )\n    \n    # Full Fine-Tuning: We unfreeze the base to learn specific diabetic retinopathy features\n    base.trainable = True \n\n    # Model architecture (Kept IDENTICAL to EfficientNet/ResNet for fair comparison)\n    inputs = layers.Input(shape=input_shape)\n    x = base(inputs, training=True)\n    x = layers.GlobalAveragePooling2D()(x)\n    x = layers.Dropout(0.3)(x)\n    outputs = layers.Dense(n_classes, activation='softmax')(x)\n    model = Model(inputs, outputs)\n\n    # Compile (Same optimizer and loss as previous models)\n    model.compile(\n        optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),\n        loss='sparse_categorical_crossentropy',\n        metrics=['accuracy']\n    )\n    return model\n\n# Build the DenseNet model\nmodel = build_densenet_model()\n\n# Print summary to confirm architecture\n# Note: DenseNet121 usually has around 7-8 Million parameters (lighter than ResNet)\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-27T18:03:14.779242Z","iopub.execute_input":"2025-11-27T18:03:14.779986Z","iopub.status.idle":"2025-11-27T18:03:18.394814Z","shell.execute_reply.started":"2025-11-27T18:03:14.779962Z","shell.execute_reply":"2025-11-27T18:03:18.394272Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ## Cell 9: Define Callbacks for DenseNet\n\n# 1. ModelCheckpoint: Save to a NEW file 'best_model_densenet.h5'\n# We rename this so we don't overwrite the ResNet or EfficientNet models\ncheckpoint = ModelCheckpoint(\n    'best_model_densenet.h5',  \n    monitor='val_accuracy',\n    save_best_only=True,\n    mode='max',\n    verbose=1)\n\n# 2. CSVLogger: Save the training history to a CSV file\n# This allows you to plot the Learning Curves for your paper later\ncsv_logger = CSVLogger('training_log_densenet.csv', separator=',', append=False)\n\n# 3. EarlyStopping (Same patience as previous models for fair comparison)\nearly_stopping = EarlyStopping(\n    monitor='val_accuracy',\n    patience=5, \n    restore_best_weights=True,\n    mode='max',\n    verbose=1)\n\n# 4. ReduceLROnPlateau (Same settings as previous models)\nreduce_lr = ReduceLROnPlateau(\n    monitor='val_loss',\n    factor=0.5,\n    patience=2,\n    verbose=1,\n    min_lr=1e-7)\n\n# Combine all callbacks into a list\ncallbacks_list = [checkpoint, early_stopping, reduce_lr, csv_logger]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-27T18:04:09.854021Z","iopub.execute_input":"2025-11-27T18:04:09.854827Z","iopub.status.idle":"2025-11-27T18:04:09.860160Z","shell.execute_reply.started":"2025-11-27T18:04:09.854802Z","shell.execute_reply":"2025-11-27T18:04:09.859266Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ## Cell 10: Train the DenseNet121 Model\n\n# We store history in 'history_densenet' variable\n# This allows you to compare it against 'history' (EfficientNet) and 'history_resnet' (ResNet)\nhistory_densenet = model.fit(\n    train_gen,\n    validation_data=val_gen,\n    epochs=EPOCHS,\n    class_weight=class_weight_dict,\n    callbacks=callbacks_list,\n    verbose=1\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ## Cell 11: Plot Training History (DenseNet121)\n\ndef plot_history(history, model_name=\"Model\"):\n    acc = history.history['accuracy']\n    val_acc = history.history['val_accuracy']\n    loss = history.history['loss']\n    val_loss = history.history['val_loss']\n    epochs_range = range(len(acc))\n\n    plt.figure(figsize=(12, 4))\n    \n    # Plot Accuracy\n    plt.subplot(1, 2, 1)\n    plt.plot(epochs_range, acc, label='Training Accuracy')\n    plt.plot(epochs_range, val_acc, label='Validation Accuracy')\n    plt.legend(loc='lower right')\n    plt.title(f'{model_name} Accuracy')\n\n    # Plot Loss\n    plt.subplot(1, 2, 2)\n    plt.plot(epochs_range, loss, label='Training Loss')\n    plt.plot(epochs_range, val_loss, label='Validation Loss')\n    plt.legend(loc='upper right')\n    plt.title(f'{model_name} Loss')\n    \n    # Save the plot for your Research Paper\n    plt.savefig(f'{model_name}_training_curves.png')\n    plt.show()\n\n# Plot the history for DenseNet\n# IMPORTANT: Ensure you pass 'history_densenet' here\nif 'history_densenet' in locals():\n    plot_history(history_densenet, model_name=\"DenseNet121\")\nelse:\n    # Fallback: If you restarted the notebook, load from CSV\n    print(\"History variable not found in RAM. Loading from CSV...\")\n    if os.path.exists('training_log_densenet.csv'):\n        history_df = pd.read_csv('training_log_densenet.csv')\n        # Mock a history object structure for the function\n        class HistoryObj:\n            pass\n        mock_history = HistoryObj()\n        mock_history.history = history_df.to_dict(orient='list')\n        plot_history(mock_history, model_name=\"DenseNet121\")\n    else:\n        print(\"No history found.\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ## Cell 12: Evaluate the DenseNet121 Model (Experiment A - Part 3 Results)\n\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nfrom sklearn.metrics import classification_report, confusion_matrix, cohen_kappa_score, fbeta_score\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport numpy as np\nimport os\n\n# -----------------------------------------------------------\n# FIX 1: Define Custom Layer and FIX 2: Automatic Path Search\n# -----------------------------------------------------------\n\n# Define the custom Cast layer (Needed because of Mixed Precision saving)\n@tf.keras.utils.register_keras_serializable()\nclass Cast(tf.keras.layers.Layer):\n    def __init__(self, **kwargs):\n        super(Cast, self).__init__(**kwargs)\n    def call(self, inputs):\n        return inputs\n    def get_config(self):\n        return super(Cast, self).get_config()\n\ncustom_objects_dict = {'Cast': Cast}\nMODEL_FILENAME = 'best_model_densenet.h5' # <--- TARGETS DENSENET MODEL\nMODEL_PATH = None\nmodel_found = False\n\n# Search for the model file in the entire Kaggle file system\nfor root, _, files in os.walk('/kaggle/'):\n    if MODEL_FILENAME in files:\n        MODEL_PATH = os.path.join(root, MODEL_FILENAME)\n        model_found = True\n        break\n\nif model_found:\n    print(f\"Loading model from: {MODEL_PATH}\")\n    try:\n        # Load the ENTIRE model (architecture + weights + custom objects)\n        model = load_model(MODEL_PATH, custom_objects=custom_objects_dict)\n        print(\"✅ Model loaded successfully!\")\n    except Exception as e:\n        print(f\"❌ Critical Error during model loading: {e}\")\n        model = None \nelse:\n    print(f\"❌ File Not Found: Could not locate '{MODEL_FILENAME}'. Ensure you ran the training commit successfully.\")\n    model = None\n\n# -----------------------------------------------------------\n# Evaluation Logic\n# -----------------------------------------------------------\n\nif model:\n    # Make predictions on the validation set\n    # val_gen must be defined by Cell 6 before running this cell!\n    preds = model.predict(val_gen)\n    pred_classes = np.argmax(preds, axis=1)\n\n    # Get true labels directly from the generator\n    true_classes = val_gen.classes\n\n    # Calculate Metrics\n    qwk = cohen_kappa_score(true_classes, pred_classes, weights='quadratic')\n    print(f\"\\n📈 Validation Quadratic Weighted Kappa (QWK): {qwk:.4f}\\n\")\n\n    f2 = fbeta_score(true_classes, pred_classes, beta=2, average='weighted')\n    print(f\"🔬 Weighted F2 Score (Sensitivity Emphasis): {f2:.4f}\\n\")\n\n    # Print Classification Report\n    print(\"📊 Classification Report (DenseNet121):\\n\")\n    print(classification_report(true_classes, pred_classes, target_names=[str(i) for i in classes]))\n\n    # Display Confusion Matrix\n    cm = confusion_matrix(true_classes, pred_classes)\n    plt.figure(figsize=(8, 6))\n    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',\n                xticklabels=classes, yticklabels=classes)\n    plt.title('Confusion Matrix (DenseNet121)')\n    plt.xlabel('Predicted Label')\n    plt.ylabel('True Label')\n    plt.show()\nelse:\n    print(\"\\nEvaluation skipped because the model could not be loaded.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-28T05:36:47.357936Z","iopub.execute_input":"2025-11-28T05:36:47.358997Z","iopub.status.idle":"2025-11-28T05:38:18.680343Z","shell.execute_reply.started":"2025-11-28T05:36:47.358970Z","shell.execute_reply":"2025-11-28T05:38:18.679727Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}