{"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":3175,"databundleVersionId":44352},{"sourceType":"datasetVersion","sourceId":15890446,"datasetId":10189043,"databundleVersionId":16844725},{"sourceType":"datasetVersion","sourceId":15890480,"datasetId":10189070,"databundleVersionId":16844761},{"sourceType":"datasetVersion","sourceId":15956928,"datasetId":10233317,"databundleVersionId":16916334},{"sourceType":"datasetVersion","sourceId":15890336,"datasetId":10188977,"databundleVersionId":16844609},{"sourceType":"datasetVersion","sourceId":15890358,"datasetId":10188984,"databundleVersionId":16844632},{"sourceType":"datasetVersion","sourceId":15890414,"datasetId":10189017,"databundleVersionId":16844691},{"sourceType":"datasetVersion","sourceId":15890505,"datasetId":10189092,"databundleVersionId":16844789},{"sourceType":"datasetVersion","sourceId":15889774,"datasetId":10188570,"databundleVersionId":16844020},{"sourceType":"datasetVersion","sourceId":15889799,"datasetId":10188588,"databundleVersionId":16844048},{"sourceType":"datasetVersion","sourceId":15909195,"datasetId":10202123,"databundleVersionId":16864940}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nimport cv2\nfrom tqdm import tqdm","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-04-26T12:26:10.267707Z","iopub.execute_input":"2026-04-26T12:26:10.268399Z","iopub.status.idle":"2026-04-26T12:26:10.272442Z","shell.execute_reply.started":"2026-04-26T12:26:10.268367Z","shell.execute_reply":"2026-04-26T12:26:10.271582Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import zipfile\nimport os\n\nzip_path = \"/kaggle/input/competitions/galaxy-zoo-the-galaxy-challenge/training_solutions_rev1.zip\"\nextract_path = \"/kaggle/working/label\"\n\nwith zipfile.ZipFile(zip_path, 'r') as zip_ref:\n    zip_ref.extractall(extract_path)\n\nprint(\"Done extracting!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-22T17:23:52.541497Z","iopub.execute_input":"2026-04-22T17:23:52.542387Z","iopub.status.idle":"2026-04-22T17:23:52.788910Z","shell.execute_reply.started":"2026-04-22T17:23:52.542325Z","shell.execute_reply":"2026-04-22T17:23:52.788169Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras import layers, models","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-26T12:26:13.534727Z","iopub.execute_input":"2026-04-26T12:26:13.535601Z","iopub.status.idle":"2026-04-26T12:26:13.540066Z","shell.execute_reply.started":"2026-04-26T12:26:13.535569Z","shell.execute_reply":"2026-04-26T12:26:13.539183Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\ndf = pd.read_csv(\"/kaggle/working/label/training_solutions_rev1.csv\")\nprint(df.shape)\ndf.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-27T15:05:43.798332Z","iopub.execute_input":"2026-04-27T15:05:43.798855Z","iopub.status.idle":"2026-04-27T15:05:44.089629Z","shell.execute_reply.started":"2026-04-27T15:05:43.798808Z","shell.execute_reply":"2026-04-27T15:05:44.088793Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df['GalaxyID'] = df['GalaxyID'].astype(str) + \".jpg\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-23T04:15:35.071877Z","iopub.execute_input":"2026-04-23T04:15:35.072233Z","iopub.status.idle":"2026-04-23T04:15:35.085779Z","shell.execute_reply.started":"2026-04-23T04:15:35.072201Z","shell.execute_reply":"2026-04-23T04:15:35.084422Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df['label'] = df['label'].astype(str)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-23T04:16:41.864662Z","iopub.execute_input":"2026-04-23T04:16:41.865599Z","iopub.status.idle":"2026-04-23T04:16:41.873462Z","shell.execute_reply.started":"2026-04-23T04:16:41.865557Z","shell.execute_reply":"2026-04-23T04:16:41.872251Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_label(row):\n    if row['Class1.1'] > 0.5:\n        return 0\n    elif row['Class1.2'] > 0.5:\n        return 1\n    else:\n        return 2\n\ndf['label'] = df.apply(get_label, axis=1)\n\n# FIX HERE\ndf['label'] = df['label'].astype(str)\n\n# Also fix image names\ndf['GalaxyID'] = df['GalaxyID'].astype(str) + \".jpg\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T13:33:26.208472Z","iopub.execute_input":"2026-04-30T13:33:26.209231Z","iopub.status.idle":"2026-04-30T13:33:26.219767Z","shell.execute_reply.started":"2026-04-30T13:33:26.209194Z","shell.execute_reply":"2026-04-30T13:33:26.218478Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(df['label'].value_counts(normalize=True))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-26T13:45:13.313272Z","iopub.execute_input":"2026-04-26T13:45:13.314255Z","iopub.status.idle":"2026-04-26T13:45:13.333166Z","shell.execute_reply.started":"2026-04-26T13:45:13.314179Z","shell.execute_reply":"2026-04-26T13:45:13.332141Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df['label'].head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-26T12:26:23.075063Z","iopub.execute_input":"2026-04-26T12:26:23.075860Z","iopub.status.idle":"2026-04-26T12:26:23.082844Z","shell.execute_reply.started":"2026-04-26T12:26:23.075829Z","shell.execute_reply":"2026-04-26T12:26:23.082110Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"test_path = os.path.join(image_folder, \"100037.jpg\")\nprint(test_path)\nprint(os.path.exists(test_path))","metadata":{"execution":{"iopub.status.busy":"2026-04-20T20:42:08.803653Z","iopub.execute_input":"2026-04-20T20:42:08.804700Z","iopub.status.idle":"2026-04-20T20:42:08.810912Z","shell.execute_reply.started":"2026-04-20T20:42:08.804635Z","shell.execute_reply":"2026-04-20T20:42:08.809942Z"}}},{"cell_type":"code","source":"df['GalaxyID'] = df['GalaxyID'].astype(str).str.strip()\n\n# Remove .jpg if already exists (safe fix)\ndf['GalaxyID'] = df['GalaxyID'].str.replace('.jpg', '', regex=False)\n\n# Add .jpg cleanly\ndf['GalaxyID'] = df['GalaxyID'] + '.jpg'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-27T15:05:51.844722Z","iopub.execute_input":"2026-04-27T15:05:51.845269Z","iopub.status.idle":"2026-04-27T15:05:51.886173Z","shell.execute_reply.started":"2026-04-27T15:05:51.845242Z","shell.execute_reply":"2026-04-27T15:05:51.885527Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\n\ndatagen = tf.keras.preprocessing.image.ImageDataGenerator(\n    rescale=1./255,\n    validation_split=0.2\n)\n\ntrain_gen = datagen.flow_from_dataframe(\n    df,\n    directory='/kaggle/working/images_training/images_training_rev1',\n    x_col='GalaxyID',\n    y_col='label',\n    target_size=(128,128),\n    batch_size=32,\n    class_mode='sparse',\n    subset='training'\n)\n\nval_gen = datagen.flow_from_dataframe(\n    df,\n    directory='/kaggle/working/images_training/images_training_rev1',\n    x_col='GalaxyID',\n    y_col='label',\n    target_size=(128,128),\n    batch_size=32,\n    class_mode='sparse',\n    subset='validation'\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-27T15:05:54.656257Z","iopub.execute_input":"2026-04-27T15:05:54.657137Z","iopub.status.idle":"2026-04-27T15:05:55.633958Z","shell.execute_reply.started":"2026-04-27T15:05:54.657103Z","shell.execute_reply":"2026-04-27T15:05:55.633076Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"base_model = tf.keras.applications.ResNet50(\n    weights='imagenet',\n    include_top=False,\n    input_shape=(128,128,3)\n)\n\nbase_model.trainable = True\n\nx = base_model.output\nx = tf.keras.layers.GlobalAveragePooling2D()(x)\nx = tf.keras.layers.Dense(128, activation='relu')(x)\nx = tf.keras.layers.Dropout(0.5)(x)\n\noutputs = tf.keras.layers.Dense(3, activation='softmax')(x)\n\nmodel = tf.keras.models.Model(inputs=base_model.input, outputs=outputs)\n\nmodel.compile(\n    optimizer=tf.keras.optimizers.Adam(learning_rate=1e-5),\n    loss='sparse_categorical_crossentropy',\n    metrics=['accuracy']\n)\n\nmodel.fit(train_gen, validation_data=val_gen, epochs=10)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.save(\"/kaggle/working/galaxy_model.h5\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-23T04:41:41.250801Z","iopub.execute_input":"2026-04-23T04:41:41.251555Z","iopub.status.idle":"2026-04-23T04:41:41.729820Z","shell.execute_reply.started":"2026-04-23T04:41:41.251479Z","shell.execute_reply":"2026-04-23T04:41:41.729192Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"loss, acc = model.evaluate(val_gen)\nprint(\"Validation Accuracy:\", acc)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-27T15:06:00.935160Z","iopub.execute_input":"2026-04-27T15:06:00.936028Z","iopub.status.idle":"2026-04-27T15:06:00.941895Z","shell.execute_reply.started":"2026-04-27T15:06:00.935989Z","shell.execute_reply":"2026-04-27T15:06:00.940872Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom tensorflow.keras.models import load_model\n\nclass_names = [\"Elliptical\", \"Spiral\", \"Irregular\"]\n\nmodel = load_model(\"/kaggle/working/galaxy_model.h5\")\nimg_path = \"/kaggle/input/datasets/ushnardraghosh/spiral/Screenshot 2026-04-23 121356.png\"  # your image\n\nimg = cv2.imread(img_path)\n\n# Step 1: center crop (important)\nh, w, _ = img.shape\nmin_dim = min(h, w)\nimg = img[(h-min_dim)//2:(h+min_dim)//2,\n          (w-min_dim)//2:(w+min_dim)//2]\n\n# Step 2: resize\nimg = cv2.resize(img, (128,128))\n\n# Step 3: normalize\nimg = img / 255.0\npred = model.predict(np.expand_dims(img, axis=0))\nclass_id = np.argmax(pred)\nconfidence = np.max(pred)\n\nplt.imshow(img)\nplt.title(f\"{class_names[class_id]} ({confidence:.2f})\")\nplt.axis('off')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T15:52:23.114908Z","iopub.execute_input":"2026-04-30T15:52:23.115683Z","iopub.status.idle":"2026-04-30T15:52:30.608484Z","shell.execute_reply.started":"2026-04-30T15:52:23.115639Z","shell.execute_reply":"2026-04-30T15:52:30.607674Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport tensorflow as tf\nimport cv2\nimport matplotlib.pyplot as plt\nfrom tensorflow.keras.models import load_model\n\n# 1. Setup and Load Model\n# We target the last convolutional layer of ResNet50 for the heatmap\nmodel = load_model(\"/kaggle/working/galaxy_model.h5\")\nlast_conv_layer_name = \"conv4_block6_out\" \nclass_names = [\"Elliptical\", \"Spiral\", \"Irregular\"]\n\ndef get_refined_heatmap(img_array, model, last_conv_layer_name):\n    grad_model = tf.keras.models.Model(\n        [model.inputs], [model.get_layer(last_conv_layer_name).output, model.output]\n    )\n\n    with tf.GradientTape() as tape:\n        last_conv_layer_output, preds = grad_model(img_array)\n        class_channel = preds[:, np.argmax(preds[0])]\n\n    grads = tape.gradient(class_channel, last_conv_layer_output)\n    pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2))\n\n    last_conv_layer_output = last_conv_layer_output[0]\n    heatmap = last_conv_layer_output @ pooled_grads[..., tf.newaxis]\n    heatmap = tf.squeeze(heatmap).numpy()\n\n    # REFINEMENT: Apply ReLU and normalize\n    heatmap = np.maximum(heatmap, 0)\n    if np.max(heatmap) != 0:\n        heatmap /= np.max(heatmap)\n        \n    return heatmap\n\ndef display_proper_gradcam(img_path, model, last_conv_layer_name):\n    # Load and Preprocess\n    img = cv2.imread(img_path)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    h, w, _ = img.shape\n    min_dim = min(h, w)\n    img = img[(h-min_dim)//2:(h+min_dim)//2, (w-min_dim)//2:(w+min_dim)//2]\n    img_resized = cv2.resize(img, (128, 128))\n    img_array = np.expand_dims(img_resized / 255.0, axis=0)\n\n    # 1. Get Prediction\n    preds = model.predict(img_array)\n    idx = np.argmax(preds[0])\n    \n    # 2. Get Heatmap\n    heatmap = get_refined_heatmap(img_array, model, last_conv_layer_name)\n\n    # 3. FIX DIVERSION: Use INTER_CUBIC for smoother, more centered upscaling\n    heatmap_resized = cv2.resize(heatmap, (128, 128), interpolation=cv2.INTER_CUBIC)\n    \n    # 4. Sharpen the mask to reduce \"bleeding\" into the background\n    heatmap_resized = np.uint8(255 * heatmap_resized)\n    jet_heatmap = cv2.applyColorMap(heatmap_resized, cv2.COLORMAP_JET)\n    jet_heatmap = cv2.cvtColor(jet_heatmap, cv2.COLOR_BGR2RGB)\n\n    # 5. Combined visualization\n    # We increase the weight of the original image (0.6) vs heatmap (0.4) for clarity\n    superimposed_img = cv2.addWeighted(img_resized, 0.6, jet_heatmap, 0.4, 0)\n\n    fig, ax = plt.subplots(1, 2, figsize=(12, 6))\n    ax[0].imshow(img_resized)\n    ax[0].set_title(f\"Target: {class_names[idx]} ({preds[0][idx]:.2%})\")\n    ax[1].imshow(superimposed_img)\n    ax[1].set_title(\"Sharpened Explainability Heatmap\")\n    plt.show()\n    \n    # Technical Explanation\n    print(f\"--- MODEL INTERPRETATION REPORT ---\")\n    print(f\"Classification: {class_names[class_id]}\")\n    print(f\"Reasoning: The red/yellow areas indicate regions that contributed most to this decision.\")\n    if class_id == 1: # Spiral\n        print(\"Note: The model is correctly triggering on the curvilinear structures (arms).\")\n    elif class_id == 0: # Elliptical\n        print(\"Note: The focus is concentrated on the dense, central luminosity core.\")\n\n# Run the function\nimg_path = \"/kaggle/input/datasets/ushnardraghosh/iirgeukar/Screenshot 2026-04-23 121220.png\"\ndisplay_gradcam(img_path, model, last_conv_layer_name)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-30T16:05:00.455711Z","iopub.execute_input":"2026-04-30T16:05:00.456618Z","iopub.status.idle":"2026-04-30T16:05:05.937989Z","shell.execute_reply.started":"2026-04-30T16:05:00.456586Z","shell.execute_reply":"2026-04-30T16:05:05.937049Z"}},"outputs":[],"execution_count":null}]}