{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Table of Contents\n\n1. [Preparation & Understanding the data structure](#prep)\n2. [Exploratory Data Analysis](#eda)\n3. [Data Preprocessing](#data)\n4. [Defining the Model](#model)\n5. [Training the Model](#train)\n6. [Making & Visualising Predictions](#pred)","metadata":{}},{"cell_type":"markdown","source":"# Preparation & Understanding the data structure <a class=\"anchor\" id=\"prep\"></a>\n\n### Importing packages","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nfrom os import listdir\nimport cv2\n\nimport matplotlib.pyplot as plt\n%matplotlib inline\nimport seaborn as sns\nsns.set()\nfrom PIL import Image\nfrom glob import glob\nfrom skimage.io import imread\n\nimport sklearn\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import roc_auc_score\n\nimport torch \nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torchvision\nimport torchvision.transforms as transforms\nfrom torch.utils.data import TensorDataset, DataLoader, Dataset\nimport torch.optim as optim\n\nimport time\nimport copy\nfrom tqdm import tqdm_notebook as tqdm\n\nimport warnings\nwarnings.filterwarnings(\"ignore\", category=DeprecationWarning)\nwarnings.filterwarnings(\"ignore\", category=UserWarning)\nwarnings.filterwarnings(\"ignore\", category=FutureWarning)\n\nprint('Imports complete')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-01-27T18:33:16.334383Z","iopub.execute_input":"2022-01-27T18:33:16.335483Z","iopub.status.idle":"2022-01-27T18:33:20.354121Z","shell.execute_reply.started":"2022-01-27T18:33:16.335337Z","shell.execute_reply":"2022-01-27T18:33:20.352119Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Configurations","metadata":{}},{"cell_type":"code","source":"# Model Parameters\nnum_epochs = 10\nbatch_size = 128\nnum_classes = 2\nlearning_rate = 0.002\n\n# Device configuration\ndevice = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\ndevice","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:20.356639Z","iopub.execute_input":"2022-01-27T18:33:20.357078Z","iopub.status.idle":"2022-01-27T18:33:20.424962Z","shell.execute_reply.started":"2022-01-27T18:33:20.35703Z","shell.execute_reply":"2022-01-27T18:33:20.423772Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Loading and understanding the data structure","metadata":{}},{"cell_type":"code","source":"base_dir = '../input/histopathologic-cancer-detection/'\nprint(os.listdir(base_dir))","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:20.426689Z","iopub.execute_input":"2022-01-27T18:33:20.427283Z","iopub.status.idle":"2022-01-27T18:33:20.440467Z","shell.execute_reply.started":"2022-01-27T18:33:20.427223Z","shell.execute_reply":"2022-01-27T18:33:20.439227Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels = pd.read_csv(base_dir + \"train_labels.csv\")\nlabels.head()","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:20.444034Z","iopub.execute_input":"2022-01-27T18:33:20.444589Z","iopub.status.idle":"2022-01-27T18:33:21.099728Z","shell.execute_reply.started":"2022-01-27T18:33:20.44454Z","shell.execute_reply":"2022-01-27T18:33:21.09879Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels.shape","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:21.101655Z","iopub.execute_input":"2022-01-27T18:33:21.102008Z","iopub.status.idle":"2022-01-27T18:33:21.109639Z","shell.execute_reply.started":"2022-01-27T18:33:21.101963Z","shell.execute_reply":"2022-01-27T18:33:21.108648Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels.info()","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:21.11144Z","iopub.execute_input":"2022-01-27T18:33:21.112159Z","iopub.status.idle":"2022-01-27T18:33:21.16167Z","shell.execute_reply.started":"2022-01-27T18:33:21.112112Z","shell.execute_reply":"2022-01-27T18:33:21.160608Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This file contains the ids of images for training and their labels for cancer. ","metadata":{}},{"cell_type":"code","source":"train_path = base_dir + \"train/\"\ntest_path = base_dir + \"test/\"\ntrain_files = listdir(train_path)\ntest_files = listdir(test_path)","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:21.163172Z","iopub.execute_input":"2022-01-27T18:33:21.164155Z","iopub.status.idle":"2022-01-27T18:33:26.494636Z","shell.execute_reply.started":"2022-01-27T18:33:21.164105Z","shell.execute_reply":"2022-01-27T18:33:26.493662Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_files[:5]","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:26.496465Z","iopub.execute_input":"2022-01-27T18:33:26.496793Z","iopub.status.idle":"2022-01-27T18:33:26.504637Z","shell.execute_reply.started":"2022-01-27T18:33:26.496747Z","shell.execute_reply":"2022-01-27T18:33:26.503363Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_files[:5]","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:26.507013Z","iopub.execute_input":"2022-01-27T18:33:26.507929Z","iopub.status.idle":"2022-01-27T18:33:26.522609Z","shell.execute_reply.started":"2022-01-27T18:33:26.507881Z","shell.execute_reply":"2022-01-27T18:33:26.52078Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Number of images in train and test\nprint(\"Train size: \", len(train_files))\nprint(\"Test size: \", len(test_files))","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:26.534354Z","iopub.execute_input":"2022-01-27T18:33:26.534937Z","iopub.status.idle":"2022-01-27T18:33:26.540957Z","shell.execute_reply.started":"2022-01-27T18:33:26.534896Z","shell.execute_reply":"2022-01-27T18:33:26.539914Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print((len(train_files)/(len(train_files)+len(test_files)))*100, (len(test_files)/(len(train_files)+len(test_files)))*100)","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:26.542408Z","iopub.execute_input":"2022-01-27T18:33:26.543253Z","iopub.status.idle":"2022-01-27T18:33:26.553691Z","shell.execute_reply.started":"2022-01-27T18:33:26.543203Z","shell.execute_reply":"2022-01-27T18:33:26.5526Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The directories train and test contain the actual images with 79.3% and 20.7% of the total images respectively.","metadata":{}},{"cell_type":"code","source":"sub = pd.read_csv(base_dir + \"sample_submission.csv\")\nsub.head()","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:26.555373Z","iopub.execute_input":"2022-01-27T18:33:26.556566Z","iopub.status.idle":"2022-01-27T18:33:26.723678Z","shell.execute_reply.started":"2022-01-27T18:33:26.556515Z","shell.execute_reply":"2022-01-27T18:33:26.722719Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub.shape","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:26.725272Z","iopub.execute_input":"2022-01-27T18:33:26.725692Z","iopub.status.idle":"2022-01-27T18:33:26.733171Z","shell.execute_reply.started":"2022-01-27T18:33:26.725642Z","shell.execute_reply":"2022-01-27T18:33:26.732061Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub.info()","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:26.735004Z","iopub.execute_input":"2022-01-27T18:33:26.735909Z","iopub.status.idle":"2022-01-27T18:33:26.760488Z","shell.execute_reply.started":"2022-01-27T18:33:26.735834Z","shell.execute_reply":"2022-01-27T18:33:26.759448Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This file contains the ids of test images and all the labels are set to 0. We need to modify the labels in this file according to our predictions.","metadata":{}},{"cell_type":"markdown","source":"# Exploratory Data Analysis <a class=\"anchor\" id=\"eda\"></a>\n\n### Visualizing the number of patches with cancer vs without cancer.","metadata":{}},{"cell_type":"code","source":"plt.pie(labels.label.value_counts(), labels=['No Cancer', 'Cancer'], colors=['#90EE91', '#F47174'], autopct='%1.1f')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:26.762375Z","iopub.execute_input":"2022-01-27T18:33:26.762974Z","iopub.status.idle":"2022-01-27T18:33:26.895748Z","shell.execute_reply.started":"2022-01-27T18:33:26.762925Z","shell.execute_reply":"2022-01-27T18:33:26.894734Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Visualizing healthy and cancer patches","metadata":{}},{"cell_type":"code","source":"positive_images = np.random.choice(labels[labels.label==1].id, size=50, replace=False)\nnegative_images = np.random.choice(labels[labels.label==0].id, size=50, replace=False)","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:26.897534Z","iopub.execute_input":"2022-01-27T18:33:26.898131Z","iopub.status.idle":"2022-01-27T18:33:26.929436Z","shell.execute_reply.started":"2022-01-27T18:33:26.898082Z","shell.execute_reply":"2022-01-27T18:33:26.928431Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Cancer patches**","metadata":{}},{"cell_type":"code","source":"fig, ax = plt.subplots(5, 10, figsize=(20,10))\n\nfor n in range(5):\n    for m in range(10):\n        img_id = positive_images[m + n*10]\n        image = Image.open(train_path + img_id + \".tif\")\n        ax[n,m].imshow(image)\n        ax[n,m].grid(False)\n        ax[n,m].tick_params(labelbottom=False, labelleft=False)","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:26.932113Z","iopub.execute_input":"2022-01-27T18:33:26.932689Z","iopub.status.idle":"2022-01-27T18:33:33.314912Z","shell.execute_reply.started":"2022-01-27T18:33:26.932641Z","shell.execute_reply":"2022-01-27T18:33:33.30592Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Healthy patches**","metadata":{}},{"cell_type":"code","source":"fig, ax = plt.subplots(5, 10, figsize=(20,10))\n\nfor n in range(5):\n    for m in range(10):\n        img_id = negative_images[m + n*10]\n        image = Image.open(train_path + img_id + \".tif\")\n        ax[n,m].imshow(image)\n        ax[n,m].grid(False)\n        ax[n,m].tick_params(labelbottom=False, labelleft=False)","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:33.320578Z","iopub.execute_input":"2022-01-27T18:33:33.320934Z","iopub.status.idle":"2022-01-27T18:33:39.568033Z","shell.execute_reply.started":"2022-01-27T18:33:33.32089Z","shell.execute_reply":"2022-01-27T18:33:39.565925Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Analysis**\n\nVisualising cancerous and healthy patches, it is hard to identify metastatic cancer for an untrained eye. One observation could be that the healthy patches have higher contrast than the cancerous patches. However, this observation doesn't seem to be applicable on all the images. It would be interesting to see what criterion pathologists use for identification of metastatic cancer!","metadata":{}},{"cell_type":"markdown","source":"# Data Preprocessing <a class=\"anchor\" id=\"data\"></a>\n\n### Splitting the data into train and validation sets","metadata":{}},{"cell_type":"code","source":"train, val = train_test_split(labels, stratify=labels.label, test_size=0.1)\nprint(len(train), len(val))","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:39.569367Z","iopub.execute_input":"2022-01-27T18:33:39.571013Z","iopub.status.idle":"2022-01-27T18:33:39.747721Z","shell.execute_reply.started":"2022-01-27T18:33:39.570968Z","shell.execute_reply":"2022-01-27T18:33:39.746522Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"I have split the train data into train and validation sets in the ratio 9:1.\n\n**Plotting the positive and negative ratio in train and val sets**","metadata":{}},{"cell_type":"code","source":"fig, ax = plt.subplots(1, 2, figsize=(10,4))\n\nsns.countplot(train.label, palette=\"Blues\", ax=ax[0])\nax[0].set_title(\"Train dataset\")\nfor i, rows in enumerate(train['label'].value_counts().values):\n    ax[0].annotate(int(rows), xy=(i, rows), ha='center')\nsns.countplot(val.label, palette=\"Greens\", ax=ax[1])\nax[1].set_title(\"Validation dataset\")\nfor i, rows in enumerate(val['label'].value_counts().values):\n    ax[1].annotate(int(rows), xy=(i, rows), ha='center')","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:39.749813Z","iopub.execute_input":"2022-01-27T18:33:39.750227Z","iopub.status.idle":"2022-01-27T18:33:40.187707Z","shell.execute_reply.started":"2022-01-27T18:33:39.750174Z","shell.execute_reply":"2022-01-27T18:33:40.186727Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Custom Dataset\n\nI have created a dataset that loads an image patch, converts it to RGB, performs the augmentation if it's desired, and returns the image and its label.","metadata":{}},{"cell_type":"code","source":"class CancerDataset(Dataset):\n    \n    def __init__(self, df_data, data_dir = './', transform=None):\n        super().__init__()\n        self.df = df_data.values\n        self.data_dir = data_dir\n        self.transform = transform\n        \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, index):\n        img_name,label = self.df[index]\n        img_path = os.path.join(self.data_dir, img_name + '.tif')\n        image = cv2.imread(img_path)\n        if self.transform is not None:\n            image = self.transform(image)\n        return image, label","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:40.189627Z","iopub.execute_input":"2022-01-27T18:33:40.190271Z","iopub.status.idle":"2022-01-27T18:33:40.199534Z","shell.execute_reply.started":"2022-01-27T18:33:40.190221Z","shell.execute_reply":"2022-01-27T18:33:40.198385Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Data Augmentation\n\nNow to increase the data size, I have applied transformation like flipping and rotation to the train dataset, and then converted the datasets into tensors.","metadata":{}},{"cell_type":"code","source":"transform_train = transforms.Compose([transforms.ToPILImage(),\n                                  transforms.RandomHorizontalFlip(), \n                                  transforms.RandomVerticalFlip(),\n                                  transforms.RandomRotation(20), \n                                  transforms.ToTensor(),\n                                  transforms.Normalize(mean=[0.5, 0.5, 0.5],std=[0.5, 0.5, 0.5])])\n\ntransform_val = transforms.Compose([transforms.ToPILImage(),\n                                  transforms.ToTensor(),\n                                  transforms.Normalize(mean=[0.5, 0.5, 0.5],std=[0.5, 0.5, 0.5])])\n\ntransform_test = transforms.Compose([transforms.ToPILImage(), \n                                  transforms.ToTensor(),\n                                  transforms.Normalize(mean=[0.5, 0.5, 0.5],std=[0.5, 0.5, 0.5])])","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:40.201409Z","iopub.execute_input":"2022-01-27T18:33:40.20176Z","iopub.status.idle":"2022-01-27T18:33:40.214632Z","shell.execute_reply.started":"2022-01-27T18:33:40.201722Z","shell.execute_reply":"2022-01-27T18:33:40.21353Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataset = CancerDataset(df_data=train, data_dir=train_path, transform=transform_train)\nval_dataset = CancerDataset(df_data=val, data_dir=train_path, transform=transform_val)\ntest_dataset = CancerDataset(df_data=sub, data_dir=test_path, transform=transform_test)","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:40.216486Z","iopub.execute_input":"2022-01-27T18:33:40.217603Z","iopub.status.idle":"2022-01-27T18:33:40.250922Z","shell.execute_reply.started":"2022-01-27T18:33:40.217553Z","shell.execute_reply":"2022-01-27T18:33:40.249909Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Creating pytorch dataloader\n\n* The training data is shuffled after epochs so that the batches in the epochs are different every time and the model doesn't learn in a specific sequence.\n* The last batch is dropped as it might contain less images than the batch size.","metadata":{}},{"cell_type":"code","source":"train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, drop_last=True)\nval_dataloader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, drop_last=True)\ntest_dataloader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:40.254523Z","iopub.execute_input":"2022-01-27T18:33:40.254969Z","iopub.status.idle":"2022-01-27T18:33:40.262203Z","shell.execute_reply.started":"2022-01-27T18:33:40.254934Z","shell.execute_reply":"2022-01-27T18:33:40.261135Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(len(train_dataloader), len(val_dataloader), len(test_dataloader))","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:40.265441Z","iopub.execute_input":"2022-01-27T18:33:40.265887Z","iopub.status.idle":"2022-01-27T18:33:40.275506Z","shell.execute_reply.started":"2022-01-27T18:33:40.265827Z","shell.execute_reply":"2022-01-27T18:33:40.274529Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Defining the Model <a class=\"anchor\" id=\"model\"></a>\n\nI am using a CNN as the model with 5 layers.","metadata":{}},{"cell_type":"code","source":"class CNN(nn.Module):\n    def __init__(self):\n        super(CNN,self).__init__()\n        \n        self.conv1 = nn.Sequential(\n                        nn.Conv2d(3, 32, 3, stride=1, padding=1),\n                        nn.BatchNorm2d(32),\n                        nn.ReLU(inplace=True),\n                        nn.MaxPool2d(2,2))\n        \n        self.conv2 = nn.Sequential(\n                        nn.Conv2d(32, 64, 3, stride=1, padding=1),\n                        nn.BatchNorm2d(64),\n                        nn.ReLU(inplace=True),\n                        nn.MaxPool2d(2,2))\n        \n        self.conv3 = nn.Sequential(\n                        nn.Conv2d(64, 128, 3, stride=1, padding=1),\n                        nn.BatchNorm2d(128),\n                        nn.ReLU(inplace=True),\n                        nn.MaxPool2d(2,2))\n        \n        self.conv4 = nn.Sequential(\n                        nn.Conv2d(128, 256, 3, stride=1, padding=1),\n                        nn.BatchNorm2d(256),\n                        nn.ReLU(inplace=True),\n                        nn.MaxPool2d(2,2))\n        \n        self.conv5 = nn.Sequential(\n                        nn.Conv2d(256, 512, 3, stride=1, padding=1),\n                        nn.BatchNorm2d(512),\n                        nn.ReLU(inplace=True),\n                        nn.MaxPool2d(2,2))\n        \n        self.fc=nn.Sequential(\n                nn.Linear(512*3*3, 256),\n                nn.ReLU(inplace=True),\n                nn.BatchNorm1d(256),\n                nn.Dropout(0.4),\n                nn.Linear(256, num_classes))\n        \n    def forward(self,x):\n        x=self.conv1(x)\n        x=self.conv2(x)\n        x=self.conv3(x)\n        x=self.conv4(x)\n        x=self.conv5(x)\n#        print(x.shape)\n        x=x.view(x.shape[0],-1)\n        x=self.fc(x)\n        return x","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:40.278407Z","iopub.execute_input":"2022-01-27T18:33:40.278868Z","iopub.status.idle":"2022-01-27T18:33:40.297469Z","shell.execute_reply.started":"2022-01-27T18:33:40.278768Z","shell.execute_reply":"2022-01-27T18:33:40.295997Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Printing the training model.","metadata":{}},{"cell_type":"code","source":"model = CNN().to(device)\nprint(model)","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:40.299161Z","iopub.execute_input":"2022-01-27T18:33:40.299679Z","iopub.status.idle":"2022-01-27T18:33:43.628043Z","shell.execute_reply.started":"2022-01-27T18:33:40.29963Z","shell.execute_reply":"2022-01-27T18:33:43.627076Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Loss and Optimizer\n\nThis task is a binary classification problem that has two classes, 1 for cancer positive images and 0 for cancer negative images. For loss function, I have used cross entropy loss.\nI have used adam for optimizer.","metadata":{}},{"cell_type":"code","source":"criterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adamax(model.parameters(), lr=learning_rate)","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:43.63315Z","iopub.execute_input":"2022-01-27T18:33:43.633414Z","iopub.status.idle":"2022-01-27T18:33:43.638894Z","shell.execute_reply.started":"2022-01-27T18:33:43.633382Z","shell.execute_reply":"2022-01-27T18:33:43.637828Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training the Model <a class=\"anchor\" id=\"train\"></a>\n\nBuilding the training loop for the model. It prints the loss and accuracy for training and validation after each epoch.\nFor accuracy, I have calculated the area under the ROC curve between the predicted probability and the observed target.\nThe losses and accuracies are also saved in an array for further evaluation of the model.","metadata":{}},{"cell_type":"code","source":"train_losses = []\nval_losses = []\ntrain_auc = []\nval_auc = []\ntrain_auc_epoch = []\nval_auc_epoch = []\nbest_acc = 0.0\nmin_loss = np.Inf\n\nsince = time.time()\n\nfor e in range(num_epochs):\n    \n    train_loss = 0.0\n    val_loss = 0.0\n    \n    # Train the model\n    model.train()\n    for i, (images, labels) in enumerate(tqdm(train_dataloader, total=int(len(train_dataloader)))):\n        images = images.to(device)\n        labels = labels.to(device)\n        \n        # Forward pass\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        \n        # Backward and optimize\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n        \n        # Loss and accuracy\n        train_loss += loss.item()\n        y_actual = labels.data.cpu().numpy()\n        y_pred = outputs[:,-1].detach().cpu().numpy()\n        train_auc.append(roc_auc_score(y_actual, y_pred))\n    \n    # Evaluate the model\n    model.eval()\n    for i, (images, labels) in enumerate(tqdm(val_dataloader, total=int(len(val_dataloader)))):\n        images = images.to(device)\n        labels = labels.to(device)\n        \n        # Forward pass\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        \n        # Loss and accuracy\n        val_loss += loss.item()\n        y_actual = labels.data.cpu().numpy()\n        y_pred = outputs[:,-1].detach().cpu().numpy()\n        val_auc.append(roc_auc_score(y_actual, y_pred))\n    \n    # Average losses and accuracies\n    train_loss = train_loss/len(train_dataloader)\n    val_loss = val_loss/len(val_dataloader)\n    train_losses.append(train_loss)\n    val_losses.append(val_loss)\n    training_auc = np.mean(train_auc)\n    validation_auc = np.mean(val_auc)\n    train_auc_epoch.append(training_auc)\n    val_auc_epoch.append(validation_auc)\n    \n    # Updating best validation accuracy\n    if best_acc < validation_auc:\n        best_acc = validation_auc\n        \n    # Saving best model\n    if min_loss >= val_loss:\n        torch.save(model.state_dict(), 'best_model.pt')\n        min_loss = val_loss\n    \n    print('EPOCH {}/{}'.format(e+1, num_epochs))\n    print('-' * 10)\n    print(\"Train loss: {:.6f}, Train AUC: {:.4f}\".format(train_loss, training_auc))\n    print(\"Validation loss: {:.6f}, Validation AUC: {:.4f}\\n\".format(val_loss, validation_auc))\n\ntime_elapsed = time.time() - since\nprint('Training completed in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))\nprint('Best validation accuracy: {:4f}'.format(best_acc))","metadata":{"execution":{"iopub.status.busy":"2022-01-27T18:33:43.640876Z","iopub.execute_input":"2022-01-27T18:33:43.641539Z","iopub.status.idle":"2022-01-27T20:20:11.445443Z","shell.execute_reply.started":"2022-01-27T18:33:43.641486Z","shell.execute_reply":"2022-01-27T20:20:11.442921Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Plotting training history\n\n**Loss Convergence**","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(20,5))\nplt.plot(train_losses, '-o', label=\"train\")\nplt.plot(val_losses, '-o', label=\"val\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.title(\"Loss change over epoch\")\nplt.legend()","metadata":{"execution":{"iopub.status.busy":"2022-01-27T20:20:11.447522Z","iopub.execute_input":"2022-01-27T20:20:11.448123Z","iopub.status.idle":"2022-01-27T20:20:11.984125Z","shell.execute_reply.started":"2022-01-27T20:20:11.448075Z","shell.execute_reply":"2022-01-27T20:20:11.982885Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Accuracy trend**","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(20,5))\nplt.plot(train_auc_epoch, '-o', label=\"train\")\nplt.plot(val_auc_epoch, '-o', label=\"val\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy\")\nplt.title(\"Accuracy over epoch\")\nplt.legend()","metadata":{"execution":{"iopub.status.busy":"2022-01-27T20:20:11.986335Z","iopub.execute_input":"2022-01-27T20:20:11.986874Z","iopub.status.idle":"2022-01-27T20:20:12.387827Z","shell.execute_reply.started":"2022-01-27T20:20:11.986806Z","shell.execute_reply":"2022-01-27T20:20:12.386765Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Loading the best model","metadata":{}},{"cell_type":"code","source":"model.load_state_dict(torch.load('best_model.pt'))","metadata":{"execution":{"iopub.status.busy":"2022-01-27T20:20:12.389615Z","iopub.execute_input":"2022-01-27T20:20:12.389904Z","iopub.status.idle":"2022-01-27T20:20:12.413959Z","shell.execute_reply.started":"2022-01-27T20:20:12.389869Z","shell.execute_reply":"2022-01-27T20:20:12.412775Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Making & Visualising Predictions <a class=\"anchor\" id=\"pred\"></a>\n\n### Predictions on test dataset\n\nI have used my best model to make predictions on the test dataset.","metadata":{}},{"cell_type":"code","source":"model.eval()\n\npredictions = []\n\nfor i, (images, labels) in enumerate(tqdm(test_dataloader, total=int(len(test_dataloader)))):\n    images = images.to(device)\n    labels = labels.to(device)\n    \n    outputs = model(images)\n    pred = outputs[:,1].detach().cpu().numpy()\n    \n    for j in pred:\n        predictions.append(j)","metadata":{"execution":{"iopub.status.busy":"2022-01-27T20:20:12.415822Z","iopub.execute_input":"2022-01-27T20:20:12.416261Z","iopub.status.idle":"2022-01-27T20:28:54.209818Z","shell.execute_reply.started":"2022-01-27T20:20:12.416211Z","shell.execute_reply":"2022-01-27T20:28:54.208671Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Modifying the submission file\n\nNow I am using the predictions made by the model to create a submission file.","metadata":{}},{"cell_type":"code","source":"sub['label'] = predictions\nsub.to_csv('submission.csv', index=False)\nsub.info()","metadata":{"execution":{"iopub.status.busy":"2022-01-27T20:28:54.211659Z","iopub.execute_input":"2022-01-27T20:28:54.212275Z","iopub.status.idle":"2022-01-27T20:28:54.500563Z","shell.execute_reply.started":"2022-01-27T20:28:54.212218Z","shell.execute_reply":"2022-01-27T20:28:54.49954Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Visualising predictions\n\nI have displayed some of the test images along with their predicted result. For a probability less than 0.5, images are labelled 'Healthy', otherwise they are labelled 'Cancer'.","metadata":{}},{"cell_type":"code","source":"test_images = np.random.choice(sub.id, size=50, replace=False)     \n\nfig, ax = plt.subplots(5, 10, figsize=(20,10))\n\nfor n in range(5):\n    for m in range(10):\n        img_id = test_images[m + n*10]\n        image = Image.open(test_path + img_id + \".tif\")\n        pred = sub.loc[sub['id'] == img_id, 'label'].values[0]\n        label = \"Cancer\" if(pred >= 0.5) else \"Healthy\"  \n        ax[n,m].imshow(image)\n        ax[n,m].grid(False)\n        ax[n,m].tick_params(labelbottom=False, labelleft=False)\n        ax[n,m].set_title(\"Label: \" + label)","metadata":{"execution":{"iopub.status.busy":"2022-01-27T20:28:54.502052Z","iopub.execute_input":"2022-01-27T20:28:54.502925Z","iopub.status.idle":"2022-01-27T20:29:01.058346Z","shell.execute_reply.started":"2022-01-27T20:28:54.502875Z","shell.execute_reply":"2022-01-27T20:29:01.055956Z"},"trusted":true},"execution_count":null,"outputs":[]}]}