{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# import libraries we will need later\nimport numpy as np \nimport pandas as pd \nimport matplotlib.pyplot as plt\nfrom matplotlib import image as mp_image\nimport seaborn as sns\nimport os\nimport shutil\n%matplotlib inline\n\n# for assessing model later\nfrom sklearn import metrics\nfrom sklearn.metrics import accuracy_score, confusion_matrix\nfrom sklearn.model_selection import train_test_split","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-06-04T00:41:52.793318Z","iopub.execute_input":"2021-06-04T00:41:52.793682Z","iopub.status.idle":"2021-06-04T00:41:52.800784Z","shell.execute_reply.started":"2021-06-04T00:41:52.793653Z","shell.execute_reply":"2021-06-04T00:41:52.799598Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# The images are in a folder named 'input/natural-images/natural_images'\ntraining_folder_name = '../input/histopathologic-cancer-detection/train'\n\n# All images are 96x96 pixels\nimg_size = (96,96)\n\n# Make classes easy to access for later\nclasses = [\"cancer\",\"no_cancer\"]\nprint(classes)","metadata":{"execution":{"iopub.status.busy":"2021-06-04T00:41:52.802611Z","iopub.execute_input":"2021-06-04T00:41:52.803035Z","iopub.status.idle":"2021-06-04T00:41:52.812655Z","shell.execute_reply.started":"2021-06-04T00:41:52.803001Z","shell.execute_reply":"2021-06-04T00:41:52.811604Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Import PyTorch libraries\nimport torch\nimport torchvision\nimport torchvision.transforms as transforms\nimport torch.nn as nn\nimport torch.optim as optim\nimport torch.nn.functional as F\n\nprint(\"Libraries imported - ready to use PyTorch\", torch.__version__)","metadata":{"execution":{"iopub.status.busy":"2021-06-04T00:41:52.814906Z","iopub.execute_input":"2021-06-04T00:41:52.815239Z","iopub.status.idle":"2021-06-04T00:41:52.82415Z","shell.execute_reply.started":"2021-06-04T00:41:52.815207Z","shell.execute_reply":"2021-06-04T00:41:52.823368Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from PIL import Image\n\n# function to resize images, in case any are not actually 96 x 96 pixels\ndef resize_image(src_image, size=(96,96), bg_color=\"white\"): \n    from PIL import Image, ImageOps \n    \n    # resize the image so the longest dimension matches our target size\n    src_image.thumbnail(size, Image.ANTIALIAS)\n    \n    # Create a new square background image\n    new_image = Image.new(\"RGB\", size, bg_color)\n    \n    # Paste the resized image into the center of the square background\n    new_image.paste(src_image, (int((size[0] - src_image.size[0]) / 2), int((size[1] - src_image.size[1]) / 2)))\n    return new_image","metadata":{"execution":{"iopub.status.busy":"2021-06-04T00:41:52.825327Z","iopub.execute_input":"2021-06-04T00:41:52.825613Z","iopub.status.idle":"2021-06-04T00:41:52.83507Z","shell.execute_reply.started":"2021-06-04T00:41:52.825589Z","shell.execute_reply":"2021-06-04T00:41:52.834275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# main folder to use later, just saving file directory\ntraining_folder_name = '../input/histopathologic-cancer-detection'\n\n# new location for the resized images\ntrain_folder = '../working/data/edadatafolder'\n\n# to use to read all the files\nimport glob\n\n# store the size we want\nsize = (96,96)\n\n# Create the output folder if it doesn't already exist\nif os.path.exists(train_folder):\n    shutil.rmtree(train_folder)\n\n# Create a dictionary with the file names and if they have cancer or not\n#skiprows = 1, so we skip the first row, which is just \"label\" and \"id\"\ncancer_dict = pd.read_csv('../input/histopathologic-cancer-detection/train_labels.csv', header=None, index_col=0, squeeze=True, skiprows = 1).to_dict()\n\n# Loop through each subfolder in the input folder\nprint('Transforming images...')\nfileglob = glob.glob('../input/histopathologic-cancer-detection/train', recursive = False)\ntotal_files = 200000\n\n#loop through everything we want to use\nfor files in fileglob:\n    print(\"started for loop\")\n    file_names = os.listdir('../input/histopathologic-cancer-detection/train')\n    # start a for loop that loops thorugh all the files we want\n    for i in range(total_files):\n        if (i % (total_files/10) == 0):\n            print (\"working \" + str(i/(total_files/10)) + \"/10\")\n        # the file number 15037 supposedly crashes the program, so we avoid it\n        if (i != 15037):\n            \n            # get the file names, then add it to the output folder, with the labels under the respective folder\n            file_name = file_names[i]\n            result = cancer_dict[file_name[:-4]]\n            saveFolder = os.path.join(train_folder, classes[result])\n            if not os.path.exists(saveFolder):\n                os.makedirs(saveFolder)\n            file_path = os.path.join('../input/histopathologic-cancer-detection/train', file_name)\n            \n            # now get the images, resize them, then throw them into the new folders\n            image = Image.open(file_path)\n            resized_image = resize_image(image, size)\n            saveAs = os.path.join(saveFolder, file_name)\n            resized_image.save(saveAs)\n\nprint('Done')","metadata":{"execution":{"iopub.status.busy":"2021-06-04T00:41:52.836454Z","iopub.execute_input":"2021-06-04T00:41:52.837026Z","iopub.status.idle":"2021-06-04T00:41:53.353629Z","shell.execute_reply.started":"2021-06-04T00:41:52.836985Z","shell.execute_reply":"2021-06-04T00:41:53.352851Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_dataset(data_path):\n    import torch\n    import torchvision\n    import torchvision.transforms as transforms\n    # Load all the images\n    transformation = transforms.Compose([\n        # Randomly augment the image data\n            # Random horizontal flip\n        transforms.RandomHorizontalFlip(0.5),\n            # Random vertical flip\n        transforms.RandomVerticalFlip(0.3),\n        # transform to tensors\n        transforms.ToTensor(),\n        # Normalize the pixel values (in R, G, and B channels)\n        transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])\n    ])\n\n    # Load all of the images, transforming them\n    full_dataset = torchvision.datasets.ImageFolder(\n        root=data_path,\n        transform=transformation\n    )\n    \n    \n    # Split into training (70% for training, 30% for testing)\n    train_size = int(0.7 * len(full_dataset))\n    test_size = len(full_dataset) - train_size\n    \n    # use torch.utils.data.random_split for training/test split\n    train_dataset, test_dataset = torch.utils.data.random_split(full_dataset, [train_size, test_size])\n    \n    # define a loader for the training data \n    train_loader = torch.utils.data.DataLoader(\n        train_dataset,\n        batch_size=3,\n        num_workers=0,\n        shuffle=False\n    )\n    \n    # define a loader for the testing data \n    test_loader = torch.utils.data.DataLoader(\n        test_dataset,\n        batch_size=3,\n        num_workers=0,\n        shuffle=False\n    )\n        \n    return train_loader, test_loader\n\n\n\n\n#####################################################################################################\n\n\n\n\n# Recall that we have resized the images and saved them into\ntrain_folder = '../working/data/edadatafolder'\n\n# Set the iterative dataloaders for test and training data\ntrain_loader, test_loader = load_dataset(train_folder)\nbatch_size = train_loader.batch_size\nprint(\"Data loaders ready to read\", train_folder)\n","metadata":{"execution":{"iopub.status.busy":"2021-06-04T00:41:53.354922Z","iopub.execute_input":"2021-06-04T00:41:53.355266Z","iopub.status.idle":"2021-06-04T00:41:53.366525Z","shell.execute_reply.started":"2021-06-04T00:41:53.355229Z","shell.execute_reply":"2021-06-04T00:41:53.365501Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create a neural net class\nclass Net(nn.Module):\n    \n    \n    # Defining the Constructor\n    def __init__(self, num_classes=3):\n        super(Net, self).__init__()\n        \n        \n        # Our images are RGB, so we have input channels = 3 and output = 32. Kernel size = 3\n        # because it worked well with my neural network\n        self.conv1 = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, padding=2)\n        \n        # doubling the input to output with each hidden layer\n        self.conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding=2)\n        self.conv3 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, padding=2)\n        self.conv4 = nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, padding=2)\n        \n        # batchnorm has learnable parameters, but pass in the correct size to use in relu\n        self.bn1 = nn.BatchNorm2d(32)\n        self.bn2 = nn.BatchNorm2d(64)\n        self.bn3 = nn.BatchNorm2d(128)\n        self.bn4 = nn.BatchNorm2d(256)\n        \n        #setting variables to use inside of the relu and when we actually maxpool\n        self.pool = nn.MaxPool2d(kernel_size=2, stride=2)\n        self.avg=nn.AvgPool2d(4)\n        #256*1*1 becuase 256 input channels. out_features = 2, cancer and non_cancer\n        self.fc = nn.Linear(in_features=256*1*1, out_features=num_classes)\n\n    def forward(self, x):\n        # relu using variables we previously defined\n        x = self.pool(F.relu(self.bn1(self.conv1(x))))\n        x = self.pool(F.relu(self.bn2(self.conv2(x))))\n        x = self.pool(F.relu(self.bn3(self.conv3(x))))\n        x = self.pool(F.relu(self.bn4(self.conv4(x))))\n        x = self.avg(x)\n        \n        # Flatten\n        x = x.view(-1, 256*1*1)\n        # Feed to fully-connected layer to predict class\n        x = self.fc(x)\n        # Return class probabilities via a log_softmax function \n        return torch.log_softmax(x, dim=1)\n    \ndevice = \"cpu\"\nif (torch.cuda.is_available()):\n    # if GPU available, use cuda, so faster training\n    device = \"cuda\"\n\n# Create an instance of the model class and allocate it to the device\nmodel = Net(num_classes=len(classes)).to(device)\n\nprint(model)","metadata":{"execution":{"iopub.status.busy":"2021-06-04T00:41:53.36885Z","iopub.execute_input":"2021-06-04T00:41:53.369509Z","iopub.status.idle":"2021-06-04T00:41:53.39591Z","shell.execute_reply.started":"2021-06-04T00:41:53.369474Z","shell.execute_reply":"2021-06-04T00:41:53.395047Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train(model, device, train_loader, optimizer, epoch):\n    # Set the model to training mode\n    model.train()\n    train_loss = 0\n    print(\"Starting Epoch:\", epoch)\n    # Process the images in batches\n    for batch_idx, (data, target) in enumerate(train_loader):\n        data, target = data.to(device), target.to(device)\n        \n        # Reset the optimizer\n        optimizer.zero_grad()\n        \n        # Push the data forward through the model layers\n        output = model(data)\n        \n        # Get the loss\n        loss = loss_criteria(output, target)\n\n        # Keep a running total\n        train_loss += loss.item()\n        \n        # Backpropagate and learn\n        loss.backward()\n        optimizer.step()\n        \n        # not printing loss during epochs, as it lags quite a bit with too many inputs. \n        # loss after each epoch is what we care about after all, not after x images inside an epoch\n            \n    # return average loss for the epoch\n    avg_loss = train_loss / (batch_idx+1)\n    return avg_loss","metadata":{"execution":{"iopub.status.busy":"2021-06-04T00:41:53.398407Z","iopub.execute_input":"2021-06-04T00:41:53.398762Z","iopub.status.idle":"2021-06-04T00:41:53.405831Z","shell.execute_reply.started":"2021-06-04T00:41:53.398727Z","shell.execute_reply":"2021-06-04T00:41:53.404823Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def test(model, device, test_loader):\n    # Switch the model to evaluation mode (so we don't backpropagate or drop)\n    model.eval()\n    test_loss = 0\n    correct = 0\n    with torch.no_grad():\n        batch_count = 0\n        for data, target in test_loader:\n            batch_count += 1\n            data, target = data.to(device), target.to(device)\n            \n            # Get the predicted classes for this batch\n            output = model(data)\n            \n            # Calculate the loss for this batch\n            test_loss += loss_criteria(output, target).item()\n            \n            # Calculate the accuracy for this batch\n            _, predicted = torch.max(output.data, 1)\n            correct += torch.sum(target==predicted).item()\n\n    # Calculate the average loss for this epoch then return it\n    avg_loss = test_loss / batch_count\n    return avg_loss","metadata":{"execution":{"iopub.status.busy":"2021-06-04T00:41:53.40745Z","iopub.execute_input":"2021-06-04T00:41:53.407948Z","iopub.status.idle":"2021-06-04T00:41:53.414849Z","shell.execute_reply.started":"2021-06-04T00:41:53.407901Z","shell.execute_reply":"2021-06-04T00:41:53.413983Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# learning rate set to 0.005, which was what i found to be best\noptimizer = optim.Adam(model.parameters(), lr=0.005)\n\n# Specify the loss criteria\nloss_criteria = nn.CrossEntropyLoss()\n\n# Track metrics in these arrays\nepoch_nums = []\ntraining_loss = []\nvalidation_loss = []\n\n# Train over 15 epochs because we have the time, but I found that 5 to 15 doesn't change too much honestly\nepochs = 15\nprint('Training on', device)\nfor epoch in range(1, epochs + 1):\n        train_loss = train(model, device, train_loader, optimizer, epoch)\n        test_loss = test(model, device, test_loader)\n        print(\"Average Loss After Epoch \" + str(epoch) + \": \" + str(test_loss))\n        epoch_nums.append(epoch)\n        training_loss.append(train_loss)\n        validation_loss.append(test_loss)\n#make a plot to see the progression of loss\nplt.figure(figsize=(15,15))\nplt.plot(epoch_nums, training_loss)\nplt.plot(epoch_nums, validation_loss)\nplt.xlabel('epoch')\nplt.ylabel('loss')\nplt.legend(['training', 'validation'], loc='upper right')\nplt.show() ","metadata":{"execution":{"iopub.status.busy":"2021-06-04T00:41:53.416046Z","iopub.execute_input":"2021-06-04T00:41:53.41655Z","iopub.status.idle":"2021-06-04T00:41:54.470103Z","shell.execute_reply.started":"2021-06-04T00:41:53.416512Z","shell.execute_reply":"2021-06-04T00:41:54.469288Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Defining Labels and Predictions\ntruelabels = []\npredictions = []\nmodel.eval()\nprint(\"Getting predictions from test set...\")\n#get predictions and real answers\nfor data, target in test_loader:\n    for label in target.data.numpy():\n        truelabels.append(label)\n    for prediction in model(data.to(device)).cpu():\n        p = prediction.data.numpy()\n        predictions.append(p.argmax(0))\n\n# define then plot confusion matrix\ncm = confusion_matrix(truelabels, predictions)\ntick_marks = np.arange(len(classes))\nprint(cm)\n# create a dataframe of the confusion matrix\ndf_cm = pd.DataFrame(cm, index = classes, columns = classes)\nprint(\"PLEASE WORK\")\n#write accuracy, then print it\naccuracy = (100*(cm[0,0] + cm[1,1])/(cm[0,0]+cm[0,1]+cm[1,0]+cm[1,1]))\nprint(\"accuracy: \"+ str(accuracy) + \"%\")\n#plot the final confusion matrix\nplt.figure(figsize = (7,7))\nsns.heatmap(df_cm, annot=True, cmap=plt.cm.Blues, fmt='g')\nplt.xlabel(\"Predicted Shape\", fontsize = 20)\nplt.ylabel(\"True Shape\", fontsize = 20)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-04T00:41:54.471377Z","iopub.execute_input":"2021-06-04T00:41:54.471855Z","iopub.status.idle":"2021-06-04T00:41:54.711816Z","shell.execute_reply.started":"2021-06-04T00:41:54.471818Z","shell.execute_reply":"2021-06-04T00:41:54.711039Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import shutil\nshutil.rmtree(\"../working/data/edadatafolder\")","metadata":{"execution":{"iopub.status.busy":"2021-06-04T00:41:54.713018Z","iopub.execute_input":"2021-06-04T00:41:54.713358Z","iopub.status.idle":"2021-06-04T00:41:54.719951Z","shell.execute_reply.started":"2021-06-04T00:41:54.713304Z","shell.execute_reply":"2021-06-04T00:41:54.718932Z"},"trusted":true},"execution_count":null,"outputs":[]}]}