{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"colab":{"provenance":[]},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":10304197,"sourceType":"datasetVersion","datasetId":6378311}],"dockerImageVersionId":30823,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Context\n\nRecent studies have shown that breast cancer continues to be the leading cause of death among women over the world. If detected at an early stage, it can be cured in 9 out of 10 cases.\n\nAutomated detection and segmentation of cells from images are the crucial and fundamental steps for the measurement of cellular morphology that is crucial for brest cancer diagnosis and prognosis.\n\n\nIn this notebook, you will learn how to train a segmentation as UNet with **monai** - a framwork based Pytorch Stadard for healthcare imaging.","metadata":{"id":"8ImoQ0-hwC2W"}},{"cell_type":"code","source":"! pip install monai","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:18:37.134874Z","iopub.execute_input":"2024-12-26T16:18:37.135207Z","iopub.status.idle":"2024-12-26T16:18:42.647507Z","shell.execute_reply.started":"2024-12-26T16:18:37.135178Z","shell.execute_reply":"2024-12-26T16:18:42.646488Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport re\n\nimport torch\nfrom torch.optim import Adam\nfrom torch.utils.data import random_split, DataLoader\nimport matplotlib.pyplot as plt\nfrom PIL import Image\n\nfrom monai.transforms import Compose, LoadImage, EnsureChannelFirst, ToTensor, NormalizeIntensity, ScaleIntensity\nfrom monai.data import PILReader\nfrom monai.networks.nets import UNet, AttentionUnet\nfrom monai.losses import DiceLoss","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:18:42.648955Z","iopub.execute_input":"2024-12-26T16:18:42.649313Z","iopub.status.idle":"2024-12-26T16:18:57.233976Z","shell.execute_reply.started":"2024-12-26T16:18:42.649275Z","shell.execute_reply":"2024-12-26T16:18:57.233174Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Monai\n\nMONAI is a pytorch based open source AI framework launched by NVIDIA and King’s College London.\nIt is integrated with training and modelling workflows in a native PyTorch Standard. t several places.\n","metadata":{"id":"GzFCO5Oxw33j"}},{"cell_type":"markdown","source":"Install monai","metadata":{"id":"Hz5QCkOJPLZa"}},{"cell_type":"code","source":"! pip install monai","metadata":{"id":"fHn1Sg81-_wk","trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:18:57.235428Z","iopub.execute_input":"2024-12-26T16:18:57.236082Z","iopub.status.idle":"2024-12-26T16:19:00.499982Z","shell.execute_reply.started":"2024-12-26T16:18:57.236030Z","shell.execute_reply":"2024-12-26T16:19:00.498837Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Check the installation by running the following cell","metadata":{"id":"4XsRM6vPhTyc"}},{"cell_type":"code","source":"import monai\nmonai.config.print_config()","metadata":{"id":"_r3cOnh9BNAU","outputId":"c33f0803-186d-44b3-a68c-ca1a94612237","trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:19:00.501561Z","iopub.execute_input":"2024-12-26T16:19:00.501792Z","iopub.status.idle":"2024-12-26T16:19:00.530183Z","shell.execute_reply.started":"2024-12-26T16:19:00.501774Z","shell.execute_reply":"2024-12-26T16:19:00.529528Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#  Dataset\n\nTo train a model, we need to prepare some ingredients:\n\n1. Dataset\n2. Model\n3. Loss function\n4. Optimizer","metadata":{"id":"fMqjrNJdxMb8"}},{"cell_type":"markdown","source":"# I. Create Dataset\n\nThere are two ways to create your dataset:\n- with pytorch Dataset\n- with monai.data.Dataset.\n\nIn this exercise, we will create our dataset using torch.utils.data.Dataset.","metadata":{"id":"R0aIL2Hj-Awt"}},{"cell_type":"markdown","source":"## 1. List all files in folder\n\nDownload the dataset from https://zenodo.org/record/1175282#.YMn_Qy-FDox\n\nNotice that there are two kind of folder : original cell picture folder and mask folders. Using your file explorer or some code, display one image and the corresponding image","metadata":{"id":"JryJX0iF-VFE"}},{"cell_type":"code","source":"mask = Image.open(os.path.join(\"/kaggle/input/tbnc-nuclei-segmentation/TNBC_NucleiSegmentation/GT_01/\", \"01_1.png\"))\noriginal = Image.open(os.path.join(\"/kaggle/input/tbnc-nuclei-segmentation/TNBC_NucleiSegmentation/Slide_01/\", \"01_1.png\"))\n\nplt.figure(figsize=(10, 5))\n\nplt.subplot(1, 2, 1)\nplt.imshow(original, cmap='gray')\nplt.title(\"Original Cell Picture\")\nplt.axis('off')\n\nplt.subplot(1, 2, 2)\nplt.imshow(mask, cmap='gray')\nplt.title(\"Mask\")\nplt.axis('off')\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:19:00.531101Z","iopub.execute_input":"2024-12-26T16:19:00.531430Z","iopub.status.idle":"2024-12-26T16:19:00.982552Z","shell.execute_reply.started":"2024-12-26T16:19:00.531398Z","shell.execute_reply":"2024-12-26T16:19:00.981661Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 2. Define a transform\nWhen you load your data, you need to define some transformation. For example, we want to convert image to the format [num_channels, spatial_dim_1, spatial_dim_2] because monai/pytorch use this format. We'll also need to convert the images to PyTorch tensors with transforms.ToTensor()\n\nThe following code lets you load image and the labels and define several steps to transform the data.\n","metadata":{"id":"5YNnKfAb-f7x"}},{"cell_type":"code","source":"\nimage_trans = Compose(\n    [\n        LoadImage(image_only=True, reader = PILReader(converter=lambda image: image.convert(\"RGB\"))),\n        EnsureChannelFirst(),\n        NormalizeIntensity(),\n        ToTensor(),\n    ])\n\n\nlabel_trans = Compose(\n    [\n        LoadImage(image_only=True),\n        EnsureChannelFirst(),\n        ScaleIntensity(),\n        ToTensor(),\n    ])","metadata":{"id":"b2anGmiYpof_","trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:19:00.983503Z","iopub.execute_input":"2024-12-26T16:19:00.983807Z","iopub.status.idle":"2024-12-26T16:19:00.993400Z","shell.execute_reply.started":"2024-12-26T16:19:00.983782Z","shell.execute_reply":"2024-12-26T16:19:00.992520Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"\n## 3. Create dataset\n\nThe following class CellDataset allows us to create our dataset from \"image_files\" and \"label_files\" where:\n- \"image_files\" is a list of image names\n- \"label_files\" is the list of segmentation names respectively.\n\n\"im_trans\" and \"label_trans\" are respectively the transforms for the images and their labels.\n\n\n","metadata":{"id":"ddVJrwCj_zbo"}},{"cell_type":"code","source":"class CellDataset(torch.utils.data.Dataset):\n    def __init__(self, image_files, label_files, im_trans, label_trans):\n        self.image_files = image_files\n        self.label_files = label_files\n        self.im_trans = im_trans\n        self.label_trans = label_trans\n\n    def __len__(self):\n        return(len(self.image_files))\n\n    def __getitem__(self, index):\n        return self.im_trans(self.image_files[index]), self.label_trans(label_files[index])\n\n","metadata":{"id":"vWK4PlpWpolZ","trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:19:00.994380Z","iopub.execute_input":"2024-12-26T16:19:00.994706Z","iopub.status.idle":"2024-12-26T16:19:01.005290Z","shell.execute_reply.started":"2024-12-26T16:19:00.994672Z","shell.execute_reply":"2024-12-26T16:19:01.004473Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"By using this class, create your training dataset et your test dataset. Remember to check if your dataset is loaded correctly.","metadata":{"id":"laSzipHBP5T2"}},{"cell_type":"code","source":"image_files = []\nlabel_files = []\n\nfor root, dirs, files in os.walk(\"/kaggle/input/tbnc-nuclei-segmentation/TNBC_NucleiSegmentation\", topdown=False):\n    current_folder = root.split(\"/\")[-1]\n    for name in files:\n        filepath = os.path.join(root, name)\n\n        if current_folder.startswith(\"GT\"):\n            label_files.append(filepath)\n        else:\n            image_files.append(filepath)\n\nimage_files = sorted(image_files, key=lambda f: re.search(r'_(\\d+)', f).group(1))\nlabel_files = sorted(label_files, key=lambda f: re.search(r'_(\\d+)', f).group(1))\n\ndataset = CellDataset(image_files=image_files, label_files=label_files, im_trans=image_trans, label_trans=label_trans) # to change\n\ntrain_size = int(0.8 * len(dataset))\ntest_size = len(dataset) - train_size\n\ntrain_dataset, test_dataset = random_split(dataset, [train_size, test_size])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:19:01.007337Z","iopub.execute_input":"2024-12-26T16:19:01.007552Z","iopub.status.idle":"2024-12-26T16:19:01.115050Z","shell.execute_reply.started":"2024-12-26T16:19:01.007528Z","shell.execute_reply":"2024-12-26T16:19:01.114131Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 4. DataLoader\n\nWith the your dataset loaded, you have to pass it to a DataLoader. The torch.utils.data.DataLoader takes a dataset and returns batches of images and the corresponding labels. You can set various parameters like the batch size and if the data is shuffled after each epoch.\n\n\nThe following code let you create a data loader for the train dataset, do the same to create a test_loader on the test_dataset. Name it **test_load**\n","metadata":{"id":"6EhMFYfzARsM"}},{"cell_type":"code","source":"def plot_image_and_label(image, label, title_image=\"Image\", title_label=\"Label\"):\n    image = image.permute(1, 2, 0).cpu().numpy() if isinstance(image, torch.Tensor) else image\n    label = label.squeeze(0).cpu().numpy() if isinstance(label, torch.Tensor) else label\n    \n    fig, ax = plt.subplots(1, 2, figsize=(10, 5))\n    \n    ax[0].imshow(image)\n    ax[0].set_title(title_image)\n    ax[0].axis('off')\n    \n    ax[1].imshow(label)\n    ax[1].set_title(title_label)\n    ax[1].axis('off')\n    \n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:19:01.116402Z","iopub.execute_input":"2024-12-26T16:19:01.116621Z","iopub.status.idle":"2024-12-26T16:19:01.121824Z","shell.execute_reply.started":"2024-12-26T16:19:01.116602Z","shell.execute_reply":"2024-12-26T16:19:01.120878Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_loader = DataLoader(train_dataset, batch_size=4, shuffle=True)\ntest_loader = DataLoader(test_dataset, batch_size=4, shuffle=False)\n\nfor idx, (image, label) in enumerate(train_loader):\n    if idx >= 5:\n        break\n    plot_image_and_label(image[0], label[0])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:19:01.122626Z","iopub.execute_input":"2024-12-26T16:19:01.122860Z","iopub.status.idle":"2024-12-26T16:19:03.613121Z","shell.execute_reply.started":"2024-12-26T16:19:01.122831Z","shell.execute_reply":"2024-12-26T16:19:03.612200Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 5. Now, time to check your dataloader.\n\nExecute the code following to check if your dataloader works correctly","metadata":{"id":"KkjvNXseAhNG"}},{"cell_type":"code","source":"import monai\nim, seg = monai.utils.misc.first(train_loader)\nim.shape, seg.shape","metadata":{"id":"IXKEr86dAQyZ","trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:19:03.614025Z","iopub.execute_input":"2024-12-26T16:19:03.614337Z","iopub.status.idle":"2024-12-26T16:19:03.713467Z","shell.execute_reply.started":"2024-12-26T16:19:03.614298Z","shell.execute_reply":"2024-12-26T16:19:03.712611Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# II. Build your segmentation model with monai\n\nMonai already has a UNet model architecture : https://docs.monai.io/en/stable/networks.html#unet\n\nBy using the monai.networks.nets module, build a UNet model for segmentation task in 2D. You'll have to choose the following parameters for the model:\n\n1. dimensions (number of spatial dimensions)\n2. in_channels (number of input channel)\n3. out_channels (number of output channel)\n4. channels\n5. strides\n","metadata":{"id":"H6a-OUu_AsK5"}},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nmodel = UNet(spatial_dims=2, in_channels=3, out_channels=1, channels=(64, 128, 256, 512), strides=(2, 2, 2)).to(device)","metadata":{"id":"0E0jUzAjqzxQ","trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:22:26.733353Z","iopub.execute_input":"2024-12-26T16:22:26.733704Z","iopub.status.idle":"2024-12-26T16:22:26.763423Z","shell.execute_reply.started":"2024-12-26T16:22:26.733677Z","shell.execute_reply":"2024-12-26T16:22:26.762514Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# III. Define your loss function and optimizer\n\nFor a segmentation prob, we usually use DiceLoss.\nUsing monai.losses.DiceLoss, define your loss function and store it in the variable named **loss_function**. The option sigmoid = True should be used.\n\n\n","metadata":{"id":"nH_L4VtQA8Ot"}},{"cell_type":"code","source":"loss_function = DiceLoss(sigmoid=True)","metadata":{"id":"ngCBrBnYrJzG","trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:20:56.633095Z","iopub.execute_input":"2024-12-26T16:20:56.633424Z","iopub.status.idle":"2024-12-26T16:20:56.637165Z","shell.execute_reply.started":"2024-12-26T16:20:56.633397Z","shell.execute_reply":"2024-12-26T16:20:56.636243Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"With torch.optim, define an optimizer for your model. Use the Adam optimiser\n\n","metadata":{"id":"TiZr14RbQixP"}},{"cell_type":"code","source":"optimizer = Adam(model.parameters(), lr=0.01)","metadata":{"id":"_tVi8zq9QhT7","trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:20:58.003323Z","iopub.execute_input":"2024-12-26T16:20:58.003624Z","iopub.status.idle":"2024-12-26T16:20:58.007802Z","shell.execute_reply.started":"2024-12-26T16:20:58.003601Z","shell.execute_reply":"2024-12-26T16:20:58.006824Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# IV. Trainning the model\n\nThis time, we have all ingredients to train a segmentation model: a model, an optimizer, train_loader and a loss function.  \n\nMonai use a standard PyTorch program style for training a deep learning model.\n\n\nThe general process with Monai/Pytorch just for one learning step  as follows:\n\n1. Load input and label of each batch.\n\n2. Zero accumulated gradients with optimizer.zero_grad()\n\n3. Compute the output from the model\n\n4. Calculate the loss\n\n5. Perform backprop with loss.backward()\n\n6. Update the optimizer with optimizer.step()\n\n\nComplete the following code so that it do the training\n","metadata":{"id":"36QIfwDqA_k_"}},{"cell_type":"code","source":"epoch_loss_values = list()\nfor epoch in range(2):\n\n    model.train()\n    epoch_loss = 0\n    step = 0\n    for batch_data in train_loader:\n        step += 1\n        inputs, labels = batch_data[0].to(device), batch_data[1].to(device)\n        optimizer.zero_grad()\n\n        #compute the model predictions using the model variable and inputs\n        predictions = model(inputs)\n\n        # compute the loss using the loss function, the predictions and labels\n        loss = loss_function(predictions, labels)\n\n        # use the backward method of the loss variable to  compute the gradient of the loss used to find the minimum of the loss function\n        loss.backward()\n        # call the step method of the optimizer\n        optimizer.step()\n\n        epoch_loss += loss.item()\n        epoch_len = len(train_dataset) // train_loader.batch_size\n        print(f\"{step}/{epoch_len}, train_loss: {loss.item():.4f}\")\n\n\n    epoch_loss /= step\n    epoch_loss_values.append(epoch_loss)\n    print(f\"epoch {epoch + 1} average loss: {epoch_loss:.4f}\")\n","metadata":{"id":"otkLPW8eBLla","trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:21:00.099889Z","iopub.execute_input":"2024-12-26T16:21:00.100226Z","iopub.status.idle":"2024-12-26T16:21:04.389924Z","shell.execute_reply.started":"2024-12-26T16:21:00.100199Z","shell.execute_reply":"2024-12-26T16:21:04.389013Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Display the prediction of your model on several image","metadata":{"id":"8ZH0CLztSbz7"}},{"cell_type":"code","source":"model.eval()\nfor batch_data in test_loader:\n    images, labels = batch_data[0].to(device), batch_data[1].to(device)\n    with torch.no_grad():\n        predictions = model(images)\n\n    predictions = predictions.squeeze(1)\n\n    for idx in range(len(images)):\n        if idx >= 5:\n            break\n        plot_image_and_label(images[idx], predictions[idx], title_image=\"Image\", title_label=\"Prediction\")","metadata":{"id":"NsbEyhTWQgVM","trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:21:06.755216Z","iopub.execute_input":"2024-12-26T16:21:06.755558Z","iopub.status.idle":"2024-12-26T16:21:10.695090Z","shell.execute_reply.started":"2024-12-26T16:21:06.755528Z","shell.execute_reply":"2024-12-26T16:21:10.694127Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Train another architecture (either another Unet architecture or find another segmentation model in the available models of Monai). Compare the results with the first model","metadata":{"id":"BJWSuBSyjhU_"}},{"cell_type":"code","source":"model = AttentionUnet(spatial_dims=2, in_channels=3, out_channels=1, channels=(64, 128, 256, 512), strides=(2, 2, 2)).to(device)","metadata":{"id":"mkSbqrcIjTit","trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:21:30.568655Z","iopub.execute_input":"2024-12-26T16:21:30.568978Z","iopub.status.idle":"2024-12-26T16:21:30.645861Z","shell.execute_reply.started":"2024-12-26T16:21:30.568951Z","shell.execute_reply":"2024-12-26T16:21:30.645228Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"epoch_loss_values = list()\nfor epoch in range(2):\n\n    model.train()\n    epoch_loss = 0\n    step = 0\n    for batch_data in train_loader:\n        step += 1\n        inputs, labels = batch_data[0].to(device), batch_data[1].to(device)\n        optimizer.zero_grad()\n\n        #compute the model predictions using the model variable and inputs\n        predictions = model(inputs)\n\n        # compute the loss using the loss function, the predictions and labels\n        loss = loss_function(predictions, labels)\n\n        # use the backward method of the loss variable to  compute the gradient of the loss used to find the minimum of the loss function\n        loss.backward()\n        # call the step method of the optimizer\n        optimizer.step()\n\n        epoch_loss += loss.item()\n        epoch_len = len(train_dataset) // train_loader.batch_size\n        print(f\"{step}/{epoch_len}, train_loss: {loss.item():.4f}\")\n\n\n    epoch_loss /= step\n    epoch_loss_values.append(epoch_loss)\n    print(f\"epoch {epoch + 1} average loss: {epoch_loss:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:21:32.094465Z","iopub.execute_input":"2024-12-26T16:21:32.094756Z","iopub.status.idle":"2024-12-26T16:21:42.093572Z","shell.execute_reply.started":"2024-12-26T16:21:32.094734Z","shell.execute_reply":"2024-12-26T16:21:42.092824Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.eval()\nfor batch_data in test_loader:\n    images, labels = batch_data[0].to(device), batch_data[1].to(device)\n    with torch.no_grad():\n        predictions = model(images)\n\n    predictions = predictions.squeeze(1)\n\n    for idx in range(len(images)):\n        if idx >= 5:\n            break\n        plot_image_and_label(images[idx], predictions[idx], title_image=\"Image\", title_label=\"Prediction\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-26T16:21:47.754190Z","iopub.execute_input":"2024-12-26T16:21:47.754491Z","iopub.status.idle":"2024-12-26T16:21:51.620844Z","shell.execute_reply.started":"2024-12-26T16:21:47.754468Z","shell.execute_reply":"2024-12-26T16:21:51.619878Z"}},"outputs":[],"execution_count":null}]}