{"cells":[{"metadata":{},"cell_type":"markdown","source":"As the title suggests, we will be using to fastai to achieve the best possible score with minimum lines of code.\n\nWe will also use **timm** for its pretrained models. Lets start by installing the latest version of both the libraries"},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install -U -q timm\n!pip install -U -q fastai","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Great, time to import both the libraries . . . "},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"from timm import create_model\nfrom fastai.vision.all import *","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"For experimentation, we will keeping the `config` at the top. \n\nNote: I prefer using dataclass, because dictionary syntax is very verbose. With dataclass, you can access the config values using `.` notation (that also means, tab auto-completion)"},{"metadata":{"trusted":true},"cell_type":"code","source":"from dataclasses import dataclass\n\n@dataclass\nclass config:\n    seed = 42\n    \n    # training\n    lr = 5e-2\n    epochs = 7\n    freeze_epochs = 3\n    arch = 'resnet200d_320'\n    \n    # data\n    bs = 16\n    fold = 2\n    img_size = 456\n    pre_img_size = 512","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Before we write any code, lets set the seed for reproducibility. We will use fastai's `set_seed` function."},{"metadata":{"trusted":true},"cell_type":"code","source":"set_seed(config.seed)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Next, we will initialize some path (& other) variables (for use throughout the notebook)\n\nPS: I will be using my version for the dataset. I have resized all the images so that its much faster to load them into the RAM."},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"path = Path('../input/ranzcr-clip-catheter-line-classification')\nresized_path = Path('../input/rancer-resized-dataset/img_sz_512')\nfolds_path = Path('../input/ranzcr-folds')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"targets = [\"ETT - Abnormal\", \"ETT - Borderline\", \"ETT - Normal\", \"NGT - Abnormal\",\n    \"NGT - Borderline\", \"NGT - Incompletely Imaged\", \"NGT - Normal\", \"CVC - Abnormal\",\n    \"CVC - Borderline\", \"CVC - Normal\", \"Swan Ganz Catheter Present\"]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Data\n\nEnough prep-work! Lets dive in . . ."},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.read_csv(folds_path/'train_folds.csv')\ndf.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Fastai's datablock API is amazing. Infinite flexibility and incredibly powerful. To use the datablock API, you need to define some functions."},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_x(x): return resized_path/'train'/(x['StudyInstanceUID']+'.jpg')\ndef get_y(y): return y[targets].tolist()\ndef splitter(df):\n    trn_idx = df[df.kfold != config.fold].index.to_list()\n    val_idx = df[df.kfold == config.fold].index.to_list()\n    return trn_idx, val_idx\n\ndb = DataBlock(blocks= (ImageBlock, MultiCategoryBlock(encoded=True, vocab=targets)),\n               get_x = get_x, \n               get_y = get_y,\n               splitter = splitter,\n               item_tfms = Resize(config.pre_img_size),\n               batch_tfms = [*aug_transforms(size=config.img_size, min_scale=0.9), \n                             Normalize.from_stats(*imagenet_stats)]\n               )","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Don't worry a lot if the above code looks cryptic. You can read the [6th notebook (or chapter)](https://github.com/fastai/fastbook/blob/master/06_multicat.ipynb) in fastbook. It explains the topic in the most simplest way possible. And once you master datablock API, you will feel like a Ninja (trust me on this)! \n\nYou are amazing! Now lets create our dataloaders, & then take a look at some images."},{"metadata":{"trusted":true},"cell_type":"code","source":"dls = db.dataloaders(df, bs=config.bs)\ndls.show_batch()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Looks great to me, What do you think?\n\nWe are done with data, time to build our deep learning model."},{"metadata":{},"cell_type":"markdown","source":"## Model\n\nWe will wrap timm's `create_model` function inside `create_timm_body` function (inspired from fastai's `create_body` function), to use fastai's esoteric features like differential learning rate.\n\nHere is an amazing [colab notebook](https://colab.research.google.com/github/muellerzr/Practical-Deep-Learning-for-Coders-2.0/blob/master/Computer%20Vision/05_EfficientNet_and_Custom_Weights.ipynb) to help you better understand \"how you can use custom models with fastai\". "},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_timm_body(arch:str, pretrained=True, cut=None):\n    model = create_model(arch, pretrained=pretrained)\n    if cut is None:\n        ll = list(enumerate(model.children()))\n        cut = next(i for i,o in reversed(ll) if has_pool_type(o))\n    if isinstance(cut, int): return nn.Sequential(*list(model.children())[:cut])\n    elif callable(cut): return cut(model)\n    else: raise NamedError(\"cut must be either integer or function\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Once we have the *model body*, we will calculate the *output* shape and create the *model head* accordingly. \n\nYou can easily calculate the output shape easily by using fastai's `num_features_model` function😉\n\nFinally, we will use *kaimming initialization* to initialize the *model head* weights.\n\nI know it sounds complex, but its just a few lines of code."},{"metadata":{"trusted":true},"cell_type":"code","source":"body = create_timm_body(config.arch, pretrained=True)\nnf = num_features_model(nn.Sequential(*body.children()))\nhead = create_head(nf, 11) \nmodel = nn.Sequential(body, head)\napply_init(model[1], nn.init.kaiming_normal_)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Learner\n\nTime to put everything together. Fastai has an awesome class for it, called `Learner`. "},{"metadata":{"trusted":true},"cell_type":"code","source":"learner = Learner(dls, model, \n                  splitter=default_split, \n                  loss_func=BCEWithLogitsLossFlat(),\n                  metrics=[accuracy_multi]).to_fp16()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"And finally, lets train (technically, finetuning 🤯) our model."},{"metadata":{"trusted":true},"cell_type":"code","source":"learner.fine_tune(config.epochs, freeze_epochs=config.freeze_epochs, base_lr=config.lr)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Amazing! Lets export the model so that we can deploy it to production 😂. Just kidding, we will (only) use it for inference."},{"metadata":{"trusted":true},"cell_type":"code","source":"learner.export(f'{config.arch}_foldx{config.fold}.pkl')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Looking forward to inference? "}],"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}