{"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"},{"sourceId":12292262,"sourceType":"datasetVersion","datasetId":7747338}],"dockerImageVersionId":31040,"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-06-26T11:24:06.867807Z","iopub.execute_input":"2025-06-26T11:24:06.868148Z","iopub.status.idle":"2025-06-26T11:24:36.655843Z","shell.execute_reply.started":"2025-06-26T11:24:06.868116Z","shell.execute_reply":"2025-06-26T11:24:36.654324Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install -q imagecodecs","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:36.657703Z","iopub.execute_input":"2025-06-26T11:24:36.658134Z","iopub.status.idle":"2025-06-26T11:24:44.368271Z","shell.execute_reply.started":"2025-06-26T11:24:36.658108Z","shell.execute_reply":"2025-06-26T11:24:44.367043Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\n# There are two ways to load the data from the PANDA dataset:\n# Option 1: Load images using openslide\nimport openslide\n# Option 2: Load images using skimage (requires that tifffile is installed)\nimport skimage.io\nimport random\nimport seaborn as sns\nimport cv2\n\n# General packages\nimport pandas as pd\nimport numpy as np\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport PIL\nfrom IPython.display import Image, display\n\n# Plotly for the interactive viewer (see last section)\nimport plotly.graph_objs as go","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:44.369784Z","iopub.execute_input":"2025-06-26T11:24:44.370187Z","iopub.status.idle":"2025-06-26T11:24:46.168656Z","shell.execute_reply.started":"2025-06-26T11:24:44.370143Z","shell.execute_reply":"2025-06-26T11:24:46.16743Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Location of the training images\n\nBASE_PATH = '../input/prostate-cancer-grade-assessment'\n\n# image and mask directories\ndata_dir = f'{BASE_PATH}/train_images'\nmask_dir = f'{BASE_PATH}/train_label_masks'\n\n\n# Location of training labels\ntrain = pd.read_csv(f'{BASE_PATH}/train.csv').set_index('image_id')\ntest = pd.read_csv(f'{BASE_PATH}/test.csv')\nsubmission = pd.read_csv(f'{BASE_PATH}/sample_submission.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:46.170105Z","iopub.execute_input":"2025-06-26T11:24:46.170721Z","iopub.status.idle":"2025-06-26T11:24:46.233372Z","shell.execute_reply.started":"2025-06-26T11:24:46.170683Z","shell.execute_reply":"2025-06-26T11:24:46.232323Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"display(train.head())\nprint(\"Shape of training data :\", train.shape)\nprint(\"unique data provider :\", len(train.data_provider.unique()))\nprint(\"unique isup_grade(target) :\", len(train.isup_grade.unique()))\nprint(\"unique gleason_score :\", len(train.gleason_score.unique()))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:46.234549Z","iopub.execute_input":"2025-06-26T11:24:46.234872Z","iopub.status.idle":"2025-06-26T11:24:46.26755Z","shell.execute_reply.started":"2025-06-26T11:24:46.234843Z","shell.execute_reply":"2025-06-26T11:24:46.26657Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.isna().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:46.268478Z","iopub.execute_input":"2025-06-26T11:24:46.268778Z","iopub.status.idle":"2025-06-26T11:24:46.277912Z","shell.execute_reply.started":"2025-06-26T11:24:46.268749Z","shell.execute_reply":"2025-06-26T11:24:46.276884Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"display(test.head())\nprint(\"Shape of training data :\", test.shape)\nprint(\"unique data provider :\", len(test.data_provider.unique()))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:46.279139Z","iopub.execute_input":"2025-06-26T11:24:46.279558Z","iopub.status.idle":"2025-06-26T11:24:46.301063Z","shell.execute_reply.started":"2025-06-26T11:24:46.279508Z","shell.execute_reply":"2025-06-26T11:24:46.300019Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\n\ndef plot_count(df, feature, title='', size=2):\n    f, ax = plt.subplots(1, 1, figsize=(4 * size, 3 * size))\n    total = float(len(df))\n    \n    # Fix: pass feature name as x=... and df as data\n    sns.countplot(x=feature, data=df, order=df[feature].value_counts().index, palette='Set2', ax=ax)\n    \n    plt.title(title)\n    \n    # Loop through bars to add text\n    for p in ax.patches:\n        height = p.get_height()\n        ax.text(p.get_x() + p.get_width() / 2.,\n                height + 3,\n                '{:1.2f}%'.format(100 * height / total),\n                ha=\"center\")\n    \n    plt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:46.304607Z","iopub.execute_input":"2025-06-26T11:24:46.305351Z","iopub.status.idle":"2025-06-26T11:24:46.318962Z","shell.execute_reply.started":"2025-06-26T11:24:46.305325Z","shell.execute_reply":"2025-06-26T11:24:46.317972Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plot_count(df=train, feature='data_provider', title='Data Provider Count and % Plot')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:46.319972Z","iopub.execute_input":"2025-06-26T11:24:46.320242Z","iopub.status.idle":"2025-06-26T11:24:46.565958Z","shell.execute_reply.started":"2025-06-26T11:24:46.320222Z","shell.execute_reply":"2025-06-26T11:24:46.564868Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plot_count(df=train, feature='isup_grade', title='isup_grade count and %age plot')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:46.56694Z","iopub.execute_input":"2025-06-26T11:24:46.567212Z","iopub.status.idle":"2025-06-26T11:24:46.756999Z","shell.execute_reply.started":"2025-06-26T11:24:46.567191Z","shell.execute_reply":"2025-06-26T11:24:46.75603Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plot_count(df=train, feature='gleason_score', title = 'gleason_score count and %age plot', size=3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:46.758379Z","iopub.execute_input":"2025-06-26T11:24:46.758835Z","iopub.status.idle":"2025-06-26T11:24:47.00848Z","shell.execute_reply.started":"2025-06-26T11:24:46.758795Z","shell.execute_reply":"2025-06-26T11:24:47.007449Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_relative_distribution(df, feature, hue, title='', size=2):\n    f, ax = plt.subplots(1,1, figsize=(4*size,3*size))\n    total = float(len(df))\n    sns.countplot(x=feature, hue=hue, data=df, palette='Set2')\n    plt.title(title)\n    for p in ax.patches:\n        height = p.get_height()\n        ax.text(p.get_x()+p.get_width()/2.,\n                height + 3,\n                '{:1.2f}%'.format(100*height/total),\n                ha=\"center\") \n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:47.009639Z","iopub.execute_input":"2025-06-26T11:24:47.009887Z","iopub.status.idle":"2025-06-26T11:24:47.016415Z","shell.execute_reply.started":"2025-06-26T11:24:47.009869Z","shell.execute_reply":"2025-06-26T11:24:47.01548Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plot_relative_distribution(df=train, feature='isup_grade', hue='data_provider', title = 'relative count plot of isup_grade with data_provider', size=2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:47.017464Z","iopub.execute_input":"2025-06-26T11:24:47.017866Z","iopub.status.idle":"2025-06-26T11:24:47.297074Z","shell.execute_reply.started":"2025-06-26T11:24:47.017844Z","shell.execute_reply":"2025-06-26T11:24:47.295949Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plot_relative_distribution(df=train, feature='gleason_score', hue='data_provider', title = 'relative count plot of gleason_score with data_provider', size=3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:47.298248Z","iopub.execute_input":"2025-06-26T11:24:47.298621Z","iopub.status.idle":"2025-06-26T11:24:47.664469Z","shell.execute_reply.started":"2025-06-26T11:24:47.298587Z","shell.execute_reply":"2025-06-26T11:24:47.663468Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plot_relative_distribution(df=train, feature='isup_grade', hue='gleason_score', title = 'relative count plot of isup_grade with gleason_score', size=3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:47.665602Z","iopub.execute_input":"2025-06-26T11:24:47.66649Z","iopub.status.idle":"2025-06-26T11:24:48.289265Z","shell.execute_reply.started":"2025-06-26T11:24:47.666464Z","shell.execute_reply":"2025-06-26T11:24:48.288046Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def display_images(slides): \n    f, ax = plt.subplots(5,3, figsize=(18,22))\n    for i, slide in enumerate(slides):\n        image = openslide.OpenSlide(os.path.join(data_dir, f'{slide}.tiff'))\n        spacing = 1 / (float(image.properties['tiff.XResolution']) / 10000)\n        patch = image.read_region((1780,1950), 0, (256, 256))\n        ax[i//3, i%3].imshow(patch) \n        image.close()       \n        ax[i//3, i%3].axis('off')\n        \n        image_id = slide\n        data_provider = train.loc[slide, 'data_provider']\n        isup_grade = train.loc[slide, 'isup_grade']\n        gleason_score = train.loc[slide, 'gleason_score']\n        ax[i//3, i%3].set_title(f\"ID: {image_id}\\nSource: {data_provider} ISUP: {isup_grade} Gleason: {gleason_score}\")\n\n    plt.show() \n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:48.290587Z","iopub.execute_input":"2025-06-26T11:24:48.290896Z","iopub.status.idle":"2025-06-26T11:24:48.298599Z","shell.execute_reply.started":"2025-06-26T11:24:48.290874Z","shell.execute_reply":"2025-06-26T11:24:48.297442Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"images = [\n    '07a7ef0ba3bb0d6564a73f4f3e1c2293',\n    '037504061b9fba71ef6e24c48c6df44d',\n    '035b1edd3d1aeeffc77ce5d248a01a53',\n    '059cbf902c5e42972587c8d17d49efed',\n    '06a0cbd8fd6320ef1aa6f19342af2e68',\n    '06eda4a6faca84e84a781fee2d5f47e1',\n    '0a4b7a7499ed55c71033cefb0765e93d',\n    '0838c82917cd9af681df249264d2769c',\n    '046b35ae95374bfb48cdca8d7c83233f',\n    '074c3e01525681a275a42282cd21cbde',\n    '05abe25c883d508ecc15b6e857e59f32',\n    '05f4e9415af9fdabc19109c980daf5ad',\n    '060121a06476ef401d8a21d6567dee6d',\n    '068b0e3be4c35ea983f77accf8351cc8',\n    '08f055372c7b8a7e1df97c6586542ac8'\n]\n\ndisplay_images(images)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:48.300008Z","iopub.execute_input":"2025-06-26T11:24:48.300349Z","iopub.status.idle":"2025-06-26T11:24:51.379173Z","shell.execute_reply.started":"2025-06-26T11:24:48.300317Z","shell.execute_reply":"2025-06-26T11:24:51.377758Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def display_masks(slides): \n    f, ax = plt.subplots(5,3, figsize=(18,22))\n    for i, slide in enumerate(slides):\n        \n        mask = openslide.OpenSlide(os.path.join(mask_dir, f'{slide}_mask.tiff'))\n        mask_data = mask.read_region((0,0), mask.level_count - 1, mask.level_dimensions[-1])\n        cmap = matplotlib.colors.ListedColormap(['black', 'gray', 'green', 'yellow', 'orange', 'red'])\n\n        ax[i//3, i%3].imshow(np.asarray(mask_data)[:,:,0], cmap=cmap, interpolation='nearest', vmin=0, vmax=5) \n        mask.close()       \n        ax[i//3, i%3].axis('off')\n        \n        image_id = slide\n        data_provider = train.loc[slide, 'data_provider']\n        isup_grade = train.loc[slide, 'isup_grade']\n        gleason_score = train.loc[slide, 'gleason_score']\n        ax[i//3, i%3].set_title(f\"ID: {image_id}\\nSource: {data_provider} ISUP: {isup_grade} Gleason: {gleason_score}\")\n        f.tight_layout()\n        \n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:51.38077Z","iopub.execute_input":"2025-06-26T11:24:51.381225Z","iopub.status.idle":"2025-06-26T11:24:51.394134Z","shell.execute_reply.started":"2025-06-26T11:24:51.381186Z","shell.execute_reply":"2025-06-26T11:24:51.393044Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"display_masks(images)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:51.395117Z","iopub.execute_input":"2025-06-26T11:24:51.395424Z","iopub.status.idle":"2025-06-26T11:24:56.951887Z","shell.execute_reply.started":"2025-06-26T11:24:51.3954Z","shell.execute_reply":"2025-06-26T11:24:56.950626Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data_providers = ['karolinska', 'radboud']\ntrain_df = pd.read_csv(f'{BASE_PATH}/train.csv')\nmasks = os.listdir(mask_dir)\nmasks_df = pd.Series(masks).to_frame()\nmasks_df.columns = ['mask_file_name']\nmasks_df['image_id'] = masks_df.mask_file_name.apply(lambda x: x.split('_')[0])\ntrain_df = pd.merge(train_df, masks_df, on='image_id', how='outer')\ndel masks_df\nprint(f\"There are {len(train_df[train_df.mask_file_name.isna()])} images without a mask.\")\n\n## removing items where image mask is null\ntrain_df = train_df[~train_df.mask_file_name.isna()]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:56.953373Z","iopub.execute_input":"2025-06-26T11:24:56.953717Z","iopub.status.idle":"2025-06-26T11:24:57.010917Z","shell.execute_reply.started":"2025-06-26T11:24:56.953692Z","shell.execute_reply":"2025-06-26T11:24:57.009727Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_and_resize_image(img_id):\n    \"\"\"\n    Edited from https://www.kaggle.com/xhlulu/panda-resize-and-save-train-data\n    \"\"\"\n    biopsy = skimage.io.MultiImage(os.path.join(data_dir, f'{img_id}.tiff'))\n    return cv2.resize(biopsy[-1], (512, 512))\n\ndef load_and_resize_mask(img_id):\n    \"\"\"\n    Edited from https://www.kaggle.com/xhlulu/panda-resize-and-save-train-data\n    \"\"\"\n    biopsy = skimage.io.MultiImage(os.path.join(mask_dir, f'{img_id}_mask.tiff'))\n    return cv2.resize(biopsy[-1], (512, 512))[:,:,0]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:57.011901Z","iopub.execute_input":"2025-06-26T11:24:57.012144Z","iopub.status.idle":"2025-06-26T11:24:57.019026Z","shell.execute_reply.started":"2025-06-26T11:24:57.012127Z","shell.execute_reply":"2025-06-26T11:24:57.018082Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install imagecodecs","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:24:57.020055Z","iopub.execute_input":"2025-06-26T11:24:57.020292Z","iopub.status.idle":"2025-06-26T11:25:00.925116Z","shell.execute_reply.started":"2025-06-26T11:24:57.020274Z","shell.execute_reply":"2025-06-26T11:25:00.923898Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels = []\nfor grade in range(train.isup_grade.nunique()):\n    fig, ax = plt.subplots(nrows=4, ncols=4, figsize=(22, 22))\n\n    for i, row in enumerate(ax):\n        idx = i // 2\n        temp = train_df[\n            (train_df.isup_grade == grade) & \n            (train_df.data_provider == data_providers[idx])\n        ].image_id.head(4).reset_index(drop=True)\n\n        if i % 2 < 1:\n            labels.append(f'{data_providers[idx]} (image)')\n            for j, col in enumerate(row):\n                col.imshow(load_and_resize_image(temp[j]))\n                col.set_title(f\"ID: {temp[j]}\")\n        else:\n            labels.append(f'{data_providers[idx]} (mask)')\n            for j, col in enumerate(row):\n                cmap_vals = ['white', 'green', 'red']\n                if data_providers[idx] == 'radboud':\n                    cmap_vals = ['white', 'lightgrey', 'green', 'orange', 'red', 'darkred']\n\n                col.imshow(load_and_resize_mask(temp[j]),\n                           cmap=matplotlib.colors.ListedColormap(cmap_vals),\n                           norm=matplotlib.colors.Normalize(vmin=0, vmax=len(cmap_vals)-1, clip=True))\n                col.set_title(f\"ID: {temp[j]}\")\n\n   #Fixed here: remove `size` and keep only `fontsize`\n    for row, r in zip(ax[:, 0], labels):\n        row.set_ylabel(r, rotation=90, fontsize=14)\n\n    plt.suptitle(f'ISUP Grade {grade}', fontsize=20)\n    plt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:25:00.926808Z","iopub.execute_input":"2025-06-26T11:25:00.927214Z","iopub.status.idle":"2025-06-26T11:28:34.798585Z","shell.execute_reply.started":"2025-06-26T11:25:00.927171Z","shell.execute_reply":"2025-06-26T11:28:34.797606Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def overlay_mask_on_slide(images, center='radboud', alpha=0.8, max_size=(800, 800)):\n    \"\"\"Show a mask overlayed on a slide.\"\"\"\n    f, ax = plt.subplots(5,3, figsize=(18,22))\n    \n    \n    for i, image_id in enumerate(images):\n        slide = openslide.OpenSlide(os.path.join(data_dir, f'{image_id}.tiff'))\n        mask = openslide.OpenSlide(os.path.join(mask_dir, f'{image_id}_mask.tiff'))\n        slide_data = slide.read_region((0,0), slide.level_count - 1, slide.level_dimensions[-1])\n        mask_data = mask.read_region((0,0), mask.level_count - 1, mask.level_dimensions[-1])\n        mask_data = mask_data.split()[0]\n        \n        \n        # Create alpha mask\n        alpha_int = int(round(255*alpha))\n        if center == 'radboud':\n            alpha_content = np.less(mask_data.split()[0], 2).astype('uint8') * alpha_int + (255 - alpha_int)\n        elif center == 'karolinska':\n            alpha_content = np.less(mask_data.split()[0], 1).astype('uint8') * alpha_int + (255 - alpha_int)\n\n        alpha_content = PIL.Image.fromarray(alpha_content)\n        preview_palette = np.zeros(shape=768, dtype=int)\n\n        if center == 'radboud':\n            # Mapping: {0: background, 1: stroma, 2: benign epithelium, 3: Gleason 3, 4: Gleason 4, 5: Gleason 5}\n            preview_palette[0:18] = (np.array([0, 0, 0, 0.5, 0.5, 0.5, 0, 1, 0, 1, 1, 0.7, 1, 0.5, 0, 1, 0, 0]) * 255).astype(int)\n        elif center == 'karolinska':\n            # Mapping: {0: background, 1: benign, 2: cancer}\n            preview_palette[0:9] = (np.array([0, 0, 0, 0, 1, 0, 1, 0, 0]) * 255).astype(int)\n\n        mask_data.putpalette(data=preview_palette.tolist())\n        mask_rgb = mask_data.convert(mode='RGB')\n        overlayed_image = PIL.Image.composite(image1=slide_data, image2=mask_rgb, mask=alpha_content)\n        overlayed_image.thumbnail(size=max_size, resample=0)\n\n        \n        ax[i//3, i%3].imshow(overlayed_image) \n        slide.close()\n        mask.close()       \n        ax[i//3, i%3].axis('off')\n        \n        data_provider = train.loc[image_id, 'data_provider']\n        isup_grade = train.loc[image_id, 'isup_grade']\n        gleason_score = train.loc[image_id, 'gleason_score']\n        ax[i//3, i%3].set_title(f\"ID: {image_id}\\nSource: {data_provider} ISUP: {isup_grade} Gleason: {gleason_score}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:28:34.799786Z","iopub.execute_input":"2025-06-26T11:28:34.80036Z","iopub.status.idle":"2025-06-26T11:28:34.819206Z","shell.execute_reply.started":"2025-06-26T11:28:34.80033Z","shell.execute_reply":"2025-06-26T11:28:34.818109Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"overlay_mask_on_slide(images)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:28:34.820237Z","iopub.execute_input":"2025-06-26T11:28:34.820545Z","iopub.status.idle":"2025-06-26T11:28:39.140465Z","shell.execute_reply.started":"2025-06-26T11:28:34.820494Z","shell.execute_reply":"2025-06-26T11:28:39.139355Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pen_marked_images = [\n    'fd6fe1a3985b17d067f2cb4d5bc1e6e1',\n    'ebb6a080d72e09f6481721ef9f88c472',\n    'ebb6d5ca45942536f78beb451ee43cc4',\n    'ea9d52d65500acc9b9d89eb6b82cdcdf',\n    'e726a8eac36c3d91c3c4f9edba8ba713',\n    'e90abe191f61b6fed6d6781c8305fe4b',\n    'fd0bb45eba479a7f7d953f41d574bf9f',\n    'ff10f937c3d52eff6ad4dd733f2bc3ac',\n    'feee2e895355a921f2b75b54debad328',\n    'feac91652a1c5accff08217d19116f1c',\n    'fb01a0a69517bb47d7f4699b6217f69d',\n    'f00ec753b5618cfb30519db0947fe724',\n    'e9a4f528b33479412ee019e155e1a197',\n    'f062f6c1128e0e9d51a76747d9018849',\n    'f39bf22d9a2f313425ee201932bac91a',\n]\n\noverlay_mask_on_slide(pen_marked_images)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:28:39.146619Z","iopub.execute_input":"2025-06-26T11:28:39.146963Z","iopub.status.idle":"2025-06-26T11:28:43.56595Z","shell.execute_reply.started":"2025-06-26T11:28:39.146939Z","shell.execute_reply":"2025-06-26T11:28:43.564862Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## refer: https://www.kaggle.com/c/prostate-cancer-grade-assessment/discussion/145182\n\nimport random\nrandom.seed(42)\n\n\nresults = np.random.randint(0,6,len(submission))\nsubmission['isup_grade'] = results\nsubmission.to_csv('submission.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:28:43.566989Z","iopub.execute_input":"2025-06-26T11:28:43.567289Z","iopub.status.idle":"2025-06-26T11:28:43.582835Z","shell.execute_reply.started":"2025-06-26T11:28:43.567266Z","shell.execute_reply":"2025-06-26T11:28:43.581846Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport openslide\nimport cv2\nfrom tqdm import tqdm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:28:43.583861Z","iopub.execute_input":"2025-06-26T11:28:43.584219Z","iopub.status.idle":"2025-06-26T11:28:43.603261Z","shell.execute_reply.started":"2025-06-26T11:28:43.584185Z","shell.execute_reply":"2025-06-26T11:28:43.602028Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Constants and paths\nDATA_DIR = '/kaggle/input/prostate-cancer-grade-assessment'\nIMAGE_DIR = os.path.join(DATA_DIR, 'train_images')\nPATCH_SAVE_DIR = './tiles'\nPATCH_SIZE = 256\nSTRIDE = 256\nTISSUE_THRESHOLD = 0.5  # keep patches with >50% tissue\n\n# Load train metadata\ndf = pd.read_csv(os.path.join(DATA_DIR, 'train.csv'))\n\n# Create save directory\nos.makedirs(PATCH_SAVE_DIR, exist_ok=True)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:28:43.604186Z","iopub.execute_input":"2025-06-26T11:28:43.60455Z","iopub.status.idle":"2025-06-26T11:28:43.627481Z","shell.execute_reply.started":"2025-06-26T11:28:43.604492Z","shell.execute_reply":"2025-06-26T11:28:43.626759Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def is_tissue(patch, threshold=0.5):\n    \"\"\"Returns True if tissue ratio > threshold in the patch\"\"\"\n    gray = cv2.cvtColor(patch, cv2.COLOR_RGB2GRAY)\n    _, tissue_mask = cv2.threshold(gray, 220, 255, cv2.THRESH_BINARY_INV)\n    tissue_ratio = np.count_nonzero(tissue_mask) / tissue_mask.size\n    return tissue_ratio > threshold","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:28:43.628433Z","iopub.execute_input":"2025-06-26T11:28:43.628744Z","iopub.status.idle":"2025-06-26T11:28:43.634452Z","shell.execute_reply.started":"2025-06-26T11:28:43.628714Z","shell.execute_reply":"2025-06-26T11:28:43.633414Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tile_info = []\n\nfor _, row in tqdm(df.iterrows(), total=len(df)):\n    image_id = row['image_id']\n    isup = row['isup_grade']\n    gleason = row['gleason_score']\n    path = os.path.join(IMAGE_DIR, f'{image_id}.tiff')\n\n    try:\n        slide = openslide.OpenSlide(path)\n    except:\n        print(f\"Could not open {image_id}\")\n        continue\n\n    # Use a lower resolution for faster processing\n    slide_level = slide.level_count - 1\n    dims = slide.level_dimensions[slide_level]\n\n    slide_img = slide.read_region((0, 0), slide_level, dims).convert(\"RGB\")\n    slide_img = np.array(slide_img)\n\n    h, w, _ = slide_img.shape\n\n    for y in range(0, h - PATCH_SIZE, STRIDE):\n        for x in range(0, w - PATCH_SIZE, STRIDE):\n            patch = slide_img[y:y+PATCH_SIZE, x:x+PATCH_SIZE]\n\n            if patch.shape[0] != PATCH_SIZE or patch.shape[1] != PATCH_SIZE:\n                continue\n\n            if is_tissue(patch, threshold=TISSUE_THRESHOLD):\n                patch_filename = f'{image_id}_{x}_{y}.png'\n                patch_path = os.path.join(PATCH_SAVE_DIR, patch_filename)\n                cv2.imwrite(patch_path, cv2.cvtColor(patch, cv2.COLOR_RGB2BGR))\n\n                tile_info.append({\n                    'tile_path': patch_path,\n                    'image_id': image_id,\n                    'x': x,\n                    'y': y,\n                    'isup_grade': isup,\n                    'gleason_score': gleason\n                })\n\n    slide.close()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:28:43.635676Z","iopub.execute_input":"2025-06-26T11:28:43.636092Z","iopub.status.idle":"2025-06-26T11:57:02.120846Z","shell.execute_reply.started":"2025-06-26T11:28:43.636029Z","shell.execute_reply":"2025-06-26T11:57:02.119713Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tile_df = pd.DataFrame(tile_info)\ntile_df.to_csv('tile_metadata.csv', index=False)\nprint(f\"Extracted {len(tile_df)} tiles and saved metadata to tile_metadata.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:57:02.122278Z","iopub.execute_input":"2025-06-26T11:57:02.122642Z","iopub.status.idle":"2025-06-26T11:57:02.1353Z","shell.execute_reply.started":"2025-06-26T11:57:02.122609Z","shell.execute_reply":"2025-06-26T11:57:02.134048Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nfrom torch.utils.data import Dataset, DataLoader\nimport torchvision.transforms as transforms\nfrom PIL import Image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:57:02.13634Z","iopub.execute_input":"2025-06-26T11:57:02.136652Z","iopub.status.idle":"2025-06-26T11:57:06.397958Z","shell.execute_reply.started":"2025-06-26T11:57:02.136621Z","shell.execute_reply":"2025-06-26T11:57:06.39694Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class TileDataset(Dataset):\n    def __init__(self, csv_file, transform=None):\n        self.df = pd.read_csv(csv_file)\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        img_path = row['tile_path']\n        label = int(row['isup_grade'])\n\n        # Load image\n        image = Image.open(img_path).convert('RGB')\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:57:06.399085Z","iopub.execute_input":"2025-06-26T11:57:06.399569Z","iopub.status.idle":"2025-06-26T11:57:06.406021Z","shell.execute_reply.started":"2025-06-26T11:57:06.399543Z","shell.execute_reply":"2025-06-26T11:57:06.40499Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Transformations for training\ntrain_transforms = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomVerticalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.5]*3, std=[0.5]*3)\n])\n\n# Dataset\ndataset = TileDataset('tile_metadata.csv', transform=train_transforms)\n\n# Dataloader\ndataloader = DataLoader(dataset, batch_size=16, shuffle=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:57:06.406963Z","iopub.execute_input":"2025-06-26T11:57:06.407317Z","iopub.status.idle":"2025-06-26T11:57:06.435121Z","shell.execute_reply.started":"2025-06-26T11:57:06.407293Z","shell.execute_reply":"2025-06-26T11:57:06.4339Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torchvision\nimport matplotlib.pyplot as plt\n\ndef show_batch(dl):\n    images, labels = next(iter(dl))\n    grid = torchvision.utils.make_grid(images, nrow=4)\n    plt.figure(figsize=(12, 8))\n    plt.imshow(grid.permute(1, 2, 0).numpy() * 0.5 + 0.5)\n    plt.title([str(label.item()) for label in labels])\n    plt.axis('off')\n    plt.show()\n\nshow_batch(dataloader)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:57:06.436183Z","iopub.execute_input":"2025-06-26T11:57:06.436432Z","iopub.status.idle":"2025-06-26T11:57:07.019904Z","shell.execute_reply.started":"2025-06-26T11:57:06.436414Z","shell.execute_reply":"2025-06-26T11:57:07.018832Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torchvision.models as models\nfrom torchvision.models import ResNet18_Weights\n\n# Load pretrained ResNet18 with correct weights syntax\nmodel = models.resnet18(weights=ResNet18_Weights.IMAGENET1K_V1)\n\n# Replace final FC layer for 6 ISUP grade classes\nmodel.fc = nn.Linear(model.fc.in_features, 6)\n\n# Use GPU if available\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel = model.to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:57:07.020949Z","iopub.execute_input":"2025-06-26T11:57:07.021316Z","iopub.status.idle":"2025-06-26T11:57:07.272425Z","shell.execute_reply.started":"2025-06-26T11:57:07.021291Z","shell.execute_reply":"2025-06-26T11:57:07.271291Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch.optim as optim\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=1e-4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:57:07.273424Z","iopub.execute_input":"2025-06-26T11:57:07.273726Z","iopub.status.idle":"2025-06-26T11:57:07.280374Z","shell.execute_reply.started":"2025-06-26T11:57:07.273705Z","shell.execute_reply":"2025-06-26T11:57:07.279396Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_model(model, dataloader, criterion, optimizer, epochs=5):\n    model.train()\n\n    for epoch in range(epochs):\n        total_loss = 0\n        correct = 0\n        total = 0\n\n        for images, labels in dataloader:\n            images, labels = images.to(device), labels.to(device)\n\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n\n            optimizer.zero_grad()\n            loss.backward()\n            optimizer.step()\n\n            total_loss += loss.item()\n\n            _, preds = torch.max(outputs, 1)\n            correct += (preds == labels).sum().item()\n            total += labels.size(0)\n\n        acc = 100 * correct / total\n        print(f\"Epoch {epoch+1}/{epochs} - Loss: {total_loss:.4f} - Accuracy: {acc:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:57:07.281452Z","iopub.execute_input":"2025-06-26T11:57:07.281878Z","iopub.status.idle":"2025-06-26T11:57:07.303294Z","shell.execute_reply.started":"2025-06-26T11:57:07.281843Z","shell.execute_reply":"2025-06-26T11:57:07.302247Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_model(model, dataloader, criterion, optimizer, epochs=5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T11:57:07.304556Z","iopub.execute_input":"2025-06-26T11:57:07.304932Z","iopub.status.idle":"2025-06-26T11:59:55.601589Z","shell.execute_reply.started":"2025-06-26T11:57:07.304902Z","shell.execute_reply":"2025-06-26T11:59:55.600192Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Used train test split ","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\n# Load tile metadata\ntile_df = pd.read_csv('tile_metadata.csv')\n\n# Split\ntrain_df, val_df = train_test_split(tile_df, test_size=0.2, stratify=tile_df['isup_grade'], random_state=42)\n\n# Save to separate CSVs\ntrain_df.to_csv('tile_train.csv', index=False)\nval_df.to_csv('tile_val.csv', index=False)\n\nprint(f\"Train: {len(train_df)} tiles, Validation: {len(val_df)} tiles\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T12:00:13.839356Z","iopub.execute_input":"2025-06-26T12:00:13.839743Z","iopub.status.idle":"2025-06-26T12:00:14.164731Z","shell.execute_reply.started":"2025-06-26T12:00:13.839712Z","shell.execute_reply":"2025-06-26T12:00:14.163552Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class TileDataset(Dataset):\n    def __init__(self, csv_file, transform=None):\n        self.df = pd.read_csv(csv_file)\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        img_path = row['tile_path']\n        label = int(row['isup_grade'])\n\n        image = Image.open(img_path).convert('RGB')\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T12:01:10.238933Z","iopub.execute_input":"2025-06-26T12:01:10.239347Z","iopub.status.idle":"2025-06-26T12:01:10.246286Z","shell.execute_reply.started":"2025-06-26T12:01:10.239321Z","shell.execute_reply":"2025-06-26T12:01:10.245424Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Transforms\ntrain_transforms = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.RandomHorizontalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.5]*3, std=[0.5]*3)\n])\n\nval_transforms = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.5]*3, std=[0.5]*3)\n])\n\n# Datasets\ntrain_dataset = TileDataset('tile_train.csv', transform=train_transforms)\nval_dataset = TileDataset('tile_val.csv', transform=val_transforms)\n\n# Dataloaders\ntrain_loader = DataLoader(train_dataset, batch_size=16, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=16, shuffle=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T12:01:18.646771Z","iopub.execute_input":"2025-06-26T12:01:18.647245Z","iopub.status.idle":"2025-06-26T12:01:18.66177Z","shell.execute_reply.started":"2025-06-26T12:01:18.647213Z","shell.execute_reply":"2025-06-26T12:01:18.660631Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_model(model, train_loader, val_loader, criterion, optimizer, scheduler=None, epochs=5):\n    model.train()\n    best_val_acc = 0.0\n\n    for epoch in range(epochs):\n        total_loss = 0\n        correct = 0\n        total = 0\n\n        for images, labels in train_loader:\n            images, labels = images.to(device), labels.to(device)\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n\n            optimizer.zero_grad()\n            loss.backward()\n            optimizer.step()\n\n            total_loss += loss.item()\n            _, preds = torch.max(outputs, 1)\n            correct += (preds == labels).sum().item()\n            total += labels.size(0)\n\n        train_acc = 100 * correct / total\n        val_acc = evaluate_model(model, val_loader)\n\n        if val_acc > best_val_acc:\n            best_val_acc = val_acc\n            torch.save(model.state_dict(), \"best_model.pth\")\n            print(f\"Best model saved at epoch {epoch+1}\")\n\n        if scheduler:\n            scheduler.step()\n\n        print(f\"Epoch {epoch+1}: Loss = {total_loss:.4f} | Train Acc = {train_acc:.2f}% | Val Acc = {val_acc:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T12:43:08.090617Z","iopub.execute_input":"2025-06-26T12:43:08.091076Z","iopub.status.idle":"2025-06-26T12:43:08.100653Z","shell.execute_reply.started":"2025-06-26T12:43:08.091021Z","shell.execute_reply":"2025-06-26T12:43:08.099709Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import classification_report, confusion_matrix, f1_score\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\ndef evaluate_model(model, dataloader):\n    model.eval()\n    correct = 0\n    total = 0\n    all_preds = []\n    all_labels = []\n\n    with torch.no_grad():\n        for images, labels in dataloader:\n            images, labels = images.to(device), labels.to(device)\n            outputs = model(images)\n            _, preds = torch.max(outputs, 1)\n            correct += (preds == labels).sum().item()\n            total += labels.size(0)\n\n            all_preds.extend(preds.cpu().numpy())\n            all_labels.extend(labels.cpu().numpy())\n\n    acc = 100 * correct / total\n    f1 = f1_score(all_labels, all_preds, average='macro')\n\n    print(f\"\\n📊 Accuracy: {acc:.2f}% | Macro F1 Score: {f1:.4f}\")\n    print(\"Classification Report:\\n\", classification_report(all_labels, all_preds))\n\n    cm = confusion_matrix(all_labels, all_preds)\n    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', \n                xticklabels=[0, 1, 2, 3, 4, 5], yticklabels=[0, 1, 2, 3, 4, 5])\n    plt.xlabel(\"Predicted\")\n    plt.ylabel(\"True\")\n    plt.title(\"Confusion Matrix\")\n    plt.show()\n\n    model.train()\n    return acc","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T12:43:37.969411Z","iopub.execute_input":"2025-06-26T12:43:37.969762Z","iopub.status.idle":"2025-06-26T12:43:37.979872Z","shell.execute_reply.started":"2025-06-26T12:43:37.969737Z","shell.execute_reply":"2025-06-26T12:43:37.97872Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Loss with label smoothing (if using PyTorch >= 1.10)\ncriterion = nn.CrossEntropyLoss(label_smoothing=0.1)\n\n# Optimizer\noptimizer = torch.optim.Adam(model.parameters(), lr=1e-4)\n\n# Optional: Learning Rate Scheduler\nscheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T12:44:22.323368Z","iopub.execute_input":"2025-06-26T12:44:22.323741Z","iopub.status.idle":"2025-06-26T12:44:22.331307Z","shell.execute_reply.started":"2025-06-26T12:44:22.323715Z","shell.execute_reply":"2025-06-26T12:44:22.330291Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_model(\n    model=model,\n    train_loader=train_loader,\n    val_loader=val_loader,\n    criterion=criterion,\n    optimizer=optimizer,\n    scheduler=scheduler,\n    epochs=5  # Change as needed\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T12:44:34.362681Z","iopub.execute_input":"2025-06-26T12:44:34.363003Z","iopub.status.idle":"2025-06-26T12:46:55.709203Z","shell.execute_reply.started":"2025-06-26T12:44:34.362985Z","shell.execute_reply":"2025-06-26T12:46:55.708321Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load best model\nmodel.load_state_dict(torch.load(\"best_model.pth\"))\nmodel.eval()\n\n# Evaluate on validation set\nevaluate_model(model, val_loader)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T12:47:05.330815Z","iopub.execute_input":"2025-06-26T12:47:05.331252Z","iopub.status.idle":"2025-06-26T12:47:07.859028Z","shell.execute_reply.started":"2025-06-26T12:47:05.331216Z","shell.execute_reply":"2025-06-26T12:47:07.857848Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from PIL import Image\nimport torch\nimport torchvision.transforms as transforms\nimport matplotlib.pyplot as plt\n\n# Set the path to your uploaded test image\nimg_path = \"/kaggle/input/testimg/trailcancer.jpeg\"  # ✅ Make sure this path is correct\n\n# Same transforms as validation set\nval_transforms = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.5]*3, std=[0.5]*3)\n])\n\n# Load trained ResNet18 model\nfrom torchvision.models import resnet18, ResNet18_Weights\nimport torch.nn as nn\n\nmodel = resnet18(weights=ResNet18_Weights.IMAGENET1K_V1)\nmodel.fc = nn.Linear(model.fc.in_features, 6)  # ISUP grades: 0–5\nmodel = model.to(device)\n\n# Load best model weights\nmodel.load_state_dict(torch.load(\"best_model.pth\", map_location=device))\nmodel.eval()\n\n# Load & preprocess image\nimage = Image.open(img_path).convert(\"RGB\")\ninput_tensor = val_transforms(image).unsqueeze(0).to(device)\n\n# Predict\nwith torch.no_grad():\n    outputs = model(input_tensor)\n    pred_class = torch.argmax(outputs, dim=1).item()\n\n#Print and visualize\nprint(f\"Predicted ISUP Grade: {pred_class}\")\nplt.imshow(image)\nplt.title(f\"Predicted ISUP Grade: {pred_class}\", fontsize=16)\nplt.axis('off')\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T12:56:27.088281Z","iopub.execute_input":"2025-06-26T12:56:27.088644Z","iopub.status.idle":"2025-06-26T12:56:27.61407Z","shell.execute_reply.started":"2025-06-26T12:56:27.088617Z","shell.execute_reply":"2025-06-26T12:56:27.612944Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}