{"cells":[{"metadata":{},"cell_type":"markdown","source":"# <center> <span style=\"color:green\"> PytorchCV EfficientNetB3 Inference Baseline\n  \n* Model: EfficientNetb3b.\n* Submission is an ensemble of 3 folds.\n* Models are trained using https://pypi.org/project/pytorchcv/.\n* Very compact tissue input\n\n\nIf you have questions on how to train, please comment!\n\n\n\n### Thank you!\n"},{"metadata":{},"cell_type":"markdown","source":"## Import Packages"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport torch.nn.functional as F\nimport matplotlib.pyplot as plt\nimport os\n\n# Any results you write to the current directory are saved as output.\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset,DataLoader\nfrom torchvision import transforms,models\nfrom tqdm import tqdm_notebook as tqdm\nimport math\nimport torch.utils.model_zoo as model_zoo\nimport cv2\n\nimport openslide\n# Option 2: Load images using skimage (requires that tifffile is installed)\nimport skimage.io\nimport random\nfrom sklearn.metrics import cohen_kappa_score\nfrom sklearn.model_selection import KFold\n\nimport albumentations\n\n# import PIL\nfrom PIL import Image\n\n!pip install /kaggle/input/pytorchcv/pytorchcv-0.0.55-py2.py3-none-any.whl --quiet\nfrom pytorchcv.model_provider import get_model\nimport warnings\nwarnings.filterwarnings(\"ignore\")\nimport gc\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Configs"},{"metadata":{"trusted":true},"cell_type":"code","source":"def seed_torch(seed=42):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n\nclass config:\n    device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n\n    IMG_WIDTH = 400\n    IMG_HEIGHT = 400\n    TRAIN_BATCH_SIZE = 16\n    TEST_BATCH_SIZE = 16\n    CLASSES = 6\n    \nseed_torch(seed=42)\nN_SPLITS = 3","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Data I/O"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"# Location of the training images\nBASE_PATH = '../input/prostate-cancer-grade-assessment'\n\n# image and mask directories\ndata_dir = f'{BASE_PATH}/train_images'\nmask_dir = f'{BASE_PATH}/train_label_masks'\ntest_dir = f'{BASE_PATH}/test_images'\nmodel_path = '/kaggle/input/zenify-pandas-models/'\n\n\n# Location of training labels\ntrain = pd.read_csv(f'{BASE_PATH}/train.csv')\ntest = pd.read_csv(f'{BASE_PATH}/test.csv')\nsubmission = pd.read_csv(f'{BASE_PATH}/sample_submission.csv')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Image Packing Utilities"},{"metadata":{"trusted":true},"cell_type":"code","source":"def enhance_image(image):\n    img_enhanced = cv2.addWeighted(image, 1, image, 0, 15)\n    return img_enhanced\n    \ndef estimate_forground_packing_factor(image):\n    \"\"\" Estimate % of forground\"\"\"\n    image = enhance_image(image)\n    white = len(image[np.where(image>=250)])\n    non_white = len(image[np.where(image<250)])\n    return 100 * (non_white/(white+non_white))\n\ndef split_image(image, img_enhanced, n_tile=4):\n    \"\"\"\n    Splits the given image into multiple images \n    \"\"\"\n    \n    w = int(image.shape[0]/n_tile)\n    h = int(image.shape[1]/n_tile)\n    \n    tiles = []\n    factors = []\n    for i in range(n_tile):\n        for j in range(n_tile):\n            tile = image[int(i*w):int((i+1)*w),int(j*h):int((j+1)*h)]\n            tile_enhanced = img_enhanced[int(i*w):int((i+1)*w),int(j*h):int((j+1)*h)]\n            tiles.append(tile)\n            factors.append(estimate_forground_packing_factor(tile_enhanced))\n\n    return tiles, factors\n\ndef generate_tiles(img):\n    img_up = remove_border(img)\n    \n    img_enhanced = enhance_image(img_up)\n    tiles, factors = split_image(img_up, img_enhanced, 12)\n    \n    ind = np.argsort(factors)[::-1]\n\n    im1 = np.concatenate((tiles[ind[0]],tiles[ind[1]],tiles[ind[2]],tiles[ind[3]],tiles[ind[4]]),axis=0)\n    im2 = np.concatenate((tiles[ind[5]],tiles[ind[6]],tiles[ind[7]],tiles[ind[8]],tiles[ind[9]]),axis=0)\n    im3 = np.concatenate((tiles[ind[10]],tiles[ind[11]],tiles[ind[12]],tiles[ind[13]],tiles[ind[14]]),axis=0)\n    im4 = np.concatenate((tiles[ind[15]],tiles[ind[16]],tiles[ind[17]],tiles[ind[18]],tiles[ind[19]]),axis=0)\n    im5 = np.concatenate((tiles[ind[20]],tiles[ind[21]],tiles[ind[22]],tiles[ind[23]],tiles[ind[24]]),axis=0)\n\n    im_final= np.concatenate((im1,im2, im3, im4, im5),axis=1) \n    \n    return im_final\n\ndef remove_border(image, mask=None):\n    try:\n        borders = np.where(image.sum(2) != 3*255)\n        x_min = np.min(borders[0])\n        x_max = np.max(borders[0]) + 1\n        y_min = np.min(borders[1])\n        y_max = np.max(borders[1]) + 1\n        image = image[x_min:x_max, y_min:y_max]\n        if mask is not None:\n            mask = mask[x_min:x_max, y_min:y_max]\n            return image, mask\n        return image\n    except:\n        return image","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"class PandaDataset(Dataset):\n    def __init__(self, images, folder, img_height, img_width, mode=\"train\", rotate=0):\n        self.images = images\n        self.folder = folder\n        self.mode = mode\n        self.img_height = img_height\n        self.img_width = img_width\n        self.rotate = rotate\n        \n        # we are in validation part\n        self.aug = albumentations.Compose([\n            albumentations.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225], always_apply=True)\n        ])\n\n    def __len__(self):\n        return len(self.images)\n\n\n    def __getitem__(self, idx):\n\n        img_name = self.images[idx]\n        img_path = os.path.join(self.folder, f'{img_name}.tiff')\n\n        img = skimage.io.MultiImage(img_path)\n        \n        # TTA as needed\n        if self.rotate==0:\n            img = cv2.resize(img[-1], (1028, 1028))\n \n        \n        img = generate_tiles(img)\n        img = cv2.resize(img, (self.img_height, self.img_width))\n        \n        img = Image.fromarray(img).convert(\"RGB\")\n        img = self.aug(image=np.array(img))[\"image\"]\n        img = np.transpose(img, (2, 0, 1)).astype(np.float32)\n        \n        try: \n            os.remove(save_path)\n        except: pass\n\n        return torch.tensor(img, dtype=torch.float)\n    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"class Head(torch.nn.Module):\n    def __init__(self, in_f, out_f):\n        super(Head, self).__init__()\n\n        self.f = nn.Flatten()\n        self.b1 = nn.BatchNorm1d(in_f)\n        self.d = nn.Dropout(0.75)\n        \n        self.l = nn.Linear(in_f, 512)\n        self.r = nn.ReLU()\n        self.b2 = nn.BatchNorm1d(512)\n        \n        self.o = nn.Linear(512, out_f)\n        \n\n    def forward(self, x):\n        x = self.f(x)\n        x = self.b1(x)\n        x = self.d(x)\n\n        x = self.l(x)\n        x = self.r(x)\n        x = self.b2(x)\n        x = self.d(x)\n\n        out = self.o(x)\n        return out\n    \nclass FCN(torch.nn.Module):\n    def __init__(self, base, in_f):\n        super(FCN, self).__init__()\n        self.base = base\n        self.h1 = Head(in_f, config.CLASSES)\n\n    def forward(self, x):\n        x = self.base(x)\n        return self.h1(x)\n    \n    def freeze_until(self, param_name):\n        \"\"\"\n        Freeze layers of a model\n        \"\"\"\n        found_name = False\n        for name, params in self.named_parameters():\n            if name == param_name:\n                found_name = True\n            params.requires_grad = found_name","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Load Models"},{"metadata":{"trusted":true},"cell_type":"code","source":"# FOLD 1\nmodel = get_model(\"efficientnet_b3b\", pretrained=False)\nmodel = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\ncheckpoint = torch.load(model_path + \"model_efficientnet_b3b_0_2.pth\", map_location=config.device)    \nmodel1 = FCN(model, 1536).to(config.device)  \nmodel1.load_state_dict(checkpoint)\n_ = model1.eval()\ndel checkpoint, model \n\n# FOLD 2\nmodel = get_model(\"efficientnet_b3b\", pretrained=False)\nmodel = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\ncheckpoint = torch.load(model_path + \"model_efficientnet_b3b_1_2.pth\", map_location=config.device)    \nmodel2 = FCN(model, 1536).to(config.device)  \nmodel2.load_state_dict(checkpoint)\n_ = model2.eval()\ndel checkpoint, model \n\n# FOLD 3\nmodel = get_model(\"efficientnet_b3b\", pretrained=False)\nmodel = nn.Sequential(*list(model.children())[:-1]) # Remove original output layer\ncheckpoint = torch.load(model_path + \"model_efficientnet_b3b_2_2.pth\", map_location=config.device)    \nmodel3 = FCN(model, 1536).to(config.device)  \nmodel3.load_state_dict(checkpoint)\n_ = model3.eval()\ndel checkpoint, model \n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Submission"},{"metadata":{"trusted":true},"cell_type":"code","source":"predictions = []\n\nmodels = []\nmodels.append(model1)\nmodels.append(model2)\nmodels.append(model3)\n\nDEBUG = False\ndef check_for_images_dir():\n    if DEBUG:\n        return os.path.exists('../input/prostate-cancer-grade-assessment/train_images')\n    else:\n        return os.path.exists('../input/prostate-cancer-grade-assessment/test_images')\n\nif DEBUG:\n    test = train\n    test_dir = data_dir\n\nif check_for_images_dir():\n    for model in models:\n        model.eval()\n        \n        preds_all = []\n        for rotate_id in range(1): # Maybe turn all rotation for final sub to beat overfit. Public LB was not improved so not doing TTA for now\n            test_dataset = PandaDataset(\n                test.image_id.values,\n                test_dir,\n                img_height=config.IMG_HEIGHT,\n                img_width=config.IMG_WIDTH,\n                mode=\"test\",\n                rotate=rotate_id\n            )\n\n            test_data_loader = torch.utils.data.DataLoader(\n                test_dataset,\n                batch_size=config.TEST_BATCH_SIZE,\n                shuffle=False,\n            )\n            preds = []\n            for idx, d in tqdm(enumerate(test_data_loader), total=len(test_data_loader)):\n                inputs = d\n                inputs = inputs.to(config.device)\n\n                with torch.no_grad():\n                    outputs = model(inputs)\n                preds.append(outputs.to('cpu').numpy())\n                #print(np.shape(preds))\n            preds_all.append(np.concatenate(preds))\n\n        predictions.append(np.mean(preds_all,axis=0))\n                    \n    predictions = np.mean(predictions, axis=0)\n    predictions = predictions.argmax(1)\n\n\nif len(predictions) > 0:\n    submission.isup_grade = predictions\nsubmission.isup_grade = submission['isup_grade'].astype(int)\nsubmission.to_csv('submission.csv',index=False)\nprint(submission.head())   ","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}