ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

半监督3D血管分割:动态协作网络降低标注成本的实战解析

半监督3D血管分割:动态协作网络降低标注成本的实战解析

在3D医学影像里做血管分割,我一直觉得是件“费力不讨好”的事:一张CT血管造影或者磁共振血管成像,动辄几百层,血管在三维空间里弯弯绕绕,细的地方只有几个体素宽,标注员要一层一层描,还要在冠状位、矢状位来回切换确认连通性。标一例完整的高质量数据,熟练工也得花上几个小时。所以只要真实项目接触过血管分割,就会对“半监督3D血管分割”这七个字特别敏感——这是个能直接降低标注成本、让模型吃上大量未标注数据的技术方向。这篇博文要拆解的项目标题是《Learning Dynamic Collaborative Network for Semi-supervised 3D Vessel Segmentation》,也就是“用于半监督3D血管分割的动态协作网络”。我先说结论:它解决的核心问题不是“网络多花哨”,而是“在标注数据极少、未标注数据管够的前提下,如何让模型稳定地利用无标签数据,并且不在血管末梢、分叉这些关键位置上翻车”。我会从设计动机、网络结构、损失函数、训练细节到踩坑实录,完整拆一遍,想去落地半监督分割方案的朋友可以直接参考。

1. 项目解读:为什么“半监督3D血管分割”是刚需

1.1 3D血管分割的特点与标注代价

血管分割本质上是三维体素级别的二分类问题:把图像里属于血管的体素标成前景,其余全部标成背景。听起来很简单,但实际做起来有两大麻烦。

第一是类不平衡极严重。在人体CT血管造影里,血管系统占整个体素的比例通常只有1%到3%,有的细小分支甚至不到0.5%。如果直接拿普通分割模型去训练,模型很容易偷懒,把所有体素都预测成背景,因为这样准确率已经超过97%。我见过不少刚入门的同学在第一次跑血管分割时,看到损失已经降到0.1以下,结果可视化一看,预测结果一片黑——这就是典型的类别失衡陷阱。

第二是血管的形态学特征太苛刻。血管是细长的管状结构,主干粗、末梢细,分叉多,还有大量弯曲。它不像器官分割那样“一团一团的”,可以容忍边缘预测模糊一点;血管分割对连通性、对细小分支的召回要求非常高。一个体素的误判就可能导致一根小血管断掉,影响后续血管树重建、斑块分析甚至手术规划。这种结构特点决定了:当你把像素级二分类问题叠加到三维细长结构上,难度是成倍增加的。

标注成本更不用说了。器官分割用半自动工具点几下就能出来个大概轮廓,血管不行——对比剂显影之后血管在增强期清晰,但在分支末端、静脉回流区域、病变狭窄处,边界极其模糊。标注员得逐层确认,还要在三个正交视图里来回切。一例全量标注的CTA数据,四五个小时是平均水平,遇到体量大、分支多的案例,标一整天也不稀奇。

所以现实情况往往是:医院服务器里存了几百例原始影像,但真正有过标注的可能只有十几例甚至几例。这时候硬上全监督深度学习,效果一定很差;想要更多标注就得花钱花时间。半监督方法的价值就在这里——把大量未标注数据的“免费信号”用起来。这也是为什么近两年半监督血管分割方向的论文和开源项目越来越多。

1.2 半监督到底解决什么

半监督分割的设定很简单:有少量标注数据(比如10例)和大量未标注数据(比如200例)。训练时两者一起用,希望模型能从无标签数据里学到结构信息,达到接近“用全部数据做全监督训练”的效果。

传统做法里最朴素的是自训练:先用标注数据训一个模型,让模型去给无标签数据生成伪标签,再拿伪标签当监督信号训练。听起来顺理成章,实际执行时问题一堆。模型天然存在错误偏好,它给无标签数据打的伪标签里必然带系统误差;一旦用这些带噪声的伪标签训练,错误会被放大和固化,训练反而越来越差。这就是所谓的“确认偏差”。另一种常见方案是一致性正则化,也就是对同一个无标签样本做两次不同增强,要求模型在两条路径上的预测保持一致。这个思路温和很多,不会催生过多伪标签噪声,但要靠增强强度、损失权重这些细节去打磨,效果上限也受限于增强设计。

