{"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\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":"2023-07-02T19:32:47.804531Z","iopub.execute_input":"2023-07-02T19:32:47.805403Z","iopub.status.idle":"2023-07-02T19:33:07.913901Z","shell.execute_reply.started":"2023-07-02T19:32:47.805362Z","shell.execute_reply":"2023-07-02T19:33:07.912753Z"},"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":"2023-07-02T19:33:07.916069Z","iopub.execute_input":"2023-07-02T19:33:07.916590Z","iopub.status.idle":"2023-07-02T19:33:13.433291Z","shell.execute_reply.started":"2023-07-02T19:33:07.916561Z","shell.execute_reply":"2023-07-02T19:33:13.431872Z"},"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":"2023-07-02T19:33:13.456151Z","iopub.execute_input":"2023-07-02T19:33:13.456470Z","iopub.status.idle":"2023-07-02T19:33:13.474100Z","shell.execute_reply.started":"2023-07-02T19:33:13.456443Z","shell.execute_reply":"2023-07-02T19:33:13.473162Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":"2023-07-02T19:33:13.475442Z","iopub.execute_input":"2023-07-02T19:33:13.475759Z","iopub.status.idle":"2023-07-02T19:33:13.978762Z","shell.execute_reply.started":"2023-07-02T19:33:13.475732Z","shell.execute_reply":"2023-07-02T19:33:13.977753Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"os.listdir('/kaggle/input/histopathologic-cancer-detection/')","metadata":{"execution":{"iopub.status.busy":"2023-07-02T19:33:13.980257Z","iopub.execute_input":"2023-07-02T19:33:13.980575Z","iopub.status.idle":"2023-07-02T19:33:13.987093Z","shell.execute_reply.started":"2023-07-02T19:33:13.980548Z","shell.execute_reply":"2023-07-02T19:33:13.986160Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels_df.shape","metadata":{"execution":{"iopub.status.busy":"2023-07-02T19:33:13.988245Z","iopub.execute_input":"2023-07-02T19:33:13.988523Z","iopub.status.idle":"2023-07-02T19:33:13.998656Z","shell.execute_reply.started":"2023-07-02T19:33:13.988498Z","shell.execute_reply":"2023-07-02T19:33:13.997761Z"},"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":"2023-07-02T19:33:13.999896Z","iopub.execute_input":"2023-07-02T19:33:14.000186Z","iopub.status.idle":"2023-07-02T19:33:14.083418Z","shell.execute_reply.started":"2023-07-02T19:33:14.000162Z","shell.execute_reply":"2023-07-02T19:33:14.082496Z"},"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":"2023-07-02T19:33:14.084665Z","iopub.execute_input":"2023-07-02T19:33:14.085034Z","iopub.status.idle":"2023-07-02T19:33:14.094840Z","shell.execute_reply.started":"2023-07-02T19:33:14.085003Z","shell.execute_reply":"2023-07-02T19:33:14.093930Z"},"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":"2023-07-02T19:33:14.098776Z","iopub.execute_input":"2023-07-02T19:33:14.099142Z","iopub.status.idle":"2023-07-02T19:33:14.119218Z","shell.execute_reply.started":"2023-07-02T19:33:14.099104Z","shell.execute_reply":"2023-07-02T19:33:14.118178Z"},"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":"2023-07-02T19:33:14.120374Z","iopub.execute_input":"2023-07-02T19:33:14.120665Z","iopub.status.idle":"2023-07-02T19:33:14.129561Z","shell.execute_reply.started":"2023-07-02T19:33:14.120640Z","shell.execute_reply":"2023-07-02T19:33:14.128727Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_fig(malignant,'Malignant Cases')","metadata":{"execution":{"iopub.status.busy":"2023-07-02T19:33:14.130661Z","iopub.execute_input":"2023-07-02T19:33:14.130937Z","iopub.status.idle":"2023-07-02T19:33:18.053492Z","shell.execute_reply.started":"2023-07-02T19:33:14.130914Z","shell.execute_reply":"2023-07-02T19:33:18.052443Z"},"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":"2023-07-02T19:33:18.056218Z","iopub.execute_input":"2023-07-02T19:33:18.056562Z","iopub.status.idle":"2023-07-02T19:33:21.557268Z","shell.execute_reply.started":"2023-07-02T19:33:18.056531Z","shell.execute_reply":"2023-07-02T19:33:21.555173Z"},"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":"2023-07-02T19:33:21.558710Z","iopub.execute_input":"2023-07-02T19:33:21.559036Z","iopub.status.idle":"2023-07-02T19:33:21.573767Z","shell.execute_reply.started":"2023-07-02T19:33:21.559008Z","shell.execute_reply":"2023-07-02T19:33:21.572873Z"},"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":"2023-07-02T19:33:21.574919Z","iopub.execute_input":"2023-07-02T19:33:21.575206Z","iopub.status.idle":"2023-07-02T19:33:21.587773Z","shell.execute_reply.started":"2023-07-02T19:33:21.575182Z","shell.execute_reply":"2023-07-02T19:33:21.586802Z"},"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":"2023-07-02T19:33:21.589145Z","iopub.execute_input":"2023-07-02T19:33:21.589493Z","iopub.status.idle":"2023-07-02T19:33:25.639712Z","shell.execute_reply.started":"2023-07-02T19:33:21.589467Z","shell.execute_reply":"2023-07-02T19:33:25.638620Z"},"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":"2023-07-02T19:33:25.641063Z","iopub.execute_input":"2023-07-02T19:33:25.641390Z","iopub.status.idle":"2023-07-02T19:33:25.665235Z","shell.execute_reply.started":"2023-07-02T19:33:25.641362Z","shell.execute_reply":"2023-07-02T19:33:25.664340Z"},"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":"2023-07-02T19:34:05.857787Z","iopub.execute_input":"2023-07-02T19:34:05.859320Z","iopub.status.idle":"2023-07-02T19:34:05.868158Z","shell.execute_reply.started":"2023-07-02T19:34:05.859262Z","shell.execute_reply":"2023-07-02T19:34:05.866814Z"},"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":"2023-07-02T19:34:07.503218Z","iopub.execute_input":"2023-07-02T19:34:07.504092Z","iopub.status.idle":"2023-07-02T19:34:07.581352Z","shell.execute_reply.started":"2023-07-02T19:34:07.504056Z","shell.execute_reply":"2023-07-02T19:34:07.580424Z"},"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":"2023-07-02T19:34:08.731633Z","iopub.execute_input":"2023-07-02T19:34:08.732059Z","iopub.status.idle":"2023-07-02T19:34:08.770196Z","shell.execute_reply.started":"2023-07-02T19:34:08.732027Z","shell.execute_reply":"2023-07-02T19:34:08.768871Z"},"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":"2023-07-02T19:34:11.254906Z","iopub.execute_input":"2023-07-02T19:34:11.255409Z","iopub.status.idle":"2023-07-02T19:34:11.685516Z","shell.execute_reply.started":"2023-07-02T19:34:11.255374Z","shell.execute_reply":"2023-07-02T19:34:11.684360Z"},"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":"2023-07-02T19:34:11.845371Z","iopub.execute_input":"2023-07-02T19:34:11.845689Z","iopub.status.idle":"2023-07-02T19:34:12.250971Z","shell.execute_reply.started":"2023-07-02T19:34:11.845661Z","shell.execute_reply":"2023-07-02T19:34:12.249692Z"},"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":"2023-07-02T19:34:15.578594Z","iopub.execute_input":"2023-07-02T19:34:15.579611Z","iopub.status.idle":"2023-07-02T19:34:15.585180Z","shell.execute_reply.started":"2023-07-02T19:34:15.579570Z","shell.execute_reply":"2023-07-02T19:34:15.584171Z"},"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":"2023-07-02T19:34:17.393423Z","iopub.execute_input":"2023-07-02T19:34:17.393871Z","iopub.status.idle":"2023-07-02T19:34:17.399238Z","shell.execute_reply.started":"2023-07-02T19:34:17.393839Z","shell.execute_reply":"2023-07-02T19:34:17.398242Z"},"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":"2023-07-02T19:34:18.676765Z","iopub.execute_input":"2023-07-02T19:34:18.677184Z","iopub.status.idle":"2023-07-02T19:34:18.683755Z","shell.execute_reply.started":"2023-07-02T19:34:18.677152Z","shell.execute_reply":"2023-07-02T19:34:18.682924Z"},"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":"2023-07-02T19:34:20.160472Z","iopub.execute_input":"2023-07-02T19:34:20.161033Z","iopub.status.idle":"2023-07-02T19:34:20.167443Z","shell.execute_reply.started":"2023-07-02T19:34:20.160901Z","shell.execute_reply":"2023-07-02T19:34:20.166361Z"},"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":"2023-07-02T19:34:25.398740Z","iopub.execute_input":"2023-07-02T19:34:25.399231Z","iopub.status.idle":"2023-07-02T19:34:25.761250Z","shell.execute_reply.started":"2023-07-02T19:34:25.399197Z","shell.execute_reply":"2023-07-02T19:34:25.760045Z"},"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":"2023-07-02T19:34:32.579952Z","iopub.execute_input":"2023-07-02T19:34:32.580959Z","iopub.status.idle":"2023-07-02T19:34:32.600212Z","shell.execute_reply.started":"2023-07-02T19:34:32.580916Z","shell.execute_reply":"2023-07-02T19:34:32.599183Z"},"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":"2023-07-02T19:34:32.845167Z","iopub.execute_input":"2023-07-02T19:34:32.845491Z","iopub.status.idle":"2023-07-02T19:34:32.854694Z","shell.execute_reply.started":"2023-07-02T19:34:32.845464Z","shell.execute_reply":"2023-07-02T19:34:32.853661Z"},"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":"2023-07-02T19:34:34.112307Z","iopub.execute_input":"2023-07-02T19:34:34.112759Z","iopub.status.idle":"2023-07-02T19:34:34.136433Z","shell.execute_reply.started":"2023-07-02T19:34:34.112729Z","shell.execute_reply":"2023-07-02T19:34:34.135072Z"},"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":"2023-07-02T19:34:37.615984Z","iopub.execute_input":"2023-07-02T19:34:37.616438Z","iopub.status.idle":"2023-07-02T19:34:37.621585Z","shell.execute_reply.started":"2023-07-02T19:34:37.616404Z","shell.execute_reply":"2023-07-02T19:34:37.620385Z"},"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":"2023-07-02T19:34:39.627355Z","iopub.execute_input":"2023-07-02T19:34:39.627791Z","iopub.status.idle":"2023-07-02T19:34:39.634050Z","shell.execute_reply.started":"2023-07-02T19:34:39.627759Z","shell.execute_reply":"2023-07-02T19:34:39.633024Z"},"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":"2023-07-02T19:34:44.602224Z","iopub.execute_input":"2023-07-02T19:34:44.603481Z","iopub.status.idle":"2023-07-02T19:34:44.614585Z","shell.execute_reply.started":"2023-07-02T19:34:44.603440Z","shell.execute_reply":"2023-07-02T19:34:44.613513Z"},"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":"2023-07-02T19:34:46.787371Z","iopub.execute_input":"2023-07-02T19:34:46.787801Z","iopub.status.idle":"2023-07-02T19:34:46.794696Z","shell.execute_reply.started":"2023-07-02T19:34:46.787769Z","shell.execute_reply":"2023-07-02T19:34:46.793767Z"},"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), desc=\"Training\"):\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":"2023-07-02T19:39:49.728671Z","iopub.execute_input":"2023-07-02T19:39:49.729116Z","iopub.status.idle":"2023-07-02T19:39:49.744789Z","shell.execute_reply.started":"2023-07-02T19:39:49.729084Z","shell.execute_reply":"2023-07-02T19:39:49.743783Z"},"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":"2023-07-02T19:39:50.010766Z","iopub.execute_input":"2023-07-02T19:39:50.011172Z","iopub.status.idle":"2023-07-02T19:39:50.127722Z","shell.execute_reply.started":"2023-07-02T19:39:50.011141Z","shell.execute_reply":"2023-07-02T19:39:50.126547Z"},"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":"2023-07-02T19:39:50.662765Z","iopub.execute_input":"2023-07-02T19:39:50.663165Z","iopub.status.idle":"2023-07-02T19:39:50.701987Z","shell.execute_reply.started":"2023-07-02T19:39:50.663115Z","shell.execute_reply":"2023-07-02T19:39:50.700791Z"},"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":"2023-07-02T19:39:15.765577Z","iopub.execute_input":"2023-07-02T19:39:15.766455Z","iopub.status.idle":"2023-07-02T19:39:15.771104Z","shell.execute_reply.started":"2023-07-02T19:39:15.766409Z","shell.execute_reply":"2023-07-02T19:39:15.770070Z"},"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":"2023-07-02T19:39:17.137178Z","iopub.execute_input":"2023-07-02T19:39:17.138278Z","iopub.status.idle":"2023-07-02T19:39:17.148029Z","shell.execute_reply.started":"2023-07-02T19:39:17.138240Z","shell.execute_reply":"2023-07-02T19:39:17.147113Z"},"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":"2023-07-02T19:33:25.988767Z","iopub.status.idle":"2023-07-02T19:33:25.989133Z","shell.execute_reply.started":"2023-07-02T19:33:25.988941Z","shell.execute_reply":"2023-07-02T19:33:25.988958Z"},"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":"2023-07-02T19:33:25.990375Z","iopub.status.idle":"2023-07-02T19:33:25.990710Z","shell.execute_reply.started":"2023-07-02T19:33:25.990539Z","shell.execute_reply":"2023-07-02T19:33:25.990555Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\n#### **<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":"2023-07-02T19:33:25.992038Z","iopub.status.idle":"2023-07-02T19:33:25.992397Z","shell.execute_reply.started":"2023-07-02T19:33:25.992222Z","shell.execute_reply":"2023-07-02T19:33:25.992238Z"},"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":"2023-07-02T19:33:25.993458Z","iopub.status.idle":"2023-07-02T19:33:25.993792Z","shell.execute_reply.started":"2023-07-02T19:33:25.993620Z","shell.execute_reply":"2023-07-02T19:33:25.993636Z"},"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":"2023-07-02T19:33:25.994719Z","iopub.status.idle":"2023-07-02T19:33:25.995057Z","shell.execute_reply.started":"2023-07-02T19:33:25.994889Z","shell.execute_reply":"2023-07-02T19:33:25.994904Z"},"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":"2023-07-02T19:33:25.996236Z","iopub.status.idle":"2023-07-02T19:33:25.996634Z","shell.execute_reply.started":"2023-07-02T19:33:25.996428Z","shell.execute_reply":"2023-07-02T19:33:25.996446Z"},"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":"2023-07-02T19:33:25.998319Z","iopub.status.idle":"2023-07-02T19:33:25.998666Z","shell.execute_reply.started":"2023-07-02T19:33:25.998487Z","shell.execute_reply":"2023-07-02T19:33:25.998503Z"},"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":"2023-07-02T19:33:25.999935Z","iopub.status.idle":"2023-07-02T19:33:26.000300Z","shell.execute_reply.started":"2023-07-02T19:33:26.000109Z","shell.execute_reply":"2023-07-02T19:33:26.000138Z"},"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":"2023-07-02T19:33:26.001393Z","iopub.status.idle":"2023-07-02T19:33:26.001727Z","shell.execute_reply.started":"2023-07-02T19:33:26.001556Z","shell.execute_reply":"2023-07-02T19:33:26.001572Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#THANK YOU FOR TAKING TIME AND GOING THROUGH THIS","metadata":{"execution":{"iopub.status.busy":"2023-07-02T19:33:26.002729Z","iopub.status.idle":"2023-07-02T19:33:26.003066Z","shell.execute_reply.started":"2023-07-02T19:33:26.002897Z","shell.execute_reply":"2023-07-02T19:33:26.002913Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}