{"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 -q /kaggle/input/pretrainedmodels-0-7-4/pretrainedmodels-0.7.4-py3-none-any.whl\n!pip install -q /kaggle/input/timm-0-4-9/timm-0.4.9-py3-none-any.whl\n!pip install -q /kaggle/input/python-gdcm-3-0-9-0-cp37/python_gdcm-3.0.9.0-cp37-cp37m-manylinux2014_x86_64.whl","metadata":{"execution":{"iopub.status.busy":"2021-08-26T08:48:10.033007Z","iopub.execute_input":"2021-08-26T08:48:10.033455Z","iopub.status.idle":"2021-08-26T08:49:29.611465Z","shell.execute_reply.started":"2021-08-26T08:48:10.033416Z","shell.execute_reply":"2021-08-26T08:49:29.610378Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pretrainedmodels\nimport timm\nimport torch\nfrom torch import nn\nimport numpy as np\nimport pandas as pd\nimport pydicom\nimport gdcm\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nimport cv2\nimport os\nimport copy\nimport torchvision\nfrom torchvision.models.detection import FasterRCNN\nfrom torchvision.ops import misc as misc_nn_ops\nfrom torchvision.ops.feature_pyramid_network import FeaturePyramidNetwork, LastLevelMaxPool\nfrom collections import OrderedDict, defaultdict\nfrom tqdm import tqdm\nfrom multiprocessing import Pool","metadata":{"execution":{"iopub.status.busy":"2021-08-26T08:49:29.61322Z","iopub.execute_input":"2021-08-26T08:49:29.613582Z","iopub.status.idle":"2021-08-26T08:49:33.217991Z","shell.execute_reply.started":"2021-08-26T08:49:29.61354Z","shell.execute_reply":"2021-08-26T08:49:33.217052Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class FrozenBatchNorm2dWithEpsilon(torchvision.ops.misc.FrozenBatchNorm2d):\n    \"\"\"This class aims to make the epsilon consistent with the default value of torch.nn.BatchNorm2d\"\"\"\n    \n    def __init__(self, *args, **kwargs):\n        if 'eps' not in kwargs:\n            kwargs['eps'] = 1e-5\n        super().__init__(*args, **kwargs)","metadata":{"execution":{"iopub.status.busy":"2021-08-26T08:49:33.220011Z","iopub.execute_input":"2021-08-26T08:49:33.220295Z","iopub.status.idle":"2021-08-26T08:49:33.226007Z","shell.execute_reply.started":"2021-08-26T08:49:33.220264Z","shell.execute_reply":"2021-08-26T08:49:33.224623Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"args_dict = {'cls': {}, 'det': {}}\nargs_dict['batch_size'] = 16\n\nargs_dict['cls']['input_size'] = 512\nargs_dict['cls']['ckpt_paths'] = [\n    '../input/covid-cls-model-v6-25-5-fold/covid-cls-model-v6-25-fold-0.pth',\n    '../input/covid-cls-model-v6-25-5-fold/covid-cls-model-v6-25-fold-1.pth',\n    '../input/covid-cls-model-v6-25-5-fold/covid-cls-model-v6-25-fold-2.pth',\n    '../input/covid-cls-model-v6-25-5-fold/covid-cls-model-v6-25-fold-3.pth',\n    '../input/covid-cls-model-v6-25-5-fold/covid-cls-model-v6-25-fold-4.pth',\n]\nargs_dict['cls']['cascade_clses'] = [0]\nargs_dict['cls']['flip_TTA'] = True\n\n#args_dict['det']['input_size'] = 800\nargs_dict['det']['norm_layer'] = FrozenBatchNorm2dWithEpsilon # misc_nn_ops.FrozenBatchNorm2d\nargs_dict['det']['ckpt_paths'] = [\n    '../input/covid-det-model-v5-17-5-fold/covid_det_v5_17_cv_0.pth',\n    '../input/covid-det-model-v5-17-5-fold/covid_det_v5_17_cv_1.pth',\n    '../input/covid-det-model-v5-17-5-fold/covid_det_v5_17_cv_2.pth',\n    '../input/covid-det-model-v5-17-5-fold/covid_det_v5_17_cv_3.pth',\n    '../input/covid-det-model-v5-17-5-fold/covid_det_v5_17_cv_4.pth',\n]\nargs_dict['det']['box_score_thresh']= 0.0\nargs_dict['det']['flip_TTA'] = True","metadata":{"execution":{"iopub.status.busy":"2021-08-26T08:49:33.227706Z","iopub.execute_input":"2021-08-26T08:49:33.228242Z","iopub.status.idle":"2021-08-26T08:49:33.238255Z","shell.execute_reply.started":"2021-08-26T08:49:33.228201Z","shell.execute_reply":"2021-08-26T08:49:33.237433Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = 'cuda' if torch.cuda.is_available() else 'cpu'\ntest_root = '/kaggle/input/siim-covid19-detection/test/'\ncls_names = [\"negative\", \"typical\", \"indeterminate\", \"atypical\"]","metadata":{"execution":{"iopub.status.busy":"2021-08-26T08:49:33.239554Z","iopub.execute_input":"2021-08-26T08:49:33.240017Z","iopub.status.idle":"2021-08-26T08:49:33.300254Z","shell.execute_reply.started":"2021-08-26T08:49:33.23998Z","shell.execute_reply":"2021-08-26T08:49:33.299253Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def read_xray(path, voi_lut=True, fix_monochrome=True):\n    # Original from: https://www.kaggle.com/raddar/convert-dicom-to-np-array-the-correct-way\n    dicom = pydicom.read_file(path)\n    \n    # VOI LUT (if available by DICOM device) is used to transform raw DICOM data to \n    # \"human-friendly\" view\n    if voi_lut:\n        data = apply_voi_lut(dicom.pixel_array, dicom)\n    else:\n        data = dicom.pixel_array\n               \n    max_value = 2 ** dicom.BitsStored - 1\n    # depending on this value, X-ray may look inverted - fix that:\n    if fix_monochrome and dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        data = max_value - data\n    \n    if max_value != 255:\n        data = data.astype(np.float) / max_value\n        data = (data * 255).astype(np.uint8)\n        \n    return data\n\n\ndef dicom2png_kaggler(dicom_path, png_path=None, output_width=None, output_height=None):\n    np_image = read_xray(dicom_path)\n    if output_width is not None and output_height is not None:\n        np_image = cv2.resize(np_image, (output_width, output_height), interpolation=cv2.INTER_LINEAR)\n    else:\n        assert output_width is None and output_height is None\n    if png_path is not None:\n        cv2.imwrite(png_path, np_image)\n    return np_image","metadata":{"execution":{"iopub.status.busy":"2021-08-26T08:49:33.301742Z","iopub.execute_input":"2021-08-26T08:49:33.302381Z","iopub.status.idle":"2021-08-26T08:49:33.312148Z","shell.execute_reply.started":"2021-08-26T08:49:33.302329Z","shell.execute_reply":"2021-08-26T08:49:33.311388Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"source_root_folder = test_root\ntarget_root_folder = 'png_1024/'\nos.makedirs(target_root_folder, exist_ok=True)\nsource_paths = []\ntarget_paths = []\nfor source_folder, folders, files in os.walk(source_root_folder):\n    for file in files:\n        if file.endswith('.dcm'):\n            source_paths.append(os.path.join(source_folder, file))\n            target_folder = source_folder.replace(source_root_folder, target_root_folder)\n            target_file = file.replace('dcm', 'png')\n            target_paths.append(os.path.join(target_folder, target_file))\nprint(f'Found {len(source_paths)} dcms.')\ndef transfer(info):\n    i, (source_path, target_path) = info\n    os.makedirs(os.path.split(target_path)[0], exist_ok=True)\n    #dicom2png_kaggler(source_path, png_path=target_path, output_width=1024, output_height=1024)\n    #dicom2png_kaggler(source_path, png_path=target_path) #, output_width=1024, output_height=1024)\n    cv2_img = dicom2png_kaggler(source_path)\n    org_WH = (cv2_img.shape[-1], cv2_img.shape[-2])\n    cv2_img = cv2.resize(cv2_img, (1024, 1024), interpolation=cv2.INTER_LINEAR)\n    cv2.imwrite(target_path, cv2_img)\n    accno = os.path.splitext(os.path.split(source_path)[1])[0]\n    return accno, org_WH\nwith Pool(8) as pool:\n    accno2WH = OrderedDict(tqdm(pool.imap(transfer, enumerate(zip(source_paths, target_paths)))))","metadata":{"execution":{"iopub.status.busy":"2021-08-26T08:49:33.313476Z","iopub.execute_input":"2021-08-26T08:49:33.314011Z","iopub.status.idle":"2021-08-26T08:52:02.831597Z","shell.execute_reply.started":"2021-08-26T08:49:33.313974Z","shell.execute_reply":"2021-08-26T08:52:02.830603Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Dataset():\n    def __init__(self, transforms=None):\n        self.transforms = transforms\n\n    def __len__(self):\n        return len(target_paths)\n\n    def __getitem__(self, idx):\n        path = target_paths[idx]\n        cv2_img = cv2.imread(path)\n        #if len(cv2_img.shape) == 2:\n        #    cv2_img = np.tile(cv2_img[..., None], 3)\n        cv2_img = cv2.cvtColor(cv2_img, cv2.COLOR_RGB2BGR)\n        return self.transforms(cv2_img)\n        #return self.transforms(cv2_img), accno2WH[os.path.splitext(os.path.split(path)[1])[0]]","metadata":{"execution":{"iopub.status.busy":"2021-08-26T08:52:02.834675Z","iopub.execute_input":"2021-08-26T08:52:02.835058Z","iopub.status.idle":"2021-08-26T08:52:02.841419Z","shell.execute_reply.started":"2021-08-26T08:52:02.835015Z","shell.execute_reply":"2021-08-26T08:52:02.840208Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#class SplitTransformPath(torch.nn.Module):\n#    def __init__(self, n_outs):\n#        super().__init__()\n#        self.n_outs = n_outs\n#        \n#    def forward(self, inputs):\n#        return (inputs, ) + tuple(copy.deepcopy(inputs) for _ in range(self.n_outs - 1))\n#    \n#    \n#class SplitTransformWrapper(torch.nn.Module):\n#    def __init__(self, transform, target_copy_idxes):\n#        super().__init__()\n#        self.transform = transform\n#        self.target_copy_idxes = target_copy_idxes\n#        \n#    def forward(self, inputs):\n#        return tuple(self.transform(inp) if i in self.target_copy_idxes else inp for i, inp in enumerate(inputs))","metadata":{"execution":{"iopub.status.busy":"2021-08-26T08:52:02.843141Z","iopub.execute_input":"2021-08-26T08:52:02.843516Z","iopub.status.idle":"2021-08-26T08:52:02.855625Z","shell.execute_reply.started":"2021-08-26T08:52:02.843481Z","shell.execute_reply":"2021-08-26T08:52:02.854744Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def freeze_layers(model, stage_name, trainable_stages):\n    if trainable_stages >= len(stage_name):\n        return\n    freeze_to = stage_name[len(stage_name) - trainable_stages - 1]\n    \n    if any([any([name.startswith(x) for x in freeze_to]) for name, param in model.named_parameters()]) and freeze_to != '':\n        freeze_flag = False\n        for name, param in reversed(list(model.named_parameters())):\n            if not freeze_flag and any([name.startswith(x) for x in freeze_to]):\n                freeze_flag = True\n            if freeze_flag:\n                param.requires_grad_(False)\n        return\n    \n    trainable = stage_name[len(stage_name) - trainable_stages:]\n    freeze_flag = True\n    for name, param in model.named_parameters():\n        if freeze_flag and any([name.startswith(y) for x in trainable for y in x]):\n            freeze_flag = False\n        if freeze_flag:\n            param.requires_grad_(False)\n    return\n\ndef change_layer(model, source_module, target_module, params_map={}, state_dict_map={}):\n    def check_children(model):\n        for name, module in model.named_children():\n            if isinstance(module, source_module):\n                state_dict_to_load = {(k if k not in state_dict_map else state_dict_map[k]): v for k, v in module.state_dict().items()}\n                setattr(model, name, target_module(**{new_param: getattr(module, org_param) for org_param, new_param in params_map.items()}))\n                getattr(model, name).load_state_dict(state_dict_to_load, strict=False)\n            check_children(getattr(model, name))\n    check_children(model)\n    \nclass TIMMBackboneBodyWrapper(torch.nn.Module):\n    def __init__(self, timm_model):\n        super().__init__()\n        self.timm_model = timm_model\n    \n    def forward(self, *args, **kwargs):\n        out_list = self.timm_model(*args, **kwargs)\n        out_dict = OrderedDict([(str(i), feature) for i, feature in enumerate(out_list)])\n        return out_dict\n    \nclass NormalBackboneWithFPN(torch.nn.Module):\n    \"\"\"Backbone with FPN builder adapted from torchvision.models.detection.backbone_utils.BackboneWithFPN\n    \"\"\"\n    def __init__(self, backbone_body, in_channels_list, out_channels=256):\n        super().__init__()\n        self.body = backbone_body\n        self.fpn = FeaturePyramidNetwork(\n            in_channels_list=in_channels_list,\n            out_channels=out_channels,\n            extra_blocks=LastLevelMaxPool(),\n        )\n        self.out_channels = out_channels\n\n    def forward(self, x):\n        x = self.body(x)\n        x.device = next(iter(x.values())).device\n        x = self.fpn(x)\n        x.device = next(iter(x.values())).device\n        return x\n    \ndef timm_fpn_backbone(model_name, pretrained=False, norm_layer=misc_nn_ops.FrozenBatchNorm2d, trainable_layers=3):\n    try:\n        backbone = timm.create_model(model_name, pretrained=pretrained, features_only=True, norm_layer=norm_layer, out_indices=(1, 2, 3, 4))\n    except:\n        backbone = timm.create_model(model_name, pretrained=pretrained, features_only=True, out_indices=(1, 2, 3, 4))\n        if norm_layer != torch.nn.BatchNorm2d:\n            change_layer(backbone, torch.nn.BatchNorm2d, norm_layer, params_map={'num_features':'num_features', 'eps':'eps'})\n    stage_name = [[info['module'].replace('.', '_'), info['module']] for info in backbone.feature_info.info]\n    freeze_layers(backbone, stage_name, trainable_layers)\n    \n    return NormalBackboneWithFPN(TIMMBackboneBodyWrapper(backbone), backbone.feature_info.channels())","metadata":{"execution":{"iopub.status.busy":"2021-08-26T08:52:02.856979Z","iopub.execute_input":"2021-08-26T08:52:02.857583Z","iopub.status.idle":"2021-08-26T08:52:02.879498Z","shell.execute_reply.started":"2021-08-26T08:52:02.857544Z","shell.execute_reply":"2021-08-26T08:52:02.878556Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class MSCAM(nn.Module):\n    \"\"\"Module needed in AttentionalFeatureFusionFPN\"\"\"\n    def __init__(self, num_channels, r):\n        super().__init__()\n        bottleneck = num_channels // r\n        self.pool = nn.AdaptiveAvgPool2d((1, 1))\n        self.w1 = nn.Conv2d(num_channels, bottleneck, 1)\n        self.w2 = nn.Conv2d(bottleneck, num_channels, 1)\n        self.pwc1 = nn.Conv2d(num_channels, bottleneck, 1)\n        self.pwc2 = nn.Conv2d(bottleneck, num_channels, 1)\n        self.gn_w1 = nn.GroupNorm(num_groups=32, num_channels=bottleneck)\n        self.gn_w2 = nn.GroupNorm(num_groups=32, num_channels=num_channels)\n        self.gn_pwc1 = nn.GroupNorm(num_groups=32, num_channels=bottleneck)\n        self.gn_pwc2 = nn.GroupNorm(num_groups=32, num_channels=num_channels)\n\n    def forward(self, x):\n        x1 = self.pool(x)\n        x1 = self.w1(x1)\n        x1 = self.gn_w1(x1).relu()\n        x1 = self.w2(x1)\n        x1 = self.gn_w2(x1)\n        \n        x2 = self.pwc1(x)\n        x2 = self.gn_pwc1(x2).relu()\n        x2 = self.pwc2(x2)\n        x2 = self.gn_pwc2(x2)\n        \n        return (x1 + x2).sigmoid()\n\n\nclass AttentionalFeatureFusionFPN(nn.Module):\n    \"\"\" Deprecated, please use ModulerFPN with iAFF=True. Re-implementation of the paper: \"Attentional Feature Fusion\" \"\"\"\n    \n    def __init__(self, in_channels_list, out_channels, extra_blocks=None):\n        super().__init__()\n        self.inner_blocks = nn.ModuleList()\n        self.layer_blocks = nn.ModuleList()\n        for in_channels in in_channels_list:\n            if in_channels == 0:\n                continue\n            inner_block_module = nn.Conv2d(in_channels, out_channels, 1)\n            layer_block_module = nn.Conv2d(out_channels, out_channels, 3, padding=1)\n            self.inner_blocks.append(inner_block_module)\n            self.layer_blocks.append(layer_block_module)\n\n        # initialize parameters now to avoid modifying the initialization of top_blocks\n        for m in self.children():\n            if isinstance(m, nn.Conv2d):\n                nn.init.kaiming_uniform_(m.weight, a=1)\n                nn.init.constant_(m.bias, 0)\n\n        if extra_blocks is not None:\n            assert isinstance(extra_blocks, torchvision.ops.feature_pyramid_network.ExtraFPNBlock)\n        self.extra_blocks = extra_blocks\n        \n        self.MSCAM1s = nn.ModuleList([MSCAM(out_channels, 4) for _ in range(len(in_channels_list))])\n        self.MSCAM2s = nn.ModuleList([MSCAM(out_channels, 4) for _ in range(len(in_channels_list))])\n    \n    @staticmethod\n    def iAFF(MSCAM1, MSCAM2, x, y):\n        assert x.shape == y.shape, f\"input shape is not the same: {x.shape}, {y.shape}\"\n        att_weight = MSCAM2(MSCAM1(x + y))\n        return att_weight * x + (1 - att_weight) * y\n    \n    def forward(self, x):\n        \"\"\"\n        Computes the FPN for a set of feature maps.\n\n        Arguments:\n            x (OrderedDict[Tensor]): feature maps for each feature level.\n\n        Returns:\n            results (OrderedDict[Tensor]): feature maps after FPN layers.\n                They are ordered from highest resolution first.\n        \"\"\"\n        # unpack OrderedDict into two lists for easier handling\n        names = list(x.keys())\n        x = list(x.values())\n\n        last_inner = self.inner_blocks[-1](x[-1])\n        results = []\n        results.append(self.layer_blocks[-1](last_inner))\n        for i, (feature, inner_block, layer_block) in enumerate(zip(\n            x[:-1][::-1], self.inner_blocks[:-1][::-1], self.layer_blocks[:-1][::-1],\n        )):\n            if not inner_block:\n                continue\n            inner_lateral = inner_block(feature)\n            feat_shape = inner_lateral.shape[-2:]\n            inner_top_down = nn.functional.interpolate(last_inner, size=feat_shape, mode=\"nearest\")\n            last_inner = self.iAFF(self.MSCAM1s[i], self.MSCAM2s[i], inner_lateral, inner_top_down)\n            results.insert(0, layer_block(last_inner))\n\n        if self.extra_blocks is not None:\n            results, names = self.extra_blocks(results, x, names)\n\n        # make it back an OrderedDict\n        out = OrderedDict([(k, v) for k, v in zip(names, results)])\n\n        return out\n\n\nclass AttentionGuidedFPN(nn.Module):\n    \"\"\" Deprecated, please use ModulerFPN with ACFPN=True. Re-implementation of the CEM module in the paper:\n    \"Attention-guided Context Feature Pyramid Network for Object Detection\".\n    Theis implementation is based on this repository:\n    https://github.com/Caojunxu/AC-FPN\n    \"\"\"\n    def __init__(self, in_channels_list, out_channels, extra_blocks=None):\n        super().__init__()\n        self.inner_blocks = nn.ModuleList()\n        self.layer_blocks = nn.ModuleList()\n        for in_channels in in_channels_list:\n            if in_channels == 0:\n                continue\n            inner_block_module = nn.Conv2d(in_channels, out_channels, 1)\n            layer_block_module = nn.Conv2d(out_channels, out_channels, 3, padding=1)\n            self.inner_blocks.append(inner_block_module)\n            self.layer_blocks.append(layer_block_module)\n\n        # initialize parameters now to avoid modifying the initialization of top_blocks\n        for m in self.children():\n            if isinstance(m, nn.Conv2d):\n                nn.init.kaiming_uniform_(m.weight, a=1)\n                nn.init.constant_(m.bias, 0)\n\n        if extra_blocks is not None:\n            assert isinstance(extra_blocks, torchvision.ops.feature_pyramid_network.ExtraFPNBlock)\n        self.extra_blocks = extra_blocks\n        self.num_dilations = [3, 6, 12, 18, 24]\n        aspp_blocks = []\n        dropout0 = 0.1\n        d_feature0 = 512\n        d_feature1 = 256\n        dim_in = in_channels_list[-1]\n        for i, dilation in enumerate(self.num_dilations):\n            aspp_blocks.append(self.dense_aspp_block(\n                input_num=dim_in + d_feature1 * i,\n                num1=d_feature0,\n                num2=d_feature1,\n                dilation_rate=dilation,\n                drop_out=dropout0\n            ))\n        self.aspp_blocks = torch.nn.ModuleList(aspp_blocks)\n        self.CEM_final_conv = torch.nn.Conv2d(len(self.num_dilations) * d_feature1, out_channels, 1)\n        self.CEM_final_gn = torch.nn.GroupNorm(num_groups=32, num_channels=out_channels)\n    \n    @staticmethod\n    def dense_aspp_block(input_num, num1, num2, dilation_rate, drop_out):\n        return torch.nn.Sequential(\n            torch.nn.Conv2d(input_num, num1, kernel_size=1),\n            torch.nn.GroupNorm(num_groups=32, num_channels=num1),\n            torch.nn.ReLU(),\n            torch.nn.Conv2d(num1, num2, kernel_size=3, padding=dilation_rate, dilation=dilation_rate),\n            torch.nn.ReLU(),\n            torch.nn.Dropout(drop_out)\n        )\n\n    @staticmethod\n    def iAFF(MSCAM1, MSCAM2, x, y):\n        assert x.shape == y.shape, f\"input shape is not the same: {x.shape}, {y.shape}\"\n        att_weight = MSCAM2(MSCAM1(x + y))\n        return att_weight * x + (1 - att_weight) * y\n\n    def dense_aspp_forward(self, _input):\n        conv_outs = []\n        \n        conv_out = self.aspp_blocks[0](_input)\n        if 0 != len(self.num_dilations) - 1:\n            x = torch.cat((conv_out, _input), dim=1)\n            conv_outs.append(conv_out)\n            \n        for i, dilation in enumerate(self.num_dilations[1:], 1):\n            conv_out = self.aspp_blocks[i](x)\n            if i != len(self.num_dilations) - 1:\n                x = torch.cat((conv_out, x), dim=1)\n            conv_outs.append(conv_out)\n        x = torch.cat(conv_outs, dim=1)\n        x = self.CEM_final_conv(x)\n        x = self.CEM_final_gn(x)\n        return x\n    \n    def forward(self, x):\n        \"\"\"\n        Computes the FPN for a set of feature maps.\n\n        Arguments:\n            x (OrderedDict[Tensor]): feature maps for each feature level.\n\n        Returns:\n            results (OrderedDict[Tensor]): feature maps after FPN layers.\n                They are ordered from highest resolution first.\n        \"\"\"\n        # unpack OrderedDict into two lists for easier handling\n        names = list(x.keys())\n        x = list(x.values())\n\n        last_inner = self.inner_blocks[-1](x[-1]) + self.dense_aspp_forward(x[-1])\n        results = []\n        results.append(self.layer_blocks[-1](last_inner))\n        for feature, inner_block, layer_block in zip(\n            x[:-1][::-1], self.inner_blocks[:-1][::-1], self.layer_blocks[:-1][::-1]\n        ):\n            if not inner_block:\n                continue\n            inner_lateral = inner_block(feature)\n            feat_shape = inner_lateral.shape[-2:]\n            inner_top_down = torch.nn.functional.interpolate(last_inner, size=feat_shape, mode=\"nearest\")\n            last_inner = inner_lateral + inner_top_down\n            results.insert(0, layer_block(last_inner))\n\n        if self.extra_blocks is not None:\n            results, names = self.extra_blocks(results, x, names)\n\n        # make it back an OrderedDict\n        out = OrderedDict([(k, v) for k, v in zip(names, results)])\n\n        return out\n\n\nclass ModulerFPN(nn.Module):\n    \"\"\"\n    Combination of ACFPN and iAFF\n    \"\"\"\n    def __init__(self, in_channels_list, out_channels, extra_blocks=None):\n        super().__init__()\n        self.ACFPN = self.iAFF = False\n        self.in_channels_list = in_channels_list\n        self.out_channels = out_channels\n        self.inner_blocks = nn.ModuleList()\n        self.layer_blocks = nn.ModuleList()\n        for in_channels in self.in_channels_list:\n            if in_channels == 0:\n                continue\n            inner_block_module = nn.Conv2d(in_channels, out_channels, 1)\n            layer_block_module = nn.Conv2d(out_channels, out_channels, 3, padding=1)\n            self.inner_blocks.append(inner_block_module)\n            self.layer_blocks.append(layer_block_module)\n\n        # initialize parameters now to avoid modifying the initialization of top_blocks\n        for m in self.children():\n            if isinstance(m, nn.Conv2d):\n                nn.init.kaiming_uniform_(m.weight, a=1)\n                nn.init.constant_(m.bias, 0)\n\n        if extra_blocks is not None:\n            assert isinstance(extra_blocks, torchvision.ops.feature_pyramid_network.ExtraFPNBlock)\n        self.extra_blocks = extra_blocks\n    \n    def setup_ACFPN(self):\n        self.ACFPN = True\n        self.num_dilations = [3, 6, 12, 18, 24]\n        aspp_blocks = []\n        dropout0 = 0.1\n        d_feature0 = 512\n        d_feature1 = 256\n        dim_in = self.in_channels_list[-1]\n        for i, dilation in enumerate(self.num_dilations):\n            aspp_blocks.append(self.dense_aspp_block(\n                input_num=dim_in + d_feature1 * i,\n                num1=d_feature0,\n                num2=d_feature1,\n                dilation_rate=dilation,\n                drop_out=dropout0\n            ))\n        self.aspp_blocks = torch.nn.ModuleList(aspp_blocks)\n        self.CEM_final_conv = torch.nn.Conv2d(len(self.num_dilations) * d_feature1, self.out_channels, 1)\n        self.CEM_final_gn = torch.nn.GroupNorm(num_groups=32, num_channels=self.out_channels)\n        \n    def setup_iAFF(self):\n        self.iAFF = True\n        self.MSCAM1s = nn.ModuleList([MSCAM(self.out_channels, 4) for _ in range(len(self.in_channels_list))])\n        self.MSCAM2s = nn.ModuleList([MSCAM(self.out_channels, 4) for _ in range(len(self.in_channels_list))])\n\n    @staticmethod\n    def apply_iAFF(MSCAM1, MSCAM2, x, y):\n        assert x.shape == y.shape, f\"input shape is not the same: {x.shape}, {y.shape}\"\n        att_weight = MSCAM2(MSCAM1(x + y))\n        return att_weight * x + (1 - att_weight) * y\n    \n    @staticmethod\n    def dense_aspp_block(input_num, num1, num2, dilation_rate, drop_out):\n        return torch.nn.Sequential(\n            torch.nn.Conv2d(input_num, num1, kernel_size=1),\n            torch.nn.GroupNorm(num_groups=32, num_channels=num1),\n            torch.nn.ReLU(),\n            torch.nn.Conv2d(num1, num2, kernel_size=3, padding=dilation_rate, dilation=dilation_rate),\n            torch.nn.ReLU(),\n            torch.nn.Dropout(drop_out)\n        )\n\n    def dense_aspp_forward(self, _input):\n        conv_outs = []\n        \n        conv_out = self.aspp_blocks[0](_input)\n        if 0 != len(self.num_dilations) - 1:\n            x = torch.cat((conv_out, _input), dim=1)\n            conv_outs.append(conv_out)\n            \n        for i, dilation in enumerate(self.num_dilations[1:], 1):\n            conv_out = self.aspp_blocks[i](x)\n            if i != len(self.num_dilations) - 1:\n                x = torch.cat((conv_out, x), dim=1)\n            conv_outs.append(conv_out)\n        x = torch.cat(conv_outs, dim=1)\n        x = self.CEM_final_conv(x)\n        x = self.CEM_final_gn(x)\n        return x\n    \n    def forward(self, x):\n        \"\"\"\n        Computes the FPN for a set of feature maps.\n\n        Arguments:\n            x (OrderedDict[Tensor]): feature maps for each feature level.\n\n        Returns:\n            results (OrderedDict[Tensor]): feature maps after FPN layers.\n                They are ordered from highest resolution first.\n        \"\"\"\n        # unpack OrderedDict into two lists for easier handling\n        names = list(x.keys())\n        x = list(x.values())\n\n        last_inner = self.inner_blocks[-1](x[-1])\n        \n        if self.ACFPN:\n            last_inner += self.dense_aspp_forward(x[-1])\n            \n        results = []\n        results.append(self.layer_blocks[-1](last_inner))\n        for i, (feature, inner_block, layer_block) in enumerate(zip(\n            x[:-1][::-1], self.inner_blocks[:-1][::-1], self.layer_blocks[:-1][::-1]\n        )):\n            if not inner_block:\n                continue\n            inner_lateral = inner_block(feature)\n            feat_shape = inner_lateral.shape[-2:]\n            inner_top_down = torch.nn.functional.interpolate(last_inner, size=feat_shape, mode=\"nearest\")\n            \n            if not self.iAFF:\n                last_inner = inner_lateral + inner_top_down\n            else:\n                last_inner = self.apply_iAFF(self.MSCAM1s[i], self.MSCAM2s[i], inner_lateral, inner_top_down)\n                \n            results.insert(0, layer_block(last_inner))\n\n        if self.extra_blocks is not None:\n            results, names = self.extra_blocks(results, x, names)\n\n        # make it back an OrderedDict\n        out = OrderedDict([(k, v) for k, v in zip(names, results)])\n\n        return out\n","metadata":{"execution":{"iopub.status.busy":"2021-08-26T08:52:02.881109Z","iopub.execute_input":"2021-08-26T08:52:02.881538Z","iopub.status.idle":"2021-08-26T08:52:02.943712Z","shell.execute_reply.started":"2021-08-26T08:52:02.881502Z","shell.execute_reply":"2021-08-26T08:52:02.942871Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_cls_model(model_name):\n    assert model_name in timm.list_models(), 'Other model is not implemented now.'\n    model = timm.create_model(\n        model_name,\n        features_only=True,\n    )\n    change_layer(model, torch.nn.BatchNorm2d, torchvision.ops.misc.FrozenBatchNorm2d, params_map={'num_features':'num_features', 'eps':'eps'})\n    return model\n\nclass FuseClsFeatsBackboneBody(torch.nn.Module):\n    def __init__(self,\n                 det_backbone_body,\n                 cls_model,\n                 cls_size,\n                 cls_norm_mean,\n                 cls_norm_std,\n                 cls_feat_name=[1, 2, 3, 4],\n                 det_feat_ch=[],\n                 cls_feat_ch=[],\n                ):\n        super().__init__()\n        self.det_backbone_body = det_backbone_body\n        self.cls_model = cls_model\n        self.cls_feat_name = cls_feat_name\n        self.att_convs = torch.nn.ModuleList([torch.nn.Conv2d(in_ch, out_ch, 1) for in_ch, out_ch in zip(cls_feat_ch, det_feat_ch)])\n        self.cls_normalize = torchvision.transforms.Normalize(cls_norm_mean, cls_norm_std)\n        self.cls_resize = torchvision.transforms.Resize((cls_size, cls_size))\n    \n    def forward(self, image):\n        det_feats = self.det_backbone_body(image)\n        det_feats_keys = det_feats.keys()\n        det_feats = list(det_feats.values())\n        cls_input_image = torch.stack([self.cls_normalize(self.cls_resize(img)) for img in self.org_image])\n        with torch.no_grad():\n            cls_feats = self.cls_model(cls_input_image)\n        cls_feats = [cls_feats[k] for k in self.cls_feat_name]\n        det_feats = OrderedDict([(det_feats_key, torch.nn.functional.interpolate(conv(cls_feat).sigmoid(), det_feat.shape[2:]) * det_feat) for cls_feat, conv, det_feat, det_feats_key in zip(cls_feats, self.att_convs, det_feats, det_feats_keys)])\n        return det_feats\n\ndef add_backup_org_image(model):\n    model.org_forward = model.forward\n    def forward_with_backup(self, image, target=None):\n        self.backbone.body.org_image = copy.deepcopy(image)\n        return self.org_forward(image, target)\n    model.forward = forward_with_backup.__get__(model, model.__class__)","metadata":{"execution":{"iopub.status.busy":"2021-08-26T08:52:02.944993Z","iopub.execute_input":"2021-08-26T08:52:02.945366Z","iopub.status.idle":"2021-08-26T08:52:02.960574Z","shell.execute_reply.started":"2021-08-26T08:52:02.94532Z","shell.execute_reply":"2021-08-26T08:52:02.959661Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def GeneralEnsemble(dets, iou_thresh = 0.5, weights=None):\n    assert(type(iou_thresh) == float)\n    \n    ndets = len(dets)\n    \n    if weights is None:\n        w = 1/float(ndets)\n        weights = [w]*ndets\n    else:\n        assert(len(weights) == ndets)\n        \n        s = sum(weights)\n        for i in range(0, len(weights)):\n            weights[i] /= s\n\n    out = list()\n    used = list()\n    \n    for idet in range(0,ndets):\n        det = dets[idet]\n        for box in det:\n            if box in used:\n                continue\n                \n            used.append(box)\n            # Search the other detectors for overlapping box of same class\n            found = []\n            for iodet in range(0, ndets):\n                odet = dets[iodet]\n                \n                if odet == det:\n                    continue\n                \n                bestbox = None\n                bestiou = iou_thresh\n                for obox in odet:\n                    if not obox in used:\n                        # Not already used\n                        if box[4] == obox[4]:\n                            # Same class\n                            iou = computeIOU(box, obox)\n                            if iou > bestiou:\n                                bestiou = iou\n                                bestbox = obox\n                                \n                if not bestbox is None:\n                    w = weights[iodet]\n                    found.append((bestbox,w))\n                    used.append(bestbox)\n                            \n            # Now we've gone through all other detectors\n            if len(found) == 0:\n                new_box = list(box)\n                new_box[5] /= ndets\n                out.append(new_box)\n            else:\n                allboxes = [(box, weights[idet])]\n                allboxes.extend(found)\n                \n                xc = 0.0\n                yc = 0.0\n                bw = 0.0\n                bh = 0.0\n                conf = 0.0\n                \n                wsum = 0.0\n                for bb in allboxes:\n                    w = bb[1]\n                    wsum += w\n\n                    b = bb[0]\n                    xc += w*b[0]\n                    yc += w*b[1]\n                    bw += w*b[2]\n                    bh += w*b[3]\n                    conf += w*b[5]\n                \n                xc /= wsum\n                yc /= wsum\n                bw /= wsum\n                bh /= wsum    \n\n                new_box = [xc, yc, bw, bh, box[4], conf]\n                out.append(new_box)\n    return out\n    \ndef getCoords(box):\n    x1 = float(box[0]) - float(box[2])/2\n    x2 = float(box[0]) + float(box[2])/2\n    y1 = float(box[1]) - float(box[3])/2\n    y2 = float(box[1]) + float(box[3])/2\n    return x1, x2, y1, y2\n    \ndef computeIOU(box1, box2):\n    x11, x12, y11, y12 = getCoords(box1)\n    x21, x22, y21, y22 = getCoords(box2)\n    \n    x_left   = max(x11, x21)\n    y_top    = max(y11, y21)\n    x_right  = min(x12, x22)\n    y_bottom = min(y12, y22)\n\n    if x_right < x_left or y_bottom < y_top:\n        return 0.0    \n        \n    intersect_area = (x_right - x_left) * (y_bottom - y_top)\n    box1_area = (x12 - x11) * (y12 - y11)\n    box2_area = (x22 - x21) * (y22 - y21)        \n    \n    iou = intersect_area / (box1_area + box2_area - intersect_area)\n    return iou\n\ndef ensemble_outputs(outputs):\n    dets = [[[(box[0]+box[2])/2, (box[1]+box[3])/2, box[2]-box[0], box[3]-box[1], label, score] for box, label, score in zip(output['boxes'].tolist(), output['labels'].tolist(), output['scores'].tolist())] for output in outputs]\n    ensemble_dets = GeneralEnsemble(dets)\n    #print(ensemble_dets)\n    ensemble_outs = {'boxes': torch.FloatTensor([[det[0]-det[2]/2, det[1]-det[3]/2, det[0]+det[2]/2, det[1]+det[3]/2] for det in ensemble_dets]).view(-1, 4), 'labels': torch.LongTensor([det[4] for det in ensemble_dets]), 'scores': torch.FloatTensor([det[5] for det in ensemble_dets])}\n    return ensemble_outs","metadata":{"execution":{"iopub.status.busy":"2021-08-26T08:52:02.962099Z","iopub.execute_input":"2021-08-26T08:52:02.962528Z","iopub.status.idle":"2021-08-26T08:52:02.986502Z","shell.execute_reply.started":"2021-08-26T08:52:02.962475Z","shell.execute_reply":"2021-08-26T08:52:02.985741Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#cls_transforms = torch.nn.Sequential(\n#    torchvision.transforms.Resize((args_dict['cls']['input_size'], args_dict['cls']['input_size'])),\n#    torchvision.transforms.Normalize(args_dict['cls']['norm_mean'], args_dict['cls']['norm_std']),\n#)\n#transforms = torchvision.transforms.Compose([\n#    torchvision.transforms.ToTensor(),\n#    SplitTransformPath(2),\n#    SplitTransformWrapper(cls_transforms, [0]),\n#])\n\ndet_transforms = torchvision.transforms.Compose([\n    torchvision.transforms.ToTensor(),\n])\n#cls_collate = torch.utils.data._utils.collate.default_collate\ndet_collate = lambda x: x\n#def collates(inputs):\n#    return cls_collate([inp[0][0] for inp in inputs]) , det_collate([inp[0][1] for inp in inputs]), [inp[1] for inp in inputs]\ncls_dataset = Dataset()\ndet_dataset = Dataset(det_transforms)\ncls_dataloader = torch.utils.data.DataLoader(cls_dataset, batch_size=args_dict['batch_size'], num_workers=4, pin_memory=True)\ndet_dataloader = torch.utils.data.DataLoader(det_dataset, batch_size=args_dict['batch_size'], num_workers=4, pin_memory=True, collate_fn=det_collate)","metadata":{"execution":{"iopub.status.busy":"2021-08-26T08:52:02.987863Z","iopub.execute_input":"2021-08-26T08:52:02.988325Z","iopub.status.idle":"2021-08-26T08:52:03.000954Z","shell.execute_reply.started":"2021-08-26T08:52:02.988287Z","shell.execute_reply":"2021-08-26T08:52:03.000191Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_cls_outs(model, dataloader, use_sigmoid=False, flipH=False):\n    outs = []\n    for data in tqdm(dataloader):\n        data = data.to(device)\n        if flipH:\n            data = data.flip(-1)\n        out = model(data)\n        outs.append(out.detach().cpu())\n    outs = torch.cat(outs)\n    if use_sigmoid:\n        outs = outs.sigmoid()\n    else:\n        outs = outs.softmax(dim=1)\n    return outs\n\nwith torch.no_grad():\n    cls_outs = []\n    for ckpt_path in args_dict['cls']['ckpt_paths']:\n        ckpt = torch.load(ckpt_path, map_location='cpu')\n        args_dict['cls']['norm_mean'] = ckpt['norm_mean']\n        args_dict['cls']['norm_std'] = ckpt['norm_std']\n        \n        cls_transforms = torchvision.transforms.Compose([\n            torchvision.transforms.ToTensor(),\n            torchvision.transforms.Resize((args_dict['cls']['input_size'], args_dict['cls']['input_size'])),\n            torchvision.transforms.Normalize(args_dict['cls']['norm_mean'], args_dict['cls']['norm_std']),\n        ])\n        cls_dataloader.dataset.transforms = cls_transforms\n\n        if ckpt['args_dict']['model_name'] == 'inceptionresnetv2':\n            model = pretrainedmodels.__dict__['inceptionresnetv2'](pretrained=False, num_classes=4)\n        elif ckpt['args_dict']['model_name'] in timm.list_models():\n            model = timm.create_model(ckpt['args_dict']['model_name'], pretrained=False, num_classes=4)\n\n        model.load_state_dict(ckpt['model'])    \n        model.eval().to(device)\n        outs = get_cls_outs(\n            model, cls_dataloader,\n            use_sigmoid=ckpt['args_dict'].get('use_sigmoid', False), flipH=False)\n        if args_dict['cls']['flip_TTA']:\n            outs2 = get_cls_outs(\n                model, cls_dataloader,\n                use_sigmoid=ckpt['args_dict'].get('use_sigmoid', False), flipH=True)\n            outs = (outs + outs2) / 2\n        cls_outs.append(outs)\n    cls_outs = torch.stack(cls_outs)","metadata":{"execution":{"iopub.status.busy":"2021-08-26T08:52:03.002331Z","iopub.execute_input":"2021-08-26T08:52:03.002768Z","iopub.status.idle":"2021-08-26T08:53:46.016391Z","shell.execute_reply.started":"2021-08-26T08:52:03.002729Z","shell.execute_reply":"2021-08-26T08:53:46.014913Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with torch.no_grad():\n    fpn_bak = copy.deepcopy(FeaturePyramidNetwork)\n    det_outs = []\n    for ckpt_path in args_dict['det']['ckpt_paths']:\n        ckpt = torch.load(ckpt_path, map_location='cpu')\n        if ckpt['args_dict']['ACFPN'] or ckpt['args_dict']['iAFF']:\n            FeaturePyramidNetwork = ModulerFPN \n        else:\n            FeaturePyramidNetwork = fpn_bak\n        \n        args_dict['det']['norm_mean'] = ckpt['args_dict']['normalize_mean']\n        args_dict['det']['norm_std'] = ckpt['args_dict']['normalize_std']\n        backbone = timm_fpn_backbone(ckpt['args_dict']['backbone_body'], pretrained=False, norm_layer=args_dict['det']['norm_layer'])\n        model = FasterRCNN(\n            backbone,\n            num_classes=2,\n            min_size=ckpt['args_dict']['input_size'],\n            max_size=ckpt['args_dict']['input_size'],\n            image_mean=args_dict['det']['norm_mean'],\n            image_std=args_dict['det']['norm_std'],\n            box_score_thresh=args_dict['det']['box_score_thresh'],\n        )\n        if ckpt['args_dict']['ACFPN']:\n            model.backbone.fpn.setup_ACFPN()\n        if ckpt['args_dict']['iAFF']:\n            model.backbone.fpn.setup_iAFF()\n        cls_model = get_cls_model(ckpt['cls_model_name'])\n        cls_feat_name = [1, 2, 3, 4]\n        model.backbone.body = FuseClsFeatsBackboneBody(\n            model.backbone.body,\n            cls_model,\n            ckpt['cls_size'],\n            ckpt['cls_norm_mean'],\n            ckpt['cls_norm_std'],\n            cls_feat_name=cls_feat_name,\n            det_feat_ch=[model.backbone.body.timm_model.feature_info.info[name]['num_chs'] for name in [1,2,3,4]],\n            cls_feat_ch=[cls_model.feature_info.info[name]['num_chs'] for name in cls_feat_name],\n        )\n        add_backup_org_image(model)\n        model.load_state_dict(ckpt['model'])\n        model.eval().to(device)\n        outs = []\n        for data in tqdm(det_dataloader):\n            data = [d.to(device) for d in data]\n            out = model(data)\n            out = [{k: v.detach().cpu() for k, v in o.items()} for o in out]\n            if args_dict['det']['flip_TTA']:\n                shapes = [d.shape[-2:] for d in data]\n                data = [d.flip(-1) for d in data]\n                out2 = model(data)\n                out2 = [{k: v.detach().cpu() for k, v in o.items()} for o in out2]\n                for i, shape in enumerate(shapes):\n                    out2[i]['boxes'][:, [0, 2]] = shape[1] - out2[i]['boxes'][:, [2, 0]]\n                out = [ensemble_outputs([o, o2]) for o, o2 in zip(out, out2)]\n            outs += out           \n        det_outs.append(outs)","metadata":{"execution":{"iopub.status.busy":"2021-08-26T08:53:46.017876Z","iopub.execute_input":"2021-08-26T08:53:46.018153Z","iopub.status.idle":"2021-08-26T09:01:21.066686Z","shell.execute_reply.started":"2021-08-26T08:53:46.018124Z","shell.execute_reply":"2021-08-26T09:01:21.065843Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from collections import defaultdict\nstudy2idxes = defaultdict(list)\nfor i, path in enumerate(target_paths):\n    study2idxes[path.split('/')[-3]].append(i)\n\ndet_confs = torch.tensor([[out['scores'].max().item() if len(out['scores']) > 0 else 0. for out in outs] for outs in zip(*det_outs)], dtype=cls_outs.dtype, device=cls_outs.device).mean(dim=1)\ncls_confs = cls_outs.mean(dim=0)\nfor cls_idx in args_dict['cls']['cascade_clses']:\n    if cls_idx == 0:\n        cls_outs[:, :, 0] = 1 - det_confs[None] * (1 - cls_outs[:, :, 0])\n    else:\n        cls_outs[:, :, cls_idx] *= det_confs[None]\n\n\nids, pred_strs = [], []\nfor study, idxes in study2idxes.items():\n    ids.append(study + '_study')\n    scores = cls_outs[:, idxes].mean(dim=(0, 1))\n    pred_strs.append(' '.join([f'{name} {score} 0 0 1 1' for name, score in zip(cls_names, scores)]))\n\n#assert len(det_outs) == 1, 'ensembles not implemented.'\nfor i, (accno, org_WH) in enumerate(accno2WH.items()):\n    ids.append(accno + '_image')\n    #det_out = det_outs[0][i]\n    det_out = ensemble_outputs([det_out[i] for det_out in det_outs])\n    if len(det_out['boxes']) == 0:\n        pred_strs.append('none 1 0 0 1 1')\n    else:\n        xmins = (det_out['boxes'][:, 0] * (org_WH[0] / 1024)).tolist()\n        ymins = (det_out['boxes'][:, 1] * (org_WH[1] / 1024)).tolist()\n        xmaxes = (det_out['boxes'][:, 2] * (org_WH[0] / 1024)).tolist()\n        ymaxes = (det_out['boxes'][:, 3] * (org_WH[1] / 1024)).tolist()\n        pred_strs.append(\n            f\"none {1 - det_out['scores'].max().item()} 0 0 1 1 \" + \n            ' '.join([f'opacity {score.item()} {xmin} {ymin} {xmax} {ymax}' for score, xmin, ymin, xmax, ymax in zip(det_out['scores'], xmins, ymins, xmaxes, ymaxes)]))\n\nout_df = pd.DataFrame(OrderedDict([('Id', ids), ('PredictionString', pred_strs)]))","metadata":{"execution":{"iopub.status.busy":"2021-08-26T09:01:21.0682Z","iopub.execute_input":"2021-08-26T09:01:21.068571Z","iopub.status.idle":"2021-08-26T09:01:22.985402Z","shell.execute_reply.started":"2021-08-26T09:01:21.068532Z","shell.execute_reply":"2021-08-26T09:01:22.984538Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"out_df.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2021-08-26T09:01:22.986648Z","iopub.execute_input":"2021-08-26T09:01:22.987013Z","iopub.status.idle":"2021-08-26T09:01:23.671962Z","shell.execute_reply.started":"2021-08-26T09:01:22.986976Z","shell.execute_reply":"2021-08-26T09:01:23.671043Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"os.system(f'rm -rf {target_root_folder}')","metadata":{"execution":{"iopub.status.busy":"2021-08-26T08:24:23.187629Z","iopub.execute_input":"2021-08-26T08:24:23.187994Z","iopub.status.idle":"2021-08-26T08:24:23.474984Z","shell.execute_reply.started":"2021-08-26T08:24:23.18794Z","shell.execute_reply":"2021-08-26T08:24:23.474014Z"},"trusted":true},"execution_count":null,"outputs":[]}]}