{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":45867,"databundleVersionId":6924515,"sourceType":"competition"},{"sourceId":6774400,"sourceType":"datasetVersion","datasetId":3895136},{"sourceId":6774553,"sourceType":"datasetVersion","datasetId":3898019},{"sourceId":6950745,"sourceType":"datasetVersion","datasetId":3992046},{"sourceId":6987163,"sourceType":"datasetVersion","datasetId":4015657},{"sourceId":6992640,"sourceType":"datasetVersion","datasetId":4019175},{"sourceId":7006799,"sourceType":"datasetVersion","datasetId":4028088}],"dockerImageVersionId":30559,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Cancer🔬 Classification: Inference with ⚡`lightning`\n\n**It is continuation of Training: https://www.kaggle.com/code/jirkaborovec/cancer-subtype-tiles-w-lightning-timm-models**","metadata":{}},{"cell_type":"code","source":"!ls /kaggle/input/pyvips-python-and-deb-package-gpu\n# intall the deb packages\n!yes | dpkg -i --force-depends /kaggle/input/pyvips-python-and-deb-package-gpu/linux_packages/archives/*.deb\n# install the python wrapper\n!pip install -q pyvips -f /kaggle/input/pyvips-python-and-deb-package-gpu/python_packages/ --no-index","metadata":{"_kg_hide-output":true,"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-21T01:36:21.19809Z","iopub.execute_input":"2023-11-21T01:36:21.19837Z","iopub.status.idle":"2023-11-21T01:37:24.306652Z","shell.execute_reply.started":"2023-11-21T01:36:21.198344Z","shell.execute_reply":"2023-11-21T01:37:24.305378Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nfrom torch.utils.data import Dataset\n\nimport os, glob\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nimport cv2\nimport pyvips\nimport random\nfrom PIL import Image","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-21T01:37:24.308874Z","iopub.execute_input":"2023-11-21T01:37:24.309666Z","iopub.status.idle":"2023-11-21T01:37:28.057294Z","shell.execute_reply.started":"2023-11-21T01:37:24.309626Z","shell.execute_reply":"2023-11-21T01:37:28.056497Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DATASET_FOLDER = \"/kaggle/input/UBC-OCEAN/\"\nIMAGES_FOLDER = \"./test_tiles\"\n\nos.environ['VIPS_CONCURRENCY'] = '4'\nos.environ['VIPS_DISC_THRESHOLD'] = '15gb'","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-21T01:37:28.058424Z","iopub.execute_input":"2023-11-21T01:37:28.058821Z","iopub.status.idle":"2023-11-21T01:37:28.063524Z","shell.execute_reply.started":"2023-11-21T01:37:28.058795Z","shell.execute_reply":"2023-11-21T01:37:28.062374Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data for inference\n\n**Note, we canot extract all tiles from all imges because of unsufficient space/storage**\n\nThis needs porting several code (classes) form the training notebook:\n\n- extracting the tiles from whole image\n- validation argmention, mainly color mean & STD","metadata":{}},{"cell_type":"code","source":"CFG = {\n    \"seed\": 3047,\n    \"img_size\": (300,225),#Final size\n    \"model_dir\": \"/kaggle/input/cancer225\",\n    \"path\": \"/kaggle/input/m70823/model-708-23.ckpt\",\n    \"test_batch_size\": 8,\n    \"scale\": 0.5,\n    \"max_samples\": 32,\n    \"category\": 5,\n    \"num_workers\": 4,\n    \"learning_rate\": 3e-2,\n#     \"device\": torch.device('cuda' if torch.cuda.is_available() else \"cpu\")\n}","metadata":{"execution":{"iopub.status.busy":"2023-11-21T01:37:28.066024Z","iopub.execute_input":"2023-11-21T01:37:28.066359Z","iopub.status.idle":"2023-11-21T01:37:28.076809Z","shell.execute_reply.started":"2023-11-21T01:37:28.066329Z","shell.execute_reply":"2023-11-21T01:37:28.075973Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train = pd.read_csv(os.path.join(DATASET_FOLDER, \"train.csv\"))\nlabels = sorted(df_train[\"label\"].unique())\nprint(f\"{labels=}\")\ndel df_train","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-21T01:37:28.078009Z","iopub.execute_input":"2023-11-21T01:37:28.078585Z","iopub.status.idle":"2023-11-21T01:37:28.106772Z","shell.execute_reply.started":"2023-11-21T01:37:28.078554Z","shell.execute_reply":"2023-11-21T01:37:28.10595Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def drop_image(image_array, threshold=0.55):\n\n    total_pixels = image_array.size\n    condition_black = np.logical_not(image_array > 5)\n    num_condition_black = np.sum(condition_black)\n\n    condition_white = np.logical_not(image_array < 235)\n    num_condition_white = np.sum(condition_white)\n\n    white = num_condition_white / total_pixels\n    black = num_condition_black / total_pixels\n    discard = white + black\n\n    if discard < threshold:\n        return False  \n    else:\n        return True # drop","metadata":{"execution":{"iopub.status.busy":"2023-11-21T01:37:28.107843Z","iopub.execute_input":"2023-11-21T01:37:28.108424Z","iopub.status.idle":"2023-11-21T01:37:28.114082Z","shell.execute_reply.started":"2023-11-21T01:37:28.108384Z","shell.execute_reply":"2023-11-21T01:37:28.113166Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def generate_image_blocks(image, block_size=(2048, 2048)):\n    i=0\n    for y in range(0, image.height, block_size[1]):\n        i += 1\n        j = 0\n        for x in range(0, image.width, block_size[0]):\n            j += 1\n            left = x\n            top = y\n            width = min(block_size[0], image.width - x)\n            height = min(block_size[1], image.height - y)\n\n            block = image.crop(left, top, width, height).numpy()\n            yield block,i,j","metadata":{"execution":{"iopub.status.busy":"2023-11-21T01:37:28.115298Z","iopub.execute_input":"2023-11-21T01:37:28.115618Z","iopub.status.idle":"2023-11-21T01:37:28.123553Z","shell.execute_reply.started":"2023-11-21T01:37:28.115587Z","shell.execute_reply":"2023-11-21T01:37:28.122808Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# def crop_tma(image,block=(600,450)):\n#     width, height = image.width, image.height\n#     center_x, center_y = width // 2, height // 2\n#     X,Y=block[0]//2,block[1]//2\n#     top_left = image.extract_area(X, Y, block[0], block[1])\n#     top_right = image.extract_area(center_x-X, Y, block[0], block[1])\n#     center  = image.extract_area(center_x-X, center_y-Y, block[0], block[1])\n#     bottom_left = image.extract_area(Y, center_y, block[0], block[1])\n#     bottom_right = image.extract_area(center_x, center_y, block[0], block[1])\n#     pyvips_images = [top_left, top_right, center,bottom_left, bottom_right]\n#     #out = input.extract_area(left, top, width, height)\n#     return pyvips_images","metadata":{"execution":{"iopub.status.busy":"2023-11-21T01:37:28.124398Z","iopub.execute_input":"2023-11-21T01:37:28.124669Z","iopub.status.idle":"2023-11-21T01:37:28.135858Z","shell.execute_reply.started":"2023-11-21T01:37:28.124638Z","shell.execute_reply":"2023-11-21T01:37:28.135172Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def crop_tma(image,block=(600,450)):\n    width, height = image.width, image.height\n    X,Y = block[0]//2,block[1]//2\n    center_x, center_y = width // 2, height // 2\n    top_left = image.extract_area(center_x-2*X,center_y-2*Y,block[0], block[1])\n    top_right = image.extract_area(center_x,center_y-2*Y, block[0],block[1])\n    center  = image.extract_area(center_x-X, center_y-Y, block[0], block[1])\n    bottom_left = image.extract_area(center_x-2*X,center_y, block[0], block[1])\n    bottom_right = image.extract_area(center_x, center_y,block[0],block[1])\n    # out = input.extract_area(left, top, width, height)\n    pyvips_images = [top_left, top_right, center,bottom_left, bottom_right]\n    \n    return pyvips_images","metadata":{"execution":{"iopub.status.busy":"2023-11-21T01:37:28.136819Z","iopub.execute_input":"2023-11-21T01:37:28.13711Z","iopub.status.idle":"2023-11-21T01:37:28.145227Z","shell.execute_reply.started":"2023-11-21T01:37:28.137077Z","shell.execute_reply":"2023-11-21T01:37:28.144486Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def resize_and_save(image, output_path, target_size=(600, 450)):\n    # Convert PyVips image to NumPy array\n    image_np = np.ndarray(buffer=image.write_to_memory(), dtype=np.uint8, shape=[image.height, image.width, image.bands])\n    \n    # Resize the NumPy array\n    resized_image = cv2.resize(image_np, target_size, interpolation=cv2.INTER_LANCZOS4)\n    resized_image = cv2.cvtColor(resized_image, cv2.COLOR_BGR2RGB)\n    # Save the resized image\n    cv2.imwrite(output_path, resized_image)","metadata":{"execution":{"iopub.status.busy":"2023-11-21T01:37:28.148675Z","iopub.execute_input":"2023-11-21T01:37:28.148974Z","iopub.status.idle":"2023-11-21T01:37:28.155451Z","shell.execute_reply.started":"2023-11-21T01:37:28.148942Z","shell.execute_reply":"2023-11-21T01:37:28.154604Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def extract_image_tiles(\n    p_img, folder, scale: float = CFG[\"scale\"],\n    drop_thr: float = 0.35, white_thr: int = 240, max_samples: int = CFG[\"max_samples\"]\n    ) -> list:\n    name, _ = os.path.splitext(os.path.basename(p_img))\n    im = pyvips.Image.new_from_file(p_img)\n    block_size = (600, 450)\n    w,h = block_size[0],block_size[1]\n    files = []\n    weights = [2, 5]  # roughly True:False = 3:7\n    result_files = []  # Result files list\n    new_size = int(w * scale), int(h * scale)\n    if im.height < 7180 or im.width < 7180:\n        pyvips_images = crop_tma(im)\n        output_paths = ['top.png', 't_right.png', 'center.png','b_left.png', 'b_right.png']\n\n        for tile, output_path in zip(pyvips_images, output_paths):\n            p_img = os.path.join(folder,output_path)\n            resize_and_save(tile, p_img,target_size=new_size)\n            result_files.append(p_img)\n    else:\n        for tile, i, j in generate_image_blocks(im, block_size):\n            if drop_image(tile, threshold=drop_thr):\n                continue\n#             if tile.shape[:2] != (h, w):\n#                     tile_ = tile\n#                     tile_size = (h, w) if tile.ndim == 2 else (h, w, tile.shape[2])\n#                     tile = np.zeros(tile_size, dtype=tile.dtype)\n#                     tile[:tile_.shape[0], :tile_.shape[1], ...] = tile_\n#             black_bg = np.sum(tile, axis=2) == 0\n#             tile[black_bg, :] = 255\n#             mask_bg = np.mean(tile, axis=2) > white_thr\n#             if np.sum(mask_bg) >= (np.prod(mask_bg.shape) * drop_thr):\n#                 continue\n            p_img = os.path.join(folder, f\"{i}-{j}.png\")\n\n            tile = cv2.resize(tile, new_size, interpolation=cv2.INTER_LANCZOS4)\n            tile = cv2.cvtColor(tile, cv2.COLOR_BGR2RGB)\n            cv2.imwrite(p_img, tile)\n            result_files.append(p_img)\n            # need to set counter check as some empty tiles could be skipped earlier\n            if len(result_files) >= max_samples:\n                break\n\n    return result_files","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-21T01:37:28.156592Z","iopub.execute_input":"2023-11-21T01:37:28.156844Z","iopub.status.idle":"2023-11-21T01:37:28.169231Z","shell.execute_reply.started":"2023-11-21T01:37:28.156822Z","shell.execute_reply":"2023-11-21T01:37:28.168321Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def extract_prune_tiles(\n    path_img: str, folder: str, \n) -> str:\n    print(f\"processing: {path_img}\")\n    name, _ = os.path.splitext(os.path.basename(path_img))\n    folder = os.path.join(folder, name)\n    os.makedirs(folder, exist_ok=True)\n    tiles = extract_image_tiles(path_img, folder,)\n    return folder","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-21T01:37:28.170259Z","iopub.execute_input":"2023-11-21T01:37:28.170522Z","iopub.status.idle":"2023-11-21T01:37:28.182637Z","shell.execute_reply.started":"2023-11-21T01:37:28.1705Z","shell.execute_reply":"2023-11-21T01:37:28.181907Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import albumentations as A\nfrom albumentations.pytorch import ToTensorV2\n\nimg_color_mean = [0.8721593659261734, 0.7799686061900686, 0.8644588534918227]\nimg_color_std = [0.08258995918115268, 0.10991684444009092, 0.06839816226731532]\n\nVALID_TRANSFORM = A.Compose([\n#                 A.CenterCrop(512,512),\n                A.Resize(height=CFG[\"img_size\"][1], width=CFG[\"img_size\"][0]),\n#                 A.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),\n                A.Normalize(img_color_mean, img_color_std),\n                ToTensorV2(),\n])","metadata":{"execution":{"iopub.status.busy":"2023-11-21T01:37:28.183664Z","iopub.execute_input":"2023-11-21T01:37:28.183972Z","iopub.status.idle":"2023-11-21T01:37:29.888689Z","shell.execute_reply.started":"2023-11-21T01:37:28.183918Z","shell.execute_reply":"2023-11-21T01:37:29.887696Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class TilesFolderDataset(Dataset):\n\n    def __init__(\n        self,\n        folder: str,\n        image_ext: str =  '.png',\n        transforms = None\n    ):\n        assert os.path.isdir(folder)\n        self.transforms = transforms\n        self.imgs = glob.glob(os.path.join(folder, \"*\" + image_ext))\n\n    def __getitem__(self, idx: int) -> tuple:\n        img_path = self.imgs[idx]\n        assert os.path.isfile(img_path), f\"missing: {img_path}\"\n#         img = np.array(Image.open(img_path))[..., :3]\n        img = cv2.imread(img_path)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        # filter background to white\n#         mask = np.sum(img, axis=2) == 0\n#         img[mask, :] = 255\n#         if np.max(img) < 1.5:\n#             img = np.clip(img * 255, 0, 255).astype(np.uint8)\n        # augmentation\n        if self.transforms:\n#             img = self.transforms(Image.fromarray(img))\n            img = self.transforms(image=img)[\"image\"]\n        #print(f\"img dim: {img.shape}\")\n        return img\n\n    def __len__(self) -> int:\n        return len(self.imgs)","metadata":{"execution":{"iopub.status.busy":"2023-11-21T01:37:29.890082Z","iopub.execute_input":"2023-11-21T01:37:29.890644Z","iopub.status.idle":"2023-11-21T01:37:29.899082Z","shell.execute_reply.started":"2023-11-21T01:37:29.890605Z","shell.execute_reply":"2023-11-21T01:37:29.898088Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ls = sorted(glob.glob(os.path.join(DATASET_FOLDER, \"test_images\", '*.png')))\n# print(f\"found images: {len(ls)}\")\nfolder_tiles = extract_prune_tiles(ls[0], IMAGES_FOLDER)#, size=2048, scale=0.25)\ndataset = TilesFolderDataset(folder_tiles)\nprint(f\"found tiles: {len(dataset)}\")\n\n# quick view\nfig, axes = plt.subplots(nrows=3, ncols=3, figsize=(10, 10))\nfor i in range(9):\n    img = dataset[i]\n    axes[i // 3, i % 3].imshow(img)\n# fig.tight_layout()","metadata":{"execution":{"iopub.status.busy":"2023-11-21T01:37:29.900648Z","iopub.execute_input":"2023-11-21T01:37:29.901023Z","iopub.status.idle":"2023-11-21T01:37:49.254821Z","shell.execute_reply.started":"2023-11-21T01:37:29.900992Z","shell.execute_reply":"2023-11-21T01:37:49.25333Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# os.system(f\"rm -rf /kaggle/working/test_tiles\")","metadata":{"execution":{"iopub.status.busy":"2023-11-21T01:37:49.256133Z","iopub.execute_input":"2023-11-21T01:37:49.256471Z","iopub.status.idle":"2023-11-21T01:37:49.26053Z","shell.execute_reply.started":"2023-11-21T01:37:49.256441Z","shell.execute_reply":"2023-11-21T01:37:49.259621Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## CNN Model\n\nWe start with some stanrd CNN models taken from torch vision.","metadata":{}},{"cell_type":"code","source":"import sys\nsys.path.append(CFG['model_dir'])\n\nfrom model import CapsNet","metadata":{"execution":{"iopub.status.busy":"2023-11-21T01:37:49.261709Z","iopub.execute_input":"2023-11-21T01:37:49.261979Z","iopub.status.idle":"2023-11-21T01:37:49.32066Z","shell.execute_reply.started":"2023-11-21T01:37:49.261952Z","shell.execute_reply":"2023-11-21T01:37:49.319871Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"network = CapsNet(conv_inputs=3, \n                     num_classes=CFG[\"category\"],\n                     init_weights=True,)","metadata":{"execution":{"iopub.status.busy":"2023-11-21T01:37:49.321682Z","iopub.execute_input":"2023-11-21T01:37:49.321953Z","iopub.status.idle":"2023-11-21T01:37:49.395147Z","shell.execute_reply.started":"2023-11-21T01:37:49.321912Z","shell.execute_reply":"2023-11-21T01:37:49.394298Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torchvision\nimport pytorch_lightning as pl\nfrom torch import nn\nfrom torch import linalg as LA\nfrom torch.nn import functional as F\n\n\nclass LitCancerSubtype(pl.LightningModule):\n\n    def __init__(self, net):\n        super().__init__()\n        self.net = net\n        self.num_classes = CFG[\"category\"]\n\n    def forward(self, x):\n        return self.net(x)\n\n# ==============================\n# ==============================\n\nPATH_CKPT = CFG[\"path\"]\nModel_PT = os.path.join(CFG['model_dir'],'image_classification_model.pt')\n\nckpt = torch.load(PATH_CKPT)#, map_location=torch.device('cpu'))\n\nmodel = LitCancerSubtype(net=network)\n# model = LitCancerSubtype.load_from_checkpoint(Model_PT,net=network)#,lr=CFG[\"learning_rate\"])\nmodel.load_state_dict(ckpt['state_dict'])\n# print(model)","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-11-21T01:37:49.396149Z","iopub.execute_input":"2023-11-21T01:37:49.396391Z","iopub.status.idle":"2023-11-21T01:38:05.666045Z","shell.execute_reply.started":"2023-11-21T01:37:49.39637Z","shell.execute_reply":"2023-11-21T01:38:05.665091Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Test data & submission\n\nlest load sample submission and add append images we can predict","metadata":{}},{"cell_type":"code","source":"df_test = pd.read_csv(os.path.join(DATASET_FOLDER, \"test.csv\"))\n# default label\ndf_test['label'] = ['HGSC'] * len(df_test)\n# labels = list(df_train[\"label\"].unique())\nprint(f\"Dataset/test size: {len(df_test)}\")\ndisplay(df_test.head())","metadata":{"execution":{"iopub.status.busy":"2023-11-21T01:38:05.667755Z","iopub.execute_input":"2023-11-21T01:38:05.668619Z","iopub.status.idle":"2023-11-21T01:38:05.686652Z","shell.execute_reply.started":"2023-11-21T01:38:05.668583Z","shell.execute_reply":"2023-11-21T01:38:05.685704Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!cat /kaggle/input/UBC-OCEAN/sample_submission.csv","metadata":{"execution":{"iopub.status.busy":"2023-11-21T01:38:05.687722Z","iopub.execute_input":"2023-11-21T01:38:05.688021Z","iopub.status.idle":"2023-11-21T01:38:06.672487Z","shell.execute_reply.started":"2023-11-21T01:38:05.687997Z","shell.execute_reply":"2023-11-21T01:38:06.671547Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Inference","metadata":{}},{"cell_type":"code","source":"import shutil\nfrom collections import Counter\nfrom torch.utils.data import DataLoader\n\nmodel.eval()\nmodel = model.cuda()\n\nsubmission = []\nfor _, row in df_test.iterrows():\n    row = dict(row)\n    # prepare data - cut and load tiles\n    folder_tiles = extract_prune_tiles(\n        os.path.join(DATASET_FOLDER, \"test_images\", f\"{str(row['image_id'])}.png\"),\n        IMAGES_FOLDER)#, size=2048, scale=0.25)\n    dataset = TilesFolderDataset(folder_tiles, transforms=VALID_TRANSFORM)\n    if not len(dataset):\n        print (f\"seem no tiles were cut for `{folder_tiles}`\")\n        submission.append(row)\n        continue\n    dataloader = DataLoader(dataset, batch_size=4, num_workers=4, shuffle=False)\n    # iterate over images and collect predictions\n    preds = []\n    scores = []\n    for imgs in dataloader:\n        with torch.no_grad():\n            output = model(imgs.cuda())\n            v_mag = LA.norm(output,ord='nuc',dim=(2,3))#(B,Num_classes)\n            score = F.softmax(v_mag,dim=1)\n            y_hat = torch.argmax(v_mag, dim=1)\n        preds += y_hat.cpu().numpy().tolist()\n        scores += v_mag.cpu().numpy().tolist()\n    # decide label\n    element_counts = Counter(preds)\n    most_common_elements = element_counts.most_common()\n    max_count = most_common_elements[0][1]\n    most_common_elements = [elem for elem, count in most_common_elements if count == max_count]\n\n    if len(most_common_elements) == 1:\n        lb = most_common_elements[0]\n    else:\n        p = np.argmax(np.sum(scores, axis=0))\n        if p in most_common_elements:\n            lb = p\n        else:\n            lb = np.min(most_common_elements)\n#     lb = np.argmax(np.sum(scores, axis=0))# Plan B\n\n    row['label'] = labels[lb]\n\n    submission.append(row)\n    # cleaning\n    #shutil.rmtree(folder_tiles)\n    os.system(f\"rm -rf {folder_tiles}\")\n\ndf_sub = pd.DataFrame(submission)","metadata":{"execution":{"iopub.status.busy":"2023-11-21T01:38:06.674152Z","iopub.execute_input":"2023-11-21T01:38:06.674487Z","iopub.status.idle":"2023-11-21T01:38:28.030192Z","shell.execute_reply.started":"2023-11-21T01:38:06.674459Z","shell.execute_reply":"2023-11-21T01:38:28.029031Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Finalize - export submission","metadata":{}},{"cell_type":"code","source":"display(df_sub.head())\ndf_sub[[\"image_id\", \"label\"]].to_csv(\"submission.csv\", index=False)\n\n! head submission.csv","metadata":{"execution":{"iopub.status.busy":"2023-11-21T01:38:28.031664Z","iopub.execute_input":"2023-11-21T01:38:28.032002Z","iopub.status.idle":"2023-11-21T01:38:29.071071Z","shell.execute_reply.started":"2023-11-21T01:38:28.031973Z","shell.execute_reply":"2023-11-21T01:38:29.069934Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}