{"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":"Reference: [Plant Pathology with Lightning ⚡- By Jirka](https://www.kaggle.com/jirkaborovec/plant-pathology-with-lightning)\n","metadata":{}},{"cell_type":"code","source":"!pip install git+https://github.com/PytorchLightning/lightning-flash.git@master -q\n!pip install git+https://github.com/PytorchLightning/metrics.git@master -q\n!pip install timm -q","metadata":{"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)\nfrom sklearn.preprocessing import MultiLabelBinarizer\nimport matplotlib.pyplot as plt\n\nimport flash\nfrom flash.vision import ImageClassificationData, ImageClassifier\nimport torchmetrics\n\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","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.read_csv('/kaggle/input/plant-pathology-2021-fgvc8/train.csv')\ndf['label_org'] = df.labels.values\ndf.labels = df.labels.str.split()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import 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":{"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":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"mlb = MultiLabelBinarizer(sparse_output=True)\nmlb = mlb.fit(df.labels)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def 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","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = create_ohe(df, mlb)\ndf = df.sample(frac=1, random_state=42).reset_index(drop=True)\ndf.head()","metadata":{"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":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"IMAGE_SIZE = 224","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from torchvision import transforms as T\n\nTRAIN_TRANSFORM = T.Compose([\n    T.Resize(512),\n    T.RandomPerspective(),\n    T.RandomResizedCrop(224),\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    # T.Normalize([0.431, 0.498,  0.313], [0.237, 0.239, 0.227]),  # custom\n])\n\nVALID_TRANSFORM = T.Compose([\n    T.Resize(256),\n    T.CenterCrop(224),\n    T.ToTensor(),\n    T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),\n    # T.Normalize([0.431, 0.498,  0.313], [0.237, 0.239, 0.227]),  # custom\n])","metadata":{"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 = f'/kaggle/input/plant-pathology-2021-fgvc8/{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 image, labels","metadata":{"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":{"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        )\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        )\n\n    def test_dataloader(self):\n        pass\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dm = PlantPathologyDM(train_dataset, val_dataset)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # quick view\n# fig = plt.figure(figsize=(3, 7))\n# for imgs, lbs in dm.train_dataloader():\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":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class PLModel(pl.LightningModule):\n    def __init__(self, backbone, num_classes, lr: float = 1e-4):\n        super().__init__()\n        self.model = ImageClassifier(num_classes, backbone='xception')\n        \n        self.num_classes = num_classes\n        self.train_accuracy = torchmetrics.Accuracy()\n        self.val_accuracy = torchmetrics.Accuracy()\n        self.val_f1_score = torchmetrics.F1(self.num_classes)\n        self.learn_rate = lr\n        self.loss = nn.BCEWithLogitsLoss()\n    \n    def forward(self, x):\n        return torch.sigmoid(self.model.forward(x))\n\n    def compute_loss(self, y_hat, y):\n        return self.loss(y_hat, y.to(float))\n    \n    def training_step(self, batch, batch_idx):\n        x, y = batch\n        y_hat = self(x)\n        loss = self.compute_loss(y_hat, y)\n        self.log(\"train_loss\", loss, prog_bar=True)\n        self.log(\"train_acc\", self.train_accuracy(y_hat, y), prog_bar=False)\n        return loss\n\n    def validation_step(self, batch, batch_idx):\n        x, y = batch\n        y_hat = self(x)\n        loss = self.compute_loss(y_hat, y)\n        self.log(\"valid_loss\", loss, prog_bar=False)\n        self.log(\"valid_acc\", self.val_accuracy(y_hat, y), prog_bar=True)\n        self.log(\"valid_f1\", self.val_f1_score(y_hat, y), prog_bar=True)\n\n    def configure_optimizers(self):\n        optimizer = torch.optim.AdamW(self.model.parameters(), lr=self.learn_rate)\n        scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, self.trainer.max_epochs, 0)\n        return [optimizer], [scheduler]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pl_model = PLModel('xception', dm.num_classes)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"logger = pl.loggers.CSVLogger(save_dir='logs/', name='xception_v0')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"trainer = pl.Trainer(\n    gpus=1,\n    logger=logger,\n    max_epochs=10,\n    accumulate_grad_batches=8,\n    val_check_interval=0.25,\n    progress_bar_refresh_rate=1,\n)\n\n\ntrainer.fit(model=pl_model, datamodule=dm)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# !nvidia-smi","metadata":{"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":[]}]}