{"cells":[{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import sys\n#sys.path = [ ###ResNext Models\n#\t'/gpfs/research/chaohuang/panda/models/semi-supervised-ImageNet1K-models-master',\n#] + sys.path\nsys.path = [ ###Utility scripts\n\t'../input/utility-script',\n] + sys.path\nimport fastai\nfrom fastai.vision import *\nfrom fastai.callbacks import SaveModelCallback\nimport os\nimport numpy as np\nimport pandas as pd\n\n#from sklearn.model_selection import KFold\nfrom radam import *\nfrom csvlogger import *\nfrom mish_activation import *\n#from hubconf import *\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.metrics import cohen_kappa_score,confusion_matrix\nfrom skimage.transform import resize\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport math\nfrom torch.optim.optimizer import Optimizer, required\n#import spams\n\nfastai.__version__","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# remove this cell if run locally\n!mkdir 'cache'\n!mkdir 'cache/torch'\n!mkdir 'cache/torch/checkpoints'\n!cp '../input/pytorch-pretrained-models/semi_supervised_resnext50_32x4-ddb3e555.pth' 'cache/torch/checkpoints/'\ntorch.hub.DEFAULT_CACHE_DIR = 'cache'\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sz = 128\nbs = 8\nnfolds = 4\nSEED = 2020\nN = 36 #number of tiles per image\n#TRAIN = '/gpfs/research/chaohuang/panda/data/concat_image/tiles_36x256x256_stain_norm/'\nTRAIN = '../input/panda-tiles-36x256x256-stain-norm-downsampling128/tiles_36x256x256_stain_norm_downsampling128/tiles_36x256x256_stain_norm_downsampling128/'\nLABELS = '../input/prostate-cancer-grade-assessment/train.csv'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def seed_everything(seed):\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    torch.backends.cudnn.benchmark = True\n\nseed_everything(SEED)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"########################## Data ###########################\n### Use stratified KFold split.\n\ndf = pd.read_csv(LABELS).set_index('image_id')\nfiles = sorted(set([p[:32] for p in os.listdir(TRAIN)]))\ndf = df.loc[files]\ndf = df.reset_index()\nsplits = StratifiedKFold(n_splits=nfolds, random_state=SEED, shuffle=True)\nsplits = list(splits.split(df,df.isup_grade))\nfolds_splits = np.zeros(len(df)).astype(np.int)\nfor i in range(nfolds): folds_splits[splits[i][1]] = i\ndf['split'] = folds_splits\nprint(df.head())\n#df.to_csv('/gpfs/research/chaohuang/panda/data/train_folds.csv',index = False)\n\ndf_folds = pd.read_csv('../input/panda-tiles-36x256x256-stain-norm-downsampling128/train_folds_4.csv')\nprint(df_folds.head())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"mean = torch.tensor([1.0-0.8182546, 1.0-0.65889897, 1.0-0.84993991])  \nstd = torch.tensor([0.33994367, 0.48929796, 0.3426114])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def open_image(fn:PathOrStr, div:bool=True, convert_mode:str='RGB', cls:type=Image,\n        after_open:Callable=None)->Image:\n    with warnings.catch_warnings():\n        warnings.simplefilter(\"ignore\", UserWarning) # EXIF warning from TiffPlugin\n        x = PIL.Image.open(fn).convert(convert_mode)\n    if after_open: x = after_open(x)\n    x = pil2tensor(x,np.float32)\n    if div: x.div_(255)\n    return cls(1.0-x) #invert image for zero padding\n\nclass MImage(ItemBase):\n    def __init__(self, imgs):\n        self.obj, self.data = \\\n          (imgs), [(imgs.data - mean[...,None,None])/std[...,None,None]]\n        #(imgs), [(imgs[i].data - mean[...,None,None])/std[...,None,None] for i in range(len(imgs))]\n\n    def apply_tfms(self, tfms,*args, **kwargs):\n        self.obj = self.obj.apply_tfms(tfms, *args, **kwargs)\n        self.data = (self.obj.data - mean[...,None,None])/std[...,None,None]\n        return self\n        ##for i in range(len(self.obj)):        \n            #self.obj[i] = self.obj[i].apply_tfms(tfms, *args, **kwargs)\n            #self.data[i] = (self.obj[i].data - mean[...,None,None])/std[...,None,None]\n        #return self\n\n\n    def __repr__(self): return f'{self.__class__.__name__} {self.obj.shape}'\n    #def __repr__(self): return f'{self.__class__.__name__} {img.shape for img in self.obj}'\n    def to_one(self):\n        img = self.data\n        #img = torch.stack(self.data,1)\n        #img = img.view(3,-1,N,sz,sz).permute(0,1,3,2,4).contiguous().view(3,-1,sz*N)\n        img = img.view(3,-1,1,3*sz,4*sz).permute(0,1,3,2,4).contiguous().view(3,-1,4*sz)\n        return Image(1.0 - (mean[...,None,None]+img*std[...,None,None]))\n\nclass MImageItemList(ImageList):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n    \n    def __len__(self)->int: return len(self.items) or 1 \n    \n    def get(self, i):\n        fn = Path(self.items[i])\n        fnames = Path(str(fn)+'.png')\n        imgs = open_image(fnames, convert_mode=self.convert_mode, after_open=self.after_open)\n        return MImage(imgs)\n\n    #def reconstruct(self, t):\n    #    return MImage([mean[...,None,None]+_t*std[...,None,None] for _t in t])\n    \n    def show_xys(self, xs, ys, figsize:Tuple[int,int]=(300,50), **kwargs):\n        rows = min(len(xs),8)\n        fig, axs = plt.subplots(rows,1,figsize=figsize)\n        xs.show(ax=axs, y=ys, **kwargs)\n        plt.tight_layout()        \n        #for i, ax in enumerate(axs.flatten() if rows > 1 else [axs]):\n        #    xs[i].to_one().show(ax=ax, y=ys[i], **kwargs)\n        #plt.tight_layout()\n\n#collate function to combine multiple images into one tensor\ndef MImage_collate(batch:ItemsList)->Tensor:\n    result = torch.utils.data.dataloader.default_collate(to_data(batch))\n    if isinstance(result[0],list):\n        result = [torch.stack(result[0],1),result[1]]\n    return result","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fn = TRAIN\n\nfnames = [Path(str(fn) + files[i]+'.png') for i in range(12)]\nimgs = [open_image(fname) for fname in fnames]\nimgs =[(imgs[i].data - mean[...,None,None])/std[...,None,None] for i in range(len(imgs))]\nprint(imgs[0].shape)\nIMG = imgs[0].view(3,-1,1,6*sz,6*sz).permute(0,1,3,2,4).contiguous().view(3,-1,6*sz)\nprint(IMG.shape)\n\ndef get_data(fold=0):\n    return (MImageItemList.from_df(df_folds, path='.', folder=TRAIN, cols='image_id')\n      .split_by_idx(df_folds.index[df_folds.split == fold].tolist())\n      .label_from_df(cols=['isup_grade'])\n      .transform(get_transforms(flip_vert=True,max_rotate=15),size=6*sz,padding_mode='zeros')\n      .databunch(bs=bs,num_workers=4))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"### Model ###\n# The code below implements Concat Tile pooling idea. \n# As a backbone I use Semi-Weakly Supervised ImageNet pretrained ResNeXt50 model, \n# which worked for me quite well in a number of previous competitions.\n\ndef _resnext(url, block, layers, pretrained, progress, **kwargs):\n    model = ResNet(block, layers, **kwargs)\n    #state_dict = load_state_dict_from_url(url, progress=progress)\n    #model.load_state_dict(state_dict)\n    return model\n\nclass Model(nn.Module):\n    def __init__(self, arch='resnext50_32x4d_ssl', n=6, pre=True):\n        super().__init__()\n        m = torch.hub.load('facebookresearch/semi-supervised-ImageNet1K-models', arch)\n        #m = _resnext(semi_supervised_model_urls[arch], Bottleneck, [3, 4, 6, 3], False, \n        #        progress=False,groups=32,width_per_group=4)\n        self.enc = nn.Sequential(*list(m.children())[:-2])       \n        nc = list(m.children())[-1].in_features\n        self.head = nn.Sequential(AdaptiveConcatPool2d(),Flatten(),nn.Linear(2*nc,512),\n                            Mish(),nn.BatchNorm1d(512), nn.Dropout(0.5),nn.Linear(512,n))\n        \n    def forward(self, x):\n        N = 1\n        sz1 = 6*sz\n        sz2 = 6*sz\n        x = x.view(-1,N,3,sz1,sz2)\n        \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        shape = x.shape\n        x = x.view(-1,n,shape[1],shape[2],shape[3]).permute(0,2,1,3,4).contiguous()\\\n          .view(-1,shape[1],shape[2]*n,shape[3])\n        x = self.head(x)\n        x = F.softmax(x,dim=1)\n        return x\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fold = 0","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"### Training ###\nfname = 'RNXT50_36x256x256_stain_norm_downsampling128'\npred,prob,target = [],[],[]\n#for fold in range(nfolds):\n#for fold in range(1):\ndata = get_data(fold)\nmodel = Model()\nlearn = Learner(data, model, loss_func=nn.CrossEntropyLoss(), opt_func=Over9000, \n            metrics=[KappaScore(weights='quadratic')]).to_fp16()\n#logger = tf.keras.callbacks.CSVLogger(learn, f'log_{fname}_{fold}')\nlogger = CSVLogger(learn, f'log_{fname}_{fold}')\nlearn.clip_grad = 1.0\nlearn.split([model.head])\nlearn.unfreeze()\n\ncyc_len = 16\nlearn.fit_one_cycle(cyc_len, max_lr=1e-4, div_factor=100, pct_start=0.0, \n  callbacks = [SaveModelCallback(learn,name=f'model',monitor='kappa_score')])\ntorch.save(learn.model.state_dict(), f'{fname}_{fold}.pth')\n\nlearn.model.eval()\nwith torch.no_grad():\n    for step, (x, y) in progress_bar(enumerate(data.dl(DatasetType.Valid)),\n                                 total=len(data.dl(DatasetType.Valid))):\n        p = learn.model(x)\n        prob.append(p.float().cpu())\n        target.append(y.cpu())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"### Display probabilities, prediction labels and target labels of train data ###           \nprobs = torch.cat(prob,0).numpy()      \npreds = torch.argmax(torch.cat(prob,0),1).numpy()\nt = torch.cat(target)\n#print(probs,preds,t)\nprint(f'probs = ',probs,'\\npreds = ',preds,'\\ntargets = ',t)\nprint('quadratic kappa score for valid dataset = ', cohen_kappa_score(t,preds,weights='quadratic'))\nprint('confusion matrix = ', confusion_matrix(t,preds))","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}