{"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":"# 3rd place inference code\n\nThis is the 3rd place inference notebook for VCID 2023.\n\nFor any details, please refer our [solution post](https://www.kaggle.com/competitions/vesuvius-challenge-ink-detection/discussion/417536).\n\nWe released all our final submission checkpoints which include weights trained on input sizes of 224, 384 and 576.\n\nWe also release the additional checkpoints trained on input size of 640. By adding these to the pipeline, one can verify our private score 0.687 solution.\n\nOur training code is released [here](https://github.com/traptinblur/VCID_2023_3rd_place_code/tree/main). \n\n## To change the checkpoints from training, you just need to:\n\n* copy&edit the notebook\n\n* upload your own weights for models with input sizes of 224, 384, 576 and maybe 640 as well\n\n* change the path value in the `ckpts` dictionary defined in `CFG` class for each model like this:\n\n  > ```python\n  >     ckpts = dict(\n  >         adaptive_silu=dict(\n  >             size224=[\n  >                 \"your_input_size_224_fold1_path\",\n  >                 \"your_input_size_224_fold2_path\",\n  >                 \"your_input_size_224_fold3_path\",\n  >                 \"your_input_size_224_fold4_path\",\n  >                 \"your_input_size_224_fold5_path\",\n  >             ],\n  >             size384=[\n  >                 \"your_input_size_384_fold1_path\",\n  >                 \"your_input_size_384_fold2_path\",\n  >                 \"your_input_size_384_fold3_path\",\n  >                 \"your_input_size_384_fold4_path\",\n  >                 \"your_input_size_384_fold5_path\"\n  >             ],\n  >         ),\n  >         adaptive_silu_r152=dict(\n  >             size576=[\n  >                 \"your_input_size_576_fold1_path\",\n  >                 \"your_input_size_576_fold2_path\",\n  >                 \"your_input_size_576_fold3_path\",\n  >                 \"your_input_size_576_fold4_path\",\n  >                 \"your_input_size_576_fold5_path\"\n  >             ],\n  >             size640=[\n  >                 \"your_input_size_640_fold1_path\",\n  >                 \"your_input_size_640_fold2_path\",\n  >                 \"your_input_size_640_fold3_path\",\n  >                 \"your_input_size_640_fold4_path\",\n  >                 \"your_input_size_640_fold5_path\"\n  >             ]\n  >         ),\n  >     )\n  > ```\n\n* save and submit\n\n## private score 0.687 settings\n\n```python\nmodel_list = [\"adaptive_silu_r152\", \"adaptive_silu_r152\",\\\n              \"adaptive_silu\", \"adaptive_silu\"]\nbs_list = [1, 4, 8, 32]\ncrop_sizes = [640, 576, 384, 224]\ncrop_strides = [640//5, 576//4, 384//3, 224//2]\nsl_rge_ls = [range(14,50), range(18,46), range(18,46), range(18,46)]\n```\nYou just need to change these settings in class `CFG` and submit.","metadata":{}},{"cell_type":"code","source":"import os\nos.listdir('/kaggle/input/mmdetection-2-17-offline')\n\n!pip install /kaggle/input/mmdetection-2-17-offline/mmcv_full-1.3.14-cp37-cp37m-linux_x86_64.whl --no-deps\n!pip install /kaggle/input/mmdetection-2-17-offline/pycocotools-2.0.2-cp37-cp37m-linux_x86_64.whl --no-deps\n!pip install /kaggle/input/mmdetection-2-17-offline/terminaltables-3.1.0-py3-none-any.whl --no-deps\n!pip install /kaggle/input/mmdetection-2-17-offline/pytest_runner-5.3.1-py3-none-any.whl --no-deps\n!pip install /kaggle/input/mmdetection-2-17-offline/mmpycocotools-12.0.3-cp37-cp37m-linux_x86_64.whl --no-deps\n!pip install /kaggle/input/mmdetection-2-17-offline/terminal-0.4.0-py3-none-any.whl --no-deps\n!pip install /kaggle/input/mmdetection-2-17-offline/mmdet-2.17.0-py3-none-any.whl --no-deps\n!pip install /kaggle/input/mmdetection-2-17-offline/addict-2.4.0-py3-none-any.whl --no-deps\n!pip install /kaggle/input/mmdetection-2-17-offline/yapf-0.31.0-py2.py3-none-any.whl --no-deps","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.metrics import roc_auc_score, accuracy_score, f1_score, log_loss\nimport pickle\nfrom torch.utils.data import DataLoader\nfrom torch.cuda.amp import autocast, GradScaler\nimport warnings\nimport sys\nimport pandas as pd\nimport os\nimport gc\nimport sys\nimport math\nimport time\nimport random\nimport shutil\nfrom pathlib import Path\nfrom contextlib import contextmanager\nfrom collections import defaultdict, Counter\nimport cv2\nimport PIL\n\nimport scipy as sp\nimport numpy as np\nimport pandas as pd\n\nimport matplotlib.pyplot as plt\nfrom tqdm.auto import tqdm\nfrom functools import partial\n\nimport argparse\nimport importlib\nimport torch\nimport torch.nn as nn\nfrom torch.optim import Adam, SGD, AdamW\n\nimport datetime\nimport wandb","metadata":{"execution":{"iopub.status.busy":"2023-06-07T14:06:04.813198Z","iopub.execute_input":"2023-06-07T14:06:04.814204Z","iopub.status.idle":"2023-06-07T14:06:05.460271Z","shell.execute_reply.started":"2023-06-07T14:06:04.81416Z","shell.execute_reply":"2023-06-07T14:06:05.458847Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nfrom torch.utils.data import DataLoader, Dataset\nimport cv2\nimport torch\nimport os\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nfrom albumentations import ImageOnlyTransform\nfrom copy import deepcopy","metadata":{"execution":{"iopub.status.busy":"2023-06-07T14:06:05.462037Z","iopub.execute_input":"2023-06-07T14:06:05.462486Z","iopub.status.idle":"2023-06-07T14:06:05.46937Z","shell.execute_reply.started":"2023-06-07T14:06:05.46244Z","shell.execute_reply":"2023-06-07T14:06:05.468387Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sys.path.append('/kaggle/input/pretrainedmodels/pretrainedmodels-0.7.4')\nsys.path.append('/kaggle/input/efficientnet-pytorch/EfficientNet-PyTorch-master')\nsys.path.append('/kaggle/input/timm-pytorch-image-models/pytorch-image-models-master')\nsys.path.append('/kaggle/input/segmentation-models-pytorch/segmentation_models.pytorch-master')","metadata":{"execution":{"iopub.status.busy":"2023-06-07T14:06:05.472908Z","iopub.execute_input":"2023-06-07T14:06:05.474038Z","iopub.status.idle":"2023-06-07T14:06:05.481784Z","shell.execute_reply.started":"2023-06-07T14:06:05.474Z","shell.execute_reply":"2023-06-07T14:06:05.480945Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Config","metadata":{}},{"cell_type":"code","source":"import os\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\n\nclass CFG:\n    # ============== comp exp name =============\n    comp_name = 'vesuvius'\n    comp_dir_path = '/kaggle/input/'\n    comp_folder_name = 'vesuvius-challenge-ink-detection'\n    comp_dataset_path = f'{comp_dir_path}{comp_folder_name}/'\n    \n    exp_name = 'vesuvius_2d_slide_exp002'\n\n    # ============== pred target =============\n    target_size = 1\n\n    # ============== model cfg =============\n    ckpts = dict(\n        adaptive_silu=dict(\n            size224=[\n                \"/kaggle/input/best-cv-lb/exp35_1_06404_none-ema.pth\",\n                \"/kaggle/input/best-cv-lb/exp30_2_07018.pth\",\n                \"/kaggle/input/best-cv-lb/exp30_3_06979.pth\",\n                \"/kaggle/input/best-cv-lb/exp20_4_07399.pth\",\n                \"/kaggle/input/0611-exp29-fold5-none-ema/exp29_5_07440_03320_6-1_none-ema.pth\",\n            ],\n            size384=[\n                \"/kaggle/input/0607-exp45-384/adaptive_1_28-24_384_06177_03088.pth\",\n                \"/kaggle/input/0607-exp45-384/ema_adaptive_2_28-24_384_07110_03025.pth\",\n                \"/kaggle/input/0607-exp45-384/adaptive_3_28-24_384_07036_02153.pth\",\n                \"/kaggle/input/0607-exp45-384/adaptive_4_28-24_384_07212_03441.pth\",\n                \"/kaggle/input/0607-exp45-384/ema_adaptive_5_28-24_384_07303_03035.pth\"\n            ],\n        ),\n        adaptive_silu_r152=dict(\n            size576=[\n                \"/kaggle/input/0611-r152-576-fold-1-4/adaptive_1_28-24_576_06511.pth\",\n                \"/kaggle/input/0609-adaptive-r152-576/ema_adaptive_r152_2_28-24_576_07279_02480.pth\",\n                \"/kaggle/input/0609-adaptive-r152-576/ema_adaptive_r152_3_28-24_576_06907_02394.pth\",\n                \"/kaggle/input/0611-r152-576-fold-1-4/ema_adaptive_4_28-24_576_07670.pth\",\n                \"/kaggle/input/0609-adaptive-r152-576/ema_adaptive_r152_5_28-24_576_07714_02732.pth\",\n            ],\n            size640=[\n                \"/kaggle/input/0611-r152-640-fold-1-2-3-4/adaptive_r152_1_36-32_640_06304_03784.pth\",\n                \"/kaggle/input/0611-r152-640-fold-1-2-3-4/ema_adaptive_r152_2_36-32_640_06964_02289.pth\",\n                \"/kaggle/input/0611-r152-640-fold-1-2-3-4/ema_adaptive_r152_3_36-32_640_07010_02232.pth\",\n                \"/kaggle/input/0611-r152-640-fold-1-2-3-4/adaptive_r152_4_36-32_640_07334_03456.pth\",\n                \"/kaggle/input/0611-adaptive-r152-640/ema_adaptive_silu_r152_fold5_slices36_resolution640_stride128_best.pth\",\n            ]\n        ),\n    )\n    model_list = [\"adaptive_silu_r152\", \"adaptive_silu\", \"adaptive_silu\"]\n    # ============== inference cfg ============= \n    rot_tta = False\n    normalization = True\n    bs_list = [4, 8, 32]\n    crop_sizes = [576, 384, 224]\n    crop_strides = [576//8, 384//6, 224//8]\n    sl_rge_ls = [range(18,46), range(18,46), range(18,46)]\n    \n    assert len(crop_sizes) == len(model_list)\n    \n    adaptive_cfg = dict(\n        adaptive_silu=dict(\n            size224=dict(\n                num_slices=28,\n                tr_idx=[0,2,4],\n                tr_slices=24,\n            ),\n            size384=dict(\n                num_slices=28,\n                tr_idx=[0,2,4],\n                tr_slices=24,\n            ),\n        ),\n        adaptive_silu_r152=dict(\n            size576=dict(\n                num_slices=28,\n                tr_idx=[0,2,4],\n                tr_slices=24,\n            ),\n            size640=dict(\n                num_slices=36,\n                tr_idx=[0,2,4],\n                tr_slices=32,\n            ),\n        ),\n    )\n    # ============== fixed =============\n    num_workers = 2","metadata":{"execution":{"iopub.status.busy":"2023-06-07T14:06:05.483442Z","iopub.execute_input":"2023-06-07T14:06:05.484016Z","iopub.status.idle":"2023-06-07T14:06:05.49669Z","shell.execute_reply.started":"2023-06-07T14:06:05.483979Z","shell.execute_reply":"2023-06-07T14:06:05.495619Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"IS_DEBUG = False\nmode = 'train' if IS_DEBUG else 'test'\nTH = 0.5","metadata":{"execution":{"iopub.status.busy":"2023-06-07T14:06:05.49813Z","iopub.execute_input":"2023-06-07T14:06:05.499398Z","iopub.status.idle":"2023-06-07T14:06:05.510666Z","shell.execute_reply.started":"2023-06-07T14:06:05.499369Z","shell.execute_reply":"2023-06-07T14:06:05.509701Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')","metadata":{"execution":{"iopub.status.busy":"2023-06-07T14:06:05.512357Z","iopub.execute_input":"2023-06-07T14:06:05.512748Z","iopub.status.idle":"2023-06-07T14:06:05.586797Z","shell.execute_reply.started":"2023-06-07T14:06:05.512712Z","shell.execute_reply":"2023-06-07T14:06:05.585595Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## helper","metadata":{}},{"cell_type":"code","source":"from io import StringIO\ndef combined_rle(img):\n    \"\"\"ref: https://www.kaggle.com/code/kotaiizuka/faster-rle\n    \"\"\"\n    pixels = img.flatten()\n    pixels[0] = 0\n    pixels[-1] = 0\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 2\n    runs[1::2] = runs[1::2] - runs[:-1:2]\n    f = StringIO()\n    np.savetxt(f, runs.reshape(1, -1), delimiter=\" \", fmt=\"%d\")\n    predicted = f.getvalue().strip()\n    return predicted\n\ndef normalization(x:torch.Tensor)->torch.Tensor:\n    \"\"\"input.shape=(batch,f1,f2,...)\"\"\"\n    #[batch,f1,f2]->dim[1,2]\n    dim=list(range(1,x.ndim))\n    mean=x.mean(dim=dim,keepdim=True)\n    std=x.std(dim=dim,keepdim=True)\n    return (x-mean)/(std+1e-9)","metadata":{"execution":{"iopub.status.busy":"2023-06-07T14:06:05.589802Z","iopub.execute_input":"2023-06-07T14:06:05.59062Z","iopub.status.idle":"2023-06-07T14:06:05.601641Z","shell.execute_reply.started":"2023-06-07T14:06:05.59058Z","shell.execute_reply":"2023-06-07T14:06:05.600385Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## dataset","metadata":{}},{"cell_type":"code","source":"def read_image(fragment_id):\n    images = []\n    \n    idcs = CFG.slice_idcs\n\n    for cnt, i in enumerate(tqdm(idcs)):\n        \n        image = cv2.imread(CFG.comp_dataset_path + f\"{mode}/{fragment_id}/surface_volume/{i:02}.tif\", 0)\n        \n        if cnt+1 == len(idcs):\n            ori_h, ori_w = image.shape\n\n        pad0 = (CFG.tile_size - image.shape[0] % CFG.tile_size)\n        pad1 = (CFG.tile_size - image.shape[1] % CFG.tile_size)\n\n        image = np.pad(image, [(0, pad0), (0, pad1)], constant_values=0)\n        \n        images.append(image)\n    images = np.stack(images, axis=2)\n    \n    return images, ori_h, ori_w","metadata":{"execution":{"iopub.status.busy":"2023-06-07T14:06:05.604503Z","iopub.execute_input":"2023-06-07T14:06:05.604932Z","iopub.status.idle":"2023-06-07T14:06:05.616682Z","shell.execute_reply.started":"2023-06-07T14:06:05.604904Z","shell.execute_reply":"2023-06-07T14:06:05.615619Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CustomDataset(Dataset):\n    def __init__(self, images, xyxys, cfg, labels=None, transform=None):\n        self.images = images\n        self.cfg = cfg\n        self.labels = labels\n        self.transform = transform\n        self.xyxys = xyxys\n\n    def __len__(self):\n        return len(self.images)\n\n    def __getitem__(self, idx):\n        image = self.images[idx]\n        image = torch.from_numpy(image).permute(2,0,1).to(torch.float32)/255.\n        \n        return image.unsqueeze(0), self.xyxys[idx]","metadata":{"execution":{"iopub.status.busy":"2023-06-07T14:06:05.622194Z","iopub.execute_input":"2023-06-07T14:06:05.623525Z","iopub.status.idle":"2023-06-07T14:06:05.632415Z","shell.execute_reply.started":"2023-06-07T14:06:05.623486Z","shell.execute_reply":"2023-06-07T14:06:05.631135Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def make_test_dataset(fragment_id):\n    test_images, ori_h, ori_w = read_image(fragment_id)\n\n    padded_h, padded_w = test_images.shape[:-1]\n    x1_list = list(range(0, padded_w-CFG.tile_size+1, CFG.stride))\n    y1_list = list(range(0, padded_h-CFG.tile_size+1, CFG.stride))\n    \n    test_images_list = []\n    xyxys = []\n    for y1 in y1_list:\n        for x1 in x1_list:\n            y2 = y1 + CFG.tile_size\n            x2 = x1 + CFG.tile_size\n            \n            if np.all(test_images[y1:y2, x1:x2]==0):\n                continue\n            \n            test_images_list.append(test_images[y1:y2, x1:x2])\n            xyxys.append((x1, y1, x2, y2))\n    xyxys = np.stack(xyxys)\n\n    test_dataset = CustomDataset(test_images_list, xyxys, CFG)\n    \n    test_loader = DataLoader(test_dataset,\n                          batch_size=CFG.batch_size,\n                          shuffle=False,\n                          num_workers=CFG.num_workers, pin_memory=True, drop_last=False)\n    \n    return test_loader, ori_h, ori_w, padded_h, padded_w","metadata":{"execution":{"iopub.status.busy":"2023-06-07T14:06:05.63388Z","iopub.execute_input":"2023-06-07T14:06:05.634983Z","iopub.status.idle":"2023-06-07T14:06:05.646479Z","shell.execute_reply.started":"2023-06-07T14:06:05.634946Z","shell.execute_reply":"2023-06-07T14:06:05.645523Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## model","metadata":{}},{"cell_type":"code","source":"import sys\nsys.path.append('/kaggle/input/hybrid-unet')\nimport hybrid_unet2\n\n\ndef build_model(cfg, weight=\"imagenet\"):\n    if CFG.model_name == \"hybrid_silu\":\n        model = hybrid_unet2.__dict__[CFG.model_name]()\n    elif CFG.model_name == \"hybrid_silu_r152\":\n        model = hybrid_unet2.__dict__[CFG.model_name]()\n    elif CFG.model_name == \"adaptive_silu\":\n        model = hybrid_unet2.__dict__[CFG.model_name](**CFG.adaptive_cfg[CFG.model_name][\"size\"+str(CFG.tile_size)])\n    elif CFG.model_name == \"adaptive_silu_r152\":\n        model = hybrid_unet2.__dict__[CFG.model_name](**CFG.adaptive_cfg[CFG.model_name][\"size\"+str(CFG.tile_size)])\n    return model","metadata":{"execution":{"iopub.status.busy":"2023-06-07T14:06:05.647672Z","iopub.execute_input":"2023-06-07T14:06:05.649572Z","iopub.status.idle":"2023-06-07T14:06:18.552662Z","shell.execute_reply.started":"2023-06-07T14:06:05.649543Z","shell.execute_reply":"2023-06-07T14:06:18.551423Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class EnsembleModel(nn.Module):\n    def __init__(self, input_size, crop_size, ckpts=CFG.ckpts):\n        super().__init__()\n        self.model = nn.ModuleList()\n        self.input_size = input_size\n        self.crop_size = crop_size\n        self.ckpts = ckpts\n        \n        assert self.input_size == self.crop_size\n        \n        for ckpt_path in self.ckpts[CFG.model_name][\"size\"+str(self.input_size)]:\n        \n            _model = build_model(CFG, weight=None)\n\n            state = torch.load(ckpt_path, map_location='cpu')['model']\n\n            _model.load_state_dict(state)\n            _model.eval()\n\n            self.model.append(_model)\n    \n    def forward(self,x):\n        output=[]\n        for m in self.model:\n            output.append(m(x).sigmoid())\n        output=torch.stack(output,dim=0).mean(0)\n        return output","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def TTA(x:torch.Tensor,model:nn.Module):\n    if CFG.rot_tta:\n        b,c,t,h,w = x.shape\n        x=[x,*[torch.rot90(x,k=i,dims=(-2,-1)) for i in range(1,4)]]\n        x=torch.cat(x,dim=0)\n        x=model(x)\n        x=x.reshape(4,b,1,h,w)\n        x=[torch.rot90(x[i],k=-i,dims=(-2,-1)) for i in range(4)]\n        x=torch.stack(x,dim=0)\n        return x.mean(0)\n    else:\n        x=model(x)\n        return x","metadata":{"execution":{"iopub.status.busy":"2023-06-07T14:06:18.575935Z","iopub.execute_input":"2023-06-07T14:06:18.576418Z","iopub.status.idle":"2023-06-07T14:06:18.592497Z","shell.execute_reply.started":"2023-06-07T14:06:18.576381Z","shell.execute_reply":"2023-06-07T14:06:18.591526Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if mode == 'test':\n    fragment_ids = sorted(os.listdir(CFG.comp_dataset_path + mode))\nelse:\n    fragment_ids = [3]","metadata":{"execution":{"iopub.status.busy":"2023-06-07T14:06:18.594168Z","iopub.execute_input":"2023-06-07T14:06:18.594677Z","iopub.status.idle":"2023-06-07T14:06:18.607514Z","shell.execute_reply.started":"2023-06-07T14:06:18.59464Z","shell.execute_reply":"2023-06-07T14:06:18.606308Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## main","metadata":{}},{"cell_type":"code","source":"# results = []\n# fragment_ids = [\"b\", \"a\"]\npred_cnt = {}  # safty check\npred_cnt[\"a\"] = 0\npred_cnt[\"b\"] = 0\n\nfor CFG.tile_size, CFG.stride, CFG.model_name, CFG.slice_idcs, CFG.batch_size in zip(CFG.crop_sizes, CFG.crop_strides, CFG.model_list, CFG.sl_rge_ls, CFG.bs_list):\n    for fragment_id in fragment_ids:\n        os.makedirs(f\"/kaggle/working/{fragment_id}\", exist_ok=True)\n        \n        model = EnsembleModel(CFG.tile_size, CFG.tile_size).cuda()\n        \n        test_loader, ori_h, ori_w, padded_h, padded_w = make_test_dataset(fragment_id)\n        \n        assert (padded_h - ori_h) == (CFG.tile_size - ori_h % CFG.tile_size)\n        assert (padded_w - ori_w) == (CFG.tile_size - ori_w % CFG.tile_size)\n\n        mask_pred = np.zeros((padded_h, padded_w))\n        mask_count = np.zeros((padded_h, padded_w))\n\n        for step, (images, xyxys) in tqdm(enumerate(test_loader), total=len(test_loader)):\n            images = images.cuda()\n\n            if CFG.normalization:\n                images = normalization(images)\n\n            batch_size = images.size(0)\n\n            with torch.no_grad():\n                y_preds = TTA(images,model).detach().cpu().numpy()\n\n            for i, (x1, y1, x2, y2) in enumerate(xyxys):\n                mask_pred[y1:y2, x1:x2] += y_preds[i].squeeze(0)  # (N,1,224,224)->(1,224,224)->(224,224)\n                mask_count[y1:y2, x1:x2] += np.ones((CFG.tile_size, CFG.tile_size))\n\n        print(f'mask_count_min: {mask_count.min()}')\n        mask_pred /= (mask_count+1e-7)  # suppress the overlap preds\n        mask_pred = mask_pred[:ori_h, :ori_w]\n        \n        np.save(f\"/kaggle/working/{fragment_id}/crop{CFG.tile_size}_{CFG.model_name}.npy\", mask_pred)\n        pred_cnt[fragment_id] += 1\n        \n        assert pred_cnt[fragment_id] == len(os.listdir(f\"/kaggle/working/{fragment_id}\"))\n\n        del mask_pred, mask_count, model\n        del test_loader\n\n        gc.collect()\n        torch.cuda.empty_cache()","metadata":{"execution":{"iopub.status.busy":"2023-05-01T08:13:32.601168Z","iopub.execute_input":"2023-05-01T08:13:32.601541Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## submission","metadata":{}},{"cell_type":"code","source":"results = []\nfor fragment_id in fragment_ids:\n    \n    binary_mask = cv2.imread(CFG.comp_dataset_path + f\"{mode}/{fragment_id}/mask.png\", 0)\n    binary_mask = (binary_mask / 255).astype(int)\n    \n    mask_preds = []\n    pred_list = sorted(os.listdir(f\"/kaggle/working/{fragment_id}\"))\n    num_preds = len(pred_list)\n    \n    assert num_preds == len(CFG.crop_sizes)\n    \n    fig, axes = plt.subplots(1, num_preds, figsize=(12, 8))\n    for idx, pred_path in enumerate(pred_list):  # visualize each ensemble pred\n        pred = np.load(f\"/kaggle/working/{fragment_id}/{pred_path}\")\n        mask_preds.append(pred)\n        try:\n            axes[idx].imshow(pred)\n        except:  # ncols=1 is not subscriptable\n            axes.imshow(pred)\n    plt.show()\n        \n    fig, axes = plt.subplots(1, 3, figsize=(12, 8))\n    mask_preds = np.mean(mask_preds, 0)\n    axes[0].imshow(mask_preds)  # show mean pred\n    mask_preds = (mask_preds >= TH).astype(int)\n    axes[1].imshow(mask_preds)  # show THed pred\n    mask_preds *= binary_mask\n    axes[2].imshow(mask_preds)  # show suppressed pred\n    plt.show()\n    \n    inklabels_rle = combined_rle(mask_preds)\n    results.append((fragment_id, inklabels_rle))\n    \n    del mask_preds\n    gc.collect()\n    \n    plt.clf()\n    fig.clear()\n    plt.close(fig)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub = pd.DataFrame(results, columns=['Id', 'Predicted'])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_sub = pd.read_csv(CFG.comp_dataset_path + 'sample_submission.csv')\nsample_sub = pd.merge(sample_sub[['Id']], sub, on='Id', how='left')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_sub","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_sub.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}