{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":14774,"databundleVersionId":875431,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport cv2\nimport tensorflow as tf\nimport tensorflow_hub as hub\nfrom tensorflow.keras import layers, models, optimizers, callbacks\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import accuracy_score, roc_auc_score, f1_score, cohen_kappa_score, confusion_matrix\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\n# Set random seeds for reproducibility\ntf.random.set_seed(42)\nnp.random.seed(42)\n\n# Step 1: Problem Definition\nNUM_CLASSES = 2  # Binary: No DR (0), DR (1)\nCLASS_NAMES = ['No DR', 'DR']\n\n# Step 2: Dataset Understanding & Preprocessing\ndef load_dataset(csv_path, image_dir):\n    \"\"\"Load dataset from CSV and image directory.\"\"\"\n    df = pd.read_csv(csv_path)\n    df['image_path'] = df['id_code'].apply(lambda x: os.path.join(image_dir, f\"{x}.png\"))\n    # Convert to binary labels: 0 (No DR), 1 (DR)\n    df['diagnosis'] = df['diagnosis'].apply(lambda x: 0 if x == 0 else 1).astype(str)\n    return df\n\ndef preprocess_image(image, target_size=(224, 224)):\n    \"\"\"\n    Preprocess image: crop black borders, resize, normalize.\n    \n    Args:\n        image (np.array): Input image (BGR format from cv2.imread or float32 from ImageDataGenerator).\n        target_size (tuple): Target size for resizing.\n    \n    Returns:\n        image (np.array): Preprocessed RGB image normalized to [0, 1].\n    \"\"\"\n    # Ensure image is uint8 for contour detection\n    if image.dtype == np.float32 or image.max() <= 1.0:\n        image = (image * 255).astype(np.uint8)\n    \n    # Crop black borders (Ben Graham's preprocessing)\n    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n    _, thresh = cv2.threshold(gray, 10, 255, cv2.THRESH_BINARY)\n    contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n    if contours:\n        cnt = max(contours, key=cv2.contourArea)\n        x, y, w, h = cv2.boundingRect(cnt)\n        image = image[y:y+h, x:x+w]\n    \n    # Resize and normalize\n    image = cv2.resize(image, target_size)\n    return image / 255.0\n\n# Step 3: Data Augmentation and Generator\ndef create_data_generator(df, batch_size=16, augment=False, target_size=(224, 224)):\n    \"\"\"Create data generator for efficient loading.\"\"\"\n    datagen = ImageDataGenerator(\n        rotation_range=15 if augment else 0,\n        zoom_range=0.2 if augment else 0,\n        width_shift_range=0.1 if augment else 0,\n        height_shift_range=0.1 if augment else 0,\n        horizontal_flip=True if augment else False,\n        brightness_range=[0.8, 1.2] if augment else None,\n        preprocessing_function=preprocess_image\n    )\n    return datagen.flow_from_dataframe(\n        df,\n        x_col='image_path',\n        y_col='diagnosis',\n        target_size=target_size,\n        batch_size=batch_size,\n        class_mode='categorical',\n        shuffle=augment\n    )\n\n# Load dataset\ncsv_path = '/kaggle/input/aptos2019-blindness-detection/train.csv'\nimage_dir = '/kaggle/input/aptos2019-blindness-detection/train_images'\ndf = load_dataset(csv_path, image_dir)\n\n# Debugging Tip 1: Check dataset\nprint(\"Dataset Head:\")\nprint(df.head())\nprint(f\"Total images: {len(df)}\")\nprint(\"Label distribution:\", df['diagnosis'].value_counts())\n\n# Split dataset\ntrain_df, test_df = train_test_split(df, test_size=0.2, random_state=42, stratify=df['diagnosis'])\ntrain_df, val_df = train_test_split(train_df, test_size=0.2, random_state=42, stratify=train_df['diagnosis'])\n\n# Create data generators\ntrain_generator = create_data_generator(train_df, augment=True)\nval_generator = create_data_generator(val_df, augment=False)\ntest_generator = create_data_generator(test_df, augment=False)\n\n# Debugging Tip 2: Check generators\nprint(f\"Training samples: {train_generator.n}\")\nprint(f\"Validation samples: {val_generator.n}\")\nprint(f\"Test samples: {test_generator.n}\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Step 4: Model Selection & Training (Pretrained ViT)\nclass ViTModel(tf.keras.Model):\n    def __init__(self, num_classes=2):\n        super(ViTModel, self).__init__()\n        self.vit_layer = hub.KerasLayer(\"https://tfhub.dev/sayakpaul/vit_b16_fe/1\", trainable=True)\n        self.dense1 = layers.Dense(256, activation='relu', kernel_regularizer=tf.keras.regularizers.l2(0.01))\n        self.dropout = layers.Dropout(0.5)\n        self.dense2 = layers.Dense(num_classes, activation='softmax')\n\n    def call(self, inputs, training=False):\n        x = self.vit_layer(inputs, training=training)\n        x = self.dense1(x)\n        x = self.dropout(x, training=training)\n        return self.dense2(x)\n\n# Build and compile the model\nmodel = ViTModel(num_classes=NUM_CLASSES)\nmodel.compile(\n    optimizer=optimizers.Adam(learning_rate=1e-4),\n    loss='categorical_crossentropy',\n    metrics=['accuracy', tf.keras.metrics.AUC(name='auc')]\n)\nmodel.build(input_shape=(None, 224, 224, 3))\nmodel.summary()\n\n# Step 5: Train the Model with Class Weighting\nfrom sklearn.utils.class_weight import compute_class_weight\nclass_weights = compute_class_weight('balanced', classes=np.unique(train_df['diagnosis'].astype(int)), y=train_df['diagnosis'].astype(int))\nclass_weights = dict(enumerate(class_weights))\nprint(\"Class weights:\", class_weights)\n\nhistory = model.fit(\n    train_generator,\n    validation_data=val_generator,\n    epochs=20,\n    class_weight=class_weights,\n    callbacks=[\n        callbacks.EarlyStopping(patience=7, restore_best_weights=True),\n        callbacks.ModelCheckpoint('vit_model_binary.keras', save_best_only=True),\n        callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, min_lr=1e-6)\n    ]\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Step 6: Model Evaluation & Comparison\n# Generate predictions\ny_pred = model.predict(test_generator)\ny_pred_classes = np.argmax(y_pred, axis=1)\ny_true_classes = test_generator.labels  # Corrected to use generator labels\n\n# Metrics\naccuracy = accuracy_score(y_true_classes, y_pred_classes)\nauc = roc_auc_score(y_true_classes, y_pred[:, 1])\nf1 = f1_score(y_true_classes, y_pred_classes)\nqwk = cohen_kappa_score(y_true_classes, y_pred_classes, weights='quadratic')\n\nprint(f\"Accuracy: {accuracy}\")\nprint(f\"AUC-ROC: {auc}\")\nprint(f\"F1-score: {f1}\")\nprint(f\"Quadratic Weighted Kappa: {qwk}\")\n\n# Confusion Matrix\ncm = confusion_matrix(y_true_classes, y_pred_classes)\nplt.figure(figsize=(8, 6))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=CLASS_NAMES, yticklabels=CLASS_NAMES)\nplt.xlabel('Predicted')\nplt.ylabel('True')\nplt.title('Confusion Matrix (Binary Classification)')\nplt.tight_layout()\nplt.savefig('confusion_matrix_binary.png', dpi=300)\nplt.show()\n\n# Step 7: Deployment & Inference\nmodel.save('binary_vit_model.keras')\n\n# Accuracy Plot (Actual Data)\nplt.figure(figsize=(12, 7))\nplt.plot(history.history['accuracy'], label='Training Accuracy', color='#2ecc71', linewidth=2.5, marker='o', markersize=4)\nplt.plot(history.history['val_accuracy'], label='Validation Accuracy', color='#e74c3c', linewidth=2.5, marker='s', markersize=4)\nplt.axhspan(0.85, 0.90, facecolor='#3498db', alpha=0.2, label='Expected Test Accuracy (85–90%)')\nplt.title('Training and Validation Accuracy for Pretrained ViT Model (Binary Classification, APTOS 2019)', fontsize=16, pad=15)\nplt.xlabel('Epoch', fontsize=14)\nplt.ylabel('Accuracy', fontsize=14)\nplt.ylim(0, 1)\nplt.grid(True, linestyle='--', alpha=0.7)\nplt.legend(fontsize=12, loc='lower right')\nplt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{int(x*100)}%'))\nplt.tick_params(axis='both', which='major', labelsize=12)\nplt.tight_layout()\nplt.savefig('vit_accuracy_binary.png', dpi=300, bbox_inches='tight')\nplt.show()\n\n# Simulated Accuracy Plot (Actual ~50% vs. Expected ~88%)\nepochs_sim = np.arange(1, 21)  # Assume early stopping at 20 epochs\ntrain_accuracy_sim = [\n    0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.73, 0.75, 0.77,\n    0.79, 0.80, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89\n]\nval_accuracy_sim = [\n    0.35, 0.37, 0.39, 0.41, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48,\n    0.48, 0.49, 0.49, 0.50, 0.50, 0.50, 0.50, 0.50, 0.50, 0.50\n]\nepochs_exp = np.arange(1, 31)\ntrain_accuracy_exp = [\n    0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.74, 0.77, 0.80,\n    0.82, 0.84, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.90, 0.91,\n    0.92, 0.92, 0.93, 0.93, 0.94, 0.94, 0.95, 0.95, 0.95, 0.95\n]\nval_accuracy_exp = [\n    0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.64, 0.67, 0.70, 0.73,\n    0.75, 0.77, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86,\n    0.86, 0.87, 0.87, 0.88, 0.88, 0.88, 0.89, 0.89, 0.89, 0.89\n]\n\nplt.figure(figsize=(12, 7))\nplt.plot(epochs_sim, train_accuracy_sim, label='Training Accuracy (Actual, Simulated)', color='#2ecc71', linewidth=2.5, marker='o', markersize=4)\nplt.plot(epochs_sim, val_accuracy_sim, label='Validation Accuracy (Actual, ~50%)', color='#e74c3c', linewidth=2.5, marker='s', markersize=4)\nplt.plot(epochs_exp, train_accuracy_exp, label='Training Accuracy (Expected)', color='#27ae60', linestyle='--', linewidth=2)\nplt.plot(epochs_exp, val_accuracy_exp, label='Validation Accuracy (Expected)', color='#c0392b', linestyle='--', linewidth=2)\nplt.axhspan(0.85, 0.90, facecolor='#3498db', alpha=0.2, label='Expected Test Accuracy (85–90%)')\nplt.title('Actual vs. Expected Accuracy for Pretrained ViT Model (Binary Classification, APTOS 2019)', fontsize=16, pad=15)\nplt.xlabel('Epoch', fontsize=14)\nplt.ylabel('Accuracy', fontsize=14)\nplt.ylim(0, 1)\nplt.grid(True, linestyle='--', alpha=0.7)\nplt.legend(fontsize=12, loc='lower right')\nplt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{int(x*100)}%'))\nplt.tick_params(axis='both', which='major', labelsize=12)\nplt.tight_layout()\nplt.savefig('vit_accuracy_comparison_binary.png', dpi=300, bbox_inches='tight')\nplt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}