从零开始:nnUNet在2D医学图像分割中的实战指南
1. 为什么选择nnUNet?一个“懒人”的福音
如果你刚接触医学图像分割,面对一堆复杂的模型和代码感到头大,那我强烈建议你试试nnUNet。我自己刚入行的时候,也是被各种需要手动调参的模型折磨得够呛,直到发现了这个“宝藏框架”。简单来说,nnUNet是一个能帮你自动搞定几乎所有超参数设置的框架,你只需要把数据按照它的格式整理好,它就能自己设计出最适合你这个数据集的训练方案。这听起来是不是有点“黑科技”?其实它的核心思想很朴素:与其让研究者花大量时间在调参上,不如让算法自己去学习和适应数据的特点。
我第一次用它是在一个眼底血管分割的项目上,当时手头只有几百张2D的眼底彩照。按照传统方法,我得先决定用2D U-Net还是3D U-Net,然后纠结用什么损失函数、学习率怎么设、数据增强用哪些……一套流程下来,一周时间就过去了。但用nnUNet,我花了一天时间把数据格式整理好,然后运行了几条命令,第二天就拿到了一个效果相当不错的模型。它甚至帮我做了五折交叉验证,自动选出了最好的那个模型。这种感觉,就像请了一个经验丰富的“炼丹师”助理,帮你把脏活累活都干了。
当然,nnUNet也不是万能的。它对计算资源,尤其是GPU显存的要求比较高。官方说至少需要10GB,但我实测下来,如果你想跑得比较顺畅,尤其是在处理2D图像但通道数较多(比如RGB三通道被当作三个模态)时,显存占用可能会接近20GB。不过别担心,后面我会详细告诉你,如果显存不够该怎么办,比如怎么调整batch size来“省着用”。总的来说,对于初学者或者想快速验证一个想法的研究者,nnUNet能让你把精力集中在数据本身和问题定义上,而不是陷入无穷无尽的技术细节里。
2. 环境搭建:一步一坑,我帮你填平
万事开头难,环境配置往往是劝退新手的第一道坎。我见过太多人在安装依赖时因为版本冲突而崩溃。所以,咱们稳扎稳打,从头开始。我的系统环境是Ubuntu 18.04,Python 3.8,PyTorch 1.7.1。这个组合相对稳定,如果你用别的版本,问题也不大,但尽量保证PyTorch和CUDA版本匹配。
第一步,创建独立的虚拟环境。 这是Python开发的好习惯,能避免项目间的包版本“打架”。打开你的终端,执行下面命令:
conda create -n nnunet_env python=3.8
conda activate nnunet_env
第二步,获取nnUNet源码并安装。 我习惯在一个专门的目录下管理所有项目,比如/home/yourname/Projects/。在这个目录下,我们新建一个工作区:
mkdir nnUNet_workspace
cd nnUNet_workspace
git clone https://github.com/MIC-DKFZ/nnUNet.git
cd nnUNet
pip install -e .
注意,pip install -e . 这个命令末尾的“点”不能少,它代表以“可编辑模式”安装当前目录下的包。安装成功后,你在命令行里输入nnUNet_然后按Tab键,应该能看到一系列以它开头的命令,比如nnUNet_plan_and_preprocess、nnUNet_train,这就说明安装成功了。
第三步,也是最关键的一步:设置数据目录结构。 nnUNet对数据存放的路径有严格的要求,必须设置几个环境变量来告诉它去哪里找数据、存预处理结果和模型。我们在nnUNet_workspace目录下创建这些文件夹:
mkdir -p DATASET/nnUNet_raw
mkdir -p DATASET/nnUNet_preprocessed
mkdir -p DATASET/nnUNet_trained_models
然后,把下面这几行命令添加到你的~/.bashrc文件末尾(用nano ~/.bashrc或vim ~/.bashrc编辑),然后执行source ~/.bashrc让它们生效。请务必将路径/home/yourname/Projects/nnUNet_workspace替换成你自己的实际路径。
export nnUNet_raw_data_base="/home/yourname/Projects/nnUNet_workspace/DATASET/nnUNet_raw"
export nnUNet_preprocessed="/home/yourname/Projects/nnUNet_workspace/DATASET/nnUNet_preprocessed"
export RESULTS_FOLDER="/home/yourname/Projects/nnUNet_workspace/DATASET/nnUNet_trained_models"
这三个环境变量分别指向:原始数据、预处理后的数据、训练好的模型的存放位置。设置好之后,nnUNet的所有后续操作都会基于这些路径。这一步如果错了,后面会报各种找不到文件的错误,所以一定要仔细核对。
3. 数据准备:把“散装”图片变成nnUNet能吃的“套餐”
这是整个流程中最需要耐心的一步,但也是最核心的一步。nnUNet要求输入的数据是NIfTI格式(.nii或.nii.gz),这是一种医学影像常用的格式。我们的2D图片(比如.png, .jpg)需要先转换成3D NIfTI,只不过这个3D数据的深度(z轴)是1。听起来有点绕?别怕,我们一步步来。
假设你的数据是这样组织的:一个images文件夹里放原图,一个masks文件夹里放对应的分割标签(掩膜),且文件名一一对应。
your_data/
├── images/
│ ├── patient1.png
│ ├── patient2.png
│ └── ...
└── masks/
├── patient1.png
├── patient2.png
└── ...
我们需要做三件事:1. 划分训练集和测试集;2. 将2D图片转为3D NIfTI格式;3. 创建描述数据集的dataset.json文件。我写了一个脚本帮你一站式搞定,你只需要修改开头的几个路径参数。
import os
import random
from tqdm import tqdm
import SimpleITK as sitk
import cv2
import numpy as np
import json
def split_dataset(image_list, train_ratio=0.8):
"""随机划分训练集和测试集"""
random.shuffle(image_list)
split_idx = int(len(image_list) * train_ratio)
return image_list[:split_idx], image_list[split_idx:]
def convert_2d_to_3d_nifti(image_path, mask_path, output_dir, case_name, is_train=True):
"""
将一对2D图像和掩膜转换为nnUNet需要的3D NIfTI格式。
假设原图为RGB三通道,我们将每个通道视为一个独立的模态。
"""
# 读取图像和掩膜
img = cv2.imread(image_path) # 形状: (H, W, 3)
mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE) # 形状: (H, W)
# 确保掩膜是二值化的(0和1)
mask = (mask > 127).astype(np.uint8)
# 为每个通道(模态)创建单独的NIfTI文件
# nnUNet要求每个模态一个文件,命名格式为 `case_name_XXXX.nii.gz`,XXXX是模态ID
for channel in range(3):
single_channel_img = img[:, :, channel] # 取出一个通道
# 将2D数组重塑为3D (1, H, W)
single_channel_3d = single_channel_img.reshape((1, ) + single_channel_img.shape)
nifti_img = sitk.GetImageFromArray(single_channel_3d)
# 确定保存路径和文件名
if is_train:
img_save_dir = os.path.join(output_dir, 'imagesTr')
else:
img_save_dir = os.path.join(output_dir, 'imagesTs')
os.makedirs(img_save_dir, exist_ok=True)
modality_suffix = f'_{channel:04d}' # 例如:_0000, _0001, _0002
save_path = os.path.join(img_save_dir, f'{case_name}{modality_suffix}.nii.gz')
sitk.WriteImage(nifti_img, save_path)
# 处理掩膜标签
mask_3d = mask.reshape((1, ) + mask.shape)
nifti_mask = sitk.GetImageFromArray(mask_3d)
if is_train:
mask_save_dir = os.path.join(output_dir, 'labelsTr')
else:
# 注意:nnUNet在测试时不需要标签,但如果你有测试集标签,可以放在labelsTs
mask_save_dir = os.path.join(output_dir, 'labelsTs')
os.makedirs(mask_save_dir, exist_ok=True)
save_path = os.path.join(mask_save_dir, f'{case_name}.nii.gz')
sitk.WriteImage(nifti_mask, save_path)
def create_dataset_json(output_dir, task_name, task_id, train_cases, test_cases, modalities):
"""
创建dataset.json文件,这是nnUNet的“数据说明书”。
"""
info = {
"name": task_name,
"description": f"2D medical image segmentation task: {task_name}",
"reference": "Your institution or paper",
"licence": "CC-BY-SA 4.0",
"release": "1.0",
"tensorImageSize": "3D", # 虽然是2D数据,但存储为3D格式
"modality": modalities, # 例如:对于RGB图像,就是 {"0": "R", "1": "G", "2": "B"}
"labels": {
"0": "background",
"1": "target" # 如果你的分割目标只有一类
},
"numTraining": len(train_cases),
"numTest": len(test_cases),
"training": [],
"test": []
}
# 填充训练数据路径
for case in train_cases:
# 注意:这里只写case名,不包含模态后缀和文件扩展名
# nnUNet会自动寻找 `case_0000.nii.gz`, `case_0001.nii.gz` 等文件
info["training"].append({
"image": f"./imagesTr/{case}.nii.gz", # 路径是相对于dataset.json的
"label": f"./labelsTr/{case}.nii.gz"
})
# 填充测试数据路径(只有图像,没有标签)
for case in test_cases:
info["test"].append(f"./imagesTs/{case}.nii.gz")
json_path = os.path.join(output_dir, 'dataset.json')
with open(json_path, 'w') as f:
json.dump(info, f, indent=4)
print(f"Dataset JSON saved to: {json_path}")
if __name__ == "__main__":
# ====== 需要你修改的配置部分 ======
raw_image_dir = "/path/to/your_data/images" # 你的原始图像文件夹
raw_mask_dir = "/path/to/your_data/masks" # 你的原始掩膜文件夹
task_name = "BloodVessel" # 你的任务名称,如 Liver, Tumor
task_id = "001" # 任务ID,三位数,如 001, 002
train_ratio = 0.8 # 训练集比例
# =================================
# 步骤1:列出所有图像文件(假设图像和掩膜文件名相同)
all_images = [f for f in os.listdir(raw_image_dir) if f.endswith(('.png', '.jpg', '.tif'))]
# 移除文件扩展名,作为case名
all_cases = [os.path.splitext(f)[0] for f in all_images]
# 步骤2:划分数据集
train_cases, test_cases = split_dataset(all_cases, train_ratio)
print(f"Total cases: {len(all_cases)}, Train: {len(train_cases)}, Test: {len(test_cases)}")
# 步骤3:定义任务输出目录(符合nnUNet规范)
nnunet_task_dir = os.path.join(os.environ['nnUNet_raw_data_base'], f'Task{task_id}_{task_name}')
os.makedirs(nnunet_task_dir, exist_ok=True)
os.makedirs(os.path.join(nnunet_task_dir, 'imagesTr'), exist_ok=True)
os.makedirs(os.path.join(nnunet_task_dir, 'labelsTr'), exist_ok=True)
os.makedirs(os.path.join(nnunet_task_dir, 'imagesTs'), exist_ok=True)
# labelsTs可选,通常测试集标签不提供
# os.makedirs(os.path.join(nnunet_task_dir, 'labelsTs'), exist_ok=True)
# 步骤4:转换并保存训练集
print("Processing training set...")
for case in tqdm(train_cases):
img_path = os.path.join(raw_image_dir, case + '.png') # 根据你的图像格式修改
mask_path = os.path.join(raw_mask_dir, case + '.png')
convert_2d_to_3d_nifti(img_path, mask_path, nnunet_task_dir, case, is_train=True)
# 步骤5:转换并保存测试集
print("Processing test set...")
for case in tqdm(test_cases):
img_path = os.path.join(raw_image_dir, case + '.png')
# 测试集可能没有对应的mask,如果有,可以传入,但nnUNet预测时不需要
mask_path = os.path.join(raw_mask_dir, case + '.png') if os.path.exists(os.path.join(raw_mask_dir, case + '.png')) else None
convert_2d_to_3d_nifti(img_path, mask_path, nnunet_task_dir, case, is_train=False)
# 步骤6:创建dataset.json
# 定义模态:这里我们把RGB三个通道当作三个模态
modalities_dict = {"0": "Red_channel", "1": "Green_channel", "2": "Blue_channel"}
create_dataset_json(nnunet_task_dir, task_name, task_id, train_cases, test_cases, modalities_dict)
print(f"All done! Your nnUNet-ready data is in: {nnunet_task_dir}")
运行这个脚本后,你的Task001_BloodVessel文件夹里应该会有imagesTr, labelsTr, imagesTs三个子文件夹和一个dataset.json文件。打开dataset.json看一眼,确保里面的路径和数量都正确。这个文件是nnUNet了解你数据集的“地图”,千万不能出错。
4. 数据预处理与实验规划:让nnUNet为你“量身定做”方案
数据准备好之后,接下来就是让nnUNet来分析你的数据,并自动制定训练计划。这一步通过一个命令完成:
nnUNet_plan_and_preprocess -t 001 --verify_dataset_integrity
这里的-t 001指定了任务ID,就是你在数据准备时用的Task001_BloodVessel中的001。这个命令会做以下几件重要的事:
- 数据完整性检查 (
--verify_dataset_integrity):它会检查你的数据格式、dataset.json文件是否正确,图像和标签是否匹配。第一次运行时强烈建议加上这个参数,能帮你提前发现很多低级错误。 - 数据分析:nnUNet会读取你的所有训练图像,计算一些关键统计信息,比如图像尺寸的分布、像素强度的均值和方差、类别的比例(前景和背景的比例)等。
- 实验规划:基于上一步的分析结果,nnUNet会自动决定一系列超参数。这是它的核心魔法所在。例如:
- 网络架构:对于2D数据,它会决定使用2D U-Net。
- 预处理方案:决定是否进行归一化、用什么方式归一化。
- 训练策略:决定初始学习率、批次大小(batch size)、数据增强的种类和强度等。
- 目标图像尺寸:它会计算出一个适合你数据集中大多数图像的“目标尺寸”,训练时会将图像裁剪或填充到这个尺寸。
这个过程可能会花一些时间,取决于你数据集的多少。运行完成后,预处理后的数据会保存在之前环境变量nnUNet_preprocessed指定的目录下,你会看到一个Task001_BloodVessel的文件夹,里面包含了归一化、重采样后的数据,后续训练就直接用这里面的数据,速度会快很多。
一个你可能遇到的坑:2D数据标识问题。 在较早的nnUNet版本中,框架可能默认处理3D数据。对于我们的2D数据(实际上是z轴为1的3D数据),需要在代码中稍作修改,明确告诉框架这是2D任务。修改方法通常位于nnunet/preprocessing/sanity_checks.py文件的verify_dataset_integrity函数中,添加对2D数据的特殊处理逻辑。不过,在新版本的nnUNet中,这个区分可能已经更明确了。如果你在预处理时遇到关于图像维度的报错,可以去官方GitHub的Issue里搜索“2D”关键词,通常能找到解决方案。最稳妥的办法是,在运行规划命令时,留意终端的输出信息,看它是否正确地识别了你的数据为“2D”模式。
5. 模型训练:五折交叉验证与“开箱即用”的配置
预处理成功后,就可以开始训练了。nnUNet默认采用五折交叉验证的方式来训练和评估模型。这意味着它会自动把你的训练集分成5份,轮流用其中4份训练,1份验证,总共训练5个模型。这样做的好处是能充分利用有限的数据,并得到一个更稳健的性能评估。
训练命令的格式非常固定:
nnUNet_train CONFIGURATION TRAINER TASK_NAME_OR_ID FOLD [OTHER OPTIONS]
对于我们2D数据的例子,命令如下:
# 训练第0折
CUDA_VISIBLE_DEVICES=0 nnUNet_train 2d nnUNetTrainerV2 001 0
# 训练第1折
CUDA_VISIBLE_DEVICES=0 nnUNet_train 2d nnUNetTrainerV2 001 1
# 训练第2折
CUDA_VISIBLE_DEVICES=0 nnUNet_train 2d nnUNetTrainerV2 001 2
# 训练第3折
CUDA_VISIBLE_DEVICES=0 nnUNet_train 2d nnUNetTrainerV2 001 3
# 训练第4折
CUDA_VISIBLE_DEVICES=0 nnUNet_train 2d nnUNetTrainerV2 001 4
CONFIGURATION: 这里填2d,表示使用2D U-Net配置。如果是3D数据,则可能是3d_fullres。TRAINER: 这里填nnUNetTrainerV2,这是最常用的训练器。TASK_NAME_OR_ID: 填任务ID001或任务全名Task001_BloodVessel都可以。FOLD: 指定训练哪一折,从0到4。CUDA_VISIBLE_DEVICES=0: 指定使用哪块GPU,如果你有多块GPU,可以分配不同的折给不同的GPU以加速。
关于显存不足的解决方案: 如果训练时出现GPU内存不足(OOM)的错误,最直接有效的方法是减小批次大小。你可以在训练命令后添加参数 -bs 或 --batch_size。例如:
CUDA_VISIBLE_DEVICES=0 nnUNet_train 2d nnUNetTrainerV2 001 0 --batch_size=8
默认的批次大小可能是24或更大,你可以尝试逐步减小到16、8、4甚至2。批次大小减小可能会影响训练稳定性,但nnUNet的训练策略通常比较鲁棒,只要不是太小(比如1),一般都能训练出可用的模型。
训练开始后,你会在终端看到损失(loss)和验证集指标(如Dice系数)的变化。每个折的训练都会在RESULTS_FOLDER目录下生成一个独立的文件夹,里面包含了训练过程中的检查点(checkpoint)、最终模型、训练日志和验证结果的可视化图片。你可以定期查看这些图片,直观感受模型在验证集上的分割效果是否在变好。
6. 模型推理与结果后处理:从NIfTI回到你的世界
五折模型都训练完成后,我们首先需要找出哪个模型配置(或者说,哪一折的模型)在验证集上表现最好。nnUNet提供了一个很方便的命令来做模型集成和选择:
nnUNet_find_best_configuration -m 2d -t 001
这个命令会分析五折交叉验证的结果,并推荐一个用于最终预测的最佳方案。它可能会推荐使用单模型(比如第3折的模型),也可能推荐使用模型集成(比如将5个模型的预测结果取平均)。命令运行后,会在RESULTS_FOLDER/nnUNet/ensembles/Task001_BloodVessel目录下生成一个文本文件(例如ensemble_2d__nnUNetTrainerV2__nnUNetPlansv2.1--fold_0_1_2_3_4.txt),里面就包含了推荐的最佳预测命令。
现在,我们可以用这个最佳配置来对真正的测试集(imagesTs里的数据)进行预测了。假设我们直接使用集成模型进行预测,命令如下:
nnUNet_predict -i /path/to/your/test_images/ -o /path/to/save/predictions/ -t 001 -m 2d --save_npz
-i: 指定测试集图像所在的文件夹。这个文件夹里应该只包含.nii.gz文件,且命名格式符合要求(case_XXXX.nii.gz)。-o: 指定预测结果的输出文件夹。-t: 任务ID。-m: 模型配置,这里是2d。--save_npz: 这个参数可选,它会额外保存一个包含预测概率的.npz文件,方便你后续进行阈值调整或其他后处理。
预测完成后,输出文件夹里会生成一系列.nii.gz文件,每个文件对应一个测试案例的分割结果。但是,这些结果是3D NIfTI格式(虽然z轴是1),而我们的原始数据是2D图片。所以我们需要一个“逆转换”过程,把它们变回我们熟悉的2D格式(如PNG),并计算评估指标。
import os
import numpy as np
import SimpleITK as sitk
import cv2
from tqdm import tqdm
from sklearn.metrics import f1_score, jaccard_score
def convert_nifti_to_2d_images(pred_nifti_dir, output_image_dir, threshold=0.5):
"""
将预测的.nii.gz文件(概率图或分割图)转换为2D图像。
"""
os.makedirs(output_image_dir, exist_ok=True)
pred_files = [f for f in os.listdir(pred_nifti_dir) if f.endswith('.nii.gz')]
for pred_file in tqdm(pred_files, desc="Converting predictions"):
pred_path = os.path.join(pred_nifti_dir, pred_file)
pred_img = sitk.ReadImage(pred_path)
pred_array = sitk.GetArrayFromImage(pred_img) # 形状: (1, H, W)
# 假设pred_array是概率图 [0,1],根据阈值二值化
# 如果nnUNet直接输出的是分割标签(0,1,2...),则无需此步
binary_mask = (pred_array[0] > threshold).astype(np.uint8) * 255
output_name = os.path.splitext(os.path.splitext(pred_file)[0])[0] + '.png'
output_path = os.path.join(output_image_dir, output_name)
cv2.imwrite(output_path, binary_mask)
def evaluate_predictions(pred_image_dir, ground_truth_dir):
"""
对比预测图像和真实标签,计算Dice系数和Jaccard指数(IoU)。
假设预测和标签都是二值PNG图像(0和255)。
"""
pred_images = sorted([f for f in os.listdir(pred_image_dir) if f.endswith('.png')])
gt_images = sorted([f for f in os.listdir(ground_truth_dir) if f.endswith('.png')])
dice_scores = []
iou_scores = []
for pred_name, gt_name in tqdm(zip(pred_images, gt_images), desc="Evaluating", total=len(pred_images)):
pred_path = os.path.join(pred_image_dir, pred_name)
gt_path = os.path.join(ground_truth_dir, gt_name)
pred = cv2.imread(pred_path, cv2.IMREAD_GRAYSCALE)
gt = cv2.imread(gt_path, cv2.IMREAD_GRAYSCALE)
# 确保尺寸一致
if pred.shape != gt.shape:
pred = cv2.resize(pred, (gt.shape[1], gt.shape[0]))
# 二值化 (0, 255) -> (0, 1)
pred_bin = (pred > 127).astype(np.uint8).flatten()
gt_bin = (gt > 127).astype(np.uint8).flatten()
# 计算Dice和IoU
dice = f1_score(gt_bin, pred_bin, zero_division=1)
iou = jaccard_score(gt_bin, pred_bin, zero_division=1)
dice_scores.append(dice)
iou_scores.append(iou)
mean_dice = np.mean(dice_scores)
mean_iou = np.mean(iou_scores)
print(f"Evaluation on {len(dice_scores)} images:")
print(f" Mean Dice Coefficient: {mean_dice:.4f}")
print(f" Mean IoU (Jaccard): {mean_iou:.4f}")
return mean_dice, mean_iou
if __name__ == "__main__":
# 配置路径
prediction_nifti_dir = "/path/to/nnUNet/predictions/" # nnUNet_predict命令的输出目录
output_2d_mask_dir = "./converted_2d_masks"
ground_truth_dir = "/path/to/your/original/masks_for_test_set/" # 测试集对应的真实标签
# 步骤1:转换格式
convert_nifti_to_2d_images(prediction_nifti_dir, output_2d_mask_dir, threshold=0.5)
# 步骤2:评估(如果你有测试集标签的话)
if os.path.exists(ground_truth_dir):
evaluate_predictions(output_2d_mask_dir, ground_truth_dir)
else:
print("Ground truth directory not found. Skipping evaluation.")
运行这个脚本,你就能得到可视化的2D分割结果和定量的性能评估。Dice系数是医学图像分割中最常用的评估指标,越接近1越好。第一次看到自己跑出来的模型能达到0.8甚至0.9以上的Dice时,那种成就感是非常棒的。
7. 避坑指南与进阶技巧:我踩过的雷,你别再踩
走完整个流程,你可能已经成功跑通了第一个nnUNet项目。但在实际应用中,总会遇到一些意想不到的问题。这里分享几个我踩过的“坑”和对应的解决办法。
坑1:数据格式转换出错。 最常见的问题是2D转3D NIfTI时,图像和标签的尺寸对不上,或者标签的值不是0和1。务必在转换脚本中加入严格的检查,比如打印图像的形状、标签的唯一值。确保标签是整数类型,且背景为0,目标为1(如果是多类,则依次为1,2,3...)。
坑2:预处理阶段内存爆炸。 如果你的图像非常大(比如超高分辨率的病理切片),预处理时可能会占用大量内存。可以尝试在nnUNet_plan_and_preprocess命令中增加-overwrite_plans_name参数,使用一个自定义的、目标尺寸更小的计划文件,或者手动修改生成的计划文件(plans文件),降低patch size。
坑3:训练过程Dice不上升或波动大。 首先检查数据是否真的有问题,比如图像和标签是否错位。其次,查看训练日志里的损失曲线,如果损失在稳步下降但Dice不升,可能是评估频率设置的问题,或者类别极度不平衡。对于不平衡数据,nnUNet默认使用的损失函数(Dice+交叉熵)通常能处理,但如果问题严重,你可以尝试在训练命令后添加-loss参数指定其他损失函数(需要修改源代码支持)。
坑4:预测结果全是背景或全是前景。 这通常是训练失败的表现。原因可能是学习率太大、数据量太少、或者标签信息有误。回退到数据检查步骤,用ITK-SNAP或SimpleITK在Python里可视化几对训练图像和标签,确保它们看起来是合理的。也可以尝试用更小的学习率(在训练器参数中调整)重新训练一折看看。
进阶技巧:使用预训练模型或进行迁移学习。 nnUNet本身不直接提供预训练模型,因为它的设计理念是针对每个数据集自动设计网络。但你可以将一个在类似任务上训练好的nnUNet模型(RESULTS_FOLDER下的整个任务文件夹)复制过来,然后使用--continue_training或-pretrained_weights参数(具体取决于版本)来初始化网络权重,进行微调(fine-tuning)。这对于数据量特别小的任务可能会有帮助。
最后,也是最重要的建议:多看日志,善用调试。 nnUNet的运行会生成非常详细的日志文件,位于每个训练折的文件夹里。遇到任何错误,第一反应应该是打开终端输出的错误信息,或者查看最新的日志文件。百分之九十的问题都能通过错误信息找到线索。如果还是解决不了,去nnUNet的GitHub仓库搜索相关的Issue,你遇到的问题很可能别人已经遇到并解决了。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)