{"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":"## https://www.kaggle.com/lostbox/rsna-classification-87-6-best-accuracy-p-cda585 was used as a guide in the transformation process\n\nimport os\nimport pandas as pd\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torchvision\nimport torchvision.transforms as transforms\nimport matplotlib.pyplot as plt\nfrom PIL import Image\nfrom pydicom import dcmread\nfrom tqdm import tqdm\nfrom torch.utils import data\nfrom sklearn.model_selection import train_test_split","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Preparing labels","metadata":{}},{"cell_type":"code","source":"label_data = pd.read_csv('../input/rsna-pneumonia-detection-challenge/stage_2_train_labels.csv')\ncolumns = ['patientId', 'Target']\n\nlabel_data = label_data.filter(columns)\nlabel_data.head(5)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_labels, val_labels = train_test_split(label_data.values, test_size=0.1)\nprint(train_labels.shape)\nprint(val_labels.shape)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_f = '../input/rsna-pneumonia-detection-challenge/stage_2_train_images'\ntest_f = '../input/rsna-pneumonia-detection-challenge/stage_2_test_images'\n\ntrain_paths = [os.path.join(train_f, image[0]) for image in train_labels]\nval_paths = [os.path.join(train_f, image[0]) for image in val_labels]\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"transform = transforms.Compose([\n    transforms.RandomHorizontalFlip(),\n    transforms.Resize(224),\n    transforms.ToTensor()])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Write a custom dataset ","metadata":{}},{"cell_type":"code","source":"class Dataset(data.Dataset):\n    \n    def __init__(self, paths, labels, transform=None):\n        self.paths = paths\n        self.labels = labels\n        self.transform = transform\n    \n    def __getitem__(self, index):\n        image = dcmread(f'{self.paths[index]}.dcm')\n        image = image.pixel_array\n        image = image / 255.0\n\n        image = (255*image).clip(0, 255).astype(np.uint8)\n        image = Image.fromarray(image).convert('RGB')\n\n        label = self.labels[index][1]\n        \n        if self.transform is not None:\n            image = self.transform(image)\n            \n        return image, label\n    \n    def __len__(self):\n        \n        return len(self.paths)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataset = Dataset(train_paths, train_labels, transform=transform)\nimage = iter(train_dataset)\nimg, label = next(image)\nprint(f'Tensor:{img}, Label:{label}')\nimg = np.transpose(img, (1, 2, 0))\nplt.imshow(img)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Train image shape","metadata":{}},{"cell_type":"code","source":"img.shape","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Prepare training and validation dataloader","metadata":{}},{"cell_type":"code","source":"train_dataset = Dataset(train_paths, train_labels, transform=transform)\nval_dataset = Dataset(val_paths, val_labels, transform=transform)\ntrain_loader = data.DataLoader(dataset=train_dataset, batch_size=128, shuffle=True)\nval_loader = data.DataLoader(dataset=val_dataset, batch_size=128, shuffle=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Check dataloader","metadata":{}},{"cell_type":"code","source":"batch = iter(train_loader)\nimages, labels = next(batch)\n\nimage_grid = torchvision.utils.make_grid(images[:4])\nimage_np = image_grid.numpy()\nimg = np.transpose(image_np, (1, 2, 0))\nplt.imshow(img)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Specify device object","metadata":{}},{"cell_type":"code","source":"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Load pre-trained ResNet18 and fine-tune","metadata":{}},{"cell_type":"code","source":"model = torchvision.models.resnet18(pretrained=True)\nnum_ftrs = model.fc.in_features\n# Here the size of each output sample is set to 2.\n# Alternatively, it can be generalized to nn.Linear(num_ftrs, len(class_names)).\nmodel.fc = nn.Linear(num_ftrs, 2)\n\nmodel.to(device)\n\ncriterion = nn.CrossEntropyLoss()\ncriterion2 = nn.CrossEntropyLoss()\n# Observe that all parameters are being optimized\noptimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)\n\n# Decay LR by a factor of 0.1 every 7 epochs\nexp_lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(model) # ResNet18 Model","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Write a train code and RUN","metadata":{}},{"cell_type":"code","source":"num_epochs = 20\n# Train the model\ntotal_step = len(train_loader)\n\ntrain_loss, validation_loss = [], []\ntrain_acc, validation_acc = [], []\n\nfor epoch in range(num_epochs):\n    model.train()\n    \n    running_loss = 0.\n    correct, total = 0, 0\n    steps = 0\n    # Training step\n    for i, (images, labels) in tqdm(enumerate(train_loader)):\n        images = images.to(device)\n        labels = labels.to(device)\n        \n        # Forward pass\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        \n        print(\"Loss: \" + str(loss.item()))\n        \n        steps += 1\n        running_loss += loss.item()\n                \n        # Backward and optimize\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n        \n        # get accuracy\n        _, predicted = torch.max(outputs, 1)\n        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n\n        \n        if (i+1) % 2000 == 0:\n            \n            print(\"Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}\"\n                   .format(epoch+1, num_epochs, i+1, total_step, loss.item()))\n\n    train_loss.append(running_loss/len(train_loader))\n    train_acc.append(correct/total)\n    \n    print(f'Epoch: {epoch + 1},  Training Loss: {running_loss/len(train_loader):.4f}, Training Accuracy: {100*correct/total: .2f}%')\n    \n    # Validation step\n    model.eval()\n    running_loss = 0.\n    correct = 0\n    total = 0\n    with torch.no_grad():\n        for images, labels in tqdm(val_loader):\n            images = images.to(device)\n            labels = labels.to(device)\n            predictions = model(images)\n            loss = criterion(predictions, labels)\n            running_loss += loss.item()\n            _, predicted = torch.max(predictions, 1)\n            total += labels.size(0)\n            correct += (labels == predicted).sum()\n    validation_loss.append(running_loss/len(val_loader))\n    validation_acc.append(correct/total)\n        \n    print(f'Epoch: {epoch+1}/{num_epochs}, Validation Loss : {running_loss / len(val_loader)} Val_Acc: {100*correct/total}')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_loss","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_acc","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"validation_loss","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"validation_acc","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Test model","metadata":{}},{"cell_type":"code","source":"model.eval()\n\ncorrect = 0\ntotal = 0\nval_preds = torch.tensor([]).to(device)\nfor images, labels in tqdm(val_loader):\n    images = images.to(device)\n    labels = labels.to(device)\n    predictions = model(images)\n    _, predicted = torch.max(predictions, 1)\n    val_preds = torch.cat((val_preds, predicted),dim=0)\n    total += labels.size(0)\n    correct += (labels == predicted).sum()\nprint(f'Val_Acc: {100*correct/total}')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"val_preds.shape","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pip install git+git://github.com/raghakot/keras-vis.git --upgrade --no-deps","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def print_saliency(i):\n    image_test = images[i]\n    # from: https://towardsdatascience.com/saliency-map-using-pytorch-68270fe45e80\n    image_test = image_test.reshape(1, 3, 224, 224)\n    image_test = image_test.to(device)\n    image_test.requires_grad_()\n\n    # Retrieve output from the image\n    output = model(image_test)\n\n    # Catch the output\n    output_idx = output.argmax()\n    output_max = output[0, output_idx]\n\n    # Do backpropagation to get the derivative of the output based on the image\n    output_max.backward()\n    \n    # Retireve the saliency map and also pick the maximum value from channels on each pixel.\n    # In this case, we look at dim=1. Recall the shape (batch_size, channel, width, height)\n    saliency, _ = torch.max(image_test.grad.data.abs(), dim=1) \n    saliency = saliency.reshape(224, 224)\n\n    # Reshape the image\n    image_test = image_test.reshape(-1, 224, 224)\n\n    # Visualize the image and the saliency map\n    fig, ax = plt.subplots(1, 2)\n    ax[0].imshow(image_test.cpu().detach().numpy().transpose(1, 2, 0))\n    ax[0].axis('off')\n    ax[1].imshow(saliency.cpu(), cmap='hot')\n    ax[1].axis('off')\n    plt.tight_layout()\n    fig.suptitle('The Image and Its Saliency Map')\n    plt.show()\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print_saliency(0)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(labels[0])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print_saliency(50)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(labels[50])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print_saliency(78)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(labels[78])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print_saliency(20)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(labels[20])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"torch.save(model.state_dict(), \"Transfer_Learning_Resnet.pt\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Confusion Matrix\n# ","metadata":{}},{"cell_type":"code","source":"model.load_state_dict(torch.load('../input/transfer-learning-resnetpt/Transfer_Learning_Resnet.pt'))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.eval()\n\ncorrect = 0\ntotal = 0\nval_preds = torch.tensor([]).to(device)\nval_labels = torch.tensor([]).to(device)\nfor images, labels in tqdm(val_loader):\n    images = images.to(device)\n    labels = labels.to(device)\n    predictions = model(images)\n    _, predicted = torch.max(predictions, 1)\n    val_preds = torch.cat((val_preds, predicted),dim=0)\n    val_labels = torch.cat((val_labels, labels),dim=0)\n    total += labels.size(0)\n    correct += (labels == predicted).sum()\nprint(f'Val_Acc: {100*correct/total}') # Acc may be messed up because train split may be diff this time around","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(val_preds.shape, val_labels.shape)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.eval()\n\ncorrect = 0\ntotal = 0\ntrain_preds = torch.tensor([]).to(device)\ntrain_labels = torch.tensor([]).to(device)\nfor images, labels in tqdm(train_loader):\n    images = images.to(device)\n    labels = labels.to(device)\n    predictions = model(images)\n    _, predicted = torch.max(predictions, 1)\n    train_preds = torch.cat((train_preds, predicted),dim=0)\n    train_labels = torch.cat((train_labels, labels),dim=0)\n    total += labels.size(0)\n    correct += (labels == predicted).sum()\nprint(f'Train_Acc: {100*correct/total}') # Acc may be messed up because train split may be diff this time around","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_preds.shape","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"preds = torch.cat((train_preds, val_preds), dim = 0)\nlabels = torch.cat((train_labels, val_labels), dim = 0)\nprint(preds.shape, labels.shape)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import seaborn as sn\nfrom sklearn.metrics import confusion_matrix\n# def plot_confusion_matrix(cm,channels, title='Confusion matrix', cmap=plt.cm.Blues,filename=\"confusion_matrix.png\"):\n#     #plt.figure()\n#     plt.imshow(cm, interpolation='nearest', cmap=cmap)\n#     plt.title(title)\n#     plt.colorbar()\n#     tick_marks = np.arange(len(channels))\n#     plt.xticks(tick_marks, channels, rotation=45,ha='right')\n#     plt.yticks(tick_marks, channels)\n#     plt.tight_layout()\n#     plt.ylabel('True label')\n#     plt.xlabel('Predicted label')\n#     plt.savefig(\"data/\"+filename)\n#     plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels.cpu()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cm = confusion_matrix(labels.cpu(), preds.cpu())\nprint(type(cm))\ncm\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cm/sum(cm)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"classes = np.array([\"Negative\", \"Positive\"])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_cm = pd.DataFrame(cm/cm.sum(), classes, classes)\n# plt.figure(figsize=(10,7))\nsn.set(font_scale=1.4) # for label size\nsn.heatmap(df_cm, annot=True, fmt='.2%', annot_kws={\"size\": 16}, cmap='Blues') # font size\nplt.xlabel(\"Predicted\")\nplt.ylabel(\"Actual\")\n\nplt.show()\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}