{"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":"markdown","source":"<a id=\"toc\"></a>\n# Table of content\n\n* [My other fast.ai notebooks](#links)\n* [Introduction](#introduction)\n* [Setup & Imports](#setup)\n    * [Imports](#intro.imports)\n    * [Constants](#intro.consts)\n* [Extract input](#extract_input)\n* [EDA](#EDA)\n* [Model](#model)\n    * [DataBlock](#model.datablock)\n    * [DataLoaders](#model.dataloaders)\n    * [Learner](#model.learner)\n    * [Find threshold](#model.threshold)\n* [Make predictions](#predictions)\n* [Save predictions](#save)\n* [Conclusions](#conclusions)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"markdown","source":"<a id=\"links\"></a>\n# My other fast.ai notebooks\n[[back to top]](#toc)","metadata":{}},{"cell_type":"markdown","source":"I'm going through [fast.ai's excellent book](https://course.fast.ai/start_colab#Opening-a-chapter-of-the-book) chapter by chapter. On the way I'm solving related problems on Kaggle. These are the notebooks which I created so far:\n\n* [Image classification - dog breeds (nov. 2021)](https://www.kaggle.com/angyalfold/fastai-imgclass-dog-breeds-step-by-step)\n* Image multi-label classification (this notebook)","metadata":{}},{"cell_type":"markdown","source":"<a id=\"introduction\"></a>\n# Introduction\n[[back to top]](#toc)","metadata":{}},{"cell_type":"markdown","source":"In this notebook I'm solving the [DSEG660: Multi-Label Image Classification competition](https://www.kaggle.com/c/hbku2019/overview) using [fast.ai](https://fast.ai). My solution is based on their [excellent tutorial](https://colab.research.google.com/github/fastai/fastbook/blob/master/06_multicat.ipynb). The aim of this notebook is to implement a solution which can serve as a baseline, hence there aren't any fancy optimizations in the code. My goal here is to create an easy-to-implement model.\n\nThe [first chapter](#setup) describes the basic setup which is required. The data is provided in compressed zip files, therefore the [following chapter](#extract_input) describes how to extract the data. Next, the [exploratory data analysis](#EDA) takes place.\n\nThe model is implemented in the [fourth chapter](#model), then [the predictions are made](#predictions) and then [saved](#save).","metadata":{}},{"cell_type":"markdown","source":"<a id=\"setup\"></a>\n# Setup & Imports\n[[back to top]](#toc)","metadata":{}},{"cell_type":"markdown","source":"<a id=\"intro.imports\"></a>\n## Imports\n[[back to top]](#toc)","metadata":{}},{"cell_type":"code","source":"from fastai.data.all import *\nfrom fastai.vision.all import *\n\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport os\nimport torch\nimport zipfile\n\nprint(\"Libraries imported.\")","metadata":{"execution":{"iopub.status.busy":"2021-11-10T09:34:38.021495Z","iopub.execute_input":"2021-11-10T09:34:38.021785Z","iopub.status.idle":"2021-11-10T09:34:38.028896Z","shell.execute_reply.started":"2021-11-10T09:34:38.021751Z","shell.execute_reply":"2021-11-10T09:34:38.028154Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"intro.consts\"></a>\n## Constants\n[[back to top]](#toc)","metadata":{}},{"cell_type":"code","source":"imgs_zip_path = \"../input/hbku2019/imgs.zip\"\nlabels_zip_path = \"../input/hbku2019/labels.zip\"\n\nsubmission_path = \"./submission.csv\"\n\nimgs_extracted_path = \"./\"\nlabels_extracted_path = \"./\"\n\ntrain_img_path = \"./imgs/train/\"\ntest_img_path = \"./imgs/test/\"\n\ncategories_csv_path = \"./labels/categories.csv\"\nlabels_train_csv_path = \"./labels/labels_train.csv\"\n\nprint(\"Constants defined.\")","metadata":{"execution":{"iopub.status.busy":"2021-11-10T09:34:38.030500Z","iopub.execute_input":"2021-11-10T09:34:38.030921Z","iopub.status.idle":"2021-11-10T09:34:38.043109Z","shell.execute_reply.started":"2021-11-10T09:34:38.030885Z","shell.execute_reply":"2021-11-10T09:34:38.042343Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"extract_input\"></a>\n# Extract input\n[[back to top]](#toc)","metadata":{}},{"cell_type":"code","source":"def extract_folder(zip_path, dest_folder):\n    with zipfile.ZipFile(zip_path, \"r\") as zip_ref:\n        namelist = zip_ref.namelist()\n        print(f'The number of files to extract: {len(namelist)}.')\n\n        # tqdm throws an error, that's why the ugly solution\n        for i, file in enumerate(namelist):\n            zip_ref.extract(member=file, path=dest_folder)\n            if i % 5000 == 0:\n                print(i)\n                \nprint(\"Extract images:\")\nextract_folder(imgs_zip_path, imgs_extracted_path)\nprint(\"DONE\")\n\nprint(\"Extract labels:\")\nextract_folder(labels_zip_path, labels_extracted_path)\nprint(\"DONE\")","metadata":{"execution":{"iopub.status.busy":"2021-11-10T09:34:38.044872Z","iopub.execute_input":"2021-11-10T09:34:38.045129Z","iopub.status.idle":"2021-11-10T09:36:37.479596Z","shell.execute_reply.started":"2021-11-10T09:34:38.045091Z","shell.execute_reply":"2021-11-10T09:36:37.478780Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"EDA\"></a>\n# EDA\n[[back to top]](#toc)","metadata":{}},{"cell_type":"markdown","source":"First, I examine the content of the `labels_train.csv` file. This is the file which contains the classes for each image in the training set. The first five elements:","metadata":{}},{"cell_type":"code","source":"labels_train_df = pd.read_csv(labels_train_csv_path, header=None)\nlabels_train_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-11-10T09:36:37.485018Z","iopub.execute_input":"2021-11-10T09:36:37.487195Z","iopub.status.idle":"2021-11-10T09:36:38.161405Z","shell.execute_reply.started":"2021-11-10T09:36:37.487148Z","shell.execute_reply":"2021-11-10T09:36:38.160630Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"... and the last five:","metadata":{}},{"cell_type":"code","source":"labels_train_df.tail()","metadata":{"execution":{"iopub.status.busy":"2021-11-10T09:36:38.162544Z","iopub.execute_input":"2021-11-10T09:36:38.162815Z","iopub.status.idle":"2021-11-10T09:36:38.196490Z","shell.execute_reply.started":"2021-11-10T09:36:38.162778Z","shell.execute_reply":"2021-11-10T09:36:38.195838Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(labels_train_df.shape)","metadata":{"execution":{"iopub.status.busy":"2021-11-10T09:36:38.198594Z","iopub.execute_input":"2021-11-10T09:36:38.198881Z","iopub.status.idle":"2021-11-10T09:36:38.207294Z","shell.execute_reply.started":"2021-11-10T09:36:38.198844Z","shell.execute_reply":"2021-11-10T09:36:38.206433Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The dimensions of this file is 97774X81, so there are 97774 images and there are 80 possible classes which could appear on a picture.","metadata":{}},{"cell_type":"code","source":"print(len(os.listdir(train_img_path)))","metadata":{"execution":{"iopub.status.busy":"2021-11-10T09:36:38.208348Z","iopub.execute_input":"2021-11-10T09:36:38.208583Z","iopub.status.idle":"2021-11-10T09:36:38.269981Z","shell.execute_reply.started":"2021-11-10T09:36:38.208557Z","shell.execute_reply":"2021-11-10T09:36:38.269188Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The number of images in the train folder is indeed 97774 so that matches.","metadata":{}},{"cell_type":"markdown","source":"Next, I'm checking the content of the `categories.csv` file:","metadata":{}},{"cell_type":"code","source":"categories_df = pd.read_csv(categories_csv_path, header=None)\ncategories_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-11-10T09:36:38.271027Z","iopub.execute_input":"2021-11-10T09:36:38.271336Z","iopub.status.idle":"2021-11-10T09:36:38.283422Z","shell.execute_reply.started":"2021-11-10T09:36:38.271300Z","shell.execute_reply":"2021-11-10T09:36:38.282425Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(categories_df.shape)","metadata":{"execution":{"iopub.status.busy":"2021-11-10T09:36:38.285685Z","iopub.execute_input":"2021-11-10T09:36:38.286167Z","iopub.status.idle":"2021-11-10T09:36:38.290675Z","shell.execute_reply.started":"2021-11-10T09:36:38.286132Z","shell.execute_reply":"2021-11-10T09:36:38.289992Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"There are 80 categories just like in the `labels_train.csv` file, so that matches too. The vocabulary (the names of the possible categories):","metadata":{}},{"cell_type":"code","source":"vocab = categories_df[0].tolist()\nprint(vocab)","metadata":{"execution":{"iopub.status.busy":"2021-11-10T09:36:38.292075Z","iopub.execute_input":"2021-11-10T09:36:38.292682Z","iopub.status.idle":"2021-11-10T09:36:38.299658Z","shell.execute_reply.started":"2021-11-10T09:36:38.292646Z","shell.execute_reply":"2021-11-10T09:36:38.298828Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The number of test images is:","metadata":{}},{"cell_type":"code","source":"print(len(os.listdir(test_img_path)))","metadata":{"execution":{"iopub.status.busy":"2021-11-10T09:36:38.301093Z","iopub.execute_input":"2021-11-10T09:36:38.301702Z","iopub.status.idle":"2021-11-10T09:36:38.323499Z","shell.execute_reply.started":"2021-11-10T09:36:38.301665Z","shell.execute_reply":"2021-11-10T09:36:38.322688Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"...so the model should produce 24444 predictions.","metadata":{}},{"cell_type":"markdown","source":"<a id=\"model\"></a>\n# Model\n[[back to top]](#toc)","metadata":{}},{"cell_type":"markdown","source":"To feed the training data into the convolutional network, first wee need to have [datablock](#model.datablock), which is essentially a blueprint about how to store the data. Then the [dataloaders](#model.dataloaders) can be made based on the datablocks. Next, the [model (learner)](#model.learner) is initialized and fine tuned. The model returns a probability for each category, therefore a [threshold](#model.threshold) needs to be found above which a category is considered present on an image.","metadata":{}},{"cell_type":"markdown","source":"<a id=\"model.datablock\"></a>\n## DataBlock\n[[back to top]](#toc)","metadata":{}},{"cell_type":"markdown","source":"The aim of fast.ai's [datablock API](https://docs.fast.ai/tutorial.datablock.html) is to create a blueprint which describes how to load and store the training data.\n\nThe `blocks` parameter stores a tuple which defines the type of the input data and the labels. In this case the input is an image (`ImageBlock`) and the labels are categories (`MultiCategoryBlock`). The categories are encoded (that's why `encoded=True`) which means that they are stored [one-hot-encoded](https://en.wikipedia.org/wiki/One-hot) (instead of actually showing the categories like \"car, hill, person\"). The possible categories are passed in the`vocab` parameter.\n\nThe `get_x` parameter defines how to fetch an image from one row of the csv file (assemble a path to the image using the first column) and the `get_y` defines how to collect the categories to the corresponding image (use the rest of the values as an integers from that row).\n\nThe `item_tfms` define the transformation which is performed on each image. As described in the documentation [RandomResizedCrop](https://docs.fast.ai/vision.augment.html#RandomResizedCrop) \"Picks a random scaled crop of an image and resize it to size\".","metadata":{}},{"cell_type":"code","source":"dblock = DataBlock(\n    blocks=(ImageBlock, MultiCategoryBlock(encoded=True, vocab=vocab)),\n    get_x=lambda df: train_img_path + '/' + df[0],\n    get_y=lambda df: df[1:].values.astype(int),\n    item_tfms=RandomResizedCrop(128, min_scale=0.35)\n)\n\nprint(\"Datablock is ready.\")","metadata":{"execution":{"iopub.status.busy":"2021-11-10T09:36:38.326403Z","iopub.execute_input":"2021-11-10T09:36:38.326654Z","iopub.status.idle":"2021-11-10T09:36:38.332795Z","shell.execute_reply.started":"2021-11-10T09:36:38.326621Z","shell.execute_reply":"2021-11-10T09:36:38.332000Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The next code snippet shows how the data looks. First, a [`DataSets` object](https://docs.fast.ai/data.core.html#Datasets) is created from a single row of the `labels_train.csv`. Then the independent (`x`) and the dependent varaibles (`y`) are printed.","metadata":{}},{"cell_type":"code","source":"ds = dblock.datasets(labels_train_df.iloc[[1]])\n\nx, y = ds.train[0]\n\nprint(x)\nprint('--')\nprint(y)","metadata":{"execution":{"iopub.status.busy":"2021-11-10T09:36:38.334317Z","iopub.execute_input":"2021-11-10T09:36:38.334844Z","iopub.status.idle":"2021-11-10T09:36:38.535100Z","shell.execute_reply.started":"2021-11-10T09:36:38.334806Z","shell.execute_reply":"2021-11-10T09:36:38.534177Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"model.dataloaders\"></a>\n## DataLoaders\n[[back to top]](#toc)","metadata":{}},{"cell_type":"markdown","source":"Once the [datablock](#model.datablock) is ready, the dataloaders can be created.","metadata":{}},{"cell_type":"code","source":"dls = dblock.dataloaders(labels_train_df)\ndls.show_batch(nrows=3, ncols=3)\n\nprint(\"Dataloaders created.\")","metadata":{"execution":{"iopub.status.busy":"2021-11-10T09:36:38.536657Z","iopub.execute_input":"2021-11-10T09:36:38.536940Z","iopub.status.idle":"2021-11-10T09:36:41.286058Z","shell.execute_reply.started":"2021-11-10T09:36:38.536905Z","shell.execute_reply":"2021-11-10T09:36:41.285322Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"model.learner\"></a>\n## Learner\n[[back to top]](#toc)","metadata":{}},{"cell_type":"markdown","source":"A convolutional neural network seems to be the appropriate choice to solve this problem. A CNN can be created by calling fast.ai's `cnn_learner` method. The`dls` parameter is the previously initialized `DataLoaders` object and [resnet50](https://www.kaggle.com/pytorch/resnet50) is a pretrained model. The idea is that it might be more effective to further train a model which is already capable of recognizing everyday objects, rathar than starting the whole process from scratch (transfer learning).\n\nWhen a model is based on a pretrained model it has two parts: one is the pretrained model and the other is a custom head on the top of the pretrained model. It is reasonable to assume that most of the learning takes place in the custom head and therefore it makes sense not to train the pretrained model for a couple of epochs (the pretrained part could be \"freezed\"). The `freeze_epochs` parameter determines the number of epochs in the begining of the training during which the wheights are not changed in the pretrained model. There are 11 epochs in total in the code below: 6 in which the pretrained model is freezed and then 5 other in which every weights could change.","metadata":{}},{"cell_type":"code","source":"learner = cnn_learner(dls, resnet50)\nlearner.fine_tune(5, base_lr=3e-3, freeze_epochs=6)","metadata":{"execution":{"iopub.status.busy":"2021-11-10T09:36:41.287415Z","iopub.execute_input":"2021-11-10T09:36:41.287896Z","iopub.status.idle":"2021-11-10T11:28:20.052482Z","shell.execute_reply.started":"2021-11-10T09:36:41.287856Z","shell.execute_reply":"2021-11-10T11:28:20.051588Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"An example of the epochs looks something like this:\n\n![epochs_and_losses.png](attachment:56bae47c-3f71-4b57-803d-8e7afab82875.png)","metadata":{},"attachments":{"56bae47c-3f71-4b57-803d-8e7afab82875.png":{"image/png":"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"}}},{"cell_type":"markdown","source":"<a id=\"model.threshold\"></a>\n## Find threshold\n[[back to top]](#toc)","metadata":{}},{"cell_type":"markdown","source":"The `learner` defined previously produces the probabilty of each category being present for a given picture. Therefore, a threshold has to be defined above which a category is considered to be present.\n\nThe way to find a threshold is to get the predictions for the validation set...","metadata":{}},{"cell_type":"code","source":"valids, targs = learner.get_preds() # predict from the validation set\n\nprint(valids)","metadata":{"execution":{"iopub.status.busy":"2021-11-10T11:28:20.054106Z","iopub.execute_input":"2021-11-10T11:28:20.054401Z","iopub.status.idle":"2021-11-10T11:30:28.463395Z","shell.execute_reply.started":"2021-11-10T11:28:20.054358Z","shell.execute_reply":"2021-11-10T11:30:28.462429Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"valids.shape","metadata":{"execution":{"iopub.status.busy":"2021-11-10T11:30:28.465006Z","iopub.execute_input":"2021-11-10T11:30:28.465366Z","iopub.status.idle":"2021-11-10T11:30:28.472197Z","shell.execute_reply.started":"2021-11-10T11:30:28.465325Z","shell.execute_reply":"2021-11-10T11:30:28.471259Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"...and plot the accuracy of the predictions using [fast.ai's `accuracy_multi`](https://docs.fast.ai/metrics.html#accuracy_multi):","metadata":{}},{"cell_type":"code","source":"xs = torch.linspace(0.05,0.95,29)\naccs = [accuracy_multi(valids, targs, thresh=i, sigmoid=False) for i in xs]\nplt.plot(xs,accs);","metadata":{"execution":{"iopub.status.busy":"2021-11-10T11:30:28.473354Z","iopub.execute_input":"2021-11-10T11:30:28.474267Z","iopub.status.idle":"2021-11-10T11:30:28.768192Z","shell.execute_reply.started":"2021-11-10T11:30:28.474231Z","shell.execute_reply":"2021-11-10T11:30:28.767498Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"0.5 seems to be a reasonable choice for the threshold (that's also the default value). An example of the chart could look something like this:\n\n![accuracy.png](attachment:4cff150f-cc00-4301-88eb-5f0fb293f307.png)","metadata":{},"attachments":{"4cff150f-cc00-4301-88eb-5f0fb293f307.png":{"image/png":"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"}}},{"cell_type":"code","source":"threshold = 0.5","metadata":{"execution":{"iopub.status.busy":"2021-11-10T11:30:28.769294Z","iopub.execute_input":"2021-11-10T11:30:28.770861Z","iopub.status.idle":"2021-11-10T11:30:28.774661Z","shell.execute_reply.started":"2021-11-10T11:30:28.770819Z","shell.execute_reply":"2021-11-10T11:30:28.773980Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"predictions\"></a>\n# Make predictions\n[[back to top]](#toc)","metadata":{}},{"cell_type":"markdown","source":"In order to make predictions, first the test images have to be loaded:","metadata":{}},{"cell_type":"code","source":"test_imgs = get_image_files(test_img_path)\n\nprint(\"Test images loaded.\")","metadata":{"execution":{"iopub.status.busy":"2021-11-10T11:30:28.776109Z","iopub.execute_input":"2021-11-10T11:30:28.776594Z","iopub.status.idle":"2021-11-10T11:30:28.966546Z","shell.execute_reply.started":"2021-11-10T11:30:28.776555Z","shell.execute_reply":"2021-11-10T11:30:28.965777Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Then the `DataLoaders#test_dl` method can be used to create a `dataloader` for the test images (see the [related section of my other fast.ai notebook](https://www.kaggle.com/angyalfold/fastai-imgclass-dog-breeds-step-by-step#Make-predictions) for further details). Once the `dataloader` is ready, predictions can be made by passing it to `learner#get_preds`.","metadata":{}},{"cell_type":"code","source":"test_dataloader = learner.dls.test_dl(test_imgs)\npreds, _ = learner.get_preds(dl=test_dataloader)","metadata":{"execution":{"iopub.status.busy":"2021-11-10T11:30:28.967873Z","iopub.execute_input":"2021-11-10T11:30:28.968194Z","iopub.status.idle":"2021-11-10T11:33:00.612756Z","shell.execute_reply.started":"2021-11-10T11:30:28.968159Z","shell.execute_reply":"2021-11-10T11:33:00.612008Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(preds)","metadata":{"execution":{"iopub.status.busy":"2021-11-10T11:33:00.614419Z","iopub.execute_input":"2021-11-10T11:33:00.614664Z","iopub.status.idle":"2021-11-10T11:33:00.621190Z","shell.execute_reply.started":"2021-11-10T11:33:00.614633Z","shell.execute_reply":"2021-11-10T11:33:00.620446Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"save\"></a>\n# Save predictions\n[[back to top]](#toc)","metadata":{}},{"cell_type":"markdown","source":"Once the predictions are ready, they need to be saved into the `submission.csv` file. The expected format consists of two colums, one named `id` and the other `predictions`. The `id` column stores the entire file name (e.g.:`123.jpg` with the file extensions) and the predictions store the number of the categories separated with a space.","metadata":{}},{"cell_type":"code","source":"predicted_classes = [\"\" for i in range(len(test_imgs))]\n\n# i guess it's not the most pythonic, please advise me in the comments how to improve\nfor i, j in torch.nonzero(preds > threshold):\n    predicted_classes[i] += str(j.item())\n    predicted_classes[i] += \" \"\n\ndf = pd.DataFrame()\n\ndf[\"id\"] = [os.path.basename(test_img) for test_img in test_imgs]\ndf[\"values\"] = predicted_classes\n\ndf.info()\n\ndf.to_csv(submission_path, header=[\"id\", \"predictions\"], index=False)\n\nprint(\"Predictions saved to file.\")","metadata":{"execution":{"iopub.status.busy":"2021-11-10T11:33:00.622476Z","iopub.execute_input":"2021-11-10T11:33:00.623088Z","iopub.status.idle":"2021-11-10T11:33:01.621975Z","shell.execute_reply.started":"2021-11-10T11:33:00.623051Z","shell.execute_reply":"2021-11-10T11:33:01.621197Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"conclusions\"></a>\n# Conclusions\n[[back to top]](#toc)","metadata":{}},{"cell_type":"markdown","source":"In this notebook my aim was to create a simple model using fast.ai. The goal was not to build a state-of-the-art solution but to come up with a base-line model. Therefore, I didn't apply any fancy optimization technique. Still, the solution scores ~85% on the leaderboard.\n\nThe code is indeed fairly simple and consists of the following steps:\n\n1. Define a `DataBlock`:\n\n`dblock = DataBlock(\n    blocks=(ImageBlock, MultiCategoryBlock(encoded=True, vocab=vocab)),\n    get_x=lambda df: train_img_path + '/' + df[0],\n    get_y=lambda df: df[1:].values.astype(int),\n    item_tfms=RandomResizedCrop(128, min_scale=0.35)\n)`\n\n2. Create a `Dataloaders` object based on the `DataBlock`:\n\n`dls = dblock.dataloaders(labels_train_df)`\n\n3. Define the model:\n\n`learner = cnn_learner(dls, resnet50)`\n\n4. Fine tune it:\n\n`learner.fine_tune(5, base_lr=3e-3, freeze_epochs=6)`\n\n5. Find/double-check threshold:\n\n`valids, targs = learner.get_preds() # predict from the validation set\nxs = torch.linspace(0.05,0.95,29)\naccs = [accuracy_multi(valids, targs, thresh=i, sigmoid=False) for i in xs]\nplt.plot(xs,accs);`\n\n6. Load test data:\n\n`test_imgs = get_image_files(test_img_path)\ntest_dataloader = learner.dls.test_dl(test_imgs)`\n\n7. Make actual predictions:\n\n`preds, _ = learner.get_preds(dl=test_dataloader)`\n\nFast.ai indeed provides an easy way to implement models. It was surprising for me how similar the code is to the one which I implemented in [my previous notebook about fast.ai](https://www.kaggle.com/angyalfold/fastai-imgclass-dog-breeds-step-by-step/comments) which was about finding dog breeds of images: the only difference was in the initialization of the dataloaders. In the previous notebook the target block was categorical (which was hidden in an `ImageDataLoaders` object), whereas in this case it was a `MultiCategoryBlock`. Apart from this, the two solutions are pretty much the same.","metadata":{}}]}