{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.12.12"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":10338,"databundleVersionId":862042,"isSourceIdPinned":false}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true},"papermill":{"default_parameters":{},"duration":10897.315267,"end_time":"2026-04-29T01:20:08.775209+00:00","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2026-04-28T22:18:31.459942+00:00","version":"2.7.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"e17f0dc7","cell_type":"code","source":"!pip install -q torchxrayvision pydicom opencv-python","metadata":{"execution":{"iopub.status.busy":"2026-05-01T18:57:57.645529Z","iopub.execute_input":"2026-05-01T18:57:57.645903Z","iopub.status.idle":"2026-05-01T18:58:03.359266Z","shell.execute_reply.started":"2026-05-01T18:57:57.645870Z","shell.execute_reply":"2026-05-01T18:58:03.358439Z"},"papermill":{"duration":6.657503,"end_time":"2026-04-28T22:18:41.630084+00:00","exception":false,"start_time":"2026-04-28T22:18:34.972581+00:00","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"5a8eddc8","cell_type":"code","source":"import os\nimport copy\nimport random\nimport cv2\nimport pydicom\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import (\n    confusion_matrix, classification_report,\n    f1_score, precision_score, recall_score,\n    roc_auc_score, accuracy_score\n)\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nimport torchxrayvision as xrv","metadata":{"execution":{"iopub.status.busy":"2026-05-01T18:58:03.361367Z","iopub.execute_input":"2026-05-01T18:58:03.361728Z","iopub.status.idle":"2026-05-01T18:58:14.919786Z","shell.execute_reply.started":"2026-05-01T18:58:03.361696Z","shell.execute_reply":"2026-05-01T18:58:14.919125Z"},"papermill":{"duration":21.13427,"end_time":"2026-04-28T22:19:02.768397+00:00","exception":false,"start_time":"2026-04-28T22:18:41.634127+00:00","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"6f8780f1","cell_type":"markdown","source":"# Configuration","metadata":{"papermill":{"duration":0.003614,"end_time":"2026-04-28T22:19:02.777600+00:00","exception":false,"start_time":"2026-04-28T22:19:02.773986+00:00","status":"completed"},"tags":[]}},{"id":"a9feef00","cell_type":"code","source":"IMAGE_SIZE = 224\nBATCH_SIZE = 32\nEPOCHS_HEAD = 10\nEPOCHS_FINE = 20\nLR_HEAD = 1e-3\nLR_FINE = 1e-5\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nDATA_DIR = \"/kaggle/input/competitions/rsna-pneumonia-detection-challenge\"\nTRAIN_IMG_DIR = os.path.join(DATA_DIR, \"stage_2_train_images\")\nLABEL_CSV = os.path.join(DATA_DIR, \"stage_2_train_labels.csv\")","metadata":{"execution":{"iopub.status.busy":"2026-05-01T18:58:14.920815Z","iopub.execute_input":"2026-05-01T18:58:14.921305Z","iopub.status.idle":"2026-05-01T18:58:15.179884Z","shell.execute_reply.started":"2026-05-01T18:58:14.921270Z","shell.execute_reply":"2026-05-01T18:58:15.178831Z"},"papermill":{"duration":0.271934,"end_time":"2026-04-28T22:19:03.053234+00:00","exception":false,"start_time":"2026-04-28T22:19:02.781300+00:00","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"8025a039","cell_type":"markdown","source":"# Load Labels","metadata":{"papermill":{"duration":0.003525,"end_time":"2026-04-28T22:19:03.060636+00:00","exception":false,"start_time":"2026-04-28T22:19:03.057111+00:00","status":"completed"},"tags":[]}},{"id":"defba769","cell_type":"code","source":"labels_df = pd.read_csv(LABEL_CSV)\n\n# Binary label per patient\npatient_labels = labels_df.groupby(\"patientId\")[\"Target\"].max().reset_index()\n\ntrain_df, temp_df = train_test_split(\n    patient_labels,\n    test_size=0.2,\n    stratify=patient_labels[\"Target\"],\n    random_state=42\n)\n\nval_df, test_df = train_test_split(\n    temp_df,\n    test_size=0.5,\n    stratify=temp_df[\"Target\"],\n    random_state=42\n)\n\nprint(\"Train:\", len(train_df), \"Val:\", len(val_df), \"Test:\", len(test_df))","metadata":{"execution":{"iopub.status.busy":"2026-05-01T18:58:15.181047Z","iopub.execute_input":"2026-05-01T18:58:15.181468Z","iopub.status.idle":"2026-05-01T18:58:15.317199Z","shell.execute_reply.started":"2026-05-01T18:58:15.181431Z","shell.execute_reply":"2026-05-01T18:58:15.316432Z"},"papermill":{"duration":0.139151,"end_time":"2026-04-28T22:19:03.203272+00:00","exception":false,"start_time":"2026-04-28T22:19:03.064121+00:00","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"03ec89b1","cell_type":"markdown","source":"# Dataset","metadata":{"papermill":{"duration":0.003913,"end_time":"2026-04-28T22:19:03.210973+00:00","exception":false,"start_time":"2026-04-28T22:19:03.207060+00:00","status":"completed"},"tags":[]}},{"id":"373caebb","cell_type":"code","source":"class RSNADataset(Dataset):\n    def __init__(self, dataframe, img_dir, transform=None):\n        self.df = dataframe.reset_index(drop=True)\n        self.img_dir = img_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        patient_id = self.df.loc[idx, \"patientId\"]\n        label = self.df.loc[idx, \"Target\"]\n\n        img_path = os.path.join(self.img_dir, f\"{patient_id}.dcm\")\n\n        dicom = pydicom.dcmread(img_path)\n        image = dicom.pixel_array.astype(np.float32)\n\n        # Normalize for TorchXRayVision\n        image = xrv.datasets.normalize(image, 255)\n\n        # Resize\n        image = cv2.resize(image, (IMAGE_SIZE, IMAGE_SIZE))\n\n        # Convert to tensor [1,H,W]\n        image = torch.tensor(image, dtype=torch.float32).unsqueeze(0)\n\n        # Apply augmentations\n        if self.transform:\n            image = self.transform(image)\n\n        return image, torch.tensor(label, dtype=torch.float32)","metadata":{"execution":{"iopub.status.busy":"2026-05-01T18:58:15.319677Z","iopub.execute_input":"2026-05-01T18:58:15.320115Z","iopub.status.idle":"2026-05-01T18:58:15.327138Z","shell.execute_reply.started":"2026-05-01T18:58:15.320087Z","shell.execute_reply":"2026-05-01T18:58:15.326255Z"},"papermill":{"duration":0.012374,"end_time":"2026-04-28T22:19:03.227006+00:00","exception":false,"start_time":"2026-04-28T22:19:03.214632+00:00","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"ef957de7","cell_type":"markdown","source":"# Transforms","metadata":{"papermill":{"duration":0.00354,"end_time":"2026-04-28T22:19:03.234277+00:00","exception":false,"start_time":"2026-04-28T22:19:03.230737+00:00","status":"completed"},"tags":[]}},{"id":"0b8e8722","cell_type":"code","source":"train_transform = transforms.Compose([])\n\ntest_transform = transforms.Compose([])","metadata":{"execution":{"iopub.status.busy":"2026-05-01T19:00:04.080101Z","iopub.execute_input":"2026-05-01T19:00:04.080842Z","iopub.status.idle":"2026-05-01T19:00:04.085821Z","shell.execute_reply.started":"2026-05-01T19:00:04.080810Z","shell.execute_reply":"2026-05-01T19:00:04.084763Z"},"papermill":{"duration":0.010545,"end_time":"2026-04-28T22:19:03.248414+00:00","exception":false,"start_time":"2026-04-28T22:19:03.237869+00:00","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"e711a42d","cell_type":"markdown","source":"# Load Dataset","metadata":{"papermill":{"duration":0.003658,"end_time":"2026-04-28T22:19:03.257009+00:00","exception":false,"start_time":"2026-04-28T22:19:03.253351+00:00","status":"completed"},"tags":[]}},{"id":"e4abc21e","cell_type":"code","source":"train_ds = RSNADataset(train_df, TRAIN_IMG_DIR, train_transform)\nval_ds = RSNADataset(val_df, TRAIN_IMG_DIR, test_transform)\ntest_ds = RSNADataset(test_df, TRAIN_IMG_DIR, test_transform)\n\ntrain_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True)\nval_loader = DataLoader(val_ds, batch_size=BATCH_SIZE, shuffle=False)\ntest_loader = DataLoader(test_ds, batch_size=BATCH_SIZE, shuffle=False)","metadata":{"execution":{"iopub.status.busy":"2026-05-01T19:00:06.673812Z","iopub.execute_input":"2026-05-01T19:00:06.674633Z","iopub.status.idle":"2026-05-01T19:00:06.681523Z","shell.execute_reply.started":"2026-05-01T19:00:06.674602Z","shell.execute_reply":"2026-05-01T19:00:06.680533Z"},"papermill":{"duration":0.011222,"end_time":"2026-04-28T22:19:03.271853+00:00","exception":false,"start_time":"2026-04-28T22:19:03.260631+00:00","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"8d36e401","cell_type":"markdown","source":"# Model","metadata":{"papermill":{"duration":0.003534,"end_time":"2026-04-28T22:19:03.279094+00:00","exception":false,"start_time":"2026-04-28T22:19:03.275560+00:00","status":"completed"},"tags":[]}},{"id":"fb50abc6","cell_type":"code","source":"base_model = xrv.models.DenseNet(weights=\"densenet121-res224-all\")\n\n# Disable original pathology threshold normalization\nbase_model.op_threshs = None\n\n# Freeze backbone\nfor param in base_model.parameters():\n    param.requires_grad = False\n\n# Replace classifier for binary pneumonia\nin_features = base_model.classifier.in_features\n\nbase_model.classifier = nn.Sequential(\n    nn.Linear(in_features, 256),\n    nn.ReLU(),\n    nn.Dropout(0.5),\n    nn.Linear(256, 1)\n)\n\nmodel = base_model.to(DEVICE)\n\ncriterion = nn.BCEWithLogitsLoss()\noptimizer = optim.Adam(model.classifier.parameters(), lr=LR_HEAD)","metadata":{"execution":{"iopub.status.busy":"2026-05-01T19:00:07.583065Z","iopub.execute_input":"2026-05-01T19:00:07.583771Z","iopub.status.idle":"2026-05-01T19:00:08.553502Z","shell.execute_reply.started":"2026-05-01T19:00:07.583733Z","shell.execute_reply":"2026-05-01T19:00:08.552755Z"},"papermill":{"duration":1.182551,"end_time":"2026-04-28T22:19:04.465400+00:00","exception":false,"start_time":"2026-04-28T22:19:03.282849+00:00","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"6c10b676","cell_type":"markdown","source":"# Train","metadata":{"papermill":{"duration":0.004586,"end_time":"2026-04-28T22:19:04.475235+00:00","exception":false,"start_time":"2026-04-28T22:19:04.470649+00:00","status":"completed"},"tags":[]}},{"id":"ca91468c","cell_type":"code","source":"def train_one_epoch(model, loader, optimizer):\n    model.train()\n    total_loss = 0\n\n    for images, labels in loader:\n        images, labels = images.to(DEVICE), labels.to(DEVICE).unsqueeze(1)\n\n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n\n        loss.backward()\n        optimizer.step()\n\n        total_loss += loss.item()\n\n    return total_loss / len(loader)\n\n\ndef evaluate(model, loader):\n    model.eval()\n    preds, probs, labels_all = [], [], []\n\n    with torch.no_grad():\n        for images, labels in loader:\n            images = images.to(DEVICE)\n\n            outputs = model(images)\n            probabilities = torch.sigmoid(outputs)\n\n            preds.extend((probabilities > 0.5).cpu().numpy())\n            probs.extend(probabilities.cpu().numpy())\n            labels_all.extend(labels.numpy())\n\n    return np.array(labels_all), np.array(preds).flatten(), np.array(probs).flatten()","metadata":{"execution":{"iopub.status.busy":"2026-05-01T19:00:08.555091Z","iopub.execute_input":"2026-05-01T19:00:08.555808Z","iopub.status.idle":"2026-05-01T19:00:08.562645Z","shell.execute_reply.started":"2026-05-01T19:00:08.555782Z","shell.execute_reply":"2026-05-01T19:00:08.562016Z"},"papermill":{"duration":0.014024,"end_time":"2026-04-28T22:19:04.493819+00:00","exception":false,"start_time":"2026-04-28T22:19:04.479795+00:00","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"62e39226","cell_type":"markdown","source":"# Checkpoint Tracking","metadata":{"papermill":{"duration":0.004564,"end_time":"2026-04-28T22:19:04.503078+00:00","exception":false,"start_time":"2026-04-28T22:19:04.498514+00:00","status":"completed"},"tags":[]}},{"id":"a50e8891","cell_type":"code","source":"best_auc = 0\nbest_epoch = 0\nbest_model_wts = copy.deepcopy(model.state_dict())","metadata":{"execution":{"iopub.status.busy":"2026-05-01T19:00:08.563601Z","iopub.execute_input":"2026-05-01T19:00:08.564175Z","iopub.status.idle":"2026-05-01T19:00:08.631774Z","shell.execute_reply.started":"2026-05-01T19:00:08.564138Z","shell.execute_reply":"2026-05-01T19:00:08.631181Z"},"papermill":{"duration":0.062138,"end_time":"2026-04-28T22:19:04.569896+00:00","exception":false,"start_time":"2026-04-28T22:19:04.507758+00:00","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"2ee3a5a2","cell_type":"markdown","source":"# Head Training","metadata":{"papermill":{"duration":0.004685,"end_time":"2026-04-28T22:19:04.579431+00:00","exception":false,"start_time":"2026-04-28T22:19:04.574746+00:00","status":"completed"},"tags":[]}},{"id":"1db8a680","cell_type":"code","source":"print(\"\\n===== HEAD TRAINING =====\")\nfor epoch in range(EPOCHS_HEAD):\n    loss = train_one_epoch(model, train_loader, optimizer)\n\n    y_val_true, y_val_pred, y_val_prob = evaluate(model, val_loader)\n    val_auc = roc_auc_score(y_val_true, y_val_prob)\n\n    print(f\"Head Epoch {epoch+1}: Loss={loss:.4f}, Val AUC={val_auc:.4f}\")\n\n    if val_auc > best_auc:\n        best_auc = val_auc\n        best_epoch = epoch + 1\n        best_model_wts = copy.deepcopy(model.state_dict())","metadata":{"execution":{"iopub.status.busy":"2026-05-01T19:00:11.492094Z","iopub.execute_input":"2026-05-01T19:00:11.492921Z","iopub.status.idle":"2026-05-01T19:10:07.625135Z","shell.execute_reply.started":"2026-05-01T19:00:11.492889Z","shell.execute_reply":"2026-05-01T19:10:07.624155Z"},"papermill":{"duration":3721.9616,"end_time":"2026-04-28T23:21:06.545738+00:00","exception":false,"start_time":"2026-04-28T22:19:04.584138+00:00","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"e074ecbc","cell_type":"markdown","source":"# Fine Tuning","metadata":{"papermill":{"duration":0.005539,"end_time":"2026-04-28T23:21:06.557328+00:00","exception":false,"start_time":"2026-04-28T23:21:06.551789+00:00","status":"completed"},"tags":[]}},{"id":"fe0d27b3","cell_type":"code","source":"for param in model.features.denseblock4.parameters():\n    param.requires_grad = True\n\noptimizer = optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=LR_FINE)\n\nprint(\"\\n===== FINE TUNING =====\")\nfor epoch in range(EPOCHS_FINE):\n    loss = train_one_epoch(model, train_loader, optimizer)\n\n    y_val_true, y_val_pred, y_val_prob = evaluate(model, val_loader)\n    val_auc = roc_auc_score(y_val_true, y_val_prob)\n\n    print(f\"FineTune Epoch {epoch+1}: Loss={loss:.4f}, Val AUC={val_auc:.4f}\")\n\n    if val_auc > best_auc:\n        best_auc = val_auc\n        best_epoch = EPOCHS_HEAD + epoch + 1\n        best_model_wts = copy.deepcopy(model.state_dict())","metadata":{"execution":{"iopub.status.busy":"2026-05-01T19:10:07.627080Z","iopub.execute_input":"2026-05-01T19:10:07.627319Z","iopub.status.idle":"2026-05-01T19:21:43.821862Z","shell.execute_reply.started":"2026-05-01T19:10:07.627297Z","shell.execute_reply":"2026-05-01T19:21:43.821038Z"},"papermill":{"duration":7080.872896,"end_time":"2026-04-29T01:19:07.436100+00:00","exception":false,"start_time":"2026-04-28T23:21:06.563204+00:00","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"e5a15ea3","cell_type":"code","source":"print(f\"\\nRestoring Best Model from Epoch {best_epoch} with Val AUC={best_auc:.4f}\")\nmodel.load_state_dict(best_model_wts)","metadata":{"execution":{"iopub.status.busy":"2026-05-01T19:21:43.823177Z","iopub.execute_input":"2026-05-01T19:21:43.823670Z","iopub.status.idle":"2026-05-01T19:21:43.852548Z","shell.execute_reply.started":"2026-05-01T19:21:43.823644Z","shell.execute_reply":"2026-05-01T19:21:43.851890Z"},"papermill":{"duration":0.033856,"end_time":"2026-04-29T01:19:07.475975+00:00","exception":false,"start_time":"2026-04-29T01:19:07.442119+00:00","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"89d74a5d","cell_type":"markdown","source":"# Evaluation","metadata":{"papermill":{"duration":0.006063,"end_time":"2026-04-29T01:19:07.487902+00:00","exception":false,"start_time":"2026-04-29T01:19:07.481839+00:00","status":"completed"},"tags":[]}},{"id":"2cc73eac","cell_type":"code","source":"y_true, y_pred, y_prob = evaluate(model, test_loader)\n\nprint(\"\\n===== TEST CLASSIFICATION REPORT =====\")\nprint(classification_report(y_true, y_pred))\n\naccuracy = accuracy_score(y_true, y_pred)\nf1 = f1_score(y_true, y_pred)\nprecision = precision_score(y_true, y_pred)\nrecall = recall_score(y_true, y_pred)\nauc = roc_auc_score(y_true, y_prob)\n\nprint(f\"Accuracy : {accuracy:.4f}\")\nprint(f\"F1       : {f1:.4f}\")\nprint(f\"Precision: {precision:.4f}\")\nprint(f\"Recall   : {recall:.4f}\")\nprint(f\"AUC      : {auc:.4f}\")","metadata":{"execution":{"iopub.status.busy":"2026-05-01T19:21:43.854041Z","iopub.execute_input":"2026-05-01T19:21:43.854320Z","iopub.status.idle":"2026-05-01T19:22:42.834781Z","shell.execute_reply.started":"2026-05-01T19:21:43.854298Z","shell.execute_reply":"2026-05-01T19:22:42.833779Z"},"papermill":{"duration":57.460452,"end_time":"2026-04-29T01:20:04.954373+00:00","exception":false,"start_time":"2026-04-29T01:19:07.493921+00:00","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"4bf35174","cell_type":"code","source":"cm = confusion_matrix(y_true, y_pred)\n\nplt.figure(figsize=(6,5))\nsns.heatmap(\n    cm,\n    annot=True,\n    fmt=\"d\",\n    cmap=\"Blues\",\n    xticklabels=[\"Normal\", \"Pneumonia\"],\n    yticklabels=[\"Normal\", \"Pneumonia\"]\n)\nplt.title(\"Confusion Matrix\")\nplt.xlabel(\"Predicted\")\nplt.ylabel(\"Actual\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2026-05-01T19:22:42.836125Z","iopub.execute_input":"2026-05-01T19:22:42.836901Z","iopub.status.idle":"2026-05-01T19:22:43.024612Z","shell.execute_reply.started":"2026-05-01T19:22:42.836872Z","shell.execute_reply":"2026-05-01T19:22:43.023820Z"},"papermill":{"duration":0.206344,"end_time":"2026-04-29T01:20:05.167553+00:00","exception":false,"start_time":"2026-04-29T01:20:04.961209+00:00","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"71d38b76","cell_type":"markdown","source":"# Save Model","metadata":{"papermill":{"duration":0.007296,"end_time":"2026-04-29T01:20:05.182228+00:00","exception":false,"start_time":"2026-04-29T01:20:05.174932+00:00","status":"completed"},"tags":[]}},{"id":"515ce467","cell_type":"code","source":"torch.save(model.state_dict(), \"rsna_densenet121.pth\")\nprint(\"\\nBest model saved as rsna_densenet121.pth\")","metadata":{"execution":{"iopub.status.busy":"2026-05-01T19:22:43.025549Z","iopub.execute_input":"2026-05-01T19:22:43.025875Z","iopub.status.idle":"2026-05-01T19:22:43.131995Z","shell.execute_reply.started":"2026-05-01T19:22:43.025851Z","shell.execute_reply":"2026-05-01T19:22:43.130918Z"},"papermill":{"duration":0.109766,"end_time":"2026-04-29T01:20:05.299705+00:00","exception":false,"start_time":"2026-04-29T01:20:05.189939+00:00","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"538ad6c3","cell_type":"code","source":"print(\"\\n===== SAMPLE TEST PREDICTIONS =====\")\n\nsample_indices = random.sample(range(len(test_ds)), 2)\n\nplt.figure(figsize=(12,5))\n\nfor i, idx in enumerate(sample_indices):\n\n    # Dataset returns only image, label\n    image, label = test_ds[idx]\n\n    # Get patient ID from dataframe\n    patient_id = test_df.iloc[idx][\"patientId\"]\n\n    # Prediction\n    model.eval()\n    with torch.no_grad():\n        output = model(image.unsqueeze(0).to(DEVICE))\n        prob = torch.sigmoid(output).item()\n        pred = 1 if prob > 0.5 else 0\n\n    # Convert image for display\n    img_np = image.squeeze().numpy()\n\n    plt.subplot(1, 2, i + 1)\n    plt.imshow(img_np, cmap=\"gray\")\n    plt.axis(\"off\")\n\n    plt.title(\n        f\"Patient: {patient_id}\\n\"\n        f\"Predicted: {'Pneumonia' if pred else 'Normal'} ({prob:.2f})\\n\"\n        f\"Actual: {'Pneumonia' if label.item()==1 else 'Normal'}\"\n    )\n\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2026-05-01T19:22:43.133244Z","iopub.execute_input":"2026-05-01T19:22:43.133606Z","iopub.status.idle":"2026-05-01T19:22:43.602276Z","shell.execute_reply.started":"2026-05-01T19:22:43.133560Z","shell.execute_reply":"2026-05-01T19:22:43.601389Z"},"papermill":{"duration":0.432035,"end_time":"2026-04-29T01:20:05.738496+00:00","exception":false,"start_time":"2026-04-29T01:20:05.306461+00:00","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null}]}