{"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":"# libraries\nimport numpy as np\nimport pandas as pd\nimport os\nimport cv2\nimport matplotlib.pyplot as plt\n%matplotlib inline\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import roc_auc_score\nimport torch\nfrom torch.utils.data import TensorDataset, DataLoader,Dataset, random_split\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torchvision\nimport torchvision.transforms as transforms\nimport torch.optim as optim\nfrom torch.optim import lr_scheduler\nimport time \nfrom PIL import Image\ntrain_on_gpu = True\nfrom torch.utils.data.sampler import SubsetRandomSampler\nfrom torch.optim.lr_scheduler import StepLR, ReduceLROnPlateau, CosineAnnealingLR\n\n!pip install torchsummary\nfrom torchsummary import summary\n\nimport copy\n\ntorch.manual_seed(0)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-03-11T07:17:06.493386Z","iopub.execute_input":"2023-03-11T07:17:06.494368Z","iopub.status.idle":"2023-03-11T07:17:21.264367Z","shell.execute_reply.started":"2023-03-11T07:17:06.494269Z","shell.execute_reply":"2023-03-11T07:17:21.263092Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Introduction\n\nIn this notebook we'll create an algorithm using deep learning and computer vision to identify metastatic cancer in small image patches taken from larger digital pathology scans. ","metadata":{}},{"cell_type":"markdown","source":"# Dataset","metadata":{}},{"cell_type":"markdown","source":"## Load & Explore dataset\n\n The file *train_labels.csv* has the dataset information; reference to image (id) and its class (label).","metadata":{}},{"cell_type":"code","source":"path2labels = \"/kaggle/input/histopathologic-cancer-detection/train_labels.csv\"\nlabels_df = pd.read_csv(path2labels)","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:25:33.972592Z","iopub.execute_input":"2023-03-11T07:25:33.973603Z","iopub.status.idle":"2023-03-11T07:25:34.441707Z","shell.execute_reply.started":"2023-03-11T07:25:33.973559Z","shell.execute_reply":"2023-03-11T07:25:34.440663Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels_df.head()","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:26:17.984777Z","iopub.execute_input":"2023-03-11T07:26:17.985141Z","iopub.status.idle":"2023-03-11T07:26:18.003660Z","shell.execute_reply.started":"2023-03-11T07:26:17.985111Z","shell.execute_reply":"2023-03-11T07:26:18.002743Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels_df.shape","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:26:21.822603Z","iopub.execute_input":"2023-03-11T07:26:21.823120Z","iopub.status.idle":"2023-03-11T07:26:21.830616Z","shell.execute_reply.started":"2023-03-11T07:26:21.823075Z","shell.execute_reply":"2023-03-11T07:26:21.829550Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels_df['label'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:26:22.645372Z","iopub.execute_input":"2023-03-11T07:26:22.645831Z","iopub.status.idle":"2023-03-11T07:26:22.668572Z","shell.execute_reply.started":"2023-03-11T07:26:22.645788Z","shell.execute_reply":"2023-03-11T07:26:22.667768Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The dataset contains 220025 images, of which 89117 are malignant images.","metadata":{}},{"cell_type":"code","source":"print(f'The dataset has {sum(labels_df.duplicated())} duplicates')","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:26:41.046662Z","iopub.execute_input":"2023-03-11T07:26:41.047429Z","iopub.status.idle":"2023-03-11T07:26:41.145578Z","shell.execute_reply.started":"2023-03-11T07:26:41.047392Z","shell.execute_reply":"2023-03-11T07:26:41.144470Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's visualize a few images from the dataset","metadata":{}},{"cell_type":"code","source":"fig = plt.figure(figsize=(25, 4))\npath2data = \"/kaggle/input/histopathologic-cancer-detection/train\"\ntrain_imgs = os.listdir(path2data)\nfor idx, img in enumerate(np.random.choice(train_imgs, 20)):\n    ax = fig.add_subplot(2, 20//2, idx+1)\n    im = Image.open(path2data + \"/\" + img)\n    plt.imshow(im)\n    lab = labels_df.loc[labels_df[\"id\"] == img.split('.')[0], 'label'].values[0]\n    ax.set_title(f'Label: {lab}')","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:26:48.684397Z","iopub.execute_input":"2023-03-11T07:26:48.684778Z","iopub.status.idle":"2023-03-11T07:26:53.397148Z","shell.execute_reply.started":"2023-03-11T07:26:48.684743Z","shell.execute_reply":"2023-03-11T07:26:53.396103Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can note that it's hard to classify the tumor in an image. ","metadata":{}},{"cell_type":"markdown","source":"# Data Preparation\nWe will create a custom Dataset class to hold the data","metadata":{}},{"cell_type":"code","source":"class cancer_dataset(Dataset):\n    def __init__(self, data_dir, transform, data_type=\"train\"):\n        # path to images\n        path2data = os.path.join(data_dir, data_type)\n        \n        # list of images in directory\n        filenames = os.listdir(path2data)\n        \n        # get full path to images\n        self.full_filenames = [os.path.join(path2data, f) for f in filenames]\n        \n        # get labels\n        path2labels = os.path.join(data_dir, \"train_labels.csv\")\n        labels_df = pd.read_csv(path2labels)\n        \n        # seg dataframe index to id\n        labels_df.set_index(\"id\", inplace=True)\n        \n        # obtain labels from df\n        self.labels = [labels_df.loc[filename[:-4]].values[0] for filename in filenames]\n        \n        self.transform = transform\n        \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        img = Image.open(self.full_filenames[idx]) # PIL image\n        img = self.transform(img)\n        return img, self.labels[idx]","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:27:51.962530Z","iopub.execute_input":"2023-03-11T07:27:51.962906Z","iopub.status.idle":"2023-03-11T07:27:51.972253Z","shell.execute_reply.started":"2023-03-11T07:27:51.962875Z","shell.execute_reply":"2023-03-11T07:27:51.971134Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We will create transformers to convert PIL image to PyTorch tensors","metadata":{}},{"cell_type":"code","source":"data_transformer = transforms.Compose([transforms.ToTensor(),\n                                       transforms.Resize((46,46))])","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:28:19.764813Z","iopub.execute_input":"2023-03-11T07:28:19.765404Z","iopub.status.idle":"2023-03-11T07:28:19.770621Z","shell.execute_reply.started":"2023-03-11T07:28:19.765371Z","shell.execute_reply":"2023-03-11T07:28:19.769504Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's load the dataset and look at a sample","metadata":{}},{"cell_type":"code","source":"data_dir = \"/kaggle/input/histopathologic-cancer-detection\"\nimg_dataset = cancer_dataset(data_dir, data_transformer, \"train\")","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:28:48.241104Z","iopub.execute_input":"2023-03-11T07:28:48.241447Z","iopub.status.idle":"2023-03-11T07:29:06.215218Z","shell.execute_reply.started":"2023-03-11T07:28:48.241417Z","shell.execute_reply":"2023-03-11T07:29:06.214212Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img, label = img_dataset[19]\nprint(img.shape, torch.min(img), torch.max(img))","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:29:09.717520Z","iopub.execute_input":"2023-03-11T07:29:09.717961Z","iopub.status.idle":"2023-03-11T07:29:09.741114Z","shell.execute_reply.started":"2023-03-11T07:29:09.717926Z","shell.execute_reply":"2023-03-11T07:29:09.739996Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Splitting the Dataset\nWe will split the dataset into train and validation with a ration 80:20","metadata":{}},{"cell_type":"code","source":"len_dataset = len(img_dataset)\nlen_train = int(0.8 * len_dataset)\nlen_val = len_dataset - len_train\n\ntrain_ds, val_ds = random_split(img_dataset, [len_train, len_val])\n\nprint(f'train dataset length: {len(train_ds)}')\nprint(f'validation dataset length: {len(val_ds)}')\n","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:29:15.607828Z","iopub.execute_input":"2023-03-11T07:29:15.608662Z","iopub.status.idle":"2023-03-11T07:29:15.633776Z","shell.execute_reply.started":"2023-03-11T07:29:15.608624Z","shell.execute_reply":"2023-03-11T07:29:15.632782Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"i = 0\nfor x, y in train_ds:\n    print(x.shape, y)\n    i += 1\n    if i > 5:\n        break","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:29:33.018596Z","iopub.execute_input":"2023-03-11T07:29:33.019089Z","iopub.status.idle":"2023-03-11T07:29:33.057382Z","shell.execute_reply.started":"2023-03-11T07:29:33.019055Z","shell.execute_reply":"2023-03-11T07:29:33.056337Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Image Transformation\nWe will define some transformers for image augmentation","metadata":{}},{"cell_type":"code","source":"# transformer for trainging dataset\ntrain_transf = transforms.Compose([\n    transforms.RandomHorizontalFlip(p=0.5),\n    transforms.RandomVerticalFlip(p=0.5),\n    transforms.RandomRotation(45),\n    transforms.RandomResizedCrop(96, scale=(0.8, 1.0), ratio=(1.0, 1.0)),\n#     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),\n    transforms.ToTensor()\n    ])\n\n# No augmentation for validation dataset\nval_transf = transforms.Compose([\n#     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),\n    transforms.ToTensor()\n    ])\n\n# Overwrite the transforms functions\ntrain_ds.transform = train_transf\nval_ds.transform = val_transf","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:29:58.915254Z","iopub.execute_input":"2023-03-11T07:29:58.915629Z","iopub.status.idle":"2023-03-11T07:29:58.932915Z","shell.execute_reply.started":"2023-03-11T07:29:58.915598Z","shell.execute_reply":"2023-03-11T07:29:58.931331Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_ds.transform","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:30:01.899326Z","iopub.execute_input":"2023-03-11T07:30:01.899706Z","iopub.status.idle":"2023-03-11T07:30:01.907267Z","shell.execute_reply.started":"2023-03-11T07:30:01.899673Z","shell.execute_reply":"2023-03-11T07:30:01.905936Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Dataloaders\nWe create a dataloader with a *batch_size*","metadata":{}},{"cell_type":"code","source":"train_dl = DataLoader(train_ds, batch_size=32, shuffle=True)\nval_dl = DataLoader(val_ds, batch_size=32, shuffle=False)\n\n# check batches\nfor x, y in train_dl:\n    print(x.shape)\n    print(y.shape)\n    break\n    \nfor x, y in val_dl:\n    print(x.shape)\n    print(y.shape)\n    break","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:30:04.228521Z","iopub.execute_input":"2023-03-11T07:30:04.228902Z","iopub.status.idle":"2023-03-11T07:30:04.599698Z","shell.execute_reply.started":"2023-03-11T07:30:04.228870Z","shell.execute_reply":"2023-03-11T07:30:04.598070Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define Classifier","metadata":{}},{"cell_type":"markdown","source":"## Simple CNN model\nThis model has: \n    - Four CNN layers with poolin layer for each layer.\n    - Two fully connected layers, with a dropout layer\n    - activation function is log_softmax","metadata":{}},{"cell_type":"code","source":"class Network(nn.Module):\n    \n    def __init__(self):\n        \n        super(Network, self).__init__()\n        self.conv1 = nn.Conv2d(3, 8, kernel_size=3)\n        self.conv2 = nn.Conv2d(8, 16, kernel_size=3)\n        self.conv3 = nn.Conv2d(16, 32, kernel_size=3)\n        self.conv4 = nn.Conv2d(32, 64, kernel_size=3)\n        \n        self.dropout_rate = 0.25\n        self.pool = nn.MaxPool2d(2, 2)\n        self.fc1 = nn.Linear(1*1*64, 100)\n        self.fc2 = nn.Linear(100, 2)\n        \n    def forward(self, X):\n        \n        x = self.pool(F.relu(self.conv1(X)))\n        x = self.pool(F.relu(self.conv2(x)))\n        x = self.pool(F.relu(self.conv3(x)))\n        x = self.pool(F.relu(self.conv4(x)))\n        # flatten\n        x = x.view(-1, 1*1*64)\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)\n    \n    \n# create instant of model\ncnn_model = Network()\n\n# define hardware\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nmodel = cnn_model.to(device)\nprint(device)\n\nsummary(cnn_model, input_size=(3, 46, 46), device=device.type)","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:37:58.181997Z","iopub.execute_input":"2023-03-11T07:37:58.182926Z","iopub.status.idle":"2023-03-11T07:37:58.203369Z","shell.execute_reply.started":"2023-03-11T07:37:58.182878Z","shell.execute_reply":"2023-03-11T07:37:58.202386Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"m =torchvision.models.resnet50()\nm.fc = nn.Linear(2048 , 2)\ncnn_model = m\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nmodel = cnn_model.to(device)\nprint(device)\n\nsummary(cnn_model, input_size=(3, 46, 46), device=device.type)","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:36:48.437765Z","iopub.execute_input":"2023-03-11T07:36:48.438126Z","iopub.status.idle":"2023-03-11T07:36:49.048835Z","shell.execute_reply.started":"2023-03-11T07:36:48.438096Z","shell.execute_reply":"2023-03-11T07:36:49.046539Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Loss function\nWe will use **NLLLoss** ","metadata":{}},{"cell_type":"code","source":"loss_func = nn.NLLLoss(reduction=\"sum\")","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:38:29.528902Z","iopub.execute_input":"2023-03-11T07:38:29.529298Z","iopub.status.idle":"2023-03-11T07:38:29.534282Z","shell.execute_reply.started":"2023-03-11T07:38:29.529266Z","shell.execute_reply":"2023-03-11T07:38:29.533229Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Optimiser\nWe will use **Adam**","metadata":{}},{"cell_type":"code","source":"opt = optim.Adam(cnn_model.parameters(), lr=3e-4)\nlr_scheduler = ReduceLROnPlateau(opt, mode='min', factor=0.5, patience=20, verbose=0)","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:38:39.231062Z","iopub.execute_input":"2023-03-11T07:38:39.231903Z","iopub.status.idle":"2023-03-11T07:38:39.241856Z","shell.execute_reply.started":"2023-03-11T07:38:39.231854Z","shell.execute_reply":"2023-03-11T07:38:39.238487Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training Model","metadata":{}},{"cell_type":"markdown","source":"## Helper functions","metadata":{}},{"cell_type":"code","source":"\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 tqdm(dataset_dl, leave=False):\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\n","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:39:27.395369Z","iopub.execute_input":"2023-03-11T07:39:27.395774Z","iopub.status.idle":"2023-03-11T07:39:27.407544Z","shell.execute_reply.started":"2023-03-11T07:39:27.395735Z","shell.execute_reply":"2023-03-11T07:39:27.406386Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Training function","metadata":{}},{"cell_type":"code","source":"from tqdm.notebook import trange, tqdm\n\ndef train_val(model, params, verbose=False):\n    \n    # Get parameters\n    epochs = params[\"epochs\"]\n    opt = params[\"optimiser\"]\n    loss_func = params[\"f_loss\"]\n    train_dl = params[\"train\"]\n    val_dl = params[\"val\"]\n    lr_scheduler = params[\"lr_change\"]\n    weight_path = params[\"weight_path\"]\n    \n    # history of loss and metric values in each epoch\n    loss_history = {\"train\": [], \"val\": []}\n    metric_history = {\"train\": [], \"val\": []}\n    \n    # a deep copy of weights for the best model\n    best_model_wts = copy.deepcopy(model.state_dict())\n    \n    best_loss = float('inf')      # init loss\n    \n    # Train loop\n    for epoch in tqdm(range(epochs), leave=False):\n        \n        # get lr\n        current_lr = get_lr(opt)\n        if(verbose):\n            print(f'Epoch {epoch +1}/{epochs}, current lr={current_lr}')\n        \n        # train model\n        model.train()\n        train_loss, train_metric = loss_epoch(model, loss_func, train_dl, opt)\n        \n        loss_history[\"train\"].append(train_loss)\n        metric_history[\"train\"].append(train_metric)\n        \n        # evaluate model\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            # save weights in a local file\n            torch.save(model.state_dict(), weight_path)\n            if verbose:\n                print(\"Saved best model weights\")\n            \n        loss_history[\"val\"].append(val_loss)\n        metric_history[\"val\"].append(val_metric)\n        \n        # lr 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(\"-\"*20)\n            \n    model.load_state_dict(best_model_wts)\n    \n    return model, loss_history, metric_history","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:39:29.571524Z","iopub.execute_input":"2023-03-11T07:39:29.571908Z","iopub.status.idle":"2023-03-11T07:39:29.585966Z","shell.execute_reply.started":"2023-03-11T07:39:29.571876Z","shell.execute_reply":"2023-03-11T07:39:29.584645Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Train","metadata":{}},{"cell_type":"code","source":"params_train={\n \"train\": train_dl,\"val\": val_dl,\n \"epochs\": 1,\n \"optimiser\": opt,\n \"lr_change\": lr_scheduler,\n \"f_loss\": loss_func,\n \"weight_path\": \"weights.pt\",\n}\n\n# Train model\n\nmodel, loss_hist, metric_hist = train_val(model, params_train, verbose=True)","metadata":{"execution":{"iopub.status.busy":"2023-03-11T07:39:34.288061Z","iopub.execute_input":"2023-03-11T07:39:34.288631Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Loss & Metrics visual\n","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":{"execution":{"iopub.status.busy":"2023-02-24T04:40:21.304358Z","iopub.status.idle":"2023-02-24T04:40:21.305281Z","shell.execute_reply.started":"2023-02-24T04:40:21.305007Z","shell.execute_reply":"2023-02-24T04:40:21.305034Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Inference\nWe will use our trained model to classify the test dataset","metadata":{}},{"cell_type":"code","source":"class cancerdata_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-02-24T19:13:52.954689Z","iopub.execute_input":"2023-02-24T19:13:52.955033Z","iopub.status.idle":"2023-02-24T19:13:52.966511Z","shell.execute_reply.started":"2023-02-24T19:13:52.955001Z","shell.execute_reply":"2023-02-24T19:13:52.965615Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# load any model weights for the model\nmodel.load_state_dict(torch.load('weights.pt'))","metadata":{"execution":{"iopub.status.busy":"2023-02-24T19:13:52.968015Z","iopub.execute_input":"2023-02-24T19:13:52.96836Z","iopub.status.idle":"2023-02-24T19:13:52.988834Z","shell.execute_reply.started":"2023-02-24T19:13:52.968322Z","shell.execute_reply":"2023-02-24T19:13:52.987527Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"path2sub = \"/kaggle/input/histopathologic-cancer-detection/sample_submission.csv\"\nlabels_df = pd.read_csv(path2sub)\ndata_dir = '/kaggle/input/histopathologic-cancer-detection/'\n\ndata_transformer = transforms.Compose([transforms.ToTensor(),\n                                       transforms.Resize((46,46))])\n\nimg_dataset_test = cancerdata_test(data_dir,data_transformer,data_type=\"test\")\nprint(len(img_dataset_test), 'samples found')","metadata":{"execution":{"iopub.status.busy":"2023-02-24T19:13:52.99024Z","iopub.execute_input":"2023-02-24T19:13:52.990828Z","iopub.status.idle":"2023-02-24T19:14:02.431049Z","shell.execute_reply.started":"2023-02-24T19:13:52.990794Z","shell.execute_reply":"2023-02-24T19:14:02.429947Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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    model.eval()\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           \n\ny_test_out,_ = inference(model,img_dataset_test, device)  \ny_test_pred=np.argmax(y_test_out,axis=1)\n\ntest_ids = [name.split('/')[-1].split('.')[0] for name in img_dataset_test.full_filenames]\ntest_preds = pd.DataFrame({\"img\": test_ids, \"preds\": y_test_pred})\nsubmission = pd.merge(labels_df, test_preds, left_on='id', right_on='img')\nsubmission = submission[['id', 'preds']]\nsubmission.columns = ['id', 'label']\nsubmission.head()\n","metadata":{"execution":{"iopub.status.busy":"2023-02-24T19:14:02.435799Z","iopub.execute_input":"2023-02-24T19:14:02.436704Z","iopub.status.idle":"2023-02-24T19:25:06.028092Z","shell.execute_reply.started":"2023-02-24T19:14:02.436662Z","shell.execute_reply":"2023-02-24T19:25:06.026973Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2023-02-24T19:25:06.029815Z","iopub.execute_input":"2023-02-24T19:25:06.030197Z","iopub.status.idle":"2023-02-24T19:25:06.109536Z","shell.execute_reply.started":"2023-02-24T19:25:06.03016Z","shell.execute_reply":"2023-02-24T19:25:06.108552Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}