{"cells":[{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","collapsed":true,"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":false},"cell_type":"markdown","source":"# Load Metadata"},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install timm","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nimport numpy as np \nimport pandas as pd \nimport glob\nimport openslide\nimport PIL\nimport torch\nimport tqdm\nimport timm\nfrom torch.utils.data import TensorDataset, DataLoader\nimport torch.optim as optim\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau\nfrom datetime import datetime\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\ncpu = torch.device('cpu')\nprint('Running on device: {}'.format(device))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_img_dir = '../input/prostate-cancer-grade-assessment/train_images/'\ntrain_msk_dir = '../input/prostate-cancer-grade-assessment/train_label_masks/'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_meta = pd.read_csv('../input/prostate-cancer-grade-assessment/train.csv')\ntest_meta = pd.read_csv('../input/prostate-cancer-grade-assessment/test.csv')\ntrain_img_dir = '../input/prostate-cancer-grade-assessment/train_images/'\ntrain_mask_dir = '../input/prostate-cancer-grade-assessment/train_label_masks/'\n# sample = pd.read_csv('../input/prostate-cancer-grade-assessment/sample_submission.csv')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data Loader"},{"metadata":{"trusted":true},"cell_type":"code","source":"from PIL import Image, ImageChops, ImageOps\nfrom sklearn.feature_extraction.image import extract_patches_2d\n\ndef trim(im):\n    bg = Image.new(im.mode, im.size, (255,255,255))\n    diff = ImageChops.difference(im, bg)\n    diff = ImageChops.add(diff, diff, 2.0, -100)\n    bbox = diff.getbbox()\n    if bbox:\n        return im.crop(bbox)\ndef tile(img, N):\n    sz = 256\n    result = []\n    shape = img.shape\n    pad0,pad1 = (sz - shape[0]%sz)%sz, (sz - shape[1]%sz)%sz\n    img = np.pad(img,[[pad0//2,pad0-pad0//2],[pad1//2,pad1-pad1//2],[0,0]],\n                constant_values=255)\n    img = img.reshape(img.shape[0]//sz,sz,img.shape[1]//sz,sz,3)\n    img = img.transpose(0,2,1,3,4).reshape(-1,sz,sz,3)\n    if len(img) < N:\n        img = np.pad(img,[[0,N-len(img)],[0,0],[0,0],[0,0]],constant_values=255)\n    idxs = np.argsort(img.reshape(img.shape[0],-1).sum(-1))[:N]\n    img = img[idxs]\n    return img\n\ndef pre_process(image_id, n_patches= 10):\n    \"\"\"Show a mask overlayed on a slide.\"\"\"\n\n    slide = openslide.OpenSlide(os.path.join(train_img_dir, image_id + '.tiff'))\n    slide_data = slide.read_region((0,0), 2, slide.level_dimensions[2])\n    slide_data_crop = trim(Image.fromarray(np.asarray(slide_data)[:,:,0:3], 'RGB'))\n    if slide_data_crop == None:\n        slide_data_crop = Image.fromarray(np.asarray(slide_data)[:,:,0:3], 'RGB')\n    w, h = slide_data_crop.size\n    if (h < 266) or (w < 266):\n        slide_data_crop = slide_data_crop.resize(size = (np.max([w, 266]), np.max([h, 266])))\n    image_crop = np.asarray(slide_data_crop)\n#     image_patches = extract_patches_2d(image_crop, (256,256), max_patches=n_patches, random_state=None)\n    image_patches = tile(image_crop, N = 10)\n    image_patches = np.transpose(image_patches, (0, 3, 1, 2)).astype(np.float32)\n\n    slide.close()\n    return(image_patches)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_o = pre_process(image_id = 'a3794ec31a02fbf429486dd464f83d25')\nimage_o.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from torch.utils import data\nclass Dataset(data.Dataset):\n    'Characterizes a dataset for PyTorch'\n    def __init__(self, list_IDs, labels):\n        'Initialization'\n        self.labels = labels\n        self.list_IDs = list_IDs\n\n    def __len__(self):\n        'Denotes the total number of samples'\n        return len(self.list_IDs)\n\n    def __getitem__(self, index):\n        'Generates one sample of data'\n        # Select sample\n        ID = self.list_IDs[index]\n        # Load data and get label\n#         print(index)\n        X = pre_process(image_id = ID)\n        y = self.labels[index]\n\n        return X, y","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataset = Dataset(list_IDs = train_meta.image_id,\n                  labels = train_meta.isup_grade)\ntrain_data, val_data = torch.utils.data.random_split(dataset, [8000, 2616])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_batch_size = 5\nval_batch_size = 5\ntrain_loader = DataLoader(train_data, shuffle=False, batch_size=train_batch_size, num_workers = 4)\nval_loader = DataLoader(val_data, shuffle=False, batch_size=val_batch_size, num_workers = 4)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# mixnet = timm.create_model(\"mixnet_s\", pretrained=True)\n# del(mixnet)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# count = 0\n# for inputs, labels in train_loader:\n#     if count > 0:\n#         break\n#     print(inputs.shape)\n#     count += 1\n# mixnet.to(device)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"markdown","source":"# Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch.nn as nn\n\ndef qwk3(a1, a2, max_rat=6):\n    assert(len(a1) == len(a2))\n    a1 = np.asarray(a1, dtype=np.int32)\n    a2 = np.asarray(a2, dtype=np.int32)\n\n    hist1 = np.zeros((max_rat + 1, ))\n    hist2 = np.zeros((max_rat + 1, ))\n\n    o = 0\n    for k in range(a1.shape[0]):\n        i, j = a1[k], a2[k]\n        hist1[i] += 1\n        hist2[j] += 1\n        o +=  (i - j) * (i - j)\n\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\n    e = e / a1.shape[0]\n\n    return 1 - o / e\n\nclass Identity(nn.Module):\n    def __init__(self):\n        super(Identity, self).__init__()\n        \n    def forward(self, x):\n        return x\n    \nclass PandaNet(nn.Module):\n    def __init__(self, drop_prob=0.5):\n        super(PandaNet, self).__init__()\n                \n        self.mixnet = timm.create_model(\"mixnet_s\", pretrained=True)\n        self.mixnet.classifier = Identity()\n        self.lstm = nn.LSTM(1536, 256, 2, dropout=.75, bidirectional=True, batch_first=True)\n        self.fc1 = nn.Linear(512, 128)\n        self.fc4 = nn.Linear(128, output_size)\n        self.elu = nn.ELU()\n        self.sigmoid = nn.Sigmoid()\n        \n    def forward(self, x):\n        b, p, c, h, w = x.shape\n        out = self.mixnet(x.reshape(b*p, c, h, w))\n        out = out.reshape(b, p, 1536)\n        out, _ = self.lstm(out)\n        out, _ = torch.max(out, 1)\n        out = self.fc1(out)\n        out = self.elu(out)\n        out = self.fc4(out)\n        out = self.sigmoid(out)\n        \n        out = out.view(x.size()[0], -1)\n#         out = out[:,-1]\n        return out","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"input_size = 512\noutput_size = 6\nhidden_dim = 512\n\nmodel = PandaNet()\nmodel.to(device)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_criterion = nn.CrossEntropyLoss()\nval_criterion = nn.CrossEntropyLoss()\n\noptimizer = optim.Adam(model.parameters(), lr=0.0001)\nscheduler = ReduceLROnPlateau(optimizer, mode='min', factor=0.5, patience=100, verbose=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"epochs = 10\ncounter = 0\nprint_every = 800\nclip = .5\nvalid_loss_min = np.Inf\nval_loss = torch.tensor(np.Inf)\nmodel.train()\nfor i in range(epochs):\n    for inputs, labels in train_loader:\n        counter += 1\n        inputs, labels = inputs.to(device), labels.to(device)\n        model.zero_grad()\n        output = model(inputs)\n        loss = train_criterion(output, labels)\n        nn.utils.clip_grad_norm_(model.parameters(), clip)\n        loss.backward()\n        optimizer.step()\n        train_qwk = qwk3(output.argmax(1).detach().cpu(), labels.cpu())\n        torch.cuda.empty_cache() \n        if counter % 100 == 0:\n            print(\"Time: {}...\".format(datetime.now().strftime(\"%H:%M:%S\")) + \n                  \"Epoch: {}/{}...\".format(i+1, epochs) +  \n                  \"Step: {}...\".format(counter) +\n                  \"Loss: {:.6f}...\".format(loss.item())) \n        \n        if counter%print_every == 0:\n            val_losses = []\n            val_qwk = []\n            model.eval()\n            for inp, lab in val_loader:\n                \n                inp, lab = inp.to(device), lab.to(device)\n                out = model(inp)\n                val_loss = val_criterion(out, lab)\n                val_losses.append(val_loss.item())\n                val_qwk.append(qwk3(out.argmax(1).detach().cpu(), lab.cpu()))\n            model.train()\n            print(\"Time: {}...\".format(datetime.now().strftime(\"%H:%M:%S\")) + \n                  \"Epoch: {}/{}...\".format(i+1, epochs),\n                  \"Step: {}...\".format(counter),\n                  \"Loss: {:.6f}...\".format(loss.item()),\n                  \"Val Loss: {:.6f}\".format(np.mean(val_losses)),\n                  \"QWK: {:.2f}...\".format(np.mean(val_qwk)))\n            if np.mean(val_losses) <= valid_loss_min:\n                torch.save(model.state_dict(), './model_mz.pt')\n                print('Validation loss decreased ({:.6f} --> {:.6f}).  Saving model ...'.format(valid_loss_min,np.mean(val_losses)))\n                valid_loss_min = np.mean(val_losses)\n                torch.cuda.empty_cache() \n    scheduler.step(loss.item())","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}