{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":18647,"databundleVersionId":1126921,"sourceType":"competition"}],"dockerImageVersionId":31012,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-05-07T16:27:14.282113Z","iopub.execute_input":"2025-05-07T16:27:14.283931Z","iopub.status.idle":"2025-05-07T16:27:35.292397Z","shell.execute_reply.started":"2025-05-07T16:27:14.283851Z","shell.execute_reply":"2025-05-07T16:27:35.291242Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nimport openslide\nfrom PIL import Image\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, models\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import accuracy_score, f1_score, cohen_kappa_score, roc_auc_score\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-07T16:27:35.293648Z","iopub.execute_input":"2025-05-07T16:27:35.293909Z","iopub.status.idle":"2025-05-07T16:27:35.29919Z","shell.execute_reply.started":"2025-05-07T16:27:35.293889Z","shell.execute_reply":"2025-05-07T16:27:35.298184Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"CSV_PATH = \"/kaggle/input/prostate-cancer-grade-assessment/train.csv\"\nIMG_DIR = \"/kaggle/input/prostate-cancer-grade-assessment/train_images\"\n\ndf = pd.read_csv(CSV_PATH)\ndf = df[['image_id', 'isup_grade']]\ndf = df.sample(500, random_state=42).reset_index(drop=True)\ndf['image_path'] = df['image_id'].apply(lambda x: os.path.join(IMG_DIR, f\"{x}.tiff\"))\n\nprint(df.head())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-07T16:27:35.300336Z","iopub.execute_input":"2025-05-07T16:27:35.300882Z","iopub.status.idle":"2025-05-07T16:27:35.3415Z","shell.execute_reply.started":"2025-05-07T16:27:35.300858Z","shell.execute_reply":"2025-05-07T16:27:35.340432Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMG_SIZE = 224\n\ntransform = transforms.Compose([\n    transforms.Resize((IMG_SIZE, IMG_SIZE)),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406],\n                         [0.229, 0.224, 0.225])\n])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-07T16:27:35.343799Z","iopub.execute_input":"2025-05-07T16:27:35.34416Z","iopub.status.idle":"2025-05-07T16:27:35.351043Z","shell.execute_reply.started":"2025-05-07T16:27:35.344137Z","shell.execute_reply":"2025-05-07T16:27:35.349956Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ProstateFeatureDataset(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 = row['image_path']\n        slide = openslide.OpenSlide(img_path)\n        w, h = slide.dimensions\n        patch = slide.read_region((w//2 - 256, h//2 - 256), 0, (512, 512)).convert(\"RGB\")\n        if self.transform:\n            patch = self.transform(patch)\n        label = row['isup_grade']  # multiclass 0 to 5\n        return patch, label\n# Load pretrained ResNet50 and remove last fully connected layer\nresnet = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1)\nresnet = nn.Sequential(*list(resnet.children())[:-1])  # remove final FC layer\nresnet.eval()\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nresnet = resnet.to(device)\n\n# Dataset and DataLoader\nfeature_dataset = ProstateFeatureDataset(df, transform=transform)\nfeature_loader = DataLoader(feature_dataset, batch_size=16, shuffle=False)\n\nfeatures_list = []\nlabels_list = []\n\nwith torch.no_grad():\n    for images, labels in feature_loader:\n        images = images.to(device)\n        features = resnet(images).squeeze()\n        if len(features.shape) == 1:\n            features = features.unsqueeze(0)  # batch size 1 fix\n        features_list.append(features.cpu().numpy())\n        labels_list.extend(labels.numpy())\n\n# Stack features and labels\nfeatures_array = np.vstack(features_list)  # shape: [500, 2048]\nlabels_array = np.array(labels_list)       # multiclass labels (0 to 5)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-07T16:27:35.352625Z","iopub.execute_input":"2025-05-07T16:27:35.352972Z","iopub.status.idle":"2025-05-07T16:28:59.257573Z","shell.execute_reply.started":"2025-05-07T16:27:35.35292Z","shell.execute_reply":"2025-05-07T16:28:59.256801Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X_train, X_temp, y_train, y_temp = train_test_split(features_array, labels_array,\n                                                    test_size=0.4, stratify=labels_array,\n                                                    random_state=42)\n\nX_val, X_test, y_val, y_test = train_test_split(X_temp, y_temp,\n                                                test_size=0.5, stratify=y_temp,\n                                                random_state=42)\n\nprint(f\"Training set: {X_train.shape[0]} samples\")\nprint(f\"Validation set: {X_val.shape[0]} samples\")\nprint(f\"Test set: {X_test.shape[0]} samples\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-07T16:28:59.258625Z","iopub.execute_input":"2025-05-07T16:28:59.259015Z","iopub.status.idle":"2025-05-07T16:28:59.270736Z","shell.execute_reply.started":"2025-05-07T16:28:59.258986Z","shell.execute_reply":"2025-05-07T16:28:59.269973Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class SNN(nn.Module):\n    def __init__(self, input_dim, hidden_dim=512, num_classes=6):\n        super(SNN, self).__init__()\n        self.fc1 = nn.Linear(input_dim, hidden_dim)\n        self.relu = nn.ReLU()\n        self.fc2 = nn.Linear(hidden_dim, num_classes)  # 6 classes\n\n    def forward(self, x):\n        x = self.fc1(x)\n        x = self.relu(x)\n        x = self.fc2(x)\n        return x\n\ninput_dim = X_train.shape[1]  # 2048 features from ResNet\nmodel_snn = SNN(input_dim).to(device)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-07T16:28:59.271518Z","iopub.execute_input":"2025-05-07T16:28:59.271827Z","iopub.status.idle":"2025-05-07T16:28:59.302837Z","shell.execute_reply.started":"2025-05-07T16:28:59.271804Z","shell.execute_reply":"2025-05-07T16:28:59.30186Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import TensorDataset, DataLoader\n\n# Convert numpy to tensors\nX_train_tensor = torch.tensor(X_train, dtype=torch.float32)\ny_train_tensor = torch.tensor(y_train, dtype=torch.long)\n\nX_val_tensor = torch.tensor(X_val, dtype=torch.float32)\ny_val_tensor = torch.tensor(y_val, dtype=torch.long)\n\nX_test_tensor = torch.tensor(X_test, dtype=torch.float32)\ny_test_tensor = torch.tensor(y_test, dtype=torch.long)\n\n# Create datasets\ntrain_dataset = TensorDataset(X_train_tensor, y_train_tensor)\nval_dataset = TensorDataset(X_val_tensor, y_val_tensor)\ntest_dataset = TensorDataset(X_test_tensor, y_test_tensor)\n\n# DataLoaders\ntrain_loader = DataLoader(train_dataset, batch_size=16, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=16)\ntest_loader = DataLoader(test_dataset, batch_size=16)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-07T16:28:59.304106Z","iopub.execute_input":"2025-05-07T16:28:59.304435Z","iopub.status.idle":"2025-05-07T16:28:59.316947Z","shell.execute_reply.started":"2025-05-07T16:28:59.30441Z","shell.execute_reply":"2025-05-07T16:28:59.315796Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"criterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(model_snn.parameters(), lr=0.001)\n\nepochs = 10\nfor epoch in range(epochs):\n    model_snn.train()\n    total_loss = 0\n    correct = 0\n    total = 0\n\n    for features, labels in train_loader:\n        features, labels = features.to(device), labels.to(device)\n\n        optimizer.zero_grad()\n        outputs = model_snn(features)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n\n        total_loss += loss.item() * labels.size(0)\n        preds = outputs.argmax(dim=1)\n        correct += (preds == labels).sum().item()\n        total += labels.size(0)\n\n    train_acc = correct / total\n    avg_loss = total_loss / total\n    print(f\"Epoch {epoch+1}: Loss={avg_loss:.4f}, Train Accuracy={train_acc:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-07T16:28:59.317887Z","iopub.execute_input":"2025-05-07T16:28:59.318146Z","iopub.status.idle":"2025-05-07T16:29:00.491457Z","shell.execute_reply.started":"2025-05-07T16:28:59.318126Z","shell.execute_reply":"2025-05-07T16:29:00.490444Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model_snn.eval()\ny_true_train = []\ny_pred_train = []\n\nwith torch.no_grad():\n    for features, labels in train_loader:\n        features = features.to(device)\n        outputs = model_snn(features)\n        preds = outputs.argmax(dim=1).cpu().numpy()\n\n        y_true_train.extend(labels.cpu().numpy())\n        y_pred_train.extend(preds)\n\ntrain_accuracy = accuracy_score(y_true_train, y_pred_train)\nprint(f\"Training Accuracy: {train_accuracy:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-07T16:29:00.493818Z","iopub.execute_input":"2025-05-07T16:29:00.494089Z","iopub.status.idle":"2025-05-07T16:29:00.525487Z","shell.execute_reply.started":"2025-05-07T16:29:00.494067Z","shell.execute_reply":"2025-05-07T16:29:00.524468Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_true_val = []\ny_pred_val = []\n\nwith torch.no_grad():\n    for features, labels in val_loader:\n        features = features.to(device)\n        outputs = model_snn(features)\n        preds = outputs.argmax(dim=1).cpu().numpy()\n\n        y_true_val.extend(labels.cpu().numpy())\n        y_pred_val.extend(preds)\n\nval_accuracy = accuracy_score(y_true_val, y_pred_val)\nval_f1 = f1_score(y_true_val, y_pred_val, average='weighted')\nval_kappa = cohen_kappa_score(y_true_val, y_pred_val)\n\n# For ROC, we need binary labels: cancer (grade > 0) vs benign (grade 0)\ny_true_val_bin = [1 if y > 0 else 0 for y in y_true_val]\ny_pred_val_bin = [1 if y > 0 else 0 for y in y_pred_val]\nval_roc = roc_auc_score(y_true_val_bin, y_pred_val_bin)\n\nprint(f\"Validation Accuracy: {val_accuracy:.4f}\")\nprint(f\"F1 Score: {val_f1:.4f}\")\nprint(f\"Kappa: {val_kappa:.4f}\")\nprint(f\"ROC AUC: {val_roc:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-07T16:29:00.526629Z","iopub.execute_input":"2025-05-07T16:29:00.527016Z","iopub.status.idle":"2025-05-07T16:29:00.553828Z","shell.execute_reply.started":"2025-05-07T16:29:00.526934Z","shell.execute_reply":"2025-05-07T16:29:00.552421Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_true_test = []\ny_pred_test = []\n\nwith torch.no_grad():\n    for features, labels in test_loader:\n        features = features.to(device)\n        outputs = model_snn(features)\n        preds = outputs.argmax(dim=1).cpu().numpy()\n\n        y_true_test.extend(labels.cpu().numpy())\n        y_pred_test.extend(preds)\n\ntest_accuracy = accuracy_score(y_true_test, y_pred_test)\ntest_f1 = f1_score(y_true_test, y_pred_test, average='weighted')\ntest_kappa = cohen_kappa_score(y_true_test, y_pred_test)\n\n# For ROC, convert to binary\ny_true_test_bin = [1 if y > 0 else 0 for y in y_true_test]\ny_pred_test_bin = [1 if y > 0 else 0 for y in y_pred_test]\ntest_roc = roc_auc_score(y_true_test_bin, y_pred_test_bin)\n\nprint(f\"Test Accuracy: {test_accuracy:.4f}\")\nprint(f\"F1 Score: {test_f1:.4f}\")\nprint(f\"Kappa: {test_kappa:.4f}\")\nprint(f\"ROC AUC: {test_roc:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-07T16:29:00.55474Z","iopub.execute_input":"2025-05-07T16:29:00.555062Z","iopub.status.idle":"2025-05-07T16:29:00.580794Z","shell.execute_reply.started":"2025-05-07T16:29:00.555024Z","shell.execute_reply":"2025-05-07T16:29:00.579077Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"results = pd.DataFrame({\n    \"Set\": [\"Training\", \"Validation\", \"Test\"],\n    \"Accuracy\": [train_accuracy, val_accuracy, test_accuracy],\n    \"F1 Measure\": [\"-\", f\"{val_f1:.4f}\", f\"{test_f1:.4f}\"],\n    \"Kappa\": [\"-\", f\"{val_kappa:.4f}\", f\"{test_kappa:.4f}\"],\n    \"ROC Area\": [\"-\", f\"{val_roc:.4f}\", f\"{test_roc:.4f}\"]\n})\n\nprint(results.to_string(index=False))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-07T16:29:00.581858Z","iopub.execute_input":"2025-05-07T16:29:00.582136Z","iopub.status.idle":"2025-05-07T16:29:00.593838Z","shell.execute_reply.started":"2025-05-07T16:29:00.582116Z","shell.execute_reply":"2025-05-07T16:29:00.592789Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Accuracy:\", test_accuracy)\nprint(\"\\n📊 Classification Report:\")\nprint(classification_report(y_true_test, y_pred_test))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-07T16:29:00.59492Z","iopub.execute_input":"2025-05-07T16:29:00.595178Z","iopub.status.idle":"2025-05-07T16:29:00.633361Z","shell.execute_reply.started":"2025-05-07T16:29:00.595158Z","shell.execute_reply":"2025-05-07T16:29:00.632345Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cm = confusion_matrix(y_true_test, y_pred_test)\n\nplt.figure(figsize=(6,5))\nsns.heatmap(cm, annot=True, fmt='d', cmap=\"Blues\",\n            xticklabels=[0, 1, 2, 3, 4, 5],\n            yticklabels=[0, 1, 2, 3, 4, 5])\nplt.title(\"🧩 Confusion Matrix\")\nplt.xlabel(\"Predicted Label\")\nplt.ylabel(\"True Label\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-07T16:29:00.634253Z","iopub.execute_input":"2025-05-07T16:29:00.6345Z","iopub.status.idle":"2025-05-07T16:29:00.921775Z","shell.execute_reply.started":"2025-05-07T16:29:00.634481Z","shell.execute_reply":"2025-05-07T16:29:00.920883Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}