{"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":"!pip install -qU wandb\n!pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cu116","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:04:52.592804Z","iopub.execute_input":"2023-02-01T00:04:52.593532Z","iopub.status.idle":"2023-02-01T00:05:20.908761Z","shell.execute_reply.started":"2023-02-01T00:04:52.593436Z","shell.execute_reply":"2023-02-01T00:05:20.907469Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Import Libraries\n* Will use pytorch to build and run our model\n* Use pandas to aggregate our data","metadata":{}},{"cell_type":"code","source":"import torch.nn as nn\nimport torch\nfrom torch import Tensor\nimport torchvision\nimport torchvision.transforms as T\nfrom torchvision.utils import make_grid\nfrom torchvision.io import ImageReadMode\nfrom torch.utils.data import DataLoader, Dataset, random_split\n\nimport tensorflow as tf\n\nfrom typing import Type\n# from torchvision.models import resnet18\n\nimport pydicom as dicom\nfrom PIL import Image\nimport imageio\n\nimport numpy as np \nimport pandas as pd\nfrom pathlib import Path\nimport os\nimport math\n\n%matplotlib inline\n\nimport matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\n\nfrom joblib import Parallel, delayed","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-02-01T00:05:20.911599Z","iopub.execute_input":"2023-02-01T00:05:20.91207Z","iopub.status.idle":"2023-02-01T00:05:28.680168Z","shell.execute_reply.started":"2023-02-01T00:05:20.912022Z","shell.execute_reply":"2023-02-01T00:05:28.678656Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from kaggle_secrets import UserSecretsClient\nuser_secrets = UserSecretsClient()\nclass CFG:\n    # file paths\n    train_data = \"/kaggle/input/rsna-breast-cancer-detection/train.csv\"\n    test_data = \"/kaggle/input/rsna-breast-cancer-detection/test.csv\"\n    train_images = \"/kaggle/input/rsna-mammography-images-as-pngs/images_as_pngs_512/train_images_processed_512\"\n    test_images = \"/kaggle/input/rsna-breast-cancer-detection/test_images/10008\"\n    \n    # wandb\n    project= user_secrets.get_secret(\"PROJECT\")\n    entity= user_secrets.get_secret(\"ENTITY\")\n    \n    # Device config\n    device = \"GPU\"\n    \n    # image \n    image_size = 256\n    \n    # batching \n    batch_size = 64\n    \n    random_seed = 123\n    num_workers = 2\n    random = True\n    \n    # loss, optimizer, epochs\n    loss_fn = nn.CrossEntropyLoss()\n    epochs = 10\n    \n    ","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:06:41.833499Z","iopub.execute_input":"2023-02-01T00:06:41.834423Z","iopub.status.idle":"2023-02-01T00:06:42.318587Z","shell.execute_reply.started":"2023-02-01T00:06:41.834385Z","shell.execute_reply":"2023-02-01T00:06:42.317453Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Device Configuration","metadata":{}},{"cell_type":"code","source":"if CFG.device == \"GPU\":\n    device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n    CFG.device = \"cuda\"\nprint(device)","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:06:42.483642Z","iopub.execute_input":"2023-02-01T00:06:42.484385Z","iopub.status.idle":"2023-02-01T00:06:42.608408Z","shell.execute_reply.started":"2023-02-01T00:06:42.484337Z","shell.execute_reply":"2023-02-01T00:06:42.607118Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Initialize Weights and Biases","metadata":{}},{"cell_type":"code","source":"# import wandb\n# try:\n#     api_key = user_secrets.get_secret(\"WANDB\")\n\n#     wandb.login(key=api_key)\n#     anonymous = None\n# except:\n#     anonymous = \"must\"\n#     print('To use your W&B account,\\nGo to Add-ons -> Secrets and provide your W&B access token. Use the Label name as WANDB. \\nGet your W&B access token from here: https://wandb.ai/authorize')\n# wandb.init(project=CFG.project, entity=CFG.entity)\n# wandb.config = {\n#   \"learning_rate\": 0.01,\n#   \"epochs\": CFG.epochs,\n#   \"batch_size\": CFG.batch_size\n# }","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:06:43.178858Z","iopub.execute_input":"2023-02-01T00:06:43.179697Z","iopub.status.idle":"2023-02-01T00:06:43.186377Z","shell.execute_reply.started":"2023-02-01T00:06:43.179665Z","shell.execute_reply":"2023-02-01T00:06:43.184945Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Import train data","metadata":{}},{"cell_type":"code","source":"TRAIN_DATA_PATH = CFG.train_data\nTRAIN_IMG_PATH = CFG.train_images\nTEST_DATA_PATH = CFG.test_data\nTEST_IMAGE_PATH = CFG.test_images\n\ntrain_df = pd.read_csv(f'{TRAIN_DATA_PATH}')\ntrain_df['image_path'] = f'{TRAIN_IMG_PATH}'\\\n                    + '/' + train_df.patient_id.astype(str)\\\n                    + '/' + train_df.image_id.astype(str)\\\n                    + '.png'\nprint('Train:')\ndisplay(train_df.head(2))","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:06:44.01827Z","iopub.execute_input":"2023-02-01T00:06:44.018649Z","iopub.status.idle":"2023-02-01T00:06:44.250007Z","shell.execute_reply.started":"2023-02-01T00:06:44.018615Z","shell.execute_reply":"2023-02-01T00:06:44.248839Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data = train_df[\"cancer\"].value_counts().values\nfig, ax = plt.subplots(figsize=(16, 8), subplot_kw=dict(aspect=\"equal\"))\ndef func(pct, allvals):\n    absolute = int(np.round(pct/100.*np.sum(allvals)))\n    return \"{:.1f}%\\n({:d} g)\".format(pct, absolute)\n\nwedges, texts, autotexts = ax.pie(data, autopct=lambda pct: func(pct, data),\n                                  textprops=dict(color=\"w\"))\nvalues = [\"No Cancer\", \"Cancer\"]\nax.legend(wedges, values,\n          title=\"Diagnosis\",\n          loc=\"center left\",\n          bbox_to_anchor=(1, 0, 0.5, 1))\nplt.setp(autotexts, size=8, weight=\"bold\")\n\nax.set_title(\"Patient Distribution\")\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:06:44.476822Z","iopub.execute_input":"2023-02-01T00:06:44.477607Z","iopub.status.idle":"2023-02-01T00:06:44.707219Z","shell.execute_reply.started":"2023-02-01T00:06:44.477564Z","shell.execute_reply":"2023-02-01T00:06:44.705575Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The data is very asymmetrical with 97.9% of patients presenting negative and 2.1% of patients presenting positive.","metadata":{}},{"cell_type":"code","source":"from sklearn.utils import resample\ncancer_df = train_df[train_df[\"cancer\"] == 1]\nclear_df = train_df[train_df[\"cancer\"] == 0]\n\ncancer_df = resample(cancer_df,\n                    replace=True,\n                    n_samples=len(clear_df),\n                    random_state=123)\ntrain_df = cancer_df.merge(clear_df, how=\"outer\")\n\ntrain_df","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:06:45.417683Z","iopub.execute_input":"2023-02-01T00:06:45.418089Z","iopub.status.idle":"2023-02-01T00:06:45.898941Z","shell.execute_reply.started":"2023-02-01T00:06:45.418056Z","shell.execute_reply":"2023-02-01T00:06:45.897843Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = train_df.sample(frac=0.5)\ntrain_df = train_df.reset_index()","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:06:45.901087Z","iopub.execute_input":"2023-02-01T00:06:45.902195Z","iopub.status.idle":"2023-02-01T00:06:45.95003Z","shell.execute_reply.started":"2023-02-01T00:06:45.902153Z","shell.execute_reply":"2023-02-01T00:06:45.948497Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data = train_df[\"cancer\"].value_counts().values\nfig, ax = plt.subplots(figsize=(16, 8), subplot_kw=dict(aspect=\"equal\"))\ndef func(pct, allvals):\n    absolute = int(np.round(pct/100.*np.sum(allvals)))\n    return \"{:.1f}%\\n({:d} g)\".format(pct, absolute)\n\nwedges, texts, autotexts = ax.pie(data, autopct=lambda pct: func(pct, data),\n                                  textprops=dict(color=\"w\"))\nvalues = [\"No Cancer\", \"Cancer\"]\nax.legend(wedges, values,\n          title=\"Diagnosis\",\n          loc=\"center left\",\n          bbox_to_anchor=(1, 0, 0.5, 1))\nplt.setp(autotexts, size=8, weight=\"bold\")\n\nax.set_title(\"Patient Distribution\")\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:06:46.266769Z","iopub.execute_input":"2023-02-01T00:06:46.267218Z","iopub.status.idle":"2023-02-01T00:06:46.43973Z","shell.execute_reply.started":"2023-02-01T00:06:46.267182Z","shell.execute_reply":"2023-02-01T00:06:46.437991Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data Pipeline","metadata":{}},{"cell_type":"markdown","source":"Split the train data into a train, val and test dataset\ncomprised of 80% train, 10% val, 10% test","metadata":{}},{"cell_type":"code","source":"# train test split for the train, val\nfrom sklearn.model_selection import train_test_split\ntrain_dataset, val_dataset = train_test_split(train_df, train_size=math.floor(0.80*(len(train_df))), random_state=CFG.random_seed)\nval_dataset, test_dataset = train_test_split(val_dataset, train_size=math.floor(0.50*(len(val_dataset))), random_state=CFG.random_seed)\n\n# reset index\ntrain_dataset.reset_index(inplace=True)\nval_dataset.reset_index(inplace=True)\ntest_dataset.reset_index(inplace=True)\n\n# display the length\ndisplay(len(train_dataset))\ndisplay(len(val_dataset))\ndisplay(len(test_dataset))","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:06:47.529392Z","iopub.execute_input":"2023-02-01T00:06:47.529781Z","iopub.status.idle":"2023-02-01T00:06:47.638886Z","shell.execute_reply.started":"2023-02-01T00:06:47.529749Z","shell.execute_reply":"2023-02-01T00:06:47.63794Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class buildDataset():\n    def __init__(self, X, y, test=False):\n        self.X = X\n        self.y = y\n        self.target_size = [CFG.image_size, CFG.image_size]\n        self.test = test\n        \n    def __getitem__(self, index):\n        \"\"\"generates one sample of the data\"\"\"\n        # Select sample\n        target_size = self.target_size\n        path = self.X[index]\n        y = self.y[index]\n        img = torchvision.io.read_image(path, mode = ImageReadMode.RGB)\n        X = self.transform(img) if not self.test else self.test_transform(img)\n        return X, y\n    \n    transform = T.Compose([\n        T.ToPILImage(),\n        T.Resize(CFG.image_size),\n        T.RandomRotation(45),\n        T.AutoAugment(),\n        T.ToTensor()])\n    \n    test_transform = T.Compose([\n        T.ToPILImage(),\n        T.Resize(CFG.image_size),\n        T.ToTensor()])\n\n    def __len__(self):\n        \"\"\"denotes number of samples\"\"\"\n        return len(self.X)","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:06:47.974033Z","iopub.execute_input":"2023-02-01T00:06:47.9746Z","iopub.status.idle":"2023-02-01T00:06:47.987223Z","shell.execute_reply.started":"2023-02-01T00:06:47.974564Z","shell.execute_reply":"2023-02-01T00:06:47.985952Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define the X, y values for the train and val datasets\nX_train = train_dataset[\"image_path\"]\ny_train = train_dataset[\"cancer\"]\n\nX_val = val_dataset[\"image_path\"]\ny_val = val_dataset[\"cancer\"]","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:06:48.362635Z","iopub.execute_input":"2023-02-01T00:06:48.363169Z","iopub.status.idle":"2023-02-01T00:06:48.372566Z","shell.execute_reply.started":"2023-02-01T00:06:48.363122Z","shell.execute_reply":"2023-02-01T00:06:48.37146Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Construct the pytorch datasets for the train and val data\ntrain_dataset = buildDataset(X_train, y_train)\nval_dataset = buildDataset(X_val, y_val)\n\n# Construct the pytorch dataloaders for the train and val data\ntrain_dl = DataLoader(train_dataset, CFG.batch_size, shuffle=CFG.random, num_workers=CFG.num_workers, pin_memory=True)\nval_dl = DataLoader(val_dataset, CFG.batch_size, shuffle=CFG.random, num_workers=CFG.num_workers, pin_memory=True)","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:06:48.819324Z","iopub.execute_input":"2023-02-01T00:06:48.820208Z","iopub.status.idle":"2023-02-01T00:06:48.827146Z","shell.execute_reply.started":"2023-02-01T00:06:48.820165Z","shell.execute_reply":"2023-02-01T00:06:48.826115Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def show_images(image, label, nmax=64):\n    fig, ax = plt.subplots(figsize=(8, 8))\n    ax.set_xticks([]); ax.set_yticks([])\n    ax.set_title(label)\n    ax.imshow(make_grid((image.detach()[:nmax]), nrow=8).permute(1, 2, 0))\ndef show_batch(dl, nmax=64):\n    for images, labels in dl:\n        show_images(images, labels, nmax)\n        break","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:06:49.298436Z","iopub.execute_input":"2023-02-01T00:06:49.299301Z","iopub.status.idle":"2023-02-01T00:06:49.309155Z","shell.execute_reply.started":"2023-02-01T00:06:49.299265Z","shell.execute_reply":"2023-02-01T00:06:49.30802Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for image, label in train_dl:\n    print(len(label))\n    print(len(image))\n    break","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:06:49.743569Z","iopub.execute_input":"2023-02-01T00:06:49.744542Z","iopub.status.idle":"2023-02-01T00:06:59.137276Z","shell.execute_reply.started":"2023-02-01T00:06:49.744506Z","shell.execute_reply":"2023-02-01T00:06:59.135995Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"show_batch(train_dl)","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:06:59.143308Z","iopub.execute_input":"2023-02-01T00:06:59.145858Z","iopub.status.idle":"2023-02-01T00:07:03.523177Z","shell.execute_reply.started":"2023-02-01T00:06:59.145808Z","shell.execute_reply":"2023-02-01T00:07:03.521882Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"show_batch(val_dl)","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:07:03.52475Z","iopub.execute_input":"2023-02-01T00:07:03.526198Z","iopub.status.idle":"2023-02-01T00:07:07.354293Z","shell.execute_reply.started":"2023-02-01T00:07:03.526153Z","shell.execute_reply":"2023-02-01T00:07:07.353117Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Pytorch Model","metadata":{}},{"cell_type":"markdown","source":"class ResNet18(nn.Module):\n    def __init__(self, num_classes=2):\n        super(ResNet18, self).__init__()\n\n        self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)\n        self.bn1 = nn.BatchNorm2d(64)\n        self.layer1 = nn.Sequential(\n            nn.Conv2d(64, 64, kernel_size=3, stride=2, padding=1, bias=False),\n            nn.BatchNorm2d(64),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1, bias=False),\n            nn.BatchNorm2d(64),\n            nn.ReLU(inplace=True)\n        )\n        self.layer2 = nn.Sequential(\n            nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=1, bias=False),\n            nn.BatchNorm2d(128),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1, bias=False),\n            nn.BatchNorm2d(128),\n            nn.ReLU(inplace=True)\n        )\n        \n        self.layer3 = nn.Sequential(\n            nn.Conv2d(128, 256, kernel_size=3, stride=2, padding=1, bias=False),\n            nn.BatchNorm2d(256),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1, bias=False),\n            nn.BatchNorm2d(256),\n            nn.ReLU(inplace=True)\n        )\n        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))\n        self.fc = nn.Linear(256, num_classes)\n\n    def forward(self, x):\n        x = self.conv1(x)\n        x = self.bn1(x)\n        x = self.layer1(x)\n        x = self.layer2(x)\n        x = self.layer3(x)\n        \n        \n    def save(self, model_path):\n        torch.save(self.state_dict(), model_path)\n","metadata":{"execution":{"iopub.status.busy":"2023-01-27T21:27:57.502694Z","iopub.execute_input":"2023-01-27T21:27:57.50309Z","iopub.status.idle":"2023-01-27T21:27:57.518506Z","shell.execute_reply.started":"2023-01-27T21:27:57.503054Z","shell.execute_reply":"2023-01-27T21:27:57.517312Z"}}},{"cell_type":"code","source":"torch.cuda.empty_cache()","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:07:07.358333Z","iopub.execute_input":"2023-02-01T00:07:07.359378Z","iopub.status.idle":"2023-02-01T00:07:07.365504Z","shell.execute_reply.started":"2023-02-01T00:07:07.359332Z","shell.execute_reply":"2023-02-01T00:07:07.364304Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def build_model():\n    # Create a ResNet-18 model\n    model = torchvision.models.resnet18(pretrained=True)\n\n    # Replace the last fully connected layer with a new layer for binary classification\n    model.fc = nn.Linear(512, 2)\n\n    model = model.to(device)\n    return model\n\n    \n    ","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:07:07.367288Z","iopub.execute_input":"2023-02-01T00:07:07.368042Z","iopub.status.idle":"2023-02-01T00:07:07.376865Z","shell.execute_reply.started":"2023-02-01T00:07:07.368Z","shell.execute_reply":"2023-02-01T00:07:07.375787Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = build_model()\n#wandb magic\n# wandb.watch(model, log_freq=100)","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:07:07.378334Z","iopub.execute_input":"2023-02-01T00:07:07.378834Z","iopub.status.idle":"2023-02-01T00:07:10.176465Z","shell.execute_reply.started":"2023-02-01T00:07:07.378796Z","shell.execute_reply":"2023-02-01T00:07:10.175275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"code","source":"class Train:\n    def __init__(self):\n        self = self\n    # Training Function \n    def train(self):\n        num_epochs = CFG.epochs\n        optimizer = torch.optim.SGD(model.parameters(), lr=0.01)\n\n        \n        best_accuracy = 0.0 \n\n        print(\"Begin training...\") \n        for epoch in range(1, num_epochs+1): \n            running_train_loss = 0.0 \n            running_accuracy = 0.0 \n            running_vall_loss = 0.0 \n            total = 0 \n\n            # Training Loop \n            for i, data in enumerate(train_dl, 0): \n            #for data in enumerate(train_loader, 0): \n                inputs, outputs = data  # get the input and real species as outputs; data is a list of [inputs, outputs]\n                inputs = torch.as_tensor(inputs, device=\"cuda\")\n                outputs = torch.as_tensor(outputs, device=\"cuda\")\n                optimizer.zero_grad()   # zero the parameter gradients          \n                predicted_outputs = model(inputs)   # predict output from the model \n                train_loss = CFG.loss_fn(predicted_outputs, outputs)   # calculate loss for the predicted output  \n                train_loss.backward()   # backpropagate the loss \n                optimizer.step()        # adjust parameters based on the calculated gradients \n                running_train_loss +=train_loss.item()  # track the loss value \n                wandb.log({\"loss\": running_train_loss})\n                if i % 100 == 99:    # print every 100 mini-batches\n                    print(f'[{epoch + 1}, {i + 1:5d}] loss: {running_train_loss / 2000:.3f}')\n                    running_loss = 0.0\n\n            # Calculate training loss value \n            train_loss_value = running_train_loss/len(train_dl) \n\n            # Validation Loop \n            with torch.no_grad(): \n                model.eval() \n                for data in val_dl:\n                    inputs, outputs = data\n                    inputs = torch.as_tensor(inputs, device=\"cuda\")\n                    outputs = torch.as_tensor(outputs, device=\"cuda\")\n                    predicted_outputs = model(inputs) \n                    val_loss = CFG.loss_fn(predicted_outputs, outputs) \n\n                    # The label with the highest value will be our prediction \n                    _, predicted = torch.max(predicted_outputs, 1) \n                    running_vall_loss += val_loss.item()  \n                    total += outputs.size(0) \n                    running_accuracy += (predicted == outputs).sum().item() \n\n            # Calculate validation loss value \n            val_loss_value = running_vall_loss/len(val_dl) \n\n            # Calculate accuracy as the number of correct predictions in the validation batch divided by the total number of predictions done.  \n            accuracy = (100 * running_accuracy / total)     \n\n            # Save the model if the accuracy is the best \n            if accuracy > best_accuracy: \n                self.saveModel() \n                best_accuracy = accuracy \n\n            # Print the statistics of the epoch \n            print('Completed training batch', epoch, 'Training Loss is: %.4f' %train_loss_value, 'Validation Loss is: %.4f' %val_loss_value, 'Accuracy is %d %%' % (accuracy))\n    \n    def saveModel(self): \n        path = \"./ResNet18Model2.pth\" \n        torch.save(model.state_dict(), path)\n    ","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:07:10.178459Z","iopub.execute_input":"2023-02-01T00:07:10.179207Z","iopub.status.idle":"2023-02-01T00:07:10.210488Z","shell.execute_reply.started":"2023-02-01T00:07:10.179163Z","shell.execute_reply":"2023-02-01T00:07:10.205093Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class KFoldValidation:\n    def __init__(self, model, dataset, epochs=10, criterion=CFG.criterion, optimizer=CFG.optimizer, device=CFG.device, path=\"./ResNet18Model3.pth\"):\n        self.model = model\n        self.dataset = dataset\n        self.criterion = criterion\n        self.optimizer = optimizer\n        self.device = device\n        self.path = path\n        \n    def train(self, train_loader):\n        running_train_loss = 0.0 \n        running_accuracy = 0.0 \n        for epoch in epochs:\n            for data in train_loader:\n                inputs, outputs = data  # get the input and real species as outputs; data is a list of [inputs, outputs]\n                inputs = torch.as_tensor(inputs, device=\"cuda\")\n                outputs = torch.as_tensor(outputs, device=\"cuda\")\n                optimizer.zero_grad()   # zero the parameter gradients          \n                predicted_outputs = self.model(inputs)   # predict output from the model \n                train_loss = CFG.loss_fn(predicted_outputs, outputs)   # calculate loss for the predicted output  \n                train_loss.backward()   # backpropagate the loss \n                optimizer.step()        # adjust parameters based on the calculated gradients \n                running_train_loss += train_loss.item()  # track the loss value \n\n                wandb.log({\"loss\": running_train_loss}) # log on wandb\n        return running_train_loss / len(train_loader) # return the loss\n            \n    def val(self, val_load):\n            model.eval()\n            val_loss = 0\n            with torch.no_grad():\n                for data in val_loader:\n                    inputs = torch.as_tensor(inputs, device=\"cuda\")\n                    outputs = torch.as_tensor(outputs, device=\"cuda\")\n                    predicted_output = model(data)\n                    loss = criterion(output, target)\n                    val_loss += loss.item()\n                    return val_loss / len(val_loader)\n    \n    def k_fold_cross_validation(self, k):\n        dataset_size = len(self.dataset)\n        fold_size = dataset_size // k\n        train_losses, val_losses = [], []\n        for i in range(k):\n            print(f'Fold {i+1}/{k}')\n            val_indices = list(range(i * fold_size, (i + 1) * fold_size))\n            train_indices = list(set(range(dataset_size)) - set(val_indices))\n            train_sampler = torch.utils.data.SubsetRandomSampler(train_indices)\n            val_sampler = torch.utils.data.SubsetRandomSampler(val_indices)\n            train_loader = DataLoader(dataset, batch_size=32, sampler=train_sampler)\n            val_loader = DataLoader(dataset, batch_size=32, sampler=val_sampler)\n            self.model.to(device)\n            train_loss = train(model, train_loader, criterion, optimizer, device)\n            val_loss = validate(model, val_loader, criterion, device)\n            train_losses.append(train_loss)\n            val_losses.append(val_loss)\n            avg_train_loss = sum(train_losses) / k\n            avg_val_loss = sum(val_losses) / k\n\n            # Save the model if the accuracy is the best \n            if avg_val_loss > best_val_loss: \n                self.saveModel() \n                best_val_loss = avg_val_loss\n        return avg_train_loss, avg_val_loss\n    \n    def saveModel(self):  \n        torch.save(model.state_dict(), self.path)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# trainer = Train()\n# trainer.train()\n# print('Finished Training\\n') ","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:07:10.213392Z","iopub.execute_input":"2023-02-01T00:07:10.214132Z","iopub.status.idle":"2023-02-01T00:07:10.228715Z","shell.execute_reply.started":"2023-02-01T00:07:10.214081Z","shell.execute_reply":"2023-02-01T00:07:10.22767Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Testing","metadata":{}},{"cell_type":"code","source":"checkpoint_path = \"/kaggle/input/resnet18model2/ResNet18Model2.pth\"\ncheckpoint  = torch.load(checkpoint_path)\nmodel.load_state_dict(checkpoint, strict=False)\nmodel.eval()","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:07:10.23045Z","iopub.execute_input":"2023-02-01T00:07:10.231015Z","iopub.status.idle":"2023-02-01T00:07:10.967911Z","shell.execute_reply.started":"2023-02-01T00:07:10.230968Z","shell.execute_reply":"2023-02-01T00:07:10.966958Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_test, y_test = test_dataset[\"image_path\"], test_dataset[\"cancer\"]\ntest_dataset = buildDataset(X_test, y_test)\ntest_dl = DataLoader(test_dataset, CFG.batch_size, shuffle=CFG.random, num_workers=CFG.num_workers, pin_memory=True)","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:07:10.971726Z","iopub.execute_input":"2023-02-01T00:07:10.972082Z","iopub.status.idle":"2023-02-01T00:07:10.979107Z","shell.execute_reply.started":"2023-02-01T00:07:10.972052Z","shell.execute_reply":"2023-02-01T00:07:10.977971Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Test:\n    def __init__(self, model, test_dl):\n        self.model = model\n        self.test_dl = test_dl\n    def testAccuracy(self):\n        test_dl = self.test_dl\n        accuracy = 0.0\n        total = 0.0\n\n        with torch.no_grad():\n            for data in test_dl:\n                images, labels = data\n                # run the model on the test set to predict labels\n                images = torch.as_tensor(images, device=\"cuda\")\n                labels = torch.as_tensor(labels, device=\"cuda\")\n                outputs = self.model(images)\n                # the label with the highest energy will be our prediction\n                _, predicted = torch.max(outputs.data, 1)\n                total += labels.size(0)\n                accuracy += (predicted == labels).sum().item()\n\n        # compute the accuracy over all test images\n        accuracy = (100 * accuracy / total)\n        return(accuracy)\n                \n    # Function to show the images\n    def imageshow(self, img):\n        img = img.cpu()\n        img = img / 2 + 0.5     # unnormalize\n        npimg = img.numpy()\n        plt.imshow(np.transpose(npimg, (1, 2, 0)))\n        plt.show()\n\n\n    # Function to test the model with a batch of images and show the labels predictions\n    def testBatch(self):\n        classes = [\"No Cancer\", \"Cancer\"]\n        batch_size = CFG.batch_size\n        test_dl = self.test_dl\n        # get batch of images from the test DataLoader  \n        images, labels = next(iter(test_dl))\n        images = torch.as_tensor(images, device=\"cuda\")\n        labels = torch.as_tensor(labels, device=\"cuda\")\n\n        # show all images as one image grid\n        self.imageshow(torchvision.utils.make_grid(images))\n\n        # Show the real labels on the screen \n        print('Real labels: ', ' '.join('%5s' % classes[labels[j]] \n                                   for j in range(batch_size)))\n\n        # Let's see what if the model identifiers the  labels of those example\n        outputs = model(images)\n\n        # We got the probability for every 10 labels. The highest (max) probability should be correct label\n        _, predicted = torch.max(outputs, 1)\n\n        # Let's show the predicted labels on the screen to compare with the real ones\n        print('Predicted: ', ' '.join('%5s' % classes[predicted[j]] \n                                  for j in range(batch_size)))\n    ","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:07:10.98085Z","iopub.execute_input":"2023-02-01T00:07:10.981837Z","iopub.status.idle":"2023-02-01T00:07:10.996279Z","shell.execute_reply.started":"2023-02-01T00:07:10.981798Z","shell.execute_reply":"2023-02-01T00:07:10.995141Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tester = Test(model, test_dl)\ntester.testAccuracy()","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:07:10.99774Z","iopub.execute_input":"2023-02-01T00:07:10.998322Z","iopub.status.idle":"2023-02-01T00:08:16.33507Z","shell.execute_reply.started":"2023-02-01T00:07:10.998284Z","shell.execute_reply":"2023-02-01T00:08:16.333882Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tester = Test(model, test_dl)\ntester.testBatch()","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:08:16.337315Z","iopub.execute_input":"2023-02-01T00:08:16.337722Z","iopub.status.idle":"2023-02-01T00:08:19.755091Z","shell.execute_reply.started":"2023-02-01T00:08:16.337682Z","shell.execute_reply":"2023-02-01T00:08:19.753675Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"preds = torch.tensor([]).to(device=\"cuda\")\ntarget = torch.tensor([]).to(device=\"cuda\")\nfor data in test_dl:\n    images, labels = data\n    # run the model on the test set to predict labels\n    images = torch.as_tensor(images, device=\"cuda\")\n    labels = torch.as_tensor(labels, device=\"cuda\")\n    outputs = model(images)\n    # the label with the highest energy will be our prediction\n    _, predicted = torch.max(outputs.data, 1)\n    preds = torch.cat((preds, predicted), 0)\n    target = torch.cat((target, labels), 0)","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:09:47.612322Z","iopub.execute_input":"2023-02-01T00:09:47.612861Z","iopub.status.idle":"2023-02-01T00:10:33.040202Z","shell.execute_reply.started":"2023-02-01T00:09:47.612812Z","shell.execute_reply":"2023-02-01T00:10:33.038724Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from torchmetrics.classification import BinaryConfusionMatrix\nmetric = BinaryConfusionMatrix().to(device=\"cuda\")\ncfn_mtrx = metric(preds, target)","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:10:33.043104Z","iopub.execute_input":"2023-02-01T00:10:33.043444Z","iopub.status.idle":"2023-02-01T00:10:34.666866Z","shell.execute_reply.started":"2023-02-01T00:10:33.043411Z","shell.execute_reply":"2023-02-01T00:10:34.665582Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cfn_mtrx = pd.DataFrame(cfn_mtrx.cpu().numpy(), columns=[\"Positive\", \"Negative\"])\ncfn_mtrx","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:10:34.669033Z","iopub.execute_input":"2023-02-01T00:10:34.669439Z","iopub.status.idle":"2023-02-01T00:10:34.684111Z","shell.execute_reply.started":"2023-02-01T00:10:34.669397Z","shell.execute_reply":"2023-02-01T00:10:34.682874Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.DataFrame()\ndf[\"predicted\"] = preds.cpu().numpy()\ndf[\"target\"] = target.cpu().numpy()\ndf[\"diff\"] = abs(df[\"predicted\"] - df[\"target\"])","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:10:34.687674Z","iopub.execute_input":"2023-02-01T00:10:34.68919Z","iopub.status.idle":"2023-02-01T00:10:34.699371Z","shell.execute_reply.started":"2023-02-01T00:10:34.689148Z","shell.execute_reply":"2023-02-01T00:10:34.698286Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"f\"Model produces {(df['diff'].sum() / len(df)) * 100:.2f}% inaccurate results\"","metadata":{"execution":{"iopub.status.busy":"2023-02-01T00:10:34.70101Z","iopub.execute_input":"2023-02-01T00:10:34.702116Z","iopub.status.idle":"2023-02-01T00:10:34.71158Z","shell.execute_reply.started":"2023-02-01T00:10:34.702063Z","shell.execute_reply":"2023-02-01T00:10:34.710246Z"},"trusted":true},"execution_count":null,"outputs":[]}]}