{"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":"# Import","metadata":{}},{"cell_type":"code","source":"!pip install pytorch-lightning","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from typing import List, Dict\nimport os\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n%matplotlib inline\nimport seaborn as sns\n\nimport tqdm\n\nimport cv2\nimport albumentations as A\nfrom albumentations.core.composition import Compose\nfrom albumentations.pytorch import ToTensorV2\n\nfrom torch.utils.data import Dataset, TensorDataset, DataLoader\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn import metrics\n\nimport pytorch_lightning as pl\nfrom pytorch_lightning import Trainer\nfrom pytorch_lightning.callbacks import ModelCheckpoint, EarlyStopping\nfrom pytorch_lightning.metrics import FBeta\nfrom pytorch_lightning.loggers import CSVLogger\n\nimport torch\nimport torchvision.models as models\nfrom torch import nn\nfrom torch.optim import AdamW, Adam\nimport torch.nn.functional as F\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau\nfrom PIL import Image","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(f\"PyTorch Lightning version: {pl.__version__}\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Config","metadata":{}},{"cell_type":"code","source":"ROOT_DIR = '../input/plant-pathology-2021-fgvc8/'\nFOOL_CSV = '../input/pp2021-kfold-tfrecords-0/folds.csv'\nTRAIN_CSV = 'train.csv'\nTRAIN_IMAGES_FOLDER = 'train_images'\nTEST_IMAGES_FOLDER = 'test_images'\nSAMPLE_SUBMISSION_CSV = 'sample_submission.csv'\n\nconfigurations = {\n    \"BATCH_SIZE\": 16,\n    \"NUM_WORKERS\": 2,\n    \"IMAGE_HEIGHT\": 334, \n    \"IMAGE_WIDTH\": 334,\n    \"LEARNING_RATE\": 0.001,\n    \"MAX_EPOCHS\": 1,\n}\n\nclass CFG:\n    \n    '''\n    keep these\n    '''\n    root = '../input/plant-pathology-2021-fgvc8/train_images'\n    classes = [\n        'complex', \n        'frog_eye_leaf_spot', \n        'powdery_mildew', \n        'rust', \n        'scab',\n        'healthy']\n    batch_size = 16\n    \n    '''\n    tune these\n    '''\n    img_size = 334 # image size\n    folds = 5 # number of KFold n_splits\n    seed = 42 # random seed (only for KFold)\n    subfolds = 16 # number of .tfrec files in each fold\n    transform = True # whether to apply pre-augmentations or not\n    epochs = 5 # (>=5) number of pre-augmented dataset copies to save when transform = True","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"RANDOM_SEED = 42\n# Set seed for everythin(numpy, torch and python)\n\nfrom pytorch_lightning import seed_everything\nseed_everything(RANDOM_SEED)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataset_df = pd.read_csv(os.path.join(ROOT_DIR, TRAIN_CSV))\nfolds_df = pd.read_csv(FOOL_CSV)\n\ndef get_single_labels(unique_labels) -> List[str]:\n    \"\"\"Splitting multi-labels and returning a list of classes\"\"\"\n    single_labels = []\n    for label in unique_labels:\n        single_labels += label.split()\n        \n    single_labels = set(single_labels)\n    \n    return list(single_labels)\n\ndef get_one_hot_encoded_dataframe(dataset_df):\n    # copy dataframe\n    dataset_df_copy = dataset_df.copy()\n    \n    unique_labels = dataset_df_copy.labels.unique()\n    \n    new_column_names = get_single_labels(unique_labels)\n    # initialize columns with zero\n    dataset_df_copy[new_column_names] = 0        \n    \n    # one-hot-encoding using the column names\n    for label in unique_labels:                \n        label_indices = dataset_df_copy[dataset_df_copy['labels'] == label].index\n        splited_labels = label.split()\n        dataset_df_copy.loc[label_indices, splited_labels] = 1\n    \n    return dataset_df_copy\n\ndf = get_one_hot_encoded_dataframe(dataset_df)\ndf = folds_df.merge(df, on='image')\ndf.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define Dataset","metadata":{}},{"cell_type":"code","source":"class ImageDataset(Dataset):\n    \"\"\" Leaf Disease Dataset \"\"\"\n    def __init__(self,\n                image_names: List[str],\n                labels: List[List[int]],\n                image_dir: str, \n                transforms):        \n        self.image_names = image_names\n        self.image_dir = image_dir\n        self.transforms = transforms                \n        self.labels = labels\n\n\n    def __len__(self) -> int:\n        return len(self.image_names)\n\n    def __getitem__(self, idx: int):\n        image_path = os.path.join(self.image_dir, self.image_names[idx])           \n        image = np.array(Image.open(image_path))\n\n        target = self.labels[idx]\n\n        transformed_image = self.transforms(image=image)['image']\n        return transformed_image, target","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class ImageDataModule(pl.LightningDataModule):\n    def __init__(self,\n                 df: pd.DataFrame,\n                 train_transforms,\n                 valid_transforms,\n                 image_dir: str,\n                 fold_num: int,\n                 configurations: Dict[str, int]):\n        super().__init__()\n        self.df = df\n        self.train_transforms = train_transforms\n        self.valid_transforms = valid_transforms\n        self.image_dir = image_dir\n        self.fold_num = fold_num\n    \n    def setup(self, stage=None) -> None:\n        train_df = self.df[df.fold != self.fold_num].reset_index()\n        valid_df = self.df[df.fold == self.fold_num].reset_index()\n        \n        print(f\"Size of Train Dataset: {len(train_df.index)}\")\n        print(f\"Size of Validation Dataset: {len(valid_df.index)}\")\n        if stage is None or stage == 'fit':\n            self.train_dataset = ImageDataset(image_names=train_df.image.values, \n                                            labels=train_df[CFG.classes].values, \n                                            image_dir=self.image_dir, \n                                            transforms=self.train_transforms,\n                                            )\n\n            self.valid_dataset = ImageDataset(image_names=valid_df.image.values, \n                                            labels=valid_df[CFG.classes].values, \n                                            image_dir=self.image_dir, \n                                            transforms=self.valid_transforms,\n                                            )\n        elif stage == 'test':\n            self.test_dataset = ImageDataset(image_names=valid_df.image.values, \n                                            labels=valid_df[CFG.classes].values, \n                                            image_dir=self.image_dir, \n                                            transforms=self.valid_transforms,\n                                            )\n        \n        \n    def train_dataloader(self):        \n        train_loader = DataLoader(\n            self.train_dataset,\n            batch_size=configurations.get(\"BATCH_SIZE\"),\n            num_workers=configurations.get(\"NUM_WORKERS\"),\n            shuffle=True,\n            pin_memory=True,\n        )\n        return train_loader\n\n    def val_dataloader(self):        \n        valid_loader = DataLoader(\n            self.valid_dataset,\n            batch_size=configurations.get(\"BATCH_SIZE\"),\n            num_workers=configurations.get(\"NUM_WORKERS\"),\n            shuffle=False,\n            pin_memory=True,\n        )\n        return valid_loader\n\n    def test_dataloader(self):\n        return None","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_aug = A.Compose([\n       A.RandomResizedCrop(CFG.img_size, CFG.img_size, scale=(0.9, 1), p=1), \n       A.HorizontalFlip(p=0.5),\n       A.VerticalFlip(p=0.5),\n       A.ShiftScaleRotate(p=0.5),\n       A.HueSaturationValue(hue_shift_limit=10, sat_shift_limit=10, val_shift_limit=10, p=0.7),\n       A.RandomBrightnessContrast(brightness_limit=(-0.2,0.2), contrast_limit=(-0.2, 0.2), p=0.7),\n       A.CLAHE(clip_limit=(1,4), p=0.5),\n       A.OneOf([\n           A.OpticalDistortion(distort_limit=1.0),\n           A.GridDistortion(num_steps=5, distort_limit=1.),\n           A.ElasticTransform(alpha=3),\n       ], p=0.2),\n       A.OneOf([\n           A.GaussNoise(var_limit=[10, 50]),\n           A.GaussianBlur(),\n           A.MotionBlur(),\n           A.MedianBlur(),\n       ], p=0.2),\n      A.Resize(CFG.img_size, CFG.img_size),\n      A.OneOf([\n          A.JpegCompression(),\n          A.Downscale(scale_min=0.1, scale_max=0.15),\n      ], p=0.2),\n      A.IAAPiecewiseAffine(p=0.2),\n      A.IAASharpen(p=0.2),\n      A.Cutout(max_h_size=int(CFG.img_size * 0.1), max_w_size=int(CFG.img_size * 0.1), num_holes=5, p=0.5),\n])\n\ntrain_transform = A.Compose([\n      train_aug,\n      A.Normalize(),\n      ToTensorV2(),\n])\n\nvalid_transform = A.Compose([\n    A.Resize(height=CFG.img_size, width=CFG.img_size, p=1.0),\n    A.Normalize(),\n    ToTensorV2(),\n])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define Model","metadata":{}},{"cell_type":"code","source":"import os\nimport sys\nsys.path = [\n    '../input/efficientnet-pytorch/EfficientNet-PyTorch/EfficientNet-PyTorch-master',\n] + sys.path\nfrom efficientnet_pytorch import model as enet","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class ClassifierModule(pl.LightningModule):\n    def __init__(self, learning_rate=0.003, num_classes=6):\n        super().__init__()        \n        self.metric = FBeta(num_classes=num_classes, beta=0.5, multilabel=True)\n        self.learning_rate = learning_rate\n        # Try different architectures\n        self.model = enet.EfficientNet.from_name('efficientnet-b4')\n        self.model.load_state_dict(torch.load('../input/efficientnet-pytorch/efficientnet-b4-e116e8b3.pth'))\n        self.model._fc = nn.Linear(in_features=self.model._fc.in_features, out_features=num_classes)     \n        \n        \n    def forward(self, x):\n        batch_size = x.size(0)\n        print(x.device)\n        x = self.model(x)                \n        x = torch.sigmoid(x)\n        \n        return x.reshape(batch_size, -1)\n    \n    def configure_optimizers(self):\n        optimizer = AdamW(self.model.parameters(), lr=self.learning_rate, weight_decay=0.001)        \n        scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, \n                                                               mode='min', \n                                                               factor=0.1, \n                                                               patience=2)\n        return {'optimizer': optimizer,\n                'lr_scheduler': scheduler,\n                'monitor': 'valid_loss',\n            }\n    \n    def _get_loss(self, y_hat, y): \n        loss = nn.BCELoss()        \n        return loss(y_hat.to(torch.float32), y.to(torch.float32))\n    \n    def training_step(self, batch, batch_idx):\n        image = batch[0]\n        y = batch[1]\n        y_hat = self(image)                           \n        \n        loss = self._get_loss(y_hat, y)        \n        f1_beta_score = self.metric(y_hat, y)\n        \n        self.log('train_loss', loss.item(), on_step=True, on_epoch=True, prog_bar=True, logger=True)        \n        self.log('f1_train', f1_beta_score.item(), on_step=True, on_epoch=True, prog_bar=True, logger=True)\n                \n        return {\n            'loss': loss,                        \n            'logits': y_hat,\n            'target': y,            \n        }                 \n        \n    def validation_step(self, batch, batch_idx):\n        image = batch[0]\n        y = batch[1]\n        y_hat = self(image)\n        \n        loss = self._get_loss(y_hat, y)\n        f1_beta_score = self.metric(y_hat, y)        \n        \n        self.log('valid_loss', loss.item(), on_step=True, on_epoch=True, prog_bar=True, logger=True)\n        self.log('f1_valid', f1_beta_score.item(), on_step=True, on_epoch=True, prog_bar=True, logger=True)\n\n        return {\n            'loss': loss,                        \n            'logits': y_hat,\n            'target': y,            \n        }                            ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_module = ImageDataModule(df=df,\n                               train_transforms=train_transform,\n                               valid_transforms=valid_transform,\n                               image_dir= os.path.join(ROOT_DIR, TRAIN_IMAGES_FOLDER),\n                               fold_num=0,\n                               configurations=configurations)\ntrainer = pl.Trainer(\n#         deterministic=True,\n        checkpoint_callback=ModelCheckpoint(monitor='train_loss_epoch', save_top_k=1, filename='effnetb4-foldnum-0_{epoch}_{valid_loss_epoch:.4f}_{f1_valid_epoch:.4f}', mode='min'),\n        gpus=1,\n#         tpu_cores = 8,\n        max_epochs=1,\n        num_sanity_val_steps=1,        \n#         weights_summary='top',\n        limit_train_batches=10,\n        limit_val_batches=10,\n        profiler=\"simple\",\n)\nlightning = ClassifierModule()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"code","source":"trainer.fit(lightning, data_module)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"trainer.profiler.make_report()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Result","metadata":{}},{"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['train_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":{"trusted":true},"execution_count":null,"outputs":[]}]}