{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install /kaggle/input/downloaded-wheels/dicomsdl-0.109.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl","metadata":{"execution":{"iopub.status.busy":"2023-02-26T17:39:00.40238Z","iopub.execute_input":"2023-02-26T17:39:00.402868Z","iopub.status.idle":"2023-02-26T17:39:13.490404Z","shell.execute_reply.started":"2023-02-26T17:39:00.402825Z","shell.execute_reply":"2023-02-26T17:39:13.488808Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nfrom torch import nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom torch.utils.data import WeightedRandomSampler\nimport torchvision \nfrom torchvision import datasets, transforms\n# import pydicom as pdc\nfrom typing import Tuple, List, Dict\nfrom pathlib import Path\nfrom PIL import Image\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\nimport os\nimport gc\nimport dicomsdl as dcm\nimport multiprocess as mp\nimport torch.multiprocessing as torch_mp\nfrom torch.utils.data.distributed import DistributedSampler\nfrom torch.nn.parallel import DistributedDataParallel as DDP\nfrom torch.distributed import init_process_group, destroy_process_group\nimport socket","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-02-26T17:39:13.494335Z","iopub.execute_input":"2023-02-26T17:39:13.495377Z","iopub.status.idle":"2023-02-26T17:39:16.636683Z","shell.execute_reply.started":"2023-02-26T17:39:13.495311Z","shell.execute_reply":"2023-02-26T17:39:16.635281Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = torch.device(0 if torch.cuda.is_available() else \"cpu\")\nprint(f\"Accelerated device available: {device}\")","metadata":{"execution":{"iopub.status.busy":"2023-02-26T17:39:16.638036Z","iopub.execute_input":"2023-02-26T17:39:16.638926Z","iopub.status.idle":"2023-02-26T17:39:16.645893Z","shell.execute_reply.started":"2023-02-26T17:39:16.638881Z","shell.execute_reply":"2023-02-26T17:39:16.644565Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def ddp_setup(rank, world_size):\n    sock = socket.socket()\n    sock.bind(('', 0))\n    port = sock.getsockname()[1]\n    os.environ[\"MASTER_ADDR\"] = \"localhost\"\n    os.environ[\"MASTER_PORT\"] = port\n    init_process_group(backend=\"nccl\", rank= rank , world_size= world_size)","metadata":{"execution":{"iopub.status.busy":"2023-02-26T17:39:16.649286Z","iopub.execute_input":"2023-02-26T17:39:16.650319Z","iopub.status.idle":"2023-02-26T17:39:16.658308Z","shell.execute_reply.started":"2023-02-26T17:39:16.650275Z","shell.execute_reply":"2023-02-26T17:39:16.656934Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_csv = pd.read_csv(\"/kaggle/input/rsna-breast-cancer-detection/train.csv\")\ntest_csv = pd.read_csv(\"/kaggle/input/rsna-breast-cancer-detection/test.csv\")\n\ntrain_csv.shape, test_csv.shape","metadata":{"execution":{"iopub.status.busy":"2023-02-26T17:39:16.659869Z","iopub.execute_input":"2023-02-26T17:39:16.661258Z","iopub.status.idle":"2023-02-26T17:39:16.805878Z","shell.execute_reply.started":"2023-02-26T17:39:16.661195Z","shell.execute_reply":"2023-02-26T17:39:16.804611Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_csv.head()","metadata":{"execution":{"iopub.status.busy":"2023-02-26T17:39:16.809641Z","iopub.execute_input":"2023-02-26T17:39:16.810343Z","iopub.status.idle":"2023-02-26T17:39:16.839828Z","shell.execute_reply.started":"2023-02-26T17:39:16.810295Z","shell.execute_reply":"2023-02-26T17:39:16.838641Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pydicom\n# import numpy as np\nimport cv2\n# import os\nfrom joblib import Parallel, delayed\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\n\nRESIZE_TO = (224, 224)\n\n!rm -rf test_images_processed_cv2_vl_{RESIZE_TO[0]}\n!mkdir test_images_processed_cv2_vl_{RESIZE_TO[0]}\n\n# https://www.kaggle.com/code/tanlikesmath/brain-tumor-radiogenomic-classification-eda/notebook\ndef dicom_file_to_ary(path):\n    try:\n        dicom = pydicom.dcmread(path)\n        data = dicom.pixel_array\n        data = apply_voi_lut(dicom.pixel_array, dicom)\n        \n    except RuntimeError:\n        dicom = dcm.open(str(path))\n        data = dicom.pixelData()\n\n    data = (data - data.min()) / (data.max() - data.min())\n\n    if dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        data = 1 - data\n\n    data = cv2.resize(data, RESIZE_TO)\n    data = (data * 255).astype(np.uint8)\n    return data\n\ndirectories = list(Path('/kaggle/input/rsna-breast-cancer-detection/test_images').iterdir())\n\ndef process_directory(directory_path):\n    parent_directory = str(directory_path).split('/')[-1]\n    !mkdir -p test_images_processed_cv2_vl_{RESIZE_TO[0]}/{parent_directory}\n    for image_path in directory_path.iterdir():\n        processed_ary = dicom_file_to_ary(image_path)\n        \n        cv2.imwrite(\n            f'test_images_processed_cv2_vl_{RESIZE_TO[0]}/{parent_directory}/{image_path.stem}.png',\n            processed_ary\n        )\n        \nimport multiprocessing as mp\n\nwith mp.Pool(os.cpu_count()) as p:\n    p.map(process_directory, directories)","metadata":{"execution":{"iopub.status.busy":"2023-02-26T17:39:16.84221Z","iopub.execute_input":"2023-02-26T17:39:16.842749Z","iopub.status.idle":"2023-02-26T17:39:23.899615Z","shell.execute_reply.started":"2023-02-26T17:39:16.842694Z","shell.execute_reply":"2023-02-26T17:39:23.897695Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class RSNACustomDataset(Dataset):\n    \n    def __init__(self,\n                 train_csv,\n                 img_dir: str,\n                 transforms= None,\n                ):\n        \n        self.img_paths = list(Path(img_dir).glob(\"*/*.png\"))\n        self.train_csv = train_csv.copy()\n        self.transform = transforms\n        self.cancer_neg = self.train_csv.query(\"cancer == 0\")[\"image_id\"].tolist()\n        \n    def load_image(self, index:int) -> Image.Image:\n        return Image.open(self.img_paths[index])\n#         return np.asarray(Image.open(self.img_paths[index])).astype(np.int32)\n    \n    def __len__(self) -> int:\n        return len(self.img_paths)\n    \n    def __getitem__(self, index:int) -> Tuple[torch.Tensor, int]:\n        img_arr = self.load_image(index)\n        class_name= 0 if self.img_paths[index].stem in self.cancer_neg else 1\n        \n        if self.transform:\n            return self.transform(img_arr), class_name\n        else:\n            return img_arr, class_name","metadata":{"execution":{"iopub.status.busy":"2023-02-26T17:39:23.902219Z","iopub.execute_input":"2023-02-26T17:39:23.902797Z","iopub.status.idle":"2023-02-26T17:39:23.915127Z","shell.execute_reply.started":"2023-02-26T17:39:23.902733Z","shell.execute_reply":"2023-02-26T17:39:23.913913Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class RSNACustomTestDataset(Dataset):\n    \n    def __init__(self,\n#                  test_csv,\n                 img_dir: str,\n                 transforms= None,\n                ):\n        \n#         self.img_paths = list(Path(img_dir).glob(\"*/*.dcm\"))\n        self.img_paths = list(Path(img_dir).glob(\"*/*.png\"))\n        self.test_csv = test_csv.copy()\n        self.transform = transforms\n#         self.cancer_neg = self.train_csv.query(\"cancer == 0\")[\"image_id\"].tolist()\n        \n    def load_image(self, index:int) -> Image.Image:\n#         return Image.fromarray(dicom_file_to_ary(self.img_paths[index]))\n        return Image.open(self.img_paths[index])\n    \n    def __len__(self) -> int:\n        return len(self.img_paths)\n    \n    def __getitem__(self, index:int) -> Tuple[torch.Tensor, int]:\n        img_arr = self.load_image(index)\n        img_id = int(self.img_paths[index].stem)\n        \n#         class_name= 0 if self.img_paths[index].stem in self.cancer_neg else 1\n        \n        if self.transform:\n            return self.transform(img_arr), img_id\n        else:\n            return img_arr, img_id","metadata":{"execution":{"iopub.status.busy":"2023-02-26T17:39:23.916796Z","iopub.execute_input":"2023-02-26T17:39:23.917757Z","iopub.status.idle":"2023-02-26T17:39:23.935286Z","shell.execute_reply.started":"2023-02-26T17:39:23.917711Z","shell.execute_reply":"2023-02-26T17:39:23.933862Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"IMAGE_SIZE = 224\ntrain_transforms = transforms.Compose([\n        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n        transforms.TrivialAugmentWide(num_magnitude_bins=31),\n        transforms.ToTensor()\n])\n\ntest_transforms = transforms.Compose([\n        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n        transforms.ToTensor()\n])","metadata":{"execution":{"iopub.status.busy":"2023-02-26T17:39:23.938768Z","iopub.execute_input":"2023-02-26T17:39:23.939868Z","iopub.status.idle":"2023-02-26T17:39:23.952046Z","shell.execute_reply.started":"2023-02-26T17:39:23.939817Z","shell.execute_reply":"2023-02-26T17:39:23.950684Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"_ds_train = RSNACustomDataset(train_csv=train_csv, \n                             img_dir= \"/kaggle/input/rsna-mammography-images-as-pngs/images_as_pngs_cv2_vl_256/train_images_processed_cv2_vl_256\",\n                             transforms=train_transforms)\n_ds_test = RSNACustomTestDataset(img_dir= \"/kaggle/working/test_images_processed_cv2_vl_224\",\n#                                  train_csv=test_csv, \n                                 transforms=test_transforms)\nprint(f\"Length of the train dataset is :{_ds_train.__len__()}\")\nprint(f\"Length of the test dataset is :{_ds_test.__len__()}\")","metadata":{"execution":{"iopub.status.busy":"2023-02-26T16:23:44.453895Z","iopub.execute_input":"2023-02-26T16:23:44.454292Z","iopub.status.idle":"2023-02-26T16:25:59.526759Z","shell.execute_reply.started":"2023-02-26T16:23:44.454254Z","shell.execute_reply":"2023-02-26T16:25:59.525388Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_training_testing_data(train_dataset: Dataset, test_dataset: Dataset,\n                               train_batch_size: int, test_batch_size:int,\n                               train_csv: pd.DataFrame):\n    \n    train_counts = train_csv.cancer.value_counts()\n    train_sample_weights = [1/train_counts[i] for i in train_csv.cancer.values]\n    \n    train_sampler = WeightedRandomSampler(weights= train_sample_weights, num_samples= len(train_dataset), replacement= False) \n    \n    ds_train = DataLoader(train_dataset, \n                         batch_size=train_batch_size, \n                         shuffle = False,\n                         sampler=train_sampler,\n    #                      num_workers=os.cpu_count()\n                         )\n    ds_test = DataLoader(test_dataset, \n                         batch_size=test_batch_size, \n                         shuffle = False,\n#                          sampler = DistributedSampler(_ds_test)\n    #                      num_workers=os.cpu_count()\n                         )\n    return ds_train, ds_test\nds_train, ds_test = load_training_testing_data(_ds_train, _ds_test, \n                                              512, 1024,\n                                              train_csv)\nprint(ds_train, ds_test, sep=\"\\n\")","metadata":{"execution":{"iopub.status.busy":"2023-02-26T17:35:46.660482Z","iopub.execute_input":"2023-02-26T17:35:46.660911Z","iopub.status.idle":"2023-02-26T17:35:46.934328Z","shell.execute_reply.started":"2023-02-26T17:35:46.660868Z","shell.execute_reply":"2023-02-26T17:35:46.933049Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Model_train():\n    \n    def __init__(self,\n                 model: torch.nn.Module,\n                 train_dl: torch.utils.data.DataLoader,\n                 test_dl: torch.utils.data.DataLoader,\n                 optimizer: torch.optim.Optimizer, \n                 loss_fn: torch.nn.Module, \n                 device = device,\n                ):\n        \n        self.model = model.to(device)\n        self.device = device\n        self.train_loader = train_dl\n        self.test_loader = test_dl\n        self.optim = optimizer\n        self.loss = loss_fn\n#         self.model = DDP(self.model, device_ids = [self.device])\n        self.model = nn.DataParallel(self.model,device_ids=[0,1])\n        \n    def pfbeta(self, labels, predictions, beta=1):\n        \"\"\"\n        Returns the probabilistic F1 score \n        \n        labels -- True labels\n        predictions -- predicted values by the model\n        beta -- not sure yet \n        \"\"\"\n        y_true_count = 0\n        ctp = 0\n        cfp = 0\n\n        for idx in range(len(labels)):\n            prediction = min(max(predictions[idx], 0), 1)\n            if (labels[idx]):\n                y_true_count += 1\n                ctp += prediction\n            else:\n                cfp += prediction\n\n        beta_squared = beta * beta\n        c_precision = ctp / (ctp + cfp)\n        c_recall = ctp / y_true_count\n        if (c_precision > 0 and c_recall > 0):\n            result = (1 + beta_squared) * (c_precision * c_recall) / (beta_squared * c_precision + c_recall)\n            return result\n        else:\n            return 0\n        \n    def train_loop(self):\n        \"\"\"\n        Training loop for model\n        \"\"\"\n        self.model.train()\n        train_loss, train_pfscore = 0,0\n        \n        for X, y in tqdm(self.train_loader):\n            X, y = X.to(device), y.to(device).type(torch.float)\n            y_logits = self.model(X).squeeze()\n            loss = self.loss(y_logits, y)\n            train_loss += loss.item()\n            self.optim.zero_grad()\n            loss.mean().backward()\n            self.optim.step()\n            y_pred = torch.round(torch.sigmoid(y_logits))\n            train_pfscore = self.pfbeta(y, y_pred)\n        \n        train_loss /= len(self.train_loader)\n        train_pfscore /= len(self.train_loader)\n        \n        return train_loss, train_pfscore\n    \n    def test_loop(self):\n        \"\"\"\n        Testing loop for model\n        \"\"\"\n        \n        self.model.eval()\n        with torch.inference_mode():\n            \n            test_df= {\"image_id\":[], \n                     \"prediction\":[]}\n            \n            for X, y in tqdm(self.test_loader):\n                X, y= X.to(device), y.tolist()\n                \n                test_logits = self.model(X).squeeze()\n                test_pred =torch.sigmoid(test_logits)\n                \n                test_df[\"prediction\"].extend(test_pred.tolist())\n                test_df[\"image_id\"].extend(y)\n            \n            return pd.DataFrame(test_df)\n        \n    def prediction(self, X, logits= False):\n        \"\"\"\n        Returns logits from the model\n\n        \"\"\"\n        self.model.eval()\n        X = X.to(device)\n        with torch.inference_mode():\n                y_logits = self.model(X)\n                y_pred = torch.sigmoid(y_logits)\n                return y_pred\n    \n    def train(self, epochs=3):\n#         if mp_state:\n#             results = {\"train_loss\": [],\n#                            \"train_pfscore\": [],\n#                           }\n#             for epoch in range(epochs):\n#                 print(\"Epoch {}..........\".format(epoch+1))\n                \n#                 train_loss, train_pfscore = self.train_loop()\n#                 print(\"Train loss: {:.5f} | Train pfscore: {:.5f}\".format(train_loss, train_pfscore))\n            \n#                 results[\"train_loss\"].append(train_loss)\n#                 results[\"train_pfscore\"].append(train_pfscore)\n            \n#             pred_df = self.test_loop()\n\n#             return results\n        \n#         else:\n        results = {\"train_loss\": [],\n                       \"train_pfscore\": [],\n                      }\n        for epoch in range(epochs):\n            print(\"Epoch {}..........\".format(epoch+1))\n\n            train_loss, train_pfscore = self.train_loop()\n\n            print(\"Train loss: {:.5f} | Train pfscore: {:.5f}\".format(train_loss, train_pfscore))\n\n            results[\"train_loss\"].append(train_loss)\n            results[\"train_pfscore\"].append(train_pfscore)\n\n        pred_df = self.test_loop()\n\n        return results, pred_df\n","metadata":{"execution":{"iopub.status.busy":"2023-02-25T12:19:28.442575Z","iopub.execute_input":"2023-02-25T12:19:28.443028Z","iopub.status.idle":"2023-02-25T12:19:28.461152Z","shell.execute_reply.started":"2023-02-25T12:19:28.442994Z","shell.execute_reply":"2023-02-25T12:19:28.460106Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_0 = torchvision.models.resnet18(weights = None)\nmodel_0.conv1 = torch.nn.Conv2d(1, 64, (7, 7), (2, 2), (3, 3), bias=False)\nnum_features = model_0.fc.in_features\nmodel_0.fc = torch.nn.Sequential(\n    nn.Linear(in_features= num_features, out_features= 16), \n    nn.Linear(in_features= 16, out_features= 1))\nmodel_0 = model_0.to(device)\n\noptimizer = torch.optim.SGD(params = model_0.parameters(), \n                           lr = 0.01)\nloss_fn = nn.BCEWithLogitsLoss().to(device)","metadata":{"execution":{"iopub.status.busy":"2023-02-25T12:19:28.984986Z","iopub.execute_input":"2023-02-25T12:19:28.985933Z","iopub.status.idle":"2023-02-25T12:19:29.182375Z","shell.execute_reply.started":"2023-02-25T12:19:28.985871Z","shell.execute_reply":"2023-02-25T12:19:29.181381Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_trainer = Model_train(model = model_0,\n                           train_dl = ds_train, \n                           test_dl = ds_test, \n                           optimizer=optimizer, \n                           loss_fn=loss_fn,)","metadata":{"execution":{"iopub.status.busy":"2023-02-25T12:22:09.345096Z","iopub.execute_input":"2023-02-25T12:22:09.345477Z","iopub.status.idle":"2023-02-25T12:22:09.354269Z","shell.execute_reply.started":"2023-02-25T12:22:09.345444Z","shell.execute_reply":"2023-02-25T12:22:09.353158Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def main(rank:int, world_size:int, total_epochs:int):\n    ddp_setup(rank, world_size)\n    ds_train, ds_test = load_training_testing_data()\n    model_trainer = Model_train(model = model_0,\n                           train_dl = ds_train, \n                           test_dl = ds_test, \n                           optimizer=optimizer, \n                           loss_fn=loss_fn,\n                           device = rank)\n    model_results, prediction_df = model_trainer.train(epochs = 1)\n    destroy_process_group()\n    return model_results, prediction_df","metadata":{"execution":{"iopub.status.busy":"2023-02-25T12:22:10.586383Z","iopub.execute_input":"2023-02-25T12:22:10.586767Z","iopub.status.idle":"2023-02-25T12:22:10.592845Z","shell.execute_reply.started":"2023-02-25T12:22:10.586733Z","shell.execute_reply":"2023-02-25T12:22:10.591805Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_0_results, prediction_df = model_trainer.train(epochs = 5)","metadata":{"execution":{"iopub.status.busy":"2023-02-25T12:22:12.555466Z","iopub.execute_input":"2023-02-25T12:22:12.555847Z","iopub.status.idle":"2023-02-25T12:23:28.059421Z","shell.execute_reply.started":"2023-02-25T12:22:12.555812Z","shell.execute_reply":"2023-02-25T12:23:28.057334Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# if __name__ == \"__main__\":\n#     total_epochs = 3\n#     world_size = torch.cuda.device_count()\n#     model_0_results, prediction_id = torch_mp.spawn(main, args=(world_size, total_epochs), nprocs= world_size)\n# #     model_0_results, prediction_id = main(device, total_epochs)","metadata":{"execution":{"iopub.status.busy":"2023-02-25T12:23:28.060587Z","iopub.status.idle":"2023-02-25T12:23:28.061826Z","shell.execute_reply.started":"2023-02-25T12:23:28.061532Z","shell.execute_reply":"2023-02-25T12:23:28.061558Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub_csv = test_csv.loc[:, [\"image_id\", \"prediction_id\"]].merge(prediction_df, on= \"image_id\")\nsub_csv.pop(\"image_id\")\nsub_csv = sub_csv.rename(columns={\"prediction\":\"cancer\"})\nsub_csv = sub_csv.groupby(\"prediction_id\").mean()\nsub_csv = sub_csv.reset_index()","metadata":{"execution":{"iopub.status.busy":"2023-02-25T12:23:28.063103Z","iopub.status.idle":"2023-02-25T12:23:28.063998Z","shell.execute_reply.started":"2023-02-25T12:23:28.063733Z","shell.execute_reply":"2023-02-25T12:23:28.06376Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub_csv.head()","metadata":{"execution":{"iopub.status.busy":"2023-02-25T12:23:28.065452Z","iopub.status.idle":"2023-02-25T12:23:28.066557Z","shell.execute_reply.started":"2023-02-25T12:23:28.066289Z","shell.execute_reply":"2023-02-25T12:23:28.066316Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub_csv.to_csv(\"submission.csv\", index= False)","metadata":{"execution":{"iopub.status.busy":"2023-02-25T12:23:28.068079Z","iopub.status.idle":"2023-02-25T12:23:28.068938Z","shell.execute_reply.started":"2023-02-25T12:23:28.06865Z","shell.execute_reply":"2023-02-25T12:23:28.068693Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# !nvidia-smi","metadata":{"execution":{"iopub.status.busy":"2023-02-24T20:12:20.210466Z","iopub.execute_input":"2023-02-24T20:12:20.210933Z","iopub.status.idle":"2023-02-24T20:12:21.303934Z","shell.execute_reply.started":"2023-02-24T20:12:20.210893Z","shell.execute_reply":"2023-02-24T20:12:21.30275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}