{"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":"none","dataSources":[{"sourceId":39272,"databundleVersionId":4629629,"sourceType":"competition"}],"dockerImageVersionId":31259,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install -q pydicom python-gdcm pylibjpeg","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-16T21:04:02.785349Z","iopub.execute_input":"2026-01-16T21:04:02.785722Z","iopub.status.idle":"2026-01-16T21:04:08.952791Z","shell.execute_reply.started":"2026-01-16T21:04:02.785693Z","shell.execute_reply":"2026-01-16T21:04:08.951707Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nimport cv2\nimport pydicom\nimport matplotlib.pyplot as plt\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import models, transforms\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import classification_report\nfrom tqdm.auto import tqdm\n\n# Path configuration\nBASE_DIR = \"/kaggle/input/rsna-breast-cancer-detection\"\nIMAGE_DIR = os.path.join(BASE_DIR, \"train_images\")\nTRAIN_CSV = os.path.join(BASE_DIR, \"train.csv\")\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Running on: {device}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-16T21:04:08.954917Z","iopub.execute_input":"2026-01-16T21:04:08.955265Z","iopub.status.idle":"2026-01-16T21:04:20.374856Z","shell.execute_reply.started":"2026-01-16T21:04:08.955231Z","shell.execute_reply":"2026-01-16T21:04:20.374095Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv(TRAIN_CSV)\n\n# Step: Filter rows that have 'density' labels\ndf_density = df.dropna(subset=['density']).copy()\ndensity_map = {'A': 0, 'B': 1, 'C': 2, 'D': 3}\ndf_density['label'] = df_density['density'].map(density_map)\n\n# Split into Train (250) and Val (50) for a fast demonstration\ntrain_df, val_df = train_test_split(df_density, test_size=50, stratify=df_density['label'], random_state=42)\ntrain_df = train_df.sample(250, random_state=42)\n\nprint(f\"Training on: {len(train_df)} images\")\nprint(f\"Validating on: {len(val_df)} images\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-16T21:04:20.375804Z","iopub.execute_input":"2026-01-16T21:04:20.376249Z","iopub.status.idle":"2026-01-16T21:04:20.563455Z","shell.execute_reply.started":"2026-01-16T21:04:20.376221Z","shell.execute_reply":"2026-01-16T21:04:20.562517Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def read_dicom(path, size=256):\n    try:\n        dicom = pydicom.dcmread(path)\n        data = apply_voi_lut(dicom.pixel_array, dicom)\n        if dicom.PhotometricInterpretation == \"MONOCHROME1\":\n            data = np.amax(data) - data\n        data = data - np.min(data)\n        data = data / (np.max(data) + 1e-5)\n        data = (data * 255).astype(np.uint8)\n        return cv2.resize(data, (size, size))\n    except Exception:\n        return np.zeros((size, size), dtype=np.uint8)\n\nclass MammoDataset(Dataset):\n    def __init__(self, dataframe, transform=None):\n        self.df = dataframe\n        self.transform = transform\n    def __len__(self):\n        return len(self.df)\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        img_path = f\"{IMAGE_DIR}/{row['patient_id']}/{row['image_id']}.dcm\"\n        image = read_dicom(img_path)\n        image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)\n        if self.transform: image = self.transform(image)\n        return image, torch.tensor(row['label'], dtype=torch.long)\n\n# Transforms & Loaders\ntransform = transforms.Compose([\n    transforms.ToPILImage(),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n])\n\ntrain_loader = DataLoader(MammoDataset(train_df, transform), batch_size=8, shuffle=True)\nval_loader = DataLoader(MammoDataset(val_df, transform), batch_size=8, shuffle=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-16T21:04:20.56553Z","iopub.execute_input":"2026-01-16T21:04:20.565971Z","iopub.status.idle":"2026-01-16T21:04:20.577893Z","shell.execute_reply.started":"2026-01-16T21:04:20.56594Z","shell.execute_reply":"2026-01-16T21:04:20.577055Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = models.efficientnet_b0(weights='DEFAULT')\nmodel.classifier[1] = nn.Linear(model.classifier[1].in_features, 4)\nmodel = model.to(device)\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=0.0001)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-16T21:04:20.579289Z","iopub.execute_input":"2026-01-16T21:04:20.579606Z","iopub.status.idle":"2026-01-16T21:04:21.378918Z","shell.execute_reply.started":"2026-01-16T21:04:20.579563Z","shell.execute_reply":"2026-01-16T21:04:21.378166Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Training started...\")\nmodel.train()\nfor epoch in range(1): # 1 epoch for demo\n    pbar = tqdm(train_loader, desc=f\"Epoch {epoch+1}\")\n    for images, labels in pbar:\n        images, labels = images.to(device), labels.to(device)\n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        pbar.set_postfix(loss=f\"{loss.item():.4f}\")\nprint(\"Training complete.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-16T21:04:21.380122Z","iopub.execute_input":"2026-01-16T21:04:21.380589Z","iopub.status.idle":"2026-01-16T21:08:21.942455Z","shell.execute_reply.started":"2026-01-16T21:04:21.38056Z","shell.execute_reply":"2026-01-16T21:08:21.941626Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.eval()\nall_preds, all_labels = [], []\nwith torch.no_grad():\n    for images, labels in tqdm(val_loader, desc=\"Evaluating\"):\n        images = images.to(device)\n        outputs = model(images)\n        preds = torch.argmax(outputs, 1)\n        all_preds.extend(preds.cpu().numpy())\n        all_labels.extend(labels.numpy())\n\nprint(\"\\n--- Performance Report ---\")\nprint(classification_report(all_labels, all_preds, target_names=['A', 'B', 'C', 'D'], zero_division=0))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-16T21:08:21.943273Z","iopub.execute_input":"2026-01-16T21:08:21.943526Z","iopub.status.idle":"2026-01-16T21:08:55.289916Z","shell.execute_reply.started":"2026-01-16T21:08:21.943503Z","shell.execute_reply":"2026-01-16T21:08:55.289143Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def show_xai(model, dataset, index=0):\n    model.eval()\n    img_tensor, label = dataset[index]\n    \n    # Get features for XAI\n    features = model.features(img_tensor.unsqueeze(0).to(device))\n    heatmap = torch.mean(features, dim=1).squeeze().detach().cpu().numpy()\n    heatmap = np.maximum(heatmap, 0)\n    heatmap /= (np.max(heatmap) + 1e-5)\n    \n    # Show results\n    img_display = img_tensor.permute(1, 2, 0).numpy()\n    img_display = (img_display - img_display.min()) / (img_display.max() - img_display.min())\n    \n    plt.figure(figsize=(10, 5))\n    plt.subplot(1, 2, 1)\n    plt.imshow(img_display)\n    plt.title(f\"Original (Class {label})\")\n    plt.subplot(1, 2, 2)\n    plt.imshow(img_display)\n    plt.imshow(cv2.resize(heatmap, (256, 256)), cmap='jet', alpha=0.4)\n    plt.title(\"XAI: AI Decision Focus\")\n    plt.show()\n\n# Show XAI for the first image in the validation set\nshow_xai(model, MammoDataset(val_df, transform), index=0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-16T21:12:11.611461Z","iopub.execute_input":"2026-01-16T21:12:11.611765Z","iopub.status.idle":"2026-01-16T21:12:12.503837Z","shell.execute_reply.started":"2026-01-16T21:12:11.61174Z","shell.execute_reply":"2026-01-16T21:12:12.502839Z"}},"outputs":[],"execution_count":null}]}