目录

nnUNet 使用不完全指南(上)

nnUNet 使用教程

nnUNet 是一个专门用于医学图像的深度学习程序。使用 nnUNet 可以很方便的进行网络的训练和预测。他的集成度很高,能自动的进行合适的预处理,自动的选择合适的网络。 nnUNet 的官方 github 地址是https://github.com/MIC-DKFZ/nnUNet。在 github 里面有英文版的详细教程。网上也有一个不错的教程,地址是https://blog.csdn.net/weixin_42061636/article/details/107623757。这两篇再加本教程可以交叉看,如有出入,github 上的肯定是权威。本文在kissandrun.github.io持续更新。

安装

首先是安装部分。默认读者使用Linux环境,nnUNet并没有提供其他平台的支持。nnUNet是基于PythonPyTorch。所以首先得安装这两个,这里推荐使用Anaconda安装。具体步骤如下:

  1. 查看一下系统是否已经安装完Anaconda环境。在终端中直接输入conda,然后回车。如果出现下面字样以及一堆使用方法,则环境已存在conda无需安装Anaconda。直接跳至第二步。如果提示命令不存在,则参考https://blog.csdn.net/lwgkzl/article/details/89329383。(记得安装最新的版本!)

    https://i.loli.net/2021/03/15/jzh8N3LCsiltDrb.png

  2. 装完conda,然后新建一个环境,专门用于nnUNet

    1
    
    conda create --name nnunet python=3.8
    

    然后激活这个环境。(之后如果新开终端,每次都要激活环境)

    1
    
    conda activate nnunet
    

    详细的conda手册参考:https://docs.conda.io/projects/conda/en/4.6.1/user-guide/tasks/manage-environments.html#creating-an-environment-with-commands

  3. 安装PyTorch。终端输入以下命令

    1
    
    conda install pytorch torchvision torchaudio cudatoolkit=11.1 -c pytorch -c conda-forge
    

    check一下是否成功。

    https://i.loli.net/2021/03/15/OhWJzM1laq5PciY.png

    如果能正确输出,则pytorch安装正确,且能使用GPU加速。

  4. 在这个环境中安装nnUNet。终端中输入以下命令即可。(也可以使用csdn上的教程中的那种安装方式)

    1
    
    pip install nnunet
    

    check一下是否成功

    https://i.loli.net/2021/03/15/UBfhoG6KZ4Tbdjr.png

    如果能import nnunet正常输出,则nnUNet安装正确。而且输入which nnUNet_train 会有输出

    https://i.loli.net/2021/03/15/iDUe3k6HYlfZtzM.png

