{"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":"markdown","source":"# Imports & Setup","metadata":{}},{"cell_type":"code","source":"%%capture\n! pip install wandb --upgrade","metadata":{"execution":{"iopub.status.busy":"2023-05-14T05:07:39.744668Z","iopub.execute_input":"2023-05-14T05:07:39.744981Z","iopub.status.idle":"2023-05-14T05:07:56.204572Z","shell.execute_reply.started":"2023-05-14T05:07:39.744950Z","shell.execute_reply":"2023-05-14T05:07:56.203175Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install transformers -q","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-05-14T05:07:56.207088Z","iopub.execute_input":"2023-05-14T05:07:56.207510Z","iopub.status.idle":"2023-05-14T05:08:07.794011Z","shell.execute_reply.started":"2023-05-14T05:07:56.207462Z","shell.execute_reply":"2023-05-14T05:08:07.792744Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import wandb\nwandb.login()","metadata":{"execution":{"iopub.status.busy":"2023-05-14T05:09:56.856530Z","iopub.execute_input":"2023-05-14T05:09:56.857021Z","iopub.status.idle":"2023-05-14T05:10:18.608612Z","shell.execute_reply.started":"2023-05-14T05:09:56.856976Z","shell.execute_reply":"2023-05-14T05:10:18.607240Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport cv2\nimport glob\nimport json\nimport matplotlib\nimport numpy as np \nimport pandas as pd\nfrom tqdm import tqdm\nimport tifffile as tiff \nimport tensorflow as tf\nimport albumentations as A\nfrom functools import partial\nfrom argparse import Namespace\nimport matplotlib.pyplot as plt\nimport tensorflow.keras.backend as K","metadata":{"execution":{"iopub.status.busy":"2023-05-14T05:10:32.988102Z","iopub.execute_input":"2023-05-14T05:10:32.988520Z","iopub.status.idle":"2023-05-14T05:10:42.353515Z","shell.execute_reply.started":"2023-05-14T05:10:32.988477Z","shell.execute_reply":"2023-05-14T05:10:42.352290Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"If only running with GPU accelerator, run this cell.","metadata":{}},{"cell_type":"code","source":"os.environ['TF_FORCE_GPU_ALLOW_GROWTH'] = 'true'","metadata":{"execution":{"iopub.status.busy":"2023-05-14T05:10:42.355897Z","iopub.execute_input":"2023-05-14T05:10:42.357784Z","iopub.status.idle":"2023-05-14T05:10:42.363783Z","shell.execute_reply.started":"2023-05-14T05:10:42.357739Z","shell.execute_reply":"2023-05-14T05:10:42.362062Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"configs = Namespace(\n    resize_img_height = 512,\n    resize_img_width = 512,\n    train_df_path = \"../input/hubmap-organ-segmentation/train.csv\",\n    train_image_path = \"../input/hubmap-organ-segmentation/train_images/\",\n    batch_size = 16,\n    do_cache = True\n)","metadata":{"execution":{"iopub.status.busy":"2023-05-14T05:10:42.365435Z","iopub.execute_input":"2023-05-14T05:10:42.365903Z","iopub.status.idle":"2023-05-14T05:10:42.375426Z","shell.execute_reply.started":"2023-05-14T05:10:42.365859Z","shell.execute_reply":"2023-05-14T05:10:42.374326Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Get Data\n\n* get data from csv \n* split into train and valid (shuffle first and split into 0.8 train data and 0.2 valid data)\n* return split_df(train_df, valid_df)","metadata":{}},{"cell_type":"code","source":"def download_dataset(\n    dataset_name: str, dataset_type: str, version: str = \"latest\", save_at=\"artifacts/\"\n):\n    \"\"\"\n    Utility function to download the data saved as W&B artifacts and return a dataframe\n    with path to the dataset and associated label.\n\n    Args:\n        dataset_name (str): The name of the dataset - `train`, `val`, `test`, `out-class`, and `in-class`.\n        dataset_type (str): The type of the dataset - `labelled-dataset`, `unlabelled-dataset`.\n        version (str): The version of the dataset to be downloaded. By default it's `latest`,\n            but you can provide different version as `vX`, where, X can be 0,1,...\n\n        Note that the following combination of dataset_name and dataset_type are valid:\n            - `train`, `RLE-TO-MASK dataset`\n\n    Return:\n        df_data (pandas.DataFrame): Dataframe with path to images with associated labels if present.\n    \"\"\"\n    if dataset_name == \"train\" and os.path.exists(save_at + \"wb_train.csv\"):\n        data_df = pd.read_csv(save_at + \"wb_train.csv\")\n    else:\n        data_df = None\n        print(\"Downloading dataset...\")\n\n    if data_df is None:\n        # Download the dataset.\n        wandb_api = wandb.Api()\n        artifact = wandb_api.artifact(\n            f\"cosmo3769/HuBMAP-HPA/{dataset_name}:{version}\", type=dataset_type\n        )\n        artifact_dir = artifact.download()\n\n        # Open the W&B table downloaded as a json file.\n        json_file = glob.glob(artifact_dir + \"/*.json\")\n        assert len(json_file) == 1\n        with open(json_file[0]) as f:\n            data = json.loads(f.read())\n            assert data[\"_type\"] == \"table\"\n            columns = data[\"columns\"]\n            data = data[\"data\"]\n\n        # Create a dataframe with path and label\n        df_columns = [\"image_id\", \n                      \"image_path\", \n                      \"mask_path\", \n                      \"mask_image_path\",\n                      \"organ\",\n                      \"data_source\",\n                      \"image_height\",\n                      \"image_width\",\n                      \"pixel_size\",\n                      \"tissue_thickness\",\n                      \"rle\",\n                      \"age\",\n                      \"sex\",\n                      ]\n        data_df = pd.DataFrame(columns=df_columns)\n\n        for idx, example in tqdm(enumerate(data)):\n            image_id = int(example[0])\n            image_path_dict = example[1]\n            mask_path_dict = example[2]\n            masked_image_path_dict = example[3]\n            image_path = os.path.join(artifact_dir, image_path_dict.get(\"path\"))\n            mask_path = os.path.join(artifact_dir, mask_path_dict.get(\"path\"))\n            mask_image_path = os.path.join(artifact_dir, masked_image_path_dict.get(\"path\"))\n            organ = example[4]\n            data_source = example[5]\n            image_height = image_path_dict.get(\"height\")\n            image_width = image_path_dict.get(\"width\")\n            pixel_size = example[8]\n            tissue_thickness = example[9]\n            rle = example[10]\n            age = example[11]\n            sex = example[12]\n\n            df_data = [image_id, \n                       image_path, \n                       mask_path,\n                       mask_image_path,\n                       organ,\n                       data_source,\n                       image_height,\n                       image_width,\n                       pixel_size,\n                       tissue_thickness,\n                       rle,\n                       age,\n                       sex\n                       ]\n            data_df.loc[idx] = df_data\n\n    # Shuffle only train dataframe\n    if dataset_name == \"train\":\n        data_df = data_df.sample(frac=1, random_state=42).reset_index(drop=True)\n\n    # Save the dataframes as csv\n    if dataset_name == \"train\" and not os.path.exists(save_at + \"wb_train.csv\"):\n        data_df.to_csv(save_at + \"wb_train.csv\", index=False)\n\n    return data_df\n\ndef split_dataframe(df):\n    train_df = df.sample(frac = 0.8).reset_index()\n    valid_df = df.drop(train_df.index).reset_index()\n    \n    return train_df, valid_df\n\ndef preprocess_dataframe(df):\n    df = df.drop([\"image_id\",\n                  \"mask_image_path\",\n                  \"organ\",\n                  \"data_source\",\n                  \"image_height\",\n                  \"image_width\",\n                  \"pixel_size\",\n                  \"tissue_thickness\",\n                  \"rle\",\n                  \"age\",\n                  \"sex\"\n                  ], axis=1)\n    image_paths = df.image_path.values\n    mask_paths = df.mask_path.values\n    \n    return image_paths, mask_paths","metadata":{"execution":{"iopub.status.busy":"2023-05-14T05:10:47.609217Z","iopub.execute_input":"2023-05-14T05:10:47.609638Z","iopub.status.idle":"2023-05-14T05:10:47.632321Z","shell.execute_reply.started":"2023-05-14T05:10:47.609603Z","shell.execute_reply":"2023-05-14T05:10:47.631000Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = download_dataset(\"train\", \"RLE-TO-MASK dataset\")\ntrain_df, valid_df = split_dataframe(df)\ntrain_image, train_mask = preprocess_dataframe(train_df)\nvalid_image, valid_mask = preprocess_dataframe(valid_df)","metadata":{"execution":{"iopub.status.busy":"2023-05-14T05:11:03.996752Z","iopub.execute_input":"2023-05-14T05:11:03.997508Z","iopub.status.idle":"2023-05-14T05:11:30.266356Z","shell.execute_reply.started":"2023-05-14T05:11:03.997467Z","shell.execute_reply":"2023-05-14T05:11:30.265239Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Utility Function","metadata":{}},{"cell_type":"code","source":"# https://www.kaggle.com/paulorzp/rle-functions-run-length-encode-decode\ndef mask2rle(img):\n    '''\n    img: numpy array, 1 - mask, 0 - background\n    Returns run length as string formated\n    '''\n    pixels= img.T.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n\ndef rle2mask(mask_rle, shape=(1600,256)):\n    '''\n    mask_rle: run-length as string formated (start length)\n    shape: (width,height) of array to return \n    Returns numpy array, 1 - mask, 0 - background\n\n    '''\n    s = mask_rle.split()\n    starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]\n    starts -= 1\n    ends = starts + lengths\n    img = np.zeros(shape[0]*shape[1], dtype=np.uint8)\n    for lo, hi in zip(starts, ends):\n        img[lo:hi] = 1\n    return img.reshape(shape).T","metadata":{"execution":{"iopub.status.busy":"2023-05-14T05:11:37.132865Z","iopub.execute_input":"2023-05-14T05:11:37.133261Z","iopub.status.idle":"2023-05-14T05:11:37.144972Z","shell.execute_reply.started":"2023-05-14T05:11:37.133227Z","shell.execute_reply":"2023-05-14T05:11:37.143848Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Get Dataloader\n\n* parse data (resize train_image & valid_image, rletomask train_mask & valid_mask, resize train_mask & valid_mask)\n* dataset from tensor slices\n* augmentations\n* return dataloader","metadata":{}},{"cell_type":"code","source":"AUTOTUNE = tf.data.AUTOTUNE\n\nclass GetDataloader:\n    def __init__(self, args):\n        self.args = args\n\n    def get_dataloader(self, image, mask, dataloader_type=\"train\"):\n        \"\"\"\n        Args:\n            image: List of strings, where each string is path to the image.\n            mask: List of strings, where each string is path to the mask.\n            dataloader_type: Anyone of one train or valid.\n\n        Return:\n            dataloader: train or validation dataloader\n        \"\"\"\n        # Consume dataframe\n        dataloader = tf.data.Dataset.from_tensor_slices((image, mask))\n\n        # Load the image\n        dataloader = dataloader.map(\n            partial(self.parse_data, dataloader_type=dataloader_type),\n            num_parallel_calls=AUTOTUNE,\n        )\n\n        if self.args.do_cache:\n            dataloader = dataloader.cache()\n\n        # Add augmentation to dataloader for training\n        if dataloader_type == \"train\":\n            self.transform = self.build_augmentation()\n            dataloader = dataloader.map(self.augmentation, num_parallel_calls=AUTOTUNE)\n\n        # Add general stuff\n        dataloader = dataloader.batch(self.args.batch_size)\n        \n        # Rename the elements in the dataset\n        dataloader = dataloader.map(lambda x, y: {\"pixel_values\": x, \"labels\": y})\n\n        return dataloader\n\n    def decode_image(self, image, mask, dataloader_type=\"train\"):\n        # convert the compressed string to a 3D uint8 tensor\n        image = tf.image.decode_jpeg(image, channels=3)\n        mask = tf.image.decode_jpeg(mask, channels=0)\n        # squeeze mask at axis -1\n        mask = tf.squeeze(mask)\n        # Normalize image\n        image = tf.image.convert_image_dtype(image, dtype=tf.float32)\n        mask = tf.image.convert_image_dtype(mask, dtype=tf.float32)\n        # set shape\n        if dataloader_type == \"valid\":\n            image = tf.reshape(\n                image,\n                [self.args.resize_img_height,\n                self.args.resize_img_width,\n                3]\n            )\n            mask = tf.reshape(\n                    mask,\n                    [self.args.resize_img_height,\n                    self.args.resize_img_width\n                    ]\n            )\n            # for matching segformer input shape\n            image = tf.transpose(image, (2, 0, 1))\n\n        return image, mask\n\n    def parse_data(self, image, mask, dataloader_type=\"train\"):\n        # Parse Image\n        image = tf.io.read_file(image)\n        mask = tf.io.read_file(mask)\n        image, mask = self.decode_image(image, mask, dataloader_type)\n\n        return image, mask\n\n    def build_augmentation(self):\n        transform = A.Compose(\n            [\n                A.HorizontalFlip(),\n                A.VerticalFlip(),\n                A.RandomRotate90(),\n                A.RandomBrightnessContrast(),\n                A.ShiftScaleRotate(shift_limit=0.0625, scale_limit=0.2, rotate_limit=15, p=0.9, \n                              border_mode=cv2.BORDER_REFLECT)\n            ],\n            p = 1.0\n        )\n\n        return transform\n\n    def augmentation(self, image, mask):\n        aug_img = tf.numpy_function(func=self.aug_fn, inp=[image], Tout=tf.float32)\n        aug_msk = tf.numpy_function(func=self.aug_fn, inp=[mask], Tout=tf.float32)\n        aug_img.set_shape(\n            (\n                self.args.resize_img_height,\n                self.args.resize_img_width,\n                3,\n            )\n        )\n        aug_msk.set_shape(\n            (\n                self.args.resize_img_height,\n                self.args.resize_img_width,\n            )\n        )\n        # for matching segformer input shape\n        aug_img = tf.transpose(aug_img, (2, 0, 1))\n\n        return aug_img, aug_msk\n\n    def aug_fn(self, image):\n        img_data = {\"image\": image}\n        img_aug_data = self.transform(**img_data)\n        aug_img = img_aug_data[\"image\"]\n\n        return aug_img.astype(np.float32)","metadata":{"execution":{"iopub.status.busy":"2023-05-14T06:25:58.179945Z","iopub.execute_input":"2023-05-14T06:25:58.180531Z","iopub.status.idle":"2023-05-14T06:25:58.204070Z","shell.execute_reply.started":"2023-05-14T06:25:58.180488Z","shell.execute_reply":"2023-05-14T06:25:58.202648Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataset = GetDataloader(configs)\ntrainloader = dataset.get_dataloader(\n    train_image, train_mask, dataloader_type=\"train\"\n)\nvalidloader = dataset.get_dataloader(\n    valid_image, valid_mask, dataloader_type=\"valid\"\n)","metadata":{"execution":{"iopub.status.busy":"2023-05-14T06:25:58.928464Z","iopub.execute_input":"2023-05-14T06:25:58.929623Z","iopub.status.idle":"2023-05-14T06:25:59.294250Z","shell.execute_reply.started":"2023-05-14T06:25:58.929572Z","shell.execute_reply":"2023-05-14T06:25:59.293202Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"trainloader","metadata":{"execution":{"iopub.status.busy":"2023-05-14T06:26:01.079607Z","iopub.execute_input":"2023-05-14T06:26:01.081567Z","iopub.status.idle":"2023-05-14T06:26:01.089675Z","shell.execute_reply.started":"2023-05-14T06:26:01.081515Z","shell.execute_reply":"2023-05-14T06:26:01.088449Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"validloader","metadata":{"execution":{"iopub.status.busy":"2023-05-14T06:26:06.983827Z","iopub.execute_input":"2023-05-14T06:26:06.984881Z","iopub.status.idle":"2023-05-14T06:26:06.991839Z","shell.execute_reply.started":"2023-05-14T06:26:06.984837Z","shell.execute_reply":"2023-05-14T06:26:06.990679Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# https://keras.io/examples/vision/segformer/\nimport matplotlib.pyplot as plt\n\n\ndef display(display_list):\n    plt.figure(figsize=(15, 15))\n\n    title = [\"Input Image\", \"True Mask\", \"Predicted Mask\"]\n\n    for i in range(len(display_list)):\n        plt.subplot(1, len(display_list), i + 1)\n        plt.title(title[i])\n        plt.imshow(tf.keras.utils.array_to_img(display_list[i]))\n        plt.axis(\"off\")\n    plt.show()\n\n    \nfor samples in trainloader.take(2):\n    sample_image, sample_mask = samples[\"pixel_values\"][0], samples[\"labels\"][0]\n    sample_image = tf.transpose(sample_image, (1, 2, 0))\n    sample_mask = tf.expand_dims(sample_mask, -1)\n    display([sample_image, sample_mask])","metadata":{"execution":{"iopub.status.busy":"2023-05-14T06:29:13.433128Z","iopub.execute_input":"2023-05-14T06:29:13.433949Z","iopub.status.idle":"2023-05-14T06:29:15.094256Z","shell.execute_reply.started":"2023-05-14T06:29:13.433905Z","shell.execute_reply":"2023-05-14T06:29:15.093336Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model","metadata":{}},{"cell_type":"code","source":"from transformers import TFSegformerForSemanticSegmentation\n\nmodel_checkpoint = \"nvidia/mit-b0\"\nid2label = {0: \"outer\", 1: \"inner\", 2: \"border\"}\nlabel2id = {label: id for id, label in id2label.items()}\nnum_labels = len(id2label)\nmodel = TFSegformerForSemanticSegmentation.from_pretrained(\n    model_checkpoint,\n    num_labels=num_labels,\n    id2label=id2label,\n    label2id=label2id,\n    ignore_mismatched_sizes=True,\n)","metadata":{"execution":{"iopub.status.busy":"2023-05-14T06:08:33.773238Z","iopub.execute_input":"2023-05-14T06:08:33.774385Z","iopub.status.idle":"2023-05-14T06:08:48.211822Z","shell.execute_reply.started":"2023-05-14T06:08:33.774333Z","shell.execute_reply":"2023-05-14T06:08:48.209937Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# https://keras.io/examples/vision/segformer/\nfrom IPython.display import clear_output\n\n\ndef create_mask(pred_mask):\n    pred_mask = tf.math.argmax(pred_mask, axis=1)\n    pred_mask = tf.expand_dims(pred_mask, -1)\n    return pred_mask[0]\n\n\ndef show_predictions(dataset=None, num=1):\n    if dataset:\n        for sample in dataset.take(num):\n            images, masks = sample[\"pixel_values\"], sample[\"labels\"]\n            masks = tf.expand_dims(masks, -1)\n            pred_masks = model.predict(images).logits\n            images = tf.transpose(images, (0, 2, 3, 1))\n            display([images[0], masks[0], create_mask(pred_masks)])\n    else:\n        display(\n            [\n                sample_image,\n                sample_mask,\n                create_mask(model.predict(tf.expand_dims(sample_image, 0))),\n            ]\n        )\n\n\nclass DisplayCallback(tf.keras.callbacks.Callback):\n    def __init__(self, dataset, **kwargs):\n        super().__init__(**kwargs)\n        self.dataset = dataset\n\n    def on_epoch_end(self, epoch, logs=None):\n        clear_output(wait=True)\n        show_predictions(self.dataset)\n        print(\"\\nSample Prediction after epoch {}\\n\".format(epoch + 1))","metadata":{"execution":{"iopub.status.busy":"2023-05-14T06:33:33.940986Z","iopub.execute_input":"2023-05-14T06:33:33.941435Z","iopub.status.idle":"2023-05-14T06:33:33.954718Z","shell.execute_reply.started":"2023-05-14T06:33:33.941395Z","shell.execute_reply":"2023-05-14T06:33:33.953434Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# #https://www.kaggle.com/code/queyrusi/vanilla-submission-seresnext50\n\n# def dice_coeff(y_true, y_pred, epsilon=1.):\n    \n#     \"\"\"\n#     Calculates dice coefficient\n\n#     Arguments: \n#             y_true : tensor of ground truth values.\n#             y_pred : tensor of predicted values.\n#             epsilon : constant to avoid divide by 0 errors.\n    \n#     Returns:\n#             dice_coefficient\n#     \"\"\"\n    \n#     y_true_f = K.flatten(y_true)\n#     y_pred_f = K.flatten(y_pred)\n#     intersection = K.sum(y_true_f * y_pred_f)\n#     score = (2. * intersection + epsilon) / (K.sum(y_true_f) + K.sum(y_pred_f) + epsilon)\n#     return score","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"lr = 0.00006\noptimizer = tf.keras.optimizers.Adam(learning_rate=lr)\nmodel.compile(optimizer=optimizer)","metadata":{"execution":{"iopub.status.busy":"2023-05-14T06:08:54.553602Z","iopub.execute_input":"2023-05-14T06:08:54.554536Z","iopub.status.idle":"2023-05-14T06:08:54.579579Z","shell.execute_reply.started":"2023-05-14T06:08:54.554496Z","shell.execute_reply":"2023-05-14T06:08:54.578556Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Increase the number of epochs if the results are not of expected quality.\nepochs = 10\n\nhistory = model.fit(\n    trainloader,\n    validation_data=validloader,\n    callbacks=[DisplayCallback(validloader)],\n    epochs=epochs,\n)","metadata":{"execution":{"iopub.status.busy":"2023-05-14T06:33:49.433221Z","iopub.execute_input":"2023-05-14T06:33:49.434436Z","iopub.status.idle":"2023-05-14T06:35:53.185042Z","shell.execute_reply.started":"2023-05-14T06:33:49.434374Z","shell.execute_reply":"2023-05-14T06:35:53.183911Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"show_predictions(validloader, 5)","metadata":{"execution":{"iopub.status.busy":"2023-05-14T06:36:45.856460Z","iopub.execute_input":"2023-05-14T06:36:45.857661Z","iopub.status.idle":"2023-05-14T06:36:49.205990Z","shell.execute_reply.started":"2023-05-14T06:36:45.857620Z","shell.execute_reply":"2023-05-14T06:36:49.204958Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}