{"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":"gpu","dataSources":[{"sourceId":11848,"databundleVersionId":862157,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"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\n#import numpy as np # linear algebra\n#import 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 numpy as np              # For numerical operations\nimport pandas as pd             # For working with CSVs and DataFrames\nimport matplotlib.pyplot as plt # For plotting\nimport cv2                      # For image processing\nimport seaborn as sns           # For nicer plots\nfrom sklearn.utils import shuffle                # To shuffle datasets\nfrom sklearn.metrics import confusion_matrix     # To evaluate classification results\nfrom sklearn.model_selection import train_test_split # For splitting data into train/test\nimport itertools               # Useful for looping combinations (e.g., confusion matrix plotting)\nimport shutil                  # File operations like copy, move\n\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-25T01:10:40.745124Z","iopub.execute_input":"2025-05-25T01:10:40.745350Z","iopub.status.idle":"2025-05-25T01:10:43.557566Z","shell.execute_reply.started":"2025-05-25T01:10:40.745331Z","shell.execute_reply":"2025-05-25T01:10:43.556804Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\ndata_dir = \"/kaggle/input/histopathologic-cancer-detection\"\n\nprint(os.listdir(data_dir))\n\n# Total Samples Available\nprint('Train Images =', len(os.listdir(os.path.join(data_dir, 'train'))))\nprint('Test Images =', len(os.listdir(os.path.join(data_dir, 'test'))))\n\n# Read train labels CSV\ndf = pd.read_csv(os.path.join(data_dir, 'train_labels.csv'))\nprint('Shape of DataFrame:', df.shape)\ndf.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:11:53.631878Z","iopub.execute_input":"2025-05-25T01:11:53.632167Z","iopub.status.idle":"2025-05-25T01:11:57.827546Z","shell.execute_reply.started":"2025-05-25T01:11:53.632142Z","shell.execute_reply":"2025-05-25T01:11:57.826983Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TRAIN_DIR = '/kaggle/input/histopathologic-cancer-detection/train/'\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:11:59.399741Z","iopub.execute_input":"2025-05-25T01:11:59.400000Z","iopub.status.idle":"2025-05-25T01:11:59.403597Z","shell.execute_reply.started":"2025-05-25T01:11:59.399979Z","shell.execute_reply":"2025-05-25T01:11:59.403046Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig = plt.figure(figsize = (20,8))\nindex = 1\nfor i in np.random.randint(low = 0, high = df.shape[0], size = 10):\n    file = TRAIN_DIR + df.iloc[i]['id'] + '.tif'\n    img = cv2.imread(file)\n    ax = fig.add_subplot(2, 5, index)\n    ax.imshow(img, cmap = 'gray')\n    index = index + 1\n    color = ['green' if df.iloc[i].label == 1 else 'red'][0]\n    ax.set_title(df.iloc[i].label, fontsize = 18, color = color)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:12:01.679736Z","iopub.execute_input":"2025-05-25T01:12:01.680228Z","iopub.status.idle":"2025-05-25T01:12:03.171092Z","shell.execute_reply.started":"2025-05-25T01:12:01.680202Z","shell.execute_reply":"2025-05-25T01:12:03.170271Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Removing problematic images\ndf = df[df['id'] != 'dd6dfed324f9fcb6f93f46f32fc800f2ec196be2']  # corrupted image\ndf = df[df['id'] != '9369c7278ec8bcc6c880d99194de09fc2bd4efbe']  # black image\n\nprint(df.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:12:08.024034Z","iopub.execute_input":"2025-05-25T01:12:08.024863Z","iopub.status.idle":"2025-05-25T01:12:08.086929Z","shell.execute_reply.started":"2025-05-25T01:12:08.024828Z","shell.execute_reply":"2025-05-25T01:12:08.086172Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels_count = df.label.value_counts()\n\nplt.pie(labels_count, labels=['No Cancer', 'Cancer'], startangle=180, \n        autopct='%1.1f', colors=['#00ff99','#FF96A7'], shadow=True)\nplt.figure(figsize=(16,16))\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:12:12.959917Z","iopub.execute_input":"2025-05-25T01:12:12.960498Z","iopub.status.idle":"2025-05-25T01:12:13.067516Z","shell.execute_reply.started":"2025-05-25T01:12:12.960473Z","shell.execute_reply":"2025-05-25T01:12:13.066727Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"SAMPLE_SIZE = 80000\n# take a random sample of class 0 with size equal to num samples in class 1\ndf_0 = df[df['label'] == 0].sample(SAMPLE_SIZE, random_state = 0)\n# filter out class 1\ndf_1 = df[df['label'] == 1].sample(SAMPLE_SIZE, random_state = 0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:12:16.452551Z","iopub.execute_input":"2025-05-25T01:12:16.452826Z","iopub.status.idle":"2025-05-25T01:12:16.483263Z","shell.execute_reply.started":"2025-05-25T01:12:16.452805Z","shell.execute_reply":"2025-05-25T01:12:16.482718Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# concat the dataframes\ndf_train = pd.concat([df_0, df_1], axis = 0).reset_index(drop = True)\n# shuffle\ndf_train = shuffle(df_train)\n\nprint(df_train['label'].value_counts())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:12:24.031565Z","iopub.execute_input":"2025-05-25T01:12:24.032226Z","iopub.status.idle":"2025-05-25T01:12:24.065502Z","shell.execute_reply.started":"2025-05-25T01:12:24.032200Z","shell.execute_reply":"2025-05-25T01:12:24.064756Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nimport os\n\n# Target labels for stratification\ny = df_train['label']\n\n# Stratified split\ndf_train, df_val = train_test_split(df_train, test_size=0.1, random_state=0, stratify=y)\n\n# Update base_dir path for Kaggle writable directory\nbase_dir = '/kaggle/working/base_dir'\ntrain_dir = os.path.join(base_dir, 'train_dir')\nval_dir = os.path.join(base_dir, 'val_dir')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:12:26.627330Z","iopub.execute_input":"2025-05-25T01:12:26.627600Z","iopub.status.idle":"2025-05-25T01:12:26.694951Z","shell.execute_reply.started":"2025-05-25T01:12:26.627579Z","shell.execute_reply":"2025-05-25T01:12:26.694223Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nbase_dir = '/kaggle/working/base_dir'\nos.makedirs(base_dir, exist_ok=True)  # Create base_dir if it doesn't exist\n\ntrain_dir = os.path.join(base_dir, 'train_dir')\nos.makedirs(train_dir, exist_ok=True)\n\nval_dir = os.path.join(base_dir, 'val_dir')\nos.makedirs(val_dir, exist_ok=True)\n\n# Create class subfolders inside train_dir\nos.makedirs(os.path.join(train_dir, '0'), exist_ok=True)\nos.makedirs(os.path.join(train_dir, '1'), exist_ok=True)\n\n# Create class subfolders inside val_dir\nos.makedirs(os.path.join(val_dir, '0'), exist_ok=True)\nos.makedirs(os.path.join(val_dir, '1'), exist_ok=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:12:31.219060Z","iopub.execute_input":"2025-05-25T01:12:31.219326Z","iopub.status.idle":"2025-05-25T01:12:31.225344Z","shell.execute_reply.started":"2025-05-25T01:12:31.219307Z","shell.execute_reply":"2025-05-25T01:12:31.224751Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(os.listdir('/kaggle/working/base_dir/train_dir'))\nprint(os.listdir('/kaggle/working/base_dir/val_dir'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:12:34.758842Z","iopub.execute_input":"2025-05-25T01:12:34.759379Z","iopub.status.idle":"2025-05-25T01:12:34.763637Z","shell.execute_reply.started":"2025-05-25T01:12:34.759358Z","shell.execute_reply":"2025-05-25T01:12:34.762912Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.set_index('id', inplace=True)\n\ntrain_list = list(df_train['id'])\nval_list = list(df_val['id'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:12:37.782985Z","iopub.execute_input":"2025-05-25T01:12:37.783749Z","iopub.status.idle":"2025-05-25T01:12:37.825870Z","shell.execute_reply.started":"2025-05-25T01:12:37.783717Z","shell.execute_reply":"2025-05-25T01:12:37.825060Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data_dir = '/kaggle/input/histopathologic-cancer-detection'\n\nfor image in train_list:\n    file_name = image + '.tif'\n    target = df.loc[image, 'label']\n\n    label = '0' if target == 0 else '1'\n\n    src = os.path.join(data_dir, 'train', file_name)  # Kaggle input path\n    dest = os.path.join(train_dir, label, file_name)  # Kaggle working path\n\n    shutil.copyfile(src, dest)\n\nfor image in val_list:\n    file_name = image + '.tif'\n    target = df.loc[image, 'label']\n\n    label = '0' if target == 0 else '1'\n\n    src = os.path.join(data_dir, 'train', file_name)\n    dest = os.path.join(val_dir, label, file_name)\n\n    shutil.copyfile(src, dest)\n\nprint(len(os.listdir(os.path.join(train_dir, '0'))))\nprint(len(os.listdir(os.path.join(train_dir, '1'))))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:12:40.439655Z","iopub.execute_input":"2025-05-25T01:12:40.440349Z","iopub.status.idle":"2025-05-25T01:29:15.685613Z","shell.execute_reply.started":"2025-05-25T01:12:40.440317Z","shell.execute_reply":"2025-05-25T01:29:15.684790Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.preprocessing.image import ImageDataGenerator\nimport numpy as np\n\nIMAGE_SIZE = 96\n\ntrain_path = '/kaggle/working/base_dir/train_dir'\nvalid_path = '/kaggle/working/base_dir/val_dir'\ntest_path = '/kaggle/input/histopathologic-cancer-detection/test'\n\nnum_train_samples = len(df_train)\nnum_val_samples = len(df_val)\ntrain_batch_size = 32\nval_batch_size = 32\n\ntrain_steps = np.ceil(num_train_samples / train_batch_size)\nval_steps = np.ceil(num_val_samples / val_batch_size)\n\ndatagen = ImageDataGenerator(rescale=1.0/255)\n\ntrain_gen = datagen.flow_from_directory(\n    train_path,\n    target_size=(IMAGE_SIZE, IMAGE_SIZE),\n    batch_size=train_batch_size,\n    class_mode='categorical'\n)\n\nval_gen = datagen.flow_from_directory(\n    valid_path,\n    target_size=(IMAGE_SIZE, IMAGE_SIZE),\n    batch_size=val_batch_size,\n    class_mode='categorical'\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:29:51.664701Z","iopub.execute_input":"2025-05-25T01:29:51.665389Z","iopub.status.idle":"2025-05-25T01:30:05.537498Z","shell.execute_reply.started":"2025-05-25T01:29:51.665365Z","shell.execute_reply":"2025-05-25T01:30:05.536764Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Conv2D, Dropout, MaxPooling2D, Flatten, Dense\nfrom tensorflow.keras.layers import BatchNormalization, SeparableConv2D, Activation","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:30:42.559159Z","iopub.execute_input":"2025-05-25T01:30:42.559888Z","iopub.status.idle":"2025-05-25T01:30:42.566148Z","shell.execute_reply.started":"2025-05-25T01:30:42.559865Z","shell.execute_reply":"2025-05-25T01:30:42.565544Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Net:\n    @staticmethod\n    def build(width, height, depth, classes):\n            \n            #initializa model\n            model = Sequential()\n            \n            inputShape = (height, width, depth)\n            \n            #Add First Layer CONV => ReLU => Pooling\n            model.add(Conv2D(filters = 32, kernel_size = (5,5), padding=\"same\", activation='relu', input_shape= inputShape))\n            model.add(Conv2D(filters = 32, kernel_size = (3,3), padding=\"same\", activation='relu'))\n            model.add(Conv2D(filters = 32, kernel_size = (3,3), padding=\"same\", activation='relu'))\n            model.add(MaxPooling2D(pool_size=(2, 2)))\n            model.add(Dropout(0.2))\n            \n            #Add Second Layer CONV => ReLU => Pooling\n            model.add(Conv2D(filters = 64, kernel_size = (3,3), padding=\"same\", activation='relu'))\n            model.add(Conv2D(filters = 64, kernel_size = (3,3), padding=\"same\", activation='relu'))\n            model.add(Conv2D(filters = 64, kernel_size = (3,3), padding=\"same\", activation='relu'))\n            model.add(MaxPooling2D(pool_size=(2, 2)))\n            model.add(Dropout(0.2))\n            \n            #Add Third Layer CONV => ReLU => Pooling\n            model.add(Conv2D(filters = 128, kernel_size = (3,3), padding=\"same\", activation='relu'))\n            model.add(Conv2D(filters = 128, kernel_size = (3,3), padding=\"same\", activation='relu'))\n            model.add(Conv2D(filters = 128, kernel_size = (3,3), padding=\"same\", activation='relu'))\n            model.add(MaxPooling2D(pool_size=(2, 2)))\n            model.add(Dropout(0.25))\n            \n            #FC => ReLU\n            model.add(Flatten())\n            model.add(Dense(units = 500, activation = 'relu'))\n            model.add(Dropout(0.2))\n            #FC => Output\n            model.add(Dense(classes, activation='softmax'))\n            \n            model.summary()\n            \n            return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:30:48.247957Z","iopub.execute_input":"2025-05-25T01:30:48.248654Z","iopub.status.idle":"2025-05-25T01:30:48.256063Z","shell.execute_reply.started":"2025-05-25T01:30:48.248630Z","shell.execute_reply":"2025-05-25T01:30:48.255329Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = Net.build(width = 96, height = 96, depth = 3, classes = 2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:32:08.644455Z","iopub.execute_input":"2025-05-25T01:32:08.645021Z","iopub.status.idle":"2025-05-25T01:32:11.199368Z","shell.execute_reply.started":"2025-05-25T01:32:08.644987Z","shell.execute_reply":"2025-05-25T01:32:11.198659Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.optimizers import Adam\n\nmodel.compile(optimizer=Adam(learning_rate=0.0001), \n              loss='categorical_crossentropy', \n              metrics=['accuracy'])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:32:19.202603Z","iopub.execute_input":"2025-05-25T01:32:19.202871Z","iopub.status.idle":"2025-05-25T01:32:19.218651Z","shell.execute_reply.started":"2025-05-25T01:32:19.202852Z","shell.execute_reply":"2025-05-25T01:32:19.218130Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau\n\nfilepath = \"checkpoint.h5\"\ncheckpoint = ModelCheckpoint(filepath, monitor='val_accuracy', verbose=1, \n                             save_best_only=True, mode='max')\n\nreduce_lr = ReduceLROnPlateau(monitor='val_accuracy', factor=0.5, patience=2, \n                              verbose=1, mode='max', min_lr=1e-5)\n\ncallbacks_list = [checkpoint, reduce_lr]\n\ntrain_steps = int(np.ceil(num_train_samples / train_batch_size))\nval_steps = int(np.ceil(num_val_samples / val_batch_size))\n\nhistory = model.fit(\n    train_gen,\n    steps_per_epoch=train_steps,\n    validation_data=val_gen,\n    validation_steps=val_steps,\n    epochs=11,\n    verbose=1,\n    callbacks=callbacks_list\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:32:24.140079Z","iopub.execute_input":"2025-05-25T01:32:24.140336Z","iopub.status.idle":"2025-05-25T01:52:10.474756Z","shell.execute_reply.started":"2025-05-25T01:32:24.140316Z","shell.execute_reply":"2025-05-25T01:52:10.474232Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plot training & validation accuracy values\n\nplt.plot(history.history['accuracy'])       # instead of 'acc'\nplt.plot(history.history['val_accuracy'])   # instead of 'val_acc'\nplt.title('Model accuracy')\nplt.ylabel('Accuracy')\nplt.xlabel('Epoch')\nplt.legend(['Train', 'Validation'], loc='best')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:53:15.865190Z","iopub.execute_input":"2025-05-25T01:53:15.865725Z","iopub.status.idle":"2025-05-25T01:53:16.030438Z","shell.execute_reply.started":"2025-05-25T01:53:15.865701Z","shell.execute_reply":"2025-05-25T01:53:16.029897Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plot training & validation loss values\nplt.plot(history.history['loss'])\nplt.plot(history.history['val_loss'])\nplt.title('Model loss')\nplt.ylabel('Loss')\nplt.xlabel('Epoch')\nplt.legend(['Train', 'Test'], loc='best')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:53:23.909212Z","iopub.execute_input":"2025-05-25T01:53:23.909480Z","iopub.status.idle":"2025-05-25T01:53:24.066959Z","shell.execute_reply.started":"2025-05-25T01:53:23.909460Z","shell.execute_reply":"2025-05-25T01:53:24.066435Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.preprocessing.image import ImageDataGenerator\n\nIMAGE_SIZE = 96\nval_batch_size = 32\nvalid_path = '/kaggle/working/base_dir/val_dir'\n\n# Rescale pixel values\ndatagen = ImageDataGenerator(rescale=1.0 / 255)\n\n# Define the validation generator\nval_gen = datagen.flow_from_directory(\n    valid_path,\n    target_size=(IMAGE_SIZE, IMAGE_SIZE),\n    batch_size=val_batch_size,\n    class_mode='categorical',\n    shuffle=False  # IMPORTANT for correct label alignment\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:58:12.974411Z","iopub.execute_input":"2025-05-25T01:58:12.974899Z","iopub.status.idle":"2025-05-25T01:58:13.117858Z","shell.execute_reply.started":"2025-05-25T01:58:12.974873Z","shell.execute_reply":"2025-05-25T01:58:13.117330Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load best weights\nmodel.load_weights('checkpoint.h5')\n\n# Evaluate using the generator\nval_loss, val_acc = model.evaluate(val_gen, steps=val_steps, verbose=1)\nprint('val_loss:', val_loss)\nprint('val_acc:', val_acc)\n\n# Predict using the generator\npredictions = model.predict(val_gen, steps=val_steps, verbose=1)\n\n# Convert predictions to DataFrame\ndf_preds = pd.DataFrame(predictions, columns=['no_tumor', 'has_tumor'])\ndf_preds.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T01:58:29.868413Z","iopub.execute_input":"2025-05-25T01:58:29.868649Z","iopub.status.idle":"2025-05-25T01:58:49.811620Z","shell.execute_reply.started":"2025-05-25T01:58:29.868634Z","shell.execute_reply":"2025-05-25T01:58:49.811047Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# y_true: True labels (0 or 1)\ny_true = val_gen.classes  # This is an array like [0, 1, 0, 0, 1, ...]\n\n# y_pred: Predicted probabilities for class 1 (has_tumor)\n# predictions is of shape (num_samples, 2) from model.predict\ny_pred = predictions[:, 1]  # Probability of 'has_tumor'\n\nfrom sklearn.metrics import roc_auc_score, roc_curve, auc\nimport matplotlib.pyplot as plt\n\n# Compute ROC AUC Score\nroc_auc = roc_auc_score(y_true, y_pred)\nprint('ROC AUC Score =', roc_auc)\n\n# Compute ROC Curve values\nfpr, tpr, thresholds = roc_curve(y_true, y_pred)\nroc_auc_val = auc(fpr, tpr)\n\n# Plot ROC Curve\nplt.figure(figsize=(8, 6))\nplt.plot([0, 1], [0, 1], 'k--')\nplt.plot(fpr, tpr, label=f'AUC = {roc_auc_val:.2f}')\nplt.xlabel('False Positive Rate')\nplt.ylabel('True Positive Rate')\nplt.title('ROC Curve')\nplt.legend(loc='best')\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T02:03:23.213347Z","iopub.execute_input":"2025-05-25T02:03:23.213823Z","iopub.status.idle":"2025-05-25T02:03:23.396137Z","shell.execute_reply.started":"2025-05-25T02:03:23.213803Z","shell.execute_reply":"2025-05-25T02:03:23.395354Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix, classification_report\nfrom mlxtend.plotting import plot_confusion_matrix\nimport matplotlib.pyplot as plt\n\ny_pred_binary = predictions.argmax(axis=1)\ncm = confusion_matrix(y_true, y_pred_binary)\n\nfig, ax = plot_confusion_matrix(conf_mat=cm,\n                                show_absolute=True,\n                                show_normed=True,\n                                colorbar=True,\n                                cmap='Dark2')\nplt.show()\n\nreport = classification_report(y_true, y_pred_binary, target_names=['no_tumor', 'has_tumor'])\nprint(report)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-25T02:01:00.545091Z","iopub.execute_input":"2025-05-25T02:01:00.545734Z","iopub.status.idle":"2025-05-25T02:01:00.864542Z","shell.execute_reply.started":"2025-05-25T02:01:00.545712Z","shell.execute_reply":"2025-05-25T02:01:00.863957Z"}},"outputs":[],"execution_count":null}]}