{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":29653,"databundleVersionId":2420395,"sourceType":"competition"}],"dockerImageVersionId":30805,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install -q pydicom tensorflow\n\n# Imports\nimport os\nimport numpy as np\nimport pandas as pd\nimport cv2\nimport pydicom\nimport matplotlib.pyplot as plt\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import f1_score, cohen_kappa_score, roc_auc_score, roc_curve, auc\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\n\n# Load MGMT labels\ntrain_path = '/kaggle/input/rsna-miccai-brain-tumor-radiogenomic-classification/train/'\nlabels_path = '/kaggle/input/rsna-miccai-brain-tumor-radiogenomic-classification/train_labels.csv'\nlabels_df = pd.read_csv(labels_path)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-04-30T04:24:41.442111Z","iopub.execute_input":"2025-04-30T04:24:41.442444Z","iopub.status.idle":"2025-04-30T04:25:05.573622Z","shell.execute_reply.started":"2025-04-30T04:24:41.442406Z","shell.execute_reply":"2025-04-30T04:25:05.572893Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load multiple slices per patient into a bag\ndef load_patient_slices(patient_id, img_size=(128,128), max_slices=10):\n    folder = os.path.join(train_path, str(patient_id).zfill(5), \"T1w\")\n    if not os.path.exists(folder): return None\n    files = sorted(os.listdir(folder))\n    if len(files) == 0: return None\n    slice_indices = np.linspace(0, len(files) - 1, max_slices, dtype=int)\n    slices = []\n    for idx in slice_indices:\n        path = os.path.join(folder, files[idx])\n        dcm = pydicom.dcmread(path)\n        img = dcm.pixel_array\n        img = cv2.resize(img, img_size)\n        img = img / 255.0\n        slices.append(img)\n    return np.expand_dims(np.array(slices), -1)  # (slices, 128, 128, 1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-30T04:25:05.576103Z","iopub.execute_input":"2025-04-30T04:25:05.576393Z","iopub.status.idle":"2025-04-30T04:25:05.582910Z","shell.execute_reply.started":"2025-04-30T04:25:05.576359Z","shell.execute_reply":"2025-04-30T04:25:05.581868Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Build bags and labels\nbags = []\nbag_labels = []\n\nfor _, row in labels_df.iterrows():\n    slices = load_patient_slices(row['BraTS21ID'])\n    if slices is not None:\n        bags.append(slices)\n        bag_labels.append(row['MGMT_value'])\n\nbags = np.array(bags)\nbag_labels = np.array(bag_labels)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-30T04:25:05.583991Z","iopub.execute_input":"2025-04-30T04:25:05.584338Z","iopub.status.idle":"2025-04-30T04:26:20.323140Z","shell.execute_reply.started":"2025-04-30T04:25:05.584303Z","shell.execute_reply":"2025-04-30T04:26:20.322360Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Train/Val/Test split\nX_train, X_test, y_train, y_test = train_test_split(bags, bag_labels, test_size=0.2, stratify=bag_labels, random_state=42)\nX_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.25, stratify=y_train, random_state=42)\n\n# Attention MIL Layer\nclass MILAttentionLayer(layers.Layer):\n    def __init__(self, hidden_units):\n        super(MILAttentionLayer, self).__init__()\n        self.dense = layers.Dense(hidden_units, activation=\"tanh\")\n        self.attention = layers.Dense(1)\n\n    def call(self, inputs):\n        x = self.dense(inputs)\n        alpha = self.attention(x)\n        alpha = tf.nn.softmax(alpha, axis=1)\n        return tf.reduce_sum(alpha * inputs, axis=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-30T04:26:20.324280Z","iopub.execute_input":"2025-04-30T04:26:20.324551Z","iopub.status.idle":"2025-04-30T04:26:20.676832Z","shell.execute_reply.started":"2025-04-30T04:26:20.324524Z","shell.execute_reply":"2025-04-30T04:26:20.675799Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# MIL Model Definition\ndef create_mil_model(input_shape=(10, 128, 128, 1)):\n    inputs = keras.Input(shape=input_shape)\n    x = layers.TimeDistributed(layers.Conv2D(32, 3, activation='relu'))(inputs)\n    x = layers.TimeDistributed(layers.MaxPooling2D(2))(x)\n    x = layers.TimeDistributed(layers.Conv2D(64, 3, activation='relu'))(x)\n    x = layers.TimeDistributed(layers.GlobalAveragePooling2D())(x)\n    x = MILAttentionLayer(128)(x)\n    x = layers.Dense(128, activation='relu')(x)\n    x = layers.Dropout(0.5)(x)\n    outputs = layers.Dense(1, activation='sigmoid')(x)\n    model = keras.Model(inputs, outputs)\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-30T04:26:20.677997Z","iopub.execute_input":"2025-04-30T04:26:20.678286Z","iopub.status.idle":"2025-04-30T04:26:20.684364Z","shell.execute_reply.started":"2025-04-30T04:26:20.678262Z","shell.execute_reply":"2025-04-30T04:26:20.683388Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create and compile model\nmil_model = create_mil_model()\nmil_model.compile(optimizer=keras.optimizers.Adam(1e-4),\n                  loss=\"binary_crossentropy\",\n                  metrics=[\"accuracy\"])\n\n# Train model\nhistory = mil_model.fit(X_train, y_train,\n                        validation_data=(X_val, y_val),\n                        epochs=30,\n                        batch_size=8,\n                        verbose=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-30T04:26:20.685431Z","iopub.execute_input":"2025-04-30T04:26:20.685672Z","iopub.status.idle":"2025-04-30T04:27:14.814517Z","shell.execute_reply.started":"2025-04-30T04:26:20.685649Z","shell.execute_reply":"2025-04-30T04:27:14.813803Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Evaluate\ntest_loss, test_acc = mil_model.evaluate(X_test, y_test, verbose=0)\ny_pred_prob = mil_model.predict(X_test)\ny_pred = (y_pred_prob > 0.5).astype(int)\n\ntrain_acc = history.history['accuracy'][-1]\nval_acc = history.history['val_accuracy'][-1]\nf1 = f1_score(y_test, y_pred)\nkappa = cohen_kappa_score(y_test, y_pred)\nroc_auc = roc_auc_score(y_test, y_pred_prob)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-30T04:27:14.817695Z","iopub.execute_input":"2025-04-30T04:27:14.818070Z","iopub.status.idle":"2025-04-30T04:27:18.309699Z","shell.execute_reply.started":"2025-04-30T04:27:14.818041Z","shell.execute_reply":"2025-04-30T04:27:18.308754Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(f\"Training Accuracy: {train_acc:.4f}\")\nprint(f\"Validation Accuracy: {val_acc:.4f}\")\nprint(f\"Test Accuracy: {test_acc:.4f}\")\nprint(f\"F1 Score: {f1:.4f}\")\nprint(f\"Cohen's Kappa: {kappa:.4f}\")\nprint(f\"AUC: {roc_auc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-30T04:27:18.310891Z","iopub.execute_input":"2025-04-30T04:27:18.311248Z","iopub.status.idle":"2025-04-30T04:27:18.316701Z","shell.execute_reply.started":"2025-04-30T04:27:18.311219Z","shell.execute_reply":"2025-04-30T04:27:18.315713Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Accuracy/Loss plots\nplt.figure(figsize=(14, 5))\nplt.subplot(1, 2, 1)\nplt.plot(history.history['accuracy'], label='Train Acc')\nplt.plot(history.history['val_accuracy'], label='Val Acc')\nplt.title('Accuracy')\nplt.legend()\n\nplt.subplot(1, 2, 2)\nplt.plot(history.history['loss'], label='Train Loss')\nplt.plot(history.history['val_loss'], label='Val Loss')\nplt.title('Loss')\nplt.legend()\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-30T04:27:18.318063Z","iopub.execute_input":"2025-04-30T04:27:18.318341Z","iopub.status.idle":"2025-04-30T04:27:19.118109Z","shell.execute_reply.started":"2025-04-30T04:27:18.318298Z","shell.execute_reply":"2025-04-30T04:27:19.117279Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ROC Curve\nfpr, tpr, _ = roc_curve(y_test, y_pred_prob)\nroc_val = auc(fpr, tpr)\nplt.figure()\nplt.plot(fpr, tpr, label=f'ROC (AUC = {roc_val:.2f})')\nplt.plot([0, 1], [0, 1], 'k--')\nplt.xlabel('False Positive Rate')\nplt.ylabel('True Positive Rate')\nplt.title('ROC Curve - MIL Model')\nplt.legend()\nplt.show()\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-30T04:27:19.119350Z","iopub.execute_input":"2025-04-30T04:27:19.120028Z","iopub.status.idle":"2025-04-30T04:27:19.362674Z","shell.execute_reply.started":"2025-04-30T04:27:19.119986Z","shell.execute_reply":"2025-04-30T04:27:19.361920Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(12, 8))\nfor i in range(9):\n    p_idx = np.random.randint(0, len(X_train))\n    s_idx = np.random.randint(0, X_train.shape[1])\n    plt.subplot(3, 3, i+1)\n    plt.imshow(X_train[p_idx][s_idx].squeeze(), cmap='gray')\n    plt.title(f\"Label: {y_train[p_idx]}\")\n    plt.axis('off')\nplt.suptitle('Sample Training Slices from MIL Bags')\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-30T04:27:19.363814Z","iopub.execute_input":"2025-04-30T04:27:19.364220Z","iopub.status.idle":"2025-04-30T04:27:20.101299Z","shell.execute_reply.started":"2025-04-30T04:27:19.364176Z","shell.execute_reply":"2025-04-30T04:27:20.100486Z"}},"outputs":[],"execution_count":null}]}