{"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":31192,"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\nimport tensorflow as tf\n\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras import layers, models\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\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-30T20:14:34.898051Z","iopub.execute_input":"2025-11-30T20:14:34.898331Z","iopub.status.idle":"2025-11-30T20:14:34.913720Z","shell.execute_reply.started":"2025-11-30T20:14:34.898310Z","shell.execute_reply":"2025-11-30T20:14:34.912994Z"}},"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 as string for classifier generator\ntrain_df[\"diagnosis\"] = train_df[\"diagnosis\"].astype(str)\n\n# Check for missing files\nmissing = train_df[~train_df[\"file_path\"].apply(os.path.exists)]\nprint(\"Missing training images:\", len(missing))\n\n# Base ImageDataGenerator (same style as Q1)\nbase_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# 1) Generators for CLASSIFIER (like Q1)\ntrain_classifier_gen = base_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\nval_classifier_gen = base_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# 2) Generators for AUTOENCODER (input = output image)\ntrain_ae_gen = base_datagen.flow_from_dataframe(\n    train_df,\n    x_col=\"file_path\",\n    y_col=\"file_path\",         # dummy, we ignore labels; just use images as both input/output\n    target_size=(224, 224),\n    batch_size=32,\n    class_mode=\"input\",        # Keras will use images as y\n    subset=\"training\"\n)\n\nval_ae_gen = base_datagen.flow_from_dataframe(\n    train_df,\n    x_col=\"file_path\",\n    y_col=\"file_path\",\n    target_size=(224, 224),\n    batch_size=32,\n    class_mode=\"input\",\n    subset=\"validation\"\n)\n\n# Test generator (for final predictions)\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(\"Train classifier samples:\", train_classifier_gen.samples)\nprint(\"Val classifier samples:\",   val_classifier_gen.samples)\nprint(\"Train AE samples:\",         train_ae_gen.samples)\nprint(\"Val AE samples:\",           val_ae_gen.samples)\nprint(\"Test samples:\",             test_generator.samples)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T20:14:39.596161Z","iopub.execute_input":"2025-11-30T20:14:39.596797Z","iopub.status.idle":"2025-11-30T20:14:42.316188Z","shell.execute_reply.started":"2025-11-30T20:14:39.596771Z","shell.execute_reply":"2025-11-30T20:14:42.315693Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# For visualization\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.axis(\"off\")\n    plt.suptitle(title)\n    plt.show()\n\n# Training images\nshow_sample(train_ae_gen, \"Training Images (Autoencoder)\")\n\n# Test images\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-30T20:15:06.108519Z","iopub.execute_input":"2025-11-30T20:15:06.109252Z","iopub.status.idle":"2025-11-30T20:15:11.456924Z","shell.execute_reply.started":"2025-11-30T20:15:06.109227Z","shell.execute_reply":"2025-11-30T20:15:11.456199Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, UpSampling2D\nfrom tensorflow.keras.models import Model\n\ninput_img = Input(shape=(224, 224, 3))\n\n# ----- Encoder -----\nx = Conv2D(32, (3,3), activation='relu', padding='same')(input_img)\nx = MaxPooling2D((2,2), padding='same')(x)\n\nx = Conv2D(64, (3,3), activation='relu', padding='same')(x)\nx = MaxPooling2D((2,2), padding='same')(x)\n\nx = Conv2D(128, (3,3), activation='relu', padding='same')(x)\nencoded = MaxPooling2D((2,2), padding='same', name=\"encoded_layer\")(x)  # bottleneck\n\n# ----- Decoder -----\nx = UpSampling2D((2,2))(encoded)\nx = Conv2D(128, (3,3), activation='relu', padding='same')(x)\n\nx = UpSampling2D((2,2))(x)\nx = Conv2D(64, (3,3), activation='relu', padding='same')(x)\n\nx = UpSampling2D((2,2))(x)\ndecoded = Conv2D(3, (3,3), activation='sigmoid', padding='same')(x)\n\nautoencoder = Model(input_img, decoded, name=\"autoencoder\")\nautoencoder.compile(optimizer='adam', loss='mse')\nautoencoder.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T20:15:20.731582Z","iopub.execute_input":"2025-11-30T20:15:20.732443Z","iopub.status.idle":"2025-11-30T20:15:22.643839Z","shell.execute_reply.started":"2025-11-30T20:15:20.732412Z","shell.execute_reply":"2025-11-30T20:15:22.643230Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"callbacks_ae = [\n    EarlyStopping(patience=3, restore_best_weights=True, monitor=\"val_loss\"),\n    ReduceLROnPlateau(monitor=\"val_loss\", factor=0.5, patience=2)\n]\n\nprint(\"Training autoencoder...\")\n\nhistory_ae = autoencoder.fit(\n    train_ae_gen,\n    validation_data=val_ae_gen,\n    epochs=10,\n    callbacks=callbacks_ae\n)\n\nprint(\"Autoencoder training complete.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T20:15:28.451219Z","iopub.execute_input":"2025-11-30T20:15:28.451495Z","iopub.status.idle":"2025-11-30T20:54:41.855926Z","shell.execute_reply.started":"2025-11-30T20:15:28.451475Z","shell.execute_reply":"2025-11-30T20:54:41.855272Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Build encoder model: from input to bottleneck \"encoded_layer\"\nencoder = Model(inputs=autoencoder.input,\n                outputs=autoencoder.get_layer(\"encoded_layer\").output,\n                name=\"encoder\")\n\nencoder.summary()\n\n# build classifier on top of encoder\nfrom tensorflow.keras.layers import GlobalAveragePooling2D, Dropout, Dense, Flatten\n\nencoded_output = encoder.output\n\n# Option 1: flatten features\nx = Flatten()(encoded_output)\nx = Dense(256, activation='relu')(x)\nx = Dropout(0.5)(x)\nclassifier_output = Dense(5, activation='softmax')(x)\n\nae_classifier = Model(inputs=encoder.input, outputs=classifier_output, name=\"ae_classifier\")\n\nae_classifier.compile(\n    optimizer=tf.keras.optimizers.Adam(1e-4),\n    loss=\"categorical_crossentropy\",\n    metrics=[\"accuracy\"]\n)\n\nae_classifier.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T21:05:22.844195Z","iopub.execute_input":"2025-11-30T21:05:22.844940Z","iopub.status.idle":"2025-11-30T21:05:22.894854Z","shell.execute_reply.started":"2025-11-30T21:05:22.844915Z","shell.execute_reply":"2025-11-30T21:05:22.894182Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"callbacks_cls = [\n    EarlyStopping(patience=5, restore_best_weights=True, monitor=\"val_loss\"),\n    ReduceLROnPlateau(monitor=\"val_loss\", factor=0.5, patience=2)\n]\n\nprint(\"Training classifier on top of pretrained encoder...\")\n\nhistory_cls = ae_classifier.fit(\n    train_classifier_gen,\n    validation_data=val_classifier_gen,\n    epochs=20,\n    callbacks=callbacks_cls\n)\n\nprint(\"Classifier training complete.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T21:05:28.176104Z","iopub.execute_input":"2025-11-30T21:05:28.176383Z","iopub.status.idle":"2025-11-30T23:09:09.740118Z","shell.execute_reply.started":"2025-11-30T21:05:28.176364Z","shell.execute_reply":"2025-11-30T23:09:09.739242Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_loss_cls, val_acc_cls = ae_classifier.evaluate(val_classifier_gen)\nprint(\"Autoencoder-based classifier validation accuracy:\", val_acc_cls)\nprint(\"Autoencoder-based classifier validation loss:\", val_loss_cls)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T23:14:20.712057Z","iopub.execute_input":"2025-11-30T23:14:20.712627Z","iopub.status.idle":"2025-11-30T23:15:48.325973Z","shell.execute_reply.started":"2025-11-30T23:14:20.712604Z","shell.execute_reply":"2025-11-30T23:15:48.325305Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_history_pair(history_ae, history_cls):\n    plt.figure(figsize=(14,6))\n\n    # Autoencoder loss\n    plt.subplot(1,2,1)\n    plt.plot(history_ae.history[\"loss\"], label=\"AE Train Loss\")\n    plt.plot(history_ae.history[\"val_loss\"], label=\"AE Val Loss\", linestyle=\"--\")\n    plt.title(\"Autoencoder Reconstruction Loss\")\n    plt.xlabel(\"Epoch\")\n    plt.ylabel(\"MSE Loss\")\n    plt.legend()\n\n    # Classifier accuracy\n    plt.subplot(1,2,2)\n    plt.plot(history_cls.history[\"accuracy\"], label=\"Classifier Train Acc\")\n    plt.plot(history_cls.history[\"val_accuracy\"], label=\"Classifier Val Acc\", linestyle=\"--\")\n    plt.title(\"AE-based Classifier Accuracy\")\n    plt.xlabel(\"Epoch\")\n    plt.ylabel(\"Accuracy\")\n    plt.legend()\n\n    plt.show()\n\nprint(\"Plotting autoencoder + classifier learning curves...\")\nplot_history_pair(history_ae, history_cls)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T23:16:01.077218Z","iopub.execute_input":"2025-11-30T23:16:01.077496Z","iopub.status.idle":"2025-11-30T23:16:01.432623Z","shell.execute_reply.started":"2025-11-30T23:16:01.077476Z","shell.execute_reply":"2025-11-30T23:16:01.431761Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Predicting test set with AE-based classifier...\")\n\ntest_preds_cls = ae_classifier.predict(test_generator)\ntest_labels_cls = np.argmax(test_preds_cls, axis=1)\n\nsubmission_ae_df = pd.DataFrame({\n    \"id_code\": test_df[\"id_code\"],\n    \"diagnosis\": test_labels_cls\n})\n\nsubmission_ae_df.to_csv(\"submission_ae.csv\", index=False)\n\nprint(\"submission_ae.csv saved!\")\nsubmission_ae_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T23:16:15.347826Z","iopub.execute_input":"2025-11-30T23:16:15.348123Z","iopub.status.idle":"2025-11-30T23:17:48.296056Z","shell.execute_reply.started":"2025-11-30T23:16:15.348102Z","shell.execute_reply":"2025-11-30T23:17:48.295365Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission_ae_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T23:18:45.750199Z","iopub.execute_input":"2025-11-30T23:18:45.750899Z","iopub.status.idle":"2025-11-30T23:18:45.759310Z","shell.execute_reply.started":"2025-11-30T23:18:45.750876Z","shell.execute_reply":"2025-11-30T23:18:45.758739Z"}},"outputs":[],"execution_count":null}]}