{"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"}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\n\nprint(\"Contents of /kaggle/input:\")\nfor f in os.listdir(\"/kaggle/input\"):\n    print(\" -\", f)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T00:03:50.835140Z","iopub.execute_input":"2025-11-30T00:03:50.835886Z","iopub.status.idle":"2025-11-30T00:03:50.840108Z","shell.execute_reply.started":"2025-11-30T00:03:50.835858Z","shell.execute_reply":"2025-11-30T00:03:50.839352Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\n\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.applications import EfficientNetB0\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Dense, Dropout\n\nroot_dir = \"/kaggle/input/aptos2019-blindness-detection\"\ntrain_img_dir = os.path.join(root_dir, \"train_images\")\ntest_img_dir  = os.path.join(root_dir, \"test_images\")\n\n# Read CSVs\ntrain_df = pd.read_csv(os.path.join(root_dir, \"train.csv\"))\ntest_df  = pd.read_csv(os.path.join(root_dir, \"test.csv\"))\n\nprint(\"Train shape:\", train_df.shape)\nprint(\"Test shape:\", test_df.shape)\ntrain_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T00:03:54.574084Z","iopub.execute_input":"2025-11-30T00:03:54.574636Z","iopub.status.idle":"2025-11-30T00:03:54.589887Z","shell.execute_reply.started":"2025-11-30T00:03:54.574610Z","shell.execute_reply":"2025-11-30T00:03:54.589065Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.preprocessing.image import ImageDataGenerator\n\n# Add file paths\ntrain_df[\"file_path\"] = train_df[\"id_code\"].apply(lambda x: os.path.join(train_img_dir, f\"{x}.png\"))\ntest_df[\"file_path\"]  = test_df[\"id_code\"].apply(lambda x: os.path.join(test_img_dir, f\"{x}.png\"))\n\n# Labels must be string for flow_from_dataframe\ntrain_df[\"diagnosis\"] = train_df[\"diagnosis\"].astype(str)\n\n# Check missing images\nmissing = train_df[~train_df[\"file_path\"].apply(os.path.exists)]\nprint(\"Missing training images:\", len(missing))\n\n# ImageDataGenerator as in template (DO NOT CHANGE)\ntrain_datagen = ImageDataGenerator(\n    preprocessing_function=tf.keras.applications.resnet.preprocess_input,\n    rotation_range=20,\n    width_shift_range=0.2,\n    height_shift_range=0.2,\n    shear_range=0.2,\n    zoom_range=0.2,\n    horizontal_flip=True,\n    fill_mode=\"nearest\",\n    validation_split=0.2\n)\n\n# Train generator\ntrain_generator = train_datagen.flow_from_dataframe(\n    train_df,\n    x_col=\"file_path\",\n    y_col=\"diagnosis\",\n    target_size=(224, 224),\n    batch_size=32,\n    class_mode=\"categorical\",\n    subset=\"training\"\n)\n\n# Validation generator\nval_generator = train_datagen.flow_from_dataframe(\n    train_df,\n    x_col=\"file_path\",\n    y_col=\"diagnosis\",\n    target_size=(224, 224),\n    batch_size=32,\n    class_mode=\"categorical\",\n    subset=\"validation\"\n)\n\n# Test generator\ntest_datagen = ImageDataGenerator(\n    preprocessing_function=tf.keras.applications.resnet.preprocess_input\n)\n\ntest_generator = test_datagen.flow_from_dataframe(\n    test_df,\n    x_col=\"file_path\",\n    y_col=None,\n    target_size=(224, 224),\n    batch_size=32,\n    class_mode=None,\n    shuffle=False\n)\n\nprint(\"Training samples:\", train_generator.samples)\nprint(\"Validation samples:\", val_generator.samples)\nprint(\"Test samples:\", test_generator.samples)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T00:06:41.337749Z","iopub.execute_input":"2025-11-30T00:06:41.338091Z","iopub.status.idle":"2025-11-30T00:06:43.457591Z","shell.execute_reply.started":"2025-11-30T00:06:41.338067Z","shell.execute_reply":"2025-11-30T00:06:43.456704Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Just to *view* images nicely – reverse ResNet preprocess for display only\ndef undo_resnet_preprocess(img):\n    img = img.copy()\n    img = img + [103.939, 116.779, 123.68]   # add mean\n    img = img[..., ::-1]                    # BGR → RGB\n    img = np.clip(img / 255.0, 0, 1)\n    return img\n\ndef show_sample(generator, title):\n    imgs, labels = next(generator)\n    plt.figure(figsize=(12,4))\n    for i in range(3):\n        plt.subplot(1,3,i+1)\n        restored = undo_resnet_preprocess(imgs[i])\n        plt.imshow(restored)\n        plt.title(f\"Label: {np.argmax(labels[i])}\")\n        plt.axis(\"off\")\n    plt.suptitle(title)\n    plt.show()\n\n# Train samples\nshow_sample(train_generator, \"Training Images (Sample)\")\n\n# Test samples (no labels)\ntest_imgs = next(test_generator)\nplt.figure(figsize=(12,4))\nfor i in range(3):\n    plt.subplot(1,3,i+1)\n    restored = undo_resnet_preprocess(test_imgs[i])\n    plt.imshow(restored)\n    plt.axis(\"off\")\nplt.suptitle(\"Test Images (Sample)\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T00:07:22.160889Z","iopub.execute_input":"2025-11-30T00:07:22.161411Z","iopub.status.idle":"2025-11-30T00:07:27.107577Z","shell.execute_reply.started":"2025-11-30T00:07:22.161387Z","shell.execute_reply":"2025-11-30T00:07:27.106687Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.applications import EfficientNetB0\nfrom tensorflow.keras.layers import Dense, Dropout\nfrom tensorflow.keras.models import Model\n\n# Base EfficientNetB0\nbase_model = EfficientNetB0(\n    include_top=False,\n    weights=\"imagenet\",\n    input_shape=(224, 224, 3),\n    pooling=\"avg\"\n)\n\n# Phase 1: freeze the entire backbone\nbase_model.trainable = False\n\n# Simple classification head\nx = base_model.output\nx = Dropout(0.4)(x)\noutputs = Dense(5, activation=\"softmax\")(x)\n\nmodel = Model(inputs=base_model.input, outputs=outputs)\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T00:07:36.176265Z","iopub.execute_input":"2025-11-30T00:07:36.176911Z","iopub.status.idle":"2025-11-30T00:07:37.578993Z","shell.execute_reply.started":"2025-11-30T00:07:36.176886Z","shell.execute_reply":"2025-11-30T00:07:37.578421Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\n\nmodel.compile(\n    optimizer=tf.keras.optimizers.Adam(1e-3),\n    loss=\"categorical_crossentropy\",\n    metrics=[\"accuracy\"]\n)\n\ncallbacks_phase1 = [\n    EarlyStopping(patience=3, restore_best_weights=True, monitor=\"val_loss\"),\n    ReduceLROnPlateau(monitor=\"val_loss\", factor=0.5, patience=2)\n]\n\nprint(\"Phase 1: training classifier head (base frozen)...\")\n\nhistory1 = model.fit(\n    train_generator,\n    validation_data=val_generator,\n    epochs=8,\n    callbacks=callbacks_phase1\n)\n\nprint(\"Phase 1 done.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T00:07:59.001670Z","iopub.execute_input":"2025-11-30T00:07:59.002221Z","iopub.status.idle":"2025-11-30T00:58:09.505746Z","shell.execute_reply.started":"2025-11-30T00:07:59.002196Z","shell.execute_reply":"2025-11-30T00:58:09.504915Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Unfreeze the top 40 layers of the EfficientNet backbone\nfor layer in base_model.layers[-40:]:\n    layer.trainable = True\n\nmodel.compile(\n    optimizer=tf.keras.optimizers.Adam(1e-4),\n    loss=\"categorical_crossentropy\",\n    metrics=[\"accuracy\"]\n)\n\ncallbacks_phase2 = [\n    EarlyStopping(patience=5, restore_best_weights=True, monitor=\"val_loss\"),\n    ReduceLROnPlateau(monitor=\"val_loss\", factor=0.5, patience=2)\n]\n\nprint(\"Phase 2: fine-tuning top EfficientNet layers...\")\n\nhistory2 = model.fit(\n    train_generator,\n    validation_data=val_generator,\n    epochs=20,\n    callbacks=callbacks_phase2\n)\n\nprint(\"Phase 2 done.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T00:59:35.606966Z","iopub.execute_input":"2025-11-30T00:59:35.607787Z","iopub.status.idle":"2025-11-30T03:01:17.319848Z","shell.execute_reply.started":"2025-11-30T00:59:35.607759Z","shell.execute_reply":"2025-11-30T03:01:17.319049Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Save model after training\nmodel.save(\"model.h5\")\nprint(\"Model saved as model.h5\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T04:00:51.537558Z","iopub.execute_input":"2025-11-30T04:00:51.538374Z","iopub.status.idle":"2025-11-30T04:00:51.967478Z","shell.execute_reply.started":"2025-11-30T04:00:51.538348Z","shell.execute_reply":"2025-11-30T04:00:51.966819Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_history(histories, labels):\n    plt.figure(figsize=(14,6))\n\n    # Accuracy\n    plt.subplot(1,2,1)\n    for h, lbl in zip(histories, labels):\n        plt.plot(h.history['accuracy'], label=f\"{lbl} Train\")\n        plt.plot(h.history['val_accuracy'], label=f\"{lbl} Val\", linestyle=\"--\")\n    plt.title(\"Training vs Validation Accuracy\")\n    plt.xlabel(\"Epoch\")\n    plt.ylabel(\"Accuracy\")\n    plt.legend()\n\n    # Loss\n    plt.subplot(1,2,2)\n    for h, lbl in zip(histories, labels):\n        plt.plot(h.history['loss'], label=f\"{lbl} Train\")\n        plt.plot(h.history['val_loss'], label=f\"{lbl} Val\", linestyle=\"--\")\n    plt.title(\"Training vs Validation Loss\")\n    plt.xlabel(\"Epoch\")\n    plt.ylabel(\"Loss\")\n    plt.legend()\n\n    plt.show()\n\nprint(\"Plotting learning curves...\")\nplot_history([history1, history2], [\"Phase 1\", \"Phase 2\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T03:01:40.204493Z","iopub.execute_input":"2025-11-30T03:01:40.204787Z","iopub.status.idle":"2025-11-30T03:01:40.556218Z","shell.execute_reply.started":"2025-11-30T03:01:40.204765Z","shell.execute_reply":"2025-11-30T03:01:40.555479Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_loss, val_acc = model.evaluate(val_generator)\nprint(\"FINAL VALIDATION ACCURACY:\", val_acc)\nprint(\"FINAL VALIDATION LOSS:\", val_loss)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T03:01:53.295175Z","iopub.execute_input":"2025-11-30T03:01:53.295710Z","iopub.status.idle":"2025-11-30T03:03:19.281915Z","shell.execute_reply.started":"2025-11-30T03:01:53.295677Z","shell.execute_reply":"2025-11-30T03:03:19.281150Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Predicting on test set...\")\n\ntest_predictions = model.predict(test_generator)\npredicted_labels = np.argmax(test_predictions, axis=1)\n\nsubmission_df = pd.DataFrame({\n    \"id_code\": test_df[\"id_code\"],\n    \"diagnosis\": predicted_labels\n})\n\nsubmission_df.to_csv(\"submission.csv\", index=False)\n\nprint(\"submission.csv saved!\")\nsubmission_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T03:05:26.863952Z","iopub.execute_input":"2025-11-30T03:05:26.864249Z","iopub.status.idle":"2025-11-30T03:06:25.573518Z","shell.execute_reply.started":"2025-11-30T03:05:26.864227Z","shell.execute_reply":"2025-11-30T03:06:25.572948Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T04:09:20.934604Z","iopub.execute_input":"2025-11-30T04:09:20.934942Z","iopub.status.idle":"2025-11-30T04:09:20.943383Z","shell.execute_reply.started":"2025-11-30T04:09:20.934917Z","shell.execute_reply":"2025-11-30T04:09:20.942807Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"","metadata":{}}]}