a b/Notebooks/old/annotated_tutorial.ipynb
1
{
2
  "nbformat": 4,
3
  "nbformat_minor": 0,
4
  "metadata": {
5
    "colab": {
6
      "name": "annotated_tutorial.ipynb",
7
      "provenance": []
8
    },
9
    "kernelspec": {
10
      "name": "python3",
11
      "display_name": "Python 3"
12
    }
13
  },
14
  "cells": [
15
    {
16
      "cell_type": "markdown",
17
      "metadata": {
18
        "id": "mLjN-UNzR7Sq"
19
      },
20
      "source": [
21
        "Setup: These commands need to be run before using our program."
22
      ]
23
    },
24
    {
25
      "cell_type": "code",
26
      "metadata": {
27
        "id": "fienv8YmR33s"
28
      },
29
      "source": [
30
        "!pip install pytorch_lightning\r\n",
31
        "!pip install torchsummaryX\r\n",
32
        "!pip install webdataset\r\n",
33
        "!git clone --branch master https://github.com/McMasterAI/Radiology-and-AI.git  \r\n",
34
        "!git clone https://github.com/black0017/MedicalZooPytorch.git"
35
      ],
36
      "execution_count": null,
37
      "outputs": []
38
    },
39
    {
40
      "cell_type": "markdown",
41
      "metadata": {
42
        "id": "muAuDLkxSKV9"
43
      },
44
      "source": [
45
        "We can get set-up with Google Colab if were using it."
46
      ]
47
    },
48
    {
49
      "cell_type": "code",
50
      "metadata": {
51
        "id": "SZKKEduBSJ6w"
52
      },
53
      "source": [
54
        "from google.colab import drive\r\n",
55
        "drive.mount('/content/drive', force_remount=True)"
56
      ],
57
      "execution_count": null,
58
      "outputs": []
59
    },
60
    {
61
      "cell_type": "code",
62
      "metadata": {
63
        "id": "m-r2Ki5nTBJJ"
64
      },
65
      "source": [
66
        "cd drive/MyDrive/MacAI"
67
      ],
68
      "execution_count": null,
69
      "outputs": []
70
    },
71
    {
72
      "cell_type": "markdown",
73
      "metadata": {
74
        "id": "lmmHx4NYTUF4"
75
      },
76
      "source": [
77
        "Imports"
78
      ]
79
    },
80
    {
81
      "cell_type": "code",
82
      "metadata": {
83
        "id": "tnMPgjbKTC8h"
84
      },
85
      "source": [
86
        "import sys\r\n",
87
        "sys.path.append('./Radiology-and-AI/Radiology_and_AI')\r\n",
88
        "sys.path.append('./MedicalZooPytorch')\r\n",
89
        "import os\r\n",
90
        "import torch\r\n",
91
        "import numpy as np\r\n",
92
        "from torch.utils.data import Dataset, DataLoader, random_split\r\n",
93
        "from pytorch_lightning.loggers import WandbLogger, TensorBoardLogger\r\n",
94
        "import pytorch_lightning as pl\r\n",
95
        "import sys\r\n",
96
        "import nibabel as nb\r\n",
97
        "from skimage import transform\r\n",
98
        "import matplotlib.pyplot as plt\r\n",
99
        "import webdataset as wds\r\n",
100
        "from collators.brats_collator import col_img\r\n",
101
        "from lightning_modules.segmentation import TumourSegmentation\r\n",
102
        "from scipy.interpolate import RegularGridInterpolator\r\n",
103
        "from scipy.ndimage.filters import gaussian_filter\r\n",
104
        "from time import time"
105
      ],
106
      "execution_count": null,
107
      "outputs": []
108
    },
109
    {
110
      "cell_type": "markdown",
111
      "metadata": {
112
        "id": "QwqglKvgTZZR"
113
      },
114
      "source": [
115
        "Loading datasets. \r\n",
116
        "Because neuroimages are really large files, we've decided to use the webdataset library to handle them during training. Essentially, we create a zip file representing our dataset and store them in some file path. However, we can work with any PyTorch dataset object (check PyTorch dataset documentation for details)."
117
      ]
118
    },
119
    {
120
      "cell_type": "code",
121
      "metadata": {
122
        "id": "spU-XeiFT3AL"
123
      },
124
      "source": [
125
        "train_dataset = wds.Dataset(\"macai_datasets/brats/train/brats_train.tar.gz\")\r\n",
126
        "eval_dataset = wds.Dataset(\"macai_datasets/brats/validation/brats_validation.tar.gz\")"
127
      ],
128
      "execution_count": null,
129
      "outputs": []
130
    },
131
    {
132
      "cell_type": "markdown",
133
      "metadata": {
134
        "id": "gMGMCMzwU67B"
135
      },
136
      "source": [
137
        "To modify/load in the dataset, we use a *collator function* which is also imported (called col_img). You should create a lambda function only taking in the DataLoader batch as an argument, and using whatever arguments you want afterwards. This sounds complex, so just check the next examples:\r\n",
138
        "\r\n",
139
        "A few notes:\r\n",
140
        "- Image augmentations randomly change training images, to artificially increase the sample size by a bit. The available augmentations, demonstrated to be most effective in literature, are the power-law transformation and elastic transformation. However, elastic transformation is relatively slow as of now. Set the augmentation probabilities (pl_prob and elastic_prob) to 0 during evaluation, but you can set them between 0 and 1 for training.\r\n",
141
        "- Image normalization is used to make the image intensity distributions more similar. We currently support two types: Nyul normalization and Z-score normalization. To use Z-score normalization, set use_zscore to True. To use Nyul normalization, the *standard_scales* and *percs* have to be trained first (more details later)\r\n",
142
        "\r\n",
143
        "Note: both Nyul normalization and Z-score normalization will normalize based on the non-background (black) pixels of the entire image, including the tumor region."
144
      ]
145
    },
146
    {
147
      "cell_type": "code",
148
      "metadata": {
149
        "id": "q7YRJmylU5ZO"
150
      },
151
      "source": [
152
        "training_collator_function = lambda batch: col_img(batch, to_tensor=True, nyul_params=None, use_zscore=True, pl_prob=0.5, elastic_prob=0)"
153
      ],
154
      "execution_count": null,
155
      "outputs": []
156
    },
157
    {
158
      "cell_type": "code",
159
      "metadata": {
160
        "id": "0Kz7Q1rpXNjy"
161
      },
162
      "source": [
163
        "eval_collator_function = lambda batch: col_img(batch, to_tensor=True, nyul_params=None, use_zscore=True, pl_prob=0, elastic_prob=0)"
164
      ],
165
      "execution_count": null,
166
      "outputs": []
167
    },
168
    {
169
      "cell_type": "markdown",
170
      "metadata": {
171
        "id": "MOm66YuoXeZo"
172
      },
173
      "source": [
174
        "Nyul normalization can be trained using the training dataset. We first create a dataloader that uses a collator function that makes no changes to the image, then feed it to an imported nyul_train_dataloader function. While this currently ignores the segmented region and background (for more accurate use in radiomics), we will create an option to also take into account the segmented region (as we won't have access to a segmentation before performing automated segmentation)."
175
      ]
176
    },
177
    {
178
      "cell_type": "code",
179
      "metadata": {
180
        "id": "Wfh9ia72X7gb"
181
      },
182
      "source": [
183
        "train_dataloader = torch.utils.data.DataLoader(train_dataset, batch_size=5, collate_fn=lambda batch:col_img(batch, to_tensor=False))\r\n",
184
        "standard_scales, percss = nyul_train_dataloader(train_dataloader, step = 5)"
185
      ],
186
      "execution_count": null,
187
      "outputs": []
188
    },
189
    {
190
      "cell_type": "markdown",
191
      "metadata": {
192
        "id": "4a_iN2jXaNN9"
193
      },
194
      "source": [
195
        "After training, we can apply Nyul normalization to our dataset."
196
      ]
197
    },
198
    {
199
      "cell_type": "code",
200
      "metadata": {
201
        "id": "5VnWBly7aDx9"
202
      },
203
      "source": [
204
        "nyul_collator_function = lambda batch: col_img(batch, to_tensor=True, nyul_params=nyul_params={'percs': percss, 'standard_scales':standard_scales})"
205
      ],
206
      "execution_count": null,
207
      "outputs": []
208
    },
209
    {
210
      "cell_type": "markdown",
211
      "metadata": {
212
        "id": "AtYAju4MUCDM"
213
      },
214
      "source": [
215
        "We have a lot going on in this line.\r\n",
216
        "Our model is a PyTorch Lightning model called TumourSegmentation, which we import above. This instantiates a new instance of the model, and i used during training.\r\n",
217
        "- The learning rate controls how quickly the model learns. Too high, and the model won't converge; too low, and it will take too long to train.\r\n",
218
        "- The collator is described previously.\r\n",
219
        "- The train_dataset is what we train the model using, and the eval_dataset is to ensure that our model is truly learning (rather than memorizing the train_dataset).\r\n",
220
        "- batch_size has to be set to the number of images in each series (including the segmentation image). In this case, we have 4 (T1, T2, T1ce, T1 FLAIR) plus a segmentation, to make a total of 5.\r\n"
221
      ]
222
    },
223
    {
224
      "cell_type": "code",
225
      "metadata": {
226
        "id": "Hb72wEgyT_3D"
227
      },
228
      "source": [
229
        "model = TumourSegmentation(learning_rate = 4e-4, collator=collator_function, batch_size=5, train_dataset=train_dataset, eval_dataset=eval_dataset)"
230
      ],
231
      "execution_count": null,
232
      "outputs": []
233
    },
234
    {
235
      "cell_type": "markdown",
236
      "metadata": {
237
        "id": "sZ3udcmzZezT"
238
      },
239
      "source": [
240
        "This code deals with training. We can check tensorboard to see how well it's been running after training; you can also use any other type of logger. I use tensorboard here, but there exists another (WandB) that handles automatic updating on Colab."
241
      ]
242
    },
243
    {
244
      "cell_type": "code",
245
      "metadata": {
246
        "id": "GA6zv9gGZcGi"
247
      },
248
      "source": [
249
        "%load_ext tensorboard"
250
      ],
251
      "execution_count": null,
252
      "outputs": []
253
    },
254
    {
255
      "cell_type": "code",
256
      "metadata": {
257
        "id": "S1fasCzOZZGq"
258
      },
259
      "source": [
260
        "#Training\r\n",
261
        "#wandb_logger = WandbLogger(project='macai',name='test_run', offline = True)\r\n",
262
        "trainer = pl.Trainer(\r\n",
263
        "    accumulate_grad_batches = 1,\r\n",
264
        "    gpus = 1,\r\n",
265
        "    max_epochs = 10,\r\n",
266
        "    precision=16,\r\n",
267
        "    check_val_every_n_epoch = 1,\r\n",
268
        "    logger = tensorboard_logger,\r\n",
269
        "    log_every_n_steps=10,            \r\n",
270
        ")\r\n",
271
        "trainer.fit(model)"
272
      ],
273
      "execution_count": null,
274
      "outputs": []
275
    },
276
    {
277
      "cell_type": "code",
278
      "metadata": {
279
        "id": "2yY14W3RZdFs"
280
      },
281
      "source": [
282
        "%tensorboard --logdir logs/"
283
      ],
284
      "execution_count": null,
285
      "outputs": []
286
    },
287
    {
288
      "cell_type": "markdown",
289
      "metadata": {
290
        "id": "Y7xR-qZ3ZxGs"
291
      },
292
      "source": [
293
        "The trainer automatically creates checkpoints, but we can interrupt the trainer and save a checkpoint like so whenever we wish."
294
      ]
295
    },
296
    {
297
      "cell_type": "code",
298
      "metadata": {
299
        "id": "JP05cPb_Zyuh"
300
      },
301
      "source": [
302
        "trainer.save_checkpoint(\"last_ckpt.ckpt\")"
303
      ],
304
      "execution_count": null,
305
      "outputs": []
306
    },
307
    {
308
      "cell_type": "markdown",
309
      "metadata": {
310
        "id": "qv1lcol5aVoj"
311
      },
312
      "source": [
313
        "Finally, it is possible to load saved models and to see the outputs. We can either visualize this in a Python notebook, or by saving the segmentation somewhere and visualizing it using a neuroimaging software (I use 3D Slicer, but I think anything will do)."
314
      ]
315
    },
316
    {
317
      "cell_type": "code",
318
      "metadata": {
319
        "id": "xRCEfRG_afOo"
320
      },
321
      "source": [
322
        "# Load the model\r\n",
323
        "model = TumourSegmentation.load_from_checkpoint('last_ckpt.ckpt').cuda().half()\r\n",
324
        "i=0\r\n",
325
        "\r\n",
326
        "for z in model.val_dataloader():\r\n",
327
        "  print('======================================================')\r\n",
328
        "  prediction = model.forward(torch.unsqueeze(z[0], axis=0).cuda().half())\r\n",
329
        "\r\n",
330
        "  # Save predictions to file for further visualization\r\n",
331
        "  prediction_img = nb.Nifti1Image(prediction, np.eye(4))\r\n",
332
        "  nb.save(prediction_img, 'prediction_'+str(i)+'.nii.gz')\r\n",
333
        "\r\n",
334
        "  # Simple visualization of a slice, but we can use Cameron's visualization method\r\n",
335
        "  # for improvements to this process.\r\n",
336
        "\r\n",
337
        "  sl = z[1][0, :, 100]\r\n",
338
        "\r\n",
339
        "  plt.title('Label')\r\n",
340
        "  plt.imshow(sl, vmin = 0, vmax=4)\r\n",
341
        "  plt.show()\r\n",
342
        "\r\n",
343
        "  prediction = prediction[0].cpu().detach().numpy().astype('float32')\r\n",
344
        "\r\n",
345
        "  plt.title('Prediction core')\r\n",
346
        "  plt.imshow(prediction[0, :, 100], vmin = 0, vmax=1)\r\n",
347
        "  plt.show()\r\n",
348
        "\r\n",
349
        "  plt.title('Prediction enhancing')\r\n",
350
        "  plt.imshow(prediction[1, :, 100], vmin = 0, vmax=1)\r\n",
351
        "  plt.show()\r\n",
352
        "\r\n",
353
        "  plt.title('Prediction edema')\r\n",
354
        "  plt.imshow(prediction[2, :, 100], vmin = 0, vmax=1)\r\n",
355
        "  plt.show()\r\n",
356
        "\r\n",
357
        "  i += 1\r\n",
358
        "  if i >= 10:\r\n",
359
        "    break"
360
      ],
361
      "execution_count": null,
362
      "outputs": []
363
    }
364
  ]
365
}