做医学图像分割的同行应该都有这种体会:不管拿到的是CT、MRI,还是病理切片、超声图像,如果要上深度学习,第一个想到的网络结构八九不离十就是U-Net。这个2015年提出的架构,到今天快十年了,依然是医学分割任务里最稳的baseline,甚至大量论文里的"改进方案"也只是在它的骨架上做局部的加减法。我自己这两年前前后后用U-Net处理过肝脏、肺叶、视网膜血管、脑肿瘤好几个任务,最大的感受是:这个网络入门门槛确实低,但想真正用好,把预处理、损失函数、训练策略这些东西吃透,还是有不少细节值得认真捋一遍。
这篇文章不打算写教科书式的网络介绍,而是按我实际做项目的顺序来:先讲清楚为什么医学分割默认用U-Net,再把网络结构一层层拆开看它在做什么,接着是数据、损失函数、训练和排查问题的方法,最后聊聊基于U-Net的那些改进到底值不值得换。目标是给准备入坑医学影像分割的同学一份能直接照着做的实践指南。
1. 为什么医学图像分割默认首选U-Net:任务本质决定架构选择
1.1 医学影像和自然图像分割的本质差异
很多人一开始不理解,为什么做自动驾驶、做遥感图像分割的人天天用各种新架构,医学影像这边却好像总绕不开U-Net。答案其实不在网络本身,而在任务上。
自然图像分割,模型可以依赖颜色、纹理、阴影这些非常丰富的低层线索。猫和背景之间的边界通常有明确的颜色跳变,模型学起来相对容易。但医学影像不是这样:CT、MRI、超声本质上都是灰度图,对比度低,噪声多,器官和周围组织的灰度值可能非常接近,边界往往是"模糊的解剖学边界"而不是清晰的像素跳变。再加上不同设备、不同扫描参数出来的图像差异很大,同一个器官在不同病人身上的位置、大小、形态都有浮动。
这种情况下,模型不能只靠"看见什么"来判断,还得依赖一种类似解剖学先验的东西——知道肝脏大概在哪个区域、肺的轮廓大致是什么形状。U-Net这种"编码器压缩语义、解码器还原细节、跳跃连接保留边界"的结构,恰好把这种需求翻译成了网络设计。它不是为了刷榜设计出来的,而是为了在有限数据、有限标注、强先验的场景下稳定工作,天生就贴合医学图像的特点。
1.2 从FCN到U-Net:全卷积思想如何解决端到端问题
医学分割领域真正意义上的起点,其实是全卷积网络(FCN)。它把分类网络里的全连接层全部换成卷积层,让网络可以接受任意尺寸输入,直接输出像素级预测。这个思路很关键,但FCN有一个明显短板:经过层层池化之后,输出特征图分辨率太低了,直接上采样回来的分割结果非常粗糙,边界基本是糊的。
FCN也尝试过把浅层特征和深层特征融合,用的是逐元素相加(element-wise sum)的方式。U-Net在这一点上做了两个关键改动:一是把相加改成通道拼接(concat),浅层细节和深层语义各走各的通道,信息量不互相淹没;二是把结构做得完全对称,左边怎么压缩,右边就怎么还原,形成一个U形。
这两个改动带来的结果就是:U-Net在训练样本很少的情况下也能学得动,因为它的结构本身就自带"保边界"的归纳偏置,不需要模型用大量数据硬学出"边缘在哪"这件事。对于医学影像这种动辄只有几十例、一两百例标注数据的场景,这一点比什么都重要。这也是为什么哪怕后来出现了更强的Transformer结构,很多实际项目依然是"先跑一个U-Net再看结果"。
2. U-Net结构逐层拆解:编码器压缩了什么,解码器恢复了什么,跳跃连接又在传递什么
2.1 收缩路径:不断"丢空间、保语义"
U-Net左边这条收缩路径,做的事情可以概括为一句话:逐层把分辨率降下来,把通道数升上去。以经典配置为例,输入一张单通道的图像,第一层用两次3×3卷积把通道变成32,然后接一个2×2最大池化,分辨率减半;第二层通道变成64,再接池化;之后是128、256,最底部的瓶颈层做到512。
这里有一个很容易被忽略的设计细节:为什么要用两个3×3卷积堆叠,而不是直接用一个5×5或者7×7的大卷积?因为两层3×3卷积叠加,感受野等效于一层5×5卷积,但参数量只有后者的18/25左右,而且中间多了一次非线性激活,表达能力反而更强。这个设计后来成了几乎所有CNN的标配,但U-Net在2015年就用上了,说明作者对计算效率和表达能力的平衡想得很清楚。
收缩路径每经过一次池化,空间细节都在丢失,但语义信息在增强。第一层输出的特征图里还保留着精细的边缘、纹理,到了底层输出的时候,特征图已经变成了"这片区域大概是什么器官"的高层语义响应。网络正是靠这种逐层抽象,把"像素值"翻译成"解剖结构"。
2.2 扩展路径和跳跃连接:语义怎么翻译回像素
右边扩展路径正好反过来:逐步上采样,把低分辨率的语义特征图恢复到原始输入尺寸,同时通道数逐层减半。每一步上采样之后,会把左边对应层"裁剪后"的特征图拿过来,在通道维度上拼接在一起,然后再做两次3×3卷积。
这个拼接操作就是U-Net最核心的设计——跳跃连接(skip connection)。为什么要拼接而不是相加?因为相加是把两路信息压成一个向量,如果两路信息的分布差异很大,互相会干扰;而拼接是各留各的通道,让卷积层自己去学"哪些浅层细节有用、哪些和深层语义冲突"。比如肝脏边缘的灰度渐变信息在浅层特征里很丰富,模型可以通过卷积权重决定:边缘附近多参考浅层特征,器官内部多参考深层语义。这种灵活性是相加做不到的。
最后用一次1×1卷积把通道数映射到类别数。如果是二分类任务,输出就是单通道的logits,接一个sigmoid得到每个像素属于前景的概率;多分类任务则输出多通道,接softmax。
2.3 一个可直接套用的结构配置实例
我用得最多的2D U-Net配置大概是这样的:
Encoder: Block 1: Conv3x3(1->32) x2 + MaxPool # 输入256x256,输出128x128 Block 2: Conv3x3(32->64) x2 + MaxPool # 64x64 Block 3: Conv3x3(64->128) x2 + MaxPool # 32x32 Block 4: Conv3x3(128->256) x2 + MaxPool # 16x16 Bottleneck: Conv3x3(256->512) x2 # 16x16 Decoder: Up 1: UpSample -> Concat(block4) -> Conv3x3(512->256) x2 Up 2: UpSample -> Concat(block3) -> Conv3x3(256->128) x2 Up 3: UpSample -> Concat(block2) -> Conv3x3(128->64) x2 Up 4: UpSample -> Concat(block1) -> Conv3x3(64->32) x2 Output: Conv1x1(32->1)这个配置的参数量只有不到2000万,单卡训练完全没压力。很多实际项目里,瓶颈层未必需要到512,数据量小的话256就够。通道数翻倍设计可以按需缩放,不必死守原论文的比例。
3. 医学影像数据的预处理:处理不好,网络结构再好也白搭
3.1 先解决空间分辨率问题:重采样和裁块
医学影像和自然图像最大的不同在于,像素值本身是有物理意义的。CT图像里每个像素对应的是空间中的一个体素,而不同设备的层厚、像素间距可能完全不同。CT的层厚从0.5mm到5mm都有,MRI的体素更是不规则。如果不管这些直接喂网络,同一个器官在不同病人身上呈现出来的大小、形状就会因为采集参数不同而失真。
所以在预处理阶段,通常要把所有数据重采样到一致的空间分辨率。比如CT做器官分割,常见做法是先插值到各向同性分辨率,1×1×1mm或者1.5×1.5×1.5mm。图像用三次插值或线性插值,标签必须用最近邻插值,这一点很重要——标签是离散的类别编号,如果用线性插值,会插出"0.3个肝脏"这种不存在的值,训练的时候直接出问题。
重采样完之后,还有一个显存问题。很多情况下整张CT是512×512×几百层,GPU根本装不下。实际做法是切patch训练:2D网络就随机裁256×256或512×512的小块,3D网络就裁128×128×64这种块。注意裁块的时候要在有前景的区域和背景区域之间做平衡,不能所有patch都来自背景,否则模型学不到前景长什么样。
3.2 数值范围处理:窗宽窗位和z-score不能混用
这是医学图像预处理里最容易踩坑的地方。CT图像的像素值对应一个物理量叫亨氏单位(HU),不同组织有固定的取值范围:空气约-1000,水约0,骨骼可以达到几百甚至上千。实际显示和训练的时候,不可能直接用原始值,因为范围太大,大部分组织的信息会被压缩掉。
常规做法是"窗宽窗位"处理:根据目标器官设置一个灰度窗口,比如做腹部器官分割,常用窗宽400、窗位40,把[-200, 200]范围内的值映射到[0, 1],窗口外的值直接截断。这个操作能极大提高软组织之间的对比度,对分割效果的影响甚至比换网络结构都大。
但MRI就完全不一样了。MRI图像的数值没有统一物理单位,同一个序列在不同设备、不同病人之间扫描出来的信号强度差异很大,不能像CT那样用固定窗口。常规做法是对每一卷数据单独做z-score归一化:减去均值除以标准差。ETL这种做法能消除大部分扫描间差异,让网络不会因为整体亮度不同就判断错误。
3.3 数据增强的"安全区"和多模态注意事项
医学图像数据量普遍小,数据增强几乎是必须的。我常规会做:随机水平翻转(注意肝脏、心脏这种有左右不对称结构的要慎重)、小角度旋转(±15度以内)、随机缩放(0.9到1.1倍)、随机亮度对比度扰动、弹性形变。弹性形变对医学图像尤其有效,因为器官本身的形态就有很大的个体差异,模拟这种差异能明显提升泛化能力。
但是要特别小心两类增强:一是超过30度的旋转,对方向性很强的解剖结构(比如视网膜血管的视盘位置、膝关节的骨骼走向)会破坏语义;二是剪切变换,做不好会引入不自然的几何畸变。我做视网膜分割项目时曾经用过随机90度旋转做增强,结果发现模型学会了"旋转不变"但忽略了血管的解剖走向,验证集Dice反而掉了2个点,去掉之后才恢复正常。
增强的幅度要跟任务本身匹配,不能光顾着增加数据多样性。医学图像分割和自然图像分割的差别就在这里:自然图像旋转90度仍然是"一只猫",但医学图像旋转90度可能就不再是"那个位置的肝脏"了。
4. 损失函数怎么选:交叉熵、Dice Loss、Focal Loss的适用场景
4.1 为什么交叉熵在医学分割里经常不够用
很多刚入门的人默认用交叉熵(Cross Entropy)作为分割损失函数,跑出来的结果往往是"预测全是背景"。原因很简单:医学图像里前景目标通常只占整张图像的很小比例。比如肝脏分割,肝脏区域可能只占整张CT切片的5%,肿瘤可能只占0.5%。在这种极端类别不平衡下,交叉熵让模型最优策略变成"把所有像素都预测为背景",因为这样损失就已经很小了。
交叉熵并非完全不能用,但纯用交叉熵来训医学分割模型,大概率会遇到上面说的"全预测背景"问题。除非你对每个类别的像素做个权重调整,或者用带类别权重的版本,否则不建议作为首选。
4.2 Dice Loss的数学直觉和陷阱
医学分割领域最常用的Dice Loss,思路和交叉熵完全不同。它直接优化的是预测结果和标注之间的区域重叠程度,公式就是1减去Dice系数:
Dice = 2 × |预测∩标注| / (|预测| + |标注|)
这个公式的含义很直观:两个区域重叠越多,Dice越接近1,损失越接近0。因为Dice Loss天然以"区域重叠"为优化目标,所以对类别不平衡不敏感,前景只占5%也不影响它优化这个5%的重叠质量。这也是它成为医学分割默认损失函数的核心原因。
但Dice Loss有两个坑:
第一个坑是梯度不稳定。Dice系数的分母包含预测值和标注值的累加,在训练初期预测值接近0或接近1的区域,梯度的数值可能会非常大,导致损失震荡甚至NaN。解决办法有两个:一是给分子分母都加一个平滑项(smooth,比如1.0);二是不用纯Dice Loss,而是把交叉熵和Dice混合起来用,让交叉熵提供稳定的梯度引导,Dice负责优化区域重叠。
第二个坑是空标签问题。如果一个训练patch里正好没有前景标注,Dice计算时标注项为0,公式就变成0/0,直接NaN。我的处理方式有两种:训练时过滤掉完全没有前景的patch,或者在后处理代码里给Dice加一个保护逻辑,当标注为空时该项损失直接置为0。
实际工程里最稳的组合是BCE + Dice,让二者共同优化,效果比单独用任何一个都好。下面是Dice Loss的一个简化实现,可以作为参考:
import torch import torch.nn.functional as F def dice_loss(pred, target, smooth=1.0): # pred: Bx1xHxW 的 logits,target: Bx1xHxW 的 0/1 标签 pred = torch.sigmoid(pred).flatten() target = target.flatten() intersection = (pred * target).sum() dice = (2.0 * intersection + smooth) / (pred.sum() + target.sum() + smooth) return 1.0 - dice这个实现里将pred和target都flatten成向量计算,效率高、逻辑清晰,实际项目里可以直接用。
4.3 Focal Loss和Tversky Loss什么时候换上去
Dice Loss解决了类别不平衡,但它对"难样本"和"易样本"一视同仁,没有聚焦机制。如果你的任务里前景占比极端低(比如肿瘤区域只有0.1%),或者存在大量难分类的边界像素,可以试试Focal Loss。它给容易分类的样本加一个低权重,让模型把注意力集中到难分类的像素上,公式核心是给交叉熵乘一个调制因子(1-p_t)^γ,γ一般取2。
Tversky Loss则是Dice Loss的推广版本。Dice Loss其实等价于把假阴性和假阳性的权重设为相等(0.5比0.5),而Tversky Loss允许你调整这两个权重。比如做肿瘤筛查,漏诊(假阴性)的代价远高于误报(假阳性),那就把假阴性的惩罚权重调高,比如设为0.7比0.3,模型就会更倾向于把边界扩大、尽量多召回前景。
我个人的经验是:常规器官分割用BCE+Dice就够,不需要花太多时间纠结损失函数;只有遇到极端不平衡(肿瘤/病灶分割)或者临床对召回率有硬性要求时,才值得用Focal Loss或者Tversky Loss,并且通常需要做一轮网格搜索来确定权重参数。
5. 训练阶段的关键细节:从优化器到显存管理
5.1 优化器、学习率与训练策略
优化器我基本固定用AdamW,初始学习率3e-4,配合Warmup和Cosine退火。Warmup前几个epoch让学习率从0逐步爬到设定值,可以有效避免训练初期因为梯度过大导致loss爆炸;Cosine退火让学习率在训练后期缓慢降到一个极小值,比固定学习率更稳定,收敛结果普遍更好。
Batch size在显存允许的情况下尽量大一点。医学图像分割里,同一批数据的前景占比差异可能非常大,batch size小的话,有些batch可能几乎没有前景像素,梯度质量很差。如果一张卡只能放下batch size为4,我建议用梯度累积,等效batch size做到8甚至16。这个方法几乎没有额外成本,只是多存几次梯度,但训练稳定性提升非常明显。
5.2 混合精度、梯度累积与大图的patch训练
混合精度(AMP)在现在的GPU上已经是标配了,显存能省接近一半,训练速度也能提升不少。但有一个容易被忽视的细节:Dice Loss这类涉及累加统计量的损失函数,如果在float16下计算,很容易因为精度不足出现梯度异常。我的做法是模型前向推理和反向传播用AMP,Dice Loss的计算单独转回float32再做,这样既省显存又不牺牲损失函数的稳定性。
patch训练是处理大尺寸医学影像的核心方案。2D网络一般输入512×512,3D网络输入常见的是128×128×64;推理时为了覆盖整个图像,还需要在重叠滑窗(sliding window)推理。滑窗推理时窗口之间会有重叠区域,通常的做法是对重叠区域取平均或者取最大值,最后再做一次整体归一化。这个细节如果不处理好,最终结果会出现明显的窗口拼接痕迹。
还有一个我后来才发现的点:标签的稀疏程度会影响patch策略。有些标注文件里,前景区域只集中在很小的角落,随机裁patch很容易裁不到。应对方案是训练时以一定概率(比如50%)在前景区域附近中心采样,其余时间从全图随机采样,这样能保证每个epoch都能看到足够多的前景像素。
5.3 数据划分:按患者划分,避免切片串扰
医学影像数据划分有个和自然图像完全不同的规则:必须按患者划分,不能按切片划分。因为同一个病人的几百张切片高度相关,如果训练集和验证集来自同一个病人,模型实际上是在记住这个病人的解剖结构,验证指标会虚高得离谱。
我在一个肺叶分割项目上就吃过这个亏:第一次划分时没注意,把同一个病人的切片散到了训练集和验证集里,验证Dice达到0.97,换成正儿八经按患者划分之后直接掉到0.92。数据量小的话,建议直接做五折交叉验证,每个fold都报告Dice均值±标准差,这比单次划分的结果可靠得多。
6. 踩坑实录:训练过程中最常见的四个问题与排查链路
6.1 损失降了,Dice却不涨
这是最让人头痛的情况:训练损失在稳步下降,但验证集上的Dice一直不涨甚至下跌。排查链路一般是这样走的:
首先检查验证集划分有没有问题,确认不同患者的切片没有混入同一个集合;然后看训练集和验证集的图像分布差异,如果训练集都是同一家医院的设备、验证集是另一家医院的,出现分布偏移很正常,这时候要加大数据增强;最后检查是不是过拟合,医学数据量小,训练十几个epoch之后过拟合非常常见,提前停(early stopping)配合更激进的增强一般能改善。
还有一个可能被忽略的原因:验证集的索引预测结果保存成了logits没有做sigmoid,导致指标始终异常。这种低级错误在项目里反复出现过,建议在计算指标前随手打印一次预测值的数值范围,看看分布是否合理。
6.2 预测结果全黑或全白
模型训练完,推理出来全是黑的或者全是白的,这个场景很多新手都遇到过。先排除一个最常见的低级错误:模型输出的是logits,直接拿它当概率用,没有做sigmoid。logits的取值范围是负无穷到正无穷,画出来自然大部分区域都接近全黑或者全白。
如果确认做了sigmoid还是不对,接着检查标签和输出的通道顺序。多分类模型里,如果通道顺序搞反,背景通道被当成前景输出,看起来也是全白的。再就是看模型的最后一层有没有被改动过——我曾经在一个加载预训练权重的项目里,把输出通道数从10改成1,忘了改最后一层卷积的初始化方式,导致输出始终是一个常数,看起来就是全黑。
排查这类问题,最快的路径是拿一个已知的小样本数据集跑前向,把模型各层的输出尺寸打印出来,逐步定位是网络结构、数据读取还是后处理的问题。
6.3 Dice Loss变成NaN
训练到一半loss变成NaN,是Dice Loss最常见的故障。最典型的原因是某个训练patch的标签恰好是全背景,Dice计算出现0/0。解决方案上面说过:要么过滤掉全背景patch,要么加平滑项,要么判断标签为空时直接跳过该patch的Dice损失。
另一个原因是混合精度下Dice累加出现精度溢出,尤其是前景像素极多、累加和特别大的时候。把Dice计算转到float32之后,这个问题通常就消失了。最后检查一遍学习率,如果初始学习率到1e-3以上,AdamW在医学分割这种小数据场景下非常容易炸。
6.4 小目标总是漏检
如果任务是分割微小病灶(比如肺结节、血管细小分支),Dice Loss的表现往往不好。因为Dice的本质是集合重叠,小目标哪怕只漏掉几个像素,Dice也会掉得很快,模型训练时的梯度容易被大面积背景带动。
解决思路有三个方向:一是把采样策略改为"小目标周围密集采样",确保训练时模型反复看到包含小目标的patch;二是把尺度问题交给多尺度方法,比如在解码器同时输出1/2、1/4分辨率的预测并加权监督;三是换Tversky Loss,把假阴性的权重调高,逼模型多预测一些像素,宁可边界大一点也不要漏掉。实测下来,第一个方向往往见效最快,代价也最小。
7. 评估指标与后处理:让分割结果真正能交付
7.1 Dice和IoU的关系与局限
Dice是医学分割论文里最常见的指标,但它有一个值得注意的局限性:对小目标特别敏感。假设黄金标准是100个像素的小病灶,预测到了80个像素,Dice能达到0.89,看起来不错;但如果漏掉了一半,Dice直接掉到0.67。所以在小目标任务里,Dice的方差会非常大,单次验证可能有很大波动。
IoU(交并比)和Dice是单调相关的,IoU = Dice / (2 - Dice),但IoU对重叠区域的要求更严格,数字普遍比Dice要低几个点。建议在项目报告里两个指标都看,Dice看整体区域重叠质量,IoU看更严格的空间拟合程度。
| 指标 | 公式 | 衡量重点 | 适合场景 |
|---|---|---|---|
| Dice | 2×交集/(并集+交集) | 区域重叠比例 | 常规分割任务 |
| IoU | 交集/并集 | 严格的空间拟合 | 目标检测和分割通用 |
| HD95 | 边界点距离的第95百分位 | 边界最大偏差 | 放疗、手术规划 |
| VS | 体积差异百分比 | 整体体积一致性 | 体积测量类任务 |
7.2 Hausdorff距离:为什么Dice高不等于边界好
医学分割项目里,有一个比Dice更"临床"的指标,叫Hausdorff距离(HD),衡量的是预测边界和标注边界之间的最大距离。Dice再高,如果边界某处凸出来或者凹进去一大块,HD会非常大,而这种大偏差对放疗计划、手术导航来说是完全不能接受的。
HD95(取距离分布的第95百分位而不是最大值)在论文和工程里更常用,因为它不容易被个别离群点干扰。如果Dice和HD95两个指标都好,说明模型在区域层面和边界层面都拟合得不错;如果Dice高但HD95很差,那就要怀疑是不是模型把边界学得太"圆润"了,丢失了解剖结构上应有的突起和凹陷。
7.3 后处理三板斧:连通域、形态学、滑窗融合
分割模型输出之后,直接拿去用通常会遇到几个小问题:预测结果里有零散的噪声点、有一些与解剖结构不符的孤立区域、边界上有锯齿。常规的后处理就是三板斧:
第一,连通域分析。把预测结果按连通域拆成一个个独立区域,只保留面积大于某个阈值的区域,或者按任务要求保留面积最大的连通域。比如做肝脏分割,肝脏在单张切片上一定是最大的连通区域,直接取最大连通域能滤掉大部分假阳性。
第二,形态学处理。腐蚀、膨胀、开闭运算能抚平边界锯齿,消除微小空洞。注意操作核心参数不要太大,3×3的核就够了,否则会把真实的解剖细节也磨平。
第三,滑窗推理结果融合。重叠窗口预测出来的分割图,融合时对重叠区域取平均是最稳妥的;如果要追求更平滑的边界,可以用高斯加权,让窗口中心区域的预测权重更高。这个步骤对3D影像尤其重要,能显著减少Z方向上的不连续感。
8. U-Net的演进与选择建议:什么时候值得换新架构
8.1 那些"U-Net改进版"到底改进在哪里
围绕U-Net的改进多如牛毛,但核心方向无非几个:Attention U-Net在跳跃连接处加注意力门控,让解码器在融合时自动抑制背景区域的浅层特征;U-Net++用密集的嵌套跳跃连接替代原来的单次拼接,多级特征相互补充;nnU-Net则不走结构路线,而是把数据预处理、网络参数、训练策略做了一套自动配置框架,本质上是用工程化方法把"调参"这件事自动化。
对一个实际项目来说,与其纠结选哪个新结构,不如先想清楚真正的瓶颈在哪里。如果问题出在边界模糊上,加Attention改进空域特征选择是合理的;如果问题出在类别极度不均衡,换损失函数比换网络结构见效更快;如果问题出在数据量少、标注质量参差不齐,那所有网络结构都救不了,应该回到数据本身去。
8.2 Transformer和U-Net的混合结构:什么时候才值得用
TransUNet、Swin-UNet这类把Transformer和U-Net结合的结构,最近热度很高。它们用Transformer替代瓶颈层的卷积特征提取,让网络能建模长距离依赖关系,对保持大器官的全局一致性确实有效。但要注意,Transformer结构对数据量的要求比CNN高得多,医学影像这种百来例的数据规模,直接上很可能学不动,效果反而不如U-Net。
我个人的建议是:常规的器官分割、病灶分割任务,经典U-Net加合理的数据处理、损失函数和训练策略,已经完全够用;如果项目不差算力和数据量,可以先用nnU-Net自动配置跑一个结果作为上界参考,再决定要不要上更复杂的结构。千万不要一上来就堆最新架构,那通常是论文需要,而不是项目需要。
8.3 我的一点真实体会
这几年前前后后做过不少医学分割项目,最深的体会是:**数据质量和标注一致性对最终效果的影响,远大于网络结构的差别。**同样的U-Net,数据预处理做到位、损失函数选对、训练策略规范,和随便跑跑的人比,Dice差距可能超过10个点。很多团队花大量时间尝试新结构,却不舍得花时间检查标注里的错误切片,方向其实是反的。
如果让我给刚开始做这个方向的朋友一条最实用的建议,那就是:先用经典U-Net把完整pipeline跑通,拿到一个可复现的baseline,然后花一周时间把预处理、损失函数、增强策略、推理后处理这些环节逐个打磨一遍,大概率能提升到80%的最终效果。剩下的那20%,才轮到新结构和新花样的发挥空间。