{"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":"# References\n1. [Plant Pathology with Lightning ⚡ : by Jirka](https://www.kaggle.com/jirkaborovec/plant-pathology-with-lightning)\n2. [Plant Pathology - PyTorch Lightning ⚡️: by Aniket](https://www.kaggle.com/aniketmaurya/plant-pathology-pytorch-lightning/comments) ","metadata":{}},{"cell_type":"markdown","source":"Installation (you might have to restart the kernel)\n```\n!pip install -U 'lightning-flash[image]'==0.5.0rc0 -q\n!pip install -U torchvision\n!pip install -U torchtext\n```","metadata":{}},{"cell_type":"code","source":"!pip install -U 'lightning-flash[image]'==0.5.0rc0 -q\n!pip install -U torchvision\n!pip install -U torchtext","metadata":{"execution":{"iopub.status.busy":"2021-09-05T05:46:13.854808Z","iopub.execute_input":"2021-09-05T05:46:13.855264Z","iopub.status.idle":"2021-09-05T05:46:13.862867Z","shell.execute_reply.started":"2021-09-05T05:46:13.855143Z","shell.execute_reply":"2021-09-05T05:46:13.862019Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport matplotlib.pyplot as plt\nfrom sklearn.preprocessing import MultiLabelBinarizer\n\nimport flash\nfrom flash.image import ImageClassificationData, ImageClassifier\n\nfrom pytorch_lightning.callbacks import ModelCheckpoint, EarlyStopping\nfrom pytorch_lightning.metrics import FBeta\nfrom pytorch_lightning.loggers import CSVLogger, TensorBoardLogger\n\n\nimport torch\nimport torchmetrics\nimport torchvision\nfrom torch import nn\nfrom torch.nn import functional as F\n\nimport os\nfrom glob import glob\nfrom tqdm import tqdm\n\nimport torch\nfrom torch.utils.data import DataLoader, Dataset\n\nfrom PIL import Image\n\nfrom torchvision import transforms\nfrom pathlib import Path","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.status.busy":"2021-09-05T05:46:14.083598Z","iopub.execute_input":"2021-09-05T05:46:14.083876Z","iopub.status.idle":"2021-09-05T05:46:19.886287Z","shell.execute_reply.started":"2021-09-05T05:46:14.083849Z","shell.execute_reply":"2021-09-05T05:46:19.885351Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_dir = Path(\"/kaggle/input/plant-pathology-2021-fgvc8/\")","metadata":{"execution":{"iopub.status.busy":"2021-09-05T05:46:19.887777Z","iopub.execute_input":"2021-09-05T05:46:19.888117Z","iopub.status.idle":"2021-09-05T05:46:19.893062Z","shell.execute_reply.started":"2021-09-05T05:46:19.888080Z","shell.execute_reply":"2021-09-05T05:46:19.892273Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.read_csv(data_dir/'train.csv')\ndf['label_org'] = df.labels.values\ndf.labels = df.labels.str.split()\n\ndf.head()","metadata":{"execution":{"iopub.status.busy":"2021-09-05T05:46:19.895024Z","iopub.execute_input":"2021-09-05T05:46:19.895615Z","iopub.status.idle":"2021-09-05T05:46:19.946658Z","shell.execute_reply.started":"2021-09-05T05:46:19.895551Z","shell.execute_reply":"2021-09-05T05:46:19.945715Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ref: Jirka\nimport itertools\nimport seaborn as sns\n\nlabels_all = list(itertools.chain(*[lbs.split(\" \") for lbs in df['label_org']]))\n\nax = sns.countplot(y=sorted(labels_all), orient='v')\nax.grid()","metadata":{"execution":{"iopub.status.busy":"2021-09-05T05:46:19.948344Z","iopub.execute_input":"2021-09-05T05:46:19.948691Z","iopub.status.idle":"2021-09-05T05:46:20.185752Z","shell.execute_reply.started":"2021-09-05T05:46:19.948657Z","shell.execute_reply":"2021-09-05T05:46:20.184775Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BS = 32\nIMAGE_SIZE = 128","metadata":{"execution":{"iopub.status.busy":"2021-09-05T05:46:20.187274Z","iopub.execute_input":"2021-09-05T05:46:20.187628Z","iopub.status.idle":"2021-09-05T05:46:20.192058Z","shell.execute_reply.started":"2021-09-05T05:46:20.187590Z","shell.execute_reply":"2021-09-05T05:46:20.190965Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ref: Jirka\nfrom torchvision import transforms as T\n\nTRAIN_TRANSFORM = T.Compose([\n    T.Resize(256),\n    T.RandomPerspective(),\n    T.RandomResizedCrop(IMAGE_SIZE),\n    T.RandomHorizontalFlip(),\n    T.RandomVerticalFlip(),\n    T.ToTensor(),\n    T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),\n])\n\nVALID_TRANSFORM = T.Compose([\n    T.Resize(256),\n    T.CenterCrop(IMAGE_SIZE),\n    T.ToTensor(),\n    T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),\n])\n\nTEST_TRANSFORM = T.Compose([\n    T.Resize(256),\n    T.CenterCrop(IMAGE_SIZE),\n    T.ToTensor(),\n    T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),\n])","metadata":{"execution":{"iopub.status.busy":"2021-09-05T05:46:20.193513Z","iopub.execute_input":"2021-09-05T05:46:20.193852Z","iopub.status.idle":"2021-09-05T05:46:20.431019Z","shell.execute_reply.started":"2021-09-05T05:46:20.193817Z","shell.execute_reply":"2021-09-05T05:46:20.430165Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"mlb = MultiLabelBinarizer(sparse_output=True)\nmlb = mlb.fit(df.labels)\ndef create_ohe(df, mlb):    \n    ohe = mlb.transform(df.labels)\n    ohe = pd.DataFrame.sparse.from_spmatrix(ohe, columns=mlb.classes_)\n    df = df.merge(ohe, left_index=True, right_index=True)\n    return df\ndf = create_ohe(df, mlb)\ndf = df.sample(frac=1, random_state=42).reset_index(drop=True)\ndf.head()","metadata":{"execution":{"iopub.status.busy":"2021-09-05T05:46:20.432502Z","iopub.execute_input":"2021-09-05T05:46:20.432906Z","iopub.status.idle":"2021-09-05T05:46:20.501046Z","shell.execute_reply.started":"2021-09-05T05:46:20.432866Z","shell.execute_reply":"2021-09-05T05:46:20.500156Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"split = 0.9\nfrac = int(split * len(df))\n\ntrain_data = df[:frac]\nval_data = df[frac:]\n\ntrain_data = train_data.sample(frac=1, random_state=42).reset_index(drop=True)\nval_data = val_data.sample(frac=1, random_state=42).reset_index(drop=True)","metadata":{"execution":{"iopub.status.busy":"2021-09-05T05:46:20.503460Z","iopub.execute_input":"2021-09-05T05:46:20.503841Z","iopub.status.idle":"2021-09-05T05:46:20.536141Z","shell.execute_reply.started":"2021-09-05T05:46:20.503802Z","shell.execute_reply":"2021-09-05T05:46:20.535382Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class PlantDataset(Dataset):\n    def __init__(self, data, transformation, folder='train'):\n        self.data = data\n        self.transform = transformation\n        self.folder = folder\n    \n    def __len__(self): return len(self.data)\n    \n    def __getitem__(self, idx):\n        folder = self.folder\n        file = data_dir/f\"{folder}_images/{self.data.loc[idx, 'image']}\"\n        image = Image.open(file)\n        if self.transform:\n            image = self.transform(image)\n        labels = self.data.iloc[idx, 3:].to_numpy().astype(int)\n        return {\"input\": image, \"target\": labels}","metadata":{"execution":{"iopub.status.busy":"2021-09-05T05:46:20.537422Z","iopub.execute_input":"2021-09-05T05:46:20.537770Z","iopub.status.idle":"2021-09-05T05:46:20.544458Z","shell.execute_reply.started":"2021-09-05T05:46:20.537733Z","shell.execute_reply":"2021-09-05T05:46:20.543452Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataset = PlantDataset(train_data, TRAIN_TRANSFORM)\nval_dataset = PlantDataset(val_data, VALID_TRANSFORM)","metadata":{"execution":{"iopub.status.busy":"2021-09-05T05:46:20.546125Z","iopub.execute_input":"2021-09-05T05:46:20.546706Z","iopub.status.idle":"2021-09-05T05:46:20.554285Z","shell.execute_reply.started":"2021-09-05T05:46:20.546649Z","shell.execute_reply":"2021-09-05T05:46:20.553499Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import multiprocessing as mproc\nimport pytorch_lightning as pl\n\nclass PlantPathologyDM(pl.LightningDataModule):\n\n    def __init__(\n        self,\n        train_dataset: Dataset = None,\n        val_dataset: Dataset = None,\n        batch_size: int = 64,\n        num_workers: int = None,\n    ):\n        super().__init__()\n        self.batch_size = batch_size\n        self.num_workers = num_workers if num_workers is not None else mproc.cpu_count()\n        self.train_dataset = train_dataset\n        self.valid_dataset = val_dataset\n\n    def prepare_data(self):\n        pass\n\n    @property\n    def num_classes(self) -> int:\n        return num_classes\n\n    \n    def train_dataloader(self):\n        return DataLoader(\n            self.train_dataset,\n            batch_size=self.batch_size,\n            num_workers=self.num_workers,\n            shuffle=True,\n            pin_memory=True\n        )\n\n    def val_dataloader(self):\n        return DataLoader(\n            self.valid_dataset,\n            batch_size=self.batch_size,\n            num_workers=self.num_workers,\n            shuffle=False,\n            pin_memory=True\n        )\n\n    def test_dataloader(self):\n        pass","metadata":{"execution":{"iopub.status.busy":"2021-09-05T05:46:22.383835Z","iopub.execute_input":"2021-09-05T05:46:22.384179Z","iopub.status.idle":"2021-09-05T05:46:22.392533Z","shell.execute_reply.started":"2021-09-05T05:46:22.384146Z","shell.execute_reply":"2021-09-05T05:46:22.391174Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dm = PlantPathologyDM(train_dataset, val_dataset)\n","metadata":{"execution":{"iopub.status.busy":"2021-09-05T05:46:22.813572Z","iopub.execute_input":"2021-09-05T05:46:22.813902Z","iopub.status.idle":"2021-09-05T05:46:22.818445Z","shell.execute_reply.started":"2021-09-05T05:46:22.813871Z","shell.execute_reply":"2021-09-05T05:46:22.817139Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # quick view\n# fig = plt.figure(figsize=(3, 7))\n# for data in dm.train_dataloader():\n#     imgs = data[\"input\"]\n#     lbs = data[\"target\"]\n#     print(f'batch labels: {torch.sum(lbs, axis=0)}')\n#     print(f'image size: {imgs[0].shape}')\n#     for i in range(3):\n#         ax = fig.add_subplot(3, 1, i + 1, xticks=[], yticks=[])\n#         # print(np.rollaxis(imgs[i].numpy(), 0, 3).shape)\n#         ax.imshow(np.rollaxis(imgs[i].numpy(), 0, 3))\n#         ax.set_title(lbs[i])\n#     break","metadata":{"execution":{"iopub.status.busy":"2021-09-05T05:46:23.801750Z","iopub.execute_input":"2021-09-05T05:46:23.802087Z","iopub.status.idle":"2021-09-05T05:46:23.806842Z","shell.execute_reply.started":"2021-09-05T05:46:23.802055Z","shell.execute_reply":"2021-09-05T05:46:23.805177Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels = []\ni = 0\nfor label in tqdm(df.labels):\n    labels.extend(label)\nlabels = set(labels)\nnum_classes = len(labels)\nlabels","metadata":{"execution":{"iopub.status.busy":"2021-09-05T05:46:24.643826Z","iopub.execute_input":"2021-09-05T05:46:24.644204Z","iopub.status.idle":"2021-09-05T05:46:24.677397Z","shell.execute_reply.started":"2021-09-05T05:46:24.644168Z","shell.execute_reply":"2021-09-05T05:46:24.676526Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def binary_cross_entropy_with_logits(x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:\n    \"\"\"Calls BCE with logits and cast the target one_hot (y) encoding to floating point precision.\"\"\"\n    return F.binary_cross_entropy_with_logits(x, y.float())","metadata":{"execution":{"iopub.status.busy":"2021-09-05T05:46:24.783182Z","iopub.execute_input":"2021-09-05T05:46:24.783461Z","iopub.status.idle":"2021-09-05T05:46:24.787840Z","shell.execute_reply.started":"2021-09-05T05:46:24.783435Z","shell.execute_reply":"2021-09-05T05:46:24.786638Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = ImageClassifier(\n    dm.num_classes,\n    'ssl_resnet50',\n    loss_fn=binary_cross_entropy_with_logits,\n    multi_label=True\n)","metadata":{"execution":{"iopub.status.busy":"2021-09-05T05:46:24.913349Z","iopub.execute_input":"2021-09-05T05:46:24.913623Z","iopub.status.idle":"2021-09-05T05:46:25.420834Z","shell.execute_reply.started":"2021-09-05T05:46:24.913598Z","shell.execute_reply":"2021-09-05T05:46:25.419767Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# model.serializer = Labels(labels, multi_label=True, threshold=0.25)","metadata":{"execution":{"iopub.status.busy":"2021-09-05T05:46:25.422383Z","iopub.execute_input":"2021-09-05T05:46:25.422823Z","iopub.status.idle":"2021-09-05T05:46:25.426971Z","shell.execute_reply.started":"2021-09-05T05:46:25.422786Z","shell.execute_reply":"2021-09-05T05:46:25.425929Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Nvidia Pytorch tips","metadata":{}},{"cell_type":"code","source":"model = model.to('cuda')","metadata":{"execution":{"iopub.status.busy":"2021-09-05T10:46:46.267807Z","iopub.execute_input":"2021-09-05T10:46:46.268147Z","iopub.status.idle":"2021-09-05T10:46:46.278274Z","shell.execute_reply.started":"2021-09-05T10:46:46.268115Z","shell.execute_reply":"2021-09-05T10:46:46.277308Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"trainer = flash.Trainer(\n    max_epochs=10,\n    auto_lr_find=True,\n    benchmark=True,\n    gpus=1,\n)","metadata":{"execution":{"iopub.status.busy":"2021-09-05T10:46:47.317968Z","iopub.execute_input":"2021-09-05T10:46:47.318328Z","iopub.status.idle":"2021-09-05T10:46:47.327837Z","shell.execute_reply.started":"2021-09-05T10:46:47.318295Z","shell.execute_reply":"2021-09-05T10:46:47.326832Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"trainer.finetune(model, datamodule=dm, strategy=\"freeze_unfreeze\")","metadata":{"execution":{"iopub.status.busy":"2021-09-05T10:46:49.147992Z","iopub.execute_input":"2021-09-05T10:46:49.148383Z","iopub.status.idle":"2021-09-05T10:47:53.020522Z","shell.execute_reply.started":"2021-09-05T10:46:49.148347Z","shell.execute_reply":"2021-09-05T10:47:53.019517Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df = pd.read_csv(data_dir/'sample_submission.csv')\n# submission_df.labels = None\nsubmission_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-09-05T10:30:08.168836Z","iopub.execute_input":"2021-09-05T10:30:08.169187Z","iopub.status.idle":"2021-09-05T10:30:08.200650Z","shell.execute_reply.started":"2021-09-05T10:30:08.169145Z","shell.execute_reply":"2021-09-05T10:30:08.199847Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_dataset = PlantDataset(submission_df, TEST_TRANSFORM, 'test')\nsubmission_dataloader = DataLoader(submission_dataset, 16, num_workers=4)","metadata":{"execution":{"iopub.status.busy":"2021-09-05T10:33:55.459259Z","iopub.execute_input":"2021-09-05T10:33:55.459650Z","iopub.status.idle":"2021-09-05T10:33:55.466628Z","shell.execute_reply.started":"2021-09-05T10:33:55.459617Z","shell.execute_reply":"2021-09-05T10:33:55.465424Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = model.eval()","metadata":{"execution":{"iopub.status.busy":"2021-09-05T10:30:12.997611Z","iopub.execute_input":"2021-09-05T10:30:12.997938Z","iopub.status.idle":"2021-09-05T10:30:13.004401Z","shell.execute_reply.started":"2021-09-05T10:30:12.997906Z","shell.execute_reply":"2021-09-05T10:30:13.003408Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"TODO: create submission data","metadata":{}},{"cell_type":"code","source":"@torch.no_grad()\ndef get_results(submission_dataloader):\n    results = []\n    for data in submission_dataloader:\n        image = data['input']\n        preds = model(image)\n        preds = (preds.sigmoid() > 0.5)\n\n        for pred in preds:\n            lab = (df.columns[3:][pred])\n            results.append(lab.tolist())\n    return results","metadata":{"execution":{"iopub.status.busy":"2021-09-05T10:48:26.457914Z","iopub.execute_input":"2021-09-05T10:48:26.458289Z","iopub.status.idle":"2021-09-05T10:48:26.465489Z","shell.execute_reply.started":"2021-09-05T10:48:26.458250Z","shell.execute_reply":"2021-09-05T10:48:26.464380Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df.labels = get_results(submission_dataloader)\nsubmission_df.labels = submission_df.labels.apply(lambda x: \" \".join(x))\nsubmission_df.to_csv(\"/kaggle/working/results.csv\")","metadata":{"execution":{"iopub.status.busy":"2021-09-05T10:48:26.667791Z","iopub.execute_input":"2021-09-05T10:48:26.668122Z","iopub.status.idle":"2021-09-05T10:48:27.829352Z","shell.execute_reply.started":"2021-09-05T10:48:26.668090Z","shell.execute_reply":"2021-09-05T10:48:27.824397Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"execution":{"iopub.status.busy":"2021-09-05T10:44:34.738534Z","iopub.execute_input":"2021-09-05T10:44:34.738904Z","iopub.status.idle":"2021-09-05T10:44:34.744637Z","shell.execute_reply.started":"2021-09-05T10:44:34.738871Z","shell.execute_reply":"2021-09-05T10:44:34.743598Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"execution":{"iopub.status.busy":"2021-09-05T10:45:02.863679Z","iopub.execute_input":"2021-09-05T10:45:02.864013Z","iopub.status.idle":"2021-09-05T10:45:03.005605Z","shell.execute_reply.started":"2021-09-05T10:45:02.863980Z","shell.execute_reply":"2021-09-05T10:45:03.004732Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"execution":{"iopub.status.busy":"2021-09-05T10:45:53.426026Z","iopub.execute_input":"2021-09-05T10:45:53.426385Z","iopub.status.idle":"2021-09-05T10:45:53.430538Z","shell.execute_reply.started":"2021-09-05T10:45:53.426352Z","shell.execute_reply":"2021-09-05T10:45:53.429294Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}