{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Meet [Tez](https://github.com/abhishekkrthakur/tez)!🚅🔥\nIn this notebook we will be creating a classifier model using [Tez](https://github.com/abhishekkrthakur/tez) trainer or helper in order to complete our task to classifier Cassava leaf disease dataset! Let's get started..."},{"metadata":{},"cell_type":"markdown","source":"# Imports "},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# Tez is a super-simple and lightweight Trainer for PyTorch. It also comes with many utils that you can use to tackle over 90% of deep learning projects in PyTorch.\n!pip install tez","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Tez keeps your code clean and readable!\n# Imports \n# Tez is very easy and super-fast trainer \n\nimport os\n\nimport albumentations\nimport matplotlib.pyplot as plt\nimport pandas as pd\n\nimport tez\nfrom tez.datasets import ImageDataset\nfrom tez.callbacks import EarlyStopping\n\nimport torch\nimport torch.nn as nn\nfrom torch.nn import functional as F\n\nimport torchvision\n\nfrom sklearn import metrics, model_selection, preprocessing\n\n%matplotlib inline","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Let's read the CSV file\ndfx = pd.read_csv('../input/cassava-leaf-disease-classification/train.csv')\n\n# and split it into training and validation sets\ndf_train, df_valid = model_selection.train_test_split(\n    dfx, \n    test_size=0.1, \n    random_state=42,\n    stratify=dfx.label.values\n)\n\n# reset index on both dataframes\ndf_train = df_train.reset_index(drop=True)\ndf_valid = df_valid.reset_index(drop=True)\n\n# where are the train/valid images located?\nimage_path = \"../input/cassava-leaf-disease-classification/train_images/\"\n\n# create a list of image paths for training\ntrain_image_paths = [os.path.join(image_path, x) for x in df_train.image_id.values]\n\n# create a list of image paths for validation\nvalid_image_paths = [os.path.join(image_path, x) for x in df_valid.image_id.values]\n\n# targets for training\ntrain_targets = df_train.label.values\n\n# targets for validation\nvalid_targets = df_valid.label.values","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Let's create training and validation datasets\n# Tez provides simple dataset class that you can use directly\n\n# we create the train_dataset\ntrain_dataset = ImageDataset(\n    image_paths=train_image_paths,\n    targets=train_targets,\n#     resize=(256, 256),      resize parameter was later deprecated from Tez\n    augmentations=None,\n)\n\n# and the validation dataset\nvalid_dataset = ImageDataset(\n    image_paths=valid_image_paths,\n    targets=valid_targets,\n    augmentations=None,\n)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# how does the output of dataset class look like?\n# lets look at an item\ntrain_dataset[0]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Some image plots/images"},{"metadata":{"trusted":true},"cell_type":"code","source":"# thus, we have image and targets\n# super-easy!\n\n# Let's see some images!\n\ndef plot_image(img_dict):\n    image_tensor = img_dict[\"image\"]\n    target = img_dict[\"targets\"]\n    print(target)\n    plt.figure(figsize=(10, 10))\n    image = image_tensor.permute(1, 2, 0) / 255\n    plt.imshow(image)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_image(train_dataset[0])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Augmentations "},{"metadata":{"trusted":true},"cell_type":"code","source":"# Now, lets add some augmentations using one of the best\n# augmentations library: albumentations\n# Tez supports albumentations exclusively\n\ntrain_aug = albumentations.Compose(\n    [\n        albumentations.RandomResizedCrop(256, 256),\n        albumentations.Transpose(p=0.5),\n        albumentations.HorizontalFlip(p=0.5),\n        albumentations.VerticalFlip(p=0.5),\n        albumentations.ShiftScaleRotate(p=0.5),\n        # albumentations.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\n\ntrain_dataset = ImageDataset(\n    image_paths=train_image_paths,\n    targets=train_targets,\n    augmentations=train_aug,\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_image(train_dataset[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Let's define a model now\n# We inherit from tez.Model instead of nn.Module\n# we have monitor_metrics if we want to monitor any metrics\n# except the loss\n# and we return 3 values in forward function.\n\nclass LeafModel(tez.Model):\n    def __init__(self, num_classes):\n        super().__init__()\n\n        self.convnet = torchvision.models.resnet18(pretrained=True)\n        self.convnet.fc = nn.Linear(512, num_classes)\n        self.step_scheduler_after = \"epoch\"\n        \n    def monitor_metrics(self, outputs, targets):\n        if targets is None:\n            return {}\n        outputs = torch.argmax(outputs, dim=1).cpu().detach().numpy()\n        targets = targets.cpu().detach().numpy()\n        accuracy = metrics.accuracy_score(targets, outputs)\n        return {\"accuracy\": accuracy}\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        batch_size, _, _, _ = image.shape\n\n        outputs = self.convnet(image)\n        \n        if targets is not None:\n            loss = nn.CrossEntropyLoss()(outputs, targets)\n            metrics = self.monitor_metrics(outputs, targets)\n            return outputs, loss, metrics\n        return outputs, None, None","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = LeafModel(num_classes=5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image = train_dataset[0][\"image\"].unsqueeze(0)\ntarget = train_dataset[0][\"targets\"].unsqueeze(0)\n\n\nmodel(image, target)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_aug = albumentations.Compose([\n            albumentations.RandomResizedCrop(256, 256),\n            albumentations.Transpose(p=0.5),\n            albumentations.HorizontalFlip(p=0.5),\n            albumentations.VerticalFlip(p=0.5),\n            albumentations.ShiftScaleRotate(p=0.5),\n            albumentations.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            )], p=1.)\n      \nvalid_aug = albumentations.Compose([\n            albumentations.CenterCrop(256, 256, p=1.),\n            albumentations.Resize(256, 256),\n            albumentations.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            )], p=1.)\n\ntrain_dataset = ImageDataset(\n    image_paths=train_image_paths,\n    targets=train_targets,\n    augmentations=train_aug,\n)\n\nvalid_dataset = ImageDataset(\n    image_paths=valid_image_paths,\n    targets=valid_targets,\n    augmentations=valid_aug,\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model Training"},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\nes = EarlyStopping(\n    monitor=\"valid_accuracy\", model_path=\"model.bin\", patience=2, mode=\"max\"\n)\nmodel.fit(\n    train_dataset,\n    valid_dataset=valid_dataset,\n    train_bs=32,\n    valid_bs=64,\n    device=\"cuda\",\n    epochs=5,\n    callbacks=[es],\n    fp16=True,\n) ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_dfx = pd.read_csv(\"../input/cassava-leaf-disease-classification/sample_submission.csv\")\nimage_path = \"../input/cassava-leaf-disease-classification/test_images/\"\ntest_image_paths = [os.path.join(image_path, x) for x in test_dfx.image_id.values]\n# fake targets\ntest_targets = test_dfx.label.values\n\n\ntest_aug = albumentations.Compose([\n            albumentations.CenterCrop(256, 256, p=1.),\n            albumentations.Resize(256, 256),\n            albumentations.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            )], p=1.)\n\ntest_dataset = ImageDataset(\n    image_paths=test_image_paths,\n    targets=test_targets,\n    augmentations=test_aug,\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Prediction Time"},{"metadata":{"trusted":true},"cell_type":"code","source":"preds = model.predict(test_dataset, batch_size=32, n_jobs=-1)\nfinal_preds = None\nfor p in preds:\n    if final_preds is None:\n        final_preds = p\n    else:\n        final_preds = np.vstack((final_preds, p))\nfinal_preds = final_preds.argmax(axis=1)\ntest_dfx.label = final_preds\ntest_dfx.to_csv(\"submission.csv\", index=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Thanks for having a glance at my kernel!😊"}],"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":4,"nbformat_minor":4}