{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Library"},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install efficientnet-pytorch -qqq","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from fastai.vision.all import *\nimport albumentations as A\nfrom efficientnet_pytorch import EfficientNet \n\nimport warnings\nwarnings.filterwarnings('ignore')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Set a seed for reproducibility"},{"metadata":{"trusted":true},"cell_type":"code","source":"set_seed(42)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# CFG"},{"metadata":{"trusted":true},"cell_type":"code","source":"class CFG:\n    size=512\n    bs=32\n    model='efficientnet-b3'","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Setting up the transforms"},{"metadata":{"trusted":true},"cell_type":"code","source":"class AlbumentationsTransform(RandTransform):\n    '''Transform handler for multiple Albumentation transforms'''\n    split_idx, order=None,2\n    def __init__(self, train_aug, valid_aug): store_attr()\n    \n    def before_call(self, b, split_idx):\n        self.idx = split_idx\n    \n    def encodes(self, img: PILImage):\n        if self.idx == 0:\n            aug_img = self.train_aug(image=np.array(img))['image']\n            \n        else:\n            aug_img = self.valid_aug(image=np.array(img))['image']\n            \n        return PILImage.create(aug_img)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Training augmentation"},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_train_aug(): \n    return A.Compose([\n        A.RandomResizedCrop(CFG.size, CFG.size),\n        A.Transpose(p=0.5),\n        A.HorizontalFlip(p=0.5),\n        A.VerticalFlip(p=0.5),\n        A.ShiftScaleRotate(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.CoarseDropout(p=0.5),\n        A.Cutout(p=0.5)\n])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Validation augmentation"},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_valid_aug(): \n    return A.Compose([\n        A.Resize(CFG.size, CFG.size),\n        A.CenterCrop(CFG.size, CFG.size, p=1.)\n], p=1.)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Get item transforms for both sets"},{"metadata":{"trusted":true},"cell_type":"code","source":"item_tfms = AlbumentationsTransform(get_train_aug(), get_valid_aug())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Get merged data from the 2019 and current competition"},{"metadata":{"trusted":true},"cell_type":"code","source":"path = Path('/kaggle/input/cassavapreprocessed')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"path.ls()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_images = path/'train_images'/'train_images'\ntest_images  = path/'test_images'/'test_images'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df = pd.read_csv(path/'new_merged.csv', low_memory=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df.drop('Unnamed: 0', axis=1, inplace=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Creating the DataBlock"},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_x(row): return train_images/row['image_id']\ndef get_y(row): return row['label']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dblock = DataBlock(blocks=(ImageBlock, CategoryBlock),\n                  # Allocate 20% of data to the validation set\n                  splitter=RandomSplitter(valid_pct=0.2, seed=42),\n                  \n                  # Use the functions defined above to get items and labels\n                  get_x=get_x,\n                  get_y=get_y,\n                   \n                  # Use our item_tfms on each image seperately \n                  item_tfms=item_tfms,\n                   \n                  # Normalize a batch of images with imagenet stats\n                  batch_tfms=[Normalize.from_stats(*imagenet_stats)])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Get DataLoaders"},{"metadata":{"trusted":true},"cell_type":"code","source":"dls = dblock.dataloaders(train_df, bs=CFG.bs)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Create the pretrained EfficientNet model"},{"metadata":{"trusted":true},"cell_type":"code","source":"class CassavaModel(Module):\n    def __init__(self, num_classes):\n        self.effnet = EfficientNet.from_pretrained(CFG.model)\n        self.dropout = nn.Dropout(0.1)\n        self.out = nn.Linear(1536, num_classes)\n        \n    def forward(self, image):\n        batch_size, _, _, _ = image.shape\n        \n        x = self.effnet.extract_features(image)\n        x = F.adaptive_avg_pool2d(x, 1).reshape(batch_size, -1)\n        output = self.out(self.dropout(x))\n        \n        return output","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"cassava_net = CassavaModel(dls.c)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Training the model"},{"metadata":{},"cell_type":"markdown","source":"We use Cross Entropy with label smoothing as our loss function and Adam as our optimization function. This loss function should be especially helpful here, because the dataset is noisy and it helps with making the model less confident and extreme about it's predictions. The augmentation callback for the model will be CutMix to help with generalization. We have the model train on mixed-precision floating points to speed up the process."},{"metadata":{"trusted":true},"cell_type":"code","source":"from fastai.callback.cutmix import *","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"learn = Learner(dls, cassava_net, loss_func=LabelSmoothingCrossEntropy(),\n               metrics=accuracy, cbs=CutMix()).to_fp16()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Find the learning rate to fine-tune the head of the model"},{"metadata":{"trusted":true},"cell_type":"code","source":"learn.lr_find()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Fine-tune using the Cosine Annealing approach (by using fit_flat_cos with pct_start=0.0). We use 3e-3 as the learning rate. We add a callback to reduce the learning rates after 3 epochs of no improvement in validation loss and a callback to save the model with the best performance during training."},{"metadata":{"trusted":true},"cell_type":"code","source":"learn.fit_flat_cos(20, lr=3e-3, pct_start=0.0,\n                  cbs=[ReduceLROnPlateau(patience=3),\n                      SaveModelCallback()])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"markdown","source":"# Save and export the model for inference"},{"metadata":{"trusted":true},"cell_type":"code","source":"learn.save('cassava_net')\nlearn.export('inference')","execution_count":null,"outputs":[]}],"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}