nnUNet使用教程

在虚拟环境中安装nnUNet

创建虚拟环境

#进入conda环境:从anaconda3/bin/中进入终端
#创建虚拟环境nnunet
conda create nnunet python=3.8
#nnunet在anaconda3的envs文件夹中
#进入虚拟环境
source activate nnunet
#退出虚拟环境
conda deactivate

查看配置

>>> nvidia-smi

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 515.86.01    Driver Version: 515.86.01    CUDA Version: 11.7     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA GeForce ...  Off  | 00000000:01:00.0  On |                  N/A |
|  0%   49C    P8    39W / 370W |    575MiB / 24576MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|    0   N/A  N/A      1083      G   /usr/lib/xorg/Xorg                 53MiB |
|    0   N/A  N/A      2122      G   /usr/lib/xorg/Xorg                385MiB |
|    0   N/A  N/A      2265      G   /usr/bin/gnome-shell               51MiB |
|    0   N/A  N/A    149785      G   ...nlogin/bin/sunloginclient       13MiB |
|    0   N/A  N/A    150173      G   ...nlogin/bin/sunloginclient       10MiB |
|    0   N/A  N/A    486236      G   ...mviewer/tv_bin/TeamViewer       20MiB |
|    0   N/A  N/A    486392      G   /usr/lib/firefox/firefox           11MiB |
+-----------------------------------------------------------------------------+

进入python环境

python
#输入python就可以进入python环境
import torch
>>> print(torch.__version__)
1.13.1
#退出python
quit()

在.Bashrc中设置环境变量export OMP_NUM_THREADS=1

验证是否安装了适合的pytorch版本:

python -c 'import torch;print(torch.backends.cudnn.version())'
python -c 'import torch;print(torch.__version__)'   

8200 1.11.0+cu113

安装nnUNet:

  1. 用作标准化基线、开箱即用的分割算法或使用预训练模型运行推理

pip install nnunet

  1. 用作集成框架(这将在您的计算机上创建 nnU-Net 代码的副本,以便您可以根据需要对其进行修改)

git clone https://github.com/MIC-DKFZ/nnUNet.git
cd nnUNet
pip install -e .
  1. 设置文件保存路径

  1. 创建隐藏层(隐藏层的作用??)

pip install --upgrade git+https://github.com/FabianIsensee/hiddenlayer.git@more_plotted_details#egg=hiddenlayer

数据的用法

预处理

nnunet首先会提取数据指纹,根据数据大小等性质创建三个Unet:2D-Unet,3D-Unet and 3D U-Net cascade

数据存放在:nnUNet_raw_data_base/nnUNet_raw_data/TaskXXX_MYTASK

运行:

nnUNet_plan_and_preprocess -t XXX --verify_dataset_integrity

XXX是Task的整数名称

输出数据存放在:nnUNet_preprocessed,这一步仅仅处理了training cases

同时会生成plan.pkl,这个文件包含分割的管线配置,并且会被nnUNetTrainer读取。

如果在预处理期间 RAM 用完,则可能需要调整进程数 与 和 选项一起使用。nnUNet_plan_and_preprocess``-h``-tl``-tf

创建的U-net配置会储存在nnUNet_preprocessed/TaskXXX_MYTASK

模型训练

对于特定的数据,不一定会创建所有的三个网络,因为对于图像尺寸较小的数据集,UNet可以省略级联,因为全分辨率的patch已经能够覆盖大部分输入图像。

nnUNet_train CONFIGURATION TRAINER_CLASS_NAME TASK_NAME_OR_ID FOLD  --npz (additional options)

CONFIGURATION:可以指定2d,3d_fullres, 3d_lowers。TRAINER_CLASS_NAME:一般指定nnUNetTrainerV2;如果自定义训练器,可以加在 TRAINER_CLASS_NAME 处自定义指定。TASK_NAME_OR_ID:训练的数据集,FOLD:整数

nnUNet每50个epoch储存一个检查点,--npz:使得模型在最后的验证中保存softmax输出。

2d UNet

nnUNet_train 2d nnUNetTrainerV2 TaskXXX_MYTASK FOLD --npz

3D full resolution U-Net

nnUNet_train 3d_fullres nnUNetTrainerV2 TaskXXX_MYTASK FOLD --npz

3D U-Net cascade

3D low resolution UNet
nnUNet_train 3d_lowres nnUNetTrainerV2 TaskXXX_MYTASK FOLD --npz

3D full resolition UNet
nnUNet_train 3d_cascade_fullres nnUNetTrainerV2CascadeFullRes TaskXXX_MYTASK FOLD --npz

※ 3D full resolution需要把3D low resolution UNet的所有训练集跑完才能运行。

训练好的模型被保存在:RESULTS_FOLDER/nnUNet folder

每次训练都得到一个自动命名的文件夹:nnUNet_preprocessed/CONFIGURATION/TaskXXX_MYTASKNAME/TRAINER_CLASS_NAME__PLANS_FILE_NAME/FOLD

以Task002_Heart为例:

