{"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","metadata":{}},{"cell_type":"code","source":"import os\nimport gc\nimport glob\nimport json\nfrom collections import defaultdict\nimport multiprocessing as mp\nfrom pathlib import Path\nfrom types import SimpleNamespace\nfrom typing import Dict, List, Optional, Tuple\nimport warnings\nimport tifffile\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\nimport numpy as np\nimport pandas as pd\nimport PIL.Image as Image\nfrom sklearn.metrics import fbeta_score\nfrom sklearn.exceptions import UndefinedMetricWarning\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torch.nn.functional as F\nimport torch.utils.data as thd\nfrom tqdm import tqdm","metadata":{"execution":{"iopub.status.busy":"2023-05-29T11:53:57.476942Z","iopub.execute_input":"2023-05-29T11:53:57.477879Z","iopub.status.idle":"2023-05-29T11:54:00.476190Z","shell.execute_reply.started":"2023-05-29T11:53:57.477821Z","shell.execute_reply":"2023-05-29T11:54:00.475112Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Set up data","metadata":{}},{"cell_type":"code","source":"class PatchDataset(thd.Dataset):\n    def __init__(self, fragment_paths, patch_size, step_size):\n        self.fragment_paths = fragment_paths\n        self.patch_size = patch_size\n        self.step_size = step_size\n        \n        # Calculate the z-range, y-dim, and x-dim from the patch size\n        (z_start, z_end), y_dim, x_dim = patch_size\n        self.z_start = z_start\n        self.z_end = z_end\n        self.y_dim = y_dim\n        self.x_dim = x_dim\n\n        # 用memory map的形式，儲存3個fragment(節省記憶體空間)\n        self.mmaps = []\n        self.masks = []\n        self.labels = []\n        for fragment_path in fragment_paths:\n            fragment_slices = []\n            mask = np.array(Image.open(str(fragment_path / \"mask.png\")).convert(\"1\"))\n            self.masks.append(mask)\n            label = (np.array(Image.open(str(fragment_path / \"inklabels.png\"))) > 0)\n            self.labels.append(label)\n            for slice_index in range(z_start, z_end):\n                slice_path = fragment_path /\"surface_volume\"/f\"{slice_index}.tif\"\n                mmap = np.memmap(str(slice_path), dtype=np.uint16, mode='r',shape = mask.shape)\n                fragment_slices.append(mmap)\n            self.mmaps.append(fragment_slices)\n            \n        self.patches = []\n        for fragment_index in range(len(self.mmaps)):\n            # get mmap & mask\n            mmap = self.mmaps[fragment_index]\n            mask = self.masks[fragment_index]\n            # 固定step挑出patch\n            # 要跳過沒有mask的地方\n            for y in range(self.y_dim//2,mask.shape[0]-self.y_dim//2,step_size):\n                for x in range(self.x_dim//2,mask.shape[1]-self.x_dim//2,step_size):  \n                    if(mask[y][x] == 1):\n                        self.patches.append((fragment_index,y,x))\n\n    def __getitem__(self, index):\n        fragment_index,y,x = self.patches[index]\n        patch = []\n        for slice in self.mmaps[fragment_index]:\n            slice_patch = slice[y-self.y_dim//2 : y+self.y_dim//2,x-self.x_dim//2 : x+self.x_dim//2]\n            patch.append(slice_patch)\n        # Stack the patches along the first dimension\n        patch = np.stack(patch)\n        patch = patch.astype(np.float32) / 65536\n        tensor = torch.from_numpy(patch.astype(np.float32))\n        label = self.labels[fragment_index][y-self.y_dim//2 : y+self.y_dim//2,x-self.x_dim//2 : x+self.x_dim//2].astype(np.float32)\n#         plt.imshow(self.labels[fragment_index], cmap='gray')\n#         rect = plt.Rectangle((x-self.x_dim//2, y-self.y_dim//2), self.x_dim, self.y_dim, linewidth=2, edgecolor=\"y\", facecolor=\"none\")\n#         plt.gca().add_patch(rect)\n#         plt.show()\n#         plt.imshow(self.masks[fragment_index], cmap='gray')\n#         plt.show()\n        label = torch.from_numpy(label)\n        return tensor,label.unsqueeze(0)\n\n    def __len__(self):\n        return len(self.patches)\n\n    def close(self):\n        # Close the memory-mapped arrays\n        for fragment_slices in self.mmaps:\n            for mmap in fragment_slices:\n                mmap._mmap.close()","metadata":{"execution":{"iopub.status.busy":"2023-05-29T11:54:00.478743Z","iopub.execute_input":"2023-05-29T11:54:00.479645Z","iopub.status.idle":"2023-05-29T11:54:00.500818Z","shell.execute_reply.started":"2023-05-29T11:54:00.479603Z","shell.execute_reply":"2023-05-29T11:54:00.499681Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"base_path = Path(\"/kaggle/input/vesuvius-challenge/\")\ntrain_path = base_path / \"train\"\ntrain_fragments = [train_path / fragment_name for fragment_name in [\"1\", \"2\", \"3\"]]\npatch_size = ((18, 38), 256, 256)\nstep_size = 32\ntrain_set = PatchDataset(train_fragments, patch_size,step_size)   ","metadata":{"execution":{"iopub.status.busy":"2023-05-29T11:54:00.502588Z","iopub.execute_input":"2023-05-29T11:54:00.503027Z","iopub.status.idle":"2023-05-29T11:54:03.290604Z","shell.execute_reply.started":"2023-05-29T11:54:00.502987Z","shell.execute_reply":"2023-05-29T11:54:03.289460Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(train_set[4][0].shape,train_set[4][1].shape)","metadata":{"execution":{"iopub.status.busy":"2023-05-29T11:54:03.294171Z","iopub.execute_input":"2023-05-29T11:54:03.294664Z","iopub.status.idle":"2023-05-29T11:54:04.978677Z","shell.execute_reply.started":"2023-05-29T11:54:03.294618Z","shell.execute_reply":"2023-05-29T11:54:04.977398Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# batch size = 32(一次算 32 pixels)\nBATCH_SIZE = 32\ntrain_loader = thd.DataLoader(train_set, batch_size=BATCH_SIZE, shuffle=True)\nprint(\"Num batches:\", len(train_loader))\nnum_batches = len(train_loader)","metadata":{"execution":{"iopub.status.busy":"2023-05-29T11:54:04.980538Z","iopub.execute_input":"2023-05-29T11:54:04.981465Z","iopub.status.idle":"2023-05-29T11:54:04.990318Z","shell.execute_reply.started":"2023-05-29T11:54:04.981410Z","shell.execute_reply":"2023-05-29T11:54:04.989030Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Set up model","metadata":{}},{"cell_type":"code","source":"DEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")","metadata":{"execution":{"iopub.status.busy":"2023-05-29T11:54:04.993449Z","iopub.execute_input":"2023-05-29T11:54:04.994644Z","iopub.status.idle":"2023-05-29T11:54:05.072223Z","shell.execute_reply.started":"2023-05-29T11:54:04.994601Z","shell.execute_reply":"2023-05-29T11:54:05.070839Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class DoubleConv(nn.Module):\n    \"\"\"(convolution => [BN] => ReLU) * 2\"\"\"\n\n    def __init__(self, in_channels, out_channels, mid_channels=None):\n        super().__init__()\n        if not mid_channels:\n            mid_channels = out_channels\n        self.double_conv = nn.Sequential(\n            nn.Conv2d(in_channels, mid_channels, kernel_size=3, padding=1, bias=False),\n            nn.BatchNorm2d(mid_channels),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(mid_channels, out_channels, kernel_size=3, padding=1, bias=False),\n            nn.BatchNorm2d(out_channels),\n            nn.ReLU(inplace=True)\n        )\n\n    def forward(self, x):\n        return self.double_conv(x)\n\n\nclass Down(nn.Module):\n    \"\"\"Downscaling with maxpool then double conv\"\"\"\n\n    def __init__(self, in_channels, out_channels):\n        super().__init__()\n        self.maxpool_conv = nn.Sequential(\n            nn.MaxPool2d(2),\n            DoubleConv(in_channels, out_channels)\n        )\n\n    def forward(self, x):\n        return self.maxpool_conv(x)\n\n\nclass Up(nn.Module):\n    \"\"\"Upscaling then double conv\"\"\"\n\n    def __init__(self, in_channels, out_channels, bilinear=True):\n        super().__init__()\n\n        # if bilinear, use the normal convolutions to reduce the number of channels\n        if bilinear:\n            self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)\n            self.conv = DoubleConv(in_channels, out_channels, in_channels // 2)\n        else:\n            self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=2, stride=2)\n            self.conv = DoubleConv(in_channels, out_channels)\n\n    def forward(self, x1, x2):\n        x1 = self.up(x1)\n        # input is CHW\n        diffY = x2.size()[2] - x1.size()[2]\n        diffX = x2.size()[3] - x1.size()[3]\n\n        x1 = F.pad(x1, [diffX // 2, diffX - diffX // 2,\n                        diffY // 2, diffY - diffY // 2])\n        # if you have padding issues, see\n        # https://github.com/HaiyongJiang/U-Net-Pytorch-Unstructured-Buggy/commit/0e854509c2cea854e247a9c615f175f76fbb2e3a\n        # https://github.com/xiaopeng-liao/Pytorch-UNet/commit/8ebac70e633bac59fc22bb5195e513d5832fb3bd\n        x = torch.cat([x2, x1], dim=1)\n        return self.conv(x)\n\n\nclass OutConv(nn.Module):\n    def __init__(self, in_channels, out_channels):\n        super(OutConv, self).__init__()\n        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1)\n\n    def forward(self, x):\n        return self.conv(x)","metadata":{"execution":{"iopub.status.busy":"2023-05-29T11:54:05.076756Z","iopub.execute_input":"2023-05-29T11:54:05.077728Z","iopub.status.idle":"2023-05-29T11:54:05.105157Z","shell.execute_reply.started":"2023-05-29T11:54:05.077679Z","shell.execute_reply":"2023-05-29T11:54:05.103675Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class UNet(nn.Module):\n    def __init__(self, n_channels, n_classes, bilinear=False):\n        super(UNet, self).__init__()\n        self.n_channels = n_channels\n        self.n_classes = n_classes\n        self.bilinear = bilinear\n\n        self.inc = (DoubleConv(n_channels, 64))\n        self.down1 = (Down(64, 128))\n        self.down2 = (Down(128, 256))\n        self.down3 = (Down(256, 512))\n        factor = 2 if bilinear else 1\n        self.down4 = (Down(512, 1024 // factor))\n        self.up1 = (Up(1024, 512 // factor, bilinear))\n        self.up2 = (Up(512, 256 // factor, bilinear))\n        self.up3 = (Up(256, 128 // factor, bilinear))\n        self.up4 = (Up(128, 64, bilinear))\n        self.outc = (OutConv(64, n_classes))\n\n    def forward(self, x):\n        x1 = self.inc(x)\n        x2 = self.down1(x1)\n        x3 = self.down2(x2)\n        x4 = self.down3(x3)\n        x5 = self.down4(x4)\n        x = self.up1(x5, x4)\n        x = self.up2(x, x3)\n        x = self.up3(x, x2)\n        x = self.up4(x, x1)\n        logits = self.outc(x)\n        return logits\n\n    def use_checkpointing(self):\n        self.inc = torch.utils.checkpoint(self.inc)\n        self.down1 = torch.utils.checkpoint(self.down1)\n        self.down2 = torch.utils.checkpoint(self.down2)\n        self.down3 = torch.utils.checkpoint(self.down3)\n        self.down4 = torch.utils.checkpoint(self.down4)\n        self.up1 = torch.utils.checkpoint(self.up1)\n        self.up2 = torch.utils.checkpoint(self.up2)\n        self.up3 = torch.utils.checkpoint(self.up3)\n        self.up4 = torch.utils.checkpoint(self.up4)\n        self.outc = torch.utils.checkpoint(self.outc)","metadata":{"execution":{"iopub.status.busy":"2023-05-29T11:54:05.107333Z","iopub.execute_input":"2023-05-29T11:54:05.108075Z","iopub.status.idle":"2023-05-29T11:54:05.130198Z","shell.execute_reply.started":"2023-05-29T11:54:05.108029Z","shell.execute_reply":"2023-05-29T11:54:05.128844Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = UNet(patch_size[0][1]-patch_size[0][0],1).to(DEVICE)","metadata":{"execution":{"iopub.status.busy":"2023-05-29T11:54:05.132322Z","iopub.execute_input":"2023-05-29T11:54:05.133124Z","iopub.status.idle":"2023-05-29T11:54:08.631423Z","shell.execute_reply.started":"2023-05-29T11:54:05.133083Z","shell.execute_reply":"2023-05-29T11:54:08.630186Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Train","metadata":{}},{"cell_type":"code","source":"TRAINING_STEPS = num_batches\nLEARNING_RATE = 1e-3\nTRAIN_RUN = True # To avoid re-running when saving the notebook\nloss_list = []\naccuracy_list = []\nfbeta_list = []","metadata":{"execution":{"iopub.status.busy":"2023-05-29T11:54:08.636186Z","iopub.execute_input":"2023-05-29T11:54:08.636585Z","iopub.status.idle":"2023-05-29T11:54:08.644882Z","shell.execute_reply.started":"2023-05-29T11:54:08.636543Z","shell.execute_reply":"2023-05-29T11:54:08.643678Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"warnings.simplefilter('ignore', UndefinedMetricWarning)","metadata":{"execution":{"iopub.status.busy":"2023-05-29T11:54:08.646775Z","iopub.execute_input":"2023-05-29T11:54:08.647188Z","iopub.status.idle":"2023-05-29T11:54:08.657677Z","shell.execute_reply.started":"2023-05-29T11:54:08.647148Z","shell.execute_reply":"2023-05-29T11:54:08.656493Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if TRAIN_RUN:\n    criterion = nn.BCEWithLogitsLoss()\n    optimizer = optim.SGD(model.parameters(), lr=LEARNING_RATE)\n    scheduler = torch.optim.lr_scheduler.OneCycleLR(optimizer, max_lr=LEARNING_RATE, total_steps=TRAINING_STEPS)\n    model.train()\n    running_loss = 0.0\n    running_accuracy = 0.0\n    running_fbeta = 0.0\n    denom = 0\n    pbar = tqdm(enumerate(train_loader))\n    for i, (subvolumes, inklabels) in pbar:\n        optimizer.zero_grad()\n        outputs = model(subvolumes.to(DEVICE))\n        loss = criterion(outputs, inklabels.to(DEVICE))\n        loss.backward()\n        optimizer.step()\n        scheduler.step()\n        pred_ink = outputs.detach().sigmoid().gt(0.4).cpu().int()\n        accuracy = (pred_ink == inklabels).sum().float().div(inklabels.numel())\n        fbeta = fbeta_score(inklabels.view(-1).numpy(), pred_ink.view(-1).numpy(), beta=0.5)\n        loss_list.append(loss.item())\n        accuracy_list.append(accuracy.item())\n        fbeta_list.append(fbeta)\n#         running_fbeta += fbeta_score(inklabels.view(-1).numpy(), pred_ink.view(-1).numpy(), beta=0.5)\n#         running_accuracy += accuracy.item()\n#         running_loss += loss.item()\n#         denom += 1\n        pbar.set_postfix({\"Loss\": loss.item(), \"Accuracy\": accuracy.item(), \"Fbeta@0.5\": fbeta})\n        # pbar.set_postfix({\"Loss\": running_loss / denom, \"Accuracy\": running_accuracy / denom, \"Fbeta@0.5\": running_fbeta / denom})\n#         if (i + 1) % 500 == 0:\n#             running_loss = 0.\n#             running_accuracy = 0.\n#             running_fbeta = 0.\n#             denom = 0\n\n    torch.save(model.state_dict(), \"/kaggle/working/model.pt\")\n\nelse:\n    model_weights = torch.load(\"/kaggle/working/model.pt\")\n    model.load_state_dict(model_weights)","metadata":{"execution":{"iopub.status.busy":"2023-05-29T11:54:08.659313Z","iopub.execute_input":"2023-05-29T11:54:08.659974Z","iopub.status.idle":"2023-05-29T12:57:20.667889Z","shell.execute_reply.started":"2023-05-29T11:54:08.659933Z","shell.execute_reply":"2023-05-29T12:57:20.665684Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.plot(accuracy_list)\nplt.plot(loss_list)\nplt.show()\nplt.plot(fbeta_list)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-05-29T12:57:41.771199Z","iopub.execute_input":"2023-05-29T12:57:41.771807Z","iopub.status.idle":"2023-05-29T12:57:42.215357Z","shell.execute_reply.started":"2023-05-29T12:57:41.771767Z","shell.execute_reply":"2023-05-29T12:57:42.214295Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"base_path = Path(\"/kaggle/input/vesuvius-challenge/\")\ntrain_path = base_path / \"train\"\ntrain_fragments = [train_path / fragment_name for fragment_name in [\"1\", \"2\", \"3\"]]\npatch_size = ((18, 38), 256, 256)\nstep_size = 256\ntest_set = PatchDataset(train_fragments, patch_size,step_size)   ","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BATCH_SIZE = 32\ntest_loader = thd.DataLoader(test_set, batch_size=BATCH_SIZE, shuffle=False)\nprint(\"Num batches:\", len(test_loader))\nnum_batches = len(test_loader)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for  subvolumes, inklabels in train_loader:\n    outputs = model(subvolumes.to(DEVICE))\n    pred_ink = outputs.detach().sigmoid().gt(0.4).cpu().int()\n    accuracy = (pred_ink == inklabels).sum().float().div(inklabels.numel())\n    fbeta = fbeta_score(inklabels.view(-1).numpy(), pred_ink.view(-1).numpy(), beta=0.5)\n    loss_list.append(loss.item())\n    accuracy_list.append(accuracy.item())\n    fbeta_list.append(fbeta)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Evaluate","metadata":{}},{"cell_type":"code","source":"# Clear memory before loading test fragments\ntrain_dset.labels = None\ntrain_dset.image_stacks = []\ndel train_loader, train_dset\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2023-05-29T12:57:20.669208Z","iopub.status.idle":"2023-05-29T12:57:20.669997Z","shell.execute_reply.started":"2023-05-29T12:57:20.669727Z","shell.execute_reply":"2023-05-29T12:57:20.669755Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_path = base_path / \"test\"\ntest_fragments = [train_path / fragment_name for fragment_name in test_path.iterdir()]\nprint(\"All fragments:\", test_fragments)","metadata":{"execution":{"iopub.status.busy":"2023-05-29T12:57:20.671727Z","iopub.status.idle":"2023-05-29T12:57:20.672969Z","shell.execute_reply.started":"2023-05-29T12:57:20.672691Z","shell.execute_reply":"2023-05-29T12:57:20.672720Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred_images = []\nmodel.eval()\nfor test_fragment in test_fragments:\n    outputs = []\n    eval_dset = SubvolumeDataset(fragments=[test_fragment], voxel_shape=(48, 64, 64), load_inklabels=False)\n    eval_loader = thd.DataLoader(eval_dset, batch_size=BATCH_SIZE, shuffle=False)\n    with torch.no_grad():\n        for i, (subvolumes, _) in enumerate(tqdm(eval_loader)):\n            output = model(subvolumes.to(DEVICE)).view(-1).sigmoid().cpu().numpy()\n            outputs.append(output)\n    # we only load 1 fragment at a time\n    image_shape = eval_dset.image_stacks[0].shape[1:]\n    eval_dset.labels = None\n    eval_dset.image_stacks = None\n    del eval_loader\n    gc.collect()\n\n    pred_image = np.zeros(image_shape, dtype=np.uint8)\n    outputs = np.concatenate(outputs)\n    for (y, x, _), prob in zip(eval_dset.pixels[:outputs.shape[0]], outputs):\n        pred_image[y ,x] = prob > 0.4\n    pred_images.append(pred_image)\n    \n    eval_dset.pixels = None\n    del eval_dset\n    gc.collect()\n    print(\"Finished\", test_fragment)","metadata":{"execution":{"iopub.status.busy":"2023-05-29T12:57:20.674471Z","iopub.status.idle":"2023-05-29T12:57:20.676750Z","shell.execute_reply.started":"2023-05-29T12:57:20.676475Z","shell.execute_reply":"2023-05-29T12:57:20.676504Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.imshow(pred_images[1], cmap='gray')","metadata":{"execution":{"iopub.status.busy":"2023-05-29T12:57:20.678553Z","iopub.status.idle":"2023-05-29T12:57:20.679484Z","shell.execute_reply.started":"2023-05-29T12:57:20.679164Z","shell.execute_reply":"2023-05-29T12:57:20.679204Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Submission","metadata":{}},{"cell_type":"code","source":"def rle(output):\n    flat_img = np.where(output > 0.4, 1, 0).astype(np.uint8)\n    starts = np.array((flat_img[:-1] == 0) & (flat_img[1:] == 1))\n    ends = np.array((flat_img[:-1] == 1) & (flat_img[1:] == 0))\n    starts_ix = np.where(starts)[0] + 2\n    ends_ix = np.where(ends)[0] + 2\n    lengths = ends_ix - starts_ix\n    return \" \".join(map(str, sum(zip(starts_ix, lengths), ())))","metadata":{"execution":{"iopub.status.busy":"2023-05-29T12:57:20.681191Z","iopub.status.idle":"2023-05-29T12:57:20.682116Z","shell.execute_reply.started":"2023-05-29T12:57:20.681842Z","shell.execute_reply":"2023-05-29T12:57:20.681870Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission = defaultdict(list)\nfor fragment_id, fragment_name in enumerate(test_fragments):\n    submission[\"Id\"].append(fragment_name.name)\n    submission[\"Predicted\"].append(rle(pred_images[fragment_id]))\n\npd.DataFrame.from_dict(submission).to_csv(\"/kaggle/working/submission.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2023-05-29T12:57:20.683968Z","iopub.status.idle":"2023-05-29T12:57:20.684999Z","shell.execute_reply.started":"2023-05-29T12:57:20.684696Z","shell.execute_reply":"2023-05-29T12:57:20.684740Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pd.DataFrame.from_dict(submission)","metadata":{"execution":{"iopub.status.busy":"2023-05-29T12:57:20.686630Z","iopub.status.idle":"2023-05-29T12:57:20.687585Z","shell.execute_reply.started":"2023-05-29T12:57:20.687260Z","shell.execute_reply":"2023-05-29T12:57:20.687291Z"},"trusted":true},"execution_count":null,"outputs":[]}]}