{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":8089,"databundleVersionId":44321,"sourceType":"competition"},{"sourceId":8041548,"sourceType":"datasetVersion","datasetId":4741093}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":" This will be a quick and dirty kernel showing how to get started on segmenting nuclei using a neural network in Pytorch.The architecture used is the so-called U-Net, which is very common for image segmentation problems such as this. \n<br>\n<br>\n    You can understand more about unet from [https://arxiv.org/abs/1505.04597]. Also implementation of same using keras is described in my [blog](http://https://medium.com/@arunm8489/an-overview-on-u-net-architecture-d6caabf7caa4) post.","metadata":{}},{"cell_type":"code","source":"import os\nimport time\nimport copy\nfrom collections import defaultdict\nimport torch\nimport shutil\nimport pandas as pd\nfrom skimage import io, transform\nimport numpy as np\nfrom PIL import Image\nimport matplotlib.pyplot as plt\nfrom torch.utils.data import Dataset, DataLoader, random_split\nfrom torchvision import transforms, utils\nfrom torch import nn\nfrom albumentations import (HorizontalFlip, ShiftScaleRotate, Normalize, Resize, Compose, GaussNoise)\nimport cv2\nfrom albumentations.pytorch import ToTensorV2\nfrom torch.autograd import Variable\nfrom torch.nn import Linear, ReLU, CrossEntropyLoss, Sequential, Conv2d, MaxPool2d, Module, Softmax, BatchNorm2d, Dropout\nfrom torch.optim import Adam, SGD\nimport torch.nn.functional as F\nfrom PIL import Image\nfrom torch import nn\nimport zipfile\nimport tqdm\nimport random\n\ndevice = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\n\ndef reset_seed():\n  torch.manual_seed(42)\n  random.seed(42)\n  torch.cuda.manual_seed(42)\nreset_seed()","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.status.busy":"2024-04-14T06:26:26.632232Z","iopub.execute_input":"2024-04-14T06:26:26.632959Z","iopub.status.idle":"2024-04-14T06:26:26.671716Z","shell.execute_reply.started":"2024-04-14T06:26:26.632930Z","shell.execute_reply":"2024-04-14T06:26:26.670686Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#! mkdir /kaggle/working/data\n# ! unzip /kaggle/input/data-science-bowl-2018/stage1_train.zip -d /kaggle/working/data","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:28.868174Z","iopub.execute_input":"2024-04-14T06:26:28.868520Z","iopub.status.idle":"2024-04-14T06:26:28.872790Z","shell.execute_reply.started":"2024-04-14T06:26:28.868495Z","shell.execute_reply":"2024-04-14T06:26:28.871693Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# if not os.path.exists('/kaggle/working/data'):\n#     os.mkdir('/kaggle/working/data')\n    \n# zip_path = '/kaggle/input/data-science-bowl-2018/stage1_train.zip'\n# with zipfile.ZipFile(zip_path, 'r') as zip_ref:\n#     zip_ref.extractall('/kaggle/working/data')","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:29.091842Z","iopub.execute_input":"2024-04-14T06:26:29.092628Z","iopub.status.idle":"2024-04-14T06:26:29.096391Z","shell.execute_reply.started":"2024-04-14T06:26:29.092596Z","shell.execute_reply":"2024-04-14T06:26:29.095422Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# !mkdir /kaggle/working/data\n# !mkdir /kaggle/working/data/images\n# !unzip -q /kaggle/input/data-science-bowl-2018/stage1_train.zip -d /kaggle/working/data/images\n# !unzip -q /kaggle/input/data-science-bowl-2018/stage1_train_labels.csv.zip -d /kaggle/working/data","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:29.264080Z","iopub.execute_input":"2024-04-14T06:26:29.264953Z","iopub.status.idle":"2024-04-14T06:26:29.268588Z","shell.execute_reply.started":"2024-04-14T06:26:29.264921Z","shell.execute_reply":"2024-04-14T06:26:29.267710Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# data_fpath = \"/kaggle/working/data/images/\"\n# all_ids = os.listdir(data_fpath)\n","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:29.403674Z","iopub.execute_input":"2024-04-14T06:26:29.403992Z","iopub.status.idle":"2024-04-14T06:26:29.408050Z","shell.execute_reply.started":"2024-04-14T06:26:29.403967Z","shell.execute_reply":"2024-04-14T06:26:29.407091Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"seed = 42\nnp.random.seed = seed\n\nimg_size = 256\nIMG_WIDTH = img_size\nIMG_HEIGHT = img_size\nIMG_CHANNELS = 3","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:29.539630Z","iopub.execute_input":"2024-04-14T06:26:29.539896Z","iopub.status.idle":"2024-04-14T06:26:29.544252Z","shell.execute_reply.started":"2024-04-14T06:26:29.539874Z","shell.execute_reply":"2024-04-14T06:26:29.543389Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# from sklearn.model_selection import train_test_split\n# all_ids = os.listdir(data_fpath)\n# train_ids, test_ids = train_test_split(all_ids, test_size=0.2, random_state=42, shuffle=False)","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:29.696106Z","iopub.execute_input":"2024-04-14T06:26:29.696443Z","iopub.status.idle":"2024-04-14T06:26:29.700473Z","shell.execute_reply.started":"2024-04-14T06:26:29.696418Z","shell.execute_reply":"2024-04-14T06:26:29.699474Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# X_train = np.zeros((len(train_ids), IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS), dtype=np.uint8)\n# Y_train = np.zeros((len(train_ids), IMG_HEIGHT, IMG_WIDTH, 1), dtype=np.uint8)\n\n# print('Resizing training images and masks')\n# for n, file_name in tqdm.tqdm(enumerate(train_ids), total = len(train_ids)):   \n#     path = data_fpath + file_name\n#     img = io.imread(path + '/images/' + file_name + '.png')[:,:,:IMG_CHANNELS]  \n#     img = transform.resize(img, (IMG_HEIGHT, IMG_WIDTH), mode='constant', preserve_range=True)\n#     X_train[n] = img  #Fill empty X_train with values from img\n#     mask = np.zeros((IMG_HEIGHT, IMG_WIDTH, 1), dtype=np.uint8)\n#     for mask_file in next(os.walk(path + '/masks/'))[2]:\n#         mask_ = io.imread(path + '/masks/' + mask_file)\n#         mask_ = np.expand_dims(transform.resize(mask_, (IMG_HEIGHT, IMG_WIDTH), mode='constant',  \n#                                       preserve_range=True), axis=-1)\n#         mask = np.maximum(mask, mask_)  \n            \n#     Y_train[n] = mask       ","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:29.873614Z","iopub.execute_input":"2024-04-14T06:26:29.874200Z","iopub.status.idle":"2024-04-14T06:26:29.878617Z","shell.execute_reply.started":"2024-04-14T06:26:29.874170Z","shell.execute_reply":"2024-04-14T06:26:29.877760Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# X_test = np.zeros((len(test_ids), IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS), dtype=np.uint8)\n# Y_test = np.zeros((len(test_ids), IMG_HEIGHT, IMG_WIDTH, 1), dtype=np.uint8)\n\n# print('Resizing training images and masks')\n# for n, file_name in tqdm.tqdm(enumerate(test_ids), total = len(test_ids)):   \n#     path = data_fpath + file_name\n#     img = io.imread(path + '/images/' + file_name + '.png')[:,:,:IMG_CHANNELS]  \n#     img = transform.resize(img, (IMG_HEIGHT, IMG_WIDTH), mode='constant', preserve_range=True)\n#     X_test[n] = img  #Fill empty X_train with values from img\n#     mask = np.zeros((IMG_HEIGHT, IMG_WIDTH, 1), dtype=np.uint8)\n#     for mask_file in next(os.walk(path + '/masks/'))[2]:\n#         mask_ = io.imread(path + '/masks/' + mask_file)\n#         mask_ = np.expand_dims(transform.resize(mask_, (IMG_HEIGHT, IMG_WIDTH), mode='constant',  \n#                                       preserve_range=True), axis=-1)\n#         mask = np.maximum(mask, mask_)  \n            \n#     Y_test[n] = mask       ","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:30.079942Z","iopub.execute_input":"2024-04-14T06:26:30.080268Z","iopub.status.idle":"2024-04-14T06:26:30.084897Z","shell.execute_reply.started":"2024-04-14T06:26:30.080242Z","shell.execute_reply":"2024-04-14T06:26:30.083995Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Loading Data\n\nNow we will load the data by using custom data loader from pytorch along with small preprocessing such as horizontal fliping and normalization.","metadata":{}},{"cell_type":"code","source":"# def get_transforms(mean, std):\n#             list_transforms = []\n            \n#             list_transforms.extend(\n#                     [\n#                 HorizontalFlip(p=0.5), # only horizontal flip as of now\n#                     ])\n#             list_transforms.extend(\n#                     [\n#             Normalize(mean=mean, std=std, p=1),\n#             ToTensor(),\n#                     ])\n#             list_trfms = Compose(list_transforms)\n#             return list_trfms\n\n\n\n\n\n# class Nuclie_data(Dataset):\n#         def __init__(self,path):\n#             self.path = path\n#             self.folders = os.listdir(path)\n#             self.transforms = get_transforms(0.5, 0.5)\n        \n#         def __len__(self):\n#             return len(self.folders)\n              \n        \n#         def __getitem__(self,idx):\n#             image_folder = os.path.join(self.path,self.folders[idx],'images/')\n#             mask_folder = os.path.join(self.path,self.folders[idx],'masks/')\n#             image_path = os.path.join(image_folder,os.listdir(image_folder)[0])\n            \n#             img = io.imread(image_path)[:,:,:3].astype('float32')\n#             img = transform.resize(img,(256,256))\n            \n#             mask = self.get_mask(mask_folder, 256, 256 ).astype('float32')\n\n#             augmented = self.transforms(image=img, mask=mask)\n#             img = augmented['image']\n#             mask = augmented['mask']\n#             mask = mask[0].permute(2, 0, 1)\n#             mask = torch.from_numpy(np.where(mask > 0.5, 1, 0))\n#             return (img,mask) \n\n\n#         def get_mask(self,mask_folder,IMG_HEIGHT, IMG_WIDTH):\n#             mask = np.zeros((IMG_HEIGHT, IMG_WIDTH, 1), dtype=np.bool)\n#             for mask_ in os.listdir(mask_folder):\n#                     mask_ = io.imread(os.path.join(mask_folder,mask_))\n#                     mask_ = transform.resize(mask_, (IMG_HEIGHT, IMG_WIDTH))\n#                     mask_ = np.expand_dims(mask_,axis=-1)\n#                     mask = np.maximum(mask, mask_)\n              \n#             return mask\n            \n","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:30.656228Z","iopub.execute_input":"2024-04-14T06:26:30.656869Z","iopub.status.idle":"2024-04-14T06:26:30.662508Z","shell.execute_reply.started":"2024-04-14T06:26:30.656838Z","shell.execute_reply":"2024-04-14T06:26:30.661610Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Nuclie_data(Dataset):\n        def __init__(self,image, mask, transform):\n            self.image = image\n            self.mask = mask\n            self.transforms = transform\n        \n        def __len__(self):\n            return len(self.image)\n              \n        \n        def __getitem__(self,idx):\n            img = self.image[idx].astype('float32')\n            \n            mask = self.mask[idx].astype('float32')\n            \n            augmented = self.transforms(image=img, mask=mask)\n            img = augmented['image']\n            mask = augmented['mask']\n            mask = mask.permute(2, 0, 1)\n            mask = torch.from_numpy(np.where(mask > 0.5, 1, 0))\n            return (img,mask) ","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:30.887942Z","iopub.execute_input":"2024-04-14T06:26:30.888592Z","iopub.status.idle":"2024-04-14T06:26:30.895875Z","shell.execute_reply.started":"2024-04-14T06:26:30.888560Z","shell.execute_reply":"2024-04-14T06:26:30.894873Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# #loading the data\n# base_dir = '/kaggle/working/data/'\n# data = Nuclie_data(base_dir)","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:31.127692Z","iopub.execute_input":"2024-04-14T06:26:31.128519Z","iopub.status.idle":"2024-04-14T06:26:31.131917Z","shell.execute_reply.started":"2024-04-14T06:26:31.128488Z","shell.execute_reply":"2024-04-14T06:26:31.131081Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # print out some sample data\n# print(data.__len__())\n# data.__getitem__(0)","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:31.383992Z","iopub.execute_input":"2024-04-14T06:26:31.384638Z","iopub.status.idle":"2024-04-14T06:26:31.388367Z","shell.execute_reply.started":"2024-04-14T06:26:31.384609Z","shell.execute_reply":"2024-04-14T06:26:31.387403Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now we will just check the shape of images and masks loaded","metadata":{}},{"cell_type":"code","source":"# for img,msk in data:\n#   print(img.shape)\n#   print(msk.shape)\n#   break","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:31.764354Z","iopub.execute_input":"2024-04-14T06:26:31.765243Z","iopub.status.idle":"2024-04-14T06:26:31.768869Z","shell.execute_reply.started":"2024-04-14T06:26:31.765210Z","shell.execute_reply":"2024-04-14T06:26:31.767879Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# some utility functions\ndef mask_convert(mask):\n    mask = mask.clone().cpu().detach().numpy()\n    mask = mask.transpose((1,2,0))\n    std = np.array((0.5))\n    mean = np.array((0.5))\n    mask  = std * mask + mean\n    mask = mask.clip(0,1)\n    mask = np.squeeze(mask)\n    return mask\n\n# converting tensor to image\ndef image_convert(image):\n    image = image.clone().cpu().numpy()\n    image = image.transpose((1,2,0))\n    std = np.array((0.5,0.5,0.5))\n    mean = np.array((0.5,0.5,0.5))\n    image  = std * image + mean\n    image = image.clip(0,1)\n    image = (image * 255).astype(np.uint8)\n    return image\n","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:31.964209Z","iopub.execute_input":"2024-04-14T06:26:31.964817Z","iopub.status.idle":"2024-04-14T06:26:31.973023Z","shell.execute_reply.started":"2024-04-14T06:26:31.964789Z","shell.execute_reply":"2024-04-14T06:26:31.972091Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_img(no_):\n    iter_ = iter(test_loader)\n    images,masks = next(iter_)\n    images = images.to(device)\n    masks = masks.to(device)\n    plt.figure(figsize=(10,6))\n    for idx in range(0,no_):\n         image = image_convert(images[idx])\n         plt.subplot(2,no_,idx+1)\n         plt.title('image')\n         plt.imshow(image)\n    for idx in range(0,no_):\n         mask = mask_convert(masks[idx])\n         plt.subplot(2,no_,idx+no_+1)\n         plt.title('mask')\n         plt.imshow(mask,cmap='gray')\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:32.192136Z","iopub.execute_input":"2024-04-14T06:26:32.192484Z","iopub.status.idle":"2024-04-14T06:26:32.199559Z","shell.execute_reply.started":"2024-04-14T06:26:32.192458Z","shell.execute_reply":"2024-04-14T06:26:32.198629Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We will use 580 images for train and 90 images for validation","metadata":{}},{"cell_type":"code","source":"import albumentations as A\ntrain_transform = A.Compose(\n    [\n        A.Resize(height=224, width=224),\n        A.Rotate(limit=20, p=1.0),\n        A.HorizontalFlip(p=0.5),\n        A.VerticalFlip(p=0.1),\n        A.Normalize(\n            #mean = (0.485, 0.456, 0.406),\n            #std = (0.229, 0.224, 0.225),\n            mean = (0., 0., 0.),\n            std = (1., 1., 1.),\n            max_pixel_value = 255.0\n        ),\n        ToTensorV2(),\n    ])\nval_transform = A.Compose(\n    [\n        #A.RandomCrop(height=IMAGE_HEIGHT, width=IMAGE_WIDTH),\n        A.Resize(height=224, width=224),\n        A.Normalize(\n            mean = (0., 0., 0.),\n            std = (1., 1., 1.),\n            max_pixel_value = 255.0\n        ),\n        ToTensorV2(),\n    ])","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:29:19.597315Z","iopub.execute_input":"2024-04-14T06:29:19.598050Z","iopub.status.idle":"2024-04-14T06:29:19.605260Z","shell.execute_reply.started":"2024-04-14T06:29:19.598005Z","shell.execute_reply":"2024-04-14T06:29:19.604200Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_train_1 = np.load(\"/kaggle/input/2018-data-science-bowl/train_256_imgs.npy\")\nY_train_1 = np.load(\"/kaggle/input/2018-data-science-bowl/train_256_masks.npy\")","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:29:20.680121Z","iopub.execute_input":"2024-04-14T06:29:20.680456Z","iopub.status.idle":"2024-04-14T06:29:20.743646Z","shell.execute_reply.started":"2024-04-14T06:29:20.680431Z","shell.execute_reply":"2024-04-14T06:29:20.742783Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_test_1 = np.load(\"/kaggle/input/2018-data-science-bowl/test_256_imgs.npy\")\nY_test_1 = np.load(\"/kaggle/input/2018-data-science-bowl/test_256_masks.npy\")","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:29:20.959837Z","iopub.execute_input":"2024-04-14T06:29:20.960184Z","iopub.status.idle":"2024-04-14T06:29:20.980914Z","shell.execute_reply.started":"2024-04-14T06:29:20.960155Z","shell.execute_reply":"2024-04-14T06:29:20.979966Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_ds = Nuclie_data(X_train_1, Y_train_1, transform=train_transform)\nnum_train = int(len(train_ds)*0.9)\ntrain_ds, val_ds = torch.utils.data.random_split(train_ds,[num_train, len(train_ds)-num_train])\ntest_ds = Nuclie_data(X_test_1, Y_test_1, transform=val_transform)","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:29:21.692176Z","iopub.execute_input":"2024-04-14T06:29:21.692934Z","iopub.status.idle":"2024-04-14T06:29:21.698315Z","shell.execute_reply.started":"2024-04-14T06:29:21.692903Z","shell.execute_reply":"2024-04-14T06:29:21.697452Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # splitting to trainset and validation set and loading the data with batch size of 10\n\n# trainset, valset, testset = random_split(data, [500, 80, 90])\n\n# train_loader = torch.utils.data.DataLoader(dataset=trainset, batch_size=10, shuffle=True)\n\n# val_loader = torch.utils.data.DataLoader(dataset=valset, batch_size=10)\n# test_loader = torch.utils.data.DataLoader(dataset=valset, batch_size=10)","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:29:24.804447Z","iopub.execute_input":"2024-04-14T06:29:24.804819Z","iopub.status.idle":"2024-04-14T06:29:24.811680Z","shell.execute_reply.started":"2024-04-14T06:29:24.804790Z","shell.execute_reply":"2024-04-14T06:29:24.810478Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Visualize input data\nimport numpy as np\nNumSamples = 5\n\nk = np.random.randint(len(train_ds)*0.8,size = NumSamples)\nfg , ax = plt.subplots(2,NumSamples,figsize = (20,8))\nfg.suptitle(\"Data visualization\")\n\nfor i,j in enumerate(k):\n\n    X, y = train_ds[j]\n    ax[0,i].imshow(X.transpose(0,2).transpose(0,1), cmap='gray')\n    ax[0,i].set_xlabel('image {}'.format(j))\n    ax[1,i].imshow(y[0], cmap='gray')\n    ax[1,i].set_xlabel('mask {}'.format(j))","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:29:25.760402Z","iopub.execute_input":"2024-04-14T06:29:25.761089Z","iopub.status.idle":"2024-04-14T06:29:27.738080Z","shell.execute_reply.started":"2024-04-14T06:29:25.761055Z","shell.execute_reply":"2024-04-14T06:29:27.737078Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for X, y in train_ds:\n  print(X.shape)\n  print(y.shape)\n  break","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:29:27.739502Z","iopub.execute_input":"2024-04-14T06:29:27.739798Z","iopub.status.idle":"2024-04-14T06:29:27.747436Z","shell.execute_reply.started":"2024-04-14T06:29:27.739772Z","shell.execute_reply":"2024-04-14T06:29:27.746539Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"batch_size=16\ntrain_loader = DataLoader(train_ds, batch_size=batch_size, shuffle=True)\nval_loader = DataLoader(val_ds, batch_size=batch_size, shuffle=True)\ntest_loader = DataLoader(test_ds, batch_size=10)","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:29:32.924547Z","iopub.execute_input":"2024-04-14T06:29:32.924914Z","iopub.status.idle":"2024-04-14T06:29:32.931209Z","shell.execute_reply.started":"2024-04-14T06:29:32.924882Z","shell.execute_reply":"2024-04-14T06:29:32.930205Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# we will try visualizing images and corresponding masks\nplot_img(5)","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:29:34.140192Z","iopub.execute_input":"2024-04-14T06:29:34.140549Z","iopub.status.idle":"2024-04-14T06:29:35.499807Z","shell.execute_reply.started":"2024-04-14T06:29:34.140522Z","shell.execute_reply":"2024-04-14T06:29:35.498871Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!git clone https://github.com/HuCaoFighting/Swin-Unet.git","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:39.642126Z","iopub.execute_input":"2024-04-14T06:26:39.642425Z","iopub.status.idle":"2024-04-14T06:26:41.124460Z","shell.execute_reply.started":"2024-04-14T06:26:39.642399Z","shell.execute_reply":"2024-04-14T06:26:41.123281Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%cd Swin-Unet","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:41.126280Z","iopub.execute_input":"2024-04-14T06:26:41.127172Z","iopub.status.idle":"2024-04-14T06:26:41.133935Z","shell.execute_reply.started":"2024-04-14T06:26:41.127131Z","shell.execute_reply":"2024-04-14T06:26:41.133035Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip -qqq install ml_collections torch-summary gdown einops timm yacs","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:41.136565Z","iopub.execute_input":"2024-04-14T06:26:41.137315Z","iopub.status.idle":"2024-04-14T06:26:57.219950Z","shell.execute_reply.started":"2024-04-14T06:26:41.137283Z","shell.execute_reply":"2024-04-14T06:26:57.218620Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!gdown 1TyMf0_uvaxyacMmVzRfqvLLAWSOE2bJR","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:26:57.221534Z","iopub.execute_input":"2024-04-14T06:26:57.221845Z","iopub.status.idle":"2024-04-14T06:27:00.747989Z","shell.execute_reply.started":"2024-04-14T06:26:57.221817Z","shell.execute_reply":"2024-04-14T06:27:00.746758Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!mkdir pretrained_ckpt","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:27:00.749774Z","iopub.execute_input":"2024-04-14T06:27:00.750735Z","iopub.status.idle":"2024-04-14T06:27:01.702765Z","shell.execute_reply.started":"2024-04-14T06:27:00.750696Z","shell.execute_reply":"2024-04-14T06:27:01.701387Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!cp swin_tiny_patch4_window7_224.pth pretrained_ckpt","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:27:01.704535Z","iopub.execute_input":"2024-04-14T06:27:01.704868Z","iopub.status.idle":"2024-04-14T06:27:02.752272Z","shell.execute_reply.started":"2024-04-14T06:27:01.704839Z","shell.execute_reply":"2024-04-14T06:27:02.751150Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from networks.vision_transformer import SwinUnet as ViT_seg\nfrom config import get_config, _C, _update_config_from_file\n","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:27:02.755352Z","iopub.execute_input":"2024-04-14T06:27:02.755684Z","iopub.status.idle":"2024-04-14T06:27:03.732484Z","shell.execute_reply.started":"2024-04-14T06:27:02.755654Z","shell.execute_reply":"2024-04-14T06:27:03.731662Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_config_2():\n    \"\"\"Get a yacs CfgNode object with default values.\"\"\"\n    # Return a clone so that the defaults will not be altered\n    # This is for the \"local variable\" use pattern\n    config = _C.clone()\n#     update_config(config, args)\n    _update_config_from_file(config, \"./configs/swin_tiny_patch4_window7_224_lite.yaml\")\n\n    return config","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:27:03.733634Z","iopub.execute_input":"2024-04-14T06:27:03.733938Z","iopub.status.idle":"2024-04-14T06:27:03.738575Z","shell.execute_reply.started":"2024-04-14T06:27:03.733913Z","shell.execute_reply":"2024-04-14T06:27:03.737564Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"config = get_config_2()","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:27:03.739644Z","iopub.execute_input":"2024-04-14T06:27:03.739894Z","iopub.status.idle":"2024-04-14T06:27:03.759877Z","shell.execute_reply.started":"2024-04-14T06:27:03.739872Z","shell.execute_reply":"2024-04-14T06:27:03.758947Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!python --version","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:27:03.761010Z","iopub.execute_input":"2024-04-14T06:27:03.761326Z","iopub.status.idle":"2024-04-14T06:27:04.727445Z","shell.execute_reply.started":"2024-04-14T06:27:03.761303Z","shell.execute_reply":"2024-04-14T06:27:04.726320Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"net = ViT_seg(config, img_size=224, num_classes=1)\nnet.load_from(config)","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:31:51.696134Z","iopub.execute_input":"2024-04-14T06:31:51.697003Z","iopub.status.idle":"2024-04-14T06:31:52.333908Z","shell.execute_reply.started":"2024-04-14T06:31:51.696958Z","shell.execute_reply":"2024-04-14T06:31:52.332957Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = net.to(device)","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:31:53.244003Z","iopub.execute_input":"2024-04-14T06:31:53.244397Z","iopub.status.idle":"2024-04-14T06:31:53.291249Z","shell.execute_reply.started":"2024-04-14T06:31:53.244368Z","shell.execute_reply":"2024-04-14T06:31:53.290479Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Loss function and IOU metric\n\n\nThis loss combines Dice loss with the standard binary cross-entropy (BCE) loss that is generally the default for segmentation models. Combined the two methods allows for some diversity in the loss, while benefitting from the stability of BCE.Similarly we use intersection over union as a metric","metadata":{}},{"cell_type":"code","source":"class DiceBCELoss(nn.Module):\n    def __init__(self, weight=None, size_average=True):\n        super(DiceBCELoss, self).__init__()\n\n    def forward(self, inputs, targets, smooth=1):\n        \n        #comment out if your model contains a sigmoid or equivalent activation layer\n        inputs = F.sigmoid(inputs)       \n        bce_weight = 0.5\n        #flatten label and prediction tensors\n        inputs = inputs.view(-1)\n        targets = targets.view(-1)\n        \n        intersection = (inputs * targets).sum()                            \n        dice_loss = 1 - (2.*intersection + smooth)/(inputs.sum() + targets.sum() + smooth)  \n        BCE = F.binary_cross_entropy(inputs, targets, reduction='mean')\n        loss_final = BCE * bce_weight + dice_loss * (1 - bce_weight)\n        return loss_final\n    \n## IOU computation\ndef iou_(y_pred,y):\n    inputs = y_pred.reshape(-1)\n    targets = y.reshape(-1)\n    intersection = (inputs * targets).sum()\n    total = (inputs + targets).sum()\n    union = total - intersection \n    smooth = 1    \n    iou = (intersection + smooth)/(union + smooth)\n    return iou\n\n\n\ndef iou_batch(y_pred,y):\n    '''computes mean iou for a batch of ground truth masks and predicted masks'''\n    ious = []\n    y_pred = F.sigmoid(y_pred)\n    y_pred = y_pred.clone().cpu().detach().numpy()\n    y = y.clone().cpu().detach().numpy() \n    \n    for pred, label in zip(y_pred, y):\n        ious.append(iou_(pred, label))\n    iou = np.nanmean(ious)\n    return iou    \n\n\ndef dice_pytorch(predictions: torch.Tensor, labels: torch.Tensor, e: float = 1e-7):\n    \"\"\"Calculates Dice coefficient for a tensor of predictions\"\"\"\n#     predictions = sigmoid(predictions)\n    predictions = torch.sigmoid(predictions)\n    predictions = torch.from_numpy(np.where(predictions.cpu() > 0.5, 1, 0))\n    labels = labels.long().cpu()\n    intersection = (predictions & labels).float().sum((1, 2))\n    return ((2 * intersection) + e) / (predictions.float().sum((1, 2)) + labels.float().sum((1, 2)) + e)","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:29:45.680288Z","iopub.execute_input":"2024-04-14T06:29:45.681159Z","iopub.status.idle":"2024-04-14T06:29:45.694562Z","shell.execute_reply.started":"2024-04-14T06:29:45.681129Z","shell.execute_reply":"2024-04-14T06:29:45.693615Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class FocalTverskyLoss_Binary(nn.Module):\n    def __init__(self, alpha=0.7, gamma=0.75):\n        super(FocalTverskyLoss_Binary, self).__init__()\n        self.alpha = alpha\n        self.beta = 1 - self.alpha\n        self.gamma = gamma\n\n    def _tversky_loss(self, score, target):\n        target = target.float()\n        smooth = 1.0\n\n        TP = torch.sum(score * target)\n        FP = torch.sum((1 - target) * score)\n        FN = torch.sum(target * (1 - score))\n\n        loss = (TP + smooth) / (TP + self.alpha*FP + self.beta*FN + smooth)  \n\n        return (1 - loss)**self.gamma\n\n    def forward(self, inputs, target, sigmoid=True):\n        if sigmoid:\n            inputs = torch.sigmoid(inputs)\n\n        assert inputs.size() == target.size(), 'predict {} & target {} shape do not match'.format(inputs.size(), target.size())\n\n        loss = self._tversky_loss(inputs[:, 0], target[:, 0])\n\n        return loss ","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:29:46.152011Z","iopub.execute_input":"2024-04-14T06:29:46.152400Z","iopub.status.idle":"2024-04-14T06:29:46.161527Z","shell.execute_reply.started":"2024-04-14T06:29:46.152371Z","shell.execute_reply":"2024-04-14T06:29:46.160494Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now we will write some utility functions to save and load best model on training","metadata":{}},{"cell_type":"code","source":"#ref https://towardsdatascience.com/how-to-save-and-load-a-model-in-pytorch-with-a-complete-example-c2920e617dee\n\ndef save_ckp(state, is_best, checkpoint_path, best_model_path):\n    \"\"\"\n    state: checkpoint we want to save\n    is_best: is this the best checkpoint; min validation loss\n    checkpoint_path: path to save checkpoint\n    best_model_path: path to save best model\n    \"\"\"\n    # save checkpoint data to the path given, checkpoint_path\n    torch.save(state, checkpoint_path)\n    # if it is a best model, min validation loss\n    if is_best:\n        # copy that checkpoint file to best path given, best_model_path\n        shutil.copyfile(checkpoint_path, best_model_path)\n        \ndef load_ckp(checkpoint_fpath, model, optimizer):\n    \"\"\"\n    checkpoint_path: path to save checkpoint\n    model: model that we want to load checkpoint parameters into       \n    optimizer: optimizer we defined in previous training\n    \"\"\"\n    # load check point\n    checkpoint = torch.load(checkpoint_fpath)\n    # initialize state_dict from checkpoint to model\n    model.load_state_dict(checkpoint['state_dict'])\n    # initialize optimizer from checkpoint to optimizer\n    optimizer.load_state_dict(checkpoint['optimizer'])\n    # initialize valid_loss_min from checkpoint to valid_loss_min\n    valid_loss_min = checkpoint['valid_loss_min']\n    # return model, optimizer, epoch value, min validation loss \n    return model, optimizer, checkpoint['epoch'], valid_loss_min.item()","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:29:46.492309Z","iopub.execute_input":"2024-04-14T06:29:46.492947Z","iopub.status.idle":"2024-04-14T06:29:46.499762Z","shell.execute_reply.started":"2024-04-14T06:29:46.492917Z","shell.execute_reply":"2024-04-14T06:29:46.498855Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model training","metadata":{}},{"cell_type":"code","source":"import tqdm","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:29:47.364108Z","iopub.execute_input":"2024-04-14T06:29:47.364903Z","iopub.status.idle":"2024-04-14T06:29:47.369067Z","shell.execute_reply.started":"2024-04-14T06:29:47.364870Z","shell.execute_reply":"2024-04-14T06:29:47.367908Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"checkpoint_path = '/kaggle/working/chkpoint.pt'\nbest_model_path = '/kaggle/working/bestmodel.pt'\nepochs = 200\ncriterion = DiceBCELoss()\n# criterion = FocalTverskyLoss_Binary()\nlearning_rate = 1e-5\noptimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)\nvalid_loss_min = 3.95275\n\n\ntrain_loss,val_loss = [],[]\ntrain_iou,val_iou = [],[]\n\n\nfor epoch in range(epochs):\n    print('Epoch {}/{}'.format(epoch + 1, epochs))\n    start_time = time.time()\n     \n\n    \n    running_train_loss = []\n    running_train_score = []\n    for image,mask in tqdm.tqdm(train_loader): \n            image = image.to(device,dtype=torch.float)\n            mask = mask.to(device,dtype=torch.float)\n            \n            pred_mask = model.forward(image) # forward propogation\n            loss = criterion(pred_mask,mask)\n            score = iou_batch(pred_mask,mask)\n            optimizer.zero_grad() # setting gradient to zero\n            loss.backward()\n            optimizer.step()\n            running_train_loss.append(loss.item())\n            running_train_score.append(score)\n                              \n\n    else:           \n        running_val_loss = []\n        running_val_score = []\n        with torch.no_grad():\n            for image,mask in val_loader:\n                    image = image.to(device,dtype=torch.float)\n                    mask = mask.to(device,dtype=torch.float)                            \n                    pred_mask = model.forward(image)\n                    loss = criterion(pred_mask,mask)\n                    score = iou_batch(pred_mask,mask)\n                    running_val_loss.append(loss.item())\n                    running_val_score.append(score)\n\n                                    \n    \n    epoch_train_loss,epoch_train_score = np.mean(running_train_loss) ,np.mean(running_train_score)\n    print('Train loss : {} iou : {}'.format(epoch_train_loss,epoch_train_score))                       \n    train_loss.append(epoch_train_loss)\n    train_iou.append(epoch_train_score)\n    \n    epoch_val_loss,epoch_val_score = np.mean(running_val_loss),np.mean(running_val_score)\n    print('Validation loss : {} iou : {}'.format(epoch_val_loss,epoch_val_score))                                \n    val_loss.append(epoch_val_loss)\n    val_iou.append(epoch_val_score)\n    \n    # create checkpoint variable and add important data\n    checkpoint = {\n            'epoch': epoch + 1,\n            'valid_loss_min': epoch_val_loss,\n            'state_dict': model.state_dict(),\n            'optimizer': optimizer.state_dict(),\n        }\n    \n    # save checkpoint\n    save_ckp(checkpoint, False, checkpoint_path, best_model_path)\n    ## TODO: save the model if validation loss has decreased\n    if epoch_val_loss <= valid_loss_min:\n            print('Validation loss decreased ({:.6f} --> {:.6f}).  Saving model ...'.format(valid_loss_min,epoch_val_loss))\n            # save checkpoint as best model\n            save_ckp(checkpoint, True, checkpoint_path, best_model_path)\n            valid_loss_min = epoch_val_loss\n    \n    time_elapsed = time.time() - start_time\n    print('{:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))\n                        \n","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:31:58.176730Z","iopub.execute_input":"2024-04-14T06:31:58.177386Z","iopub.status.idle":"2024-04-14T06:32:49.726779Z","shell.execute_reply.started":"2024-04-14T06:31:58.177345Z","shell.execute_reply":"2024-04-14T06:32:49.725730Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Plotting Loss and IOU Scores","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(20,10))\nplt.subplot(1,2,1)\nplt.plot(train_loss,label='train_loss')\nplt.plot(val_loss,label='val_loss')\nplt.legend()\nplt.title('Loss Plot')\nplt.subplot(1,2,2)\nplt.plot(train_iou,label='train_iou')\nplt.plot(val_iou,label='val_iou')\nplt.legend()\nplt.title('IOU Plot')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:32:52.979425Z","iopub.execute_input":"2024-04-14T06:32:52.980215Z","iopub.status.idle":"2024-04-14T06:32:53.539393Z","shell.execute_reply.started":"2024-04-14T06:32:52.980177Z","shell.execute_reply":"2024-04-14T06:32:53.538438Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#loading the saved model\nmodel, optimizer, start_epoch, valid_loss_min = load_ckp(best_model_path, model, optimizer)","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:32:54.227948Z","iopub.execute_input":"2024-04-14T06:32:54.228812Z","iopub.status.idle":"2024-04-14T06:32:54.542960Z","shell.execute_reply.started":"2024-04-14T06:32:54.228779Z","shell.execute_reply":"2024-04-14T06:32:54.541650Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Visualisation of actual and predicted mask","metadata":{}},{"cell_type":"code","source":"def test_run(model, test_loader):\n    model.eval()\n    running_val_loss = []\n    running_val_iou_score = []\n    running_val_dice_score = []\n    with torch.no_grad():\n        for image,mask in tqdm.tqdm(val_loader):\n                image = image.to(device,dtype=torch.float)\n                mask = mask.to(device,dtype=torch.float)\n                pred_mask = model.forward(image)\n                loss = criterion(pred_mask,mask)\n                iou_score = iou_batch(pred_mask,mask)\n                dice_score = dice_pytorch(pred_mask.float(), mask).mean().item()\n                running_val_loss.append(loss.item())\n                running_val_iou_score.append(iou_score)\n                running_val_dice_score.append(dice_score)\n    \n    print(f'| Test loss: {np.mean(np.array(running_val_loss))} | Test Mean IoU: {np.mean(np.array(running_val_iou_score))} | Test Mean Dice: {np.mean(np.array(running_val_dice_score))}')","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:32:55.380181Z","iopub.execute_input":"2024-04-14T06:32:55.380955Z","iopub.status.idle":"2024-04-14T06:32:58.077765Z","shell.execute_reply.started":"2024-04-14T06:32:55.380910Z","shell.execute_reply":"2024-04-14T06:32:58.076775Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_run(model,test_loader)","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:32:58.079256Z","iopub.execute_input":"2024-04-14T06:32:58.079553Z","iopub.status.idle":"2024-04-14T06:32:58.530352Z","shell.execute_reply.started":"2024-04-14T06:32:58.079529Z","shell.execute_reply":"2024-04-14T06:32:58.529422Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"iter_ = iter(test_loader)\nimage,mask = next(iter_)\nimage = image.to(device,dtype=torch.float)\nmask = mask.to(device,dtype=torch.float)\ny_pred = model.forward(image)\n\n\nplt.figure(figsize=(20,15))\nfor i in range(0,5):\n    plt.subplot(3,5,i+1)\n    plt.title('Actual image')\n    plt.imshow(image_convert(image[i]))\nfor i in range(0,5):\n    plt.subplot(3,5,i+5+1)\n    plt.title('Actual mask')\n    plt.imshow(mask_convert(mask[i]),cmap='gray')\nfor i in range(0,5):\n    plt.subplot(3,5,i+10+1)\n    plt.title('Predicted mask')\n    plt.imshow(mask_convert(y_pred[i]),cmap='gray')\nplt.show()\n    \n    ","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:33:00.324903Z","iopub.execute_input":"2024-04-14T06:33:00.326011Z","iopub.status.idle":"2024-04-14T06:33:03.371128Z","shell.execute_reply.started":"2024-04-14T06:33:00.325977Z","shell.execute_reply":"2024-04-14T06:33:03.370212Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from scipy.ndimage.morphology import binary_dilation\nimport matplotlib.patches as mpatches\n\nwidth = 3\ncolumns = 10\nn_examples = columns * width\n\nfig, axs = plt.subplots(columns, width, figsize=(7*width , 7*columns), constrained_layout=True)\nred_patch = mpatches.Patch(color='red', label='The red data')\nfig.legend(loc='upper right',handles=[\n    mpatches.Patch(color='red', label='Ground truth'),\n    mpatches.Patch(color='green', label='Predicted')])\n\ni = 0\nwith torch.no_grad():\n    for idx in range(n_examples):\n        image, mask = test_ds[idx]\n        image = image.unsqueeze(0).to(device,dtype=torch.float)\n        mask = mask.unsqueeze(0).to(device,dtype=torch.float)\n        prediction = model.forward(image).cpu()[0][0]\n        prediction = torch.from_numpy(np.where(prediction > 0.5, 1, 0))\n        prediction_edges = prediction - binary_dilation(prediction)\n        mask = torch.from_numpy(np.where(mask[0][0].cpu() > 0.5, 1, 0))\n        ground_truth = mask - binary_dilation(mask)\n        image[0, 0, ground_truth.bool()] = 1\n        image[0, 1, prediction_edges.bool()] = 1\n        axs[i//width][i%width].imshow(image[0].to('cpu').permute(1, 2, 0))\n        if n_examples == i + 1:\n            break\n        i += 1","metadata":{"execution":{"iopub.status.busy":"2024-04-14T06:33:03.373012Z","iopub.execute_input":"2024-04-14T06:33:03.373629Z","iopub.status.idle":"2024-04-14T06:33:19.110623Z","shell.execute_reply.started":"2024-04-14T06:33:03.373596Z","shell.execute_reply":"2024-04-14T06:33:19.109000Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}