{"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 dicomsdl --no-index --find-links=file:///kaggle/input/read-dicom-set/dicom_read","metadata":{"execution":{"iopub.status.busy":"2023-04-02T18:40:44.480847Z","iopub.execute_input":"2023-04-02T18:40:44.481576Z","iopub.status.idle":"2023-04-02T18:40:55.8404Z","shell.execute_reply.started":"2023-04-02T18:40:44.48154Z","shell.execute_reply":"2023-04-02T18:40:55.83922Z"},"jupyter":{"source_hidden":true,"outputs_hidden":true},"collapsed":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nimport os\nimport numpy as np\nimport pandas as pd\nimport dicomsdl\nimport matplotlib.pylab as plt\nfrom sklearn.preprocessing import LabelEncoder\nfrom tqdm import tqdm\nfrom albumentations import (ToFloat, Normalize, VerticalFlip, HorizontalFlip, Compose, Resize,\n                            RandomBrightnessContrast, HueSaturationValue, Blur, GaussNoise,\n                            Rotate, RandomResizedCrop, Cutout, ShiftScaleRotate, ToGray)\nfrom albumentations.pytorch import ToTensorV2\nfrom torch.utils.data import Dataset, DataLoader, Subset\n\n\ntrain = pd.read_csv('/kaggle/input/rsna-breast-cancer-detection/train.csv')\ntest = pd.read_csv('/kaggle/input/rsna-breast-cancer-detection/test.csv')\n\nbase_path = \"/kaggle/input/rsna-breast-cancer-detection/train_images/\"\nall_paths = []\nfor k in tqdm(range(len(train))):\n    row = train.iloc[k, :]\n    all_paths.append(base_path + str(row.patient_id) + \"/\" + str(row.image_id) + \".dcm\")\n    \ntrain[\"path\"] = all_paths\n\nbase_path = \"/kaggle/input/rsna-breast-cancer-detection/test_images/\"\nall_paths = []\nfor k in tqdm(range(len(test))):\n    row = test.iloc[k, :]\n    all_paths.append(base_path + str(row.patient_id) + \"/\" + str(row.image_id) + \".dcm\")\ntest[\"path\"] = all_paths","metadata":{"execution":{"iopub.status.busy":"2023-04-02T18:40:55.84272Z","iopub.execute_input":"2023-04-02T18:40:55.843055Z","iopub.status.idle":"2023-04-02T18:41:10.103983Z","shell.execute_reply.started":"2023-04-02T18:40:55.843002Z","shell.execute_reply":"2023-04-02T18:41:10.102731Z"},"jupyter":{"source_hidden":true,"outputs_hidden":true},"collapsed":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#NEED TO DO PCA or Anything Dim reducding\nclass ImDataset(Dataset):\n    \n    def __init__(self, dataframe, is_train=True):\n        self.dataframe, self.is_train = dataframe, is_train \n        \n        if is_train:\n            #NEED TO DO Bounding Box Cropping\n            self.transform = Compose([RandomResizedCrop(height=224, width=224),\n                                      ShiftScaleRotate(rotate_limit=90, scale_limit = [0.8, 1.2]),\n                                      HorizontalFlip(p = 0.5),\n                                      VerticalFlip(p = 0.5),\n                                      ToTensorV2()])\n        else:\n            self.transform = Compose([ToTensorV2()])\n            \n    def __len__(self):\n        return len(self.dataframe)\n    \n    def __getitem__(self, index):\n        '''Take each row in batch at a time.'''\n\n        # Select path and read image\n        image_path = self.dataframe['path'].values[index]\n        ds = dicomsdl.open(image_path)\n        image = ds.pixelData()\n        \n        csv_cols = ['laterality', 'view', 'age', 'implant']\n        csv_data = np.array(self.dataframe.iloc[index][csv_cols].values, dtype=np.float32)\n        \n        transf_image = self.transform(image=image)['image']\n        transf_image = np.concatenate([transf_image, transf_image, transf_image], axis=0)\n        \n        # Return info\n        if self.is_train:\n            return {\"image\": transf_image, \n                    \"meta\": csv_data, \n                    \"target\": self.dataframe['cancer'].values[index]}\n        else:\n            return {\"image\": transf_image, \n                    \"meta\": csv_data}","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2023-04-02T18:41:10.10571Z","iopub.execute_input":"2023-04-02T18:41:10.108712Z","iopub.status.idle":"2023-04-02T18:41:10.118593Z","shell.execute_reply.started":"2023-04-02T18:41:10.108679Z","shell.execute_reply":"2023-04-02T18:41:10.117492Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# https://www.kaggle.com/code/andradaolteanu/rsna-breast-cancer-eda-pytorch-baseline#3.1-Label-Encoding\n# Keep only train csv cols that are also in test csv\ntrain = train[[\"patient_id\", \"image_id\", \"laterality\", \"view\", \"age\", \"implant\", \"path\", \"cancer\"]]\nle_laterality = LabelEncoder() # 0-1\nle_view = LabelEncoder() # 0-5\n\ntrain['laterality'] = le_laterality.fit_transform(train['laterality'])\ntrain['view'] = le_view.fit_transform(train['view'])\n\ntest['laterality'] = le_laterality.fit_transform(test['laterality'])\ntest['view'] = le_view.fit_transform(test['view'])\nprint(train.info())\nprint(test.info())\n\n# Select only 500 samples for the training set\n# train = train.sample(n=500, random_state=13)\nprint(f\"{'-'*45}\\nNon-cancer: 0\\tCancer: 1\")\ntrain[\"cancer\"].value_counts()","metadata":{"jupyter":{"source_hidden":true,"outputs_hidden":true},"execution":{"iopub.status.busy":"2023-04-02T18:41:10.122337Z","iopub.execute_input":"2023-04-02T18:41:10.123222Z","iopub.status.idle":"2023-04-02T18:41:10.199434Z","shell.execute_reply.started":"2023-04-02T18:41:10.123185Z","shell.execute_reply":"2023-04-02T18:41:10.198166Z"},"collapsed":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint('Device available now:', DEVICE)\n\ndef data_to_device(data, is_train):\n    if is_train:\n        image, metadata, targets = data.values()\n        return image.to(DEVICE), metadata.to(DEVICE), targets.to(DEVICE)\n    else:\n        image, metadata = data.values()\n        return image.to(DEVICE), metadata.to(DEVICE)","metadata":{"jupyter":{"source_hidden":true,"outputs_hidden":true},"execution":{"iopub.status.busy":"2023-04-02T18:41:10.201289Z","iopub.execute_input":"2023-04-02T18:41:10.201695Z","iopub.status.idle":"2023-04-02T18:41:10.334603Z","shell.execute_reply.started":"2023-04-02T18:41:10.201657Z","shell.execute_reply":"2023-04-02T18:41:10.332177Z"},"collapsed":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch.nn as nn\nfrom torchvision.models import resnet50, ResNet50_Weights\nclass NeuralNetwork(nn.Module):\n    def __init__(self, output_size, no_columns):\n        super().__init__()\n        self.no_columns, self.output_size = no_columns, output_size\n        \n        # Define Feature part (IMAGE)\n        # https://pytorch.org/hub/nvidia_deeplearningexamples_resnet50/\n        self.features = resnet50(weights=ResNet50_Weights.DEFAULT) # 1000 neurons out\n        # (metadata)\n        self.csv = nn.Sequential(nn.Linear(self.no_columns, 500),\n                                 nn.BatchNorm1d(500),\n                                 nn.ReLU(),\n                                 nn.Dropout(p=0.2))\n        \n        # Define Classification part\n        self.classification = nn.Linear(1000 + 500, output_size)\n        \n        \n    def forward(self, image, meta, prints=False):\n        if prints: print('Input Image shape:', image.shape, '\\n'+\n                         'Input metadata shape:', meta.shape)\n        \n        # Image CNN\n        image = self.features(image)\n        if prints: print('Features Image shape:', image.shape)\n        \n        # CSV FNN\n        meta = self.csv(meta)\n        if prints: print('Meta Data:', meta.shape)\n            \n        # Concatenate layers from image with layers from csv_data\n        image_meta_data = torch.cat((image, meta), dim=1)\n        if prints: print('Concatenated Data:', image_meta_data.shape)\n        \n        # CLASSIF\n        out = self.classification(image_meta_data)\n        if prints: print('Out shape:', out.shape)\n        \n        return out","metadata":{"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2023-04-02T18:41:10.337405Z","iopub.execute_input":"2023-04-02T18:41:10.338526Z","iopub.status.idle":"2023-04-02T18:41:10.353165Z","shell.execute_reply.started":"2023-04-02T18:41:10.33848Z","shell.execute_reply":"2023-04-02T18:41:10.351564Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train_model(dataloader, model, loss_fn, optimizer):\n    size = len(dataloader.dataset)\n    model.train()\n\n    for k, data in tqdm(enumerate(dataloader)):\n        image, meta, targets = data_to_device(data, True)\n\n        # Compute prediction error\n        pred = model(image, meta, prints=False)\n        loss = loss_fn(pred, targets.unsqueeze(1).float())\n\n        # Backpropagation\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n\n        if k % 100 == 0:\n            loss, current = loss.item(), (k + 1) * len(data)\n            print(f\"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]\")","metadata":{"execution":{"iopub.status.busy":"2023-04-02T18:49:22.079135Z","iopub.execute_input":"2023-04-02T18:49:22.080358Z","iopub.status.idle":"2023-04-02T18:49:22.088312Z","shell.execute_reply.started":"2023-04-02T18:49:22.080312Z","shell.execute_reply":"2023-04-02T18:49:22.086938Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def test_model(dataloader, model, loss_fn):\n    size = len(dataloader.dataset)\n    num_batches = len(dataloader)\n    model.eval()\n    test_loss, correct = 0, 0\n    preds_1 = []\n    preds_0 = []\n    with torch.no_grad():\n        for k, data in tqdm(enumerate(dataloader)):\n            image, meta = data_to_device(data, False)\n            pred = model(image, meta, prints=False)\n            for x in enumerate(pred):\n                for i in meta:\n                    temp = 0\n                    if(i[0].item() == 0.0):\n                        temp += x[1].item()\n                    else:\n                        preds_1.append(x[1].item())\n                    break\n            preds_0.append(temp)\n    return [preds_0, preds_1]","metadata":{"execution":{"iopub.status.busy":"2023-04-02T18:49:22.375304Z","iopub.execute_input":"2023-04-02T18:49:22.375644Z","iopub.status.idle":"2023-04-02T18:49:22.383536Z","shell.execute_reply.started":"2023-04-02T18:49:22.375614Z","shell.execute_reply":"2023-04-02T18:49:22.382148Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"k-fold based on https://github.com/christianversloot/machine-learning-articles/blob/main/how-to-use-k-fold-cross-validation-with-pytorch.md\nand https://androidkt.com/pytorch-k-fold-cross-validation-using-dataloader-and-sklearn/\"\"\"\ndef reset_weights(model, verbose=False):\n    \"\"\" For resetting the weights of any layer that stores weights \n        while doing k-fold cross validation.\n        \n        model should be a torch.nn instance\n    \"\"\"\n    for layer in model.children():\n        if hasattr(layer, 'reset_parameters'):\n            if verbose:\n                print(f'Reset weights of layer: {layer}')\n            layer.reset_parameters()","metadata":{"execution":{"iopub.status.busy":"2023-04-02T18:49:22.820101Z","iopub.execute_input":"2023-04-02T18:49:22.82096Z","iopub.status.idle":"2023-04-02T18:49:22.827317Z","shell.execute_reply.started":"2023-04-02T18:49:22.820916Z","shell.execute_reply":"2023-04-02T18:49:22.82585Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.model_selection import KFold\n# Instantiate Dataset object\ndataset = ImDataset(train, is_train=True)\ntest_dataset = ImDataset(test, is_train=False)\n\nLEARNING_RATE = 1e-2 # usually some order of magnitude between 0.0001 and 1\nBATCH_SIZE = 32 # 32 is good init value\nepochs = 5\ncsv_columns = ['laterality', 'view', 'age', 'implant']\nk_folds = 5\nkfold = KFold(n_splits=k_folds, shuffle=True)\n\n# Training model\nfor fold, (train_idx,test_idx) in enumerate(kfold.split(dataset)):\n        print(f\"{'='*25} Fold: {fold} {'='*25}\")\n        \n        train_subsampler = torch.utils.data.SubsetRandomSampler(train_idx)\n        test_subsampler = torch.utils.data.SubsetRandomSampler(test_idx)\n\n        trainloader = torch.utils.data.DataLoader(\n            dataset, batch_size=BATCH_SIZE, sampler=train_subsampler)\n        testloader = torch.utils.data.DataLoader(\n                              dataset, batch_size=BATCH_SIZE, sampler=test_subsampler)\n        \n        # Instantiate new nn instead of resetting weights\n        network = NeuralNetwork(output_size=1, no_columns=len(csv_columns)).to(DEVICE)\n        network.apply(reset_weights) # TODO: does this reset resNet weights as well?\n        loss_fn = nn.BCEWithLogitsLoss() # TODO: Implement macro soft-f1 loss to perform better on probabilistic f1 score\n        optimizer = torch.optim.SGD(network.parameters(), lr=LEARNING_RATE)\n        \n        # Training\n        for t in range(epochs):\n            print(f\"{'-'*25} Epoch: {t+1} {'-'*25}\")\n            train_model(trainloader, network, loss_fn, optimizer)\n            test_model(testloader, network, loss_fn)","metadata":{"execution":{"iopub.status.busy":"2023-04-02T18:49:25.008669Z","iopub.execute_input":"2023-04-02T18:49:25.009519Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(predictions_0)\nprint(predictions_1)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predictions_0 = [i-min(predictions_0)/(max(predictions_0)-min(predictions_0)+1) for i in predictions_0 ]\npredictions_1 = [i-min(predictions_1)/(max(predictions_1)-min(predictions_1)+1) for i in predictions_1 ]\npredictions_1 = [i-min(predictions_1)/(max(predictions_1)-min(predictions_1)+1) for i in predictions_1 ]\n\nplt.xlabel(\"Epochs\")\nplt.ylabel(\"Predictions\")\n#plt.plot(range(len(predictions_0)), predictions_0, label=\"Predictions 0\")\nplt.plot(range(len(predictions_1)), predictions_1, label = \"Predictions\")\nplt.legend()\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}