{"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":"from torchvision import datasets\nimport numpy as np\nimport pandas as pd\nfrom torch import optim\nimport torch\nCUDA_LAUNCH_BLOCKING=1\nfrom torchvision import models\nimport torch.nn as nn\nimport os\nimport tqdm\nfrom skimage import io, transform\nimport matplotlib.pyplot as plt\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, utils\nimport skimage\nfrom skimage.color import rgb2gray, gray2rgb\nimport cv2\nimport gc\nfrom sklearn.metrics import mean_squared_error","metadata":{"id":"9X_3vkc0twjm","execution":{"iopub.status.busy":"2023-02-02T00:45:05.653456Z","iopub.execute_input":"2023-02-02T00:45:05.653812Z","iopub.status.idle":"2023-02-02T00:45:05.660962Z","shell.execute_reply.started":"2023-02-02T00:45:05.653783Z","shell.execute_reply":"2023-02-02T00:45:05.659861Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Preproc functions : Remove white region | Crop | Normalize","metadata":{"id":"2BIVr1SI2fTT"}},{"cell_type":"code","source":"def crop_desired_region(img_):\n    '''\n    Cropping desired region. \n    RSNA training images contain blank spaces. \n    To remove the region, apply non-zero pixels and cut.\n    And then resize to the original size.\n    '''\n    coords = cv2.findNonZero(img_) # Find all non-zero points (text)\n    x, y, w, h = cv2.boundingRect(coords) # Find minimum spanning bounding box\n    rect = img_[y:y+h, x:x+w] # Crop the image - note we do this on the original image\n    rect_originalSized = cv2.resize(rect,(img_.shape))\n    return rect_originalSized\n\ndef center_crop(img, set_size):\n    h, w, c = img.shape\n    if set_size > min(h, w):\n        return img\n    crop_width = set_size\n    crop_height = set_size\n    mid_x, mid_y = w//2, h//2\n    offset_x, offset_y = crop_width//2, crop_height//2\n    crop_img = img[mid_y - offset_y:mid_y + offset_y, mid_x - offset_x:mid_x + offset_x]\n    return crop_img\n\ndef normalize(img: np.ndarray) -> np.ndarray:\n    '''\n    Normalize processed image. MinMax.\n    '''\n    return (img - img.min()) / (img.max() - img.min())\n\ndef preproc_tabular(df,root_dir):\n    df['filename'] = root_dir+'/'+df.patient_id.astype(str)+'/'+df.image_id.astype(str)+'.png'\n    train_selected = df.drop(['patient_id','image_id','cancer','filename','laterality','view'],axis=1)\n    train_selected.BIRADS.fillna('7',inplace = True)\n    train_selected.BIRADS = train_selected.BIRADS.astype('category')\n    train_selected.density.fillna('K',inplace = True)\n    train_selected.density = train_selected.density.astype('category')\n    train_input = pd.get_dummies(train_selected)\n    return(train_input)\n\ndef preproc_tabular_test(df,root_dir):\n    df['filename'] = root_dir+'/'+df.patient_id.astype(str)+'/'+df.image_id.astype(str)+'.png'\n    train_selected = df.drop(['patient_id','image_id','filename','laterality','view'],axis=1)\n    train_input = pd.get_dummies(train_selected)\n    return(train_input)\n\n\ndef preproc_image(img_name):\n    '''\n    Foward process of forementioned pre-processes.\n    Includes : \n    1. Read \n    2. Remove blank region, resize \n    3. Convert Grayscale to RGB for ResNet18 application ( Just to fit dimension )\n    4. Center crop \n    5. Normalize processed image\n    6. Convert to Tensor\n    7. Permute tensor dimension to make as an input for pretrained model.\n    '''\n    image = io.imread(img_name)\n    image = crop_desired_region(image)\n    image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)  \n#     image = center_crop(image,100)\n    image = normalize(image)\n    image = torch.from_numpy(image)\n    image = image.permute(2,0,1)\n    return(image)","metadata":{"execution":{"iopub.status.busy":"2023-02-02T00:45:07.316237Z","iopub.execute_input":"2023-02-02T00:45:07.316906Z","iopub.status.idle":"2023-02-02T00:45:07.329876Z","shell.execute_reply.started":"2023-02-02T00:45:07.31687Z","shell.execute_reply":"2023-02-02T00:45:07.328923Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class RSNADataset(Dataset):\n\n    def __init__(self, csv_file, root_dir, transform=None):\n        self.tabular = pd.read_csv(csv_file)\n        self.root_dir = root_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.tabular)\n\n    def __getitem__(self, idx):\n        if torch.is_tensor(idx):\n            idx = idx.tolist()\n        tabular_tmp = self.tabular\n        train_input = preproc_tabular(tabular_tmp,self.root_dir)\n        img_name = tabular_tmp.filename.tolist()[idx]\n        image = preproc_image(img_name)\n        target = torch.Tensor(tabular_tmp['cancer'].tolist())[idx]\n        sample = {'image': image,'answer':target}\n        \n        if self.transform:\n            sample = self.transform(sample)\n        \n        return sample","metadata":{"execution":{"iopub.status.busy":"2023-02-02T00:45:07.345548Z","iopub.execute_input":"2023-02-02T00:45:07.345832Z","iopub.status.idle":"2023-02-02T00:45:07.354178Z","shell.execute_reply.started":"2023-02-02T00:45:07.345805Z","shell.execute_reply":"2023-02-02T00:45:07.353132Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class RSNADataset_Test(Dataset):\n\n    def __init__(self, csv_file, root_dir, transform=None):\n        self.tabular = pd.read_csv(csv_file)\n        self.root_dir = root_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.tabular)\n\n    def __getitem__(self, idx):\n        if torch.is_tensor(idx):\n            idx = idx.tolist()\n        tabular_tmp = self.tabular\n        train_input = preproc_tabular_test(tabular_tmp,self.root_dir)\n        img_name = tabular_tmp.filename.tolist()[idx]\n        image = preproc_image(img_name)\n        sample = {'image': image}\n        \n        if self.transform:\n            sample = self.transform(sample)\n        \n        return sample","metadata":{"execution":{"iopub.status.busy":"2023-02-02T00:45:07.376711Z","iopub.execute_input":"2023-02-02T00:45:07.376977Z","iopub.status.idle":"2023-02-02T00:45:07.383624Z","shell.execute_reply.started":"2023-02-02T00:45:07.376953Z","shell.execute_reply":"2023-02-02T00:45:07.382756Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def evaluation(dataloader):\n    predictions = torch.tensor([], dtype=torch.float).cuda() # Tensor for prediction value appending\n    actual = torch.tensor([], dtype=torch.float).cuda() # Tensor for answer value appending\n    with torch.no_grad():\n        model.eval()\n        for data in dataloader:\n            inputs, values = data['image'].float().cuda(),data['answer'].cuda()\n            outputs = model(inputs).cuda()\n            predictions = torch.cat((predictions, torch.stack([torch.argmax(o) for o in outputs])),0)\n            actual = torch.cat((actual, values), 0)\n    \n    predictions = predictions.cpu().numpy()\n    actual = actual.cpu().numpy()\n    rmse = np.sqrt(mean_squared_error(predictions, actual))\n    return rmse","metadata":{"execution":{"iopub.status.busy":"2023-02-02T00:45:07.408425Z","iopub.execute_input":"2023-02-02T00:45:07.408687Z","iopub.status.idle":"2023-02-02T00:45:07.415611Z","shell.execute_reply.started":"2023-02-02T00:45:07.408663Z","shell.execute_reply":"2023-02-02T00:45:07.414514Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Check for pre-processing steps","metadata":{}},{"cell_type":"code","source":"def show_preproc_image(img_name):\n    '''\n    Foward process of forementioned pre-processes.\n    Includes : \n    1. Read \n    2. Remove blank region, resize \n    3. Convert Grayscale to RGB for ResNet18 application\n    4. Center crop \n    5. Normalize processed image\n    6. Convert to Tensor\n    7. Permute tensor dimension to make as an input for pretrained model.\n    '''\n    \n    plt.figure(figsize = (15,8))\n    \n    plt.subplot(2,3,1)\n    image = io.imread(img_name)\n    plt.imshow(image)\n    plt.title('Original Image')\n    \n    plt.subplot(2,3,2)\n    image = crop_desired_region(image)\n    plt.imshow(image)\n    plt.title('Original Image + Cropped')\n\n    plt.subplot(2,3,3)\n    image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)  \n    plt.imshow(image)\n    plt.title('Original Image + Cropped + RGB')\n    \n    plt.subplot(2,3,4)\n    image = center_crop(image,100)\n    plt.imshow(image)\n    plt.title('Original Image + Cropped + RGB + center Crop')\n\n    plt.subplot(2,3,5)\n    image = normalize(image)\n    plt.imshow(image)\n    plt.title('Original Image + Cropped + RGB + center Crop + Normalize')\n\n    image = torch.from_numpy(image)\n    image = image.permute(2,0,1)\n    \n    plt.tight_layout()\n    return(0)","metadata":{"execution":{"iopub.status.busy":"2023-02-02T00:45:07.483424Z","iopub.execute_input":"2023-02-02T00:45:07.484101Z","iopub.status.idle":"2023-02-02T00:45:07.49304Z","shell.execute_reply.started":"2023-02-02T00:45:07.484065Z","shell.execute_reply":"2023-02-02T00:45:07.49203Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_file = '/kaggle/input/rsna-mammography-images-as-pngs/images_as_pngs_1024/train_images_processed_1024/10006/1874946579.png'\nshow_preproc_image(test_file)","metadata":{"execution":{"iopub.status.busy":"2023-02-02T00:45:07.513002Z","iopub.execute_input":"2023-02-02T00:45:07.513627Z","iopub.status.idle":"2023-02-02T00:45:08.662467Z","shell.execute_reply.started":"2023-02-02T00:45:07.513587Z","shell.execute_reply":"2023-02-02T00:45:08.661426Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"- This seems center crop diminishes characteristics of our image... ( Removed from real preproc function )","metadata":{}},{"cell_type":"code","source":"lr = 1e-06\nnum_epochs = 1\nbatch_size = 4\n\ncsv ='/kaggle/input/rsna-breast-cancer-detection/train.csv'\n# rootpath = '/kaggle/input/rsna-mammography-images-as-pngs/images_as_pngs_1024/train_images_processed_1024/'\nrootpath = '/kaggle/input/rsna-mammography-images-as-pngs/images_as_pngs_cv2_256/train_images_processed_cv2_256/'\nrsna_dataset = RSNADataset(csv_file=csv,root_dir=rootpath)\n# Train-Test split.\ntrain_rsna_set, val_rsna_set = torch.utils.data.random_split(rsna_dataset,[44706,10000])\n\ntrain_rsna_dataloader = DataLoader(train_rsna_set, batch_size=batch_size, shuffle=True)\nval_rsna_dataloader = DataLoader(val_rsna_set, batch_size=batch_size, shuffle=True)\n","metadata":{"execution":{"iopub.status.busy":"2023-02-02T00:45:08.664586Z","iopub.execute_input":"2023-02-02T00:45:08.66531Z","iopub.status.idle":"2023-02-02T00:45:08.733293Z","shell.execute_reply.started":"2023-02-02T00:45:08.665272Z","shell.execute_reply":"2023-02-02T00:45:08.732318Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model & device & functions & parameters","metadata":{}},{"cell_type":"code","source":"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nmodel = models.resnet50(pretrained=True) # Commented because this competition allows no internet usage\n# model = torch.load('/kaggle/input/pytorch-pretrained/resnet50-0676ba61.pth')\n# model.fc = nn.Sequential(nn.Linear(512,2),nn.Softmax()) # ResNet18\nmodel.fc = nn.Sequential(nn.Linear(2048,2),nn.Softmax()) # ResNet50\n\nmodel = model.to(device)\noptimizer = optim.Adam(model.parameters(), lr=lr)\nloss_function = nn.BCEWithLogitsLoss().to(device)\nnum_epoch = 1\nparams = {\n    'num_epochs':num_epochs,\n    'optimizer':optimizer,\n    'loss_function':loss_function,\n    'train_dataloader':train_rsna_dataloader,\n    'val_dataloader': val_rsna_dataloader,\n    'device':device,\n    'num_epoch' : num_epoch\n}","metadata":{"execution":{"iopub.status.busy":"2023-02-02T00:50:48.316559Z","iopub.execute_input":"2023-02-02T00:50:48.31725Z","iopub.status.idle":"2023-02-02T00:50:48.911206Z","shell.execute_reply.started":"2023-02-02T00:50:48.317214Z","shell.execute_reply":"2023-02-02T00:50:48.910291Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train with KFold","metadata":{}},{"cell_type":"markdown","source":"### Load KFold Function by sklearn","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import KFold\nkfold = KFold(n_splits=3, shuffle=True)","metadata":{"execution":{"iopub.status.busy":"2023-02-02T00:50:49.226202Z","iopub.execute_input":"2023-02-02T00:50:49.226889Z","iopub.status.idle":"2023-02-02T00:50:49.231414Z","shell.execute_reply.started":"2023-02-02T00:50:49.226854Z","shell.execute_reply":"2023-02-02T00:50:49.23039Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\n# Clear memory in case for GPU memory error\ntorch.cuda.empty_cache()\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2023-02-02T00:50:49.587298Z","iopub.execute_input":"2023-02-02T00:50:49.588254Z","iopub.status.idle":"2023-02-02T00:50:49.718824Z","shell.execute_reply.started":"2023-02-02T00:50:49.588197Z","shell.execute_reply":"2023-02-02T00:50:49.717789Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Train with KFold - Visulaize train process with tqdm.","metadata":{}},{"cell_type":"code","source":"batch_size = 128\nsoft = nn.Softmax()\nmodel.train()\nvalidation_loss = []\nfor fold, (train_idx, val_idx) in enumerate(kfold.split(train_rsna_set)): # Kfold\n    \n    train_subsampler = torch.utils.data.SubsetRandomSampler(train_idx) # Create Index\n    val_subsampler = torch.utils.data.SubsetRandomSampler(val_idx) # Create Index\n    \n    # Define K-fold dataloader using subsampler\n    trainloader = torch.utils.data.DataLoader(train_rsna_set, batch_size=batch_size, sampler=train_subsampler) # 해당하는 index 추출\n    valloader = torch.utils.data.DataLoader(train_rsna_set, batch_size=batch_size, sampler=val_subsampler)\n    \n    loss_function=params[\"loss_function\"]\n    train_dataloader=params[\"train_dataloader\"]\n    val_dataloader=params[\"val_dataloader\"]\n    device=params[\"device\"]\n    num_epochs = params['num_epochs']\n    \n    for epoch in range(0, num_epochs):\n        for i_batch,dat in enumerate(tqdm.tqdm(trainloader)):\n            inputs = dat['image'].float().cuda()\n            labels = dat['answer'].cuda()\n            \n            optimizer.zero_grad() \n            outputs = model(inputs)\n            outputs = torch.stack([torch.argmax(o) for o in outputs])\n            \n            train_loss = loss_function(outputs.float(),labels.float())\n            train_loss = train_loss.requires_grad_(True)\n            train_loss.backward()\n            optimizer.step()\n\n    train_rmse = evaluation(trainloader) \n    val_rmse = evaluation(valloader)\n    print(\"k-fold\", fold,\" Train Loss: %.4f, Validation Loss: %.4f\" %(train_rmse, val_rmse)) \n    validation_loss.append(val_rmse)\n\nvalidation_loss = np.array(validation_loss)\nmean = np.mean(validation_loss)\nstd = np.std(validation_loss)\nprint(\"Validation Score: %.4f, ± %.4f\" %(mean, std))","metadata":{"execution":{"iopub.status.busy":"2023-02-02T00:50:50.596781Z","iopub.execute_input":"2023-02-02T00:50:50.598119Z","iopub.status.idle":"2023-02-02T07:30:34.023748Z","shell.execute_reply.started":"2023-02-02T00:50:50.59807Z","shell.execute_reply":"2023-02-02T07:30:34.022714Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Inference","metadata":{}},{"cell_type":"markdown","source":"### Load Test dataset","metadata":{}},{"cell_type":"markdown","source":"- Test DICOM to PNG dataset from https://www.kaggle.com/datasets/suhancho/rsna-test-dicomtopng-files","metadata":{}},{"cell_type":"code","source":"csv_test = '/kaggle/input/rsna-breast-cancer-detection/test.csv'\nrootpath_test = '/kaggle/input/rsna-test-dicomtopng-files/Test_DICOM_PNGs/test_images_processed_256/'\nrsna_dataset_test = RSNADataset_Test(csv_file = csv_test,root_dir = rootpath_test)\ntest_rsna_dataloader = DataLoader(rsna_dataset_test, batch_size=batch_size)","metadata":{"execution":{"iopub.status.busy":"2023-02-02T07:33:28.212821Z","iopub.execute_input":"2023-02-02T07:33:28.213186Z","iopub.status.idle":"2023-02-02T07:33:28.224738Z","shell.execute_reply.started":"2023-02-02T07:33:28.213158Z","shell.execute_reply":"2023-02-02T07:33:28.223674Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"output_lst=[]\nfor i_batch,dat in enumerate(tqdm.tqdm(test_rsna_dataloader)):\n    inputs = dat['image'].float().cuda()\n    outputs = model(inputs)\n    outputs = torch.stack([o[1].cpu() for o in outputs])\n    outputs_transform = outputs.detach().numpy()\n    ","metadata":{"execution":{"iopub.status.busy":"2023-02-02T07:35:08.444661Z","iopub.execute_input":"2023-02-02T07:35:08.445225Z","iopub.status.idle":"2023-02-02T07:35:08.533636Z","shell.execute_reply.started":"2023-02-02T07:35:08.445179Z","shell.execute_reply":"2023-02-02T07:35:08.532507Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_base_tab = rsna_dataset_test.tabular\nsubmission_out = pd.DataFrame(outputs_transform)","metadata":{"execution":{"iopub.status.busy":"2023-02-02T07:35:14.107487Z","iopub.execute_input":"2023-02-02T07:35:14.10802Z","iopub.status.idle":"2023-02-02T07:35:14.113212Z","shell.execute_reply.started":"2023-02-02T07:35:14.107968Z","shell.execute_reply":"2023-02-02T07:35:14.112258Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission = pd.concat([submission_base_tab,submission_out],axis=1)[['prediction_id',0]]\nsubmission.columns = ['prediction_id','cancer']\nsubmission.to_csv('submission.csv')","metadata":{"execution":{"iopub.status.busy":"2023-02-02T07:35:14.447185Z","iopub.execute_input":"2023-02-02T07:35:14.447581Z","iopub.status.idle":"2023-02-02T07:35:14.457735Z","shell.execute_reply.started":"2023-02-02T07:35:14.447547Z","shell.execute_reply":"2023-02-02T07:35:14.456454Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission","metadata":{"execution":{"iopub.status.busy":"2023-02-02T07:35:15.168277Z","iopub.execute_input":"2023-02-02T07:35:15.16866Z","iopub.status.idle":"2023-02-02T07:35:15.179038Z","shell.execute_reply.started":"2023-02-02T07:35:15.168627Z","shell.execute_reply":"2023-02-02T07:35:15.178027Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Upvote if you liked my notebook!","metadata":{}},{"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":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Train without KFold","metadata":{}},{"cell_type":"code","source":"# def train(model, params):\n#     soft = nn.Softmax()\n#     loss_function=params[\"loss_function\"]\n#     train_dataloader=params[\"train_dataloader\"]\n#     val_dataloader=params[\"val_dataloader\"]\n#     device=params[\"device\"]\n#     for epoch in range(0, num_epochs):\n#         for dat in rsna_dataloader:\n#             inputs = dat['image'].float().cuda()\n#             labels = dat['answer'].cuda()\n            \n#             optimizer.zero_grad() \n#             outputs = model(inputs)\n#             outputs = torch.stack([torch.argmax(o) for o in outputs])\n            \n#             train_loss = loss_function(outputs.float(),labels.float())\n#             train_loss = train_loss.requires_grad_(True)\n#             train_loss.backward()\n#             optimizer.step()\n# #             print('Epoch: %d/%d, Train loss: %.6f' %(epoch+1, num_epochs, train_loss.item()))\n\n#   # Validation accuracy \n#     total = 0\n#     correct = 0\n#     accuracy = []\n#     for i, data in enumerate(val_dataloader, 0):\n#         inputs, labels = data\n#         inputs = inputs.to(device)\n#         labels = labels.to(device)\n\n#         outputs = model(inputs)\n\n#         _, predicted = torch.max(outputs.data, 1)\n#         total += labels.size(0)\n#         correct += (predicted == labels).sum().item()\n#         test_loss = loss_function(outputs, labels).item()\n#         accuracy.append(100 * correct/total)    \n#         print('Epoch: %d/%d, Train loss: %.6f, Test loss: %.6f, Accuracy: %.2f' %(epoch+1, num_epochs, train_loss.item(), test_loss, 100*correct/total))\n        \n#     import gc\n#     torch.cuda.empty_cache()\n#     gc.collect()\n#     return(0)\n\n\n# train(model, params)","metadata":{"id":"tehbfGEq8d8U","execution":{"iopub.status.busy":"2023-02-02T00:41:09.408858Z","iopub.status.idle":"2023-02-02T00:41:09.409324Z","shell.execute_reply.started":"2023-02-02T00:41:09.409079Z","shell.execute_reply":"2023-02-02T00:41:09.409102Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}