Skip to content

Commit 24f03f9

Browse files
committed
add monai 201
Signed-off-by: YunLiu <[email protected]>
1 parent b6a83da commit 24f03f9

File tree

1 file changed

+363
-0
lines changed

1 file changed

+363
-0
lines changed

2d_classification/monai_201.ipynb

Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
{
2+
"cells": [
3+
{
4+
"attachments": {},
5+
"cell_type": "markdown",
6+
"metadata": {},
7+
"source": [
8+
"Copyright (c) MONAI Consortium \n",
9+
"Licensed under the Apache License, Version 2.0 (the \"License\"); \n",
10+
"you may not use this file except in compliance with the License. \n",
11+
"You may obtain a copy of the License at \n",
12+
"&nbsp;&nbsp;&nbsp;&nbsp;http://www.apache.org/licenses/LICENSE-2.0 \n",
13+
"Unless required by applicable law or agreed to in writing, software \n",
14+
"distributed under the License is distributed on an \"AS IS\" BASIS, \n",
15+
"WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n",
16+
"See the License for the specific language governing permissions and \n",
17+
"limitations under the License.\n",
18+
"\n",
19+
"# MONAI 201 tutorial\n",
20+
"\n",
21+
"In this tutorial we'll revisit the [MONAI 101 notebook](https://github.com/Project-MONAI/tutorials/blob/main/2d_classification/monai_101.ipynb) and add more features representing best practice concepts. This will include evaluation and tensorboard handler techniques.\n",
22+
"\n",
23+
"These steps will be included in this tutorial, and each of them will take only a few lines of code:\n",
24+
"- Dataset download and Data pre-processing\n",
25+
"- Define a DenseNet-121 and run training\n",
26+
"- Run inference using SupervisedEvaluator\n",
27+
"\n",
28+
"This tutorial will use about 7GB of GPU memory and 10 minutes to run.\n",
29+
"\n",
30+
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Project-MONAI/tutorials/blob/main/2d_classification/monai_201.ipynb)"
31+
]
32+
},
33+
{
34+
"cell_type": "markdown",
35+
"metadata": {},
36+
"source": [
37+
"## Setup environment"
38+
]
39+
},
40+
{
41+
"cell_type": "code",
42+
"execution_count": 1,
43+
"metadata": {},
44+
"outputs": [],
45+
"source": [
46+
"!python -c \"import monai\" || pip install -q \"monai-weekly[ignite, tqdm, tensorboard]\""
47+
]
48+
},
49+
{
50+
"attachments": {},
51+
"cell_type": "markdown",
52+
"metadata": {},
53+
"source": [
54+
"## Setup imports"
55+
]
56+
},
57+
{
58+
"cell_type": "code",
59+
"execution_count": null,
60+
"metadata": {},
61+
"outputs": [],
62+
"source": [
63+
"import logging\n",
64+
"import numpy as np\n",
65+
"import os\n",
66+
"from pathlib import Path\n",
67+
"import sys\n",
68+
"import tempfile\n",
69+
"import torch\n",
70+
"import ignite\n",
71+
"\n",
72+
"from monai.apps import MedNISTDataset\n",
73+
"from monai.config import print_config\n",
74+
"from monai.data import DataLoader\n",
75+
"from monai.engines import SupervisedTrainer, SupervisedEvaluator\n",
76+
"from monai.handlers import StatsHandler, TensorBoardStatsHandler, ValidationHandler, CheckpointSaver, CheckpointLoader, ClassificationSaver\n",
77+
"from monai.handlers.utils import from_engine\n",
78+
"from monai.inferers import SimpleInferer\n",
79+
"from monai.networks import eval_mode\n",
80+
"from monai.networks.nets import densenet121\n",
81+
"from monai.transforms import LoadImageD, EnsureChannelFirstD, ScaleIntensityD, Compose, AsDiscreted\n",
82+
"\n",
83+
"print_config()"
84+
]
85+
},
86+
{
87+
"attachments": {},
88+
"cell_type": "markdown",
89+
"metadata": {},
90+
"source": [
91+
"## Setup data directory\n",
92+
"\n",
93+
"You can specify a directory with the `MONAI_DATA_DIRECTORY` environment variable. \n",
94+
"This allows you to save results and reuse downloads. \n",
95+
"If not specified a temporary directory will be used."
96+
]
97+
},
98+
{
99+
"cell_type": "code",
100+
"execution_count": 2,
101+
"metadata": {},
102+
"outputs": [
103+
{
104+
"name": "stdout",
105+
"output_type": "stream",
106+
"text": [
107+
"/workspace/Data\n"
108+
]
109+
}
110+
],
111+
"source": [
112+
"directory = os.environ.get(\"MONAI_DATA_DIRECTORY\")\n",
113+
"root_dir = tempfile.mkdtemp() if directory is None else directory\n",
114+
"print(root_dir)"
115+
]
116+
},
117+
{
118+
"attachments": {},
119+
"cell_type": "markdown",
120+
"metadata": {},
121+
"source": [
122+
"## Use MONAI transforms to preprocess data\n",
123+
"\n",
124+
"We'll first prepare the data very much like in the previous tutorial with the same transforms and dataset:"
125+
]
126+
},
127+
{
128+
"cell_type": "code",
129+
"execution_count": 3,
130+
"metadata": {},
131+
"outputs": [
132+
{
133+
"name": "stdout",
134+
"output_type": "stream",
135+
"text": [
136+
"2024-02-26 08:40:15,309 - INFO - Verified 'MedNIST.tar.gz', md5: 0bc7306e7427e00ad1c5526a6677552d.\n",
137+
"2024-02-26 08:40:15,310 - INFO - File exists: /workspace/Data/MedNIST.tar.gz, skipped downloading.\n",
138+
"2024-02-26 08:40:15,310 - INFO - Non-empty folder exists in /workspace/Data/MedNIST, skipped extracting.\n"
139+
]
140+
},
141+
{
142+
"name": "stderr",
143+
"output_type": "stream",
144+
"text": [
145+
"Loading dataset: 100%|██████████| 47164/47164 [00:20<00:00, 2334.53it/s]\n",
146+
"Loading dataset: 100%|██████████| 5895/5895 [00:02<00:00, 2431.97it/s]\n"
147+
]
148+
}
149+
],
150+
"source": [
151+
"transform = Compose(\n",
152+
" [\n",
153+
" LoadImageD(keys=\"image\", image_only=True),\n",
154+
" EnsureChannelFirstD(keys=\"image\"),\n",
155+
" ScaleIntensityD(keys=\"image\"),\n",
156+
" ]\n",
157+
")\n",
158+
"\n",
159+
"# If you use the MedNIST dataset, please acknowledge the source.\n",
160+
"dataset = MedNISTDataset(root_dir=root_dir, transform=transform, section=\"training\", download=True)\n",
161+
"valdata = MedNISTDataset(root_dir=root_dir, transform=transform, section=\"validation\", download=False)"
162+
]
163+
},
164+
{
165+
"cell_type": "markdown",
166+
"metadata": {},
167+
"source": [
168+
"## Define a network and a supervised trainer\n",
169+
"\n",
170+
"For training we have the same elements again and will slightly change the `SupervisedTrainer` by expanding its train_handlers. This upgrade will be beneficial for efficient utilization of TensorBoard.\n",
171+
"Furthermore, we introduce a `SupervisedEvaluator` object that will efficiently track model progress. Accompanied by `TensorBoardStatsHandler`, it will log statistics for TensorBoard, ensuring precise tracking and management."
172+
]
173+
},
174+
{
175+
"cell_type": "code",
176+
"execution_count": 4,
177+
"metadata": {},
178+
"outputs": [],
179+
"source": [
180+
"max_epochs = 5\n",
181+
"save_interval = 2\n",
182+
"out_dir = './eval'\n",
183+
"model = densenet121(spatial_dims=2, in_channels=1, out_channels=6).to(\"cuda:0\")\n",
184+
"\n",
185+
"logging.basicConfig(stream=sys.stdout, level=logging.INFO)\n",
186+
"\n",
187+
"evaluator = SupervisedEvaluator(\n",
188+
" device=torch.device(\"cuda:0\"),\n",
189+
" val_data_loader=DataLoader(valdata, batch_size=512, shuffle=False, num_workers=4),\n",
190+
" network=model,\n",
191+
" inferer=SimpleInferer(),\n",
192+
" key_val_metric={\"val_acc\": ignite.metrics.Accuracy(from_engine([\"pred\", \"label\"]))},\n",
193+
" val_handlers=[\n",
194+
" StatsHandler(iteration_log=False),\n",
195+
" TensorBoardStatsHandler(iteration_log=False)\n",
196+
" ],\n",
197+
")\n",
198+
"\n",
199+
"trainer = SupervisedTrainer(\n",
200+
" device=torch.device(\"cuda:0\"),\n",
201+
" max_epochs=max_epochs,\n",
202+
" train_data_loader=DataLoader(dataset, batch_size=512, shuffle=True, num_workers=4),\n",
203+
" network=model,\n",
204+
" optimizer=torch.optim.Adam(model.parameters(), lr=1e-5),\n",
205+
" loss_function=torch.nn.CrossEntropyLoss(),\n",
206+
" inferer=SimpleInferer(),\n",
207+
" train_handlers=[\n",
208+
" ValidationHandler(validator=evaluator, epoch_level=True, interval=1),\n",
209+
" CheckpointSaver(\n",
210+
" save_dir=out_dir,\n",
211+
" save_dict={\"model\": model},\n",
212+
" save_interval=save_interval,\n",
213+
" save_final=True,\n",
214+
" final_filename=\"checkpoint.pt\",\n",
215+
" ),\n",
216+
" StatsHandler(),\n",
217+
" TensorBoardStatsHandler(tag_name=\"train_loss\", output_transform=from_engine([\"loss\"], first=True))\n",
218+
" ],\n",
219+
")"
220+
]
221+
},
222+
{
223+
"attachments": {},
224+
"cell_type": "markdown",
225+
"metadata": {},
226+
"source": [
227+
"## Run the training"
228+
]
229+
},
230+
{
231+
"cell_type": "code",
232+
"execution_count": null,
233+
"metadata": {},
234+
"outputs": [],
235+
"source": [
236+
"trainer.run()"
237+
]
238+
},
239+
{
240+
"cell_type": "markdown",
241+
"metadata": {},
242+
"source": [
243+
"## Inference\n",
244+
"\n",
245+
"First thing to do is to prepare the test dataset:"
246+
]
247+
},
248+
{
249+
"cell_type": "code",
250+
"execution_count": 6,
251+
"metadata": {},
252+
"outputs": [],
253+
"source": [
254+
"dataset_dir = Path(root_dir, \"MedNIST\")\n",
255+
"class_names = sorted(f\"{x.name}\" for x in dataset_dir.iterdir() if x.is_dir())\n",
256+
"testdata = MedNISTDataset(root_dir=root_dir, transform=transform, section=\"test\", download=False, runtime_cache=True)"
257+
]
258+
},
259+
{
260+
"attachments": {},
261+
"cell_type": "markdown",
262+
"metadata": {},
263+
"source": [
264+
"Next, we're going to establish a `SupervisedEvaluator`. This evaluator will process all the files in the specified directory and persist the results into a CSV file. Validation handlers (val_handlers) will be utilized to load the checkpoint file, providing an error if any file is unavailable, and they will also save the classification outcomes."
265+
]
266+
},
267+
{
268+
"cell_type": "code",
269+
"execution_count": 10,
270+
"metadata": {},
271+
"outputs": [
272+
{
273+
"name": "stdout",
274+
"output_type": "stream",
275+
"text": [
276+
"INFO:ignite.engine.engine.SupervisedEvaluator:Engine run resuming from iteration 0, epoch 0 until 1 epochs\n",
277+
"INFO:ignite.engine.engine.SupervisedEvaluator:Restored all variables from ./eval/checkpoint.pt\n",
278+
"INFO:ignite.engine.engine.SupervisedEvaluator:Epoch[1] Complete. Time taken: 00:01:24.338\n",
279+
"INFO:ignite.engine.engine.SupervisedEvaluator:Engine run complete. Time taken: 00:01:24.390\n"
280+
]
281+
}
282+
],
283+
"source": [
284+
"evaluator = SupervisedEvaluator(\n",
285+
" device=torch.device(\"cuda:0\"),\n",
286+
" val_data_loader=DataLoader(testdata, batch_size=1, num_workers=0),\n",
287+
" network=model,\n",
288+
" inferer=SimpleInferer(),\n",
289+
" postprocessing=AsDiscreted(keys=\"pred\", argmax=True),\n",
290+
" val_handlers=[\n",
291+
" CheckpointLoader(load_path=f\"{out_dir}/checkpoint.pt\", load_dict={\"model\": model}),\n",
292+
" ClassificationSaver(\n",
293+
" batch_transform=lambda batch: batch[0][\"image\"].meta,\n",
294+
" output_transform=from_engine(['pred'])\n",
295+
" )\n",
296+
" ],\n",
297+
")\n",
298+
"\n",
299+
"evaluator.run()"
300+
]
301+
},
302+
{
303+
"cell_type": "markdown",
304+
"metadata": {},
305+
"source": [
306+
"By default, the inference results are stored in a file named \"predictions.csv\". However, this output filename can be customized within the `ClassificationSaver` handler, according to your preferences.\n",
307+
"Upon examining the output, one can note that the second column corresponds to the predicted class. A more discernable interpretation can be achieved by using these values as indices mapped to our predefined list of class names."
308+
]
309+
},
310+
{
311+
"cell_type": "code",
312+
"execution_count": 12,
313+
"metadata": {},
314+
"outputs": [
315+
{
316+
"name": "stdout",
317+
"output_type": "stream",
318+
"text": [
319+
"/workspace/Data/MedNIST/AbdomenCT/006070.jpeg AbdomenCT\n",
320+
"/workspace/Data/MedNIST/BreastMRI/006574.jpeg BreastMRI\n",
321+
"/workspace/Data/MedNIST/ChestCT/009858.jpeg ChestCT\n",
322+
"/workspace/Data/MedNIST/CXR/007398.jpeg CXR\n",
323+
"/workspace/Data/MedNIST/Hand/005663.jpeg Hand\n",
324+
"/workspace/Data/MedNIST/HeadCT/006896.jpeg HeadCT\n",
325+
"/workspace/Data/MedNIST/HeadCT/007179.jpeg HeadCT\n",
326+
"/workspace/Data/MedNIST/CXR/001190.jpeg CXR\n",
327+
"/workspace/Data/MedNIST/ChestCT/005138.jpeg ChestCT\n",
328+
"/workspace/Data/MedNIST/BreastMRI/000023.jpeg BreastMRI\n"
329+
]
330+
}
331+
],
332+
"source": [
333+
"max_items_to_print = 10\n",
334+
"for fn, idx in np.loadtxt(\"./predictions.csv\", delimiter=\",\", dtype=str):\n",
335+
" print(fn, class_names[int(float(idx))])\n",
336+
" max_items_to_print -= 1\n",
337+
" if max_items_to_print == 0:\n",
338+
" break"
339+
]
340+
}
341+
],
342+
"metadata": {
343+
"kernelspec": {
344+
"display_name": "Python 3",
345+
"language": "python",
346+
"name": "python3"
347+
},
348+
"language_info": {
349+
"codemirror_mode": {
350+
"name": "ipython",
351+
"version": 3
352+
},
353+
"file_extension": ".py",
354+
"mimetype": "text/x-python",
355+
"name": "python",
356+
"nbconvert_exporter": "python",
357+
"pygments_lexer": "ipython3",
358+
"version": "3.10.12"
359+
}
360+
},
361+
"nbformat": 4,
362+
"nbformat_minor": 2
363+
}

0 commit comments

Comments
 (0)