{"cells":[{"metadata":{},"cell_type":"markdown","source":"## What is fastai?\nFastai is a deep learning library built on top of Pytorch which makes it super easy to incorporate state-of-the-art techniques in a wide number of tasks such as Computer Vision, Natural Language Processing, Tabular classification and regression, Segmentation and what not. One of the main highlights of fastai is the ability of newbies at Deep Learning to follow the DO-LEARN-DO method of learning i.e understanding and practising code alongside prying open the mechanisms of deep learning packed with dense mathematics. This paradigm of learning accelerates work in a given field and ensures a holistic understanding of the subject matter. \n\nI thank Jeremy Howard and Rachel Thomas for bringing fastai to us."},{"metadata":{},"cell_type":"markdown","source":"## Installing Fastai\nLets start by installing the latest version of fastai with the following command.****"},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install fastai -q --upgrade","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Lets check whether the latest version of fastai has been installed. At the time of writing this kernel, the latest version seems to be version 2.2.5"},{"metadata":{"trusted":true},"cell_type":"code","source":"import fastai\nprint(fastai.__version__)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Importing Libraries\n\nWe will be requiring libraries for a number of tasks. We start of by importing some basic libraries inherent for various projects and additionally we import the vision library from fastai since our project is primarily a computer vision task.\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nfrom pathlib import Path\nfrom PIL import Image\nimport matplotlib.pyplot as plt\nfrom fastai.vision.all import *\nfrom fastai.callback.all import *","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\nWhile traditional programming practices recommend avoiding the importing the entire library, the fastai library is an exception as it is specifically designed to support this issue and will only import necessary pieces into the environment.   "},{"metadata":{},"cell_type":"markdown","source":"It is recommended that the root of the datafiles be saved as a *Path object*. This makes accessing the datafiles much easier."},{"metadata":{"trusted":true},"cell_type":"code","source":"path = Path('../input/digit-recognizer/')\nos.listdir(path)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train = pd.read_csv(path / 'train.csv')\ntest = pd.read_csv(path / 'test.csv')\nsub = pd.read_csv(path / 'sample_submission.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Our image data is stored as pixel values in every row of the dataframe along with the label pertaining to the image under the 'label' column. However fastai only accepts images as files for the given task. So our first task would be to convert the image pixel values from the dataframe to imagefiles and storing them in the respective folder of their label.\n"},{"metadata":{},"cell_type":"markdown","source":"## Adjusting the data"},{"metadata":{"trusted":true},"cell_type":"code","source":"TRAIN_DIR = Path('../TRAIN')\nTEST_DIR = Path('../TEST')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Lets start by creating a directory for each of the 10 numbers that we will be classifying"},{"metadata":{"trusted":true},"cell_type":"code","source":"for i in range(10):\n    try:\n        os.makedirs(TRAIN_DIR/str(i))\n    except:\n        pass","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sorted(os.listdir(TRAIN_DIR))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Similary we store all the test images in the Test folder on which we will be making our predictions."},{"metadata":{"trusted":true},"cell_type":"code","source":"try:\n    os.makedirs(TEST)\nexcept:\n    pass","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Now we shall write a function that converts an individual image array into PIL image object and stores it as a .jpg file."},{"metadata":{"trusted":true},"cell_type":"code","source":"def array_to_img_file(img_array, filepath):\n    img_array = img_array.reshape(28,28).astype(np.uint8)\n    img = Image.fromarray(img_array)\n    img.save(filepath)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for i, row in train.iterrows():\n    label, digit = row[0], row[1:]\n    folder = TRAIN_DIR / str(label)\n    filename = f'{i}.jpg'\n    filepath = folder/filename\n    img_array = digit.values\n    \n    array_to_img_file(img_array, filepath)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for i, digit in test.iterrows():\n    folder = TEST_DIR\n    filename = f'{i}.jpg'\n    filepaht = folder / filename\n    \n    array_to_img_file(img_array, filepath)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Data Augmentation"},{"metadata":{},"cell_type":"markdown","source":"The general motto around Deep Learning models is that 'More the data, better performs the model'. But in practice, there may not always be an abundance of usable data. How can then we get more data to enhance the performance of our models? The answer is data augmentation. Data Augmentation makes minor alterations to the input dataset by transforming, flipping, rotating etc. A major advantage of augmentation is that no extra memory is utilized to store the augmented images, the model will create the augmented image, learn from them and forget about them!\n\nHere we perform some basic augmentations."},{"metadata":{"trusted":true},"cell_type":"code","source":"batch_tfms = [*aug_transforms()]\nitem_tfms = RandomResizedCrop(460, min_scale = 0.75, ratio = (1.,1.))\nbs = 8","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"For natural photo images such as the ones we are using here, a standard set of augmentations that we have found work pretty well are provided with the aug_transforms function. To tell fastai we want to use these transforms on a batch, we use the batch_tfms parameter while item_tfms work on individual images."},{"metadata":{"trusted":true},"cell_type":"code","source":"os.path.abspath(TRAIN_DIR)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_path = Path('/kaggle/')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## DataLoaders"},{"metadata":{},"cell_type":"markdown","source":"DataLoaders is a class in fastai which is very essential to provide data to the model. It stores multiple dataloader objects that we pass to it, normally a train set and a validation set. It assembles our input data which is suitable to load to our model."},{"metadata":{"trusted":true},"cell_type":"code","source":"dls = ImageDataLoaders.from_folder(\n    path = train_path,\n    train = 'TRAIN',\n    valid_pct = 0.1,\n    seed = 44,\n    item_tfms = item_tfms,\n    batch_tfms = [*aug_transforms()],\n    bs = bs\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Here we define a new path for fastai to know where our TRAIN directory and TEST directory have been stored. Next up we split our training data into a train set comprising of 90% of data and a validation set to evaluate our model performace comprising of 10% of data. We define a seed to a number which will make sure that the same permutation of data is generated and distributed each time the kernel runs. We next add the data transforms and define a batch size. To ensure the notebook does not run out of memory, a smaller batch size is advisable."},{"metadata":{"trusted":true},"cell_type":"code","source":"dls.show_batch()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Here we are able to visualize our training data."},{"metadata":{"trusted":true},"cell_type":"code","source":"dls.vocab","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Learner and fitting the data to the model"},{"metadata":{},"cell_type":"markdown","source":"Fastai provides various state of the art models to be used with for image classification. We will need to make a model to train our data on. We shall use the smaller resnet18 for this specific task as our priorities concern performance along with speed. "},{"metadata":{"trusted":true},"cell_type":"code","source":"learn = cnn_learner(dls, resnet18, metrics = [accuracy])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Fastai also provides a function to find the optimal learning rate that might yield an optimal performance for the model."},{"metadata":{"trusted":true},"cell_type":"code","source":"learn.lr_find()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"For the learning rate, You don't want to pick the point at which loss is lowest; you want to pick the point at which it is dropping fastest per step. In this case it seems to drop at around 1e-3 to 1e-2 where the loss drops the steepest and then rises."},{"metadata":{"trusted":true},"cell_type":"code","source":"learn.fit_one_cycle(1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"About Freezing and Unfreezing: \n\nFreezing prevents the weights of a neural network layer from being modified during the backward pass of training. You progressively 'lock-in' the weights for each layer to reduce the amount of computation in the backward pass and decrease training time.\n\nYou can unfreeze a model if you decide you want to continue training - an example of this is transfer learning: start with a pre-trained model, unfreeze the weights, then continuing training on a different dataset. When you choose to freeze is a balance between freezing early enough to gain computational speed-up without freezing too early with weights that result in inaccurate predictions."},{"metadata":{"trusted":true},"cell_type":"code","source":"learn.unfreeze()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"learn.fit_one_cycle(3, lr_max = slice(1e-3, 1e-2))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Voila! We achieve great results with a few lines of code!\nIf you like this notebook please give an upvote as it would help me create more content like this.\n\nP.S - This is an incomplete notebook, complete one with interpretations and predictions will be updated soon."},{"metadata":{"trusted":true},"cell_type":"code","source":"","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}