{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.7.12"},"papermill":{"default_parameters":{},"duration":1087.035813,"end_time":"2023-01-22T10:17:16.597092","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2023-01-22T09:59:09.561279","version":"2.3.4"},"colab":{"provenance":[]},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":39272,"databundleVersionId":4629629,"sourceType":"competition"},{"sourceId":4619805,"sourceType":"datasetVersion","datasetId":2688675}],"dockerImageVersionId":30615,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# **CV Task 3**\n\n## **DJS Synapse Learning Period**\n![image.png](data:image/png;base64,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)","metadata":{"papermill":{"duration":0.010044,"end_time":"2023-01-22T09:59:18.486192","exception":false,"start_time":"2023-01-22T09:59:18.476148","status":"completed"},"tags":[],"id":"07810ece"}},{"cell_type":"markdown","source":"## importing and installing stuff","metadata":{"id":"Y3iW0S4HfaSb"}},{"cell_type":"code","source":"!pip install pydicom -q","metadata":{"id":"5nMAz_AQt3N0","outputId":"5a367117-0401-41e1-9838-07b1a238ccec"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport cv2\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pydicom\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nfrom torch.optim import Adam\nfrom torch.utils.data import DataLoader, Dataset, WeightedRandomSampler\nfrom ignite.engine import Events, create_supervised_trainer, create_supervised_evaluator\nfrom ignite.metrics import Accuracy, Loss, RunningAverage\nfrom ignite.contrib.handlers import ProgressBar\nfrom sklearn.model_selection import train_test_split\nfrom torchvision import models, transforms","metadata":{"papermill":{"duration":3.715291,"end_time":"2023-01-22T09:59:22.251424","exception":false,"start_time":"2023-01-22T09:59:18.536133","status":"completed"},"tags":[],"id":"d51b515d"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## theoretical background\n\nUntil now, you have played around with Convolutional Neural Networks. For years, they were the dominant force in Computer Vision. Until a groundbreaking paper introduced transformers. Initially introduced for Natural Language Processing tasks, they were then also employed in Computer Vision as well. This is that original paper:\n\n[Attention is All you Need](https://proceedings.neurips.cc/paper_files/paper/2017/file/3f5ee243547dee91fbd053c1c4a845aa-Paper.pdf)\n\nIf you didn't understand too much from that, worry not. Go through below resources as well:\n\n[Basic overview (from an NLP viewpoint)](https://medium.com/inside-machine-learning/what-is-a-transformer-d07dd1fbec04)\n\n[A bit more in depth (again, from an NLP viewpoint)](https://towardsdatascience.com/transformers-141e32e69591)\n\n\nTo get more background on how exactly vision transformers work:\n\n[For visual learners](https://youtu.be/qU7wO02urYU?si=bj8Xj-DG2qDbwdnH)\n\n[Read through this as well](https://www.v7labs.com/blog/vision-transformer-guide)\n\nTransformers found their initial applications in natural language processing (NLP) tasks. To use this NLP model for computer vision tasks, we have to divide our input image into patches. After flattening the patches, we can treat each flattened patches as single word. We add positional embeddings to the linear projection of flattened patches. An extra token is added at the beginning for classification tasks. In BERT model, this token is called [CLS] token.\n\nSo if our input image size is (512, 512), after dividing the image into patches of size (16, 16), we get 1024 (32 times 32) patches. After flattening the patches and projecting the flattened patches, we have 1024 tokens. After adding positional embeddings and concatenating classification token at the beginning, we have 1025 tokens.\n\nWe then feed our tokens into the transformer encoder. Transformer encoder is made up of self attention and feedforward network. This [video](https://www.youtube.com/watch?v=_UVfwBqcnbM) by AssemblyAI explains the transformer architecture beautifully.\n\nThe number of tokens in the output of the transformer encoder is equal to number of input tokens. We take the first token from the output (corresponds to the classification token) and feed the token in a multilayer perceptron head for classification.","metadata":{"papermill":{"duration":0.005588,"end_time":"2023-01-22T09:59:18.497792","exception":false,"start_time":"2023-01-22T09:59:18.492204","status":"completed"},"tags":[],"id":"7cdc0644"}},{"cell_type":"markdown","source":"## dataset info\nThe dataset was contributed by mammography screening programs in Australia and the U.S. It includes detailed labels, with radiologists’ evaluations and follow-up pathology results for suspected malignancies.\n\nThe dataset is stored in dicom formats. Converting dicom data to png/jpg just by rescaling it will harm the quality of the data. [This notebook](https://www.kaggle.com/code/raddar/convert-dicom-to-np-array-the-correct-way/notebook) is an awesome resource for anyone working with dicom files for X-Ray.\n\nTo know more about how dicom images work, go through [this](https://towardsdatascience.com/understanding-dicom-bce665e62b72). They are specially used for X - Ray images.","metadata":{"papermill":{"duration":0.005403,"end_time":"2023-01-22T09:59:18.50875","exception":false,"start_time":"2023-01-22T09:59:18.503347","status":"completed"},"tags":[],"id":"0aed2f4a"}},{"cell_type":"markdown","source":"## utility functions\nWe write some utility functions beforehand. To know how to work with PyDicom funtions, read [this](https://towardsdatascience.com/introducing-pydicom-its-classes-methods-and-attributes-518c1d71162).","metadata":{"papermill":{"duration":0.005416,"end_time":"2023-01-22T09:59:18.530519","exception":false,"start_time":"2023-01-22T09:59:18.525103","status":"completed"},"tags":[],"id":"d95bbc52"}},{"cell_type":"code","source":"def read_xray(file_path, img_size=None):\n    \"\"\"\n    Read the dicom data and get the image\n    Args:\n        file_path: The path of the dicom file\n        img_size: Size of the output image\n    \"\"\"\n\n    dicom = # read file_path using pydicom\n    img = # assign as pixel_array\n\n    if : # check if the photo is Monochrome\n        img = np.max(img) - img # we are inverting the pixel values. can you guess why?\n\n    if img_size:\n        img = # resize to img_size\n\n    # Add channel dim at First\n    img = img[np.newaxis]\n\n    # Converting img to float32\n    img = # normalize img\n    img = # convert to float32\n\n    return img","metadata":{"papermill":{"duration":0.015247,"end_time":"2023-01-22T09:59:22.272633","exception":false,"start_time":"2023-01-22T09:59:22.257386","status":"completed"},"tags":[],"id":"221fb680"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def patchify(batch, patch_size):\n    \"\"\"\n    Patchify the batch of images\n\n    Shape:\n        batch: (b, h, w, c)\n        output: (b, nh, nw, ph, pw, c)\n    \"\"\"\n    b, c, h, w = # assign shape of batch\n    ph, pw = # which size should be assigned here?\n    nh, nw = # calculate this\n    \n    # Look into torch.reshape to get the patches\n    # then, change the order of dimensions as required for the output using torch.permute\n    batch_patches = \n    batch_patches = \n\n    return batch_patches","metadata":{"papermill":{"duration":0.015657,"end_time":"2023-01-22T09:59:22.293761","exception":false,"start_time":"2023-01-22T09:59:22.278104","status":"completed"},"tags":[],"id":"d80572d3"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We test our `patchify` function on a single image.","metadata":{"papermill":{"duration":0.00537,"end_time":"2023-01-22T09:59:22.30469","exception":false,"start_time":"2023-01-22T09:59:22.29932","status":"completed"},"tags":[],"id":"645afc03"}},{"cell_type":"code","source":"FILE_PATH = ('/kaggle/input/rsna-breast-cancer-detection/'\n             'train_images/10006/1459541791.dcm')\n\nimg = read_xray(FILE_PATH, img_size=(512, 512))\n\nbatch = torch.tensor(img[None])\npatch_size = (16, 16)\nbatch_patches = patchify(batch, patch_size)\n\npatches = batch_patches[0]\nc, nh, nw, ph, pw = patches.shape\n\nplt.figure(figsize=(5, 5))\nplt.imshow(img[0], cmap=\"gray\")\nplt.axis(\"off\")\n\nplt.figure(figsize=(5, 5))\nfor i in range(nh):\n    for j in range(nw):\n        plt.subplot(nh, nw, i * nw + j + 1)\n        plt.imshow(patches[0, i, j], cmap=\"gray\")\n        plt.axis(\"off\")","metadata":{"papermill":{"duration":48.930892,"end_time":"2023-01-22T10:00:11.241361","exception":false,"start_time":"2023-01-22T09:59:22.310469","status":"completed"},"tags":[],"id":"522d21b9","outputId":"92211244-22b1-4e50-fe3d-c0f167d522e8"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_mlp(in_features, hidden_units, out_features):\n    \"\"\"\n    Returns a MLP head\n    \"\"\"\n    dims = [in_features] + hidden_units + [out_features]\n    layers = []\n    for dim1, dim2 in zip(dims[:-2], dims[1:-1]):\n        # Add a Linear ReLU Layer to layers for each iteration\n        \n    # Add a final Linear Layer and return its Sequential Model\n    return nn.Sequential(*layers)","metadata":{"papermill":{"duration":0.017061,"end_time":"2023-01-22T10:00:11.2657","exception":false,"start_time":"2023-01-22T10:00:11.248639","status":"completed"},"tags":[],"id":"5b559866"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## image to sequence block\nThis Block takes a batch of image as input and returns a batch of sequences. Later on we feed this sequences into the transformer encoder.","metadata":{"papermill":{"duration":0.006314,"end_time":"2023-01-22T10:00:11.278755","exception":false,"start_time":"2023-01-22T10:00:11.272441","status":"completed"},"tags":[],"id":"6cb491e3"}},{"cell_type":"code","source":"class Img2Seq(nn.Module):\n    \"\"\"\n    This layers takes a batch of images as input and\n    returns a batch of sequences\n\n    Shape:\n        input: (b, h, w, c)\n        output: (b, s, d)\n    \"\"\"\n    def __init__(self, img_size, patch_size, n_channels, d_model):\n        super().__init__()\n        self.patch_size = patch_size\n        self.img_size = img_size\n\n        nh, nw = img_size[0] // patch_size[0], img_size[1] // patch_size[1]\n        n_tokens = nh * nw\n\n        token_dim = patch_size[0] * patch_size[1] * n_channels\n        self.linear = # Create a Linear layer with appropriate dimensions\n        \n        # we create 2 Learnable Parameters using torch.randn for initialization. \n        # one is the Embedding Parameter of a Special Transformer Token. \n        # the other is the Positional Embeddings.\n        self.cls_token = nn.Parameter(torch.randn(1, 1, d_model))\n        self.pos_emb = nn.Parameter(torch.randn(n_tokens, d_model))\n\n    def __call__(self, batch):\n        batch = patchify(batch, self.patch_size)\n\n        b, c, nh, nw, ph, pw = batch.shape\n\n        # Flattening the patches using reshape and permute. Adjust Dimensions carefully.\n        batch = \n        batch = \n\n        batch = self.linear(batch)\n        cls = self.cls_token.expand([b, -1, -1])\n        emb = batch + self.pos_emb\n\n        return torch.cat([cls, emb], axis=1)","metadata":{"papermill":{"duration":0.018674,"end_time":"2023-01-22T10:00:11.304195","exception":false,"start_time":"2023-01-22T10:00:11.285521","status":"completed"},"tags":[],"id":"58ae05ba"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## visual transformer module\nThis modules wraps up everything. We can divide this module into 3 parts:\n* An image to sequence encoder\n* Transformer encoder\n* Multilayer perceptron head classification\n\nWe use `torch.nn.TransformerEncoder` and `torch.nn.TransformerEncoderLayer` to implement our transformer encoder. We highly recommend to read the official documentation to [learn more about the layers](https://pytorch.org/docs/stable/generated/torch.nn.TransformerEncoder.html).\nFor the loss function, we will be using [gelu](https://arxiv.org/abs/1606.08415). You can also use this video to [learn about it](https://www.youtube.com/watch?v=FWhMkpo9yuM).","metadata":{"papermill":{"duration":0.006736,"end_time":"2023-01-22T10:00:11.317817","exception":false,"start_time":"2023-01-22T10:00:11.311081","status":"completed"},"tags":[],"id":"26d7647d"}},{"cell_type":"code","source":"class ViT(nn.Module):\n    def __init__(\n        self,\n        img_size,\n        patch_size,\n        n_channels,\n        d_model,\n        nhead,\n        dim_feedforward,\n        blocks,\n        mlp_head_units,\n        n_classes,\n    ):\n        super().__init__()\n        \"\"\"\n        Args:\n            img_size: Size of the image\n            patch_size: Size of the patch\n            n_channels: Number of image channels\n            d_model: The number of features in the transformer encoder\n            nhead: The number of heads in the multiheadattention models\n            dim_feedforward: The dimension of the feedforward network model in the encoder\n            blocks: The number of sub-encoder-layers in the encoder\n            mlp_head_units: The hidden units of mlp_head\n            n_classes: The number of output classes\n        \"\"\"\n        self.img2seq = Img2Seq(img_size, patch_size, n_channels, d_model)\n        \n        # create an encoder layer for the head of the model using Gelu activation. \n        # make sure you set the parameter that gives output as (batch_size, sequence, feature)\n        encoder_layer = \n        \n        # create an encoder block\n        self.transformer_encoder = \n        self.mlp = get_mlp(d_model, mlp_head_units, n_classes)\n        \n        # create the output activation function based on the number of classes (sigmoid or softmax)\n        self.output = nn.Sigmoid() if n_classes == 1 else nn.Softmax()\n\n    def __call__(self, batch):\n\n        batch = self.img2seq(batch)\n        batch = self.transformer_encoder(batch)\n        batch = batch[:, 0, :]\n        batch = self.mlp(batch)\n        output = self.output(batch)\n        return output","metadata":{"papermill":{"duration":0.018775,"end_time":"2023-01-22T10:00:11.34348","exception":false,"start_time":"2023-01-22T10:00:11.324705","status":"completed"},"tags":[],"id":"00b544d2"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## training\n\nHere, we design a simple training to loop to train or `ViT` model on a subset of dataset. We use an already cropped dataset.","metadata":{"papermill":{"duration":0.006607,"end_time":"2023-01-22T10:00:11.356973","exception":false,"start_time":"2023-01-22T10:00:11.350366","status":"completed"},"tags":[],"id":"e396a0ac"}},{"cell_type":"code","source":"device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')","metadata":{"papermill":{"duration":0.137093,"end_time":"2023-01-22T10:00:11.514575","exception":false,"start_time":"2023-01-22T10:00:11.377482","status":"completed"},"tags":[],"id":"6a5ac8c3"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## dataset loading\nWe will be using the [CLAHE](https://www.geeksforgeeks.org/clahe-histogram-eqalization-opencv/) algorithm to improve contrast between the tiled images. Apart from this, we are using the pytorch data handling modules like DataLoaded which you can read about [here](https://pytorch.org/docs/stable/data.html).","metadata":{"papermill":{"duration":0.007039,"end_time":"2023-01-22T10:00:11.528747","exception":false,"start_time":"2023-01-22T10:00:11.521708","status":"completed"},"tags":[],"id":"4587c40c"}},{"cell_type":"code","source":"class RSNADataset(Dataset):\n\n    def __init__(self, df, img_path):\n        self.df = df\n        self.img_path = img_path\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        patient_id, image_id, cancer = self.df.iloc[idx][['patient_id', 'image_id', 'cancer']]\n        file = os.path.join(self.img_path, f'{patient_id}_{image_id}.png')\n        file = # read file\n        clahe = # use cv2.createCLAHE\n        file = # apply clahe to file\n        file = # normalize file\n        X = torch.tensor(file[np.newaxis].astype('float32')).to(device)\n        y = torch.tensor([cancer]).float().to(device)\n        return X, y","metadata":{"papermill":{"duration":0.018483,"end_time":"2023-01-22T10:00:11.554179","exception":false,"start_time":"2023-01-22T10:00:11.535696","status":"completed"},"tags":[],"id":"8349a5ae"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.read_csv('/kaggle/input/rsna-breast-cancer-detection/train.csv')\ncounts = # apply value_counts on appropriate column\ndf['weights'] = df['insert_appropriate_column'].apply(lambda x: 1/counts[x]) # why this?\n\ntrain_df, val_df = train_test_split(df, test_size=0.25, stratify=df['insert_appropriate_column'])","metadata":{"papermill":{"duration":0.432908,"end_time":"2023-01-22T10:00:11.993993","exception":false,"start_time":"2023-01-22T10:00:11.561085","status":"completed"},"tags":[],"id":"c8cdd3ff"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img_path = '/kaggle/input/rsna-breast-cancer-512-pngs'\ntrain_samples = 1000\nval_samples = 500\n\ntrain_ds = RSNADataset(train_df, img_path)\nval_ds = # call as above\n\ntrain_sampler = WeightedRandomSampler(train_df['weights'].values, train_samples)\ntrain_loader = DataLoader(train_ds, batch_size=8, sampler=train_sampler)\n\nval_sampler = # sample as above\nval_loader = # load as above","metadata":{"papermill":{"duration":0.018775,"end_time":"2023-01-22T10:00:12.019912","exception":false,"start_time":"2023-01-22T10:00:12.001137","status":"completed"},"tags":[],"id":"b2c20004"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## model creation and training start","metadata":{"papermill":{"duration":0.007287,"end_time":"2023-01-22T10:00:12.034465","exception":false,"start_time":"2023-01-22T10:00:12.027178","status":"completed"},"tags":[],"id":"87621c6e"}},{"cell_type":"code","source":"model = ViT(\n    img_size = (512, 512),\n    patch_size = (16, 16),\n    n_channels = 1,\n    d_model = 1024,\n    nhead = 4,\n    dim_feedforward = 1024,\n    blocks = 8,\n    mlp_head_units = [512, 512],\n    n_classes = 1,\n).to(device)\n\noptimizer = # run Adam optimizer on model\ncriterion = # use BCE Loss\n\ntrainer = create_supervised_trainer(model, optimizer, criterion, device=device)\nval_metrics = {\n    \"bce\": Loss(criterion)\n}\nevaluator = # create evaluator trainer","metadata":{"papermill":{"duration":3.667453,"end_time":"2023-01-22T10:00:15.709219","exception":false,"start_time":"2023-01-22T10:00:12.041766","status":"completed"},"tags":[],"id":"44c7f2df"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"log_interval = 10\nmax_epochs = 5\nbest_loss = float('inf')\n\nRunningAverage(output_transform=lambda x: x).attach(trainer, 'loss')\n\npbar = ProgressBar()\npbar.attach(trainer, ['loss'])\n\n@trainer.on(Events.EPOCH_COMPLETED)\ndef log_validation_results(trainer):\n    global best_loss\n    evaluator.run(val_loader)\n    loss = evaluator.state.metrics['bce']\n    if loss < best_loss:\n        # in this line, save best loss in best_loss\n        # in this line, use torch.save() to save model as 'best_model_vit.pt'\n    print(f\"Validation Results - Epoch: {trainer.state.epoch} Avg loss: {loss:.2f}\")\n\noutput_state = trainer.run(train_loader, max_epochs=max_epochs)","metadata":{"papermill":{"duration":848.375644,"end_time":"2023-01-22T10:14:24.091936","exception":false,"start_time":"2023-01-22T10:00:15.716292","status":"completed"},"tags":[],"id":"081e32ba"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **End of Task**\n\n> ©Synapse 2023 - 2024\n","metadata":{}}]}