{"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":112899,"databundleVersionId":13449579,"sourceType":"competition"}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# ============================================================\n# Grand X-Ray Slam Division A — Train, Evaluate, and Predict\n# ============================================================\n# Produces: /kaggle/working/submission.csv\n# ============================================================\n\nimport os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import (\n    roc_auc_score, roc_curve, auc,\n    precision_recall_fscore_support, confusion_matrix\n)\nimport itertools\n\n# -----------------------------\n# Configuration\n# -----------------------------\nDATA_DIR = \"/kaggle/input/grand-xray-slam-division-a\"\nTRAIN_DIR = os.path.join(DATA_DIR, \"train1\")\nTEST_DIR  = os.path.join(DATA_DIR, \"test1\")\nTRAIN_CSV = os.path.join(DATA_DIR, \"train1.csv\")\nSAMPLE_SUB_CSV = os.path.join(DATA_DIR, \"sample_submission_1.csv\")\nSUBMISSION_PATH = \"/kaggle/working/submission.csv\"\n\nIMG_SIZE = (96, 96)\nBATCH_SIZE = 128\nEPOCHS = 8\nRANDOM_STATE = 42\nTHRESHOLD = 0.5\n\n# -----------------------------\n# Labels\n# -----------------------------\ntargets = [\n    'Atelectasis','Cardiomegaly','Consolidation','Edema',\n    'Enlarged Cardiomediastinum','Fracture','Lung Lesion','Lung Opacity',\n    'No Finding','Pleural Effusion','Pleural Other','Pneumonia',\n    'Pneumothorax','Support Devices'\n]\n\n# -----------------------------\n# Load dataset\n# -----------------------------\nprint(\"Loading training CSV...\")\ndf = pd.read_csv(TRAIN_CSV)\ndf['path'] = df['Image_name'].apply(lambda x: os.path.join(TRAIN_DIR, x))\nlabels = df[targets].values.astype(np.int32)\n\nprint(f\"Training set shape: {df.shape}\")\nmissing = (~df['path'].apply(os.path.exists)).sum()\nprint(f\"Missing image files: {missing}\")\n\n# Display dataset summary\nprint(\"\\n=== Dataset Summary ===\")\nprint(f\"Total images: {len(df)}\")\nclass_counts = labels.sum(axis=0)\nfor i, t in enumerate(targets):\n    print(f\"{t:30s}: {class_counts[i]:6d} positives ({class_counts[i]/len(df):.2%})\")\nprint(\"========================\\n\")\n\n# -----------------------------\n# Train / Validation Split\n# -----------------------------\nstratify_col = (labels.sum(axis=1) > 0).astype(int)\ntrain_idx, val_idx = train_test_split(\n    np.arange(len(df)), test_size=0.1, stratify=stratify_col, random_state=RANDOM_STATE\n)\ntrain_paths = df.loc[train_idx, 'path'].values\nval_paths   = df.loc[val_idx, 'path'].values\ny_train = labels[train_idx]\ny_val   = labels[val_idx]\n\nprint(f\"Train size: {len(train_paths)}, Val size: {len(val_paths)}\")\n\n# -----------------------------\n# TF Datasets\n# -----------------------------\nAUTOTUNE = tf.data.AUTOTUNE\n\ndef decode_img(path, label):\n    img = tf.io.read_file(path)\n    img = tf.image.decode_jpeg(img, channels=3)\n    img = tf.image.resize(img, IMG_SIZE)\n    return tf.cast(img, tf.float32)/255.0, label\n\ndef decode_test_img(path, name):\n    img = tf.io.read_file(path)\n    img = tf.image.decode_jpeg(img, channels=3)\n    img = tf.image.resize(img, IMG_SIZE)\n    return tf.cast(img, tf.float32)/255.0, name\n\ndef make_train_ds(paths, labels, shuffle=False):\n    ds = tf.data.Dataset.from_tensor_slices((paths, labels))\n    ds = ds.map(decode_img, num_parallel_calls=AUTOTUNE)\n    if shuffle:\n        ds = ds.shuffle(4096, seed=RANDOM_STATE)\n    ds = ds.batch(BATCH_SIZE).prefetch(AUTOTUNE)\n    return ds\n\ntrain_ds = make_train_ds(train_paths, y_train, shuffle=True)\nval_ds   = make_train_ds(val_paths, y_val)\n\n# -----------------------------\n# Mixed precision\n# -----------------------------\ntry:\n    tf.keras.mixed_precision.set_global_policy(\"mixed_float16\")\n    print(\"Mixed precision enabled.\")\nexcept Exception as e:\n    print(\"Could not enable mixed precision:\", e)\n\n# -----------------------------\n# CNN Model\n# -----------------------------\nfrom tensorflow.keras import layers, models\n\ndef build_model():\n    model = tf.keras.Sequential([\n        layers.InputLayer(input_shape=(IMG_SIZE[0], IMG_SIZE[1], 3)),\n        layers.Conv2D(32, (3,3), activation='relu', padding='same'),\n        layers.MaxPooling2D((2,2)),\n        layers.Conv2D(64, (3,3), activation='relu', padding='same'),\n        layers.MaxPooling2D((2,2)),\n        layers.Conv2D(128, (3,3), activation='relu', padding='same'),\n        layers.MaxPooling2D((2,2)),\n        layers.Conv2D(256, (3,3), activation='relu', padding='same'),\n        layers.GlobalAveragePooling2D(),\n        layers.Dense(256, activation='relu'),\n        layers.Dropout(0.5),\n        layers.Dense(len(targets), activation='sigmoid', dtype='float32')\n    ])\n    return model\n\nmodel = build_model()\nmodel.compile(\n    optimizer=tf.keras.optimizers.Adam(1e-3),\n    loss='binary_crossentropy',\n    metrics=[\n        tf.keras.metrics.BinaryAccuracy(name='accuracy'),\n        tf.keras.metrics.AUC(name='auc', multi_label=True, num_labels=len(targets))\n    ]\n)\nmodel.summary()\n\n# -----------------------------\n# Train\n# -----------------------------\ncallbacks = [\n    tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=2, verbose=1),\n    tf.keras.callbacks.EarlyStopping(monitor='val_auc', mode='max', patience=3, restore_best_weights=True, verbose=1)\n]\n\nprint(\"\\nTraining model...\")\nhistory = model.fit(train_ds, validation_data=val_ds, epochs=EPOCHS, callbacks=callbacks)\n\n# -----------------------------\n# Evaluation: Metrics\n# -----------------------------\nprint(\"\\nEvaluating on validation set...\")\ny_true, y_pred = [], []\nfor x, y in val_ds:\n    preds = model.predict(x, verbose=0)\n    y_true.append(y.numpy())\n    y_pred.append(preds)\ny_true = np.vstack(y_true)\ny_pred = np.vstack(y_pred)\n\nprint(f\"Validation set predictions shape: {y_pred.shape}\")\n\n# ROC AUC\nprint(\"\\nPer-class ROC AUC:\")\naucs = []\nfor i, t in enumerate(targets):\n    try:\n        score = roc_auc_score(y_true[:,i], y_pred[:,i])\n    except ValueError:\n        score = np.nan\n    aucs.append(score)\n    print(f\"  {t:30s}: {score:.4f}\")\nprint(f\"\\nMean AUC: {np.nanmean(aucs):.4f}\")\n\n# Threshold predictions for F1/Precision/Recall\ny_pred_bin = (y_pred >= THRESHOLD).astype(int)\nprec, rec, f1, _ = precision_recall_fscore_support(y_true, y_pred_bin, average='micro', zero_division=0)\nprint(f\"\\nMicro Precision={prec:.4f} Recall={rec:.4f} F1={f1:.4f}\")\n\n# -----------------------------\n# Confusion Matrices\n# -----------------------------\ndef plot_cm(cm, title):\n    plt.imshow(cm, interpolation='nearest', cmap=plt.cm.Blues)\n    plt.title(title)\n    plt.colorbar()\n    tick_marks = np.arange(2)\n    plt.xticks(tick_marks, ['0','1'])\n    plt.yticks(tick_marks, ['0','1'])\n    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n        plt.text(j, i, cm[i, j], ha=\"center\", va=\"center\",\n                 color=\"white\" if cm[i, j] > cm.max()/2 else \"black\")\n    plt.ylabel('True'); plt.xlabel('Pred'); plt.tight_layout()\n\nprint(\"\\nPlotting per-class confusion matrices...\")\nplt.figure(figsize=(10, 20))\nfor i, t in enumerate(targets):\n    cm = confusion_matrix(y_true[:,i], y_pred_bin[:,i])\n    plt.subplot(7, 2, i+1)\n    plot_cm(cm, t)\nplt.tight_layout()\nplt.show()\n\n# -----------------------------\n# ROC Curves\n# -----------------------------\nplt.figure(figsize=(10,10))\nfor i, t in enumerate(targets):\n    try:\n        fpr, tpr, _ = roc_curve(y_true[:,i], y_pred[:,i])\n        roc_auc = auc(fpr, tpr)\n        plt.plot(fpr, tpr, label=f\"{t} (AUC={roc_auc:.2f})\")\n    except:\n        continue\nplt.plot([0,1],[0,1],'--',color='gray')\nplt.legend(fontsize=8, ncol=2)\nplt.title(\"Validation ROC Curves\")\nplt.xlabel(\"FPR\"); plt.ylabel(\"TPR\")\nplt.show()\n\n# -----------------------------\n# Predict on test1\n# -----------------------------\nprint(\"\\nPredicting on test set...\")\n\nsample_sub = pd.read_csv(SAMPLE_SUB_CSV)\ntest_rows = []\nfor img_name in sample_sub['Image_name']:\n    path = os.path.join(TEST_DIR, img_name)\n    if not os.path.exists(path):\n        raise FileNotFoundError(f\"Missing test image: {path}\")\n    test_rows.append((path, img_name))\n\ndef make_test_ds(rows):\n    paths, names = zip(*rows)\n    ds = tf.data.Dataset.from_tensor_slices((list(paths), list(names)))\n    ds = ds.map(lambda p, n: decode_test_img(p, n), num_parallel_calls=AUTOTUNE)\n    return ds.batch(BATCH_SIZE).prefetch(AUTOTUNE)\n\ntest_ds = make_test_ds(test_rows)\n\nprobs, names = [], []\nfor imgs, names_batch in test_ds:\n    preds = model.predict(imgs, verbose=0)\n    probs.append(preds)\n    names.extend([n.numpy().decode() for n in names_batch])\nprobs = np.vstack(probs)\n\n# -----------------------------\n# Build Submission (Probabilities)\n# -----------------------------\nsubmission = pd.DataFrame(probs, columns=targets)\nsubmission.insert(0, \"Image_name\", names)\nsubmission = submission.set_index(\"Image_name\").reindex(sample_sub[\"Image_name\"]).reset_index()\n\n# Clip to [0,1]\nsubmission[targets] = submission[targets].clip(0.0, 1.0)\n\nsubmission.to_csv(SUBMISSION_PATH, index=False)\nprint(f\"\\nSaved submission with probabilities to: {SUBMISSION_PATH}\")\nprint(submission.head())\nprint(f\"Total rows: {len(submission)}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-10T19:42:26.311988Z","iopub.execute_input":"2025-10-10T19:42:26.312501Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}