{"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":"In this notebook, we will try to tackle the PANDA competition dataset using fastai v2. A comprehensive EDA can be found in this [notebook](https://www.kaggle.com/tanulsingh077/prostate-cancer-in-depth-understanding-eda-model). The [solution](https://www.kaggle.com/iafoss/panda-concat-tile-pooling-starter-0-79-lb) uses concat tiling as proposed by @iafoss. The modified datasets we will use here also comes from @iafoss. We will also use tricks and models used by @DrHB in his [2nd place solution](https://github.com/DrHB/PANDA-2nd-place-solution/tree/main/train_drhb). ","metadata":{}},{"cell_type":"markdown","source":"References and resources","metadata":{}},{"cell_type":"markdown","source":"https://www.kaggle.com/iafoss/panda-concat-tile-pooling-starter-0-79-lb\n\nhttps://www.kaggle.com/tanulsingh077/prostate-cancer-in-depth-understanding-eda-model\n\nhttps://github.com/kentaroy47/Kaggle-PANDA-1st-place-solution/tree/master/src \n\nhttps://docs.google.com/presentation/d/1Ies4vnyVtW5U3XNDr_fom43ZJDIodu1SV6DSK8di6fs/edit#slide=id.g9b10629a30_0_299\n\nhttps://github.com/DrHB/PANDA-2nd-place-solution/tree/main/train_drhb","metadata":{}},{"cell_type":"markdown","source":"# Imports and initial exploration","metadata":{}},{"cell_type":"code","source":"!pip install timm -q","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-05-25T05:58:14.940838Z","iopub.execute_input":"2021-05-25T05:58:14.941207Z","iopub.status.idle":"2021-05-25T05:58:20.601193Z","shell.execute_reply.started":"2021-05-25T05:58:14.941174Z","shell.execute_reply":"2021-05-25T05:58:20.600043Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from fastai.vision.all import *\nimport torch\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.metrics import cohen_kappa_score,confusion_matrix\nfrom timm import create_model\nimport timm","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:20.604867Z","iopub.execute_input":"2021-05-25T05:58:20.605151Z","iopub.status.idle":"2021-05-25T05:58:20.610603Z","shell.execute_reply.started":"2021-05-25T05:58:20.605121Z","shell.execute_reply":"2021-05-25T05:58:20.609802Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"path = Path('../input/prostate-cancer-grade-assessment')\npath_img = Path('../input/panda-16x128x128-tiles-data/train')","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:20.612809Z","iopub.execute_input":"2021-05-25T05:58:20.61321Z","iopub.status.idle":"2021-05-25T05:58:20.624742Z","shell.execute_reply.started":"2021-05-25T05:58:20.613171Z","shell.execute_reply":"2021-05-25T05:58:20.623921Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sz = 128\nN = 12\nn_classes = 6\nBS=32","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:20.626721Z","iopub.execute_input":"2021-05-25T05:58:20.627103Z","iopub.status.idle":"2021-05-25T05:58:20.635248Z","shell.execute_reply.started":"2021-05-25T05:58:20.627067Z","shell.execute_reply":"2021-05-25T05:58:20.634351Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.read_csv(path/'train.csv')","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:20.636503Z","iopub.execute_input":"2021-05-25T05:58:20.63731Z","iopub.status.idle":"2021-05-25T05:58:20.664437Z","shell.execute_reply.started":"2021-05-25T05:58:20.637209Z","shell.execute_reply":"2021-05-25T05:58:20.663728Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df.head(5)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:20.667072Z","iopub.execute_input":"2021-05-25T05:58:20.667312Z","iopub.status.idle":"2021-05-25T05:58:20.680741Z","shell.execute_reply.started":"2021-05-25T05:58:20.667289Z","shell.execute_reply":"2021-05-25T05:58:20.679825Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(df)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:20.682158Z","iopub.execute_input":"2021-05-25T05:58:20.682518Z","iopub.status.idle":"2021-05-25T05:58:20.694148Z","shell.execute_reply.started":"2021-05-25T05:58:20.682454Z","shell.execute_reply":"2021-05-25T05:58:20.693348Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fns = []\nfor f in path_img.ls():\n    fns.append(str(f).split('/')[-1].split('_')[0])","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:20.69782Z","iopub.execute_input":"2021-05-25T05:58:20.698325Z","iopub.status.idle":"2021-05-25T05:58:22.107458Z","shell.execute_reply.started":"2021-05-25T05:58:20.698291Z","shell.execute_reply":"2021-05-25T05:58:22.106588Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"remove_fns = []\nfor f in list(df['image_id'].values):\n    if f not in fns:\n        remove_fns.append(f)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:22.109442Z","iopub.execute_input":"2021-05-25T05:58:22.109796Z","iopub.status.idle":"2021-05-25T05:58:24.406305Z","shell.execute_reply.started":"2021-05-25T05:58:22.109759Z","shell.execute_reply":"2021-05-25T05:58:24.405431Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(remove_fns)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:24.407588Z","iopub.execute_input":"2021-05-25T05:58:24.408104Z","iopub.status.idle":"2021-05-25T05:58:24.414392Z","shell.execute_reply.started":"2021-05-25T05:58:24.408063Z","shell.execute_reply":"2021-05-25T05:58:24.41333Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = df[~df['image_id'].isin(remove_fns)]","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:24.415921Z","iopub.execute_input":"2021-05-25T05:58:24.416358Z","iopub.status.idle":"2021-05-25T05:58:24.426215Z","shell.execute_reply.started":"2021-05-25T05:58:24.41632Z","shell.execute_reply":"2021-05-25T05:58:24.425346Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(df)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:24.427492Z","iopub.execute_input":"2021-05-25T05:58:24.428016Z","iopub.status.idle":"2021-05-25T05:58:24.436836Z","shell.execute_reply.started":"2021-05-25T05:58:24.42798Z","shell.execute_reply":"2021-05-25T05:58:24.435744Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"N_FOLDS = 3\ndf['fold'] = -1\n\nstrat_kfold = StratifiedKFold(n_splits=N_FOLDS, random_state=42, shuffle=True)\nfor i, (_, test_index) in enumerate(strat_kfold.split(df.image_id.values, df['isup_grade'].values)):\n    df.iloc[test_index, -1] = i\n    \ndf['fold'] = df['fold'].astype('int')","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:24.438283Z","iopub.execute_input":"2021-05-25T05:58:24.438633Z","iopub.status.idle":"2021-05-25T05:58:24.452715Z","shell.execute_reply.started":"2021-05-25T05:58:24.438597Z","shell.execute_reply":"2021-05-25T05:58:24.451933Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Preparing data and dataloader","metadata":{}},{"cell_type":"markdown","source":"The mean and std comes from @iafoss's notebook. ","metadata":{}},{"cell_type":"code","source":"mean = torch.tensor([1.0-0.90949707, 1.0-0.8188697, 1.0-0.87795304])\nstd = torch.tensor([0.36357649, 0.49984502, 0.40477625])","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:24.454013Z","iopub.execute_input":"2021-05-25T05:58:24.454373Z","iopub.status.idle":"2021-05-25T05:58:24.460376Z","shell.execute_reply.started":"2021-05-25T05:58:24.454339Z","shell.execute_reply":"2021-05-25T05:58:24.459588Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We write a function to open images and prepare them.","metadata":{}},{"cell_type":"code","source":"def open_image(fn):\n    \n    with warnings.catch_warnings():\n        warnings.simplefilter(\"ignore\", UserWarning) # EXIF warning from TiffPlugin\n        x = PILImage.create(fn)\n\n    x = torch.Tensor(np.array(x))\n    x = x.permute(2,0,1).float()/255.0\n    x = (1.0 - x) #invert image for zero padding plus normalize\n    \n    return x","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:24.461575Z","iopub.execute_input":"2021-05-25T05:58:24.462013Z","iopub.status.idle":"2021-05-25T05:58:24.470225Z","shell.execute_reply.started":"2021-05-25T05:58:24.461985Z","shell.execute_reply":"2021-05-25T05:58:24.469508Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The PandaImage class reverses modification and allows for the images to be displayed.","metadata":{}},{"cell_type":"code","source":"class PandaImage(fastuple):\n    def show(self, ctx=None, **kwargs):\n        img, label = self\n        img = img.view(N,-1,3,sz,sz).permute(1,2,3,0,4).contiguous().view(3,-1,sz*N)\n        img = 1- img\n        img = img.permute(1,2,0)\n        img = np.array(img*255).astype(np.uint8)\n        \n        return show_image(PILImage.create(img), title=label, ctx=ctx)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:24.471483Z","iopub.execute_input":"2021-05-25T05:58:24.471906Z","iopub.status.idle":"2021-05-25T05:58:24.48064Z","shell.execute_reply.started":"2021-05-25T05:58:24.47187Z","shell.execute_reply":"2021-05-25T05:58:24.479866Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We make use of fastcore's Type Dispatch to make `show_batch` work.","metadata":{}},{"cell_type":"code","source":"@typedispatch\ndef show_batch(x:PandaImage, y, samples, ctxs=None, max_n=6, nrows=None, ncols=1, figsize=(20,20), **kwargs):\n    if figsize is None: figsize = (ncols*6, max_n//ncols * 3)\n    if ctxs is None: ctxs = get_grid(min(x[0].shape[0], max_n), nrows=None, ncols=ncols, figsize=figsize)\n    for i,ctx in enumerate(ctxs): PandaImage(x[0][i], [x[1][i].item()]).show(ctx=ctx)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:24.481836Z","iopub.execute_input":"2021-05-25T05:58:24.482139Z","iopub.status.idle":"2021-05-25T05:58:24.49241Z","shell.execute_reply.started":"2021-05-25T05:58:24.482096Z","shell.execute_reply":"2021-05-25T05:58:24.491606Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"`PandaTransform` opens N tiles of a `image_id`. Then if it is a train dataset carries out data augmentation and stacks the N images to be used for preparing dataloader.   ","metadata":{}},{"cell_type":"code","source":"class PandaTransform(Transform):\n    def __init__(self, path_img, df, files, valid=False):\n        self.files = files\n        self.path_img = path_img\n        self.df = df\n        self.valid = valid\n        self.tfms = aug_transforms(flip_vert=True, max_rotate=15, pad_mode='zeros')\n        \n    def encodes(self, i):\n        files_i = self.files[i]\n        label  = self.df[self.df['image_id'] == files_i]['isup_grade'].values\n        \n        fnames = [self.path_img/f'{files_i}_{i}.png' for i in range(N)]\n        imgs = [open_image(fname) for fname in fnames]\n        \n        if not self.valid:\n            aug_img = []\n            for img in imgs: \n                for t in self.tfms:\n                    img = t(img, split_idx=0)\n                aug_img.append(img)\n\n            aug_img = torch.stack(aug_img, 0)\n            return (PandaImage(aug_img, label))\n        \n        else:\n            return (PandaImage(torch.stack(imgs, 0), label))","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:24.493592Z","iopub.execute_input":"2021-05-25T05:58:24.494087Z","iopub.status.idle":"2021-05-25T05:58:24.504005Z","shell.execute_reply.started":"2021-05-25T05:58:24.49405Z","shell.execute_reply":"2021-05-25T05:58:24.503173Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_fns = df[df['fold'] != 0]['image_id'].values\nvalid_fns = df[df['fold'] == 0]['image_id'].values","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:24.505351Z","iopub.execute_input":"2021-05-25T05:58:24.505774Z","iopub.status.idle":"2021-05-25T05:58:24.519383Z","shell.execute_reply.started":"2021-05-25T05:58:24.505732Z","shell.execute_reply":"2021-05-25T05:58:24.518569Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_tl= TfmdLists(range(len(train_fns)), [PandaTransform(path_img, df, train_fns, valid=False)])\nvalid_tl= TfmdLists(range(len(valid_fns)), [PandaTransform(path_img, df, valid_fns, valid=True)])","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:24.522044Z","iopub.execute_input":"2021-05-25T05:58:24.5223Z","iopub.status.idle":"2021-05-25T05:58:24.611551Z","shell.execute_reply.started":"2021-05-25T05:58:24.522276Z","shell.execute_reply":"2021-05-25T05:58:24.610862Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dls = DataLoaders.from_dsets(train_tl, valid_tl, \n                             after_batch=[Normalize.from_stats(*(mean, std))], bs=BS)\ndls = dls.cuda()","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:24.613597Z","iopub.execute_input":"2021-05-25T05:58:24.614064Z","iopub.status.idle":"2021-05-25T05:58:24.628428Z","shell.execute_reply.started":"2021-05-25T05:58:24.614029Z","shell.execute_reply":"2021-05-25T05:58:24.627717Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Looks like everything is working fine.","metadata":{}},{"cell_type":"code","source":"dls.show_batch()","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:24.629654Z","iopub.execute_input":"2021-05-25T05:58:24.630022Z","iopub.status.idle":"2021-05-25T05:58:27.607014Z","shell.execute_reply.started":"2021-05-25T05:58:24.629984Z","shell.execute_reply":"2021-05-25T05:58:27.606216Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"x, y = dls.one_batch()","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:27.610451Z","iopub.execute_input":"2021-05-25T05:58:27.610941Z","iopub.status.idle":"2021-05-25T05:58:29.743983Z","shell.execute_reply.started":"2021-05-25T05:58:27.610904Z","shell.execute_reply":"2021-05-25T05:58:29.743126Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"x.shape","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:29.74564Z","iopub.execute_input":"2021-05-25T05:58:29.745969Z","iopub.status.idle":"2021-05-25T05:58:29.754515Z","shell.execute_reply.started":"2021-05-25T05:58:29.745942Z","shell.execute_reply":"2021-05-25T05:58:29.753533Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model as used in DrHB's 2nd place solution","metadata":{}},{"cell_type":"markdown","source":"The following model and the tricks come from DrHB's solution in the competition. The model outputs n_classes plus one (in this case: 7) output. The extra output will be used in regression-based prediction. The `CustomEnd` that is atached to the end of the model will allow for the preparation of the output that will be fed to the `loss_function`. The model also make use of `SqueezeExcite` layer.","metadata":{}},{"cell_type":"code","source":"class CustomEnd(nn.Module):\n    def __init__(self, scaler = SigmoidRange(-1, 6.0)):\n        super().__init__()\n        self.scaler_ = scaler\n        \n    def forward(self, x):\n        classif = x[:, :-1]\n        regress = self.scaler_ (x[:, -1])\n        return classif, regress\n    \ndef make_divisible(v, divisor=8, min_value=None):\n    min_value = min_value or divisor\n    new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)\n   # Make sure that round down does not go down by more than 10%.\n    if new_v < 0.9 * v:\n        new_v += divisor\n    return new_v\n\ndef sigmoid(x, inplace: bool = False):\n    return x.sigmoid_() if inplace else x.sigmoid()\n\nclass SqueezeExcite(nn.Module):\n    def __init__(self, in_chs, se_ratio=0.25, reduced_base_chs=None,\n             act_layer=nn.ReLU, gate_fn=sigmoid, divisor=1, **_):\n        super(SqueezeExcite, self).__init__()\n        self.gate_fn = gate_fn\n        reduced_chs = make_divisible((reduced_base_chs or in_chs) * se_ratio, divisor)\n        self.avg_pool = nn.AdaptiveAvgPool2d(1)\n        self.conv_reduce = nn.Conv2d(in_chs, reduced_chs, 1, bias=True)\n        self.act1 = act_layer(inplace=True)\n        self.conv_expand = nn.Conv2d(reduced_chs, in_chs, 1, bias=True)\n    def forward(self, x):\n        x_se = self.avg_pool(x)\n        x_se = self.conv_reduce(x_se)\n        x_se = self.act1(x_se)\n        x_se = self.conv_expand(x_se)\n        x = x * self.gate_fn(x_se)\n        return x","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:29.755993Z","iopub.execute_input":"2021-05-25T05:58:29.756371Z","iopub.status.idle":"2021-05-25T05:58:29.768878Z","shell.execute_reply.started":"2021-05-25T05:58:29.756333Z","shell.execute_reply":"2021-05-25T05:58:29.76801Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class DrHBModel(nn.Module):\n    def __init__(self, N):\n        super().__init__()\n        self.N = N\n        m = models.resnet34(pretrained=True)\n        self.enc = nn.Sequential(*list(m.children())[:-2])       \n        nc = list(m.children())[-1].in_features\n        self.cb = SqueezeExcite(nc)\n        self.head = nn.Sequential(AdaptiveConcatPool2d(),\n                                  Flatten(),\n                                  nn.Linear(2*nc,512),\n                                  nn.ReLU(inplace=True),\n                                  nn.Dropout(0.4),\n                                  nn.Linear(512,7), \n                                  CustomEnd())\n        \n    def forward(self, x):\n        shape = x.shape\n        n = shape[1]\n        x = x.view(-1,shape[2],shape[3],shape[4])\n        x = self.enc(x)\n        \n        shape = x.shape\n        x = x.view(-1, n, x.shape[1], x.shape[2], x.shape[3]).permute(0, 2, 1, 3, 4).contiguous().\\\n        view(-1, x.shape[1], x.shape[2] * n, x.shape[3])\n        x = x.view(x.shape[0], x.shape[1], x.shape[2]//int(np.sqrt(n)), -1)\n        x = self.cb(x)\n        x = self.head(x)\n        return x","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:29.77014Z","iopub.execute_input":"2021-05-25T05:58:29.770495Z","iopub.status.idle":"2021-05-25T05:58:29.783209Z","shell.execute_reply.started":"2021-05-25T05:58:29.77046Z","shell.execute_reply":"2021-05-25T05:58:29.782351Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"drhbmodel= DrHBModel(N)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:29.784125Z","iopub.execute_input":"2021-05-25T05:58:29.784615Z","iopub.status.idle":"2021-05-25T05:58:30.338556Z","shell.execute_reply.started":"2021-05-25T05:58:29.784578Z","shell.execute_reply":"2021-05-25T05:58:30.334171Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def drhbmodel_splitter(m): return L(m.enc, m.cb, m.head).map(params)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:30.341052Z","iopub.execute_input":"2021-05-25T05:58:30.341739Z","iopub.status.idle":"2021-05-25T05:58:30.347715Z","shell.execute_reply.started":"2021-05-25T05:58:30.341697Z","shell.execute_reply":"2021-05-25T05:58:30.346621Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Preparing timm-based model with DrHB's tricks in his 2nd place solution","metadata":{}},{"cell_type":"markdown","source":"The following section was written to make use of `timm` models.","metadata":{}},{"cell_type":"code","source":"def get_timm_vis_model(arch:str, pretrained=True, cut=None):\n    model = create_model(arch, pretrained=pretrained)\n    if cut is None:\n        ll = list(enumerate(model.children()))\n        cut = next(i for i,o in reversed(ll) if has_pool_type(o))\n    model =  nn.Sequential(*list(model.children())[:cut])\n    \n    return model","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:30.348897Z","iopub.execute_input":"2021-05-25T05:58:30.349232Z","iopub.status.idle":"2021-05-25T05:58:30.36094Z","shell.execute_reply.started":"2021-05-25T05:58:30.349196Z","shell.execute_reply":"2021-05-25T05:58:30.359927Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class PandaModel(Module):\n    def __init__(self, n_classes, arch, *args, **kwargs):\n        self.vis_model = get_timm_vis_model(arch)\n        self.cb = SqueezeExcite(num_features_model(self.vis_model))\n        self.vis_head  = create_head(num_features_model(self.vis_model), n_classes+1)\n        self.custom_end = CustomEnd()\n                \n    def forward(self, x):\n        shape = x[0].shape\n        n = shape[0]\n        x = x.view(-1,shape[1],shape[2],shape[3]) \n        #x: bs*N x 3 x 128 x 128\n        \n        x = self.vis_model(x)\n        shape = x.shape \n        #x: bs*N x C x 4 x 4\n        #concatenate the output for tiles into a single map\n        x = x.view(-1,n,shape[1],shape[2],shape[3]).permute(0,2,1,3,4).contiguous().view(-1,shape[1],shape[2]*n,shape[3])\n        #x: bs x C x N*4 x 4\n        x = self.vis_head(x)\n        #x: bs x n\n        x = self.custom_end(x)\n        return x","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:30.36231Z","iopub.execute_input":"2021-05-25T05:58:30.362864Z","iopub.status.idle":"2021-05-25T05:58:30.375831Z","shell.execute_reply.started":"2021-05-25T05:58:30.36281Z","shell.execute_reply":"2021-05-25T05:58:30.374211Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pandamodel = PandaModel(n_classes, 'ssl_resnext50_32x4d')","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:30.37719Z","iopub.execute_input":"2021-05-25T05:58:30.377575Z","iopub.status.idle":"2021-05-25T05:58:30.968477Z","shell.execute_reply.started":"2021-05-25T05:58:30.377538Z","shell.execute_reply":"2021-05-25T05:58:30.967635Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def pandamodel_splitter(m): return L(m.vis_model, m.vis_head).map(params)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:30.969805Z","iopub.execute_input":"2021-05-25T05:58:30.970162Z","iopub.status.idle":"2021-05-25T05:58:30.974807Z","shell.execute_reply.started":"2021-05-25T05:58:30.970125Z","shell.execute_reply":"2021-05-25T05:58:30.973744Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Preparing Loss Functions and Metrics","metadata":{}},{"cell_type":"code","source":"class CustomCrossEntropy(nn.CrossEntropyLoss):\n  \n  def forward(self, input, target):\n    #target = target.long()\n    target = target.squeeze()\n    return F.cross_entropy(input, target, weight=self.weight, ignore_index=self.ignore_index, reduction=self.reduction)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:30.976301Z","iopub.execute_input":"2021-05-25T05:58:30.976638Z","iopub.status.idle":"2021-05-25T05:58:30.985364Z","shell.execute_reply.started":"2021-05-25T05:58:30.976604Z","shell.execute_reply":"2021-05-25T05:58:30.984628Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CustomLoss(nn.Module):\n    def __init__(self, loss_ce, loss_mse):\n        super().__init__()\n        self.loss_ce  = loss_ce\n        self.loss_mse = loss_mse \n        \n    def forward(self, i, o):\n        loss_cross = self.loss_ce(i[0], o)\n        loss_mserr = self.loss_mse(i[1], o.float().squeeze())\n        return loss_cross + loss_mserr","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:30.986418Z","iopub.execute_input":"2021-05-25T05:58:30.986651Z","iopub.status.idle":"2021-05-25T05:58:30.995805Z","shell.execute_reply.started":"2021-05-25T05:58:30.986629Z","shell.execute_reply":"2021-05-25T05:58:30.995052Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def qkp_class(y_hat, y):\n    y_hat = torch.argmax(F.softmax(y_hat[0], dim=1), dim=1)\n    return torch.tensor(cohen_kappa_score(y_hat.cpu(), y.cpu(), weights='quadratic'), device='cuda:0')\n\ndef qkp_regres(y_hat, y):\n    p = optR.predict(y_hat[1].cpu().numpy(), coefficients)\n    return torch.tensor(cohen_kappa_score(p, y.cpu(), weights='quadratic'), device='cuda:0')\n\ndef qkp_combine(y_hat, y):\n    return torch.tensor(cohen_kappa_score(torch.round((y_hat[1] + torch.argmax(F.softmax(y_hat[0], dim=1), dim=1))/2).cpu(), y.cpu(), weights='quadratic'),device='cuda:0')","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:30.998732Z","iopub.execute_input":"2021-05-25T05:58:30.999008Z","iopub.status.idle":"2021-05-25T05:58:31.007054Z","shell.execute_reply.started":"2021-05-25T05:58:30.998984Z","shell.execute_reply":"2021-05-25T05:58:31.006133Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class OptimizedRounder():\n    def __init__(self):\n        self.coef_ = 0\n\n    def _kappa_loss(self, coef, X, y):\n        X_p = np.copy(X)\n        for i, pred in enumerate(X_p):\n            if pred < coef[0]:\n                X_p[i] = 0\n            elif pred >= coef[0] and pred < coef[1]:\n                X_p[i] = 1\n            elif pred >= coef[1] and pred < coef[2]:\n                X_p[i] = 2\n            elif pred >= coef[2] and pred < coef[3]:\n                X_p[i] = 3\n            elif pred >= coef[3] and pred < coef[4]:\n                X_p[i] = 4\n            else:\n                X_p[i] = 5\n\n        ll = quadratic_weighted_kappa(y, X_p)\n        return -ll\n\n    def fit(self, X, y):\n        loss_partial = partial(self._kappa_loss, X=X, y=y)\n        initial_coef = [0.5, 1.5, 2.5, 3.5, 4.5]\n        self.coef_ = sp.optimize.minimize(loss_partial, initial_coef, method='nelder-mead')\n\n    def predict(self, X, coef):\n        X_p = np.copy(X)\n        for i, pred in enumerate(X_p):\n            if pred < coef[0]:\n                X_p[i] = 0\n            elif pred >= coef[0] and pred < coef[1]:\n                X_p[i] = 1\n            elif pred >= coef[1] and pred < coef[2]:\n                X_p[i] = 2\n            elif pred >= coef[2] and pred < coef[3]:\n                X_p[i] = 3\n            elif pred >= coef[3] and pred < coef[4]:\n                X_p[i] = 4\n            else:\n                X_p[i] = 5\n        return X_p\n\n    def coefficients(self):\n        return self.coef_['x']","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:31.008381Z","iopub.execute_input":"2021-05-25T05:58:31.008745Z","iopub.status.idle":"2021-05-25T05:58:31.022104Z","shell.execute_reply.started":"2021-05-25T05:58:31.008698Z","shell.execute_reply":"2021-05-25T05:58:31.021238Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"optR = OptimizedRounder()\ncoefficients = [0.5, 1.5, 2.5, 3.5, 4.5]","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:58:31.023446Z","iopub.execute_input":"2021-05-25T05:58:31.0238Z","iopub.status.idle":"2021-05-25T05:58:31.034277Z","shell.execute_reply.started":"2021-05-25T05:58:31.023763Z","shell.execute_reply":"2021-05-25T05:58:31.033312Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training DrHB Model","metadata":{}},{"cell_type":"code","source":"learn = Learner(dls, \n                drhbmodel, \n                loss_func=CustomLoss(CustomCrossEntropy(), nn.MSELoss()), \n                metrics=[qkp_class, qkp_regres, qkp_combine],\n                splitter=drhbmodel_splitter).to_fp16()","metadata":{"execution":{"iopub.status.busy":"2021-05-25T04:53:58.133665Z","iopub.execute_input":"2021-05-25T04:53:58.134213Z","iopub.status.idle":"2021-05-25T04:53:58.144817Z","shell.execute_reply.started":"2021-05-25T04:53:58.134173Z","shell.execute_reply":"2021-05-25T04:53:58.143982Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"learn.freeze()\nlearn.summary()","metadata":{"execution":{"iopub.status.busy":"2021-05-24T11:58:31.656141Z","iopub.execute_input":"2021-05-24T11:58:31.656418Z","iopub.status.idle":"2021-05-24T11:58:33.695695Z","shell.execute_reply.started":"2021-05-24T11:58:31.656391Z","shell.execute_reply":"2021-05-24T11:58:33.694746Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"learn.fit_one_cycle(2, 1e-3)","metadata":{"execution":{"iopub.status.busy":"2021-05-24T11:58:33.6973Z","iopub.execute_input":"2021-05-24T11:58:33.697671Z","iopub.status.idle":"2021-05-24T12:12:35.968811Z","shell.execute_reply.started":"2021-05-24T11:58:33.697635Z","shell.execute_reply":"2021-05-24T12:12:35.967935Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"learn.unfreeze()\nlearn.fit_one_cycle(3, 1e-3)","metadata":{"execution":{"iopub.status.busy":"2021-05-24T12:12:35.970775Z","iopub.execute_input":"2021-05-24T12:12:35.97113Z","iopub.status.idle":"2021-05-24T13:27:04.967437Z","shell.execute_reply.started":"2021-05-24T12:12:35.971091Z","shell.execute_reply":"2021-05-24T13:27:04.966541Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training timm Model","metadata":{}},{"cell_type":"code","source":"learn = Learner(dls, \n                pandamodel, \n                loss_func=CustomLoss(CustomCrossEntropy(), nn.MSELoss()), \n                metrics=[qkp_class, qkp_regres, qkp_combine],\n                splitter=pandamodel_splitter).to_fp16()","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:35:40.320319Z","iopub.execute_input":"2021-05-25T05:35:40.320664Z","iopub.status.idle":"2021-05-25T05:35:40.326412Z","shell.execute_reply.started":"2021-05-25T05:35:40.320632Z","shell.execute_reply":"2021-05-25T05:35:40.325183Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"learn.freeze()\nlearn.summary()","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:35:40.515108Z","iopub.execute_input":"2021-05-25T05:35:40.515371Z","iopub.status.idle":"2021-05-25T05:35:41.204991Z","shell.execute_reply.started":"2021-05-25T05:35:40.515344Z","shell.execute_reply":"2021-05-25T05:35:41.204042Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"learn.fit_one_cycle(2, 1e-3)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T05:35:49.2201Z","iopub.execute_input":"2021-05-25T05:35:49.220437Z","iopub.status.idle":"2021-05-25T05:45:28.269836Z","shell.execute_reply.started":"2021-05-25T05:35:49.220403Z","shell.execute_reply":"2021-05-25T05:45:28.266262Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"learn.unfreeze()\nlearn.fit_one_cycle(3, 1e-3)","metadata":{},"execution_count":null,"outputs":[]}]}