{"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":"# Installations","metadata":{}},{"cell_type":"code","source":"%%capture\n!pip install torchmetrics timm","metadata":{"execution":{"iopub.status.busy":"2023-03-14T19:44:45.134564Z","iopub.execute_input":"2023-03-14T19:44:45.134858Z","iopub.status.idle":"2023-03-14T19:44:53.812164Z","shell.execute_reply.started":"2023-03-14T19:44:45.134825Z","shell.execute_reply":"2023-03-14T19:44:53.811121Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Imports","metadata":{}},{"cell_type":"code","source":"import gc\nimport numpy as np \nimport pandas as pd\nimport cv2\nimport glob\nimport seaborn as sns  \nfrom torch.utils.data import Dataset,DataLoader\nfrom torchvision import datasets, models, transforms\nimport torch\nfrom matplotlib import pyplot as plt\nimport os\nfrom PIL import Image\nfrom cv2 import imread\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import confusion_matrix\nimport torchmetrics \nimport timm\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nfrom torch import nn\nfrom torch.optim import AdamW,Adam\nimport time\nfrom tqdm import tqdm","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","id":"xyRGDH4xARCX","execution":{"iopub.status.busy":"2023-03-14T19:44:53.814561Z","iopub.execute_input":"2023-03-14T19:44:53.814811Z","iopub.status.idle":"2023-03-14T19:44:53.821871Z","shell.execute_reply.started":"2023-03-14T19:44:53.814771Z","shell.execute_reply":"2023-03-14T19:44:53.821188Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_path = \"../input/224-224-cervical-cancer-screening/kaggle/train/train\"\nimages  =  [glob.glob(os.path.join(data_path, d, \"*.*\")) for d in os.listdir(data_path)]\ntrain_paths = np.hstack(images)\n# Additional data\n# extra_1 = \"../input/224-224-cervical-cancer-screening/kaggle/additional_Type_1_v2\"\n# extra_2 = \"../input/224-224-cervical-cancer-screening/kaggle/additional_Type_2_v2\"\n# extra_3 = \"../input/224-224-cervical-cancer-screening/kaggle/additional_Type_3_v2\"\n# images1  =  [glob.glob(os.path.join(extra_1, d, \"*.*\")) for d in os.listdir(extra_1)]\n# images2  =  [glob.glob(os.path.join(extra_2, d, \"*.*\")) for d in os.listdir(extra_2)]\n# images3  =  [glob.glob(os.path.join(extra_3, d, \"*.*\")) for d in os.listdir(extra_3)]\n# train_paths = np.append(train_paths, np.hstack(images1))\n# train_paths = np.append(train_paths, np.hstack(images2))\n# train_paths = np.append(train_paths, np.hstack(images3))\n\nprint(f'In this train set we have got a total of {len(train_paths)} images')\nN_EPOCHS = 5\nOUTPUT_PATH = './'\nBATCH_SIZE = 10\nIMAGE_HEIGHT = 224\nIMAGE_WIDTH = 224\n# detect and define device \ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(\"Using \",device)\ndevice = torch.device(device)\ncpu = torch.device('cpu')","metadata":{"id":"tIX-euMe4CYi","execution":{"iopub.status.busy":"2023-03-14T19:44:53.823234Z","iopub.execute_input":"2023-03-14T19:44:53.824015Z","iopub.status.idle":"2023-03-14T19:44:53.849054Z","shell.execute_reply.started":"2023-03-14T19:44:53.823894Z","shell.execute_reply":"2023-03-14T19:44:53.848264Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels = []\nfor p in train_paths:\n    label = int(p.split(\"/\")[-2].split(\"_\")[-1])-1\n    labels.append(label)\ndf = pd.DataFrame({\"path\":train_paths, \"label\":labels})\ndf.head()","metadata":{"execution":{"iopub.status.busy":"2023-03-14T19:44:53.850922Z","iopub.execute_input":"2023-03-14T19:44:53.851386Z","iopub.status.idle":"2023-03-14T19:44:53.868843Z","shell.execute_reply.started":"2023-03-14T19:44:53.851352Z","shell.execute_reply":"2023-03-14T19:44:53.867977Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Note- 0-type1, 1-type2, 2-type3\n#plotting the graph  \n\nsns.countplot(x='label', data=df, palette='pastel')  \nplt.show()  ","metadata":{"execution":{"iopub.status.busy":"2023-03-14T19:44:53.869906Z","iopub.execute_input":"2023-03-14T19:44:53.870159Z","iopub.status.idle":"2023-03-14T19:44:54.048779Z","shell.execute_reply.started":"2023-03-14T19:44:53.870127Z","shell.execute_reply":"2023-03-14T19:44:54.048157Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* We can clearly see that in our train dataframe type_2 (target=1) is the most common one.","metadata":{}},{"cell_type":"markdown","source":"# Preparation - data:\n* multiclass classification problem where 3 classes are:\n    1. Type_1\n    2. Type_2\n    3. Type_3","metadata":{}},{"cell_type":"code","source":"fig = plt.figure(figsize=(8, 8), dpi=80)\ncolumns = 3\nrows = 1\nimg_type1 = plt.imread('../input/224-224-cervical-cancer-screening/kaggle/train/train/Type_1/0.jpg')\nfig.add_subplot(rows, columns, 1)\nplt.title(\"Type1\")\nplt.axis('off')\nplt.imshow(img_type1)\n\nimg_type2 = plt.imread('../input/224-224-cervical-cancer-screening/kaggle/train/train/Type_2/1.jpg')\nfig.add_subplot(rows, columns, 2)\nplt.title(\"Type2\")\nplt.axis('off')\nplt.imshow(img_type2)\n\nimg_type3 = plt.imread('../input/224-224-cervical-cancer-screening/kaggle/train/train/Type_3/1000.jpg')\nfig.add_subplot(rows, columns, 3)\nplt.title(\"Type3\")\nplt.axis('off')\nplt.imshow(img_type3)","metadata":{"execution":{"iopub.status.busy":"2023-03-14T19:44:54.050064Z","iopub.execute_input":"2023-03-14T19:44:54.050311Z","iopub.status.idle":"2023-03-14T19:44:54.320201Z","shell.execute_reply.started":"2023-03-14T19:44:54.050279Z","shell.execute_reply":"2023-03-14T19:44:54.319608Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CancerDataset(Dataset):\n    def __init__(self, paths, augmentations):\n        self.paths = paths\n        self.augmentations = augmentations\n        \n    def __len__(self):\n        return len(self.paths)\n    \n    def __getitem__(self, idx):\n        p = self.paths[idx]\n        label = p.split(\"/\")[-2].split(\"_\")[-1]\n        image = np.array(Image.open(p).convert(\"RGB\"))/255\n        augmentations = self.augmentations(image=image)\n        image = augmentations[\"image\"].float()\n        return image, int(label) - 1","metadata":{"id":"bq3l5moyARC1","execution":{"iopub.status.busy":"2023-03-14T19:44:54.321403Z","iopub.execute_input":"2023-03-14T19:44:54.321810Z","iopub.status.idle":"2023-03-14T19:44:54.328916Z","shell.execute_reply.started":"2023-03-14T19:44:54.321765Z","shell.execute_reply":"2023-03-14T19:44:54.328114Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_transform = A.Compose(\n        [\n            A.Resize(height=IMAGE_HEIGHT, width=IMAGE_WIDTH),\n            A.Rotate(limit=35, p=1.0),\n            ToTensorV2(),\n        ],\n    )\n\nval_transforms = A.Compose(\n        [\n            A.Resize(height=IMAGE_HEIGHT, width=IMAGE_WIDTH),\n            ToTensorV2(),\n        ],\n    )","metadata":{"execution":{"iopub.status.busy":"2023-03-14T19:44:54.330103Z","iopub.execute_input":"2023-03-14T19:44:54.330424Z","iopub.status.idle":"2023-03-14T19:44:54.339605Z","shell.execute_reply.started":"2023-03-14T19:44:54.330370Z","shell.execute_reply":"2023-03-14T19:44:54.338841Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Split- train & validation","metadata":{}},{"cell_type":"code","source":"## random split\ntrain_paths, valid_paths = train_test_split(train_paths, test_size=0.3, random_state= 42)\n\n# create dataset for validation & train\ntrain_dataset = CancerDataset(train_paths, augmentations = train_transform) \nvalid_dataset = CancerDataset(valid_paths, augmentations = val_transforms)\n\n# create dataloaders\ntrain_dataloader = DataLoader(train_dataset,\n                              batch_size = BATCH_SIZE,\n                              shuffle = False)\n\nvalid_dataloader = DataLoader(valid_dataset,\n                              batch_size = BATCH_SIZE,\n                              shuffle = False)","metadata":{"execution":{"iopub.status.busy":"2023-03-14T19:44:54.340713Z","iopub.execute_input":"2023-03-14T19:44:54.341024Z","iopub.status.idle":"2023-03-14T19:44:54.352051Z","shell.execute_reply.started":"2023-03-14T19:44:54.340990Z","shell.execute_reply":"2023-03-14T19:44:54.351359Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class FocalLoss(nn.Module):\n    def __init__(self, alpha=0.25, gamma=2.0):\n        super(FocalLoss, self).__init__()\n        self.gamma = gamma\n        self.alpha = alpha\n        self.cross_entropy_loss = nn.CrossEntropyLoss()\n\n    def forward(self, inputs, targets):\n        bce = self.cross_entropy_loss(inputs, targets)\n        pt = torch.exp(-bce)\n        loss = bce * self.alpha * (torch.pow((1 - pt), self.gamma))\n        return loss\n\nclass Model(nn.Module):\n    def __init__(self, model_name, pretrained = True, num_classes = 3):\n        super().__init__()\n        self.model_name = model_name\n        self.cnn = timm.create_model(self.model_name, pretrained = pretrained, num_classes = num_classes)\n\n    def forward(self, x):\n        x = self.cnn(x)\n        return x\n    \n    def train_mode(self):\n        self.best_loss = np.inf\n        self.best_epoch = 0\n        self.best_acc = 0\n        self.train_loss_history = []\n        self.train_acc_history = []\n        \n    def valid_mode(self):\n        self.valid_loss_history = []\n        self.valid_acc_history = []\n        \ndef train_one_epoch(train_loader, model, criterion, optimizer, device):\n    # switch to train mode\n    model.train()   \n    size = len(train_loader.dataset)\n    num_batches = len(train_loader)\n    loss, correct = 0, 0\n    for batch, (x, y) in enumerate(train_loader):\n        device = torch.device(device)\n        x, y = x.to(device), y.to(device)  \n        optimizer.zero_grad()\n        pred = model(x)\n        loss = criterion(pred, y.long().squeeze()) \n        loss.backward()\n        optimizer.step()\n        y_pred, y_true = torch.argmax(pred, axis=1), y.long().squeeze()\n        correct += (y_pred == y_true).type(torch.float).sum().item()\n        loss = np.round(loss.item(), 5)\n    # metrics: calculate accuracy and loss for epoch (all batches)\n    correct /= size # epoch accuracy\n    loss /= num_batches # epoch loss\n    print(f\"Train: Accuracy: {(100*correct):>0.2f}%, Avg loss: {loss:>5f} \\n\")\n    model.train_loss_history.append(loss)\n    model.train_acc_history.append(correct)\n    return loss, correct\n    \ndef valid_one_epoch(valid_loader, model, criterion, device):\n    model.eval()\n    size = len(valid_loader.dataset)\n    num_batches = len(valid_loader)\n    loss, correct = 0, 0\n    with torch.no_grad():\n        for batch, (x, y) in enumerate(valid_loader):\n            device = torch.device(device)\n            x, y = x.to(device), y.to(device)\n            pred = model(x)\n            loss = criterion(pred, y.long().squeeze()) \n            y_pred, y_true = torch.argmax(pred, axis=1), y.long().squeeze()\n            correct += (y_pred == y_true).type(torch.float).sum().item()\n            loss = np.round(loss.item(), 5)\n    # metrics: calculate accuracy and loss for epoch (all batches)\n    correct /= size # epoch accuracy\n    loss /= num_batches # epoch loss\n    model.valid_loss_history.append(loss)\n    model.valid_acc_history.append(correct)\n    print(f\"Valid: Accuracy: {(100*correct):>0.2f}%, Avg loss: {loss:>5f} \\n\")\n    return loss, correct\n\ndef train_valid(train_loader,valid_loader, model, device):\n    # Create optimizer & loss\n    model.optimizer = Adam(model.parameters(),lr=1e-4)\n    loss_fn = FocalLoss()\n    \n    print('\\n ******************************* Using backbone: ', model.model_name, \" ******************************* \\n\")\n    print('Starting Training...\\n')\n    start_train_time = time.time()\n    model.train_mode()\n    model.valid_mode()\n    for epoch in tqdm(range(0, N_EPOCHS)):\n        print(f\"\\n-------------------------------   Epoch {epoch + 1}   -------------------------------\\n\")\n        start_epoch_time = time.time()\n        # train\n        train_one_epoch(train_loader, model, loss_fn, model.optimizer, device)\n        # validation\n        valid_loss, valid_acc = valid_one_epoch(valid_loader, model, loss_fn, device)\n        # save validation loss if it was improved (reduced) & validation accuracy if it was improved (increased)\n        if valid_loss < model.best_loss and valid_acc > model.best_acc:\n            model.best_epoch = epoch + 1\n            model.best_loss = valid_loss\n            model.best_acc = valid_acc\n            # save the model's weights and biases   \n            torch.save(model.state_dict(), OUTPUT_PATH + f\"{model.model_name}_ep{model.best_epoch}.pth\")        \n            torch.save(model.state_dict(), OUTPUT_PATH + f\"{model.model_name}_ep{model.best_epoch}.pth\")\n\n        end_epoch_time = time.time()\n        time_delta = np.round(end_epoch_time - start_epoch_time, 3)\n        print(\"\\n\\nEpoch Elapsed Time: {} s\".format(time_delta))\n\n    end_train_time = time.time()\n    print(\"\\n\\nTotal Elapsed Time: {} min\".format(np.round((end_train_time - start_train_time)/60, 3)))\n    print(\"Done!\")\n\ndef plot_results(model):\n    fig = plt.figure(figsize = (18, 8))\n    fig.suptitle(f\"{model.model_name} Training Results\", fontsize = 18)\n\n    space = np.arange(1, N_EPOCHS + 1, 1)\n    if N_EPOCHS <= 20:\n        x_ticks = np.arange(1, N_EPOCHS + 1, 1)\n    else:\n        x_ticks = np.arange(1, N_EPOCHS + 1, int(N_EPOCHS/20) + 1)\n\n    # Loss plot\n    ax1 = plt.subplot(1, 2, 1) \n    ax1.plot(space, model.train_loss_history, label='Training', color = 'black')\n    ax1.plot(space, model.valid_loss_history, label='Validation', color = 'blue')\n    plt.xticks(x_ticks)\n    plt.axhline(0, linestyle = 'dashed', color = 'grey')\n    plt.axvline(model.best_epoch, linestyle = 'dashed', color = 'blue', label = 'Best val loss: ep ' + str(model.best_epoch))\n    plt.title(\"Loss\")\n    ax1.legend(frameon=False);\n\n    # Accuracy plot\n    ax2 = plt.subplot(1, 2, 2)\n    ax2.plot(space, model.train_acc_history, label='Training', color = 'black')\n    ax2.plot(space, model.valid_acc_history, label='Validation', color = 'blue')\n    plt.xticks(x_ticks)\n    plt.axhline(0.99, linestyle = 'dashed', color = 'grey')\n    plt.axvline(model.best_epoch, linestyle = 'dashed', color = 'green', label = 'Best val acc: ep ' + str(model.best_epoch))\n    plt.title(\"Accuracy\")\n    ax2.legend(frameon=False);","metadata":{"execution":{"iopub.status.busy":"2023-03-14T19:47:16.432589Z","iopub.execute_input":"2023-03-14T19:47:16.432870Z","iopub.status.idle":"2023-03-14T19:47:16.465115Z","shell.execute_reply.started":"2023-03-14T19:47:16.432837Z","shell.execute_reply":"2023-03-14T19:47:16.464285Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train & Architectures","metadata":{}},{"cell_type":"markdown","source":"## MobileNetV3\n","metadata":{}},{"cell_type":"code","source":"model_mobileNet = Model('mobilenetv3_large_100', pretrained = True, num_classes = 3)\nmodel_mobileNet = model_mobileNet.to(device) # move the model to GPU before constructing optimizers for it\ntrain_valid(train_dataloader,valid_dataloader, model_mobileNet, device)\nplot_results(model_mobileNet)\n\nmodel_mobileNet = model_mobileNet.to(cpu)\ngc.collect()\ntorch.cuda.empty_cache()","metadata":{"execution":{"iopub.status.busy":"2023-03-14T19:47:17.667276Z","iopub.execute_input":"2023-03-14T19:47:17.668113Z","iopub.status.idle":"2023-03-14T19:48:03.872357Z","shell.execute_reply.started":"2023-03-14T19:47:17.668069Z","shell.execute_reply":"2023-03-14T19:48:03.871631Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## ResNet50\n","metadata":{}},{"cell_type":"code","source":"model_resNet50 = Model('resnet50', pretrained = True, num_classes = 3)\nmodel_resNet50 = model_resNet50.to(device) # move the model to GPU before constructing optimizers for it\ntrain_valid(train_dataloader,valid_dataloader, model_resNet50, device)\nplot_results(model_resNet50)\n\nmodel_resNet50 = model_resNet50.to(cpu)\ngc.collect()\ntorch.cuda.empty_cache()","metadata":{"execution":{"iopub.status.busy":"2023-03-14T19:48:10.137999Z","iopub.execute_input":"2023-03-14T19:48:10.138271Z","iopub.status.idle":"2023-03-14T19:49:14.328353Z","shell.execute_reply.started":"2023-03-14T19:48:10.138240Z","shell.execute_reply":"2023-03-14T19:49:14.327637Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## EfficentNet-B3","metadata":{}},{"cell_type":"code","source":"model_efficientnet_b3 = Model('efficientnet_b3_pruned', pretrained = True, num_classes = 3)\nmodel_efficientnet_b3 = model_efficientnet_b3.to(device) # move the model to GPU before constructing optimizers for it\ntrain_valid(train_dataloader,valid_dataloader, model_efficientnet_b3, device)\nplot_results(model_efficientnet_b3)\n\nmodel_efficientnet_b3 = model_efficientnet_b3.to(cpu)\ngc.collect()\ntorch.cuda.empty_cache()","metadata":{"execution":{"iopub.status.busy":"2023-03-14T19:49:14.331610Z","iopub.execute_input":"2023-03-14T19:49:14.331897Z","iopub.status.idle":"2023-03-14T19:50:28.422247Z","shell.execute_reply.started":"2023-03-14T19:49:14.331859Z","shell.execute_reply":"2023-03-14T19:50:28.419842Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Inception Net","metadata":{}},{"cell_type":"code","source":"model_inception_v4 = Model('inception_v4', pretrained = True, num_classes = 3)\nmodel_inception_v4 = model_inception_v4.to(device) # move the model to GPU before constructing optimizers for it\ntrain_valid(train_dataloader,valid_dataloader, model_inception_v4, device)\nplot_results(model_inception_v4)\n\nmodel_inception_v4 = model_inception_v4.to(cpu)\ngc.collect()\ntorch.cuda.empty_cache()","metadata":{"execution":{"iopub.status.busy":"2023-03-14T19:50:28.423780Z","iopub.execute_input":"2023-03-14T19:50:28.424066Z","iopub.status.idle":"2023-03-14T19:52:21.955292Z","shell.execute_reply.started":"2023-03-14T19:50:28.424030Z","shell.execute_reply":"2023-03-14T19:52:21.954604Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Confusion Matrix","metadata":{}},{"cell_type":"code","source":"from sklearn.metrics import f1_score\n#after train we want to do prediction for valdtaion data (in trainning mode) or test data (on test mode)\ndef predict(model,valid_dataloader,optimizer):\n    Y_predict = []\n    T = []\n    model.eval()\n    with torch.no_grad():\n        for x_batch,y_batch in valid_dataloader:\n            optimizer.zero_grad()\n            output = model(x_batch)\n            for i in torch.argmax(output,1):\n                Y_predict.append(i.item())\n            for i in y_batch:\n                T.append(i.item())\n    return Y_predict, T\n\nmodels = [model_mobileNet, \n               model_resNet50,\n               model_efficientnet_b3, \n               model_inception_v4]\nfor model in tqdm(models):\n    model.Y_pred,model.T =  predict(model, valid_dataloader,model.optimizer) \n    model.fscore = f1_score(model.Y_pred,model.T,average='macro')","metadata":{"execution":{"iopub.status.busy":"2023-03-14T19:52:21.957030Z","iopub.execute_input":"2023-03-14T19:52:21.957825Z","iopub.status.idle":"2023-03-14T19:54:58.698408Z","shell.execute_reply.started":"2023-03-14T19:52:21.957770Z","shell.execute_reply":"2023-03-14T19:54:58.697014Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(15,10))\n\nfor model, ax in tqdm(zip(models, axes.flatten())):\n    model.cf_matrix = confusion_matrix(model.Y_pred,model.T)\n    \n    sns.heatmap(model.cf_matrix, annot=True, cmap='Blues',fmt='g',ax=ax)\n    \n    ax.title.set_text(f'{model.model_name}\\n\\n')\n    ax.set_xlabel('\\nPredicted Values')\n    ax.set_ylabel('Actual Values ')\nplt.tight_layout()  \nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-03-14T19:54:58.699847Z","iopub.execute_input":"2023-03-14T19:54:58.700095Z","iopub.status.idle":"2023-03-14T19:54:59.813424Z","shell.execute_reply.started":"2023-03-14T19:54:58.700061Z","shell.execute_reply":"2023-03-14T19:54:59.812669Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}