RESULTS_FOLDER/nnUNet/
├── 2d
│   └── Task02_Heart
│       └── nnUNetTrainerV2__nnUNetPlansv2.1
│           ├── fold_0
│           ├── fold_1
│           ├── fold_2
│           ├── fold_3
│           └── fold_4
├── 3d_cascade_fullres
├── 3d_fullres
│   └── Task02_Heart
│       └── nnUNetTrainerV2__nnUNetPlansv2.1
│           ├── fold_0
│           │   ├── debug.json
│           │   ├── model_best.model
│           │   ├── model_best.model.pkl
│           │   ├── model_final_checkpoint.model
│           │   ├── model_final_checkpoint.model.pkl
│           │   ├── network_architecture.pdf
│           │   ├── progress.png
│           │   └── validation_raw
│           │       ├── la_007.nii.gz
│           │       ├── la_007.pkl
│           │       ├── la_016.nii.gz
│           │       ├── la_016.pkl
│           │       ├── la_021.nii.gz
│           │       ├── la_021.pkl
│           │       ├── la_024.nii.gz
│           │       ├── la_024.pkl
│           │       ├── summary.json
│           │       └── validation_args.json
│           ├── fold_1
│           ├── fold_2
│           ├── fold_3
│           └── fold_4
└── 3d_lowres

3d_lowers和3d_cascade_fullres文件夹没有输出是因为训练数据集没有触发级联。

每个模型训练输出文件夹都会创建一下文件:

  1. debug.json:包含用于训练此模型的蓝图和推断参数的摘要。不容易阅读, 但对于调试非常有用

  1. model_best.model / model_best.model.pkl:训练期间识别的最佳模型的检查点文件。现在不用。

  1. model_final_checkpoint.model / model_final_checkpoint.model.pkl:最终模型的检查点文件(训练后) 已结束)。这是用于验证和推理的内容。

  1. network_architecture.pdf(仅当安装了隐藏层时!):一个PDF文档,其中包含网络架构图。

  1. progress.png:训练期间训练(蓝色)和验证(红色)的损失图。

扩展/更改nnUNet

要扩展更改nnUNet,必须使用git clone pip install -e命令安装nnunet。

对蓝图参数的更改

本节提供如何实现损失函数、训练计划、学习率、优化器、一些架构参数、数据增强的改变。这些参数都是nnU-Net trainer class的一部分。适用于2D\3D低分辨率和3D全分辨率的默认训练器类别是nnUNetTrainerV2。级联训练中,3D全分辨率的默认训练器类别是nnUNetTrainerV2CascadeFullRes 。nnUNetTrainerV2CascadeFullRes 继承了nnUNetTrainerV2。

创建一个新的训练器,只需要继承nnUNetTrainerV2或nnUNetTrainerV2CascadeFullRes。创建的训练器需要在nnunet.training.network_training文件夹下。

对inferred parameters的修改

推断参数是在数据集指纹上生成的,是训练集的低维特征显示:图像形状、体素间距、强度信息。数据集指纹由DatasetAnalyzer生成(nnunet/preprocessing/nnUNet_plan_and_preprocess)

nnUNet_plan_and_preprocess使用ExperimentPlanners来运行适配过程。默认的ExperimentPlanner:2D U-Net使用ExperimentPlanner2D_v21,3D全分辨率U-Net和级联U-Net使用ExperimentPlanner3D_v21。ExperimentPlanner之间也互相继承,只需要给ExperimentPlanners一个自定义的名字,并且将其放在nnunet/experment_planning文件夹下。继承ExperimentPlanners的时候需要重写类变量。省略这步骤会重写plans file和处理后的数据。nnUNet_plan_and_preprocessself.data_identifierself.plans_fname

To train with your custom configuration, simply specify the correct plans identifier with when you call the command. The plans file also contains the data_identifier specified in your ExperimentPlanner, so the trainer class will automatically know what data should be used.-p``nnUNet_train(这段不太明白)

一种可行的改变inferred parameters的方法:改变批大小和patch大小的优先级(现在是patch size优先);改变空间信息处理,改变目标spacing的定义,使用不同策略找到3d低分辨率UNet配置。

nnunet/experiment_planning中的文件夹包含几个ExperimentPlanner,可以修改推断参数的各个方面。

如果希望运行不同的预处理,需要实现自己的预处理器类。ExperimentPlanner使用的预处理器类定义在preprocessor_name 这个类变量中。默认3D U-Net是使用preprocesor_name="GenericPreprocessor",2D U-Net 默认使用PreprocessorFor2D. 所有自定义的处理器都必须位于nnunet/preprocessing文件夹中。preprocessor_name被保存在plan文件中。对预处理的修改可以是对MRI图像添加偏置场矫正,CT与处理方案等。

使用不同的网络构架

实现任何新的分割网络都需要了解nnunet的结构(需要多少降采样操作、是否使用深度学习、卷积内核大小是多少),它要能够动态的改变拓补结构,此外还要能够生成一个可用于估计内存消耗的值。

