Skip to content

Commit 5f97a73

Browse files
Update modules tutorials for 1.0.0rc1 (#919)
* easy-integrate-bundle-v1 Signed-off-by: KumoLiu <yunl@nvidia.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add multigpu implementation Signed-off-by: KumoLiu <yunl@nvidia.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * update modules tutorial Signed-off-by: KumoLiu <yunl@nvidia.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update modules/3d_image_transforms.ipynb Signed-off-by: KumoLiu <yunl@nvidia.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 961a8c7 commit 5f97a73

12 files changed

+60
-69
lines changed

modules/3d_image_transforms.ipynb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"outputs": [],
3838
"source": [
3939
"from monai.transforms import (\n",
40-
" AddChanneld,\n",
40+
" EnsureChannelFirstd,\n",
4141
" LoadImage,\n",
4242
" LoadImaged,\n",
4343
" Orientationd,\n",
@@ -390,7 +390,7 @@
390390
"cell_type": "markdown",
391391
"metadata": {},
392392
"source": [
393-
"## Add the channel dimension\n",
393+
"## Ensure the first dimension is channel\n",
394394
"\n",
395395
"Most of MONAI's image transformations assume that the input data has the shape: \n",
396396
"`[num_channels, spatial_dim_1, spatial_dim_2, ... ,spatial_dim_n]` \n",
@@ -415,8 +415,8 @@
415415
}
416416
],
417417
"source": [
418-
"add_channel = AddChanneld(keys=[\"image\", \"label\"])\n",
419-
"datac_dict = add_channel(data_dict)\n",
418+
"ensure_channel_first = EnsureChannelFirstd(keys=[\"image\", \"label\"])\n",
419+
"datac_dict = ensure_channel_first(data_dict)\n",
420420
"print(f\"image shape: {datac_dict['image'].shape}\")"
421421
]
422422
},
@@ -519,7 +519,7 @@
519519
"\n",
520520
"The input volumes might have different voxel sizes. \n",
521521
"The following transform is created to normalise the volumes to have (1.5, 1.5, 5.) millimetre voxel size. \n",
522-
"The transform is set to read the original voxel size information from `data_dict['image.affine']`, \n",
522+
"The transform is set to read the original voxel size information from `data_dict['image'].affine`, \n",
523523
"which is from the corresponding NIfTI file, loaded earlier by `LoadImaged`."
524524
]
525525
},
@@ -574,7 +574,7 @@
574574
"metadata": {},
575575
"source": [
576576
"To track the spacing changes, the data_dict was updated by `Spacingd`:\n",
577-
"* An `image.original_affine` key is added to the `data_dict`, logs the original affine.\n",
577+
"* An `image.meta['original_affine']` key is added to the `data_dict`, logs the original affine.\n",
578578
"* An `image.affine` key is updated to have the current affine."
579579
]
580580
},
@@ -829,7 +829,7 @@
829829
"name": "python",
830830
"nbconvert_exporter": "python",
831831
"pygments_lexer": "ipython3",
832-
"version": "3.8.0"
832+
"version": "3.8.13"
833833
}
834834
},
835835
"nbformat": 4,

