ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Serp-Mamba:蛇形扫描如何突破视网膜血管分割的连续性难题

Serp-Mamba:蛇形扫描如何突破视网膜血管分割的连续性难题 做医学图像处理这几年我在视网膜血管分割上反复折腾过不少方案。如果你拿UNet这种经典CNN结构去跑一次DRIVE数据集很快会撞到一面很典型的墙主干血管分割得挺干净但细支血管断成一截一截的像碎掉的蛛网。这不是调参能解决的问题它涉及一个更深层的问题——CNN的归纳偏置到底适不适合血管这种极度看重长距离连续性的目标。后来Transformer家族进来自注意力理论上能捕捉全局依赖但高分辨率医学图像上的O(n²)复杂度直接把显存干穿了很多人只敢在小patch上打转。真正让我眼前一亮的是Mamba这类状态空间模型(SSM)。线性复杂度、全局感受野这两个特性几乎就是为视网膜血管分割这种任务量身定做的。而Serp-Mamba提出的蛇形扫描更是在血管拓扑学这个角度上把Mamba的序列化方式重新设计了一遍也解释通了为什么传统CNN在这种任务上确实会输给Mamba路线。这篇文章我会沿着“血管结构为什么难→CNN为什么吃亏→Mamba怎么赢→蛇形扫描为什么聪明”这条线把Serp-Mamba的设计动机、核心模块和实操复现过程彻底拆开讲。适合正在做医学图像分割的研究生、刚接触Mamba想找切入点做实验的工程师以及所有被血管断裂问题折磨过的算法同学。1. 视网膜血管分割一个“细节控”的硬核任务1.1 血管在图像里到底是什么样的视网膜血管不是粗粗一根管子它是一棵逐级分叉的树。从视盘附近的主干动脉出发一级一级细分下去最细的血管在眼底图像里只有1到2个像素宽。这意味着什么意味着任何带下采样的网络结构在第二层或者第三层特征图里这些细支血管可能只剩下半个像素的信号连“存在”都算不上。更麻烦的是血管的灰度特征并不稳定。同一根血管在走向不同的地方由于血流深度、血管壁厚度和背景反射的影响灰度值会上下波动。有些细支血管的局部对比度极低人眼需要借助上下文线索才能判断它是不是血管。这和分割猫狗那种“目标区域有明确纹理”的任务完全不同。从拓扑学的角度看视网膜血管网络有几个硬性指标连通性(connectivity)要求相邻血管段必须接上不能断分叉点(bifurcation)要求主血管分支出细支时分支的方向和曲率是连续的还有动静脉交叉处的遮挡关系这些交叉区域在2D眼底图像上看起来就是动脉和静脉重叠在一起分割算法很容易在这里产生误判。这些特性合在一起让视网膜血管分割成了一个很好的“压力测试场”。一个分割算法如果能在这种任务上表现好说明它在细节保持、长距离依赖建模和低对比度特征提取这三个维度上都经受住了考验。这也是为什么很多做分割模型创新的人都喜欢拿视网膜血管数据集来验证自己的模块。1.2 为什么说它比自然图像分割更难自然图像分割里你要分割的物体通常是“一大块”有纹理、有边界的区域你甚至可以通过颜色分布来猜出物体边界。但血管不是“块”它是“线结构”。线结构没有面积只有路径它的存在与否完全依赖于相邻像素之间的连续性。这种线状目标对分割网络有个很苛刻的要求模型必须有非常强的“沿着路径传递信息”的能力。在主干血管上这个信息距离可能只有几十个像素但对一根从视盘一直延伸到黄斑附近的细血管来说你需要关联的是图像中相隔几百个像素的两个区域。换句话说分割一个血管像素依赖的不是它周围那个小patch而是这根血管在更早分支处的走向和灰度特性。人眼是怎么看血管的我们不是逐像素判断而是先大致勾勒出血管的走向然后沿着走向去补充确认。这个“沿着走向”的动作天然就是一个序列化的过程。很可惜CNN的标准操作——卷积池化——并不是这样工作的它更擅长的是“每个像素看周围一个固定方块”。这种错配就是后面一切问题的根源。2. 传统CNN在血管分割上的“天赋天花板”2.1 感受野与局部性看得再宽也只是窗口里的游戏CNN的两个核心特性是局部连接和权值共享。局部连接意味着每个卷积核只能看到一个局部邻域权值共享则让这个卷积核在整张图上滑动。这个设计在自然图像上很合理——你在图像上识别一个纹理模式通常不需要知道它和远处像素的关系。问题在于血管分割的核心恰恰需要远处像素的关系。DeepLab系列把空洞卷积发扬光大本质目的就是扩大感受野让每个输出点能看到更大的范围。但空洞卷积在血管分割上有自己的毛病感受野变大后覆盖的区域是稀疏的有些像素之间根本没有信息交互。对于连续性和拓扑性要求极高的血管结构这种“稀疏的全局视野”反而会引入噪声。在我的实验里用UNet做视网膜分割感受野覆盖范围其实已经足够覆盖主干血管的局部结构了。但细支血管的分割依赖的是整根血管的“趋势”而不是局部强响应。这一条CNN从归纳偏置上就不占优势。2.2 下采样是双刃剑细节和视野不可兼得CNN处理高分辨率图像的标准流程是多层下采样加多层上采样。下采样扩大感受野上采样恢复分辨率中间的跳跃连接用来补细节。这个框架在UNet上取得了巨大成功但应用到视网膜血管上会碰到一个尴尬局面下采样倍数越高小血管在特征图中被“溶解”得越厉害。假设原始图像分辨率是565×584经过4次下采样后特征图尺寸只有35×36。一根宽2像素的血管在这个尺度上连一个像素都占不满。虽然跳跃连接可以把高分辨率细节直接送到解码器但编码器对这个血管的抽象表示已经丢了解码器能利用的信息非常有限。有人会想那把下采样次数减少不就行了这样做的话感受野又不够大主干血管和细支血管之间的依赖关系建不起来。这就是我在实操中反复体会到的博弈——CNN在血管分割上需要同时满足“局部细节精确”和“全局关系清晰”而它的设计约束决定了这两者很难兼得。2.3 CNN阵营里有没有补救方案当然这几年CNN阵营也做了很多补救。CSPNet这类结构通过将特征分成两条路径一条走密集卷积一条直接跨层连接可以在不增加太多计算量的前提下增强CNN的学习能力用在检测骨干网络上效果不错。把它作为backbone用进分割网络也可以把血管分割指标推高一点。但这类“补强方案”本质上还是停留在CNN的框架内不管你用CSPNet还是DenseNet每个卷积核还是在一个矩形窗口里做特征混合。窗口只能变大变小没法跟着血管“拐弯”所以它改善的是CNN的表达能力没有改变CNN对全局依赖建模的劣势。我还试过把CNN和Transformer结合用UNet作为主骨架、在底层特征上用自注意力捕捉全局依赖。效果比纯CNN好一些但训练代价高而且Transformer的注意力矩阵在高分辨率医学图像上实在费显存。这就让我开始关注另一个方向——Mamba它的设计逻辑完全不同而且复杂度是线性增长的。3. 从RNN到Mamba状态空间模型的“线性长记忆”3.1 状态空间模型给图像建模加了个“记忆单元”Mamba的前身是状态空间模型核心公式其实很简洁系统有一个隐状态h(t)输入x(t)进来后通过矩阵A更新状态通过矩阵B写入门控输出则通过矩阵C从状态中读取。如果你把图像展开成一个像素序列那么模型在扫过第100个像素的时候隐状态里可能还保存着第1个像素的“记忆”。这个机制和人眼看血管非常像视线沿着血管移动时大脑会记住前面一小段血管的方向、粗细和灰度用来推断下一段应该在哪。这种“记忆驱动”的建模方式提供了CNN局部卷积没有的全局上下文能力同时不需要像自注意力那样把每个像素和所有像素都做点积。我之前一直觉得把图像这种2D结构拉成1D序列会丢掉空间信息。但是后来想明白了血管本身就是一条近似一维的路径把图像转换成序列对血管来说不仅不丢失结构反而更贴合它的线状本质。关键问题变成了——怎么把2D图像映射成1D序列才能让“记忆”沿着血管方向传播而不是在垂直方向乱跳。3.2 Mamba的选择性机制不是所有历史都该记住原始SSM的问题在于矩阵A、B、C在扫描整个序列时是固定的。这就像一个人用同样的方式记忆所有句子不管这句话重不重要。Mamba做的最关键改进是让B和C变成“输入相关的”——参数会根据当前输入动态变化模型可以自主决定当前这个像素值对状态的更新权重有多大以及历史状态的哪些部分该被保留。这个机制放在血管分割里意义非常明显扫过背景区域时模型可以让背景对隐状态的更新权重降到很低避免把噪声记忆进状态扫到血管像素时模型会自动提高这个像素对状态的贡献让“血管的记忆”牢固地传下去。从工程实现上说这个选择性机制带来一个直接好处——你不需要预先假设图像中哪部分重要模型会在训练中自动学会。Serp-Mamba正是在这个机制基础上进一步改造了“扫描路径”这个决定SSM记忆传播方向的要素。可以说Mamba给了血管分割一个强大的“记忆存储”而Serp-Mamba要解决的是往这个存储器里塞什么、按什么顺序塞。3.3 为什么Mamba比Transformer更适合高分辨率医学图像Transformer的自注意力机制在NLP、视觉任务上很强大但计算复杂度随序列长度呈平方增长。视网膜图像从565×584到999×960不等如果按像素展开成序列序列长度高达30万到近百万。在这个尺度上任何密集自注意力都会轻松占满几十G显存。Mamba的计算复杂度是线性的几十万长度的序列可以当作一个长长的时间序列跑循环。并且它引入了硬件感知的并行扫描算法在GPU上执行效率很高。这就使得Mamba能够处理原始分辨率的整张眼底图不需要为了迁就显存而把图像切成小patch。在我实际的对比实验里把输入分辨率从512×512提升到原始尺寸UNet的精度提升有限且训练时间暴涨Transformer则直接连batch size2都跑不动。Mamba在同配置下可以稳定跑batch size4到8训练速度和UNet接近精度却更高。这种“低成本享受全局视野”的特性是我认为Mamba路线会在医学图像领域持续走红的核心原因。4. Serp-Mamba的核心蛇形扫描为什么比四向扫描更聪明4.1 “扫描”的本质如何把2D图像变成1D序列所有基于Mamba的视觉模型第一步都是把2D特征图“摊平”成一个序列。你选择什么样的摊平顺序决定了SSM的记忆在空间中沿着什么路径传播——这是整个模型的“信息高速公路”。Vision Mamba的做法是把图像切成patch然后简单地按行优先顺序拉平。Swin-Mamba类似但增加了窗口内扫描。VMamba则做了一步改进同时从四个方向扫描左上到右下、右上到左下等让不同方向的全局上下文都能被捕捉。这些方法确实打破了CNN的局部限制但它们共同的痛点是——扫描路径和血管的走向之间没有任何对齐关系。想象一下一根血管在图像中呈45度斜向走势。按行优先扫描时血管像素在序列中会以“每隔很多步出现一次”的方式分布就像把一根斜线切成了一段段孤立点。SSM的记忆沿着扫描路径传播但这条路径在多数位置上和血管是垂直的。结果就是模型能“看见”全局但全局中血管的信息被摊碎成了碎片。这就是Serp-Mamba要解决的核心问题能不能设计一种扫描路径让像素在序列中的排布方式尽量让“空间上相邻的像素在序列里也相邻”这样SSM的记忆就可以沿着连续的空间路径传播而不是在空间中跳来跳去。4.2 蛇形路径的构造逻辑蛇形扫描Serpentine Scanning的实现方式非常直观把特征图按行切成若干个带状块band在每个band内部按蛇形折返走线——第一行从左到右扫完直接进入第二行从右到左再进入第三行从左到右以此类推。这样一个band内所有像素形成一条物理上连续的路径路径上相邻的像素在图像空间里也是相邻的。如果只是这样蛇形扫描和普通的行优先扫描差别不大。Serp-Mamba的关键在于把多个band串联起来让整张图的扫描路径从头到尾是一条连续的“蛇”。同时在反向通道中再做一次方向相反的蛇形扫描让信息既能从左上角向右下角传播也能反向传播。这个设计的精妙之处是它保持了“路径连续性”。不管SSM的隐状态走到路径的哪个位置它的“记忆”来源都是空间上相邻的像素块而不是隔着十万八千里的随机像素。血管就算在图像里拐了弯只要它在某个band内是连续的它就能在扫描序列中保持连续。这个特性在数学上叫“邻域保持”(neighborhood preserving)它让扫描路径从纯工程技巧变成了一种拓扑约束刚好打在血管分割的核心需求上。需要说明的是论文里具体实现还涉及多个band设置、不同尺寸的路径分支等细节我这里描述的是核心设计逻辑具体参数需要看对应实现。但不管怎么变核心思想都是“牺牲一点点全局跳跃性换取空间连续性”——在血管分割这个任务上这笔账算下来是值的。4.3 血管拓扑学视角为什么连续性就是血管的生命线做血管分割本质上是在恢复一个血管网络的拓扑结构。拓扑学里最核心的不变量是连通性和分叉结构连接在一起的两个血管点在分割结果里也必须连接一根血管应该分叉的地方必须保留分叉点。传统CNN的分割结果最常见的拓扑错误就是“断裂”。断裂点往往出现在低对比度区域或者细支血管上。因为卷积核看局部patch时如果这个patch内血管信号太弱卷积核就不知道该不该激活。而Mamba序列化建模天然具备“根据前文预测后文”的能力——只要它记住“这里大概率有血管延续”走到低对比度区域时它对血管的响应就会更强断裂的概率就下降了。蛇形扫描则进一步把这种能力发挥到了极致。因为在蛇形路径上相邻位置的上下文被编码进了序列中的邻近位置隐状态里的“血管记忆”可以被平滑地传导到血管的下一段。你可以把蛇形扫描理解成一种“沿着血管路径打标签”的过程和血管追踪算法vessel tracking的思路有异曲同工之处。另外Serp-Mamba在局部还引入了一个自适应的局部归一化模块帮助模型应对眼底图像光照不均匀的问题。眼底相机拍摄的图像经常中央亮、四周暗或者有反光斑。全局归一化对这种器质性的灰度变化无能为力而局部自适应归一化可以让模型在不同亮度区域使用不同的统计特征来做归一化保留血管局部对比度。这个小模块对血管分割指标的提升其实很关键尤其在全视野图像上它能减少光照不均带来的伪影分割。4.4 蛇形扫描 vs 四向扫描差异有多大为了直观看出蛇形扫描的优势我把自己之前用四向扫描Mamba的实验结果和Serp-Mamba对比了一下。在DRIVE数据集上四向扫描已经能把AUC推到98%以上但细支血管的连续性只能算“能看”分叉点附近偶尔会掉链子。换成蛇形扫描后最明显的改善并不是AUC涨了多少而是结果图上的血管网络看起来“像一棵树”了——主干到细支之间过渡自然断裂明显减少。这说明一个道理评估指标有时候会掩盖拓扑错误。Dice和AUC只看像素级重合度一根细血管如果只是在中间断了2个像素对Dice的影响微乎其微但对临床医生来说这根血管就已经不可用了。蛇形扫描的价值恰恰体现在这种“指标看不出来但结构上很重要”的改善上。5. 模型整体架构与关键实现细节5.1 Serp-Mamba的总体数据流Serp-Mamba的整体结构还是走了编码器-解码器的大框架便于和UNet这类模型做公平对比。输入一张眼底图像先经过一个浅层卷积stem提取低级特征然后进入多阶段编码器每个阶段由Mamba块组成块内采用前面说的蛇形扫描作为核心操作。每个编码器阶段输出的特征图会送到解码器对应层通过跳跃连接融合编码器和解码器的特征。底层特征因为分辨率低、通道数高信息更抽象负责提供全局上下文。高层特征分辨率高、通道数少负责保留血管细节。这种U型设计在医学分割上久经考验Mamba块替换掉普通卷积块之后整体指标会有明显提升。值得留意的是Serp-Mamba并没有完全抛弃卷积。它在输入stem和解码器输出端保留了卷积操作因为浅层的高频细节毛细血管边缘、噪声用卷积提取更高效Mamba更适合在中高层特征中建模长距离关系。这种混合架构在实际工程中往往表现最优也是我在复现其他Mamba视觉模型时总结出的经验别为了秀技术把卷积全部赶走混合使用常常能得到更好的结果。5.2 金字塔掩码Mamba模块多尺度血管都不放过视网膜血管的尺度跨度非常大主干血管可能有20像素宽毛细血管只有1到2像素宽。如果只用单尺度特征建模模型很容易偏向某一类血管。Serp-Mamba针对这个问题引入了金字塔掩码Mamba模块PMM核心思路是在不同尺度上分别做蛇形扫描再融合起来。具体来说模块内部包含几个并行分支每个分支处理不同下采样倍率的特征。在像素级别、小区域级别和大区域级别分别做状态空间建模让主干血管的全局走向、分叉点的局部结构、细支血管的细微对比度都能得到专门的建模通道。最后把多尺度特征融合起来保证模型对所有血管“一视同仁”。论文把这种多尺度设计放在Mamba块内部而不是作为独立的辅助分支。这样做的优势是特征融合和状态空间建模可以在同一层完成梯度可以直接回传到所有尺度分支。我在复现时试过把它改成外挂式多尺度模块效果不如“内置式”好可能是因为外挂式模块的梯度路径太长底层特征学到的东西难以直接影响高层分支。5.3 损失函数与训练细节血管分割的类别极度不平衡血管像素通常只占整张图的10%左右背景占90%。如果直接用交叉熵损失模型学到的会是一个“把所有像素都预测为背景”的平庸解。我的做法和Serp-Mamba团队的思路接近使用加权交叉熵配合Dice损失。加权交叉熵可以给正样本更高的权重让模型关注到那10%的血管像素。Dice损失则直接优化区域重叠度对小目标更加友好。两者组合时要注意权重比例。我调过的经验是Dice损失权重可以稍微给高一点比如1:0.6到1:1之间具体看验证集表现。如果发现模型过度倾向于分割粗血管就适当降低Dice的权重如果细支血管召回率上不去就提高加权交叉熵中的正样本权重。数据增强对血管分割的重要性容易被低估。随机旋转、水平垂直翻转、弹性形变是最基本的。弹性形变特别有用因为血管的形态本来就是弯曲多变的通过弹性形变模拟不同弯曲程度的血管模型对形态变化的鲁棒性会明显提升。另外可以做一些局部光照扰动模拟眼底相机拍摄时的光线变化配合AdaLNM能有效降低光照干扰。6. 复现过程与实战记录6.1 环境与依赖选择Mamba系列的复现最大的坑在于环境。原始Mamba有官方CUDA实现编译需要比较新的GPU和CUDA版本。如果不想折腾编译可以使用纯PyTorch的Mamba实现速度慢一些但无需编译对于视网膜这种中等规模数据集完全够用。我的环境配置供参考Ubuntu 22.04PyTorch 2.1CUDA 12.1单张RTX 4090。第一次踩坑主要是gcc版本和CUDA版本不匹配导致编译失败后来直接改用纯PyTorch的Mamba实现省去了很多麻烦。如果你的显卡显存小于16G建议把输入图像resize到512×512左右原始分辨率的训练非常吃显存。6.2 数据准备与增强策略常用的公开数据集有三个DRIVE、STARE和CHASE_DB1。DRIVE共40张图官方划分20张训练、20张测试STARE共20张图一般用10张训练、10张测试CHASE_DB1共28张图通常留一部分做验证。这些数据集规模都不大即便用Mamba这种偏大模型也不会过拟合到无法接受。预处理方面我建议第一步做灰度化和归一化。眼底图像是RGB三通道但血管分割主要依赖亮度对比度所以可以转成灰度图也可以直接用RGB通道但做通道维的标准化。裁剪方面不要把包含黑色背景边框的部分直接扔进网络训练先做一个感兴趣区域ROI提取可以大幅减少无效计算。增强策略我用的是随机旋转-180°到180°、随机水平翻转、随机垂直翻转、随机弹性形变、随机gamma校正。实测下弹性形变对血管连续性指标贡献最大gamma校正对低对比度血管的鲁棒性有帮助。训练时采用patch采样从原图中随机裁剪96×96到128×128的小块增加样本数量同时降低显存压力。测试时用滑窗方式对整图做预测最后拼接出完整分割结果。6.3 训练参数与收敛经验我试验过一组比较稳定的参数组合AdamW优化器初始学习率1e-4batch size 16patch为96×96训练100个epoch学习率采用余弦退火到1e-6。这个配置在DRIVE上跑一轮大概40分钟最终AUC可以到0.98以上Dice在0.82左右。关于学习率Mamba模型对学习率的敏感度和CNN不太一样1e-4是一个保险起点。如果你发现loss震荡或者不收敛先把学习率降到3e-5再试。我有一次直接把学习率设成3e-4训练到30个epoch时loss发散回退重来白白浪费了电费。Early stopping有必要加我一般监控验证集AUC连续15个epoch不涨就停止。另外Mamba模型在训练初期收敛速度比CNN慢前10个epoch看起来指标提升不明显这很正常不要急着调参至少跑满30个epoch再下结论。7. 常见问题与排查技巧实录7.1 显存溢出怎么办这是Mamba复现时最常遇到的问题。优先检查输入图像尺寸和patch大小其次查看Mamba实现是否为标准选择扫描有些实现为了加速会用更大的中间张量。如果还不是把batch size降到4或2。另外一个容易被忽略的点是混合精度训练能大幅降低显存占用。我在训练时开启AMP自动混合精度batch size可以从8提到16显存占用基本减半而且精度几乎不掉。7.2 血管断裂依然严重怎么办如果换上蛇形扫描后断裂问题仍然严重先别怀疑模型检查两点一是损失函数中正样本权重是否足够细支血管像素太少权重不足时模型直接忽略它们二是数据增强的弹性形变幅度是否过大过强的形变会让血管在训练时被“拉断”模型学到错误的连续特征。建议先把弹性形变幅度调小再逐步增大。7.3 拓扑指标和像素指标不对应我遇到过Dice挺高但连通性很差的情况。这时候建议把评估指标从像素级指标扩展到拓扑指标比如计算分割结果的连通分量个数或者用血管中心线提取后的DiceclDice来评估。clDice对血管连续性更敏感也更有临床参考价值。Serp-Mamba论文里也明确提到了这种“中心线Dice指标”用来衡量血管分割的拓扑完整性。7.4 训练时loss不下降检查数据归一化是否按通道做了标准化如果输入是3通道RGB但模型内部只用了灰度统计会导致通道间分布冲突。另一个常见原因是学习率太高或者优化器权重衰减设置过大。我一般把weight decay设在1e-4到5e-4之间太高会严重影响Mamba这种小数据集上的收敛。8. 关于实验设计的一些个人体会最后聊一点我在复现Serp-Mamba过程中的个人感受。做对比实验时不要只盯着AUC和Dice一定要把分割结果图直接叠加在原始眼底图像上逐根血管看才能发现模型之间的真正差距。很多论文里指标差距只有0.5%但可视化结果差距巨大。尤其在医学图像这种安全敏感的场景里“看起来对”有时候比“指标高”更重要。另外Mamba方向的论文更新速度非常快Serp-Mamba只是其中一条值得深挖的路线。如果你打算在这个方向上继续扩展可以从两个角度入手一是设计新的扫描策略让路径更贴合特定器官的结构特点二是把蛇形扫描和域适应技术结合解决眼底图像跨设备、跨数据集的泛化问题。这些都是我目前在关注的方向也希望这篇拆解能给你带来一些可落地的思路。
返回列表