{"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":"# 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":{}},{"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","metadata":{"execution":{"iopub.status.busy":"2021-08-15T11:14:45.258548Z","iopub.execute_input":"2021-08-15T11:14:45.259215Z","iopub.status.idle":"2021-08-15T11:14:45.267944Z","shell.execute_reply.started":"2021-08-15T11:14:45.259049Z","shell.execute_reply":"2021-08-15T11:14:45.266379Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-08-15T11:14:45.270761Z","iopub.execute_input":"2021-08-15T11:14:45.271591Z","iopub.status.idle":"2021-08-15T11:14:53.100105Z","shell.execute_reply.started":"2021-08-15T11:14:45.271547Z","shell.execute_reply":"2021-08-15T11:14:53.098971Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_dir = '../input/50005fold'#修\ndata_dir = '../input/ranzcr-clip-catheter-line-classification'#修\nmodel1_dir = '../input/ranzcr-public-model-qishen' #修\nmodel2_dir = '../input/5000-no-flip-test' #修\nnum_workers = 2\nimage_size = 512\nbatch_size = 8","metadata":{"execution":{"iopub.status.busy":"2021-08-15T11:14:53.10294Z","iopub.execute_input":"2021-08-15T11:14:53.103239Z","iopub.status.idle":"2021-08-15T11:14:53.114072Z","shell.execute_reply.started":"2021-08-15T11:14:53.103209Z","shell.execute_reply":"2021-08-15T11:14:53.112743Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_sub = pd.read_csv(os.path.join(sample_dir, 'a25000.csv')) #修\ndf_sub = df_sub.iloc[:100] #if df_sub.shape[0] == 10000 else df_sub #修","metadata":{"execution":{"iopub.status.busy":"2021-08-15T11:14:53.11779Z","iopub.execute_input":"2021-08-15T11:14:53.1181Z","iopub.status.idle":"2021-08-15T11:14:53.229189Z","shell.execute_reply.started":"2021-08-15T11:14:53.118069Z","shell.execute_reply":"2021-08-15T11:14:53.228212Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Dataset","metadata":{}},{"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, 'train', 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)","metadata":{"execution":{"iopub.status.busy":"2021-08-15T11:14:53.234124Z","iopub.execute_input":"2021-08-15T11:14:53.234516Z","iopub.status.idle":"2021-08-15T11:14:53.24513Z","shell.execute_reply.started":"2021-08-15T11:14:53.234469Z","shell.execute_reply":"2021-08-15T11:14:53.243623Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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))","metadata":{"execution":{"iopub.status.busy":"2021-08-15T11:14:53.247683Z","iopub.execute_input":"2021-08-15T11:14:53.248631Z","iopub.status.idle":"2021-08-15T11:14:55.332658Z","shell.execute_reply.started":"2021-08-15T11:14:53.248561Z","shell.execute_reply":"2021-08-15T11:14:55.33131Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model","metadata":{}},{"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","metadata":{"execution":{"iopub.status.busy":"2021-08-15T11:14:55.334169Z","iopub.execute_input":"2021-08-15T11:14:55.334591Z","iopub.status.idle":"2021-08-15T11:14:55.34825Z","shell.execute_reply.started":"2021-08-15T11:14:55.33455Z","shell.execute_reply":"2021-08-15T11:14:55.34648Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Loading Models","metadata":{}},{"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'","metadata":{"execution":{"iopub.status.busy":"2021-08-15T11:14:55.350438Z","iopub.execute_input":"2021-08-15T11:14:55.351059Z","iopub.status.idle":"2021-08-15T11:14:55.367199Z","shell.execute_reply.started":"2021-08-15T11:14:55.351012Z","shell.execute_reply":"2021-08-15T11:14:55.366315Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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(model1_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(model2_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)","metadata":{"execution":{"iopub.status.busy":"2021-08-15T11:14:55.370414Z","iopub.execute_input":"2021-08-15T11:14:55.371316Z","iopub.status.idle":"2021-08-15T11:15:10.456058Z","shell.execute_reply.started":"2021-08-15T11:14:55.371231Z","shell.execute_reply":"2021-08-15T11:15:10.454717Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Inference","metadata":{}},{"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()","metadata":{"execution":{"iopub.status.busy":"2021-08-15T11:15:10.457842Z","iopub.execute_input":"2021-08-15T11:15:10.458302Z","iopub.status.idle":"2021-08-15T11:15:45.92085Z","shell.execute_reply.started":"2021-08-15T11:15:10.458237Z","shell.execute_reply":"2021-08-15T11:15:45.919834Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Check Distribution","metadata":{}},{"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]])","metadata":{"execution":{"iopub.status.busy":"2021-08-15T11:15:45.922697Z","iopub.execute_input":"2021-08-15T11:15:45.9231Z","iopub.status.idle":"2021-08-15T11:15:46.220027Z","shell.execute_reply.started":"2021-08-15T11:15:45.923062Z","shell.execute_reply":"2021-08-15T11:15:46.218837Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Rank Prediction & Submit","metadata":{}},{"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)","metadata":{"execution":{"iopub.status.busy":"2021-08-15T11:15:46.222302Z","iopub.execute_input":"2021-08-15T11:15:46.222741Z","iopub.status.idle":"2021-08-15T11:15:46.637124Z","shell.execute_reply.started":"2021-08-15T11:15:46.222688Z","shell.execute_reply":"2021-08-15T11:15:46.636174Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"target ","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}