modules/autoencoder_mednist.ipynb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@
9696
"from monai.data import CacheDataset, DataLoader\n",
9797
"from monai.networks.nets import AutoEncoder\n",
9898
"from monai.transforms import (\n",
99-
" AddChannelD,\n",
99+
" EnsureChannelFirstD,\n",
100100
" Compose,\n",
101101
" LoadImageD,\n",
102102
" RandFlipD,\n",
@@ -278,7 +278,7 @@
278278
"train_transforms = Compose(\n",
279279
" [\n",
280280
" LoadImageD(keys=[\"im\"]),\n",
281-
" AddChannelD(keys=[\"im\"]),\n",
281+
" EnsureChannelFirstD(keys=[\"im\"]),\n",
282282
" ScaleIntensityD(keys=[\"im\"]),\n",
283283
" RandRotateD(keys=[\"im\"], range_x=np.pi / 12, prob=0.5, keep_size=True),\n",
284284
" RandFlipD(keys=[\"im\"], spatial_axis=0, prob=0.5),\n",
@@ -291,7 +291,7 @@
291291
"test_transforms = Compose(\n",
292292
" [\n",
293293
" LoadImageD(keys=[\"im\"]),\n",
294-
" AddChannelD(keys=[\"im\"]),\n",
294+
" EnsureChannelFirstD(keys=[\"im\"]),\n",
295295
" ScaleIntensityD(keys=[\"im\"]),\n",
296296
" EnsureTypeD(keys=[\"im\"]),\n",
297297
" NoiseLambda,\n",
@@ -510,7 +510,7 @@
510510
],
511511
"metadata": {
512512
"kernelspec": {
513-
"display_name": "Python 3",
513+
"display_name": "Python 3 (ipykernel)",
514514
"language": "python",
515515
"name": "python3"
516516
},
@@ -524,7 +524,7 @@
524524
"name": "python",
525525
"nbconvert_exporter": "python",
526526
"pygments_lexer": "ipython3",
527-
"version": "3.7.10"
527+
"version": "3.8.13"
528528
}
529529
},
530530
"nbformat": 4,

