资讯动态

BraTS 2018-2020实战总结:从3D U-Net到五折集成,脑肿瘤分割的调参避坑指南

发布时间:2026/10/16 22:12:40 来源:尧图企业网站定制
1. 项目缘起与整体思路拆解1.1 为什么选择BraTS作为主攻方向脑部肿瘤影像分割这个方向我在读研期间前前后后投入了将近两年时间BraTS 2018、2019、2020三届数据集我都完整跑过一遍。说实话一开始选这个方向并不是因为什么高大上的理由纯粹是因为它是医学影像分割领域少有的、标注质量足够高、评测体系足够规范、参赛队伍足够多的公开基准。对于想入门医学图像分割的人来说BraTS几乎是绕不开的一块试金石。BraTS全称Brain Tumor Segmentation Challenge是MICCAI会议下面的一个经典挑战赛。它提供的是多模态磁共振影像每个病例包含四个模态T1、T1ce、T2、FLAIR。标注区域分为三类增强肿瘤区ET、肿瘤核心区TC包含ET和坏死区NCR、以及整个肿瘤区WT包含TC和水肿区ED。这个三层嵌套的标注体系是BraTS最核心的设计也是后面所有模型结构、损失函数设计都要围绕的核心约束。我之所以把2018到2020三届放在一起总结是因为这三届之间的变化非常典型能完整反映出一个医学影像项目从“能跑通”到“跑得好”再到“稳定复现”的整个演进过程。2018年数据量相对小评测指标以Dice和Hausdorff距离为主2019年引入了更严格的验证流程和更多样的病例2020年则进一步扩大了数据规模并且对标注一致性做了更细致的处理。把这三届串起来看你会发现很多工程上的坑是重复出现的而模型结构上的创新反而没有想象中那么关键。1.2 整体技术路线的三次迭代我最初的做法非常朴素直接拿一个3D U-Net把四个模态堆成4通道输入输出3通道的softmax用Dice损失训练。这个方案在2018数据上能跑到WT的Dice大概0.85左右但ET的Dice只有0.65上下而且Hausdorff距离非常难看。问题出在两个地方一是ET区域本身很小类别极度不平衡二是3D卷积对显存要求太高batch size只能开到1或者2训练极不稳定。第二次迭代我做了三件事第一把损失函数换成Dice加交叉熵的加权组合并且对ET类别单独加权第二引入深度监督在解码器的多个层级都加辅助损失第三把patch size从128×128×128降到96×96×96同时用随机裁剪做数据增强。这一轮下来ET的Dice提到了0.72左右WT稳定在0.88。但新的问题出现了验证集和测试集的差距开始变大过拟合迹象明显。第三次迭代也就是2020年那一版我重点解决泛化问题。具体做法包括更激进的数据增强随机弹性形变、随机亮度对比度扰动、随机模态丢弃、在训练中动态调整patch采样策略保证每个batch里至少有一定比例的肿瘤区域、以及用五折交叉验证来做模型集成。最终在本地验证集上WT的Dice做到0.90TC做到0.82ET做到0.76Hausdorff距离也降到了可接受范围。这里我想强调一个观点BraTS这类任务模型结构带来的收益往往没有数据增强和训练策略带来的收益大。我见过太多人一上来就堆Transformer、堆注意力机制结果Dice还不如一个调好参数的3D U-Net。先把数据管线、损失函数、采样策略这三件事做扎实再考虑换 backbone。1.3 项目适合谁来参考这篇总结适合三类人第一类是想入门医学图像分割的学生或者转行者BraTS是一个非常好的练手项目数据干净、评测标准明确第二类是在做类似多模态分割任务的工程师BraTS里关于多模态融合、类别不平衡、小目标分割的经验可以直接迁移第三类是做模型部署或者工程化落地的同学BraTS的预处理、后处理、模型集成流程有很强的参考价值。需要提前说明的是我下面讲的所有内容都是基于我自己的实操经验参数和结论是在我当时的硬件条件下得到的你直接照搬不一定能复现同样的数字但思路和避坑点是通用的。2. 数据预处理与核心细节解析2.1 四个模态的物理含义与融合策略很多人做BraTS的时候直接把四个模态当四个通道塞进网络这当然可以跑但如果你不理解每个模态到底在表达什么后面做模态融合、做可解释性分析的时候就会很被动。T1模态主要反映解剖结构灰白质对比清晰但对肿瘤的敏感性一般。T1ce是打了造影剂之后的T1增强肿瘤区域会明显高亮这是区分ET区域最关键的模态。T2模态对水肿和脑脊液敏感水肿区域在T2上呈现高信号。FLAIR则是抑制了脑脊液信号的T2能更清晰地显示水肿边界和肿瘤浸润区域。我在做模态融合的时候试过三种方案早期融合直接堆通道、中期融合每个模态单独过几层再拼接、晚期融合每个模态单独出一个预测再投票。实测下来早期融合在数据量充足的时候效果最好中期融合在小数据量或者模态缺失的情况下更鲁棒晚期融合几乎没有优势因为四个模态之间的互补性太强了分开处理反而丢掉了跨模态的交互信息。一个容易被忽略的细节BraTS的四个模态是已经配准好的但不同病例之间的强度分布差异很大。如果你不做强度归一化直接送进网络模型会花大量精力去拟合强度分布而不是学习解剖结构。我的做法是对每个模态单独做z-score归一化只在前景区域非零区域内计算均值和标准差这样能避免大量背景零值拉偏统计量。2.2 数据增强的取舍与参数设置数据增强这块我踩过的坑最多。一开始我用了很猛的增强随机旋转、随机缩放、随机弹性形变全上结果训练loss降得很漂亮验证集Dice却上不去。后来我逐个做消融实验才发现问题出在弹性形变上——医学影像的解剖结构是有严格拓扑约束的过度的弹性形变会破坏肿瘤和周围组织的空间关系模型学到的是扭曲后的假模式。我最终保留的增强策略是这样的随机翻转三个轴各50%概率、随机旋转-15度到15度、随机缩放0.9到1.1、随机亮度对比度扰动幅度控制在0.1以内、随机模态丢弃每个模态10%概率置零模拟模态缺失。弹性形变我只在2019年之后的版本里保留了一个非常温和的版本形变幅度控制在2个像素以内。另外有一个技巧我想单独提一下随机patch采样。BraTS的病例里肿瘤区域占整个脑体积的比例很小如果你完全随机裁剪patch大部分patch里可能根本没有肿瘤。我的做法是维护两个采样池一个是有肿瘤的patch一个是无肿瘤的patch每个batch里保证至少60%的patch包含肿瘤区域。这个策略对ET这种小目标的提升非常明显我实测下来ET的Dice能提升3到5个百分点。2.3 预处理流程的标准化步骤我把整个预处理流程固化成了一个脚本每次换数据集或者换机器的时候直接跑一遍保证输入一致。具体步骤是这样的读取四个模态的NIfTI文件检查尺寸和仿射矩阵是否一致。对每个模态做前景区域内的z-score归一化。如果某个模态缺失用其余模态的均值填充并在输入中增加一个模态存在性标记通道。将四个模态堆叠成4通道的3D数组保存为numpy的npy格式方便后续快速读取。对标注做同样的空间变换确保图像和标注严格对齐。这里有一个坑我要特别提醒BraTS的标注里标签1对应NCR标签2对应ED标签4对应ET。很多人在做多分类的时候直接把标签当成0、1、2、3来处理结果类别映射错了训练出来的模型完全不对。正确的做法是先做标签重映射把1、2、4映射成0、1、2然后再做one-hot编码。还有一个细节BraTS的验证集标注是不公开的你只能通过官方在线评测来获取结果。所以本地验证一定要自己划分出一部分训练数据作为验证集并且要保证划分的随机性。我见过有人用固定的前20个病例做验证结果模型在验证集上表现很好在线评测却很差就是因为验证集和训练集分布不一致。3. 模型结构与训练策略实操3.1 3D U-Net的改进细节我的基础模型是3D U-Net但在原始结构上做了几处关键修改。第一把编码器的每个stage从两层卷积改成三层卷积并且加入了残差连接。这个改动让模型在深层能提取到更丰富的特征同时残差连接缓解了梯度消失的问题。第二解码器上采样之后用concatenation而不是addition来融合编码器的特征这样能保留更多的空间细节。第三在瓶颈层加入了一个轻量的注意力模块对通道维度做重加权让模型更关注肿瘤相关的特征通道。具体的网络配置是这样的输入patch大小96×96×96编码器通道数从32开始每个stage翻倍最深到256。解码器对称最终输出3通道的softmax。整个模型参数量大概在1200万左右在单张24G显存的卡上batch size可以开到4配合混合精度训练显存占用大概在18G左右。这里我想说一下为什么没有用更深的网络。我试过把编码器加深到6个stage通道数最大到512结果显存直接爆了batch size只能降到1训练极其不稳定最终Dice反而下降了。医学影像分割和自然图像分割不一样数据量小、解剖结构相对固定过深的网络很容易过拟合。我的经验是3到4个下采样stage足够了再深就是浪费算力。3.2 损失函数的设计与权重调优损失函数这块我做了很多实验最终定下来的方案是Dice损失加交叉熵损失并且对三个类别分别加权。具体公式是这样的def combined_loss(y_true, y_pred): dice_loss 1 - dice_coefficient(y_true, y_pred) ce_loss categorical_crossentropy(y_true, y_pred) # 类别权重WT, TC, ET class_weights [0.2, 0.3, 0.5] weighted_ce ce_loss * class_weights return dice_loss 0.5 * weighted_ce为什么ET的权重给到0.5因为ET区域最小在Dice损失里贡献的梯度最少如果不额外加权模型会倾向于忽略ET。但权重也不能给太大我试过给到1.0结果模型在ET上过拟合WT和TC的Dice明显下降。0.5是我在验证集上试出来的一个比较平衡的值。另外深度监督的辅助损失我加在了解码器的三个层级上权重分别是0.1、0.2、0.3主损失权重是1.0。辅助损失的作用是让浅层特征也具备一定的判别能力加速收敛。实测下来加了深度监督之后训练前期收敛速度大概快了30%最终精度也有1到2个百分点的提升。3.3 训练过程的监控与调参记录训练过程我一般会监控四个指标训练loss、验证loss、验证集Dice、以及学习率。学习率我用的是余弦退火初始值1e-4最小到1e-6每个epoch更新一次。优化器用AdamWweight decay设成1e-5。这里有一个经验BraTS训练的前20个epoch非常关键如果这20个epoch里验证Dice没有明显上升后面基本很难救回来。我一般会在第20个epoch的时候做一次评估如果WT的Dice低于0.80我就会停下来检查数据管线或者调整学习率。我遇到过好几次是因为数据增强太猛导致前20个epoch完全学不动把增强幅度降下来之后立刻就正常了。还有一个细节是模型权重的保存策略。我不用最好的验证Dice来保存模型而是用验证Dice的滑动平均值。因为单次评估的波动可能很大用滑动平均能避免保存到偶然表现好的模型。具体做法是维护一个长度为5的队列每次评估后把Dice加入队列取平均值只有平均值超过历史最佳才保存模型。训练过程中我还会定期可视化中间特征图看看模型到底在关注哪些区域。有一次我发现模型在ET区域上的注意力很弱检查后发现是ET的采样比例太低调整采样策略后问题就解决了。可视化这个习惯帮我省了很多盲目调参的时间。4. 后处理与模型集成实战4.1 连通域分析与小区域过滤模型输出的原始预测往往会有一些孤立的、很小的预测区域这些区域大概率是噪声。我的后处理第一步就是做连通域分析把小于一定体积的连通域去掉。具体阈值我是这样定的WT的最小体积设为100个体素TC设为50ET设为30。这些阈值是我在验证集上通过网格搜索找到的不同的数据集可能需要微调。但这里有一个坑ET区域本身就可能很小如果你把阈值设得太大会把真正的ET区域也过滤掉。我的做法是对ET单独处理先保留所有预测的ET区域然后只过滤掉那些与WT区域没有重叠的孤立ET预测。因为从解剖学上讲ET一定在WT内部如果预测出的ET和WT完全不重叠那这个ET预测基本可以认为是错的。4.2 五折交叉验证与模型集成单模型的性能始终有限我最终上线用的是五折交叉验证的集成模型。具体做法是把训练数据分成五份每次用其中四份训练剩下一份做验证这样得到五个模型。推理的时候五个模型分别对同一个病例做预测然后把五个概率图做平均再取argmax得到最终分割结果。这个方案听起来简单但实际做的时候有几个细节要注意。第一五个模型的结构和超参数要完全一致只有训练数据的划分不同这样才能保证集成的有效性。第二推理时的数据增强也要一致我一般会用TTA测试时增强对每个模型做8次不同翻转和旋转的预测再平均。第三集成后的概率图要做一次高斯平滑减少边界处的锯齿。实测下来五折集成相比单模型WT的Dice能提升1.5个百分点左右ET的Dice能提升2到3个百分点。提升幅度不算特别大但稳定性提升非常明显不同病例之间的方差变小了。4.3 推理速度优化与显存管理BraTS的推理阶段有一个很现实的问题一个病例的完整3D体积是240×240×155如果直接整卷推理显存根本不够。我的做法是滑窗推理窗口大小和训练时一致96×96×96步长设为48也就是50%的重叠。重叠区域用高斯权重做加权平均这样能避免拼接处的接缝。滑窗推理的速度优化我做了两件事第一用混合精度推理速度大概能提升40%第二把模型转成TorchScript或者ONNX减少Python层面的开销。经过优化之后一个病例的推理时间从原来的3分钟左右降到了1分钟以内五个模型集成加上TTA总时间大概在5到8分钟对于离线评测来说完全可以接受。显存管理还有一个技巧推理的时候不要一次性把所有窗口的中间特征都保留在显存里而是每算完一个窗口就把结果写到CPU内存最后统一做加权平均。这样显存占用只和单个窗口有关和病例大小无关。5. 常见问题与排查技巧实录5.1 训练不收敛的典型原因训练不收敛是BraTS项目里最常见的问题我总结下来主要有四个原因。第一学习率太大loss直接飞掉这时候把学习率降到1e-5再试。第二数据归一化没做对不同模态的强度范围差异太大模型学不动检查一下是不是做了前景区域的z-score。第三标签映射错了把1、2、4直接当成类别索引导致类别混乱重新做标签重映射。第四batch size太小梯度噪声太大如果显存不够可以用梯度累积来模拟大batch。5.2 验证集Dice远低于训练集Dice这个问题一般有两个原因过拟合或者数据泄露。过拟合的话增加数据增强、加dropout、加weight decay都能缓解。数据泄露的话检查一下验证集和训练集有没有重叠的病例或者预处理的时候有没有把验证集的信息泄露到训练集。我遇到过一次是因为在计算归一化参数的时候用了全部数据导致验证集的信息间接泄露到了训练过程。5.3 ET区域Dice始终上不去ET区域小、类别不平衡Dice上不去很正常。除了前面说的类别加权和采样策略还有一个技巧是两阶段训练第一阶段先训练一个WT的分割模型第二阶段在WT预测的区域内单独训练ET的分割模型。这样ET模型只需要关注WT内部区域搜索空间小了很多Dice通常能提升3到5个百分点。5.4 常见问题速查表问题现象可能原因排查方法解决方案训练loss不下降学习率过大、数据未归一化检查学习率和输入分布降低学习率、做前景z-score验证Dice波动大batch size太小、验证集太小增加batch size或验证集比例用梯度累积、扩大验证集ET Dice低类别不平衡、采样偏差统计ET体素占比类别加权、定向采样推理显存不足整卷推理、窗口太大检查推理窗口大小滑窗推理、混合精度在线评测分数低过拟合、后处理不当对比本地和在线结果加强正则、调整后处理阈值6. 工程化落地的几点个人体会6.1 代码结构的模块化设计做BraTS项目最大的教训就是一定要把代码模块化。我第一版代码把所有逻辑写在一个文件里后来想换损失函数、换数据增强改一处就要动全身。第二版我拆成了数据模块、模型模块、损失模块、训练模块、推理模块五个部分每个模块之间通过配置文件交互。这样我想做消融实验的时候只需要改配置文件里的一个参数不用动代码。配置文件我用的是YAML格式把数据路径、模型参数、训练超参数、增强参数全部写在一个文件里。每次实验保存一份配置文件这样后面复现的时候直接加载对应的配置就行。这个习惯帮我省了大量“这个参数当时设的多少来着”的时间。6.2 实验记录与版本管理医学影像项目的实验周期长参数组合多如果没有好的实验记录很快就会乱掉。我的做法是用一个简单的CSV文件记录每次实验的关键信息实验编号、日期、模型结构、损失函数、数据增强、验证Dice、备注。每次跑完实验就填一行后面分析的时候直接看表格就行。版本管理我用的是Git但只管理代码和配置文件不管理数据和模型权重。数据和权重单独存在一个目录里用实验编号命名。这样代码仓库很干净数据和权重的管理也很清晰。6.3 从BraTS迁移到其他任务的注意事项BraTS上验证有效的方案迁移到其他医学影像分割任务时有几个地方需要调整。第一模态数量和含义不同融合策略要重新设计。第二类别体系不同损失函数的类别权重需要重新调。第三数据规模不同如果目标数据集比BraTS小很多模型要相应缩小增强要更保守。第四评测指标不同有些任务用IoU而不是Dice后处理的阈值也要重新搜。我后来把BraTS的经验迁移到一个腹部器官分割任务上模型结构基本没变但数据增强幅度降了一半类别权重重新调了一轮最终效果也不错。这说明核心思路是通用的但具体参数一定要根据任务特点来调。6.4 一些零散但有用的实操技巧最后分享几个零散但很实用的技巧。第一训练的时候把验证集评估频率设成每5个epoch一次太频繁会拖慢训练速度太稀疏会错过最佳保存时机。第二模型权重的EMA指数移动平均对最终性能有稳定提升我一般设decay为0.999。第三推理的时候对概率图做一次温度缩放能让softmax输出更平滑边界更自然。第四如果显存实在不够可以用梯度检查点牺牲一点速度换显存。这些技巧单独看可能只提升零点几个百分点但累积起来对最终结果的影响还是很可观的。做医学影像分割就是这样大框架大家差不多最后拼的就是这些细节。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