{"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":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nfrom PIL import Image\nimport cv2\nimport matplotlib.pyplot as plt\nimport sklearn\nfrom sklearn.model_selection import StratifiedKFold\nfrom glob import glob\n\nimport pytorch_lightning as pl\nfrom pytorch_lightning import Trainer, seed_everything\nfrom pytorch_lightning import Callback\nfrom pytorch_lightning.loggers import CSVLogger\nfrom pytorch_lightning.callbacks import ModelCheckpoint, EarlyStopping\nfrom pytorch_lightning.metrics.classification import AUROC\n\nimport torch\nfrom torch.utils.data import DataLoader, Dataset\nfrom torchvision.models import resnet18\n\nimport albumentations as A\nfrom albumentations.pytorch.transforms import ToTensorV2\n\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\n\nRANDOM_SEED = 42\nTRAIN_BATCH = 4\nWORKING_DIR = \"/kaggle/input/plant-pathology-2021-fgvc8/\"\nMODELS_DIR = '/kaggle/working'\nK_FOLD_NUM = 1\nCLASSES_NUM = 6\n\nWEIGHT_DECAY = 1e-5\nEPOCH_NUM = 20\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n# for dirname, _, filenames in os.walk('/kaggle/input'):\n#     for filename in filenames:\n#         print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-05-20T13:53:58.203032Z","iopub.execute_input":"2021-05-20T13:53:58.203409Z","iopub.status.idle":"2021-05-20T13:54:03.917598Z","shell.execute_reply.started":"2021-05-20T13:53:58.203378Z","shell.execute_reply":"2021-05-20T13:54:03.916209Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = pd.read_csv(WORKING_DIR + \"train.csv\")\n\nprint(train_df.head())\ntrain_df[\"labels\"].value_counts()","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:54:03.919342Z","iopub.execute_input":"2021-05-20T13:54:03.919661Z","iopub.status.idle":"2021-05-20T13:54:03.98866Z","shell.execute_reply.started":"2021-05-20T13:54:03.91963Z","shell.execute_reply":"2021-05-20T13:54:03.987676Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"label_name = \"scab frog_eye_leaf_spot complex\"\ntrain_img_names = train_df.loc[train_df[\"labels\"] == label_name].head().image\n\nsubplot_num = 511\nfor name in train_img_names:\n    train_img_path = WORKING_DIR + \"train_images/\" + name\n    print(train_img_path)\n\n    im = cv2.imread(train_img_path) \n#     plt.subplot(subplot_num)\n    plt.title(name + \" label: \" + label_name)\n    plt.imshow(im)\n    plt.show()\n    subplot_num += 1\n\n","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:54:03.990916Z","iopub.execute_input":"2021-05-20T13:54:03.991221Z","iopub.status.idle":"2021-05-20T13:54:10.97852Z","shell.execute_reply.started":"2021-05-20T13:54:03.991192Z","shell.execute_reply":"2021-05-20T13:54:10.97732Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)\n\ndf_folds = train_df[['image']].copy()\n\ndf_folds = df_folds.groupby('image').count()\ndf_folds.loc[:, 'labels'] = train_df[['image', 'labels']].groupby('image').min()['labels']\ndf_folds.loc[:, 'stratify_group'] = np.char.add(\n    df_folds['labels'].apply(lambda x: \"_\".join(x.split(\" \"))).values.astype(str), \n    df_folds['labels'].apply(lambda x: len(x.split(\" \"))).values.astype(str)\n)\ndf_folds.loc[:, 'fold'] = 0\n\nfor fold_number, (train_index, val_index) in enumerate(skf.split(X=df_folds.index, y=df_folds['stratify_group'])):\n    df_folds.loc[df_folds.iloc[val_index].index, 'fold'] = fold_number\n\ndf_folds.reset_index(inplace=True)\n\nfold_number = K_FOLD_NUM\ntrain_df_raw = train_df.copy()\n","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:54:10.980339Z","iopub.execute_input":"2021-05-20T13:54:10.980634Z","iopub.status.idle":"2021-05-20T13:54:12.949373Z","shell.execute_reply.started":"2021-05-20T13:54:10.980603Z","shell.execute_reply":"2021-05-20T13:54:12.948313Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_folds.loc[df_folds[\"fold\"] == 0][\"labels\"].value_counts()","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:54:12.950855Z","iopub.execute_input":"2021-05-20T13:54:12.951173Z","iopub.status.idle":"2021-05-20T13:54:12.981296Z","shell.execute_reply.started":"2021-05-20T13:54:12.95114Z","shell.execute_reply":"2021-05-20T13:54:12.98016Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"valid_df = train_df_raw.loc[train_df_raw['image'].isin(df_folds[df_folds['fold'] == fold_number].image)].copy()\ntrain_df = train_df_raw.loc[train_df_raw['image'].isin(df_folds[df_folds['fold'] != fold_number].image)].copy()\n\ntrain_df.reset_index(drop=True, inplace=True)\nvalid_df.reset_index(drop=True, inplace=True)","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:54:12.983323Z","iopub.execute_input":"2021-05-20T13:54:12.983807Z","iopub.status.idle":"2021-05-20T13:54:13.005362Z","shell.execute_reply.started":"2021-05-20T13:54:12.983759Z","shell.execute_reply":"2021-05-20T13:54:13.004243Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.labels.value_counts()","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:54:13.006958Z","iopub.execute_input":"2021-05-20T13:54:13.007369Z","iopub.status.idle":"2021-05-20T13:54:13.019381Z","shell.execute_reply.started":"2021-05-20T13:54:13.007325Z","shell.execute_reply":"2021-05-20T13:54:13.0184Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class PlantDataset(Dataset):\n    def __init__(self, df, dir_path, training=True):\n        \n        self.dir_path = dir_path\n        self.df = df\n        self.img_ids = self.df.image.unique()\n        self.training = training\n        \n    def __getitem__(self, index):\n        img_id = self.img_ids[index]\n        target = self.df[self.df[\"image\"] == img_id].labels.iloc[0]\n        target = self.encode_target(target)\n        \n        image = cv2.imread(self.dir_path + img_id, cv2.IMREAD_COLOR)\n        \n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32)\n         \n        image = self.transform()(image=image)[\"image\"]\n        \n        return {\"image\": image, \"target\": torch.FloatTensor(target), \"img_id\": img_id}\n    \n    def get_by_id(self,img_id):\n        index = np.where(self.img_ids == img_id)[0][0]\n        return self.__getitem__(index)\n    \n    def transform(self):\n        if self.training:\n            transforms = A.Compose([              \n                A.Cutout(num_holes=8, max_h_size=32, max_w_size=32, fill_value=0, p=0.5),\n                A.Blur(p=0.5),\n                A.Resize(512,512),\n                A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n                ToTensorV2(p=1.0) \n            ])\n        else:\n            transforms = A.Compose([\n                A.Resize(512,512),\n                A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n                ToTensorV2(p=1.0) \n            ])\n            \n        return transforms\n    \n    def encode_target(self, target):\n#         scab healthy frog_eye_leaf_spot rust complex powdery_mildew    \n        encoded = list(map(int, ['scab' in target, \"healthy\" in target, \"frog_eye_leaf_spot\" in target, \"rust\" in target,  \"complex\" in target, \"powdery_mildew\" in target]))\n        \n        return encoded\n    \n    def __len__(self):\n        return self.img_ids.shape[0]\n       ","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:54:13.022218Z","iopub.execute_input":"2021-05-20T13:54:13.022583Z","iopub.status.idle":"2021-05-20T13:54:13.037881Z","shell.execute_reply.started":"2021-05-20T13:54:13.02254Z","shell.execute_reply":"2021-05-20T13:54:13.036787Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# train_df.count()\n# train_path = WORKING_DIR + \"train_images/\"\ntrain_path = \"/kaggle/input/resized-plant2021/img_sz_512/\"\ntrain_dataset = PlantDataset(train_df, train_path)\nvalid_dataset = PlantDataset(valid_df, train_path, training=False)\n\n# sample = train_dataset[0]\n# print(sample)\n# # print(torch.histc(train_dataset[3][\"image\"][0]))\n# plt.hist(sample[\"image\"][0])\n# plt.show()\n# plt.hist(sample[\"image\"][1])\n# plt.show()\n# plt.hist(sample[\"image\"][2])\n# plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:54:13.039694Z","iopub.execute_input":"2021-05-20T13:54:13.040052Z","iopub.status.idle":"2021-05-20T13:54:13.060453Z","shell.execute_reply.started":"2021-05-20T13:54:13.040018Z","shell.execute_reply":"2021-05-20T13:54:13.059138Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"subplot_num = 511\n\n# train_iter = iter(train_dataset)\nfor i in range(3):\n    img_dict = train_dataset[i]\n\n    img = img_dict[\"image\"]\n#     print(img)\n    name = img_dict[\"img_id\"]\n\n    train_img_path = WORKING_DIR + \"train_images/\" + name\n    print(img, train_img_path)\n\n    im = cv2.imread(train_img_path) \n    plt.figure(figsize=(64,64))\n    plt.subplot(subplot_num)\n    plt.title(name )\n    plt.imshow(img.permute(1, 2, 0 ).numpy())\n#     plt.imshow(img.permute(1, 2, 0 ))\n#     plt.show()\n    subplot_num += 1\n# plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:54:13.062181Z","iopub.execute_input":"2021-05-20T13:54:13.062494Z","iopub.status.idle":"2021-05-20T13:54:15.139026Z","shell.execute_reply.started":"2021-05-20T13:54:13.062465Z","shell.execute_reply":"2021-05-20T13:54:15.137953Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataset.get_by_id(\"8002cb321f8bfcdf.jpg\")","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:54:15.140381Z","iopub.execute_input":"2021-05-20T13:54:15.140757Z","iopub.status.idle":"2021-05-20T13:54:15.167534Z","shell.execute_reply.started":"2021-05-20T13:54:15.140664Z","shell.execute_reply":"2021-05-20T13:54:15.166814Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# def collate_fn(batch):\n#     return tuple(zip(*batch))\n\ntrain_data_loader = DataLoader(\n    train_dataset,\n    batch_size=TRAIN_BATCH,\n    shuffle=True,\n    num_workers=4,\n#     collate_fn=collate_fn\n)\n\nvalid_data_loader = DataLoader(\n    valid_dataset,\n    batch_size=TRAIN_BATCH,\n    shuffle=False,\n    num_workers=4,\n#     collate_fn=collate_fn\n)\n# x, y, z = next(iter(train_data_loader))\n# print(x,y,z)","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:54:15.168841Z","iopub.execute_input":"2021-05-20T13:54:15.169138Z","iopub.status.idle":"2021-05-20T13:54:15.17467Z","shell.execute_reply.started":"2021-05-20T13:54:15.169108Z","shell.execute_reply":"2021-05-20T13:54:15.173257Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"### Lightning usage\n\nclass LitModel(pl.LightningModule):\n    def __init__(self, model):\n        super(LitModel, self).__init__()\n        self.model = model\n        self.metric = pl.metrics.F1(CLASSES_NUM=CLASSES_NUM)\n        self.criterion = torch.nn.BCEWithLogitsLoss()\n        self.lr = 5e-3\n        self.threshold = 0.5\n        \n    def forward(self, x, *args, **kwargs):\n        return self.model(x)\n    \n    def configure_optimizers(self):\n        self.optimizer = torch.optim.Adam(self.parameters(), lr=self.lr,weight_decay=WEIGHT_DECAY)\n        self.scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(self.optimizer, T_max=20, eta_min=1e-6)\n        \n        return {\"optimizer\": self.optimizer, \"lr_scheduler\": self.scheduler}\n    \n    def training_step(self, batch, batch_idx):\n        image = batch[\"image\"]\n        target = batch[\"target\"]\n        \n        output = self.model(image)\n        loss = self.criterion(output, target)\n        metric = self.metric(output, target)\n        \n        logs = {\"training_loss\": loss, \"train_f1\": metric, \"lr\": self.optimizer.param_groups[0][\"lr\"]}\n        \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        image = batch[\"image\"]\n        target = batch[\"target\"]\n        \n        output = self.model(image)\n        loss = self.criterion(output, target)\n        logits = torch.nn.Sigmoid()(output.detach())\n        preds = logits > self.threshold\n        preds = preds.double()\n        \n#         if torch.any(torch.all(preds == 0, dim=1)):\n#             preds = self.fill_zero_preds(logits, preds)\n                \n        metric = self.metric(preds, target)\n        \n        logs = {\"valid_loss\": loss, \"valid_f1\": metric}\n        \n        self.log_dict(logs, on_step=False, on_epoch=True, prog_bar=True, logger=True)\n        return logs\n    \n    def test_step(self, batch, batch_idx):\n        metrics = self.validation_step(batch, batch_idx)\n        metrics = {'test_f1': metrics['valid_f1'], 'test_loss': metrics['valid_loss']}\n        self.log_dict(metrics)\n        \n    def fill_zero_preds(self, logits, preds):       \n        idx = torch.argmax(preds, dim=-1)\n        mask = torch.zeros(preds.shape)\n        mask[(torch.arange(preds.shape[0]), idx)] = 1\n\n        result = mask + preds\n        preds_no_zeros = torch.where(result > 1., 1., result.double())\n            \n        return preds_no_zeros","metadata":{"execution":{"iopub.status.busy":"2021-05-20T15:04:50.825043Z","iopub.execute_input":"2021-05-20T15:04:50.825572Z","iopub.status.idle":"2021-05-20T15:04:50.841374Z","shell.execute_reply.started":"2021-05-20T15:04:50.825533Z","shell.execute_reply":"2021-05-20T15:04:50.840698Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = resnet18(pretrained=True)\n# dir(model)","metadata":{"execution":{"iopub.status.busy":"2021-05-20T14:36:48.487801Z","iopub.execute_input":"2021-05-20T14:36:48.488213Z","iopub.status.idle":"2021-05-20T14:36:48.915102Z","shell.execute_reply.started":"2021-05-20T14:36:48.488181Z","shell.execute_reply":"2021-05-20T14:36:48.914271Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.add_module(name=\"fc\", module=torch.nn.Linear(in_features=512, out_features=6, bias=True))\n\nprint(next(model.modules()))\n","metadata":{"execution":{"iopub.status.busy":"2021-05-20T14:36:53.813736Z","iopub.execute_input":"2021-05-20T14:36:53.814133Z","iopub.status.idle":"2021-05-20T14:36:53.82271Z","shell.execute_reply.started":"2021-05-20T14:36:53.814098Z","shell.execute_reply":"2021-05-20T14:36:53.821596Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# TESTING\n# path = \"../input/pp21-resnet-e10-fold1/pp21_resnet_e10_fold1--valid_f18661.ckpt\"\n# lit_model = LitModel(model).load_from_checkpoint(path,model=model)\n\n# logger = CSVLogger(save_dir='logs_test/', name=\"Resnet\")\n# trainer = Trainer(deterministic=True, logger=logger)\n# trainer.test(model=lit_model,test_dataloaders=valid_data_loader,verbose=True)","metadata":{"execution":{"iopub.status.busy":"2021-05-20T15:04:57.94294Z","iopub.execute_input":"2021-05-20T15:04:57.943632Z","iopub.status.idle":"2021-05-20T15:20:01.096618Z","shell.execute_reply.started":"2021-05-20T15:04:57.94358Z","shell.execute_reply":"2021-05-20T15:20:01.095492Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"lit_model = LitModel(model)","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:53:53.674441Z","iopub.status.idle":"2021-05-20T13:53:53.675267Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"logger = CSVLogger(save_dir='logs/', name=\"Resnet\")\ncheckpoint_callback = ModelCheckpoint(monitor='valid_loss',\n                                      save_top_k=1,\n                                      save_last=True,\n                                      save_weights_only=True,\n                                      filename='checkpoint/{fold:02d}-{epoch:02d}-{valid_loss:.4f}-{valid_f1:.4f}',\n                                      verbose=False,\n                                      mode='min')\n\ntrainer = Trainer(\n    max_epochs=EPOCH_NUM,\n    gpus=1,\n    accumulate_grad_batches=1,\n    precision=16,\n    # callbacks=[EarlyStopping(monitor='valid_loss', patience=3, mode='min')],\n    checkpoint_callback=checkpoint_callback,\n    logger=logger,\n    weights_summary='top',\n    profiler=\"simple\"\n)","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:53:53.676507Z","iopub.status.idle":"2021-05-20T13:53:53.677324Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"trainer.fit(lit_model, train_dataloader=train_data_loader, val_dataloaders=valid_data_loader)","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:53:53.678428Z","iopub.status.idle":"2021-05-20T13:53:53.679091Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"metrics = pd.read_csv(f'{trainer.logger.log_dir}/metrics.csv')\n\ntrain_acc = metrics['train_f1'].dropna().reset_index(drop=True)\nvalid_acc = metrics['valid_f1'].dropna().reset_index(drop=True)\n    \nfig = plt.figure(figsize=(7, 6))\nplt.grid(True)\nplt.plot(train_acc, color=\"r\", marker=\"o\", label='train/f1')\nplt.plot(valid_acc, color=\"b\", marker=\"x\", label='valid/f1')\nplt.ylabel('F1', fontsize=24)\nplt.xlabel('Epoch', fontsize=24)\nplt.legend(loc='lower right', fontsize=18)\nplt.savefig(f'{trainer.logger.log_dir}/f1.png')\n\ntrain_loss = metrics['training_loss'].dropna().reset_index(drop=True)\nvalid_loss = metrics['valid_loss'].dropna().reset_index(drop=True)\n\nfig = plt.figure(figsize=(7, 6))\nplt.grid(True)\nplt.plot(train_loss, color=\"r\", marker=\"o\", label='train/loss')\nplt.plot(valid_loss, color=\"b\", marker=\"x\", label='valid/loss')\nplt.ylabel('Loss', fontsize=24)\nplt.xlabel('Epoch', fontsize=24)\nplt.legend(loc='upper right', fontsize=18)\nplt.savefig(f'{trainer.logger.log_dir}/loss.png')\\\n\nlr = metrics['lr'].dropna().reset_index(drop=True)\n\nfig = plt.figure(figsize=(7, 6))\nplt.grid(True)\nplt.plot(lr, color=\"g\", marker=\"o\", label='learning rate')\nplt.ylabel('LR', fontsize=24)\nplt.xlabel('Epoch', fontsize=24)\nplt.legend(loc='upper right', fontsize=18)\nplt.savefig(f'{trainer.logger.log_dir}/lr.png')","metadata":{"execution":{"iopub.status.busy":"2021-05-20T13:53:53.680572Z","iopub.status.idle":"2021-05-20T13:53:53.6812Z"},"trusted":true},"execution_count":null,"outputs":[]}]}