modules/batch_output_transform.ipynb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
"from monai.networks.nets import UNet\n",
9595
"from monai.transforms import (\n",
9696
" Activationsd,\n",
97-
" AsChannelFirstd,\n",
97+
" EnsureChannelFirstd,\n",
9898
" AsDiscreted,\n",
9999
" Compose,\n",
100100
" KeepLargestConnectedComponentd,\n",
@@ -119,7 +119,7 @@
119119
"First of all, let's take a look at the possible data shape in `engine.state.batch` and `engine.state.output`.\n",
120120
"\n",
121121
"### engine.state.batch\n",
122-
"(1) For a common ignite program, `batch` is usually the iterable output of PyTorch DataLoader, for example: `{\"image\": Tensor, \"label\" Tensor, \"image_meta_dict\": Dict}` where `image` and `label` are batch-first arrays, `image_meta_dict` is a dictionary of meta information for the input images, every item is a batch:\n",
122+
"(1) For a common ignite program, `batch` is usually the iterable output of PyTorch DataLoader, for example: `{\"image\": MetaTensor, \"label\" MetaTensor, \"image_meta_dict\": Dict}` where `image` and `label` are batch-first arrays, `image_meta_dict` is a dictionary of meta information for the input images, every item is a batch:\n",
123123
"```\n",
124124
"image.shape = [2, 4, 64, 64, 64] # here 2 is batch size, 4 is channels\n",
125125
"label.shape = [2, 3, 64, 64, 64]\n",
@@ -129,7 +129,7 @@
129129
"(2) For MONAI engines, it will automatically `decollate` the batch data into a list of `channel-first` data after every iteration. For more details about `decollate`, please refer to: https://github.com/Project-MONAI/tutorials/blob/main/modules/decollate_batch.ipynb.\n",
130130
"\n",
131131
"The `engine.state.batch` example in (1) will be decollated into a list of dictionaries:\n",
132-
"`[{\"image\": Tensor, \"label\" Tensor, \"image_meta_dict\": Dict}, {\"image\": Tensor, \"label\" Tensor, \"image_meta_dict\": Dict}]`.\n",
132+
"`[{\"image\": MetaTensor, \"label\" MetaTensor, \"image_meta_dict\": Dict}, {\"image\": MetaTensor, \"label\" MetaTensor, \"image_meta_dict\": Dict}]`.\n",
133133
"\n",
134134
"each item of the list can be:\n",
135135
"```\n",
@@ -139,7 +139,7 @@
139139
"```\n",
140140
"\n",
141141
"### engine.state.output\n",
142-
"(1) For a common ignite program, `output` is usually the output data of current iteration, for example: `{\"pred\": Tensor, \"label\": Tensor, \"loss\": scalar}` where `pred` and `label` are batch-first arrays, `loss` is a scalar value of current iteration:\n",
142+
"(1) For a common ignite program, `output` is usually the output data of current iteration, for example: `{\"pred\": MetaTensor, \"label\": MetaTensor, \"loss\": scalar}` where `pred` and `label` are batch-first arrays, `loss` is a scalar value of current iteration:\n",
143143
"```\n",
144144
"pred.shape = [2, 3, 64, 64, 64] # here 2 is batch size, 3 is channels\n",
145145
"label.shape = [2, 3, 64, 64, 64]\n",
@@ -148,7 +148,7 @@
148148
"\n",
149149
"(2) For MONAI engines, it will also automatically `decollate` the output data into a list of `channel-first` data after every iteration.\n",
150150
"The `engine.state.output` example in (1) will be decollated into a list of dictionaries:\n",
151-
"`[{\"pred\": Tensor, \"label\": Tensor, \"loss\" 0.4534}, {\"pred\": Tensor, \"label\": Tensor, \"loss\" 0.4534}]`. Please note that it replicated the scalar value of `loss` to every item of the decollated list."
151+
"`[{\"pred\": MetaTensor, \"label\": MetaTensor, \"loss\" 0.4534}, {\"pred\": MetaTensor, \"label\": MetaTensor, \"loss\" 0.4534}]`. Please note that it replicated the scalar value of `loss` to every item of the decollated list."
152152
]
153153
},
154154
{
@@ -159,9 +159,9 @@
159159
"\n",
160160
"Now let's analyze the cases of extracting data from `engine.state.batch` or `engine.state.output`. To simplify the operation, we developed a utility function `monai.handlers.from_engine` to automatically handle all the common cases.\n",
161161
"\n",
162-
"(1) To get the meta data from dictionary format `engine.state.batch`, set arg `batch_transform=lambda x: x[\"image_meta_dict\"]`.\n",
162+
"(1) To get the meta data from dictionary format `engine.state.batch`, set arg `batch_transform=lambda x: x.meta`.\n",
163163
"\n",
164-
"(2) To get the meta data from decollated list of dictionaries `engine.state.batch`, set arg `lambda x: [i[\"image_meta_dict\"] for i in x]` or `from_engine(\"image_meta_dict\")`.\n",
164+
"(2) To get the meta data from decollated list of dictionaries `engine.state.batch`, set arg `lambda x: [i.meta for i in x]` or `from_engine(\"image_meta_dict\")`.\n",
165165
"\n",
166166
"(3) Metrics usually expect a `Tuple(pred, label)` input, if `engine.state.output` is a dictionary, set arg `output_transform=lambda x: (x[\"pred\"], x[\"label\"])`. If decollated list, set arg `lambda x: ([i[\"pred\"] for i in x], [i[\"label\"] for i in x])` or `from_engine([\"pred\", \"label\"])`.\n",
167167
"\n",
@@ -244,7 +244,7 @@
244244
"train_transforms = Compose(\n",
245245
" [\n",
246246
" LoadImaged(keys=[\"image\", \"label\"]),\n",
247-
" AsChannelFirstd(keys=[\"image\", \"label\"], channel_dim=-1),\n",
247+
" EnsureChannelFirstd(keys=[\"image\", \"label\"], channel_dim=-1),\n",
248248
" ScaleIntensityd(keys=\"image\"),\n",
249249
" RandCropByPosNegLabeld(\n",
250250
" keys=[\"image\", \"label\"], label_key=\"label\", spatial_size=[96, 96, 96], pos=1, neg=1, num_samples=4\n",
@@ -255,7 +255,7 @@
255255
"val_transforms = Compose(\n",
256256
" [\n",
257257
" LoadImaged(keys=[\"image\", \"label\"]),\n",
258-
" AsChannelFirstd(keys=[\"image\", \"label\"], channel_dim=-1),\n",
258+
" EnsureChannelFirstd(keys=[\"image\", \"label\"], channel_dim=-1),\n",
259259
" ScaleIntensityd(keys=\"image\"),\n",
260260
" EnsureTyped(keys=[\"image\", \"label\"]),\n",
261261
" ]\n",
@@ -425,7 +425,7 @@
425425
"name": "python",
426426
"nbconvert_exporter": "python",
427427
"pygments_lexer": "ipython3",
428-
"version": "3.8.12"
428+
"version": "3.8.13"
429429
}
430430
},
431431
"nbformat": 4,

