{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Intended audience for this notebook"},{"metadata":{},"cell_type":"markdown","source":"I wrote this notebook after I completed watching lesson 1 & 2, and reading about datablock and dataset API in the links mentioned below\n1. https://asvcode.github.io/Blogs/fastai/augmentation/image-augmentation/2020/03/26/Fastai2-Image-Augmentation.html\n2. https://towardsdatascience.com/advanced-dataloaders-with-fastai2-ecea62a7d97e\n3. https://towardsdatascience.com/working-with-3d-data-fastai2-5e2baa09037e\n\nSo this notebook is geared towards beginners of fastai API"},{"metadata":{},"cell_type":"markdown","source":"# Strategy"},{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5"},"cell_type":"markdown","source":"Based on the first two lessons, the steps to create a `CNN` classification learner are\n1. create dataloader using either \n    - `ImageDataLoaders` or\n    - `DataBlock`\n2. feed the dataloader to create a learner using `cnn_learner`\n3. fine-tune the learner\n3. use `ClassificationInterpretation` to plot confusion matrix and top losses to understand performance of each label\n4. use `ImageClassifierCleaner` to clean images (This step may not be relevant to this challenge)"},{"metadata":{},"cell_type":"markdown","source":"# step 0"},{"metadata":{"trusted":true},"cell_type":"code","source":"import fastai\nimport shutil\n\nimport numpy as np\nimport pandas as pd\n\nfrom matplotlib import pyplot as plt\nfrom IPython.core.interactiveshell import InteractiveShell\nfrom fastai.vision.all import *\nfrom fastai.vision.core import *\n\nInteractiveShell.ast_node_interactivity = \"all\"\n\ntrain = False","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## add data sources"},{"metadata":{},"cell_type":"markdown","source":"Add `~/.torch` location to copy existing architectures to run the model"},{"metadata":{"trusted":true},"cell_type":"code","source":"model_loc = Path('/root/.cache/torch/hub/checkpoints/')\nif not model_loc.exists():\n    model_loc.mkdir(exist_ok=True, parents=True)\nmodel_loc.ls()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### existing architectures"},{"metadata":{"trusted":true},"cell_type":"code","source":"\nPath(\"../input/cassava-leaf-disease-classification/\").exists() == Path(\"/root/.cache/torch/hub/checkpoints/\").exists()\nshutil.copy(f\"{Path(os.getcwd()).parent}/input/cassava-modelresnet34fine-tune10pkl/resnet34-333f7ec4.pth\", \"/root/.cache/torch/hub/checkpoints/resnet34-333f7ec4.pth\")\nshutil.copy(f\"{Path(os.getcwd()).parent}/input/cassava-modelresnet34fine-tune10pkl/resnet50-19c8e357.pth\", \"/root/.cache/torch/hub/checkpoints/resnet50-19c8e357.pth\")","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a"},"cell_type":"markdown","source":"# step 1 - create dataloader"},{"metadata":{"trusted":true},"cell_type":"code","source":"fastai.__version__","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"path = Path('../input/cassava-leaf-disease-classification')\nos.listdir(path)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"labels = pd.read_csv(path / 'train.csv')\nlabels[\"image_id\"] = labels[\"image_id\"].apply(\n    lambda x: f'train_images/{x}')\nlabels.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## using ImageDataloader"},{"metadata":{},"cell_type":"markdown","source":"I used `ImageDataLoaders` to create dataloaders but datablocks or datasets api offers lot of flexibility in creating dataloaders"},{"metadata":{"trusted":true},"cell_type":"code","source":"dls = ImageDataLoaders.from_df(\n    labels, \n    path='../input/cassava-leaf-disease-classification/',\n    bs=64,\n    fn_col=0, \n    label_col=1, \n    seed=42,\n    valid_pct=0.2,\n    item_tfms=RandomResizedCrop(512, min_scale=0.75, ratio=(1.,1.)),\n    batch_tfms=aug_transforms(),\n    num_workers=0)\n\ndls.valid_ds.items[:3]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# step 2 - create learner"},{"metadata":{},"cell_type":"markdown","source":"`fp16` conversion can help reduce the gpu memory requirement and may slightly reduce the accuracy. But it offers an option to change the architecture from resnet34(until version 6) to resnet50 along with decent batch size. If you get any memory errors reduce the batch size (`bs`) or change the architecture \n\n`mixup` helps in regularizing the model as it is forced to learn not just a single label from an image but a combination of labels\n\nThese concepts (`mixup` and `fp16`) are not discussed in lesson 1 or 2. I haven't used these concepts until version 6 of this notebook. So if you want barebones fastai implementation, have a look at version 6 or earlier version of this notebook"},{"metadata":{"trusted":true},"cell_type":"code","source":"learn = cnn_learner(dls, resnet50, metrics=[error_rate, accuracy]).to_native_fp16()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# step 3 - fine tune the learner"},{"metadata":{},"cell_type":"markdown","source":"`Mixup` is added here instead of as option to `cnn_learner`. This is to restrict use of `Mixup` to learner training  "},{"metadata":{"trusted":true},"cell_type":"code","source":"if train:\n    learn.fine_tune(10, cbs=[MixUp(0.5)])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# step 4 - save model"},{"metadata":{},"cell_type":"markdown","source":"`cnn_learner` is converted to `fp32` so that there won't be issues during validation. I am entirely new to `fp16` training. This concept is not mentioned in lesson 1 or 2"},{"metadata":{"trusted":true},"cell_type":"code","source":"model_name = \"resnet50-fine_tune-10_resize-512\"\n\nif train:\n    learn.export(Path(f\"{Path(os.getcwd()).parent}/output/cassava-leaf-disease-classification/{model_name}.pkl\"))\nelse:\n    # The learner should be loaded on gpu to enable test time augumentation. Thanks to @muellerzr for the tip\n    learn = load_learner(Path(f\"{Path(os.getcwd()).parent}/input/cassava-modelresnet34fine-tune10pkl/{model_name}.pkl\"), cpu=False)\n    \nlearn = learn.to_native_fp32()\nlearn","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# step 5 - plot confusion matrix"},{"metadata":{"trusted":true},"cell_type":"code","source":"if train:\n    interp = ClassificationInterpretation.from_learner(learn)\n    interp.plot_confusion_matrix()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Cleaner"},{"metadata":{"trusted":true},"cell_type":"code","source":"if train:\n    from fastai.vision.widgets import *\n    cleaner = ImageClassifierCleaner(learn)\n    cleaner","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# submission\n\nTest dataloader is creating using the instructions mentioned [here]( https://forums.fast.ai/t/a-brief-guide-to-test-sets-in-v2-you-can-do-labelled-now-too/57054)"},{"metadata":{"trusted":true},"cell_type":"code","source":"test_labels = pd.read_csv(path / 'sample_submission.csv')\ntest_labels[\"image_id\"] = test_labels[\"image_id\"].apply(\n    lambda x: f'test_images/{x}')\n\ntest_labels.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_predictions = []\n\nfor i, r in test_labels.iterrows():\n    test_predictions.append(int(learn.predict(f'../input/cassava-leaf-disease-classification/{r.image_id}')[0]))\n    \ntest_predictions[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# test_dl = dls.test_dl(test_labels, with_labels=True)\n# test_dl.show_batch()\n# test_dl.items\n\n# preds = learn.get_preds(dl=test_dl)\n# # preds = learn.tta(dl=test_dl, n=8, beta=0) # I learnt this trick from fastai forums\n# test_predictions = preds[1]\n# test_predictions[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission = pd.read_csv(path / 'sample_submission.csv')\nsubmission['label'] = test_predictions\nsubmission.head()\nsubmission.to_csv('submission.csv',index=False)","execution_count":null,"outputs":[]},{"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}