{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":14774,"databundleVersionId":875431}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import (classification_report, confusion_matrix,\n                              cohen_kappa_score, ConfusionMatrixDisplay)\nfrom sklearn.utils.class_weight import compute_class_weight\n\nimport tensorflow as tf\nfrom tensorflow.keras import layers, models\nfrom tensorflow.keras.applications import ResNet50\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\n\n# Configuration\nTRAIN_CSV = \"/kaggle/input/competitions/aptos2019-blindness-detection/train.csv\"\nTRAIN_DIR = \"/kaggle/input/competitions/aptos2019-blindness-detection/train_images/\"\nIMG_SIZE    = 224\nBATCH_SIZE  = 32\nEPOCHS      = 30\nNUM_CLASSES = 5\n\nLABELS = {\n    0: \"No Diabetic Retinopathy\",\n    1: \"Mild DR\",\n    2: \"Moderate DR\",\n    3: \"Severe DR\",\n    4: \"Proliferative DR\"\n}\n\n# Load Data\ndf = pd.read_csv(TRAIN_CSV)\ndf[\"filename\"] = df[\"id_code\"].apply(\n    lambda x: os.path.join(TRAIN_DIR, x + \".png\"))\ndf[\"diagnosis\"] = df[\"diagnosis\"].astype(str)\nprint(f\"Total images: {len(df)}\")\nprint(df[\"diagnosis\"].value_counts().sort_index())\n\n# Split Data\ntrain_df, val_df = train_test_split(\n    df, test_size=0.2, random_state=42,\n    stratify=df[\"diagnosis\"]\n)\nprint(f\"Training: {len(train_df)} | Validation: {len(val_df)}\")\n\n# Compute class weights\nclasses = np.array(sorted(df[\"diagnosis\"].astype(int).unique()))\nclass_weights_array = compute_class_weight(\n    class_weight=\"balanced\",\n    classes=classes,\n    y=train_df[\"diagnosis\"].astype(int).values\n)\nclass_weights = dict(zip(classes, class_weights_array))\nprint(\"\\nClass weights:\")\nfor k, v in class_weights.items():\n    print(f\"  Class {k} ({LABELS[k]}): {v:.3f}\")\n\n# Augmentation\ntrain_datagen = ImageDataGenerator(\n    rescale=1.0/255,\n    rotation_range=15,\n    zoom_range=0.1,\n    horizontal_flip=True,\n    vertical_flip=True\n)\nval_datagen = ImageDataGenerator(rescale=1.0/255)\n\ntrain_gen = train_datagen.flow_from_dataframe(\n    train_df, x_col=\"filename\", y_col=\"diagnosis\",\n    target_size=(IMG_SIZE, IMG_SIZE),\n    batch_size=BATCH_SIZE,\n    class_mode=\"sparse\", shuffle=True\n)\nval_gen = val_datagen.flow_from_dataframe(\n    val_df, x_col=\"filename\", y_col=\"diagnosis\",\n    target_size=(IMG_SIZE, IMG_SIZE),\n    batch_size=BATCH_SIZE,\n    class_mode=\"sparse\", shuffle=False\n)\n\n# Build Model\nbase_model = ResNet50(\n    weights=\"imagenet\",\n    include_top=False,\n    input_shape=(IMG_SIZE, IMG_SIZE, 3)\n)\n\nfor layer in base_model.layers[:-20]:\n    layer.trainable = False\nfor layer in base_model.layers[-20:]:\n    layer.trainable = True\n\ninputs = tf.keras.Input(shape=(IMG_SIZE, IMG_SIZE, 3))\nx = base_model(inputs, training=True)\nx = layers.GlobalAveragePooling2D()(x)\nx = layers.Dense(256, activation=\"relu\")(x)\nx = layers.Dropout(0.5)(x)\noutputs = layers.Dense(NUM_CLASSES, activation=\"softmax\")(x)\n\nmodel = models.Model(inputs, outputs)\nmodel.compile(\n    optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),\n    loss=\"sparse_categorical_crossentropy\",\n    metrics=[\"accuracy\"]\n)\nmodel.summary()\n\n# Train\nprint(\"\\n--- Training ---\")\ncallbacks = [\n    EarlyStopping(\n        patience=7,\n        restore_best_weights=True,\n        verbose=1,\n        monitor=\"val_accuracy\"\n    ),\n    ReduceLROnPlateau(\n        factor=0.5,\n        patience=3,\n        verbose=1,\n        min_lr=1e-7,\n        monitor=\"val_accuracy\"\n    )\n]\n\nhistory = model.fit(\n    train_gen,\n    validation_data=val_gen,\n    epochs=EPOCHS,\n    callbacks=callbacks,\n    class_weight=class_weights,\n    verbose=1\n)\n\n# Evaluate\nprint(\"\\n--- Evaluating ---\")\nval_gen.reset()\npreds = model.predict(val_gen, verbose=1)\ny_pred = np.argmax(preds, axis=1)\ny_true = val_gen.classes\n\nacc = np.mean(y_pred == y_true)\nkappa = cohen_kappa_score(y_true, y_pred, weights=\"quadratic\")\nprint(f\"\\n✅ Validation Accuracy  : {acc:.4f} ({acc*100:.2f}%)\")\nprint(f\"✅ Quadratic Kappa Score: {kappa:.4f}\")\nprint(\"\\n📋 Classification Report:\")\nprint(classification_report(\n    y_true, y_pred,\n    target_names=list(LABELS.values())\n))\n\n# Confusion Matrix\ncm = confusion_matrix(y_true, y_pred)\ndisp = ConfusionMatrixDisplay(\n    cm, display_labels=list(LABELS.values())\n)\nfig, ax = plt.subplots(figsize=(10, 8))\ndisp.plot(ax=ax, xticks_rotation=30, colorbar=False)\nplt.title(\"Confusion Matrix — Eye Disease Detection\")\nplt.tight_layout()\nplt.savefig(\"confusion_matrix.png\", dpi=150)\nplt.show()\n\n# Training Curves\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))\nax1.plot(history.history[\"accuracy\"], label=\"Train\")\nax1.plot(history.history[\"val_accuracy\"], label=\"Validation\")\nax1.set_title(\"Accuracy over Epochs\")\nax1.set_xlabel(\"Epoch\")\nax1.set_ylabel(\"Accuracy\")\nax1.legend()\n\nax2.plot(history.history[\"loss\"], label=\"Train\")\nax2.plot(history.history[\"val_loss\"], label=\"Validation\")\nax2.set_title(\"Loss over Epochs\")\nax2.set_xlabel(\"Epoch\")\nax2.set_ylabel(\"Loss\")\nax2.legend()\n\nplt.tight_layout()\nplt.savefig(\"training_curves.png\", dpi=150)\nplt.show()\n\n# Save model\nmodel.save(\"eye_disease_model.h5\")\nprint(\"\\n✅ Model saved successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T16:06:09.688262Z","iopub.execute_input":"2026-04-21T16:06:09.689052Z","iopub.status.idle":"2026-04-21T19:16:52.624528Z","shell.execute_reply.started":"2026-04-21T16:06:09.689018Z","shell.execute_reply":"2026-04-21T19:16:52.623807Z"}},"outputs":[],"execution_count":null}]}