modules/benchmark_global_mutual_information.ipynb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
"outputs": [],
7575
"source": [
7676
"!python -c \"import monai\" || pip install -q \"monai-weekly[nibabel]\"\n",
77-
"!python -c \"import ants\" || pip install -q antspyx==0.2.9\n",
77+
"!python -c \"import ants\" || pip install -q antspyx==0.3.1\n",
7878
"!python -c \"import plotly\" || pip install -q plotly==5.3"
7979
]
8080
},
@@ -470,7 +470,7 @@
470470
"name": "python",
471471
"nbconvert_exporter": "python",
472472
"pygments_lexer": "ipython3",
473-
"version": "3.8.0"
473+
"version": "3.8.13"
474474
}
475475
},
476476
"nbformat": 4,

modules/compute_metric.py

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,22 @@
1515
It can even run on multi-nodes.
1616
Main steps to set up the distributed data parallel:
1717
18-
- Execute `torch.distributed.launch` to create processes on every node for every process.
18+
- Execute `torchrun` to create processes on every node for every process.
1919
It receives parameters as below:
2020
`--nproc_per_node=NUM_PROCESSES_PER_NODE`
2121
`--nnodes=NUM_NODES`
22-
`--node_rank=INDEX_CURRENT_NODE`
23-
`--master_addr="localhost"`
24-
`--master_port=1234`
25-
For more details, refer to https://github.com/pytorch/pytorch/blob/master/torch/distributed/launch.py.
22+
For more details, refer to https://github.com/pytorch/pytorch/blob/master/torch/distributed/run.py.
2623
Alternatively, we can also use `torch.multiprocessing.spawn` to start program, but it that case, need to handle
2724
all the above parameters and compute `rank` manually, then set to `init_process_group`, etc.
28-
`torch.distributed.launch` is even more efficient than `torch.multiprocessing.spawn`.
25+
`torchrun` is even more efficient than `torch.multiprocessing.spawn`.
2926
- Use `init_process_group` to initialize every process.
3027
- Partition the saved predictions and labels into ranks for parallel computation.
3128
- Compute `Dice Metric` on every process, reduce the results after synchronization.
3229
3330
Note:
34-
`torch.distributed.launch` will launch `nnodes * nproc_per_node = world_size` processes in total.
31+
`torchrun` will launch `nnodes * nproc_per_node = world_size` processes in total.
3532
Example script to execute this program on a single node with 2 processes:
36-
`python -m torch.distributed.launch --nproc_per_node=2 compute_metric.py`
33+
`torchrun --nproc_per_node=2 compute_metric.py`
3734
3835
Referring to: https://pytorch.org/tutorials/intermediate/ddp_tutorial.html
3936
@@ -65,7 +62,8 @@
6562

6663
def compute(args):
6764
# generate synthetic data for the example
68-
if args.local_rank == 0 and not os.path.exists(args.dir):
65+
local_rank = int(os.environ["LOCAL_RANK"])
66+
if local_rank == 0 and not os.path.exists(args.dir):
6967
# create 16 random pred, label paris for evaluation
7068
print(f"generating synthetic data to {args.dir} (this may take a while)")
7169
os.makedirs(args.dir)
@@ -93,7 +91,7 @@ def compute(args):
9391
even_divisible=False,
9492
)[dist.get_rank()]
9593

