{"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":"tez_path = '../input/tez-lib/'\neffnet_path = '../input/efficientnet-pytorch/'\nimport sys\nsys.path.append(tez_path)\nsys.path.append(effnet_path)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport albumentations as A\nimport matplotlib.pyplot as plt\nimport pandas as pd\n\nimport tez\nfrom tez import enums\nfrom tez.datasets import ImageDataset\nfrom tez.utils import AverageMeter\nfrom tez.callbacks import EarlyStopping\n\nfrom tqdm import tqdm\n\nimport torch\nimport torch.nn as nn\n\nimport torchvision\n\nfrom sklearn import metrics, model_selection\n\n%matplotlib inline","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Load data**","metadata":{}},{"cell_type":"code","source":"dfx = pd.read_csv(\"../input/plant-pathology-2021-fgvc8/train.csv\")\ndfx.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Encoded labels**","metadata":{}},{"cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\n\nlabelencoder = LabelEncoder()\ndfx[\"encoded_labels\"] = labelencoder.fit_transform(dfx[\"labels\"])\ndfx.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dfx.encoded_labels.value_counts()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Split train, valid & Reset index**","metadata":{}},{"cell_type":"code","source":"df_train, df_valid = model_selection.train_test_split(dfx, test_size=0.2, random_state=42, stratify=dfx.encoded_labels.values)\n\ndf_train = df_train.reset_index(drop=True)\ndf_valid = df_valid.reset_index(drop=True)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train.shape","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_valid.shape","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Import image**","metadata":{}},{"cell_type":"code","source":"image_path = \"../input/resized-plant2021/img_sz_512\"\n\ntrain_image_paths = [\n    os.path.join(image_path, x) for x in df_train.image.values\n]\n\ntrain_image_paths = [\n    os.path.join(image_path, x) for x in df_train.image.values\n]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_image_paths[:5]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Set train, valid target**","metadata":{}},{"cell_type":"code","source":"train_target = df_train.encoded_labels.values\nvalid_target = df_valid.encoded_labels.values","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# train_target","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# valid_target","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Create train_dataset**","metadata":{}},{"cell_type":"code","source":"train_dataset = ImageDataset(\n    image_paths = train_image_paths,\n    targets = train_target,\n    augmentations = None\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Plot image**","metadata":{}},{"cell_type":"code","source":"def plot_img(img_dict):\n    img_tensor = img_dict['image']\n    target = img_dict['targets']\n    print(target)\n    plt.figure(figsize=(5,5))\n    image = img_tensor.permute(1,2,0)/255\n    plt.imshow(image)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_img(train_dataset[10])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Augmentation**","metadata":{}},{"cell_type":"code","source":"train_aug = A.Compose(\n    [\n        A.RandomResizedCrop(256, 256),\n        A.Transpose(p=0.5),\n        A.HorizontalFlip(p=0.5),\n        A.VerticalFlip(p=0.5),\n        A.HueSaturationValue(\n            hue_shift_limit=0.2, \n            sat_shift_limit=0.2,\n            val_shift_limit=0.2, \n            p=0.5\n        ),\n        A.RandomBrightnessContrast(\n            brightness_limit=(-0.1,0.1), \n            contrast_limit=(-0.1, 0.1), \n            p=0.5\n        ),\n#         A.Normalize(\n#             mean=[0.485, 0.456, 0.406], \n#             std=[0.229, 0.224, 0.225], \n#             max_pixel_value=255.0, \n#             p=1.0\n#         )\n    ]\n)\n\nvalid_aug = A.Compose(\n    [\n        A.CenterCrop(256, 256, p=1.0),\n        A.Transpose(p=0.5),\n        A.HorizontalFlip(p=0.5),\n        A.VerticalFlip(p=0.5),\n        A.HueSaturationValue(\n            hue_shift_limit=0.2, \n            sat_shift_limit=0.2,\n            val_shift_limit=0.2, \n            p=0.5\n        ),\n        A.RandomBrightnessContrast(\n            brightness_limit=(-0.1,0.1), \n            contrast_limit=(-0.1, 0.1), \n            p=0.5\n        ),\n#         A.Normalize(\n#             mean=[0.485, 0.456, 0.406], \n#             std=[0.229, 0.224, 0.225], \n#             max_pixel_value=255.0, \n#             p=1.0\n#         )\n    ]\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Apply augmentation**","metadata":{}},{"cell_type":"code","source":"train_dataset = ImageDataset(\n    image_paths = train_image_paths,\n    targets = train_target,\n    augmentations = train_aug\n)\n\nvalid_dataset = ImageDataset(\n    image_paths = train_image_paths,\n    targets = train_target,\n    augmentations = valid_aug\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_img(train_dataset[10])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Create Model**","metadata":{}},{"cell_type":"code","source":"import pickle\n\n# pretrained=True\n# pretrained_model = torchvision.models.resnet18(pretrained=pretrained)\n\n# Pkl_Filename = \"pretrained_resnet18.pkl\"\n\n# with open(Pkl_Filename, 'wb') as file:  \n#     pickle.dump(pretrained_model, file)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"Pkl_Filename = \"../input/resnet18-pretrained/pretrained_resnet18.pkl\"\n\nwith open(Pkl_Filename, 'rb') as file:  \n    pretrained_model = pickle.load(file)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_result(train_loss, train_acc, train_f1, valid_loss, valid_acc, valid_f1):\n    fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(18, 7))\n    ax1.plot(train_loss, label='Train')\n    ax1.plot(valid_loss, label='Validation')\n    ax1.set_title('Loss')\n    ax1.legend()\n\n    ax2.plot(train_acc, label='Train')\n    ax2.plot(valid_acc, label='Validation')\n    ax2.set_title('Accuracy')\n    ax2.legend()\n\n    ax3.plot(train_f1, label='Train')\n    ax3.plot(valid_f1, label='Validation')\n    ax3.set_title('F1 Score')\n    ax3.legend()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_losses = []\ntrain_acc = []\ntrain_f1= []\n\nvalid_losses = []\nvalid_acc = []\nvalid_f1 = []\n\nclass PlantModel(tez.Model):\n    def __init__(self,num_classes):\n        super().__init__()\n        self.convnet = pretrained_model\n        self.convnet.fc = nn.Linear(512, num_classes)\n        self.step_scheduler_after = \"epoch\"\n        \n    def loss(self, outputs, targets):\n        if targets is None: \n            return None\n        return nn.CrossEntropyLoss()(outputs, targets)\n    \n    def update_metrics(self, losses, monitor):\n        self.metrics[self._model_state.value].update(monitor)\n        self.metrics[self._model_state.value][\"loss\"] = losses.avg\n\n    def monitor_metrics(self, outputs, targets):\n        outputs = torch.argmax(outputs, dim = 1).cpu().detach().numpy()\n        targets = targets.cpu().detach().numpy()\n        acc = metrics.accuracy_score(targets, outputs)\n        f1_score = metrics.f1_score(outputs, targets, average='weighted')\n        if(self.train_state is not None):\n            if(self.train_state == enums.TrainingState.TRAIN_STEP_START):\n                train_acc.append(acc)\n                train_f1.append(f1_score)\n#                 train_loss.append(loss)\n            if(self.train_state == enums.TrainingState.VALID_STEP_START):\n                valid_acc.append(acc)\n                valid_f1.append(f1_score)\n#                 valid_loss.append(loss)\n#             if(self.train_state == enums.TrainingState.EPOCH_END):\n#                 print(\"end.......................................\")\n#             if(self._model_state.value == \"end\"):\n#                 print(f\"Epoch: {self.current_epoch} ended.\")\n            \n#             if(self.train_state == enums.TrainingState.EPOCH_START):\n#                 print(f\"Epoch: {self.current_epoch} started.\")\n        return{\n            \"accuracy\" : acc,\n            \"f1_score\" : f1_score\n        }\n\n    def train_one_epoch(self, data_loader):\n        self.train()\n        self.model_state = enums.ModelState.TRAIN\n        losses = AverageMeter()\n        tk0 = tqdm(data_loader, total=len(data_loader))\n        for b_idx, data in enumerate(tk0):\n            self.train_state = enums.TrainingState.TRAIN_STEP_START\n            loss, metrics = self.train_one_step(data)\n            if(self.train_state is not None):\n#                 print(f\"Train one epoch loss: {self.train_state}\")\n                if(self.train_state == enums.TrainingState.TRAIN_STEP_START):\n                    train_losses.append(round(loss.item(),4))\n#                     print(train_loss)\n        \n            self.train_state = enums.TrainingState.TRAIN_STEP_END\n            losses.update(loss.item(), data_loader.batch_size)\n            if b_idx == 0:\n                metrics_meter = {k: AverageMeter() for k in metrics}\n            monitor = {}\n            for m_m in metrics_meter:\n                metrics_meter[m_m].update(metrics[m_m], data_loader.batch_size)\n                monitor[m_m] = metrics_meter[m_m].avg\n            self.current_train_step += 1\n            tk0.set_postfix(loss=losses.avg, stage=\"train\", **monitor)\n        tk0.close()\n        self.update_metrics(losses=losses, monitor=monitor)\n        return losses.avg\n    \n    def validate_one_step(self, data):\n        _, loss, metrics = self.model_fn(data)\n        if(self.train_state is not None):\n#             print(f\"Valid one epoch loss: {self.train_state}\")\n            if(self.train_state == enums.TrainingState.VALID_STEP_START):\n#                 print(\"valid loss: \")\n                valid_losses.append(round(loss.item(),4))\n#                 print(valid_losses)\n        return loss, metrics\n    \n    def load(self, model_path, device = 'cuda' if torch.cuda.is_available() else 'cpu'):\n        self.device = device\n        if next(self.parameters()).device != self.device:\n            self.to(self.device)\n        model_dict = torch.load(model_path, map_location=torch.device(device))\n        self.load_state_dict(model_dict[\"state_dict\"])\n    \n    def fetch_optimizer(self):\n        opt = torch.optim.Adam(self.parameters(), lr=1e-3)\n        return opt\n\n    def fetch_scheduler(self):\n        sch = torch.optim.lr_scheduler.StepLR(self.optimizer, step_size=0.7)\n        return sch\n        \n    def forward(self, image, targets=None):\n        outputs = self.convnet(image)\n        if targets is not None:\n            loss = self.loss(outputs, targets)\n            mon_metrics = self.monitor_metrics(outputs, targets)\n            return outputs, loss, mon_metrics\n        return outputs, None, None\n    \n    def fit(\n        self,\n        train_dataset,\n        valid_dataset=None,\n        train_sampler=None,\n        valid_sampler=None,\n        device ='cuda' if torch.cuda.is_available() else 'cpu',\n        epochs=10,\n        train_bs=16,\n        valid_bs=16,\n        n_jobs=8,\n        callbacks=None,\n        fp16=False,\n        train_collate_fn=None,\n        valid_collate_fn=None,\n    ):\n        \"\"\"\n        The model fit function. Heavily inspired by tf/keras, this function is the core of Tez and this is the only\n        function you need to train your models.\n        \"\"\"\n        self._init_model(\n            device=device,\n            train_dataset=train_dataset,\n            valid_dataset=valid_dataset,\n            train_sampler=train_sampler,\n            valid_sampler=valid_sampler,\n            train_bs=train_bs,\n            valid_bs=valid_bs,\n            n_jobs=n_jobs,\n            callbacks=callbacks,\n            fp16=fp16,\n            train_collate_fn=train_collate_fn,\n            valid_collate_fn=valid_collate_fn,\n        )\n\n        for _ in range(epochs):\n            self.train_state = enums.TrainingState.EPOCH_START\n            self.train_state = enums.TrainingState.TRAIN_EPOCH_START\n            train_loss = self.train_one_epoch(self.train_loader)\n            self.train_state = enums.TrainingState.TRAIN_EPOCH_END\n            if self.valid_loader:\n                self.train_state = enums.TrainingState.VALID_EPOCH_START\n                valid_loss = self.validate_one_epoch(self.valid_loader)\n                self.train_state = enums.TrainingState.VALID_EPOCH_END\n            if self.scheduler:\n                if self.step_scheduler_after == \"epoch\":\n                    if self.step_scheduler_metric is None:\n                        self.scheduler.step()\n                    else:\n                        step_metric = self.name_to_metric(self.step_scheduler_metric)\n                        self.scheduler.step(step_metric)\n            self.train_state = enums.TrainingState.EPOCH_END\n#             print(valid_losses)\n            plot_result(train_losses, train_acc, train_f1, valid_losses, valid_acc, valid_f1)    \n            if self._model_state.value == \"end\":\n                break\n            self.current_epoch += 1\n        self.train_state = enums.TrainingState.TRAIN_END\n        ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Resnet18 structure**","metadata":{}},{"cell_type":"code","source":"# torchvision.models.resnet152(pretrained=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# dfx.encoded_labels.nunique()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = PlantModel(dfx.encoded_labels.nunique())","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img = train_dataset[0][\"image\"]\ny = train_dataset[0][\"targets\"]\nmodel(img.unsqueeze(0), y.unsqueeze(0))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Our custom model**","metadata":{}},{"cell_type":"markdown","source":"**Train model**","metadata":{}},{"cell_type":"code","source":"es = EarlyStopping(\n    monitor = \"train_accuracy\", \n    model_path = \"model.bin\", \n    patience = 2,\n    mode='max'\n)\n\nmodel.fit(\n    train_dataset,\n    valid_dataset = valid_dataset,\n    train_bs = 32,\n    valid_bs = 64,\n    device = \"cuda\",\n    callbacks = [es],\n    fp16 = True,\n    epochs = 5\n)\n\n# model.save(\"model.bin\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Predict testset**","metadata":{}},{"cell_type":"markdown","source":"**Load test data**","metadata":{}},{"cell_type":"code","source":"test_dfx = pd.read_csv(\"../input/plant-pathology-2021-fgvc8/sample_submission.csv\")\nimage_path = \"../input/plant-pathology-2021-fgvc8/test_images/\" \n\n# model.load(\"../input/resnet18-tez/model.bin\")\n\n# model\n\n# Pkl_Filename = \"../input/resnet18-tez/resnet18_trained_model.pkl\"\n# with open(Pkl_Filename, 'rb') as file:  \n#     model = pickle.load(file)\n\n# test_dfx.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Encode label**","metadata":{}},{"cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\n\nlabelencoder = LabelEncoder()\ntest_dfx[\"encoded_labels\"] = labelencoder.fit_transform(test_dfx[\"labels\"])\ntest_dfx.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_image_paths = [\n    os.path.join(image_path, x) for x in test_dfx.image.values\n]\n\ntest_target = test_dfx.encoded_labels","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_aug = A.Compose(\n    [\n        A.RandomResizedCrop(256, 256),\n        A.Transpose(p=0.5),\n        A.HorizontalFlip(p=0.5),\n        A.VerticalFlip(p=0.5),\n        A.HueSaturationValue(\n            hue_shift_limit=0.2, \n            sat_shift_limit=0.2,\n            val_shift_limit=0.2, \n            p=0.5\n        ),\n        A.RandomBrightnessContrast(\n            brightness_limit=(-0.1,0.1), \n            contrast_limit=(-0.1, 0.1), \n            p=0.5\n        ),\n#         A.Normalize(\n#             mean=[0.485, 0.456, 0.406], \n#             std=[0.229, 0.224, 0.225], \n#             max_pixel_value=255.0, \n#             p=1.0\n#         )\n    ]\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_dataset = ImageDataset(\n    image_paths = test_image_paths,\n    targets = test_target,\n    augmentations = test_aug\n)\n\ntest_dataset[0]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"final_preds = None\nfor j in range(5):\n    preds = model.predict(test_dataset, batch_size=32, n_jobs=-1)\n    temp_preds = None\n    for p in preds:\n        if temp_preds is None:\n            temp_preds = p\n        else:\n            temp_preds = np.vstack((temp_preds, p))\n    if final_preds is None:\n        final_preds = temp_preds\n    else:\n        final_preds += temp_preds\nfinal_preds /= 5","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"final_preds = final_preds.argmax(axis=1)\nfinal_preds","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_dfx.encoded_labels = final_preds\ntest_dfx.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"lblist = df_train.drop_duplicates(subset=['labels'])\nlblist = lblist.set_index(\"encoded_labels\")\nlblist","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# lblist.at[5, \"labels\"]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_labels(val):\n    return lblist.at[val, \"labels\"]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred_lists = []\nfor i, pred in enumerate(final_preds):\n    label = get_labels(pred)\n    pred_lists.append(label)\n    \npred_lists","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_dfx[\"labels\"] = pred_lists\ntest_dfx = test_dfx.drop(columns=['encoded_labels'])\ntest_dfx","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_dfx.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}