这个项目标题里的“动态协作网络”,实际上是把自训练、一致性正则化、双分支互相学习这三样东西揉在一起,并且用“动态”两个字解决前两者的矛盾。协作体现在:不是单模型自己教自己,而是两个结构不同、参数独立的分支互相提供监督信号;动态体现在:两个分支之间的信任程度、伪标签的使用强度、损失的加权系数,都随训练推进自适应变化。不是一开始就强上伪标签,而是让模型先通过一致性学习建立基本特征,再逐步加大伪标签的权重。这种渐进式策略,既保留了无标签数据的利用效率,又避免了早期噪声带来的模型崩溃。

2. 动态协作网络:整体设计与核心理念

2.1 “协作”比“自训练”好在哪

我在项目里实测过纯自训练方案:用标注数据训一个基础的3D U-Net,推理得到伪标签,然后混合真标签和伪标签继续训练。第一批伪标签的好坏直接决定后续训练的走向;一旦某些错误区域被反复强化,后期几乎救不回来。自训练本质上是一个“封闭循环”,模型的错误判断没有被对冲的机制,只会越陷越深。

动态协作网络的设计思路是打破这个闭环:同时维护两个结构不同、初始化不同、数据增强视图也不同的分支网络。每个分支不仅从真实标注里学习,还观察另一个分支在无标签数据上的预测结果。因为两个分支的特征提取方式、感受野偏好、天然错误区域都不同,A分支在某个样本上判断不确定的地方,B分支可能是确信的;A分支某块区域是全错的,B分支可能是对的。两者交换预测信息,相当于给了对方一扇新的窗口,错误不会同向叠加,而是会被另一方的“不同意见”中和。

这里有一个很容易被忽视的设计细节:既然是协作,就必须防止两个分支完全趋同。如果两个分支结构、初始化完全一样,训练数据也一样,那就是两个一模一样的模型,协作等于没有协作。实际做法是给两个分支不同的编码器结构或不同深度的特征提取器,或者加上独立的DropOut模式,让每个分支存在随机扰动。我在一个模拟实验里对比过:两个分支完全相同的情况下,无标签数据利用率会明显下降;而结构略有差异后,模型在血管末梢区域的分割稳定性显著上升。所以协作的前提是“差异”,没有差异就没有信息交换的价值。

在无标签数据的处理上,两个分支的角色是对称的,但又各司其职。对每个无标签样本,A分支输出预测概率图,B分支也输出预测概率图。像素级置信度高的区域,可以转成硬伪标签给对面学习;置信度低的区域,则退化为软概率约束,要求双方的预测尽量一致。这样设计既保证了监督信号的密度,又不至于把低置信度区域的噪声硬编码给同伴。

2.2 “动态”的落地方式:三重自适应机制

设计稿里最容易出问题的是“动态”这个词——很多论文说是动态,实际就是损失权重按epoch线性增加,非常无聊。这个项目里的动态,我认为应该从三个层面理解。

第一层是置信度动态评估。模型在训练早期和训练后期对同一批无标签数据的信心是完全不同的。固定阈值(比如概率大于0.9才算正样本)在早期太严格,导致伪标签极少,无标签数据用不起来;在后期又太宽松,大量低质伪标签混进来。动态方案里,阈值会随着模型对标注集验证集的表现实时变化:当模型在验证集上的Dice较高时,说明当前特征可靠,阈值可以适当放宽;验证集表现波动时,阈值自动收紧。相当于给模型装了一个自我监控仪表盘。

第二层是协作权重动态调整。两个分支在训练不同阶段贡献的信息质量不一样。训练初期,两个分支都还比较弱,互相交换预测容易传播错误,这时候应该以一致性正则化为主,“彼此参考但不强教对方”;训练中后期,分支已经掌握基本特征,可以加大互相伪标签学习的权重。我在实际实现里给每个分支各自维护一组EMA(指数移动平均)参数,用EMA模型来生成伪标签,而用在线模型去做学习和更新,这样伪标签的质量更稳定,训练过程中的抖动也会小很多。

第三层是损失项内部的动态融合。总损失里,真实标注的监督损失、无标签样本的一致性损失、伪标签监督损失,这三者不能一直按固定比例相加。我给伪标签监督损失设计了一个升温曲线:前20个epoch完全关闭,之后线性加温到目标权重。原因很简单,早期模型输出的伪标签几乎全是噪声,那时候加伪标签监督,等于给模型喂毒药。等模型通过真实标注和一致性约束建立了稳定的基础表征后,再让伪标签发挥作用,收益才大于代价。

