{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# todo change train to test\nTEST_IMAGES_PATH = '../input/prostate-cancer-grade-assessment/test_images'\nTEST_CSV_PATH = '../input/prostate-cancer-grade-assessment/test.csv'\nTRAIN_IMAGES_PATH = '../input/prostate-cancer-grade-assessment/train_images'\nTRAIN_CSV_PATH = '../input/prostate-cancer-grade-assessment/train.csv'\nSAMPLE_CSV_PATH = '../input/prostate-cancer-grade-assessment/sample_submission.csv'\nMODEL_WEIGHTS_PATH = '../input/pandamodelweights/cv-{fold}-model-29.pth.tar'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import pandas as pd\nimport os\nif os.path.exists(TEST_IMAGES_PATH):\n    TEST_DF = pd.read_csv(TEST_CSV_PATH)\nelse:\n    TEST_DF = pd.read_csv(TRAIN_CSV_PATH)[:32]\nTEST_DF['opt_angle'] = -1","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"from torch import nn\nimport torch\nfrom collections import OrderedDict\n\ndef load_model_state(model, checkpoint_path):\n    device = torch.device('cpu')\n    state_dict = torch.load(checkpoint_path, map_location=device)\n    model.load_state_dict(non_parallel_state_dict(state_dict['model_state_dict']))\n\ndef non_parallel_state_dict(state_dict):\n    new_state_dict = OrderedDict()\n    for k, v in state_dict.items():\n        k = k[k.startswith('module.') and len('module.'):]\n        new_state_dict[k] = v\n    return new_state_dict\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# layers \nimport torch.nn as nn\nfrom torch.nn import functional as F\n\n\nclass L_Conv2d(nn.Conv2d):\n\n    def __init__(self, in_channels, out_channels, kernel_size, stride=1,\n                 padding=0, dilation=1, groups=1, bias=True):\n        super(L_Conv2d, self).__init__(in_channels, out_channels, kernel_size, stride,\n                                     padding, dilation, groups, bias)\n\n    def forward(self, x):\n        # return super(Conv2d, self).forward(x)\n        weight = self.weight\n        weight_mean = weight.mean(dim=1, keepdim=True).mean(dim=2,\n                                                            keepdim=True).mean(dim=3, keepdim=True)\n        weight = weight - weight_mean\n        std = weight.view(weight.size(0), -1).std(dim=1).view(-1, 1, 1, 1) + 1e-5\n        weight = weight / std.expand_as(weight)\n        return F.conv2d(x, weight, self.bias, self.stride,\n                        self.padding, self.dilation, self.groups)\n\n\ndef L_BatchNorm2d(num_features):\n    return nn.GroupNorm(num_channels=num_features, num_groups=32)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# resnext\nfrom __future__ import division\n\n\"\"\"\nCreates a ResNeXt Model as defined in:\nXie, S., Girshick, R., Dollar, P., Tu, Z., & He, K. (2016).\nAggregated residual transformations for deep neural networks.\narXiv preprint arXiv:1611.05431.\nimport from https://github.com/facebookresearch/ResNeXt/blob/master/models/resnext.lua\n\"\"\"\nimport math\nimport torch.nn as nn\n\n\nclass Bottleneck(nn.Module):\n    \"\"\"\n    RexNeXt bottleneck type C\n    \"\"\"\n    expansion = 4\n\n    def __init__(self, inplanes, planes, baseWidth, cardinality, stride=1, downsample=None):\n        \"\"\" Constructor\n        Args:\n            inplanes: input channel dimensionality\n            planes: output channel dimensionality\n            baseWidth: base width.\n            cardinality: num of convolution groups.\n            stride: conv stride. Replaces pooling layer.\n        \"\"\"\n        super(Bottleneck, self).__init__()\n\n        D = int(math.floor(planes * (baseWidth / 64)))\n        C = cardinality\n\n        self.conv1 = L_Conv2d(inplanes, D * C, kernel_size=1, stride=1, padding=0, bias=False)\n        self.bn1 = L_BatchNorm2d(D * C)\n        self.conv2 = L_Conv2d(D * C, D * C, kernel_size=3, stride=stride, padding=1, groups=C, bias=False)\n        self.bn2 = L_BatchNorm2d(D * C)\n        self.conv3 = L_Conv2d(D * C, planes * 4, kernel_size=1, stride=1, padding=0, bias=False)\n        self.bn3 = L_BatchNorm2d(planes * 4)\n        self.relu = nn.ReLU(inplace=True)\n\n        self.downsample = downsample\n\n    def forward(self, x):\n        residual = x\n\n        out = self.conv1(x)\n        out = self.bn1(out)\n        out = self.relu(out)\n\n        out = self.conv2(out)\n        out = self.bn2(out)\n        out = self.relu(out)\n\n        out = self.conv3(out)\n        out = self.bn3(out)\n\n        if self.downsample is not None:\n            residual = self.downsample(x)\n\n        out += residual\n        out = self.relu(out)\n\n        return out\n\n\nclass ResNeXt(nn.Module):\n    \"\"\"\n    ResNext optimized for the ImageNet dataset, as specified in\n    https://arxiv.org/pdf/1611.05431.pdf\n    \"\"\"\n\n    def __init__(self, baseWidth, cardinality, layers, num_classes):\n        \"\"\" Constructor\n        Args:\n            baseWidth: baseWidth for ResNeXt.\n            cardinality: number of convolution groups.\n            layers: config of layers, e.g., [3, 4, 6, 3]\n            num_classes: number of classes\n        \"\"\"\n        super(ResNeXt, self).__init__()\n        block = Bottleneck\n\n        self.cardinality = cardinality\n        self.baseWidth = baseWidth\n        self.num_classes = num_classes\n        self.inplanes = 64\n        self.output_size = 64\n\n        self.conv1 = L_Conv2d(3, 64, 7, 2, 3, bias=False)\n        self.bn1 = L_BatchNorm2d(64)\n        self.relu = nn.ReLU(inplace=True)\n        self.maxpool1 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)\n        self.layer1 = self._make_layer(block, 64, layers[0])\n        self.layer2 = self._make_layer(block, 128, layers[1], 2)\n        self.layer3 = self._make_layer(block, 256, layers[2], 2)\n        self.layer4 = self._make_layer(block, 512, layers[3], 2)\n        self.avgpool = nn.AdaptiveAvgPool2d(1)\n        self.fc = nn.Linear(512 * block.expansion, num_classes)\n        self.multiple_devices = False\n\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels\n                m.weight.data.normal_(0, math.sqrt(2. / n))\n            elif isinstance(m, nn.BatchNorm2d):\n                m.weight.data.fill_(1)\n                m.bias.data.zero_()\n\n    def place_on_multiple_devices(self, device_0, device_1):\n        self.conv1.to(device_0)\n        self.bn1.to(device_0)\n        self.relu.to(device_0)\n        self.maxpool1.to(device_0)\n        self.layer1.to(device_0)\n        self.layer2.to(device_1)\n        self.layer3.to(device_1)\n        self.layer4.to(device_1)\n        self.avgpool.to(device_1)\n        self.fc.to(device_1)\n        self.multiple_devices = True\n\n    def load_pretrained_weights(self, state_dict):\n        state_dict.pop('fc.weight')\n        state_dict.pop('fc.bias')\n        res = self.load_state_dict(state_dict, strict=False)\n        assert set(res.missing_keys) == set(['fc.weight', 'fc.bias']), 'issue loading pretrained weights'\n        print('Loaded pretrained weights for ResNeXt')\n\n    def _make_layer(self, block, planes, blocks, stride=1):\n        \"\"\" Stack n bottleneck modules where n is inferred from the depth of the network.\n        Args:\n            block: block type used to construct ResNext\n            planes: number of output channels (need to multiply by block.expansion)\n            blocks: number of blocks to be built\n            stride: factor to reduce the spatial dimensionality in the first bottleneck of the block.\n        Returns: a Module consisting of n sequential bottlenecks.\n        \"\"\"\n        downsample = None\n        if stride != 1 or self.inplanes != planes * block.expansion:\n            downsample = nn.Sequential(\n                L_Conv2d(self.inplanes, planes * block.expansion,\n                         kernel_size=1, stride=stride, bias=False),\n                L_BatchNorm2d(planes * block.expansion),\n            )\n\n        layers = []\n        layers.append(block(self.inplanes, planes, self.baseWidth, self.cardinality, stride, downsample))\n        self.inplanes = planes * block.expansion\n        for i in range(1, blocks):\n            layers.append(block(self.inplanes, planes, self.baseWidth, self.cardinality))\n\n        return nn.Sequential(*layers)\n\n    def forward(self, x):\n        x = self.conv1(x)\n        x = self.bn1(x)\n        x = self.relu(x)\n        x = self.maxpool1(x)\n        x = self.layer1(x)\n        if self.multiple_devices:\n            x = x.to(next(self.layer2.parameters()).device)\n        x = self.layer2(x)\n        x = self.layer3(x)\n        x = self.layer4(x)\n        x = self.avgpool(x)\n        x = x.view(x.size(0), -1)\n        x = self.fc(x)\n        if self.multiple_devices:\n            x = x.to(next(self.conv1.parameters()).device)\n        return x\n\n\ndef l_resnext50(baseWidth=4, cardinality=32):\n    \"\"\"\n    Construct ResNeXt-50.\n    \"\"\"\n    model = ResNeXt(baseWidth, cardinality, [3, 4, 6, 3], 5)\n    return model\n\n\ndef l_resnext101(baseWidth=4, cardinality=32):\n    \"\"\"\n    Construct ResNeXt-101.\n    \"\"\"\n    model = ResNeXt(baseWidth, cardinality, [3, 4, 23, 3], 5)\n    return model\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = l_resnext50()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def quadratic_weighted_kappa(y_pred, y_true):\n    max_rat = 5  # number of possible classes\n    y_true = np.asarray(y_true, dtype=int)\n    y_pred = np.asarray(y_pred, dtype=int)\n\n    hist1 = np.zeros((max_rat + 1,))\n    hist2 = np.zeros((max_rat + 1,))\n    o = 0\n    for k in range(len(y_true)):\n        i, j = y_true[k], y_pred[k]\n        hist1[i] += 1\n        hist2[j] += 1\n        o += (i - j) * (i - j)\n    e = 0\n    for i in range(max_rat + 1):\n        for j in range(max_rat + 1):\n            e += hist1[i] * hist2[j] * (i - j) * (i - j)\n    e = e / y_true.shape[0]\n    return 1 - o / e","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from functools import partial\nimport numpy as np\nimport pandas as pd\nimport scipy as sp\n\n\nclass OptimizedRounder:\n    def __init__(self, num_classes=5, loss_to_optimize=quadratic_weighted_kappa):\n        self.num_classes = num_classes\n        self.coefficients = None\n        self.loss_to_optimize = loss_to_optimize\n\n    def _rounded_loss(self, coefficients, X, y):\n        # arguments are named X and y, because this is required by the optimization.\n        labels = list(range(self.num_classes + 1))\n        X_rounded = pd.cut(X, [-np.inf] + list(np.sort(coefficients)) + [np.inf], labels=labels)\n        return -self.loss_to_optimize(X_rounded, y)\n\n    def fit(self, y_pred, y_true):\n        \"\"\"\n        Optimize rounding thresholds\n\n        :param y_pred: The raw predictions\n        :param y_true: The ground truth labels\n        \"\"\"\n        if isinstance(y_true, np.ndarray):\n            y_true = y_true.tolist()\n        if isinstance(y_pred, np.ndarray):\n            y_pred = y_pred.tolist()\n        loss_partial = partial(self._rounded_loss, X=y_pred, y=y_true)\n        initial_coefficients = np.arange(self.num_classes) + 0.5\n        self.coefficients = sp.optimize.minimize(loss_partial, initial_coefficients, method='nelder-mead')['x']\n\n    def predict(self, y_pred, coefficients=None):\n        \"\"\"\n        Make predictions with specified thresholds\n\n        :param y_pred: The raw predictions\n        :param coefficients: A list of coefficients that will be used for rounding\n        \"\"\"\n        labels = list(range(self.num_classes + 1))\n        coefficients = self.coefficients if coefficients is None else coefficients\n        return pd.cut(y_pred, [-np.inf] + list(np.sort(coefficients)) + [np.inf], labels=labels).tolist()\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport skimage.io\nimport tifffile\nimport gc\n\ndef rotate_bound(image, angle):\n    # grab the dimensions of the image and then determine the\n    # center\n    (h, w) = image.shape[:2]\n    (cX, cY) = (w / 2, h / 2)\n\n    # grab the rotation matrix (applying the negative of the\n    # angle to rotate clockwise), then grab the sine and cosine\n    # (i.e., the rotation components of the matrix)\n    M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)\n    cos = np.abs(M[0, 0])\n    sin = np.abs(M[0, 1])\n\n    # compute the new bounding dimensions of the image\n    nW = int((h * sin) + (w * cos))\n    nH = int((h * cos) + (w * sin))\n\n    # adjust the rotation matrix to take into account translation\n    M[0, 2] += (nW / 2) - cX\n    M[1, 2] += (nH / 2) - cY\n\n    # perform the actual rotation and return the image\n    try:\n        return cv2.warpAffine(image, M, (nW, nH), borderValue=(255, 255, 255))\n    except Exception:\n        return image\n\n\ndef find_best_rounding(image):\n    volume = image.shape[0] * image.shape[1]\n    opt_angle = 0\n    for angle in range(-45, 45, 10):\n        rot_image = rotate_bound(image, angle + 2)\n        rot_image = cut_image(rot_image)\n        rot_volume = rot_image.shape[0] * rot_image.shape[1]\n        if rot_volume < volume:\n            volume = rot_volume\n            opt_angle = angle\n        del rot_image\n        gc.collect()\n    return opt_angle\n\n\ndef get_corners(image):\n    image_mask = ((255 - image).sum(axis=-1) != 0).astype(np.uint8)\n    rows = np.any(image_mask, axis=1)\n    cols = np.any(image_mask, axis=0)\n    x_min, x_max = np.where(rows)[0][[0, -1]]\n    y_min, y_max = np.where(cols)[0][[0, -1]]\n    return x_min, x_max, y_min, y_max\n\n\ndef cut_image(image):\n    x_min, x_max, y_min, y_max = get_corners(image)\n    image = image[x_min:x_max, y_min:y_max]\n    h, w, c = image.shape\n    if w > h:\n        image = np.transpose(image, [1, 0, 2])\n    return image\n\n\ndef get_image_mask(image):\n    image_mask = ((255 - image).sum(axis=-1) != 0).astype(np.uint8)\n    return image_mask","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np\nimport skimage.io\nimport os\nimport albumentations as albu\nimport gc\n\n\ndef extract_tiles_from_image(img, tile_size=256, n_tiles=36, mode=0):\n    h, w, c = img.shape\n    pad_h = (tile_size - h % tile_size) % tile_size + ((tile_size * mode) // 2)\n    pad_w = (tile_size - w % tile_size) % tile_size + ((tile_size * mode) // 2)\n\n    img2 = np.pad(img, [[pad_h // 2, pad_h - pad_h // 2], [pad_w // 2, pad_w - pad_w // 2], [0, 0]],\n                  constant_values=255)\n    img3 = img2.reshape(\n        img2.shape[0] // tile_size,\n        tile_size,\n        img2.shape[1] // tile_size,\n        tile_size,\n        3\n    )\n\n    img3 = img3.transpose(0, 2, 1, 3, 4).reshape(-1, tile_size, tile_size, 3)\n    n_relevant_tiles = (img3.reshape(img3.shape[0], -1).sum(1) < tile_size ** 2 * 3 * 255).sum()\n    idxs = np.argsort(img3.reshape(img3.shape[0], -1).sum(-1))[:int(np.minimum(n_relevant_tiles, n_tiles))]\n    img3 = img3[idxs]\n    del img2\n    del img\n    gc.collect()\n    return img3\n\ndef get_tiles(image_folder, image_id, tile_size=256, n_tiles=49):\n    tiff_file = os.path.join(image_folder, f'{image_id}.tiff')\n    image_base = skimage.io.MultiImage(tiff_file)\n    image = image_base[1]\n    if TEST_DF[TEST_DF.image_id == image_id].opt_angle.iloc[0] == -1:\n        low_res_image = image_base[-1]\n        low_res_image = cv2.resize(low_res_image, (low_res_image.shape[1] // 8,\n                                           low_res_image.shape[0] // 8))\n        opt_angle = find_best_rounding(low_res_image)\n    else:\n        opt_angle = TEST_DF[TEST_DF.image_id == image_id].opt_angle.iloc[0]\n    image = cut_image(image)\n    image = rotate_bound(image, opt_angle)\n    image = cut_image(image)\n    tiles = extract_tiles_from_image(image, tile_size, n_tiles)\n    del image\n    gc.collect()\n    return tiles\n\ndef assemble_image(image_tiles, image_processing_func, tile_processing_func, num_tiles, tile_size):\n    num_row_tiles = int(np.sqrt(num_tiles))\n    image_tiles = [tile_processing_func(image=tile)['image'] for tile in image_tiles]\n    image_tiles = np.array(image_tiles)\n    if len(image_tiles) == 0:\n        image_tiles = (255 * np.ones((1, tile_size, tile_size, 3))).astype(np.int8)\n    if len(image_tiles) < num_tiles:\n        image_tiles = np.pad(image_tiles,\n                             [[0, num_tiles - len(image_tiles)], [0, 0], [0, 0], [0, 0]], constant_values=255)\n    joined_image = np.zeros((tile_size * num_row_tiles, tile_size * num_row_tiles, 3))\n    for h in range(num_row_tiles):\n        for w in range(num_row_tiles):\n            i = h * num_row_tiles + w\n            h1 = h * tile_size\n            w1 = w * tile_size\n            tile_in_place = image_tiles[i]\n            joined_image[h1:h1 + tile_size, w1:w1 + tile_size] = tile_in_place\n    del image_tiles\n    gc.collect()\n    return image_processing_func(image=joined_image.astype(np.uint8))['image']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import albumentations as albu\n\ndef identity_processing():\n    def identity(image):\n        return {'image': image}\n\n    return identity\n\n\ndef simple_image_processing():\n    transformations = albu.Compose([\n        albu.Transpose(p=0.5),\n        albu.VerticalFlip(p=0.5),\n        albu.HorizontalFlip(p=0.5),\n    ])\n    return albu.Compose(transformations)\n\n\ndef spacial_image_processing():\n    transformations = [albu.Flip(),\n                       albu.Transpose(),\n                       albu.ShiftScaleRotate(rotate_limit=360, shift_limit=0, scale_limit=0.125),\n                       albu.GridDistortion()]\n    return albu.Compose(transformations)\n\n\ndef distributional_image_processing():\n    transformations = [\n        albu.OneOf([albu.RandomSnow(),\n                    albu.RandomBrightnessContrast(),\n                    albu.CLAHE()]),\n        albu.OneOf([albu.CoarseDropout(max_holes=36 * 16, fill_value=0, max_height=32, max_width=32),\n                    albu.CoarseDropout(max_holes=36 * 8, fill_value=0, max_height=32, max_width=32),\n                    albu.GridDropout()])\n    ]\n    return albu.Compose(transformations)\n\n\ndef scaling_image_processing():\n    return albu.Normalize(mean=[0.90949707, 0.8188697, 0.87795304],\n                          std=[0.36357649, 0.49984502, 0.40477625])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from torch.utils.data import Dataset\nimport cv2\n\nclass PandaDataset(Dataset):\n    def __init__(self, images_df, num_tta,\n                 tile_augmentation=None,\n                 image_augmentation=None,\n                 num_tiles=49,\n                 tile_size=256):\n        super().__init__()\n        self.images_df = images_df\n        self.tile_augmentation = identity_processing() if tile_augmentation is None else tile_augmentation\n        self.image_augmentation = identity_processing() if image_augmentation is None else image_augmentation\n        self.image_scaling = scaling_image_processing()\n        self.num_tiles = num_tiles\n        self.tile_size = tile_size\n        self.num_tta = num_tta\n\n    def __getitem__(self, item):\n        df_item = self.images_df.iloc[item]\n        image = self._get_image(df_item.image_id)\n        image = np.transpose(image, [0, 3, 1, 2])  # pytorch uses channels first format.\n        return torch.tensor(image)\n\n    def __len__(self):\n        return len(self.images_df)\n\n    def _get_image(self, image_id):\n        if os.path.exists(TEST_IMAGES_PATH):\n            images_folder = TEST_IMAGES_PATH\n        else:\n            images_folder = TRAIN_IMAGES_PATH\n        image_tile_list = get_tiles(images_folder, image_id)\n        images = []\n        for j in range(self.num_tta):\n            image = assemble_image(image_tile_list, self.image_augmentation, self.tile_augmentation,\n                                   num_tiles=self.num_tiles, tile_size=self.tile_size)\n            image = self.image_scaling(image=image)['image']\n            images.append(image)\n        return np.array(images)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport skimage.io\nimport tifffile\nimport tqdm\nimport gc\n\ndef fill_opt_angle():\n    if os.path.exists(TEST_IMAGES_PATH):\n        images_folder = TEST_IMAGES_PATH\n    else:\n        images_folder = TRAIN_IMAGES_PATH\n    for image_id in tqdm.tqdm(TEST_DF.image_id):\n        tiff_file = os.path.join(images_folder, f'{image_id}.tiff')\n        image_base = skimage.io.MultiImage(tiff_file)\n        low_res_image = image_base[-1]\n        low_res_image2 = cv2.resize(low_res_image, (low_res_image.shape[1] // 8,\n                                           low_res_image.shape[0] // 8))\n        opt_angle = find_best_rounding(low_res_image2)\n        TEST_DF.loc[TEST_DF.image_id == image_id, ['opt_angle']] = opt_angle\n        del low_res_image\n        del low_res_image2\n        del image_base\n        gc.collect()\n#fill_opt_angle()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Prediction","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"from tqdm.notebook import tqdm\nimport matplotlib.pyplot as plt\n\ndef get_preds(loader, model, device):\n    model.eval()\n    y_pred = []\n    for image_tta_batch in tqdm(loader, total=len(loader)):\n        image_tta_batch = torch.transpose(image_tta_batch, 0, 1)\n        tta_preds = []\n        for image in image_tta_batch:\n            image = image.to(device)\n            with torch.no_grad():\n                predictions = model(image).sigmoid().detach().cpu().numpy()\n            del image\n            gc.collect()\n            isup_pred = predictions.sum(axis=1)\n            tta_preds.append(isup_pred)\n        tta_preds = np.array(tta_preds)\n        isup_pred = tta_preds.mean(axis=0)\n        y_pred.extend(isup_pred)\n    return np.array(y_pred)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom tqdm import tqdm\nimport torch\nfrom torch.utils.data import DataLoader, SequentialSampler\nimport gc\ngc.collect()\n\nsub_df = pd.read_csv(SAMPLE_CSV_PATH)\ndevice = torch.device('cuda')\nmodel.to(device)\nnum_folds = 5\nif os.path.exists(TEST_IMAGES_PATH):\n    num_tta = 1\n    test_dataset = PandaDataset(images_df=TEST_DF, num_tta=num_tta,\n                                tile_augmentation=simple_image_processing())\n    test_loader = DataLoader(dataset=test_dataset, batch_size=5,\n                              sampler=SequentialSampler(test_dataset), num_workers=2)\n    y_pred_per_fold = []\n    for fold in range(num_folds):\n        load_model_state(model, MODEL_WEIGHTS_PATH.format(fold=fold))\n        y_pred = get_preds(test_loader, model, device)\n        y_pred_per_fold.append(y_pred)\n    y_pred = np.mean(y_pred_per_fold, axis=0)\n    \n    rounder = OptimizedRounder()\n    y_pred = rounder.predict(y_pred, coefficients=[0.5, 1.5, 2.5, 3.5, 4.5])\n    sub_df = pd.DataFrame({'image_id': TEST_DF.image_id, \n                           'isup_grade': y_pred})\nelse:\n    num_tta = 1\n    y_pred_per_fold = []\n    for fold in range(num_folds):\n        test_dataset = PandaDataset(images_df=TEST_DF, num_tta=num_tta,\n                                    tile_augmentation=simple_image_processing())\n        test_loader = DataLoader(dataset=test_dataset, batch_size=5,\n                              sampler=SequentialSampler(test_dataset), num_workers=2)\n        load_model_state(model, MODEL_WEIGHTS_PATH.format(fold=fold))\n        y_pred = get_preds(test_loader, model, device)\n        y_pred_per_fold.append(y_pred)\n        del test_dataset\n        del test_loader\n        gc.collect()\n    y_pred = np.mean(y_pred_per_fold, axis=0)\n\n    rounder = OptimizedRounder()\n    y_pred = rounder.predict(y_pred, coefficients=[0.5, 1.5, 2.5, 3.5, 4.5])\n    sub_df = pd.DataFrame({'image_id': TEST_DF.image_id, \n                           'isup_grade': y_pred})\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub_df.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}