面向 X_SSL_Net 当前实现,比较 lib/modules/xnet_2d.py 与 ref/fft_wave_pro/MobileMamba/model/mobilemamba/mobilemamba.py 的 encoder 设计。
本文只比较 encoder / backbone 部分,不比较 XNet 的 decoder、segmentation head,也不比较 MobileMamba 的分类 head。
对比代码入口:
| 项目 | 文件 | 主要类 / 函数 |
|---|---|---|
| XNet 当前实现 | lib/modules/xnet_2d.py |
XNetStem2d、XNetDownsample2d、XTEB2d、XNetEncoder2d |
| MobileMamba 参考实现 | ref/fft_wave_pro/MobileMamba/model/mobilemamba/mobilemamba.py |
MobileMamba、MobileMambaBlock、MobileMambaModule、MBWTConv2d、PatchMerging |
结论先写清楚:两者都使用了 convolution、wavelet、SS2D/Mamba-like global modeling,但组织方式完全不同。XNet 是为分割设计的 4 级多尺度 encoder,输出 [E1,E2,E3,E4] 给 decoder skip 使用;MobileMamba 是分类 backbone,patch embedding 后只向前堆叠 blocks,最后全局池化分类。
XNet encoder 的目标是产生分割 decoder 可用的多尺度特征金字塔。
Input [B, 3, H, W]
|
v
Stem: two stride-2 conv steps
|
v
E1 [B, C1, H/4, W/4]
|
v
Down1 + Stage2
|
v
E2 [B, C2, H/8, W/8]
|
v
Down2 + Stage3
|
v
E3 [B, C3, H/16, W/16]
|
v
Down3 + Stage4
|
v
E4 [B, C4, H/32, W/32]
默认配置是:
encoder_channels = [32, 64, 128, 192]
encoder_depths = [2, 2, 2, 2]
XNetEncoder2d.forward() 直接返回:
[e1, e2, e3, e4]
这说明它天然是 segmentation encoder,而不是分类 backbone。
MobileMamba 的目标是分类。它先用 4 个 stride-2 convolution 直接把输入降到 1/16,然后经过 3 组 blocks,中间通过 PatchMerging 再降采样。
Input [B, 3, H, W]
|
v
PatchEmbed: four stride-2 convs
|
v
X0 [B, C1, H/16, W/16]
|
v
blocks1
|
v
blocks2: pre refine + PatchMerging + post refine + stage blocks
|
v
X1 [B, C2, H/32, W/32]
|
v
blocks3: pre refine + PatchMerging + post refine + stage blocks
|
v
X2 [B, C3, H/64, W/64]
|
v
AdaptiveAvgPool2d + BN_Linear
典型配置如 MobileMamba_B2:
img_size = 384
embed_dim = [200, 376, 448]
depth = [2, 3, 2]
global_ratio = [0.8, 0.7, 0.6]
local_ratio = [0.2, 0.2, 0.3]
kernels = [7, 5, 3]
它默认不返回多尺度特征,只返回分类 logits。因此如果要把它直接用于分割,必须额外改 forward_features 或在 blocks1/2/3 后取中间特征。
| 维度 | XNet | MobileMamba |
|---|---|---|
| 初始降采样倍率 | 1/4 |
1/16 |
| 结构 | Conv s2 -> DWConv -> PWConv -> Conv s2 |
4 个连续 Conv s2 |
| 输出通道 | 默认 32 |
默认 embed_dim[0],如 200 |
| 设计目的 | 保留较高分辨率给分割 skip | 快速进入低分辨率分类 token/feature stage |
| 对小病灶/边界 | 更友好,因为 E1 是 H/4 |
早期细节丢失更多,原始实现不适合直接当分割浅层 skip |
这一点是最重要的结构差异。XNet 的 stem 只降到 H/4,因为 decoder 需要浅层边界、纹理和定位信息。MobileMamba 一开始就降到 H/16,这对 ImageNet 分类是合理的,但对医学分割尤其是超声小病灶不一定合适。
XNet 写法很直接:
e1 = self.stage1(self.stem(x))
e2 = self.stage2(self.down1(e1))
e3 = self.stage3(self.down2(e2))
e4 = self.stage4(self.down3(e3))
return [e1, e2, e3, e4]
每一级都是:
Downsample if needed -> XTEB2d x depth
它的边界非常清楚:stage 负责表征提取,downsample 负责降尺度,返回值负责 decoder skip。
MobileMamba 在构造 blocks 时把 stage 的降采样模块 append 到下一组 blocks 里:
blocks1 = stage0 blocks
blocks2 = downsample 0->1 + stage1 blocks
blocks3 = downsample 1->2 + stage2 blocks
PatchMerging 前后还各有一段 residual depthwise conv + FFN refine:
Residual(DWConv)
Residual(FFN)
PatchMerging
Residual(DWConv)
Residual(FFN)
这对分类 backbone 来说很紧凑,但如果要做 segmentation backbone,中间特征取点需要更小心。因为 blocks2 不是纯 stage2,它包含从 stage1 到 stage2 的 transition。
XTEB2d 对同一个输入特征做三条并行路径:
pre_norm(x)
|
+-- local_branch(x) : 3x3 depthwise + 5x5 depthwise
|
+-- wavelet_branch(x) : DWT -> LL/high conv -> IDWT
|
+-- global_branch(x) : VMamba SS2D
|
v
concat -> 1x1 fuse -> channel gate -> post conv -> residual -> FFN
关键点:
C 个通道3C,再压回 CXBranchFusion2d 额外用 GAP -> MLP -> sigmoid 做通道 gatepost residual 和 ffn residual这是一种“全通道多视角建模”:每个通道组都能同时被 local、wavelet、global 路径处理,代价是计算和显存更高。
MobileMamba 不把完整通道送进每个分支,而是先按比例切分:
x1, x2, x3 = torch.split(
x,
[global_channels, local_channels, identity_channels],
dim=1,
)
三路分别是:
global channels -> MBWTConv2d
local channels -> DWConv2d_BN_ReLU
identity channels -> Identity
然后 concat 回来,再用:
ReLU -> Conv2d_BN(dim, dim, bn_weight_init=0)
做输出投影。
这是一种“通道分工式建模”:部分通道做重型 global+wavelet,部分通道做轻型 local,剩余通道直接保留 identity。它比 XNet 的全通道并行三分支更省算力,也更符合 MobileNet 风格。
| 维度 | XNet | MobileMamba |
|---|---|---|
| wavelet 库 | ptwt.wavedec2 / waverec2 |
pywt 生成固定 filter,再用 conv2d / conv_transpose2d |
| 默认类型 | 只允许 haar |
默认 db1,本质上与 Haar 很接近 |
| 分解层数 | 当前只支持 level=1 |
wt_levels 可扩展,当前调用为 1 |
| 高频处理 | LH/HL/HH concat 后 depthwise + pointwise conv |
LL/LH/HL/HH reshape 成 4C 后 depthwise conv |
| 低频处理 | LL 单独 3x3 Conv2dBN + ReLU |
LL 和 high bands 一起进入 wavelet conv 后再拆分 |
| 逆变换 | ptwt.waverec2,显式 crop |
conv_transpose2d,按记录 shape crop |
| AMP 处理 | 显式关闭 autocast,内部 float32 | 没有单独 autocast 保护 |
XNet 的 wavelet branch 更像一个独立的“频率专家分支”:它把低频和高频拆开处理,再重建到原空间尺度。MobileMamba 的 wavelet 更深地嵌在 MBWTConv2d 中,和 SS2D global attention 共同构成 global branch。
| 维度 | XNet | MobileMamba |
|---|---|---|
| 使用位置 | XGlobalBranch2d,作为 XTEB 三分支之一 |
MBWTConv2d.global_atten,只作用于 global channel split |
| 作用通道 | 默认 stage2-4 全通道,stage1 可关闭 | 只作用于 global_channels |
d_state |
默认 16 |
固定 1 |
ssm_ratio |
global_ratio,默认 2.0,且至少 1.0 |
配置 ssm_ratio,常见为 2 |
initialize |
"v0" |
"v2" |
forward_type |
默认 "v3" |
默认 "v052d" |
| backend 控制 | ssm_backend=auto/oflex/torch,运行时切换 |
无显式 backend 切换 |
k_group |
未传 | k_group=2 |
这里不能简单说谁更强。XNet 的 SS2D 更重,默认 d_state=16,且在较深 stage 全通道使用,表达能力更强但显存和时间开销更高。MobileMamba 的 SS2D 更轻,d_state=1,且只对一部分 global channels 使用,更适合移动端分类骨干。
XNet local branch:
branch3: DWConv3x3 -> ReLU -> PWConv1x1
branch5: DWConv5x5 -> ReLU -> PWConv1x1
output = branch3 + branch5
MobileMamba local branch:
DWConv kxk -> BN -> ReLU -> grouped 1x1 conv -> BN
MobileMamba 的 kernel 随 stage 改变:
kernels = [7, 5, 3]
XNet 每个 stage 的 local branch 都固定使用 3x3 + 5x5 双尺度局部分支。对超声分割来说,这个设计更稳定、更直接;MobileMamba 的大核到小核递减更像分类 backbone 的经验设计,浅层更大感受野,深层更省计算。
| 维度 | XNet | MobileMamba |
|---|---|---|
| 分支融合 | concat 三个完整分支输出,然后 1x1 fuse + channel gate |
split 后各自处理,concat 后 ReLU + Conv2d_BN |
| 残差结构 | x_in + post(fusion(...)),再 x + ffn(x) |
block 内 dw0 -> ffn0 -> mixer -> dw1 -> ffn1 多重 residual,最后 shortcut + drop_path(x) |
| stochastic depth | 无 | 有 DropPath |
| BN 初始化 | 多处最后 BN weight 初始化为 0 |
FFN / proj 也使用 bn_weight_init=0 |
| 部署优化 | 未提供 BN fuse 接口 | 多个模块有 fuse(),并提供 replace_batchnorm() |
MobileMamba 的 block 工程上更偏部署友好:Conv2d_BN、DWConv2d_BN_ReLU、BN_Linear 都实现了 fuse。XNet 当前更偏研究实验清晰性,没有做推理融合优化。
这是两者最根本的任务差异。
| 维度 | XNet encoder | MobileMamba backbone |
|---|---|---|
| 原任务 | 2D segmentation | image classification |
| 输出 | [E1,E2,E3,E4] |
class logits |
| 中间特征 | 显式返回四级特征 | 默认不返回 |
| 最浅层分辨率 | H/4 |
patch embed 后为 H/16 |
| 最深层分辨率 | H/32 |
通常到 H/64 |
| 是否直接可接 U-Net decoder | 是 | 否,需要改 forward 并补浅层特征 |
如果把 MobileMamba 直接替换为 XNet encoder,会遇到两个问题:
H/4 和 H/8 的浅层 skip,decoder 的边界恢复会受影响。两者的共同点主要有:
这些共同点说明 XNet encoder 的方向和 MobileMamba 有同源性:都是把 CNN、wavelet、SSM/Mamba 混合起来,而不是单纯 CNN 或单纯 Transformer。
| 类别 | XNet encoder | MobileMamba encoder |
|---|---|---|
| 任务导向 | 分割 | 分类 |
| 多尺度输出 | 显式 4 级输出 | 默认不输出中间层 |
| 初始分辨率保留 | 保留到 H/4 |
快速降到 H/16 |
| 分支组织 | 全通道并行 local / wavelet / global | 通道切分 global / local / identity |
| Wavelet 位置 | 独立 branch | 嵌入 global branch 的 MBWTConv2d |
| SS2D 范围 | 多数 stage 全通道 | global split 部分通道 |
| 计算风格 | 更重、更适合精细分割 | 更轻、更适合分类/移动端 |
| 下采样 | 简单 stride-2 conv | PatchMerging + SE + pre/post refine |
| 工程部署 | 偏研究清晰 | 提供 BN fuse,偏部署 |
一句话概括:XNet encoder 是把 MobileMamba 类思想改造成分割特征金字塔后的实现,但它没有照搬 MobileMamba 的通道切分和移动端压缩策略,而是选择了更重的全通道三分支融合。
MobileMamba 中值得借鉴的点:
通道切分策略
XNet 当前 local、wavelet、global 都处理全通道,计算开销大。可以做一个轻量变体,把通道拆成 global/local/identity 三组,作为显存友好的 ablation。
PatchMerging 的 SE transition
XNet 的 XNetDownsample2d 是简单 3x3 stride-2 conv。MobileMamba 的 PatchMerging 有 1x1 expand -> depthwise stride-2 -> SE -> 1x1 project,可能改善 stage transition 的表达能力。
DropPath
XNet 当前没有 stochastic depth。较深配置如 [2,2,3,2] 或更宽通道时,可以考虑加入轻量 DropPath。
BN fuse / deployment path
如果后续关心推理速度,MobileMamba 的 fuse() 设计值得参考。
不建议直接照搬的点:
不要直接使用 1/16 patch embedding
超声分割需要边界和小目标细节,直接降到 H/16 会削弱浅层 skip。
不要直接改成分类式 forward
XNet 的训练链路需要 [E1,E2,E3,E4],不能只输出 pooled feature。
不要盲目把 d_state 降到 1
MobileMamba 的 d_state=1 是轻量分类选择。XNet 用于医学分割,是否能降需要单独消融。
不要把所有 stage 都按 MobileMamba 的 global_ratio 复制
XNet 的 stage1 默认关闭 global branch 是合理的,因为高分辨率 SS2D 显存开销大,且浅层更需要局部纹理。
如果要系统证明 XNet encoder 相对 MobileMamba-style encoder 的改动价值,建议做以下消融:
| 消融项 | 目的 |
|---|---|
| XNet full-channel tri-branch vs channel-split branch | 验证全通道三分支是否值得额外显存 |
| Simple downsample vs MobileMamba PatchMerging | 验证更强 transition 是否提升 Dice/IoU |
d_state=16 vs d_state=1/4/8 |
找到 SS2D 表达能力和显存的平衡点 |
| stage1 global off vs on | 验证高分辨率 global branch 是否必要 |
| XNet wavelet branch vs MBWTConv2d-style branch | 比较独立 wavelet 分支和 wavelet+SS2D 耦合分支 |
优先级最高的是第一项和第二项。第一项直接回答“XNet 为什么不用 MobileMamba 的轻量通道切分”;第二项可能以较低风险提升 encoder transition。
XNet encoder 和 MobileMamba encoder 的关系不是简单的“谁抄谁”或“谁替代谁”。更准确的判断是:
MobileMamba = classification-oriented, mobile-style, channel-split wavelet-SS2D backbone
XNet encoder = segmentation-oriented, full-channel tri-branch wavelet-SS2D feature pyramid
XNet 当前实现更适合医学分割,因为它保留了 H/4 到 H/32 的四级特征,并显式返回给 decoder。MobileMamba 更适合轻量分类,因为它快速降采样、通道切分、提供部署融合路径。若要融合两者,合理方向不是直接替换 encoder,而是把 MobileMamba 的 channel split、PatchMerging、DropPath 和 fuse 思路作为 XNet 的轻量化 ablation。