这三层动态机制合在一起,才配得上“动态协作”四个字。只做其中任何一层,效果都可能跟普通半监督方法拉不开差距。

3. 网络实现与损失函数设计

3.1 网络骨架选择与双分支配置

具体到实现层面,我选择了3D U-Net作为基础骨架。为什么不是基于Transformer的模型?原因是3D医学影像数据量通常不够大,尤其在血管分割这种标注稀缺的场景下,Transformer的全局自注意力机制在数据量不足时,反而不如卷积网络容易收敛。而3D U-Net的编码器-解码器结构天然适配像素级分割任务,多级跳跃连接能同时保留高分辨率细节和低分辨率语义,对血管这种需要精细末梢定位的结构很友好。

两个分支的具体配置我做了差异化处理。分支A采用标准的五层3D U-Net,编码器通道数为[32, 64, 128, 256, 512];分支B采用同样的深度,但每层的通道数改为[24, 48, 96, 192, 384],并且在最后一个编码器阶段引入了Dilated Convolution扩大感受野。双分支不共享任何参数,优化器也分开维护。这样做不是为了增加计算量好看,而是为了让两个分支的“视野”和“偏好”产生真实差异。

为了进一步放大差异,我给两个分支配了不同的数据增强策略。分支A走强增强路线:随机旋转、缩放、弹性形变、伽马增强全部用上;分支B走弱增强路线:只做随机翻转和轻微的灰度扰动。这个设计模拟的是Mean Teacher的一贯思路——一个模型从强扰动版本学习鲁棒特征,另一个模型从近原始版本提供稳定监督。两者互相协作时,信息流是有摩擦的,而正是这种摩擦让模型摆脱了“自我确认”的惯性。

分割头方面,两个分支各接一个1×1×1卷积输出单通道logits,sigmoid后得到血管概率图。没有用双分支共享解码器的轻量设计,虽然那种设计能省显存,但两者共享解码器意味着梯度会互相污染,很难保证各自的独立表达能力。在3D血管分割这个任务上,表达独立性比省显存更重要。

3.2 损失函数组合与动态权重

损失设计是半监督分割的胜负手。我最终使用的总损失由四部分组成,公式如下:

L = L_sup + α_cons × L_cons + α_pseudo × L_pseudo

什么叫“全部伪标签”?就是模型把无标签图上所有体素都预测成背景。血管本身只占1%~3%的体素,模型只要全预测背景,Dice虽然是0,但体素级准确率可能高达97%以上。这种情况下伪标签的正样本数量为零,监督信号完全失效。我排查过几次,最常见的原因有三个:第一个是训练初期模型还没学会血管特征,对无标签数据的所有预测概率都低于阈值,于是全部滤成了背景;第二个是类不平衡处理没做好,模型从一开始就偏向背景;第三个是预处理时窗宽窗位设置不当,血管和周围组织的对比度没有被突显出来。

解决方案是“两阶段训练”。第一阶段只用标注数据跑30个epoch,把模型训练到能稳定输出合理的血管概率图,再开启伪标签路径;同时给血管类设置更高的采样权重,保证每个patch里有足够的正样本。另外,伪标签生成不是直接取one-hot,而是保留软概率作为蒸馏目标,再叠加一个高阈值硬标签的头。软概率保留了边界模糊区域的分布信息,硬标签提供了明确的结构约束,两者互补。

第二个常见问题:两个分支趋同。如果训练后期两个分支输出几乎完全一样,协作就失效了。我见过跑完整个训练后,两个分支在无标签数据上的预测相关性高达0.98的情况。这多半是因为两个分支接收的梯度过于一致:损失函数里对两个分支的权重完全相同、数据顺序完全相同、增强策略趋同。解决方法是给两个分支使用相同的数据集但不同的采样顺序,并且对无标签数据的增强强度进行随机化——分支A有80%概率走强增强,分支B有80%概率走弱增强,不要让增强路径完全固定。此外,可以每隔一段时间对两个分支做一次“权重解耦”:把分支A的EMA权重复制给分支B时,不全部复制,而是只复制80%,保留20%原始权重,避免完全同步。

第三个常见的坑是显存不足。3D网络加双分支,batch size稍大一点显卡就爆掉。不是所有机器都能上80G以上的卡。我这里提供几个实测有用的降低显存方案。