为了解释这个过程,实现了一个带有参与编码器的UNet(FabiansUNet in generic_modular_residual_UNet.py)这个UNet有一个叫做use_this_for_3D_configuration的类变量(位于find_3d_configuration中的代码)。相应的ExperimentPlanner:ExperimentPlanner3DFabiansResUNet_v21(FabiansUNet.compute_approx_vram_consumption生成)将该值与当前配置的网络拓补生成的值比较,以确保满足GPU内存目标。

教程:edit_plans_filse

custom_spacing

custom_preprocessing

手动编辑plans文件

您可以脱离nnU-Net的默认配置,并使用不同的U-Net拓扑结构、批处理大小和patch大小。

nnUNet_plan_and_preprocess 的输出如下:

[{'batch_size': 2, 
'num_pool_per_axis': [8, 8], #每个轴有八个池化操作
'patch_size': array([1280, 1024]), 
'median_patient_size_in_voxels': array([   1, 1500, 1500]), 
'current_spacing': array([999.,   1.,   1.]), 
'original_spacing': array([999.,   1.,   1.]), 
'pool_op_kernel_sizes': [[2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2]], #池化核个数和大小,如果'num_pool_per_axis'变化为7,池化核个数相应的需要变化为7个
'conv_kernel_sizes': [[3, 3], [3, 3], [3, 3], [3, 3], [3, 3], [3, 3], [3, 3], [3, 3], [3, 3]], #卷积核大小和个数,好像必须比池化核多一个,
'do_dummy_2D_data_aug': False}]

这些都保存在计划文件的关键字“plans_per_stage”中。

决定一个像素是否属于‘road’并不依赖于大patch提供的大型上下文信息,相反,可以通过更多的定位信息确定。而batch太小可能导致网络的变化小,因此可以增大batch_size减小patch大小。

from batchgenerators.utilities.file_and_folder_operations import *
import numpy as np
from nnunet.paths import preprocessing_output_dir
task_name = 'Task120_MassRoadsSeg'
'''
这个文件需要在运行了data_conversion和nnUNet_plan_and_preproces之后运行
'''

plans_fname = join(preprocessing_output_dir, task_name, 'nnUNetPlansv2.1_plans_2D.pkl')
plans = load_pickle(plans_fname)
plans['plans_per_stage'][0]['batch_size'] = 12
plans['plans_per_stage'][0]['patch_size'] = np.array((512, 512))
plans['plans_per_stage'][0]['num_pool_per_axis'] = [7, 7]
plans['plans_per_stage'][0]['pool_op_kernel_sizes'] = [[2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2]]
plans['plans_per_stage'][0]['conv_kernel_sizes'] = [[3, 3], [3, 3], [3, 3], [3, 3], [3, 3], [3, 3], [3, 3], [3, 3]]
'''
池化核大小为1会导致轴向没有池化,为3会导致丢失信息太多
'''
'''
用新名字保存plans文件,新文件需要以'_plans_2D.pkl'结尾
'''

# save the plans under a new plans name. Note that the new plans file must end with _plans_2D.pkl!
save_pickle(plans, join(preprocessing_output_dir, task_name, 'nnUNetPlansv2.1_ps512_bs12_plans_2D.pkl'))

nnUNet_train通过-p参数支持自定义计划,自定义计划的名称是除了'plans_2D.pkl'的一切。

nnUNet_train 2d nnUNetTrainerV2 120 FOLD -p nnUNetPlansv2.1_ps512_bs

对每个plan(变体和原始plan)运行5折运算,比较结果可以使用如下命令:

nnUNet_determine_postprocessing -t 120 -tr nnUNetTrainerV2 -p nnUNetPlansv2.1_ps512_bs12 -m 2d

这将在训练输出目录中创建一个cv_niftis_raw和cv_niftis_postprocessed子文件夹。每个文件夹中都有一个summary.json文件。文件最底部的矩阵是平均值,可以用来判断实验优劣。建议使用non_postprocessed summary.json,(位于cv_niftis_raw)。因为确定的后处理后的数据集可能overfit。

当使用3d_plans('nnUNetPlansv2.1_plans_3D.pk')时,3D_fullres和3D_lowers被编码在同一个文件中,如果len(plans['plans_per_stage']) == 2则[0]是3d_lowers,[1]是3d_fullres。如果len(plans['plans_per_stage']) == 1[0]就是3d_fullres和3d_cascade_fullres共用的参数。

pool_op_kernel_sizes和 patch_size共同决定bottleneck处的feature map。这里展示feature map的大小

print(plans['plans_per_stage'][0]['patch_size'] / np.prod(plans['plans_per_stage'][0]['pool_op_kernel_sizes'], 0))

[4., 4.]

这里看到的数字一定要是整数,并且bottleneck永远不会小于4.

不能改变'current_spacing'


feature map

由通道数决定,一个通道对应的三维图像就是一个feature map。因此也可以说有几个卷积核就有几个feature map。

因为一个卷积核代表的是需要识别的一个特征,而卷积操作后的体素数值越大就与这个卷积核所代表的特征越相似。

上文中指的是feature map的大小[2,2]

bottleneck

这里可以理解为池化层,用来降低图像大小。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值