用nnUNet训练自己的数据

  1. 设置环境变量。

    在合适的地方新建如下三个文件夹,分别存放预处理后的图像,原始的图像和训练完成的模型。

    https://i.loli.net/2021/03/15/Yu4D7CTZINsg1wy.png

    修改Home目录下的.bashrc文件,在最后加入下面内容(!注意这三个目录应该是对应自己的)

    1
    2
    3
    
    export nnUNet_raw_data_base="/media/fabian/nnUNet_raw"                   
    export nnUNet_preprocessed="/media/fabian/nnUNet_preprocessed"
    export RESULTS_FOLDER="/media/fabian/nnUNet_trained_models"
    

    保存之后,在终端中输入以下命令

    1
    
    source ~/.bashrc
    
  2. 整理数据

    nnUNet 对数据的命名和结构都有严格的要求,需要调整自己的数据才能给nnUNet使用。在上面新建的nnUNet_raw下面再新建nnUNet_raw_data文件夹。然后在nnUNet_raw_data下新建文件 TaskXXX_任务名XXX是任务序号(大于100的数,前100被占用了),任务名取一个好记的英文名。

    最后的文件结构如下

    1
    2
    3
    4
    5
    6
    7
    
    nnUNet_raw/nnUNet_raw_data/
    ├── Task001_BrainTumour
    ├── Task002_Heart
    ├── Task003_Liver
    ├── Task004_Hippocampus
    ├── Task005_Prostate
    ├── ...
    

    然后在任务文件夹里构建如下结构

    1
    2
    3
    4
    5
    
    Task001_BrainTumour/
    ├── dataset.json
    ├── imagesTr
    ├── (imagesTs)
    └── labelsTr
    

    imagesTrlabelsTr 分别存放训练的原图和标签。imagesTs存放测试的原图。dataset.json 下面会说。

  3. 图像命名

    imagesTrlabelsTr 中图像的命名也有要求。其中imagesTr需要改成类似下图这样,而且必须是 .nii.gz 格式。建议写个 python 脚本批量重命名并保存成 .nii.gz 格式。

    https://i.loli.net/2021/03/15/1h4BbdVPoCUyt5r.png

    labelsTr需要改成如下样式,注意原图和标签图序号一一对应,只是原图后面带**_0000**。

    https://i.loli.net/2021/03/15/RbCvSf6uEWXtK5M.png

    imagesTs改成如下,无需从0开始标号,可以接着训练集继续标下去,比较方便。

    https://i.loli.net/2021/03/15/jL4xWvB2rP1taEo.png

  4. dataset.json编写

    上文说到这个任务文件夹应该包含一个dataset.json 文件。下面是一个例子

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    
    {
        "description": "Task128_LungLobe",
        "labels": {
            "0": "0",
            "1": "1",
            "2": "2",
            "3": "3",
            "4": "4",
            "5": "5"
        },
        "licence": "see challenge website",
        "modality": {
            "0": "CT"
        },
        "name": "Task128_LungLobe",
        "numTest": 6,
        "numTraining": 80,
        "reference": "see challenge website",
        "release": "0.0",
        "tensorImageSize": "4D",
        "test": [
            "./imagesTs/LungLobe_080.nii.gz",
            "./imagesTs/LungLobe_081.nii.gz",
            "./imagesTs/LungLobe_082.nii.gz",
            "./imagesTs/LungLobe_083.nii.gz",
            "./imagesTs/LungLobe_084.nii.gz",
            "./imagesTs/LungLobe_085.nii.gz"
        ],
        "training": [
            {
                "image": "./imagesTr/LungLobe_000.nii.gz",
                "label": "./labelsTr/LungLobe_000.nii.gz"
            },
            {
                "image": "./imagesTr/LungLobe_001.nii.gz",
                "label": "./labelsTr/LungLobe_001.nii.gz"
            },
            {
                "image": "./imagesTr/LungLobe_002.nii.gz",
                "label": "./labelsTr/LungLobe_002.nii.gz"
            },
            {
                "image": "./imagesTr/LungLobe_003.nii.gz",
                "label": "./labelsTr/LungLobe_003.nii.gz"
            },
            {
                "image": "./imagesTr/LungLobe_004.nii.gz",
                "label": "./labelsTr/LungLobe_004.nii.gz"
            },
            {
                "image": "./imagesTr/LungLobe_005.nii.gz",
                "label": "./labelsTr/LungLobe_005.nii.gz"
            },
            {
                "image": "./imagesTr/LungLobe_006.nii.gz",
                "label": "./labelsTr/LungLobe_006.nii.gz"
            },
            {
                "image": "./imagesTr/LungLobe_007.nii.gz",
                "label": "./labelsTr/LungLobe_007.nii.gz"
            },
            {
                "image": "./imagesTr/LungLobe_008.nii.gz",
                "label": "./labelsTr/LungLobe_008.nii.gz"
            },
            {
                "image": "./imagesTr/LungLobe_009.nii.gz",
                "label": "./labelsTr/LungLobe_009.nii.gz"
            },
            {
                "image": "./imagesTr/LungLobe_010.nii.gz",
                "label": "./labelsTr/LungLobe_010.nii.gz"
            }
        ]
    }
    

    按自己的任务的属性,修改上面的labelsnumTest等等信息。注意:这里image的文件名里没有 _0000 。但是修改这个json文件夹的工作量是比较大的,也可以写个 python 脚本自动生成。下面是例子

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    
    from collections import OrderedDict
    from batchgenerators.utilities.file_and_folder_operations import save_json
       
       
    def main():
        foldername = "Task128_LungLobe"
        numTraining = 80
        numTest = 6
        numClass = 6
        json_dict = OrderedDict()
        json_dict['name'] = foldername
        json_dict['description'] = foldername
        json_dict['tensorImageSize'] = "4D"
        json_dict['reference'] = "see challenge website"
        json_dict['licence'] = "see challenge website"
        json_dict['release'] = "0.0"
        json_dict['modality'] = {
            "0": "CT",
        }
        json_dict['labels'] = {i: str(i) for i in range(numClass)}
       
        json_dict['numTraining'] = numTraining
        json_dict['numTest'] = numTest
        json_dict['training'] = [{'image': "./imagesTr/LungLobe_{:0>3d}.nii.gz".format(i),
                                  "label": "./labelsTr/LungLobe_{:0>3d}.nii.gz".format(i)}
                                 for i in range(numTraining)]
       
        json_dict['test'] = ["./imagesTs/LungLobe_{:0>3d}.nii.gz".format(i)
                             for i in range(numTraining, numTraining+numTest)]
       
        save_json(json_dict, "./dataset.json")
       
       
    if __name__ == "__main__":
        main()
       
    

    需要发挥一点主观能动性,修改一下使之适应自己的任务。

  5. 整理完数据,运行下面命令

    1
    
    nnUNet_plan_and_preprocess -t XXX --verify_dataset_integrity
    

    XXX 代替为你的任务序号。该命令进行预处理。

  6. 开始训练。

    单卡训练:执行

    1
    
    nnUNet_train 3d_fullres nnUNetTrainerV2 XXX 0
    

    意思是对序号为XXX的任务进行第0折交叉验证,训练模式是3d的全像素。一共有五折交叉验证,所以最后一个数为0-4。

    注意:默认在第一块 gpu(索引为0)上进行训练,如果想指定某个gpu,请先执行:

    1
    
    export CUDA_VISIBLE_DEVICES=X
    

    X为你指定的gpu索引。再执行上面的命令。

    多卡训练:比如我现在要在0和1两张卡上执行训练: 先执行

    1
    
    export CUDA_VISIBLE_DEVICES=0,1
    

    再执行

    1
    
    nnUNet_train_DP 3d_fullres nnUNetTrainerV2_DP XXX 0 -gpus 2
    

    实验证明多卡训练速度和单卡训练速度的差别不大,只是为了减少每张卡的使用显存来弥补单卡显存不足的情况。

    参考:https://blog.csdn.net/weixin_42061636/article/details/107623757