{"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},{"sourceType":"datasetVersion","sourceId":527603,"datasetId":250877,"databundleVersionId":543908}],"dockerImageVersionId":31287,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import tensorflow as tf\nimport pandas as pd\nimport numpy as np\nimport cv2\n\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.applications import DenseNet121, ResNet50, EfficientNetB0\nfrom tensorflow.keras.layers import Dense, Dropout, GlobalAveragePooling2D\nfrom tensorflow.keras.models import Model\n\nfrom sklearn.utils.class_weight import compute_class_weight\nfrom sklearn.metrics import accuracy_score","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-26T17:12:35.103932Z","iopub.execute_input":"2026-04-26T17:12:35.104206Z","iopub.status.idle":"2026-04-26T17:13:19.573188Z","shell.execute_reply.started":"2026-04-26T17:12:35.104184Z","shell.execute_reply":"2026-04-26T17:13:19.572487Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def preprocess_image(img):\n    img = img.astype(np.uint8)\n    lab = cv2.cvtColor(img, cv2.COLOR_RGB2LAB)\n    l, a, b = cv2.split(lab)\n\n    clahe = cv2.createCLAHE(clipLimit=2.0)\n    cl = clahe.apply(l)\n\n    merged = cv2.merge((cl, a, b))\n    img = cv2.cvtColor(merged, cv2.COLOR_LAB2RGB)\n\n    return img / 255.0","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-26T17:13:19.574849Z","iopub.execute_input":"2026-04-26T17:13:19.575322Z","iopub.status.idle":"2026-04-26T17:13:19.581737Z","shell.execute_reply.started":"2026-04-26T17:13:19.575295Z","shell.execute_reply":"2026-04-26T17:13:19.581027Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# APTOS\naptos = pd.read_csv('/kaggle/input/competitions/aptos2019-blindness-detection/train.csv')\naptos['image_path'] = '/kaggle/input/competitions/aptos2019-blindness-detection/train_images/' + aptos['id_code'] + '.png'\naptos['diagnosis'] = aptos['diagnosis'].astype(str)\n\n# EyePACS\neyepacs = pd.read_csv('/kaggle/input/datasets/donkeys/retinopathy-train-2015/trainLabels.csv')\neyepacs = eyepacs.rename(columns={\"level\": \"diagnosis\"})\neyepacs['image_path'] = '/kaggle/input/datasets/donkeys/retinopathy-train-2015/rescaled_train_896/' + eyepacs['image'] + '.jpeg'\neyepacs['diagnosis'] = eyepacs['diagnosis'].astype(str)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-26T17:13:19.582528Z","iopub.execute_input":"2026-04-26T17:13:19.582875Z","iopub.status.idle":"2026-04-26T17:13:19.757225Z","shell.execute_reply.started":"2026-04-26T17:13:19.582830Z","shell.execute_reply":"2026-04-26T17:13:19.756614Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def split(df):\n    df = df.sample(frac=1, random_state=42)\n    n = int(len(df)*0.15)\n    return df[n:], df[:n]\n\naptos_train, aptos_val = split(aptos)\neyepacs_train, eyepacs_val = split(eyepacs)\n\ncombined_train = pd.concat([aptos_train, eyepacs_train]).reset_index(drop=True)\ncombined_val   = pd.concat([aptos_val, eyepacs_val]).reset_index(drop=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-26T17:13:19.758161Z","iopub.execute_input":"2026-04-26T17:13:19.758485Z","iopub.status.idle":"2026-04-26T17:13:19.797244Z","shell.execute_reply.started":"2026-04-26T17:13:19.758452Z","shell.execute_reply":"2026-04-26T17:13:19.796316Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_datagen = ImageDataGenerator(\n    preprocessing_function=preprocess_image,\n    rotation_range=30,\n    zoom_range=0.3,\n    shear_range=0.2,\n    brightness_range=[0.7,1.3],\n    horizontal_flip=True\n)\n\nval_datagen = ImageDataGenerator(preprocessing_function=preprocess_image)\n\ndef make_gen(df, datagen):\n    return datagen.flow_from_dataframe(\n        df,\n        x_col=\"image_path\",\n        y_col=\"diagnosis\",\n        target_size=(256,256),\n        batch_size=32,\n        class_mode=\"categorical\"\n    )\n\ntrain_gen = make_gen(combined_train, train_datagen)\nval_gen   = make_gen(combined_val, val_datagen)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-26T17:13:19.798347Z","iopub.execute_input":"2026-04-26T17:13:19.798986Z","iopub.status.idle":"2026-04-26T17:13:54.652756Z","shell.execute_reply.started":"2026-04-26T17:13:19.798957Z","shell.execute_reply":"2026-04-26T17:13:54.651949Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"classes = combined_train['diagnosis'].astype(int)\n\nclass_weights = compute_class_weight(\n    class_weight='balanced',\n    classes=np.unique(classes),\n    y=classes\n)\n\nclass_weights = dict(enumerate(class_weights))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-26T17:13:54.653880Z","iopub.execute_input":"2026-04-26T17:13:54.654231Z","iopub.status.idle":"2026-04-26T17:13:54.681471Z","shell.execute_reply.started":"2026-04-26T17:13:54.654204Z","shell.execute_reply":"2026-04-26T17:13:54.680913Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def create_densenet():\n    base = DenseNet121(weights='imagenet', include_top=False, input_shape=(256,256,3))\n\n    for layer in base.layers[:-80]:\n        layer.trainable = False\n    for layer in base.layers[-80:]:\n        layer.trainable = True\n\n    x = GlobalAveragePooling2D()(base.output)\n    x = Dense(256, activation='relu')(x)\n    x = Dropout(0.5)(x)\n    out = Dense(5, activation='softmax')(x)\n\n    model = Model(base.input, out)\n    model.compile(optimizer=tf.keras.optimizers.Adam(1e-4),\n                  loss='categorical_crossentropy',\n                  metrics=['accuracy'])\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-26T17:13:54.683164Z","iopub.execute_input":"2026-04-26T17:13:54.683971Z","iopub.status.idle":"2026-04-26T17:13:54.689650Z","shell.execute_reply.started":"2026-04-26T17:13:54.683944Z","shell.execute_reply":"2026-04-26T17:13:54.689038Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def create_effnet():\n    base = EfficientNetB0(weights='imagenet', include_top=False, input_shape=(256,256,3))\n\n    for layer in base.layers[:-80]:\n        layer.trainable = False\n    for layer in base.layers[-80:]:\n        layer.trainable = True\n\n    x = GlobalAveragePooling2D()(base.output)\n    x = Dense(128, activation='relu')(x)\n    x = Dropout(0.5)(x)\n    out = Dense(5, activation='softmax')(x)\n\n    model = Model(base.input, out)\n    model.compile(optimizer=tf.keras.optimizers.Adam(1e-4),\n                  loss='categorical_crossentropy',\n                  metrics=['accuracy'])\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-26T17:13:54.690500Z","iopub.execute_input":"2026-04-26T17:13:54.690811Z","iopub.status.idle":"2026-04-26T17:13:54.706152Z","shell.execute_reply.started":"2026-04-26T17:13:54.690784Z","shell.execute_reply":"2026-04-26T17:13:54.705603Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def create_resnet():\n    base = ResNet50(weights='imagenet', include_top=False, input_shape=(256,256,3))\n\n    for layer in base.layers[:-80]:\n        layer.trainable = False\n    for layer in base.layers[-80:]:\n        layer.trainable = True\n\n    x = GlobalAveragePooling2D()(base.output)\n    x = Dense(128, activation='relu')(x)\n    x = Dropout(0.5)(x)\n    out = Dense(5, activation='softmax')(x)\n\n    model = Model(base.input, out)\n    model.compile(optimizer=tf.keras.optimizers.Adam(1e-4),\n                  loss='categorical_crossentropy',\n                  metrics=['accuracy'])\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-26T17:13:54.707039Z","iopub.execute_input":"2026-04-26T17:13:54.707522Z","iopub.status.idle":"2026-04-26T17:13:54.727412Z","shell.execute_reply.started":"2026-04-26T17:13:54.707500Z","shell.execute_reply":"2026-04-26T17:13:54.726739Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.callbacks import EarlyStopping\n\nearly_stop = EarlyStopping(patience=3, restore_best_weights=True)\n\ndense_model = create_densenet()\ndense_model.fit(train_gen, validation_data=val_gen,\n                epochs=15, class_weight=class_weights,\n                callbacks=[early_stop])\n\neffnet_model = create_effnet()\neffnet_model.fit(train_gen, validation_data=val_gen,\n                 epochs=15, class_weight=class_weights,\n                 callbacks=[early_stop])\n\nresnet_model = create_resnet()\nresnet_model.fit(train_gen, validation_data=val_gen,\n                 epochs=15, class_weight=class_weights,\n                 callbacks=[early_stop])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-26T17:13:54.728495Z","iopub.execute_input":"2026-04-26T17:13:54.728816Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def tta_predict(model, gen, n=5):\n    preds = []\n    for _ in range(n):\n        preds.append(model.predict(gen))\n    return np.mean(preds, axis=0)\n\npred_dense  = tta_predict(dense_model, val_gen)\npred_effnet = tta_predict(effnet_model, val_gen)\npred_resnet = tta_predict(resnet_model, val_gen)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dense_acc  = dense_model.evaluate(val_gen, verbose=0)[1]\neffnet_acc = effnet_model.evaluate(val_gen, verbose=0)[1]\nresnet_acc = resnet_model.evaluate(val_gen, verbose=0)[1]\n\ntotal = dense_acc + effnet_acc + resnet_acc\n\nw1 = dense_acc / total\nw2 = effnet_acc / total\nw3 = resnet_acc / total\n\nfinal_pred = (w1 * pred_dense +\n              w2 * pred_effnet +\n              w3 * pred_resnet)\n\ny_true = val_gen.classes\ny_pred = np.argmax(final_pred, axis=1)\n\nprint(\"🔥 FINAL ACCURACY:\", accuracy_score(y_true, y_pred))","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}