当前位置: 首页 > 工具软件 > Plans > 使用案例 >

nnunet(五) edit plans files

温开畅
2023-12-01

 

 

https://github.com/MIC-DKFZ/nnUNet/blob/master/documentation/tutorials/edit_plans_files.md

改变plans files 可以使得训练更灵活:您可以脱离nnU-Net的默认配置,使用不同的U-Net拓扑结构topologies、批量大小batch sizes和块大小patch sizes。请参照补充材料中的6.2章节,也就是第13页。

本教程的目标是演示如何阅读和修改计划文件plans files ,以及如何在实验中使用它们。这里使用的文件与Task120一起使用,需要您下载数据集,运行nnunet.dataset_convert.Task120_Massachusetts_RoadSegm.py,然后运行nnUNet_plan_and_preprocess。

注意,这个任务只是2D的,但我们在这里使用的相同原则可以很容易地扩展到3D和其他任务。

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]], 
'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 file中键'plans_per_stage'下保存的内容。

对于这个任务,nnU-Net打算使用1280x1024的补丁大小patch size 和每个轴大小为8池化pooling操作的U-Net架构。由于GPU显存的限制,批处理的大小batch size只有2。

决定一个像素是否属于“road”,并不取决于大补丁大小 large patch size (和U-Net架构)所提供的大量上下文信息,而是可以使用更多本地信息local information,也就是与所谓的size没有决定性关系,只是和前景信息量有关。在具有800个训练数据的数据集中批次batch size=2进行训练意味着每个批次仅包含有限的变异性。因此,一个可能的结论是,小块smaller patches但大批量 larger batch sizes可能会有更好的分割结果。让我们来研究一下(使用相同的GPU显存约束,通过反复试验手动验证):

变体1:patch size 512x512, batch size 12,以下代码段对计划文件进行了必要的调整

from batchgenerators.utilities.file_and_folder_operations import *
import numpy as np
from nnunet.paths import preprocessing_output_dir
task_name = 'Task120_MassRoadsSeg'

# if it breaks upon loading the plans file, make sure to run the Task120 dataset conversion and
# nnUNet_plan_and_preprocess first!
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]#池化层数改变了,相应的卷积核和池化核个数也要改变
# because we changed the num_pool_per_axis, we need to change conv_kernel_sizes and pool_op_kernel_sizes as well!
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]]
# for a network with num_pool_per_axis [7,7] the correct length of pool kernel sizes is 7 and the length of conv
# kernel sizes is 8! Note that you can also change these numbers if you believe it makes sense. A pool kernel size
# of 1 will result in no pooling along that axis, a kernel size of 3 will reduce the size of the feature map
# representations by factor 3 instead of 2.
#如果池化核某一个轴为1,表示不进行池化,如果池化核大小是3,表示feature map大小就会缩小3倍
# 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'))#保存的plans file必须以_plans_2D.pkl结尾

变体2:patch size 256x256, batch size 60

from batchgenerators.utilities.file_and_folder_operations import *
import numpy as np
from nnunet.paths import preprocessing_output_dir
task_name = 'Task120_MassRoadsSeg'
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'] = 60
plans['plans_per_stage'][0]['patch_size'] = np.array((256, 256))
plans['plans_per_stage'][0]['num_pool_per_axis'] = [6, 6]
plans['plans_per_stage'][0]['pool_op_kernel_sizes'] = [[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]]
save_pickle(plans, join(preprocessing_output_dir, task_name, 'nnUNetPlansv2.1_ps256_bs60_plans_2D.pkl'))

 您现在可以使用这些自定义计划文件plans files 来训练网络,并比较结果!记住,所有nnUNet_*命令都有-h参数来显示它们的参数。nnUNet_train通过-p参数支持自定义计划custom plans。自定义计划必须是前缀,也就是除了'_plans_2 .pkl'之外的所有内容:

变体1:

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

 变体2:

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

让所有5 folds 运行每个计划文件 plans file(原始和两种变体)。为了比较结果,您可以使用nnUNet_determine_postprocessing来获得必要的度量,例如:

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

 这将在训练输出目录中创建一个cv_niftis_raw和cv_niftis_postprocessed子文件夹。在每个文件夹中都有一个summary.json文件,你可以用一个普通的文本编辑器打开。在这个文件中,每个训练示例的数据集中都有表示5-fold cross-validation的metrics。在文件的最底部,通过平均(字段“mean”)来汇总metrics,这是您应该用来进行实验比较的东西。作者建议使用不经过后期处理的summary.json(位于cv_niftis_raw中),因为后处理实际上可能会在训练集上过拟合。以下是我获得的结果:

Vanilla nnU-Net: 0.7720
Variant 1: 0.7724
Variant 2: 0.7734

结果是非常相似的,作者并不认为在Dice上这么小的改进就是一个重要的结果。尽管如此,还是值得一试:-)

尽管有这里显示的结果,但作者想强调的是,修改plans文件是一个非常强大的工具,可以提高nnU-Net在某些数据集上的性能。不试一试,你永远不会知道。

ADDITIONAL INFORMATION (READ THIS!

  1. 当使用3d plans(' nnunetplansv2.1 1_plans_3d .pkl')时,3d_lowres和3d_fullres阶段将被编码在同一个计划文件plans file中。如果len(plans['plans_per_stage']) == 2,则[0]是3d_lowres,[1]是3d_fullres变体。如果len(plans['plans_per_stage']) == 1,那么[0]将是3d_fullres和3d_cascade_fullres(它们使用相同的计划)。
  2. ‘pool_op_kernel_sizes’和‘patch_size’一起决定瓶颈bottleneck处的特征映射表示 feature map representations的大小。对于这里给出的Variant 1和2,feature map表示的大小为
    print(plans['plans_per_stage'][0]['patch_size'] / np.prod(plans['plans_per_stage'][0]['pool_op_kernel_sizes'], 0))
    
    [4., 4.]

    如果您在此处看到非整数,则您的模型将崩溃!确保它们始终是整数! nnU-Net永远不会制造出比4个小的bottlenecks!

  3. 不要更改计划文件中的'current_spacing' !这将不能正常工作。要改变目标间距,请看自定义间距教程。
 

 

 类似资料: