Introduction to MONAI#
MONAI is a deep learning library for medical imaging research. It provides a comprehensive set of tools and utilities for building, training, and evaluating deep learning models in the medical imaging domain.
Key Features#
Data Loading and Preprocessing: MONAI offers a wide range of data loading and preprocessing functionalities, including support for various image formats, normalization, and augmentation techniques.
Transformations: It includes a rich set of transformations for data augmentation, normalization, and other preprocessing steps.
Model Building: MONAI provides a modular approach to building deep learning models, including popular architectures like U-Net, V-Net, and Residual Networks.
Training and Evaluation: It offers tools for training models, including loss functions, optimizers, and evaluation metrics, making it easier to develop and deploy medical image segmentation models.
Integration with PyTorch: MONAI is built on top of PyTorch, allowing for seamless integration with the PyTorch ecosystem and leveraging its powerful ecosystem of tools and libraries.
Installation#
pip install monai
We have already installed MONAI in your DL_labs_GPU environment. We will do the same as we did in PyTorch in the notebook “Segmentation and UNET”. The idea is you will see the difference between the two frameworks and how MONAI may simplify the process of building and training deep learning models for medical imaging tasks.
[1]:
# Some imports
import monai
import torch
from torch.utils.data import DataLoader
from monai.transforms import (
EnsureChannelFirstd,
AsDiscreted,
Compose,
LoadImaged,
Orientationd,
Randomizable,
Resized,
ScaleIntensityd,
Spacingd,
EnsureTyped,
Lambda
)
import monai.metrics as metrics
import os
import tempfile
from utils.decathlon_dataset import get_decathlon_dataloader
from utils.unet import UNET
from utils.train import train
import matplotlib.pyplot as plt
[2]:
root_dir = './utils/datasets'
task = "Task04_Hippocampus"
train_loader = get_decathlon_dataloader(root_dir, task, "training", batch_size=4, num_workers=2, shuffle=True)
val_loader = get_decathlon_dataloader(root_dir, task, "validation", batch_size=4, num_workers=2, shuffle=False)
2024-09-18 15:23:41,224 - INFO - Verified 'Task04_Hippocampus.tar', md5: 9d24dba78a72977dbd1d2e110310f31b.
2024-09-18 15:23:41,225 - INFO - File exists: cm2003/datasets/Task04_Hippocampus.tar, skipped downloading.
2024-09-18 15:23:41,227 - INFO - Non-empty folder exists in cm2003/datasets/Task04_Hippocampus, skipped extracting.
Loading dataset: 100%|██████████| 208/208 [00:04<00:00, 49.23it/s]
2024-09-18 15:23:45,576 - INFO - Verified 'Task04_Hippocampus.tar', md5: 9d24dba78a72977dbd1d2e110310f31b.
2024-09-18 15:23:45,577 - INFO - File exists: cm2003/datasets/Task04_Hippocampus.tar, skipped downloading.
2024-09-18 15:23:45,578 - INFO - Non-empty folder exists in cm2003/datasets/Task04_Hippocampus, skipped extracting.
Loading dataset: 100%|██████████| 52/52 [00:01<00:00, 50.07it/s]
[3]:
# Load UNET model from MONAI
model = monai.networks.nets.UNet(
spatial_dims=2,
in_channels=1,
out_channels=3,
channels=(16, 32, 64, 128, 256),
strides=(2, 2, 2, 2),
num_res_units=2,
)
# Define loss function and optimizer
loss_fn = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
# Define device
device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
Implement the training loop for the model with the same structure as for PyTorch. You may even make use of the train function we wrote in PyTorch in utils/train.py.
[4]:
from utils.train import train
# Your code here
trained_model = None
trained_model = train(model, train_loader, loss_fn, optimizer, device, epochs=20)
Epoch 1/20, Loss: 0.9478
Epoch 2/20, Loss: 0.4815
Epoch 3/20, Loss: 0.2349
Epoch 4/20, Loss: 0.1539
Epoch 5/20, Loss: 0.1230
Epoch 6/20, Loss: 0.1069
Epoch 7/20, Loss: 0.0975
Epoch 8/20, Loss: 0.0881
Epoch 9/20, Loss: 0.0805
Epoch 10/20, Loss: 0.0740
Epoch 11/20, Loss: 0.0672
Epoch 12/20, Loss: 0.0613
Epoch 13/20, Loss: 0.0565
Epoch 14/20, Loss: 0.0531
Epoch 15/20, Loss: 0.0497
Epoch 16/20, Loss: 0.0469
Epoch 17/20, Loss: 0.0438
Epoch 18/20, Loss: 0.0417
Epoch 19/20, Loss: 0.0396
Epoch 20/20, Loss: 0.0373
Now let’s evaluate the model on the validation set with the metrics in MONAI. We give the example with Hausdorff Distance but you should try to find other metrics in the documentation: https://docs.monai.io/en/stable/metrics.html appropriate for the segmentation task. We can interpret the Hausdorff Distance as the maximum difference between the predicted segmentation boundary and the true boundary for 95% of the points. In this task, we are trying to segment the hippocampus, which is a small structure and the unit of the distance is millimeters.
[5]:
import torch
from monai.metrics import HausdorffDistanceMetric
hausdorff_distance = HausdorffDistanceMetric(include_background=False, reduction="mean", percentile=95)
for val_data in val_loader:
val_inputs, val_labels = val_data["image"].to(device), val_data["label"].to(device)
val_outputs = torch.nn.functional.one_hot(trained_model(val_inputs).argmax(dim=1), num_classes=3).permute(0, 3, 1, 2)
val_labels = torch.nn.functional.one_hot(val_labels.long(), num_classes=3).squeeze(1).permute(0, 3, 1, 2)
# Identify valid classes
valid_classes = torch.logical_and(torch.any(val_labels, dim=(0, 2, 3)), torch.any(val_outputs, dim=(0, 2, 3)))
# Only compute Hausdorff distance for valid classes
if torch.any(valid_classes):
hausdorff_distance(val_outputs[:, valid_classes], val_labels[:, valid_classes])
else:
print("No valid classes found for Hausdorff distance calculation.")
result = hausdorff_distance.aggregate().item()
print(f'95th percentile Hausdorff distance: {result:.4f}')
/home/maia-user/.conda/envs/DL_labs_GPU/lib/python3.12/site-packages/monai/metrics/utils.py:329: UserWarning: the ground truth of class 0 is all 0, this may result in nan/inf distance.
warnings.warn(
/home/maia-user/.conda/envs/DL_labs_GPU/lib/python3.12/site-packages/monai/metrics/utils.py:334: UserWarning: the prediction of class 0 is all 0, this may result in nan/inf distance.
warnings.warn(
/home/maia-user/.conda/envs/DL_labs_GPU/lib/python3.12/site-packages/monai/metrics/utils.py:329: UserWarning: the ground truth of class 1 is all 0, this may result in nan/inf distance.
warnings.warn(
/home/maia-user/.conda/envs/DL_labs_GPU/lib/python3.12/site-packages/monai/metrics/utils.py:334: UserWarning: the prediction of class 1 is all 0, this may result in nan/inf distance.
warnings.warn(
95th percentile Hausdorff distance: 2.5270
Evaluation for two other metrics#
Implement evaluation for two other metrics in MONAI and comment on the results.
[ ]:
Now we have trained and evaluated the model, let’s change the loss function to DiceLoss and train the model again which is another loss function commonly used in medical image segmentation. Look at the documentation of MONAI to find out how to do this: https://docs.monai.io/en/stable/losses.html
After you have trained the model, you can evaluate the model on the validation set with the new loss function.
[6]:
# Your code here
Optional part#
MONAI also provides implementations of many networks apart from UNET. You can find them in the monai.networks module, see https://docs.monai.io/en/stable/networks.html. Try to use one of them for the Hippocampus dataset and evaluate the performance. Compare the results with UNET and DiceLoss.
[7]:
# Your code here