{"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":"code","source":"package_paths = ['../input/pytorch-library/pytorch_library/pytorch-image-models-master',]\nimport sys;\nfor pth in package_paths:\n    sys.path.append(pth)\n# load the external python package\n\n# Machine Learning\nimport torch\nimport pytorch_lightning as pl\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torchvision import transforms as tsfm\nfrom torch.utils.data import Dataset, DataLoader\nfrom pytorch_lightning.metrics import Metric\nimport numpy as np\nimport pandas as pd\n\n\n# Processing Images\nimport matplotlib.pyplot as plt\nimport PIL\nimport os\nimport cv2\nimport plotly.express as px\nimport timm\nfrom PIL import Image\n%matplotlib inline\n\n# Others\nfrom kaggle_datasets import KaggleDatasets\nimport os","metadata":{"execution":{"iopub.status.busy":"2021-09-06T04:49:06.660720Z","iopub.execute_input":"2021-09-06T04:49:06.661107Z","iopub.status.idle":"2021-09-06T04:49:06.671885Z","shell.execute_reply.started":"2021-09-06T04:49:06.661069Z","shell.execute_reply":"2021-09-06T04:49:06.670943Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"\nHypterparameters\n\"\"\"\nclass CFG:\n    # dir\n    test_imgs_dir = \"../input/plant-pathology-2021-fgvc8/test_images\"\n    submit_csv_path = \"../input/plant-pathology-2021-fgvc8/sample_submission.csv\"\n    # data info\n    label_num2str = {0: 'powdery_mildew',\n                     1: 'scab',\n                     2: 'complex',\n                     3: 'frog_eye_leaf_spot',\n                     4: 'rust'}\n    \n    label_str2num = {'powdery_mildew': 0,\n                     'scab': 1,\n                     'complex': 2,\n                     'frog_eye_leaf_spot': 3,\n                     'rust': 4}\n    # model info\n    model_name = 'tf_efficientnet_b2_ns'\n    pretrained_dir = '../input/efficientnet-pp-train-weights'\n    which_to_load = 'best_perform'  # last or best_perform\n    cross_validation_k = 2\n    needed_fold = range(cross_validation_k)\n    #\n    seed = 77\n    num_classes = 5\n    img_size = [224, 224]\n    tta_step = 0\n    threshold = [0.5, 0.5, 0.5, 0.5, 0.5]","metadata":{"execution":{"iopub.status.busy":"2021-09-06T04:49:06.673441Z","iopub.execute_input":"2021-09-06T04:49:06.673800Z","iopub.status.idle":"2021-09-06T04:49:06.682869Z","shell.execute_reply.started":"2021-09-06T04:49:06.673762Z","shell.execute_reply":"2021-09-06T04:49:06.682003Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"\nDefine dataset class\n\"\"\"\n\nclass PlantDataset(Dataset):\n    def __init__(self, img_dir, img_names: list, labels: list, transform=None):\n        self.img_dir = img_dir\n        self.img_names = img_names\n        self.labels = labels\n        self.transform = transform\n    \n    def __len__(self):\n        return len(self.img_names)\n    \n    def __getitem__(self, idx):\n        img_path = os.path.join(self.img_dir, self.img_names[idx])\n        img = Image.open(img_path).convert('RGB')\n        img_ts = self.transform(img)\n        label_ts = self.labels[idx]\n        return img_ts, label_ts","metadata":{"execution":{"iopub.status.busy":"2021-09-06T04:49:06.684887Z","iopub.execute_input":"2021-09-06T04:49:06.685347Z","iopub.status.idle":"2021-09-06T04:49:06.694986Z","shell.execute_reply.started":"2021-09-06T04:49:06.685311Z","shell.execute_reply":"2021-09-06T04:49:06.694268Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"\nDefine test image transformation\n\"\"\"\n\ntest_transform_normal = tsfm.Compose([tsfm.Resize(CFG.img_size),\n                                      tsfm.ToTensor(),\n                                      tsfm.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),])\n\ntest_transform_tta =  tsfm.Compose([tsfm.Resize(CFG.img_size),\n                                    tsfm.RandomApply([tsfm.ColorJitter(0.2, 0.2, 0.2),tsfm.RandomPerspective(distortion_scale=0.2),], p=0.5),\n                                    tsfm.RandomApply([tsfm.ColorJitter(0.2, 0.2, 0.2),tsfm.RandomAffine(degrees=10),], p=0.5),\n                                    tsfm.RandomVerticalFlip(p=0.5),\n                                    tsfm.RandomHorizontalFlip(p=0.5),\n                                    tsfm.ToTensor(),\n                                    tsfm.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)), ])","metadata":{"execution":{"iopub.status.busy":"2021-09-06T04:49:06.696190Z","iopub.execute_input":"2021-09-06T04:49:06.696536Z","iopub.status.idle":"2021-09-06T04:49:06.705083Z","shell.execute_reply.started":"2021-09-06T04:49:06.696501Z","shell.execute_reply":"2021-09-06T04:49:06.704103Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"\nInit dataset instance and dataloader\n\"\"\"\ntest_img_names = os.listdir(CFG.test_imgs_dir)\nif CFG.tta_step > 0:\n    print(\"Using TTA, TTA step is: \", CFG.tta_step)\n    test_dataset = PlantDataset(CFG.test_imgs_dir, test_img_names, range(len(test_img_names)), test_transform_tta)\nelse:\n    print(\"Not using TTA\")\n    test_dataset = PlantDataset(CFG.test_imgs_dir, test_img_names, range(len(test_img_names)), test_transform_normal)\n\ntest_loader = DataLoader(test_dataset, batch_size=4, num_workers=4, shuffle=False, drop_last=False)","metadata":{"execution":{"iopub.status.busy":"2021-09-06T04:49:06.706615Z","iopub.execute_input":"2021-09-06T04:49:06.706968Z","iopub.status.idle":"2021-09-06T04:49:06.723915Z","shell.execute_reply.started":"2021-09-06T04:49:06.706933Z","shell.execute_reply":"2021-09-06T04:49:06.722889Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"\nDefine Focal-Loss\n\"\"\"\n\nclass FocalLoss(nn.Module):\n    \"\"\"\n    The focal loss for fighting against class-imbalance\n    \"\"\"\n    def __init__(self, alpha=1, gamma=2):\n        super(FocalLoss, self).__init__()\n        self.alpha = alpha\n        self.gamma = gamma\n        self.epsilon = 1e-12  # prevent training from Nan-loss error \n    \n    def forward(self, logits, target):\n        \"\"\"\n        logits & target should be tensors with shape [batch_size, num_classes]\n        \"\"\"\n        probs = F.sigmoid(logits)\n        one_subtract_probs = 1.0 - probs\n        # add epsilon\n        probs_new = probs + self.epsilon\n        one_subtract_probs_new = one_subtract_probs + self.epsilon\n        # calculate focal loss\n        log_pt =  target * torch.log(probs_new) + (1.0 - target) * torch.log(one_subtract_probs_new)\n        pt = torch.exp(log_pt)\n        focal_loss = -1.0 * (self.alpha * (1 - pt) ** self.gamma) * log_pt\n        return torch.mean(focal_loss)\n        ","metadata":{"execution":{"iopub.status.busy":"2021-09-06T04:49:06.725604Z","iopub.execute_input":"2021-09-06T04:49:06.725983Z","iopub.status.idle":"2021-09-06T04:49:06.735388Z","shell.execute_reply.started":"2021-09-06T04:49:06.725949Z","shell.execute_reply":"2021-09-06T04:49:06.734407Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"\nDefine F1 score metric\n\"\"\"\nclass MyF1Score(Metric):\n    def __init__(self, cfg, threshold: float=0.5, dist_sync_on_step=False):\n        super().__init__(dist_sync_on_step=dist_sync_on_step)\n        self.cfg = cfg\n        self.threshold = threshold\n        self.add_state(\"tp\", default=torch.tensor(0), dist_reduce_fx=\"sum\")\n        self.add_state(\"fp\", default=torch.tensor(0), dist_reduce_fx=\"sum\")\n        self.add_state(\"fn\", default=torch.tensor(0), dist_reduce_fx=\"sum\")\n\n    def update(self, preds: torch.Tensor, target: torch.Tensor):\n        assert preds.shape == target.shape\n        preds_str_batch = self.num_to_str(preds)\n        target_str_batch = self.num_to_str(target)\n        tp, fp, fn = 0, 0, 0\n        for pred_str_list, target_str_list in zip(preds_str_batch, target_str_batch):\n            for pred_str in pred_str_list:\n                if pred_str in target_str_list:\n                    tp += 1\n                if pred_str not in target_str_list:\n                    fp += 1\n            \n            for target_str in target_str_list:\n                if target_str not in pred_str_list:\n                    fn += 1\n        self.tp += tp\n        self.fp += fp\n        self.fn += fn\n\n    def compute(self):\n        f1 = 2.0 * self.tp / (2.0 * self.tp + self.fn + self.fp)\n        return f1\n    \n    def num_to_str(self, ts: torch.Tensor) -> list:\n        batch_bool_list = (ts > self.threshold).detach().cpu().numpy().tolist()\n        batch_str_list = []\n        for one_sample_bool in batch_bool_list:\n            lb_str_list = [self.cfg.label_num2str[lb_idx] for lb_idx, bool_val in enumerate(one_sample_bool) if bool_val]\n            if len(lb_str_list) == 0:\n                lb_str_list = ['healthy']\n            batch_str_list.append(lb_str_list)\n        return batch_str_list","metadata":{"execution":{"iopub.status.busy":"2021-09-06T04:49:06.737544Z","iopub.execute_input":"2021-09-06T04:49:06.738312Z","iopub.status.idle":"2021-09-06T04:49:06.752265Z","shell.execute_reply.started":"2021-09-06T04:49:06.738274Z","shell.execute_reply":"2021-09-06T04:49:06.751459Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"\nDefine neural network model\n\"\"\"\n\nclass MyNetwork(pl.LightningModule):\n    def __init__(self, cfg):\n        super(MyNetwork, self).__init__()\n        self.cfg = cfg\n        self.model = timm.create_model(cfg.model_name, pretrained=False, num_classes=cfg.num_classes)\n        self.criterion = FocalLoss()\n        self.metric = self.metric = MyF1Score(cfg)\n       \n    def forward(self, x):\n        return self.model(x)\n    \n    def configure_optimizers(self):\n        self.optimizer = torch.optim.Adam(self.model.parameters(), lr=self.cfg.lr)\n        self.scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(self.optimizer,\n                                                                    T_max=self.cfg.t_max,\n                                                                    eta_min=self.cfg.min_lr,\n                                                                    verbose=True)\n        return {'optimizer': self.optimizer, 'lr_scheduler': self.scheduler}\n    \n    def training_step(self, batch, batch_idx):\n        img_ts, lb_ts = batch\n        pred_ts = self.model(img_ts)\n        loss = self.criterion(pred_ts, lb_ts)\n        score = self.metric(pred_ts, lb_ts)\n        logs = {'train_loss': loss, 'train_f1': score, 'lr': self.optimizer.param_groups[0]['lr']}\n        self.log_dict(logs, on_step=False, on_epoch=True, prog_bar=True, logger=True)\n        return loss\n    \n    def validation_step(self, batch, batch_idx):\n        img_ts, lb_ts = batch\n        pred_ts = self.model(img_ts)\n        loss = self.criterion(pred_ts, lb_ts)\n        score = self.metric(pred_ts, lb_ts)\n        logs = {'valid_loss': loss, 'valid_f1': score}\n        self.log_dict(logs, on_step=False, on_epoch=True, prog_bar=True, logger=True)\n        return loss","metadata":{"execution":{"iopub.status.busy":"2021-09-06T04:49:06.754259Z","iopub.execute_input":"2021-09-06T04:49:06.754535Z","iopub.status.idle":"2021-09-06T04:49:06.767904Z","shell.execute_reply.started":"2021-09-06T04:49:06.754512Z","shell.execute_reply":"2021-09-06T04:49:06.767082Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"\nInit models\n\"\"\"\nmodels_list = []\nfor fold_idx in CFG.needed_fold:\n    ckpt_path = os.path.join(CFG.pretrained_dir,\n                             f\"fold{fold_idx}_logs/{CFG.model_name}/version_0/checkpoints/{CFG.which_to_load}.ckpt\")\n    \n    model = MyNetwork.load_from_checkpoint(ckpt_path, cfg=CFG)\n    model.cuda()\n    model.eval()\n    models_list.append(model)\n    \n    ","metadata":{"execution":{"iopub.status.busy":"2021-09-06T04:49:06.769091Z","iopub.execute_input":"2021-09-06T04:49:06.769426Z","iopub.status.idle":"2021-09-06T04:49:12.732079Z","shell.execute_reply.started":"2021-09-06T04:49:06.769391Z","shell.execute_reply":"2021-09-06T04:49:12.731186Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"threshold = np.array([CFG.threshold])\nsubmit_df = pd.read_csv(CFG.submit_csv_path)\n\ndef convert_num_to_str(pred: np.ndarray) -> str:\n    \"\"\"convert the numerical labels to string labels\"\"\"\n    lb_str_list = []\n    for lb_idx, bool_val in enumerate(pred):\n        if bool_val:\n            lb_str = CFG.label_num2str[lb_idx]\n            lb_str_list.append(lb_str)\n    if len(lb_str_list) == 0:\n        final_label = 'healthy'\n    else:\n        final_label = ' '.join(lb_str_list)\n    return final_label\n\n\nwith torch.no_grad():\n    test_img_idx = 0\n    pred_list_all = []\n    logit_list_all = []\n    for img_ts, lb_ts in test_loader:\n        img_ts = img_ts.cuda()\n        n_fold_pred_list = []\n        for model in models_list:\n            pred_ts = torch.sigmoid(model(img_ts)).detach().cpu()\n            pred_ts = pred_ts.unsqueeze(2)\n            #print(pred_ts.size())\n            n_fold_pred_list.append(pred_ts)\n        pred_np = torch.cat(n_fold_pred_list, axis=2).mean(dim=2).numpy()\n        pred = (pred_np > threshold).tolist()\n        logit_list_all.append(pred_np)\n        pred_list_all.append(pred)\n    pred_np_all = np.concatenate(pred_list_all, axis=0)\n    logit_np_all = np.concatenate(logit_list_all, axis=0)\n\nprint(logit_np_all)\nprint(pred_np_all)","metadata":{"execution":{"iopub.status.busy":"2021-09-06T04:49:12.733279Z","iopub.execute_input":"2021-09-06T04:49:12.733592Z","iopub.status.idle":"2021-09-06T04:49:14.729332Z","shell.execute_reply.started":"2021-09-06T04:49:12.733560Z","shell.execute_reply":"2021-09-06T04:49:14.728298Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for test_img_idx, pred in enumerate(pred_np_all):\n    # convert numerical label into string\n    final_label = convert_num_to_str(pred)\n    img_name = test_img_names[test_img_idx]\n    row_idx = submit_df[submit_df.image == img_name].index.tolist()[0]\n    submit_df.iloc[row_idx, 1] = final_label\n    test_img_idx += 1","metadata":{"execution":{"iopub.status.busy":"2021-09-06T04:49:14.731648Z","iopub.execute_input":"2021-09-06T04:49:14.732077Z","iopub.status.idle":"2021-09-06T04:49:14.747610Z","shell.execute_reply.started":"2021-09-06T04:49:14.732031Z","shell.execute_reply":"2021-09-06T04:49:14.746741Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# save prediction into csv file\nsubmit_df.to_csv(\"./submission.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2021-09-06T04:49:14.750606Z","iopub.execute_input":"2021-09-06T04:49:14.750885Z","iopub.status.idle":"2021-09-06T04:49:14.763367Z","shell.execute_reply.started":"2021-09-06T04:49:14.750837Z","shell.execute_reply":"2021-09-06T04:49:14.762566Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submit_df","metadata":{"execution":{"iopub.status.busy":"2021-09-06T04:49:14.765516Z","iopub.execute_input":"2021-09-06T04:49:14.766005Z","iopub.status.idle":"2021-09-06T04:49:14.782057Z","shell.execute_reply.started":"2021-09-06T04:49:14.765970Z","shell.execute_reply":"2021-09-06T04:49:14.781316Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Mapping Labels","metadata":{}}]}