{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Fetch the required libraries"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Installing bottleneck transformer library\n!pip install -q bottleneck-transformer-pytorch","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# Software library written for data manipulation and analysis.\nimport pandas as pd\n\n# Python library to interact with the file system.\nimport os\n\n# Python library for image augmentation\nimport albumentations as A\n\n# fastai library for computer vision tasks\nfrom fastai.vision.all import *\n\n# Developing and training neural network based deep learning models.\nimport torch\nfrom torch import nn\nfrom torchvision.models import resnet101\n\n# BotNet\nfrom bottleneck_transformer_pytorch import BottleStack","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Define path to dataset, whose benefit is that this sample is more balanced than original train data.\npath = Path('../input/hpa-cell-tiles-sample-balanced-dataset')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.read_csv(path/'cell_df.csv')\ndf.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# extract the the total number of target labels\nlabels = [str(i) for i in range(19)]\nfor x in labels: df[x] = df['image_labels'].apply(lambda r: int(x in r.split('|')))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Here a sample of the dataset has been taken, change frac to 1 to train the entire dataset!\ndfs = df.sample(frac=0.1, random_state=42)\ndfs = dfs.reset_index(drop=True)\nlen(dfs)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data Preprocessing"},{"metadata":{"trusted":true},"cell_type":"code","source":"# obtain the input images.\ndef get_x(r): \n    return path/'cells'/(r['image_id']+'_'+str(r['cell_id'])+'.jpg')\n\n# obtain the targets.\ndef get_y(r): \n    return r['image_labels'].split('|')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"'''AlbumentationsTransform will perform different transforms over both\n   the training and validation datasets ''' \nclass AlbumentationsTransform(RandTransform):\n    \n    '''split_idx is None, which allows for us to say when we're setting our split_idx.\n       We set an order to 2 which means any resize operations are done first before our new transform. '''\n    split_idx, order = None, 2\n    \n    def __init__(self, train_aug, valid_aug): store_attr()\n    \n    # Inherit from RandTransform, allows for us to set that split_idx in our before_call.\n    def before_call(self, b, split_idx):\n        self.idx = split_idx\n    \n    # If split_idx is 0, run the trainining augmentation, otherwise run the validation augmentation. \n    def encodes(self, img: PILImage):\n        if self.idx == 0:\n            aug_img = self.train_aug(image=np.array(img))['image']\n        else:\n            aug_img = self.valid_aug(image=np.array(img))['image']\n        return PILImage.create(aug_img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_train_aug(size): \n    \n    return A.Compose([\n            # allows to combine RandomCrop and RandomScale\n            A.RandomResizedCrop(size,size),\n            \n            # Transpose the input by swapping rows and columns.\n            A.Transpose(p=0.5),\n        \n            # Flip the input horizontally around the y-axis.\n            A.HorizontalFlip(p=0.5),\n        \n            # Flip the input horizontally around the x-axis.\n            A.VerticalFlip(p=0.5),\n        \n            # Randomly apply affine transforms: translate, scale and rotate the input.\n            A.ShiftScaleRotate(p=0.5),\n        \n            # Randomly change hue, saturation and value of the input image.\n            A.HueSaturationValue(hue_shift_limit=0.2, sat_shift_limit=0.2, val_shift_limit=0.2, p=0.5),\n        \n            # Randomly change brightness and contrast of the input image.\n            A.RandomBrightnessContrast(brightness_limit=(-0.1,0.1), contrast_limit=(-0.1, 0.1), p=0.5),\n        \n            # CoarseDropout of the rectangular regions in the image.\n            A.CoarseDropout(p=0.5),\n        \n            # CoarseDropout of the square regions in the image.\n            A.Cutout(p=0.5) ])\n\ndef get_valid_aug(size): \n    \n    return A.Compose([\n    # Crop the central part of the input.   \n    A.CenterCrop(size, size, p=1.),\n    \n    # Resize the input to the given height and width.    \n    A.Resize(size,size)], p=1.)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"'''The first step item_tfms resizes all the images to the same size (this happens on the CPU) \n   and then batch_tfms happens on the GPU for the entire batch of images. '''\n# Transforms we need to do for each image in the dataset\nitem_tfms = [Resize(224), AlbumentationsTransform(get_train_aug(224), get_valid_aug(224))]\n\n# Transforms that can take place on a batch of images\nbatch_tfms = [Normalize.from_stats(*imagenet_stats)]\n\nbs=6","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dblock = DataBlock(blocks=(ImageBlock, MultiCategoryBlock(vocab=labels)), # multi-label target\n                splitter=RandomSplitter(seed=42), # split data into training and validation subsets.\n                get_x=get_x, # obtain the input images.\n                get_y=get_y,  # obtain the targets.\n                item_tfms=item_tfms,\n                batch_tfms=batch_tfms\n                )\n\ndls = dblock.dataloaders(dfs, bs=bs)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# We can call show_batch() to see what a sample of a batch looks like.\ndls.show_batch()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model Definition"},{"metadata":{"trusted":true},"cell_type":"code","source":"layer = BottleStack(\n    # channels in\n    dim = 256, \n    \n    # feature map size\n    fmap_size = 56,  \n    \n    # channels out\n    dim_out = 2048, \n    \n    # projection factor\n    proj_factor = 4,\n    \n    # downsample on first layer or not\n    downsample = True, \n    \n    # number of heads\n    heads = 4, \n    \n    # dimension per head, defaults to 128\n    dim_head = 128,    \n    \n    # use relative positional embedding - uses absolute if False\n    rel_pos_emb = False, \n    \n    # activation throughout the network\n    activation = nn.ReLU()  \n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# define the backbone architecture\nresnet = resnet101()\n\n# extract the backbone layers\nbackbone = list(resnet.children())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# define the model architecture for BotNet\nmodel = nn.Sequential(*backbone[:5],\n                      layer,\n                      nn.AdaptiveAvgPool2d((1, 1)),\n                      nn.Flatten(1),\n                      nn.Linear(2048, 19))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Group together some dls, a model, and metrics to handle training\nlearn = Learner(dls, model, metrics= accuracy_multi)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Choosing a good learning rate\nlearn.lr_find()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# We can use the fine_tune function to train a model with this given learning rate\nlearn.fine_tune(4,0.0008317637839354575)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Plot training and validation losses.\nlearn.recorder.plot_loss()","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}