{"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":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport matplotlib.pyplot as plt\nfrom plotly.subplots import make_subplots\nimport plotly.graph_objs as go\nimport copy\nimport os\nimport torch\nfrom PIL import Image\nfrom PIL import Image, ImageDraw\nfrom torch.utils.data import Dataset\nimport torchvision.transforms as transforms\nfrom torch.utils.data import random_split\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau\nimport torch.nn as nn\nfrom torchvision import utils\n%matplotlib inline","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_kg_hide-input":false,"execution":{"iopub.status.busy":"2022-12-25T14:44:30.273192Z","iopub.execute_input":"2022-12-25T14:44:30.273893Z","iopub.status.idle":"2022-12-25T14:44:32.452587Z","shell.execute_reply.started":"2022-12-25T14:44:30.273802Z","shell.execute_reply":"2022-12-25T14:44:32.451164Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# library which allows us to view model summary like keras/tf\n!pip install torchsummary","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:44:43.977895Z","iopub.execute_input":"2022-12-25T14:44:43.978344Z","iopub.status.idle":"2022-12-25T14:44:54.820429Z","shell.execute_reply.started":"2022-12-25T14:44:43.978304Z","shell.execute_reply":"2022-12-25T14:44:54.819479Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from IPython.core.display import display, HTML, Javascript\n\ncolor_map = ['#FFFFFF','#FF5733']\n\nprompt = color_map[-1]\nmain_color = color_map[0]\nstrong_main_color = color_map[1]\ncustom_colors = [strong_main_color, main_color]\n\ncss_file = '''\ndiv #notebook {\nbackground-color: white;\nline-height: 20px;\n}\n\n#notebook-container {\n%s\nmargin-top: 2em;\npadding-top: 2em;\nborder-top: 4px solid %s;\n-webkit-box-shadow: 0px 0px 8px 2px rgba(224, 212, 226, 0.5);\n    box-shadow: 0px 0px 8px 2px rgba(224, 212, 226, 0.5);\n}\n\ndiv .input {\nmargin-bottom: 1em;\n}\n\n.rendered_html h1, .rendered_html h2, .rendered_html h3, .rendered_html h4, .rendered_html h5, .rendered_html h6 {\ncolor: %s;\nfont-weight: 600;\n}\n\ndiv.input_area {\nborder: none;\n    background-color: %s;\n    border-top: 2px solid %s;\n}\n\ndiv.input_prompt {\ncolor: %s;\n}\n\ndiv.output_prompt {\ncolor: %s; \n}\n\ndiv.cell.selected:before, div.cell.selected.jupyter-soft-selected:before {\nbackground: %s;\n}\n\ndiv.cell.selected, div.cell.selected.jupyter-soft-selected {\n    border-color: %s;\n}\n\n.edit_mode div.cell.selected:before {\nbackground: %s;\n}\n\n.edit_mode div.cell.selected {\nborder-color: %s;\n\n}\n'''\n\ndef to_rgb(h): \n    return tuple(int(h[i:i+2], 16) for i in [0, 2, 4])\n\nmain_color_rgba = 'rgba(%s, %s, %s, 0.1)' % (to_rgb(main_color[1:]))\nopen('notebook.css', 'w').write(css_file % ('width: 95%;', main_color, main_color, main_color_rgba, \n                                            main_color,  main_color, prompt, main_color, main_color, \n                                            main_color, main_color))\n\ndef nb(): \n    return HTML(\"<style>\" + open(\"notebook.css\", \"r\").read() + \"</style>\")\nnb()\n","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-25T14:44:54.822699Z","iopub.execute_input":"2022-12-25T14:44:54.823444Z","iopub.status.idle":"2022-12-25T14:44:54.838783Z","shell.execute_reply.started":"2022-12-25T14:44:54.823392Z","shell.execute_reply":"2022-12-25T14:44:54.837804Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"![](https://i.imgur.com/0oW3bQA.png)","metadata":{}},{"cell_type":"markdown","source":"## <b>1 <span style='color:#F1A424'>|</span> Introduction</b> \n\n<div style=\"color:white;display:fill;border-radius:8px;\n            background-color:#03112A;font-size:150%;\n            letter-spacing:1.0px;background-image: url(https://i.imgur.com/GVd0La1.png)\">\n    <p style=\"padding: 8px;color:white;\"><b><b><span style='color:#F1A424'>1.1 |</span></b> Why do we need this study?</b></p>\n</div>\n\n- Microscopic evaluation of histopathalogic stained tissue & its subsequent digitalisation is now a more feasible due to the advances in slide scanning technology, as well a reduction in digital storage cost in recent years\n- There are certain advantages that come with such digitalised pathology; including remote diagnosis, instant archival access & simplified procedure of consultations with expert pathologists\n- Digitalised Analysis based on Deep Learning has shown potential benefits as a potential diagnosis tool & strategy\n- [Gulshan et al](https://jamanetwork.com/journals/jama/fullarticle/2588763) and [Esteva et al](https://pubmed.ncbi.nlm.nih.gov/28117445/) demonstrated the <b>potential of deep learning for diabetic retinopathy screening</b> and <b>skin lesion classification</b>, respectively\n- An essential task performed by pathologist; accurate breast cancer staging \n- Assessment of the extent of cancer spread by histopathological analysis of sentinel axillary lymph nodes (SLNs) is an essential part of breast cancer staging process","metadata":{}},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;border-radius:8px;\n            background-color:#03112A;font-size:150%;\n            letter-spacing:1.0px;background-image: url(https://i.imgur.com/GVd0La1.png)\">\n    <p style=\"padding: 8px;color:white;\"><b><b><span style='color:#F1A424'>1.2 |</span></b> Problem Statement</b></p>\n</div>\n\n- The sensitivity of SLN assessment by pathologists, however, is not optimal. A retrospective study showed that pathology review by experts changed the nodal status in 24% of patients.\n- SLN assessment is <b>tedious</b> and <b>time-consuming</b>. It has been shown that deep learning algorithms could identify metastases in SLN slides with 100% sensitivity, whereas 40% of the slides without metastases could be identified as such\n- This could result in a <b>significant reduction in the workload</b> of pathologists","metadata":{}},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;border-radius:8px;\n            background-color:#03112A;font-size:150%;\n            letter-spacing:1.0px;background-image: url(https://i.imgur.com/GVd0La1.png)\">\n    <p style=\"padding: 8px;color:white;\"><b><b><span style='color:#F1A424'>1.3 |</span></b> Study Aim</b></p>\n</div>\n\nThe aim of this study was to investigate the potential of using **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">PyTorch</mark>** Deep Learning module:\n  - For the <b>detection of metastases</b> in SLN slides and compare them with the predefined pathologist diagnosis.","metadata":{}},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;border-radius:8px;\n            background-color:#03112A;font-size:150%;\n            letter-spacing:1.0px;background-image: url(https://i.imgur.com/GVd0La1.png)\">\n    <p style=\"padding: 8px;color:white;\"><b><b><span style='color:#F1A424'>1.4 |</span></b> Playground Prediction Competition</b></p>\n</div>\n\n#### **<span style='color:#F1A424'>OVERVIEW</span>**\n\nIn this competition, you must create an algorithm to identify metastatic cancer in small image patches taken from larger digital pathology scans. The data for this competition is a slightly modified version of the PatchCamelyon (PCam) benchmark dataset (the original PCam dataset contains duplicate images due to its probabilistic sampling, however, the version presented on Kaggle does not contain duplicates).\n\nPCam is highly interesting for both its size, simplicity to get started on, and approachability. In the authors' words:\n\n> [PCam] packs the clinically-relevant task of metastasis detection into a straight-forward binary image classification task, akin to CIFAR-10 and MNIST. Models can easily be trained on a single GPU in a couple hours, and achieve competitive scores in the Camelyon16 tasks of tumor detection and whole-slide image diagnosis. Furthermore, the balance between task-difficulty and tractability makes it a prime suspect for fundamental machine learning research on topics as active learning, model uncertainty, and explainability.\n\n#### **<span style='color:#F1A424'>SUBMISSION FORMAT</span>**\n\nFor each id in the test set:\n - You must predict a probability that center 32x32px region of a patch contains at least one pixel of tumor tissue. \n - The file should contain a header and have the following format: \n\nid,label <br>\n<code>0b2ea2a822ad23fdb1b5dd26653da899fbd2c0d5,0</code>","metadata":{}},{"cell_type":"markdown","source":"## <b>2 <span style='color:#F1A424'>|</span> Dataset</b> \n\n<div style=\"color:white;display:fill;border-radius:8px;\n            background-color:#03112A;font-size:150%;\n            letter-spacing:1.0px;background-image: url(https://i.imgur.com/GVd0La1.png)\">\n    <p style=\"padding: 8px;color:white;\"><b><b><span style='color:#F1A424'>2.1 |</span></b> Load Dataset</b></p>\n</div>\n\n- Load the dataset information file; <code>train_labels.csv</code>, it contains a reference to an image ID (id) & its classification allocation (label)","metadata":{}},{"cell_type":"code","source":"labels_df = pd.read_csv('/kaggle/input/histopathologic-cancer-detection/train_labels.csv')\nprint(labels_df.head().to_markdown())","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:44:58.115896Z","iopub.execute_input":"2022-12-25T14:44:58.116277Z","iopub.status.idle":"2022-12-25T14:44:58.746599Z","shell.execute_reply.started":"2022-12-25T14:44:58.116244Z","shell.execute_reply":"2022-12-25T14:44:58.745546Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"os.listdir('/kaggle/input/histopathologic-cancer-detection/')","metadata":{"execution":{"iopub.status.busy":"2022-12-24T21:21:29.074928Z","iopub.execute_input":"2022-12-24T21:21:29.075765Z","iopub.status.idle":"2022-12-24T21:21:29.083597Z","shell.execute_reply.started":"2022-12-24T21:21:29.075724Z","shell.execute_reply":"2022-12-24T21:21:29.082521Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels_df.shape","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:45:03.865253Z","iopub.execute_input":"2022-12-25T14:45:03.865571Z","iopub.status.idle":"2022-12-25T14:45:03.873666Z","shell.execute_reply.started":"2022-12-25T14:45:03.865547Z","shell.execute_reply":"2022-12-25T14:45:03.872463Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;border-radius:8px;\n            background-color:#03112A;font-size:150%;\n            letter-spacing:1.0px;background-image: url(https://i.imgur.com/GVd0La1.png)\">\n    <p style=\"padding: 8px;color:white;\"><b><b><span style='color:#F1A424'>2.2 |</span></b> Check for duplicate entries</b></p>\n</div>\n\n- Check if the dataset contains any duplicates, if there is we should drop them, which we have none","metadata":{}},{"cell_type":"code","source":"# No duplicate ids found\nlabels_df[labels_df.duplicated(keep=False)]","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:45:05.796430Z","iopub.execute_input":"2022-12-25T14:45:05.796905Z","iopub.status.idle":"2022-12-25T14:45:05.887435Z","shell.execute_reply.started":"2022-12-25T14:45:05.796864Z","shell.execute_reply":"2022-12-25T14:45:05.886524Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;border-radius:8px;\n            background-color:#03112A;font-size:150%;\n            letter-spacing:1.0px;background-image: url(https://i.imgur.com/GVd0La1.png)\">\n    <p style=\"padding: 8px;color:white;\"><b><b><span style='color:#F1A424'>2.3 |</span></b> Target feature class balance</b></p>\n</div>\n\nDefinitely not as one sides as was expected:\n- The dataset favours **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">non- malignant</mark>**, normal cases (13k) \n- compared to **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">non-malignant</mark>** cases (8.9k)","metadata":{}},{"cell_type":"code","source":"labels_df['label'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2022-12-24T21:21:29.188656Z","iopub.execute_input":"2022-12-24T21:21:29.189030Z","iopub.status.idle":"2022-12-24T21:21:29.200460Z","shell.execute_reply.started":"2022-12-24T21:21:29.188994Z","shell.execute_reply":"2022-12-24T21:21:29.199123Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;border-radius:8px;\n            background-color:#03112A;font-size:150%;\n            letter-spacing:1.0px;background-image: url(https://i.imgur.com/GVd0La1.png)\">\n    <p style=\"padding: 8px;color:white;\"><b><b><span style='color:#F1A424'>2.4 |</span></b> Dataset preview</b></p>\n</div>\n\nLet's also visualise the dataset images:\n- **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">non-malignant</mark>** cases (0) (outligned with green colour)\n- **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">malignant</mark>** cases (1) (outligned with red colour)\n\n\n- We can note that its quite a **<span style='color:#F1A424'>challenge to distinguish</span>** whether an image should be classified as **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">malignant</mark>** or **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">non-malignant</mark>** simply from an inspection\n\n\n- An **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">expert evaluation</mark>** is quite beneficial: \n    - However it is likely a very **<span style='color:#F1A424'>time consuming procedure</span>** as indicated in the introduction","metadata":{}},{"cell_type":"code","source":"imgpath =\"/kaggle/input/histopathologic-cancer-detection/train/\" # training data is stored in this folder\nmalignant = labels_df.loc[labels_df['label']==1]['id'].values    # get the ids of malignant cases\nnormal = labels_df.loc[labels_df['label']==0]['id'].values       # get the ids of the normal cases\n\nprint('normal ids')\nprint(normal[0:3],'\\n')\n\nprint('malignant ids')\nprint(malignant[0:3])","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-25T14:45:08.978017Z","iopub.execute_input":"2022-12-25T14:45:08.978564Z","iopub.status.idle":"2022-12-25T14:45:08.999118Z","shell.execute_reply.started":"2022-12-25T14:45:08.978533Z","shell.execute_reply":"2022-12-25T14:45:08.998179Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_fig(ids,title,nrows=5,ncols=15):\n\n    fig,ax = plt.subplots(nrows,ncols,figsize=(18,6))\n    plt.subplots_adjust(wspace=0, hspace=0) \n    for i,j in enumerate(ids[:nrows*ncols]):\n        fname = os.path.join(imgpath ,j +'.tif')\n        img = Image.open(fname)\n        idcol = ImageDraw.Draw(img)\n        idcol.rectangle(((0,0),(95,95)),outline='white')\n        plt.subplot(nrows, ncols, i+1) \n        plt.imshow(np.array(img))\n        plt.axis('off')\n\n    plt.suptitle(title, y=0.94)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-24T21:47:05.476751Z","iopub.execute_input":"2022-12-24T21:47:05.477155Z","iopub.status.idle":"2022-12-24T21:47:05.485323Z","shell.execute_reply.started":"2022-12-24T21:47:05.477123Z","shell.execute_reply":"2022-12-24T21:47:05.484253Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_fig(malignant,'Malignant Cases')","metadata":{"execution":{"iopub.status.busy":"2022-12-24T21:47:06.254894Z","iopub.execute_input":"2022-12-24T21:47:06.255952Z","iopub.status.idle":"2022-12-24T21:47:08.632611Z","shell.execute_reply.started":"2022-12-24T21:47:06.255914Z","shell.execute_reply":"2022-12-24T21:47:08.631420Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_fig(normal,'Non-Malignant Cases')","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-24T21:47:13.834155Z","iopub.execute_input":"2022-12-24T21:47:13.834514Z","iopub.status.idle":"2022-12-24T21:47:16.177196Z","shell.execute_reply.started":"2022-12-24T21:47:13.834486Z","shell.execute_reply":"2022-12-24T21:47:16.176114Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <b>3 <span style='color:#F1A424'>|</span> Data Preparation</b> \n\n<div style=\"color:white;display:fill;border-radius:8px;\n            background-color:#03112A;font-size:150%;\n            letter-spacing:1.0px;background-image: url(https://i.imgur.com/GVd0La1.png)\">\n    <p style=\"padding: 8px;color:white;\"><b><b><span style='color:#F1A424'>3.1 |</span></b> Custom dataset class</b></p>\n</div>\n\n- Let's create a custom <code>Dataset</code> class by subclassing the <code>Pytorch Dataset</code> class:\n    - We need just two essential fuctions <code>__len__</code> & <code>__getitem__</code> in our custom class       \n- To speed up the training process, were using only 4000 samples for the entire dataset (","metadata":{}},{"cell_type":"code","source":"torch.manual_seed(0) # fix random seed\n\nclass pytorch_data(Dataset):\n    \n    def __init__(self,data_dir,transform,data_type=\"train\"):      \n    \n        # Get Image File Names\n        cdm_data=os.path.join(data_dir,data_type)  # directory of files\n        \n        file_names = os.listdir(cdm_data) # get list of images in that directory  \n        idx_choose = np.random.choice(np.arange(len(file_names)), \n                                      4000,\n                                      replace=False).tolist()\n        file_names_sample = [file_names[x] for x in idx_choose]\n        self.full_filenames = [os.path.join(cdm_data, f) for f in file_names_sample]   # get the full path to images\n        \n        # Get Labels\n        labels_data=os.path.join(data_dir,\"train_labels.csv\") \n        labels_df=pd.read_csv(labels_data)\n        labels_df.set_index(\"id\", inplace=True) # set data frame index to id\n        self.labels = [labels_df.loc[filename[:-4]].values[0] for filename in file_names_sample]  # obtained labels from df\n        self.transform = transform\n      \n    def __len__(self):\n        return len(self.full_filenames) # size of dataset\n      \n    def __getitem__(self, idx):\n        # open image, apply transforms and return with label\n        image = Image.open(self.full_filenames[idx])  # Open Image with PIL\n        image = self.transform(image) # Apply Specific Transformation to Image\n        return image, self.labels[idx]\n","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:45:13.457436Z","iopub.execute_input":"2022-12-25T14:45:13.457937Z","iopub.status.idle":"2022-12-25T14:45:13.472595Z","shell.execute_reply.started":"2022-12-25T14:45:13.457907Z","shell.execute_reply":"2022-12-25T14:45:13.471315Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# define transformation that converts a PIL image into PyTorch tensors\nimport torchvision.transforms as transforms\ndata_transformer = transforms.Compose([transforms.ToTensor(),\n                                       transforms.Resize((46,46))])","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:45:15.857049Z","iopub.execute_input":"2022-12-25T14:45:15.857442Z","iopub.status.idle":"2022-12-25T14:45:15.862496Z","shell.execute_reply.started":"2022-12-25T14:45:15.857412Z","shell.execute_reply":"2022-12-25T14:45:15.861274Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define an object of the custom dataset for the train folder.\ndata_dir = '/kaggle/input/histopathologic-cancer-detection/'\nimg_dataset = pytorch_data(data_dir, data_transformer, \"train\") # Histopathalogic images","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:45:17.956968Z","iopub.execute_input":"2022-12-25T14:45:17.957523Z","iopub.status.idle":"2022-12-25T14:45:28.622769Z","shell.execute_reply.started":"2022-12-25T14:45:17.957493Z","shell.execute_reply":"2022-12-25T14:45:28.621787Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# load an example tensor\nimg,label=img_dataset[10]\nprint(img.shape,torch.min(img),torch.max(img))","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:45:32.279313Z","iopub.execute_input":"2022-12-25T14:45:32.279679Z","iopub.status.idle":"2022-12-25T14:45:32.314391Z","shell.execute_reply.started":"2022-12-25T14:45:32.279649Z","shell.execute_reply":"2022-12-25T14:45:32.312504Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <b>4 <span style='color:#F1A424'>|</span> Splitting the Dataset</b> \n\n<div style=\"color:white;display:fill;border-radius:8px;\n            background-color:#03112A;font-size:150%;\n            letter-spacing:1.0px;background-image: url(https://i.imgur.com/GVd0La1.png)\">\n    <p style=\"padding: 8px;color:white;\"><b><b><span style='color:#F1A424'>4.1 |</span></b> random_split</b></p>\n</div>\n\n- Among the training set, we need to evaluate the model on validation datasets to track the model's performance during training.\n- Let's use 20% of img_dataset for **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">validation</mark>** & use the rest as the **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">training</mark>** set, so we have a 80/20 split","metadata":{}},{"cell_type":"code","source":"len_img=len(img_dataset)\nlen_train=int(0.8*len_img)\nlen_val=len_img-len_train\n\n# Split Pytorch tensor\ntrain_ts,val_ts=random_split(img_dataset,\n                             [len_train,len_val]) # random split 80/20\n\nprint(\"train dataset size:\", len(train_ts))\nprint(\"validation dataset size:\", len(val_ts))","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:47:18.757507Z","iopub.execute_input":"2022-12-25T14:47:18.758059Z","iopub.status.idle":"2022-12-25T14:47:18.766316Z","shell.execute_reply.started":"2022-12-25T14:47:18.758018Z","shell.execute_reply":"2022-12-25T14:47:18.765209Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# getting the torch tensor image & target variable\nii=-1\nfor x,y in train_ts:\n    print(x.shape,y)\n    ii+=1\n    if(ii>5):\n        break","metadata":{"execution":{"iopub.status.busy":"2022-12-24T18:59:59.736393Z","iopub.execute_input":"2022-12-24T18:59:59.736837Z","iopub.status.idle":"2022-12-24T18:59:59.779313Z","shell.execute_reply.started":"2022-12-24T18:59:59.736778Z","shell.execute_reply":"2022-12-24T18:59:59.778299Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import plotly.express as px\n\ndef plot_img(x,y,title=None):\n\n    npimg = x.numpy() # convert tensor to numpy array\n    npimg_tr=np.transpose(npimg, (1,2,0)) # Convert to H*W*C shape\n    fig = px.imshow(npimg_tr)\n    fig.update_layout(template='plotly_white')\n    fig.update_layout(title=title,height=300,margin={'l':10,'r':20,'b':10})\n    fig.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-24T18:59:59.780497Z","iopub.execute_input":"2022-12-24T18:59:59.781266Z","iopub.status.idle":"2022-12-24T19:00:01.481498Z","shell.execute_reply.started":"2022-12-24T18:59:59.781240Z","shell.execute_reply":"2022-12-24T19:00:01.480526Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;border-radius:8px;\n            background-color:#03112A;font-size:150%;\n            letter-spacing:1.0px;background-image: url(https://i.imgur.com/GVd0La1.png)\">\n    <p style=\"padding: 8px;color:white;\"><b><b><span style='color:#F1A424'>4.2 |</span></b> Training subset examples</b></p>\n</div>\n\n- Some examples from our training data subset, with corresponding labels.","metadata":{}},{"cell_type":"code","source":"# Create grid of sample images \ngrid_size=30\nrnd_inds=np.random.randint(0,len(train_ts),grid_size)\nprint(\"image indices:\",rnd_inds)\n\nx_grid_train=[train_ts[i][0] for i in rnd_inds]\ny_grid_train=[train_ts[i][1] for i in rnd_inds]\n\nx_grid_train=utils.make_grid(x_grid_train, nrow=10, padding=2)\nprint(x_grid_train.shape)\n    \nplot_img(x_grid_train,y_grid_train,'Training Subset Examples')","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:00:01.483032Z","iopub.execute_input":"2022-12-24T19:00:01.483394Z","iopub.status.idle":"2022-12-24T19:00:02.536447Z","shell.execute_reply.started":"2022-12-24T19:00:01.483356Z","shell.execute_reply":"2022-12-24T19:00:02.535639Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;border-radius:8px;\n            background-color:#03112A;font-size:150%;\n            letter-spacing:1.0px;background-image: url(https://i.imgur.com/GVd0La1.png)\">\n    <p style=\"padding: 8px;color:white;\"><b><b><span style='color:#F1A424'>4.3 |</span></b> Validation subset examples</b></p>\n</div>\n\n- Some examples from the validation subset, with corresponding labels.","metadata":{}},{"cell_type":"code","source":"grid_size=30\nrnd_inds=np.random.randint(0,len(val_ts),grid_size)\nprint(\"image indices:\",rnd_inds)\nx_grid_val=[val_ts[i][0] for i in range(grid_size)]\ny_grid_val=[val_ts[i][1] for i in range(grid_size)]\n\nx_grid_val=utils.make_grid(x_grid_val, nrow=10, padding=2)\nprint(x_grid_val.shape)\n\nplot_img(x_grid_val,y_grid_val,'Validation Dataset Preview')","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:00:02.537906Z","iopub.execute_input":"2022-12-24T19:00:02.538853Z","iopub.status.idle":"2022-12-24T19:00:02.817980Z","shell.execute_reply.started":"2022-12-24T19:00:02.538817Z","shell.execute_reply":"2022-12-24T19:00:02.816958Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <b>5 <span style='color:#F1A424'>|</span> Image Augmentation Definitions</b> \n\n#### **<span style='color:#F1A424'>IMAGE AUGMENTATIONS</span>**\n\n- Among with pretrained models, image __transformation__ and __image augmentation__ are generally considered to be an essential parts of constructing deep learning models.\n- Using image transformations, we can expand our dataset or resize and normalise it to achieve better model performance.\n- Typical transformations include __horizontal__,__vertical flipping__, __rotation__, __resizing__.\n- We can use various image transformations for our binary classification model without making label changes; we can flip/rotate a __malignant__ image but it will remain the same, __malignant__.\n- We can use the torchvision module to perform image transformations during the training process.\n\n#### **<span style='color:#F1A424'>TRAINING DATA AUGMENTATIONS</span>**\n- transforms.RandomHorizontalFlip(p=0.5): Flips the image horizontally with the probability of 0.5\n- transforms.RandomVerticalFlip(p=0.5) : Flips the image vertically  \" \n- transforms.RandomRotation(45) : Rotates the images in the range of (-45,45) degrees.\n- transforms.RandomResizedCrop(96,scale=(0.8,1.0),ratio=(1.0,1.0)) : Randomly square crops the image in the range of [72,96], followed by a resize to 96x96, which is the original pixel size of our image data.\n- transforms.ToTensor() : Converts to Tensor & Normalises as shown above already.","metadata":{}},{"cell_type":"code","source":"# Define the following transformations for the training dataset\ntr_transf = transforms.Compose([\n#     transforms.Resize((40,40)),\n    transforms.RandomHorizontalFlip(p=0.5), \n    transforms.RandomVerticalFlip(p=0.5),  \n    transforms.RandomRotation(45),         \n#     transforms.RandomResizedCrop(50,scale=(0.8,1.0),ratio=(1.0,1.0)),\n    transforms.ToTensor()])","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:47:34.436707Z","iopub.execute_input":"2022-12-25T14:47:34.437089Z","iopub.status.idle":"2022-12-25T14:47:34.441877Z","shell.execute_reply.started":"2022-12-25T14:47:34.437062Z","shell.execute_reply":"2022-12-25T14:47:34.441153Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# For the validation dataset, we don't need any augmentation; simply convert images into tensors\nval_transf = transforms.Compose([\n    transforms.ToTensor()])\n\n# After defining the transformations, overwrite the transform functions of train_ts, val_ts\ntrain_ts.transform=tr_transf\nval_ts.transform=val_transf","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:47:36.696275Z","iopub.execute_input":"2022-12-25T14:47:36.696619Z","iopub.status.idle":"2022-12-25T14:47:36.704910Z","shell.execute_reply.started":"2022-12-25T14:47:36.696592Z","shell.execute_reply":"2022-12-25T14:47:36.703157Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# The subset can also have transform attribute (if we asign)\ntrain_ts.transform","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:46:52.808897Z","iopub.execute_input":"2022-12-25T14:46:52.809270Z","iopub.status.idle":"2022-12-25T14:46:52.815166Z","shell.execute_reply.started":"2022-12-25T14:46:52.809243Z","shell.execute_reply":"2022-12-25T14:46:52.814268Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <b>6 <span style='color:#F1A424'>|</span> Creating Dataloaders</b> \n\n- Ready to create a PyTorch Dataloader. If we don't use __Dataloaders__, we have to write code to loop over datasets & extract a data batch; automated.\n- We need to define a __batch_size__ : The number of images extracted from the dataset each iteration","metadata":{}},{"cell_type":"code","source":"from torch.utils.data import DataLoader\n\n# Training DataLoader\ntrain_dl = DataLoader(train_ts,\n                      batch_size=32, \n                      shuffle=True)\n\n# Validation DataLoader\nval_dl = DataLoader(val_ts,\n                    batch_size=32,\n                    shuffle=False)","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:00:02.837391Z","iopub.execute_input":"2022-12-24T19:00:02.837819Z","iopub.status.idle":"2022-12-24T19:00:02.848307Z","shell.execute_reply.started":"2022-12-24T19:00:02.837785Z","shell.execute_reply":"2022-12-24T19:00:02.847243Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# check samples\nfor x,y in train_dl:\n    print(x.shape,y)\n    break","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:00:02.849561Z","iopub.execute_input":"2022-12-24T19:00:02.850152Z","iopub.status.idle":"2022-12-24T19:00:03.020511Z","shell.execute_reply.started":"2022-12-24T19:00:02.850115Z","shell.execute_reply":"2022-12-24T19:00:03.019615Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <b>7 <span style='color:#F1A424'>|</span> Define Binary Classifier</b> \n\n- Model is comprised of:\n  - **<span style='color:#F1A424'>four CNN</span>** **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">Conv2D</mark>** layers with a **<span style='color:#F1A424'>pooling layer</span>** **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">max_pool2D</mark>** added between each layer \n  - Two **<span style='color:#F1A424'>fully connected</span>** layers **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">fc</mark>**, with a **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">dropout</mark>** layer between the two layers\n  - **<span style='color:#F1A424'>log_softmax</span>** is used as the activation function for the final layer of the **<span style='color:#F1A424'>binary classifier</span>**\n  \n  \n- PyTorch allows us to create a custom class with <code>nn.Module</code>\n","metadata":{}},{"cell_type":"code","source":"def findConv2dOutShape(hin,win,conv,pool=2):\n    # get conv arguments\n    kernel_size=conv.kernel_size\n    stride=conv.stride\n    padding=conv.padding\n    dilation=conv.dilation\n\n    hout=np.floor((hin+2*padding[0]-dilation[0]*(kernel_size[0]-1)-1)/stride[0]+1)\n    wout=np.floor((win+2*padding[1]-dilation[1]*(kernel_size[1]-1)-1)/stride[1]+1)\n\n    if pool:\n        hout/=pool\n        wout/=pool\n    return int(hout),int(wout)\n\nimport torch.nn as nn\nimport torch.nn.functional as F\n\n# Neural Network\nclass Network(nn.Module):\n    \n    # Network Initialisation\n    def __init__(self, params):\n        \n        super(Network, self).__init__()\n    \n        Cin,Hin,Win=params[\"shape_in\"]\n        init_f=params[\"initial_filters\"] \n        num_fc1=params[\"num_fc1\"]  \n        num_classes=params[\"num_classes\"] \n        self.dropout_rate=params[\"dropout_rate\"] \n        \n        # Convolution Layers\n        self.conv1 = nn.Conv2d(Cin, init_f, kernel_size=3)\n        h,w=findConv2dOutShape(Hin,Win,self.conv1)\n        self.conv2 = nn.Conv2d(init_f, 2*init_f, kernel_size=3)\n        h,w=findConv2dOutShape(h,w,self.conv2)\n        self.conv3 = nn.Conv2d(2*init_f, 4*init_f, kernel_size=3)\n        h,w=findConv2dOutShape(h,w,self.conv3)\n        self.conv4 = nn.Conv2d(4*init_f, 8*init_f, kernel_size=3)\n        h,w=findConv2dOutShape(h,w,self.conv4)\n        \n        # compute the flatten size\n        self.num_flatten=h*w*8*init_f\n        self.fc1 = nn.Linear(self.num_flatten, num_fc1)\n        self.fc2 = nn.Linear(num_fc1, num_classes)\n\n    def forward(self,X):\n        \n        # Convolution & Pool Layers\n        X = F.relu(self.conv1(X)); \n        X = F.max_pool2d(X, 2, 2)\n        X = F.relu(self.conv2(X))\n        X = F.max_pool2d(X, 2, 2)\n        X = F.relu(self.conv3(X))\n        X = F.max_pool2d(X, 2, 2)\n        X = F.relu(self.conv4(X))\n        X = F.max_pool2d(X, 2, 2)\n\n        X = X.view(-1, self.num_flatten)\n        \n        X = F.relu(self.fc1(X))\n        X=F.dropout(X, self.dropout_rate)\n        X = self.fc2(X)\n        return F.log_softmax(X, dim=1)","metadata":{"_kg_hide-input":false,"execution":{"iopub.status.busy":"2022-12-24T19:00:03.023342Z","iopub.execute_input":"2022-12-24T19:00:03.023609Z","iopub.status.idle":"2022-12-24T19:00:03.038229Z","shell.execute_reply.started":"2022-12-24T19:00:03.023584Z","shell.execute_reply":"2022-12-24T19:00:03.037254Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Neural Network Predefined Parameters\nparams_model={\n        \"shape_in\": (3,46,46), \n        \"initial_filters\": 8,    \n        \"num_fc1\": 100,\n        \"dropout_rate\": 0.25,\n        \"num_classes\": 2}\n\n# Create instantiation of Network class\ncnn_model = Network(params_model)\n\n# define computation hardware approach (GPU/CPU)\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nmodel = cnn_model.to(device)","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:00:03.039795Z","iopub.execute_input":"2022-12-24T19:00:03.040174Z","iopub.status.idle":"2022-12-24T19:00:06.093268Z","shell.execute_reply.started":"2022-12-24T19:00:03.040140Z","shell.execute_reply":"2022-12-24T19:00:06.092266Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from torchsummary import summary\nsummary(cnn_model, input_size=(3, 46, 46),device=device.type)","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:00:06.094980Z","iopub.execute_input":"2022-12-24T19:00:06.095368Z","iopub.status.idle":"2022-12-24T19:00:12.151399Z","shell.execute_reply.started":"2022-12-24T19:00:06.095333Z","shell.execute_reply":"2022-12-24T19:00:12.150300Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <b>8 <span style='color:#F1A424'>|</span> Loss Function Definition</b> \n\n- Loss Functions are one of the key pieces of an effective deep learning solution.\n- Pytorch uses <code>loss functions</code> to determine how it will update the network to reach the desired solution.\n- The standard loss function for classification tasks is __cross entropy loss__ or __logloss__\n- When defining a loss function, we need to consider, the number of model outputs and their activation functions.\n- For binary classification tasks, we can choose one or two outputs.\n- It is recommended to use __log_softmax__ as it is easier to expand to multiclass classification; PyTorch combines the log and softmax operations into one function, due to numerical stability and speed.","metadata":{}},{"cell_type":"code","source":"loss_func = nn.NLLLoss(reduction=\"sum\")","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:00:12.156878Z","iopub.execute_input":"2022-12-24T19:00:12.157175Z","iopub.status.idle":"2022-12-24T19:00:12.162317Z","shell.execute_reply.started":"2022-12-24T19:00:12.157150Z","shell.execute_reply":"2022-12-24T19:00:12.161101Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <b>9 <span style='color:#F1A424'>|</span> Optimiser Definition</b> \n\n- Training the network involves passing data through the network:\n    - Using the **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">loss function</mark>** to **<span style='color:#F1A424'>determine the difference between the prediction & true value</span>**\n    - Which is then followed by using of that info to **<span style='color:#F1A424'>update the weights</span>** of the network \n    - In an attempt to **<span style='color:#F1A424'>make the loss function return as small of a loss as possible, performing updates on the neural network</span>**, an **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">optimiser</mark>** is used\n- The <code>torch.optim</code> contains implementations of common optimisers\n- The **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">optimiser</mark>** will **<span style='color:#F1A424'>hold the current state and will update the parameters based on the computed gradients</mark>**\n- For binary classification taskss, __SGD__, __Adam__ Optimisers are commonly used, let's use the latter here.","metadata":{}},{"cell_type":"code","source":"from torch import optim\nopt = optim.Adam(cnn_model.parameters(), lr=3e-4)\nlr_scheduler = ReduceLROnPlateau(opt, mode='min',factor=0.5, patience=20,verbose=1)","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:00:12.163750Z","iopub.execute_input":"2022-12-24T19:00:12.164749Z","iopub.status.idle":"2022-12-24T19:00:12.177776Z","shell.execute_reply.started":"2022-12-24T19:00:12.164698Z","shell.execute_reply":"2022-12-24T19:00:12.176902Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <b>10 <span style='color:#F1A424'>|</span> Training Model</b> \n\n<div style=\"color:white;display:fill;border-radius:8px;\n            background-color:#03112A;font-size:150%;\n            letter-spacing:1.0px;background-image: url(https://i.imgur.com/GVd0La1.png)\">\n    <p style=\"padding: 8px;color:white;\"><b><b><span style='color:#F1A424'>10.1 |</span></b> Helper functions</b></p>\n</div>\n\nThe main training loop function <code>train_val</code> will utiliser three functions:\n- <code>get_lr</code> : get the learning rate as it is adjusted \n- <code>loss_batch</code> : get the loss value for the particular batch\n- <code>loss_epoch</code> : get the entire loss for an epoch iteration","metadata":{}},{"cell_type":"code","source":"''' Helper Functions'''\n\n# Function to get the learning rate\ndef get_lr(opt):\n    for param_group in opt.param_groups:\n        return param_group['lr']\n\n# Function to compute the loss value per batch of data\ndef loss_batch(loss_func, output, target, opt=None):\n    \n    loss = loss_func(output, target) # get loss\n    pred = output.argmax(dim=1, keepdim=True) # Get Output Class\n    metric_b=pred.eq(target.view_as(pred)).sum().item() # get performance metric\n    \n    if opt is not None:\n        opt.zero_grad()\n        loss.backward()\n        opt.step()\n\n    return loss.item(), metric_b\n\n# Compute the loss value & performance metric for the entire dataset (epoch)\ndef loss_epoch(model,loss_func,dataset_dl,opt=None):\n    \n    run_loss=0.0 \n    t_metric=0.0\n    len_data=len(dataset_dl.dataset)\n\n    # internal loop over dataset\n    for xb, yb in dataset_dl:\n        # move batch to device\n        xb=xb.to(device)\n        yb=yb.to(device)\n        output=model(xb) # get model output\n        loss_b,metric_b=loss_batch(loss_func, output, yb, opt) # get loss per batch\n        run_loss+=loss_b        # update running loss\n\n        if metric_b is not None: # update running metric\n            t_metric+=metric_b    \n    \n    loss=run_loss/float(len_data)  # average loss value\n    metric=t_metric/float(len_data) # average metric value\n    \n    return loss, metric","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-24T19:00:12.179074Z","iopub.execute_input":"2022-12-24T19:00:12.179518Z","iopub.status.idle":"2022-12-24T19:00:12.198875Z","shell.execute_reply.started":"2022-12-24T19:00:12.179476Z","shell.execute_reply":"2022-12-24T19:00:12.197937Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"params_train={\n \"train\": train_dl,\"val\": val_dl,\n \"epochs\": 50,\n \"optimiser\": optim.Adam(cnn_model.parameters(),\n                         lr=3e-4),\n \"lr_change\": ReduceLROnPlateau(opt,\n                                mode='min',\n                                factor=0.5,\n                                patience=20,\n                                verbose=0),\n \"f_loss\": nn.NLLLoss(reduction=\"sum\"),\n \"weight_path\": \"weights.pt\",\n \"check\": False, \n}\n","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:00:12.200382Z","iopub.execute_input":"2022-12-24T19:00:12.201190Z","iopub.status.idle":"2022-12-24T19:00:12.214179Z","shell.execute_reply.started":"2022-12-24T19:00:12.201115Z","shell.execute_reply":"2022-12-24T19:00:12.213254Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;border-radius:8px;\n            background-color:#03112A;font-size:150%;\n            letter-spacing:1.0px;background-image: url(https://i.imgur.com/GVd0La1.png)\">\n    <p style=\"padding: 8px;color:white;\"><b><b><span style='color:#F1A424'>10.2 |</span></b> Main training function</b></p>\n</div>\n\n`train_val` is the main function used to train the model on the **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">training set</mark>** `train_dl` & evaluate on the **<mark style=\"background-color:#F1C40F;color:white;border-radius:5px;opacity:0.9\">validation set</mark>** `val_dl`\n\n#### **<span style='color:#F1A424'>FUNCTION INPUTS</span>**\n\nThe function requires\n- **<span style='color:#F1A424'>PyTorch Classifier</span>** , `cnn_model` (we visualised in section 7)\n- Training **<span style='color:#F1A424'>parameter dictionary</span>** `params_train` (which contains both hyperparameters & input data)\n\n#### **<span style='color:#F1A424'>FUNCTION PARAMETER DICTIONARY</span>**\n\nThe parameter dictionary requires:\n- Number of training **<span style='color:#F1A424'>iterations</span>**,`epochs`\n- Training & validation **<span style='color:#F1A424'>data loaders</span>**, `train_dl`, `val_dl`\n- **<span style='color:#F1A424'>Optimiser</span>** & **<span style='color:#F1A424'>loss function</span>**,  `opt` & `loss_func`\n- **<span style='color:#F1A424'>Learning rate adjustor</span>** (on the fly) `lr_change`\n\n#### **<span style='color:#F1A424'>POST TRAINING OUTPUT</span>**\n\n`train_val` returns:\n- The best performing model on the validation dataset\n- The Loss per iteration\n- The Evaluation Metric per iteration (which is accuracy)","metadata":{}},{"cell_type":"code","source":"from tqdm.notebook import trange, tqdm\n\ndef train_val(model, params,verbose=False):\n    \n    # Get the parameters\n    epochs=params[\"epochs\"]\n    loss_func=params[\"f_loss\"]\n    opt=params[\"optimiser\"]\n    train_dl=params[\"train\"]\n    val_dl=params[\"val\"]\n    lr_scheduler=params[\"lr_change\"]\n    weight_path=params[\"weight_path\"]\n    \n    loss_history={\"train\": [],\"val\": []} # history of loss values in each epoch\n    metric_history={\"train\": [],\"val\": []} # histroy of metric values in each epoch\n    best_model_wts = copy.deepcopy(model.state_dict()) # a deep copy of weights for the best performing model\n    best_loss=float('inf') # initialize best loss to a large value\n    \n    ''' Train Model n_epochs '''\n    \n    for epoch in tqdm(range(epochs)):\n        \n        ''' Get the Learning Rate '''\n        current_lr=get_lr(opt)\n        if(verbose):\n            print('Epoch {}/{}, current lr={}'.format(epoch, epochs - 1, current_lr))\n        \n        '''\n        \n        Train Model Process\n        \n        '''\n        \n        model.train()\n        train_loss, train_metric = loss_epoch(model,loss_func,train_dl,opt)\n\n        # collect losses\n        loss_history[\"train\"].append(train_loss)\n        metric_history[\"train\"].append(train_metric)\n        \n        '''\n        \n        Evaluate Model Process\n        \n        '''\n        \n        model.eval()\n        with torch.no_grad():\n            val_loss, val_metric = loss_epoch(model,loss_func,val_dl)\n        \n        # store best model\n        if(val_loss < best_loss):\n            best_loss = val_loss\n            best_model_wts = copy.deepcopy(model.state_dict())\n            \n            # store weights into a local file\n            torch.save(model.state_dict(), weight_path)\n            if(verbose):\n                print(\"Copied best model weights!\")\n        \n        # collect loss and metric for validation dataset\n        loss_history[\"val\"].append(val_loss)\n        metric_history[\"val\"].append(val_metric)\n        \n        # learning rate schedule\n        lr_scheduler.step(val_loss)\n        if current_lr != get_lr(opt):\n            if(verbose):\n                print(\"Loading best model weights!\")\n            model.load_state_dict(best_model_wts) \n\n        if(verbose):\n            print(f\"train loss: {train_loss:.6f}, dev loss: {val_loss:.6f}, accuracy: {100*val_metric:.2f}\")\n            print(\"-\"*10) \n\n    # load best model weights\n    model.load_state_dict(best_model_wts)\n        \n    return model, loss_history, metric_history","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:00:12.217332Z","iopub.execute_input":"2022-12-24T19:00:12.217651Z","iopub.status.idle":"2022-12-24T19:00:12.230827Z","shell.execute_reply.started":"2022-12-24T19:00:12.217627Z","shell.execute_reply":"2022-12-24T19:00:12.229786Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;border-radius:8px;\n            background-color:#03112A;font-size:150%;\n            letter-spacing:1.0px;background-image: url(https://i.imgur.com/GVd0La1.png)\">\n    <p style=\"padding: 8px;color:white;\"><b><b><span style='color:#F1A424'>10.3 |</span></b> Training Process </b></p>\n</div>\n\n#### **<span style='color:#F1A424'>SET PARAMETER DICTIONARY</span>**\n\n- Define the parameters for training `params_train` & train classifier\n- We'll be training the classifier for **<span style='color:#F1A424'>50 iterations</span>** using the **<span style='color:#F1A424'>Adam optimiser</span>** & the **<span style='color:#F1A424'>negative log likelihood loss</span>** function\n- The learning rate will be adjusted on the fly using `ReduceLROnPlateau`, with a factor of 1/2","metadata":{}},{"cell_type":"code","source":"params_train={\n \"train\": train_dl,\"val\": val_dl,\n \"epochs\": 50,\n \"optimiser\": optim.Adam(cnn_model.parameters(),lr=3e-4),\n \"lr_change\": ReduceLROnPlateau(opt,\n                                mode='min',\n                                factor=0.5,\n                                patience=20,\n                                verbose=0),\n \"f_loss\": nn.NLLLoss(reduction=\"sum\"),\n \"weight_path\": \"weights.pt\",\n}\n\n''' Actual Train / Evaluation of CNN Model '''\n# train and validate the model\n\ncnn_model,loss_hist,metric_hist=train_val(cnn_model,params_train)","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:00:12.232362Z","iopub.execute_input":"2022-12-24T19:00:12.232631Z","iopub.status.idle":"2022-12-24T19:05:32.579403Z","shell.execute_reply.started":"2022-12-24T19:00:12.232608Z","shell.execute_reply":"2022-12-24T19:05:32.577650Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **<span style='color:#F1A424'>LOSS & EVALUATION METRIC VISUALISATION</span>**\n\nLet's visualise the performance of the model on the sample dataset ","metadata":{}},{"cell_type":"code","source":"import seaborn as sns; sns.set(style='whitegrid')\n\nepochs=params_train[\"epochs\"]\n\nfig,ax = plt.subplots(1,2,figsize=(12,5))\n\nsns.lineplot(x=[*range(1,epochs+1)],y=loss_hist[\"train\"],ax=ax[0],label='loss_hist[\"train\"]')\nsns.lineplot(x=[*range(1,epochs+1)],y=loss_hist[\"val\"],ax=ax[0],label='loss_hist[\"val\"]')\nsns.lineplot(x=[*range(1,epochs+1)],y=metric_hist[\"train\"],ax=ax[1],label='metric_hist[\"train\"]')\nsns.lineplot(x=[*range(1,epochs+1)],y=metric_hist[\"val\"],ax=ax[1],label='metric_hist[\"val\"]')\nplt.title('Convergence History')","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-12-24T19:05:32.580944Z","iopub.execute_input":"2022-12-24T19:05:32.582007Z","iopub.status.idle":"2022-12-24T19:05:33.710640Z","shell.execute_reply.started":"2022-12-24T19:05:32.581966Z","shell.execute_reply":"2022-12-24T19:05:33.709749Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # Train-Validation Progress\n# epochs=params_train[\"epochs\"]\n\n# fig = make_subplots(rows=1, cols=2,subplot_titles=['lost_hist','metric_hist'])\n# fig.add_trace(go.Scatter(x=[*range(1,epochs+1)], y=loss_hist[\"train\"],name='loss_hist[\"train\"]'),row=1, col=1)\n# fig.add_trace(go.Scatter(x=[*range(1,epochs+1)], y=loss_hist[\"val\"],name='loss_hist[\"val\"]'),row=1, col=1)\n# fig.add_trace(go.Scatter(x=[*range(1,epochs+1)], y=metric_hist[\"train\"],name='metric_hist[\"train\"]'),row=1, col=2)\n# fig.add_trace(go.Scatter(x=[*range(1,epochs+1)], y=metric_hist[\"val\"],name='metric_hist[\"val\"]'),row=1, col=2)\n# fig.update_layout(template='plotly_white');fig.update_layout(margin={\"r\":0,\"t\":60,\"l\":0,\"b\":0},height=300)\n# fig.show()","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2022-12-24T19:05:33.712836Z","iopub.execute_input":"2022-12-24T19:05:33.713594Z","iopub.status.idle":"2022-12-24T19:05:33.718770Z","shell.execute_reply.started":"2022-12-24T19:05:33.713555Z","shell.execute_reply":"2022-12-24T19:05:33.717781Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <b>11 <span style='color:#F1A424'>|</span> Inference</b> \n\n- Once we have trained our model using `train_val`, we can begin to utilise it to **<span style='color:#F1A424'>make some predictions</span>**\n- We have a whole dataset of **<span style='color:#F1A424'>unlabelled image</span>** data in folder test\n- The unique ids of each image in the dataset are located in file `sample_submission.csv`\n- Like for thr training dataset, well create a data loader, using only tensor transformation\n- As we have no label data, we need a slightly modified data class","metadata":{}},{"cell_type":"code","source":"class pytorchdata_test(Dataset):\n    \n    def __init__(self, data_dir, transform,data_type=\"train\"):\n        \n        path2data = os.path.join(data_dir,data_type)\n        filenames = os.listdir(path2data)\n        self.full_filenames = [os.path.join(path2data, f) for f in filenames]\n        \n        # labels are in a csv file named train_labels.csv\n        csv_filename=\"sample_submission.csv\"\n        path2csvLabels=os.path.join(data_dir,csv_filename)\n        labels_df=pd.read_csv(path2csvLabels)\n        \n        # set data frame index to id\n        labels_df.set_index(\"id\", inplace=True)\n        \n        # obtain labels from data frame\n        self.labels = [labels_df.loc[filename[:-4]].values[0] for filename in filenames]\n        self.transform = transform       \n        \n    def __len__(self):\n        # return size of dataset\n        return len(self.full_filenames)\n    \n    def __getitem__(self, idx):\n        # open image, apply transforms and return with label\n        image = Image.open(self.full_filenames[idx]) # PIL image\n        image = self.transform(image)\n        return image, self.labels[idx]","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:11:58.934044Z","iopub.execute_input":"2022-12-24T19:11:58.934412Z","iopub.status.idle":"2022-12-24T19:11:58.943215Z","shell.execute_reply.started":"2022-12-24T19:11:58.934381Z","shell.execute_reply":"2022-12-24T19:11:58.942168Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **<span style='color:#F1A424'>CHECKS</span>**\n\n- Confirm our best performing model has been saved in the working directory \n- Confirm the test folder contents are present","metadata":{}},{"cell_type":"code","source":"!ls","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:05:33.720321Z","iopub.execute_input":"2022-12-24T19:05:33.720764Z","iopub.status.idle":"2022-12-24T19:05:34.733790Z","shell.execute_reply.started":"2022-12-24T19:05:33.720699Z","shell.execute_reply":"2022-12-24T19:05:34.732539Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!ls '/kaggle/input/histopathologic-cancer-detection/test' | head -n 5","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:05:34.735802Z","iopub.execute_input":"2022-12-24T19:05:34.736115Z","iopub.status.idle":"2022-12-24T19:05:35.828717Z","shell.execute_reply.started":"2022-12-24T19:05:34.736085Z","shell.execute_reply":"2022-12-24T19:05:35.827580Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **<span style='color:#F1A424'>FUNCTION PARAMETER DICTIONARY</span>**\n\nHaving defined a model architecture, we can load model weights","metadata":{}},{"cell_type":"code","source":"# load any model weights for the model\ncnn_model.load_state_dict(torch.load('weights.pt'))","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:07:58.307768Z","iopub.execute_input":"2022-12-24T19:07:58.308768Z","iopub.status.idle":"2022-12-24T19:07:58.321454Z","shell.execute_reply.started":"2022-12-24T19:07:58.308709Z","shell.execute_reply":"2022-12-24T19:07:58.320300Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **<span style='color:#F1A424'> TEST FILE IDS</span>**\n\nThe submission file contains all the ids to the files that are located in the test folder","metadata":{}},{"cell_type":"code","source":"# sample submission\npath_sub = \"/kaggle/input/histopathologic-cancer-detection/sample_submission.csv\"\nlabels_df = pd.read_csv(path_sub)\nlabels_df.head()\nlabels_df.shape","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:05:36.075366Z","iopub.status.idle":"2022-12-24T19:05:36.075963Z","shell.execute_reply.started":"2022-12-24T19:05:36.075685Z","shell.execute_reply":"2022-12-24T19:05:36.075710Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **<span style='color:#F1A424'>TEST IMAGE DATASET</span>**\n\nLike we did with the training set, lets convert and store all image data in ","metadata":{}},{"cell_type":"code","source":"data_dir = '/kaggle/input/histopathologic-cancer-detection/'\n\ndata_transformer = transforms.Compose([transforms.ToTensor(),\n                                       transforms.Resize((46,46))])\n\nimg_dataset_test = pytorchdata_test(data_dir,data_transformer,data_type=\"test\")\nprint(len(img_dataset_test), 'samples found')","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:12:26.613310Z","iopub.execute_input":"2022-12-24T19:12:26.613667Z","iopub.status.idle":"2022-12-24T19:12:31.004415Z","shell.execute_reply.started":"2022-12-24T19:12:26.613638Z","shell.execute_reply":"2022-12-24T19:12:31.003420Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **<span style='color:#F1A424'>PREDICTION FUNCTION</span>**\n\nFor inference, we need to set the model to `torch.no_grad`\n\n","metadata":{}},{"cell_type":"code","source":"def inference(model,dataset,device,num_classes=2):\n    \n    len_data=len(dataset)\n    y_out=torch.zeros(len_data,num_classes) # initialize output tensor on CPU\n    y_gt=np.zeros((len_data),dtype=\"uint8\") # initialize ground truth on CPU\n    model=model.to(device) # move model to device\n    \n    with torch.no_grad():\n        for i in tqdm(range(len_data)):\n            x,y=dataset[i]\n            y_gt[i]=y\n            y_out[i]=model(x.unsqueeze(0).to(device))\n\n    return y_out.numpy(),y_gt            ","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:16:14.543564Z","iopub.execute_input":"2022-12-24T19:16:14.544260Z","iopub.status.idle":"2022-12-24T19:16:14.551773Z","shell.execute_reply.started":"2022-12-24T19:16:14.544219Z","shell.execute_reply":"2022-12-24T19:16:14.550660Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"y_test_out,_ = inference(cnn_model,img_dataset_test, device)            ","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:16:18.184241Z","iopub.execute_input":"2022-12-24T19:16:18.184820Z","iopub.status.idle":"2022-12-24T19:24:15.875705Z","shell.execute_reply.started":"2022-12-24T19:16:18.184761Z","shell.execute_reply":"2022-12-24T19:24:15.874773Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# class predictions 0,1\ny_test_pred=np.argmax(y_test_out,axis=1)\nprint(y_test_pred.shape)\nprint(y_test_pred[0:5])","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:25:18.385466Z","iopub.execute_input":"2022-12-24T19:25:18.385842Z","iopub.status.idle":"2022-12-24T19:25:18.392100Z","shell.execute_reply.started":"2022-12-24T19:25:18.385810Z","shell.execute_reply":"2022-12-24T19:25:18.391088Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# probabilities of predicted selection\n# return F.log_softmax(x, dim=1) ie.\npreds = np.exp(y_test_out[:, 1])\nprint(preds.shape)\nprint(preds[0:5])","metadata":{"execution":{"iopub.status.busy":"2022-12-24T19:26:00.264620Z","iopub.execute_input":"2022-12-24T19:26:00.265001Z","iopub.status.idle":"2022-12-24T19:26:00.271696Z","shell.execute_reply.started":"2022-12-24T19:26:00.264971Z","shell.execute_reply":"2022-12-24T19:26:00.270688Z"},"trusted":true},"execution_count":null,"outputs":[]}]}