96-
device = torch.device(f"cuda:{args.local_rank}")
94+
device = torch.device(f"cuda:{local_rank}")
9795
torch.cuda.set_device(device)
9896
# define transforms for predictions and labels
9997
transforms = Compose(
@@ -116,7 +114,7 @@ def compute(args):
116114
result = metric.aggregate().item()
117115
filenames = string_list_all_gather(strings=filenames)
118116

119-
if args.local_rank == 0:
117+
if local_rank == 0:
120118
print("mean dice: ", result)
121119
# generate metrics reports at: output/mean_dice_raw.csv, output/mean_dice_summary.csv, output/metrics.csv
122120
write_metrics_reports(
@@ -134,16 +132,11 @@ def compute(args):
134132

135133
# usage example(refer to https://github.com/pytorch/pytorch/blob/master/torch/distributed/launch.py):
136134

137-
# python -m torch.distributed.launch --nproc_per_node=NUM_GPUS_PER_NODE
138-
# --nnodes=NUM_NODES --node_rank=INDEX_CURRENT_NODE
139-
# --master_addr="192.168.1.1" --master_port=1234
140-
# compute_metric.py -d DIR_OF_OUTPUT
135+
# torchrun --standalone --nnodes=NUM_NODES --nproc_per_node=NUM_GPUS_PER_NODE compute_metric.py -d DIR_OF_OUTPUT
141136

142137
def main():
143138
parser = argparse.ArgumentParser()
144139
parser.add_argument("-d", "--dir", default="./output", type=str, help="root directory of labels and predictions.")
145-
# must parse the command-line argument: ``--local_rank=LOCAL_PROCESS_RANK``, which will be provided by DDP
146-
parser.add_argument("--local_rank", type=int)
147140
args = parser.parse_args()
148141

149142
compute(args=args)

modules/cross_validation_models_ensemble.ipynb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
"from monai.networks.nets import UNet\n",
9595
"from monai.transforms import (\n",
9696
" Activationsd,\n",
97-
" AsChannelFirstd,\n",
97+
" EnsureChannelFirstd,\n",
9898
" AsDiscreted,\n",
9999
" Compose,\n",
100100
" LoadImaged,\n",
@@ -249,7 +249,7 @@
249249
"train_transforms = Compose(\n",
250250
" [\n",
251251
" LoadImaged(keys=[\"image\", \"label\"]),\n",
252-
" AsChannelFirstd(keys=[\"image\", \"label\"], channel_dim=-1),\n",
252+
" EnsureChannelFirstd(keys=[\"image\", \"label\"], channel_dim=-1),\n",
253253
" ScaleIntensityd(keys=[\"image\", \"label\"]),\n",
254254
" RandCropByPosNegLabeld(\n",
255255
" keys=[\"image\", \"label\"],\n",
@@ -266,7 +266,7 @@
266266
"val_transforms = Compose(\n",
267267
" [\n",
268268
" LoadImaged(keys=[\"image\", \"label\"]),\n",
269-
" AsChannelFirstd(keys=[\"image\", \"label\"], channel_dim=-1),\n",
269+
" EnsureChannelFirstd(keys=[\"image\", \"label\"], channel_dim=-1),\n",
270270
" ScaleIntensityd(keys=[\"image\", \"label\"]),\n",
271271
" EnsureTyped(keys=[\"image\", \"label\"]),\n",
272272
" ]\n",
@@ -588,7 +588,7 @@
588588
"name": "python",
589589
"nbconvert_exporter": "python",
590590
"pygments_lexer": "ipython3",
591-
"version": "3.8.12"
591+
"version": "3.8.13"
592592
}
593593
},
594594
"nbformat": 4,