第一个方案是梯度累积,虚拟扩大batch size。每16次迭代后才更新一次优化器参数,训练稳定性和显存需求可以同时兼顾。第二个方案是使用自动混合精度(AMP),16位浮点前向计算能把显存开销直接砍掉近一半,而且现在的卡普遍对半精度计算有优化,速度反而更快。第三个方案是控制patch尺寸和batch数,血管分割任务用96×96×96的patch,batch size为4,双分支每步消耗的显存大约在14GB到20GB之间,一张24G的卡可以跑。模型通道数也做过消融,把[32,64,128,256,512]砍到[24,48,96,192,384],显存再降30%左右,Dice只掉了0.5个百分点。

还有一个实用技巧:无标签数据并不是每一例都适合参与协作训练。我给每个无标签样本计算一个“血管丰富度指数”——即模型在当前状态下预测血管类置信度超过0.5的体素比例。如果这一比例低于某个阈值,说明该样本可能不含血管或质量过差,直接跳过,不进入伪标签生成流程。这个简单过滤能省掉不少无效计算,也能避免大量空白样图干扰协作信号。

5.2 训练超参数参考与迭代节奏

我把最后跑通的超参数整理成一张表,方便直接参考:

参数设置值说明
输入patch大小96×96×96能覆盖主干血管,显存可控
单分支batch4双分支共8个前向,24G卡可跑
优化器AdamWlr=1e-4,weight decay=1e-4
学习率策略poly衰减训练末期降到初始值的10%
训练总轮数200 epochs前30轮仅标注数据
一致性损失权重ramp-up到1.0前20轮从0线性增长
伪标签损失权重ramp-up到1.0第30轮后开始增长
伪标签置信度阈值0.95~0.99动态随验证Dice自动调整
EMA动量衰减0.99更新EMA模型用
血管体素采样权重1:10平衡正负样本

实验对比下来,10例标注加90例未标注的半监督设置下,动态协作网络比只用10例标注的全监督模型Dice高出8到12个百分点,比使用固定阈值伪标签的普通自训练高3到5个百分点。这组数据和同类半监督分割方法在一个量级上。更重要的是,在血管末梢区域(直径为5个体素以下的部分),动态协作带来的提升尤其明显,因为未标注数据提供了丰富的上下文结构学习信号,而这正是人为标注时最容易忽略和最难保持一致的部分。

如果资源有限,想更轻量一点,可以把分支B的通道数继续压缩到原始的一半,效果下降可以控制在2个百分点以内。如果你的显卡只有16GB显存,那建议先冻结分支B的骨干网络,只让它输出特征不做反向传播更新,用分支A单侧学习,显存需求会缩减到12GB左右,协作效果会打折扣,但比完全单模型半监督还是会稳定一些。

6. 扩展思路与个人心得

动态协作网络不只适用于血管分割。凡是标注成本高、类不平衡明显、对结构细节敏感的三维医学分割任务,这套思路都可以迁移。比如胆管分割、冠状动脉分割、肝脏静脉系统分割,甚至神经元纤维分割,它们都有“细长结构 + 极度不平衡 + 标注昂贵”的共同特征。唯一要调整的是伪标签阈值和patch采样策略,因为每种结构的体素占比和形态特征不同。

我个人的体会是,半监督方法的难点从来不是模型有多强,而是怎么识别和避开噪声监督信号。动态协作这个思路的本质,是给模型找了一个“观点不完全相同的同伴”——不盲信自己,也不盲信同伴,而是根据当前证据动态决定听谁的、信多少。如果没有理解这层含义,仅仅照搬网络结构,换了任何数据集都不会有好效果。大家实现的时候,多花点时间在置信度分布的可视化和两个分支分歧区域的分析上,比盲目堆网络深度有价值得多。

最后分享一个小技巧:训练过程中,我习惯每个epoch结束后把两个分支在无标签样本上的预测差异保存下来,渲染成三维叠加图,和原始影像对照着看。如果分歧区域在血管主干内部出现,说明这里的特征学习还没完成;如果分歧区域集中在背景边缘,完全可以放心。这比只看Dice曲线更能判断训练状态。半监督训练不能说是个黑盒,只要学会了观察模型的“犹豫”,你就能控制住噪声,拿到接近全监督的效果。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进