{"cells":[{"metadata":{},"cell_type":"markdown","source":"# RANZCR 1st Place Solution Inference\n\nHi all,\n\nWe're very exciting to writing this notebook and the summary of our solution here.\n\nOur final pipeline has 4 training stages but the minimal pipeline I show here has only 2 stages.\n\nThe 5-fold model trained with this minimal pipeline is sufficient to achieve CV 0.968-0.969 and pub/pvt LB 0.972\n\nI published 3 notebooks to demonstrate how our MINIMAL pipeline works.\n\n* Stage1: Segmentation (https://www.kaggle.com/haqishen/ranzcr-1st-place-soluiton-seg-model-small-ver)\n* Stage2: Classification (https://www.kaggle.com/haqishen/ranzcr-1st-place-soluiton-cls-model-small-ver)\n* Inference (This notebook)\n\nThis notebook shows how we can inference my minimal pipeline to make a valid submission, using my 5 fold segmentation models (b1 w/ input size 1024) and 5 fold classification models (b1 w/ input size 512)\n\n\nOur brief summary of winning solution: https://www.kaggle.com/c/ranzcr-clip-catheter-line-classification/discussion/226633\n\n\n\n# Thanks!"},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nimport sys\n# add smp & timm into kernel w/o internet\nsys.path = [\n    '../input/smp20210127/segmentation_models.pytorch-master/segmentation_models.pytorch-master/',\n    '../input/smp20210127/EfficientNet-PyTorch-master/EfficientNet-PyTorch-master',\n    '../input/smp20210127/pytorch-image-models-master/pytorch-image-models-master',\n    '../input/smp20210127/pretrained-models.pytorch-master/pretrained-models.pytorch-master',\n] + sys.path","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import time\nimport subprocess\nimport numpy as np\nimport pandas as pd\nimport cv2\nimport PIL.Image\nimport matplotlib.pyplot as plt\n%matplotlib inline\nimport seaborn as sns\nimport torch\nfrom torch.utils.data import DataLoader, Dataset\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport albumentations\nimport segmentation_models_pytorch as smp\nimport timm\nfrom tqdm.notebook import tqdm\n\ndevice = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data_dir = '../input/ranzcr-clip-catheter-line-classification'\nmodel_dir = '../input/ranzcr-public-model-qishen'\nnum_workers = 2\nimage_size = 512\nbatch_size = 8","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_sub = pd.read_csv(os.path.join(data_dir, 'sample_submission.csv'))\ndf_sub = df_sub.iloc[:358] if df_sub.shape[0] == 3582 else df_sub","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"class RANZCRDatasetTest(Dataset):\n\n    def __init__(self, df):\n\n        self.df = df.reset_index(drop=True)\n\n    def __len__(self):\n        return self.df.shape[0]\n\n    def __getitem__(self, index):\n        row = self.df.iloc[index]\n        image = cv2.imread(os.path.join(data_dir, 'test', row.StudyInstanceUID + '.jpg'))[:, :, ::-1]\n\n        image1024 = cv2.resize(image ,(1024, 1024)).astype(np.float32).transpose(2, 0, 1) / 255.\n        image512 = cv2.resize(image ,(512, 512)).astype(np.float32).transpose(2, 0, 1) / 255.\n\n        return {\n            '1024': torch.tensor(image1024),\n            '512': torch.tensor(image512),\n        }\n\n\ndataset_test = RANZCRDatasetTest(df_sub)\ntest_loader = torch.utils.data.DataLoader(dataset_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataset_show = RANZCRDatasetTest(df_sub)\nfrom pylab import rcParams\nrcParams['figure.figsize'] = 20,10\n\nf, axarr = plt.subplots(1,5)\n\nfor p in range(5):\n    img = dataset_show[p]\n    axarr[p].imshow(img['512'].transpose(0, 1).transpose(1,2))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"class SegModel(nn.Module):\n    def __init__(self, backbone):\n        super(SegModel, self).__init__()\n        self.seg = smp.UnetPlusPlus(encoder_name=backbone, encoder_weights=None, classes=2, activation=None)\n        \n    def forward(self,x):\n        global_features = self.seg.encoder(x)\n        seg_features = self.seg.decoder(*global_features)\n        seg_features = self.seg.segmentation_head(seg_features)\n        return seg_features\n\n\nclass enetv2(nn.Module):\n    def __init__(self, backbone):\n        super(enetv2, self).__init__()\n        self.enet = timm.create_model(backbone, False)\n        self.enet.conv_stem.weight = nn.Parameter(self.enet.conv_stem.weight.repeat(1,5//3+1,1,1)[:, :5])\n        self.myfc = nn.Linear(self.enet.classifier.in_features, 12)\n        self.enet.classifier = nn.Identity()\n        \n    def extract(self, x):\n        return self.enet(x)\n    \n    def forward(self, x, mask):\n        mask = F.interpolate(mask, x.shape[2])\n        x = torch.cat([x, mask], 1)\n        x = self.extract(x)\n        x = self.myfc(x)\n        return x","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Loading Models"},{"metadata":{"trusted":true},"cell_type":"code","source":"enet_type_seg = 'timm-efficientnet-b1'\nkernel_type_seg = 'unetb1_2cbce_1024T15tip_lr1e4_bs4_augv2_30epo'\n\nenet_type_cls = 'tf_efficientnet_b1_ns'\nkernel_type_cls = 'enetb1_5ch_512_lr3e4_bs32_30epo'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"models_seg = []\nfor fold in range(5):\n    model = SegModel(enet_type_seg)\n    model = model.to(device)\n    model_file = os.path.join(model_dir, f'{kernel_type_seg}_best_fold{fold}.pth')\n    model.load_state_dict(torch.load(model_file), strict=True)\n    model.eval()\n    models_seg.append(model)\n\nmodels_cls = []\nfor fold in range(5):\n    model = enetv2(enet_type_cls)\n    model = model.to(device)\n    model_file = os.path.join(model_dir, f'{kernel_type_cls}_best_fold{fold}.pth')\n    model.load_state_dict(torch.load(model_file), strict=True)\n    model.eval()\n    models_cls.append(model)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Inference"},{"metadata":{"trusted":true},"cell_type":"code","source":"PROBS = []\nwith torch.no_grad():\n    for batch_id, data in tqdm(enumerate(test_loader), total=len(test_loader)):\n        for k in data.keys():\n            data[k] = data[k].cuda()\n        mask = torch.stack([model(data['1024']).sigmoid() for model in models_seg], 0).mean(0)\n        logits = torch.stack([model(data['512'], mask) for model in models_cls], 0)\n        logits[:, :, :4] = logits[:, :, :4].softmax(2)\n        logits[:, :, 4:] = logits[:, :, 4:].sigmoid()\n        PROBS.append(logits.cpu())\nPROBS = torch.cat(PROBS, 1)\nPROBS = PROBS[:, :, [0,1,2,4,5,6,7,8,9,10,11]]\nPROBS = PROBS.numpy()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Check Distribution"},{"metadata":{"trusted":true},"cell_type":"code","source":"df_sub[[\n    'ETT - Abnormal',\n    'ETT - Borderline',\n    'ETT - Normal',\n    'NGT - Abnormal',\n    'NGT - Borderline',\n    'NGT - Incompletely Imaged',\n    'NGT - Normal',\n    'CVC - Abnormal',\n    'CVC - Borderline',\n    'CVC - Normal',\n    'Swan Ganz Catheter Present'\n]] = PROBS.mean(0)\n\nsns.distplot(df_sub[[\n    'CVC - Abnormal',\n    'CVC - Borderline',\n    'CVC - Normal',\n]])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Rank Prediction & Submit"},{"metadata":{"trusted":true},"cell_type":"code","source":"\ndf_subs = [df_sub.copy() for _ in range(PROBS.shape[0])]\nfor i, this_sub in enumerate(df_subs):\n    this_sub[[\n        'ETT - Abnormal',\n        'ETT - Borderline',\n        'ETT - Normal',\n        'NGT - Abnormal',\n        'NGT - Borderline',\n        'NGT - Incompletely Imaged',\n        'NGT - Normal',\n        'CVC - Abnormal',\n        'CVC - Borderline',\n        'CVC - Normal',\n        'Swan Ganz Catheter Present'\n    ]] = PROBS[i]\n\n    this_sub[[\n        'ETT - Abnormal',\n        'ETT - Borderline',\n        'ETT - Normal',\n        'NGT - Abnormal',\n        'NGT - Borderline',\n        'NGT - Incompletely Imaged',\n        'NGT - Normal',\n        'CVC - Abnormal',\n        'CVC - Borderline',\n        'CVC - Normal',\n        'Swan Ganz Catheter Present'\n    ]] = this_sub[[\n        'ETT - Abnormal',\n        'ETT - Borderline',\n        'ETT - Normal',\n        'NGT - Abnormal',\n        'NGT - Borderline',\n        'NGT - Incompletely Imaged',\n        'NGT - Normal',\n        'CVC - Abnormal',\n        'CVC - Borderline',\n        'CVC - Normal',\n        'Swan Ganz Catheter Present'\n    ]].rank(pct=True)  # rank\n\ndf_sub[[\n    'ETT - Abnormal',\n    'ETT - Borderline',\n    'ETT - Normal',\n    'NGT - Abnormal',\n    'NGT - Borderline',\n    'NGT - Incompletely Imaged',\n    'NGT - Normal',\n    'CVC - Abnormal',\n    'CVC - Borderline',\n    'CVC - Normal',\n    'Swan Ganz Catheter Present'\n]] = np.stack([this_sub[[\n        'ETT - Abnormal',\n        'ETT - Borderline',\n        'ETT - Normal',\n        'NGT - Abnormal',\n        'NGT - Borderline',\n        'NGT - Incompletely Imaged',\n        'NGT - Normal',\n        'CVC - Abnormal',\n        'CVC - Borderline',\n        'CVC - Normal',\n        'Swan Ganz Catheter Present'\n    ]].values for this_sub in df_subs], 0).mean(0)  # mean\n\n\ndf_sub.to_csv('submission.csv', index=False)","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}