modules/dice_loss_metric_notes.ipynb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"import torch\n",
2121
"from monai.losses import DiceLoss\n",
2222
"from monai.metrics import DiceMetric\n",
23-
"from monai.transforms import AddChannel, AsDiscrete, Compose\n",
23+
"from monai.transforms import EnsureChannelFirst, AsDiscrete, Compose\n",
2424
"\n",
2525
"\n",
2626
"def print_tensor(name, t):\n",
@@ -108,7 +108,7 @@
108108
"outputs": [],
109109
"source": [
110110
"# make one hot and add batch dimension\n",
111-
"make_2_class = Compose([AsDiscrete(to_onehot=2), AddChannel()])\n",
111+
"make_2_class = Compose([AsDiscrete(to_onehot=2), EnsureChannelFirst()])\n",
112112
"\n",
113113
"grnd2 = make_2_class(grnd)\n",
114114
"pred2 = make_2_class(pred)"
@@ -305,7 +305,7 @@
305305
"outputs": [],
306306
"source": [
307307
"# make one hot and add batch dimension\n",
308-
"make_3_class = Compose([AsDiscrete(to_onehot=3), AddChannel()])\n",
308+
"make_3_class = Compose([AsDiscrete(to_onehot=3), EnsureChannelFirst()])\n",
309309
"\n",
310310
"mgrnd2 = make_3_class(mgrnd)\n",
311311
"mpred2 = make_3_class(mpred)"
@@ -398,9 +398,9 @@
398398
],
399399
"metadata": {
400400
"kernelspec": {
401-
"display_name": "Python [conda env:monai]",
401+
"display_name": "Python 3 (ipykernel)",
402402
"language": "python",
403-
"name": "conda-env-monai-py"
403+
"name": "python3"
404404
},
405405
"language_info": {
406406
"codemirror_mode": {
@@ -412,7 +412,7 @@
412412
"name": "python",
413413
"nbconvert_exporter": "python",
414414
"pygments_lexer": "ipython3",
415-
"version": "3.9.7"
415+
"version": "3.8.13"
416416
}
417417
},
418418
"nbformat": 4,

modules/integrate_3rd_party_transforms.ipynb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
"source": [
5757
"from monai.utils import first, set_determinism\n",
5858
"from monai.transforms import (\n",
59-
" AddChanneld,\n",
59+
" EnsureChannelFirstd,\n",
6060
" Compose,\n",
6161
" CropForegroundd,\n",
6262
" LoadImaged,\n",
@@ -265,7 +265,7 @@
265265
"source": [
266266
"monai_transforms = [\n",
267267
" LoadImaged(keys=[\"image\", \"label\"]),\n",
268-
" AddChanneld(keys=[\"image\", \"label\"]),\n",
268+
" EnsureChannelFirstd(keys=[\"image\", \"label\"]),\n",
269269
" Orientationd(keys=[\"image\", \"label\"], axcodes=\"RAS\"),\n",
270270
" Spacingd(keys=[\"image\", \"label\"], pixdim=(\n",
271271
" 1.5, 1.5, 2.0), mode=(\"bilinear\", \"nearest\")),\n",
@@ -388,7 +388,7 @@
388388
" itk_transforms,\n",
389389
" # add another dim as BatchGenerator and\n",
390390
" # Rising expects shape [B, C, H, W, D]\n",
391-
" AddChanneld(keys=[\"image\", \"label\"]),\n",
391+
" EnsureChannelFirstd(keys=[\"image\", \"label\"]),\n",
392392
" adaptor(batch_generator_transforms, {\"image\": \"image\"}),\n",
393393
" EnsureTyped(keys=[\"image\", \"label\"]),\n",
394394
" adaptor(rising_transforms, {\"image\": \"image\", \"label\": \"label\"}),\n",
@@ -487,7 +487,7 @@
487487
"name": "python",
488488
"nbconvert_exporter": "python",
489489
"pygments_lexer": "ipython3",
490-
"version": "3.8.0"
490+
"version": "3.8.13"
491491
}
492492
},
493493
"nbformat": 4,

0 commit comments

Comments
 (0)