代码定义了一个深度学习模型的部分组件,主要用于图像处理。它结合了卷积神经网络(CNN)和自注意力机制来处理图像数据。以下是对这段代码的详细解释:
环境配置
翻译并通顺处理后的内容如下:
pip install causal-conv1d>=1.4.0`:安装用于Mamba模块内的简单因果卷积Conv1d层的高效实现。
pip install mamba-ssm
:安装核心Mamba包。
pip install mamba-ssm[causal-conv1d]
:安装核心Mamba包和因果卷积Conv1d。
pip install mamba-ssm[dev]
:安装核心Mamba包和开发依赖项。
你也可以从此代码库中构建并安装源代码,命令为 pip install .
。
如果pip对PyTorch版本有问题,可以尝试传递 --no-build-isolation
参数给pip。
其他要求:
- Linux
- NVIDIA GPU
- PyTorch 1.12+
- CUDA 11.6+
1. 导入依赖库
import time
import math
from functools import partial
from typing import Optional, Callable
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.checkpoint as checkpoint
from einops import rearrange, repeat
from timm.models.layers import DropPath, to_2tuple, trunc_normal_
- 这部分代码导入了一些标准库如
time
和math
,以及 PyTorch 库和一些深度学习相关的模块。 einops
库用于张量的重排列和复制。timm.models.layers
提供了一些常用的神经网络组件。
2. flops_selective_scan_ref 函数
def flops_selective_scan_ref(B=1, L=256, D=768, N=16, with_D=True, with_Z=False, with_Group=True, with_complex=False):
...
return flops
- 这是一个计算浮点操作数(FLOPs)的函数,用于估算某些张量运算的计算复杂度。它使用
einsum
操作符来模拟高效的张量乘法。 - 该函数的输入参数包括张量的形状和一些配置选项,输出是 FLOPs 的估计值。
3. PatchEmbed2D 类
class PatchEmbed2D(nn.Module):
...
- 这个类定义了一个
PatchEmbed2D
模块,用于将输入图像分割成多个小块(patch),并通过卷积操作将这些小块嵌入到一个高维空间中。 - 其核心是一个
Conv2d
卷积层,并且可以选择性地应用一个归一化层。
4. PatchMerging2D 类
class PatchMerging2D(nn.Module):
...
- 这个模块用于将多个小块(patch)合并为更大的块,从而降低特征图的空间分辨率,同时增加通道的数量。
- 其中
reduction
层是一个Linear
层,用于减少通道数。
5. PatchExpand2D 类和 Final_PatchExpand2D 类
class PatchExpand2D(nn.Module):
...
class Final_PatchExpand2D(nn.Module):
...
- 这两个类实现了将低分辨率的特征图扩展回高分辨率的操作,通常用于生成式模型或解码器部分。
- 通过线性变换和张量的重排列操作,特征图的空间分辨率得到了扩展。
6. SS2D 类
class SS2D(nn.Module):
...
SS2D
是这个代码片段中最复杂的类,它实现了一种类似自注意力机制的操作,结合了卷积和多头注意力机制。- 这个类的核心包括几个线性层和卷积层,用于特征的提取和投影。
- 该模块特别地引入了一些特殊的初始化方式(如
dt_init
和A_log_init
),用于初始化某些特定参数,以确保模型的稳定性和有效性。
SS2D 的重要方法:
dt_init
和A_log_init
:这些方法用于初始化模型中的参数,确保在训练初期模型的行为稳定。forward_corev0
:这是核心的前向计算方法之一,处理输入特征并通过多个注意力头来计算输出。
7. VSSBlock 类
class VSSBlock(nn.Module):
...
- 这是一个较高级别的模块,通常用作模型的构建块。它将
SS2D
类和其他组件组合在一起,形成一个复杂的网络层。 VSSBlock
可以包含多个SS2D
层,并使用DropPath
来实现随机丢弃路径的效果,从而增强模型的泛化能力。
import time
import math
from functools import partial
from typing import Optional, Callable
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.checkpoint as checkpoint
from einops import rearrange, repeat
from timm.models.layers import DropPath, to_2tuple, trunc_normal_
try:
from mamba_ssm.ops.selective_scan_interface import selective_scan_fn, selective_scan_ref
except:
pass
# an alternative for mamba_ssm (in which causal_conv1d is needed)
# try:
# from selective_scan import selective_scan_fn as selective_scan_fn_v1
# from selective_scan import selective_scan_ref as selective_scan_ref_v1
# except:
# pass
DropPath.__repr__ = lambda self: f"timm.DropPath({self.drop_prob})"
def flops_selective_scan_ref(B=1, L=256, D=768, N=16, with_D=True, with_Z=False, with_Group=True, with_complex=False):
"""
u: r(B D L)
delta: r(B D L)
A: r(D N)
B: r(B N L)
C: r(B N L)
D: r(D)
z: r(B D L)
delta_bias: r(D), fp32
ignores:
[.float(), +, .softplus, .shape, new_zeros, repeat, stack, to(dtype), silu]
"""
import numpy as np
# fvcore.nn.jit_handles
def get_flops_einsum(input_shapes, equation):
np_arrs = [np.zeros(s) for s in input_shapes]
optim = np.einsum_path(equation, *np_arrs, optimize="optimal")[1]
for line in optim.split("\n"):
if "optimized flop" in line.lower():
# divided by 2 because we count MAC (multiply-add counted as one flop)
flop = float(np.floor(float(line.split(":")[-1]) / 2))
return flop
assert not with_complex
flops = 0 # below code flops = 0
if False:
...
"""
dtype_in = u.dtype
u = u.float()
delta = delta.float()
if delta_bias is not None:
delta = delta + delta_bias[..., None].float()
if delta_softplus:
delta = F.softplus(delta)
batch, dim, dstate = u.shape[0], A.shape[0], A.shape[1]
is_variable_B = B.dim() >= 3
is_variable_C = C.dim() >= 3
if A.is_complex():
if is_variable_B:
B = torch.view_as_complex(rearrange(B.float(), "... (L two) -> ... L two", two=2))
if is_variable_C:
C = torch.view_as_complex(rearrange(C.float(), "... (L two) -> ... L two", two=2))
else:
B = B.float()
C = C.float()
x = A.new_zeros((batch, dim, dstate))
ys = []
"""
flops += get_flops_einsum([[B, D, L], [D, N]], "bdl,dn->bdln")
if with_Group:
flops += get_flops_einsum([[B, D, L], [B, N, L], [B, D, L]], "bdl,bnl,bdl->bdln")
else:
flops += get_flops_einsum([[B, D, L], [B, D, N, L], [B, D, L]], "bdl,bdnl,bdl->bdln")
if False:
...
"""
deltaA = torch.exp(torch.einsum('bdl,dn->bdln', delta, A))
if not is_variable_B:
deltaB_u = torch.einsum('bdl,dn,bdl->bdln', delta, B, u)
else:
if B.dim() == 3:
deltaB_u = torch.einsum('bdl,bnl,bdl->bdln', delta, B, u)
else:
B = repeat(B, "B G N L -> B (G H) N L", H=dim // B.shape[1])
deltaB_u = torch.einsum('bdl,bdnl,bdl->bdln', delta, B, u)
if is_variable_C and C.dim() == 4:
C = repeat(C, "B G N L -> B (G H) N L", H=dim // C.shape[1])
last_state = None
"""
in_for_flops = B * D * N
if with_Group:
in_for_flops += get_flops_einsum([[B, D, N], [B, D, N]], "bdn,bdn->bd")
else:
in_for_flops += get_flops_einsum([[B, D, N], [B, N]], "bdn,bn->bd")
flops += L * in_for_flops
if False:
...
"""
for i in range(u.shape[2]):
x = deltaA[:, :, i] * x + deltaB_u[:, :, i]
if not is_variable_C:
y = torch.einsum('bdn,dn->bd', x, C)
else:
if C.dim() == 3:
y = torch.einsum('bdn,bn->bd', x, C[:, :, i])
else:
y = torch.einsum('bdn,bdn->bd', x, C[:, :, :, i])
if i == u.shape[2] - 1:
last_state = x
if y.is_complex():
y = y.real * 2
ys.append(y)
y = torch.stack(ys, dim=2) # (batch dim L)
"""
if with_D:
flops += B * D * L
if with_Z:
flops += B * D * L
if False:
...
"""
out = y if D is None else y + u * rearrange(D, "d -> d 1")
if z is not None:
out = out * F.silu(z)
out = out.to(dtype=dtype_in)
"""
return flops
# 只使用了一次,将数据使用4*4的卷积和LN产生patch,然后转化为b*w*h*c
class PatchEmbed2D(nn.Module):
r""" Image to Patch Embedding
Args:
patch_size (int): Patch token size. Default: 4.
in_chans (int): Number of input image channels. Default: 3.
embed_dim (int): Number of linear projection output channels. Default: 96.
norm_layer (nn.Module, optional): Normalization layer. Default: None
"""
def __init__(self, patch_size=4, in_chans=3, embed_dim=96, norm_layer=None, **kwargs):
super().__init__()
if isinstance(patch_size, int):
patch_size = (patch_size, patch_size)
self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)
if norm_layer is not None:
self.norm = norm_layer(embed_dim)
else:
self.norm = None
def forward(self, x):
x = self.proj(x).permute(0, 2, 3, 1)
if self.norm is not None:
x = self.norm(x)
return x
class PatchMerging2D(nn.Module):
r""" Patch Merging Layer.
Args:
input_resolution (tuple[int]): Resolution of input feature.
dim (int): Number of input channels.
norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm
"""
def __init__(self, dim, norm_layer=nn.LayerNorm):
super().__init__()
self.dim = dim
self.reduction = nn.Linear(4 * dim, 2 * dim, bias=False)
self.norm = norm_layer(4 * dim)
def forward(self, x):
B, H, W, C = x.shape
SHAPE_FIX = [-1, -1]
if (W % 2 != 0) or (H % 2 != 0):
print(f"Warning, x.shape {x.shape} is not match even ===========", flush=True)
SHAPE_FIX[0] = H // 2
SHAPE_FIX[1] = W // 2
x0 = x[:, 0::2, 0::2, :] # B H/2 W/2 C
x1 = x[:, 1::2, 0::2, :] # B H/2 W/2 C
x2 = x[:, 0::2, 1::2, :] # B H/2 W/2 C
x3 = x[:, 1::2, 1::2, :] # B H/2 W/2 C
if SHAPE_FIX[0] > 0:
x0 = x0[:, :SHAPE_FIX[0], :SHAPE_FIX[1], :]
x1 = x1[:, :SHAPE_FIX[0], :SHAPE_FIX[1], :]
x2 = x2[:, :SHAPE_FIX[0], :SHAPE_FIX[1], :]
x3 = x3[:, :SHAPE_FIX[0], :SHAPE_FIX[1], :]
x = torch.cat([x0, x1, x2, x3], -1) # B H/2 W/2 4*C
x = x.view(B, H//2, W//2, 4 * C) # B H/2*W/2 4*C
x = self.norm(x)
x = self.reduction(x)
return x
class PatchExpand2D(nn.Module):
def __init__(self, dim, dim_scale=2, norm_layer=nn.LayerNorm):
super().__init__()
self.dim = dim*2
self.dim_scale = dim_scale
self.expand = nn.Linear(self.dim, dim_scale*self.dim, bias=False)
self.norm = norm_layer(self.dim // dim_scale)
def forward(self, x):
B, H, W, C = x.shape
x = self.expand(x)
x = rearrange(x, 'b h w (p1 p2 c)-> b (h p1) (w p2) c', p1=self.dim_scale, p2=self.dim_scale, c=C//self.dim_scale)
x= self.norm(x)
return x
class Final_PatchExpand2D(nn.Module):
def __init__(self, dim, dim_scale=4, norm_layer=nn.LayerNorm):
super().__init__()
self.dim = dim # 96
self.dim_scale = dim_scale # 4
# 96 384
self.expand = nn.Linear(self.dim, dim_scale*self.dim, bias=False)
# 24
self.norm = norm_layer(self.dim // dim_scale)
def forward(self, x):
B, H, W, C = x.shape
x = self.expand(x)
x = rearrange(x, 'b h w (p1 p2 c)-> b (h p1) (w p2) c', p1=self.dim_scale, p2=self.dim_scale, c=C//self.dim_scale)
x= self.norm(x)
return x
class SS2D(nn.Module):
def __init__(
self,
d_model, # 96
d_state=16,
d_conv=3,
expand=2,
dt_rank="auto",
dt_min=0.001,
dt_max=0.1,
dt_init="random",
dt_scale=1.0,
dt_init_floor=1e-4,
dropout=0.,
conv_bias=True,
bias=False,
):
super().__init__()
self.d_model = d_model # 96
self.d_state = d_state # 16
self.d_conv = d_conv # 3
self.expand = expand # 2
self.d_inner = int(self.expand * self.d_model) # 192
self.dt_rank = math.ceil(self.d_model / 16) # 6
# 96 384
self.in_proj = nn.Linear(self.d_model, self.d_inner * 2, bias=bias)
self.conv2d = nn.Conv2d(
in_channels=self.d_inner, # 192
out_channels=self.d_inner, # 192
kernel_size=d_conv, # 3
padding=(d_conv - 1) // 2, # 1
bias=conv_bias,
groups=self.d_inner, # 192
)
self.act = nn.SiLU()
self.x_proj = (
nn.Linear(self.d_inner, (self.dt_rank + self.d_state * 2), bias=False),
nn.Linear(self.d_inner, (self.dt_rank + self.d_state * 2), bias=False),
nn.Linear(self.d_inner, (self.dt_rank + self.d_state * 2), bias=False),
nn.Linear(self.d_inner, (self.dt_rank + self.d_state * 2), bias=False),
)
# 4*38*192的数据 初始化x的数据
self.x_proj_weight = nn.Parameter(torch.stack([t.weight for t in self.x_proj], dim=0)) # (K=4, N, inner)
del self.x_proj
# 初始化dt的数据吧
self.dt_projs = (
self.dt_init(self.dt_rank, self.d_inner, dt_scale, dt_init, dt_min, dt_max, dt_init_floor),
self.dt_init(self.dt_rank, self.d_inner, dt_scale, dt_init, dt_min, dt_max, dt_init_floor),
self.dt_init(self.dt_rank, self.d_inner, dt_scale, dt_init, dt_min, dt_max, dt_init_floor),
self.dt_init(self.dt_rank, self.d_inner, dt_scale, dt_init, dt_min, dt_max, dt_init_floor),
)
self.dt_projs_weight = nn.Parameter(torch.stack([t.weight for t in self.dt_projs], dim=0)) # (K=4, inner, rank)
self.dt_projs_bias = nn.Parameter(torch.stack([t.bias for t in self.dt_projs], dim=0)) # (K=4, inner)
del self.dt_projs
# 初始化A和D
self.A_logs = self.A_log_init(self.d_state, self.d_inner, copies=4, merge=True) # (K=4, D, N)
self.Ds = self.D_init(self.d_inner, copies=4, merge=True) # (K=4, D, N)
# ss2d
self.forward_core = self.forward_corev0
self.out_norm = nn.LayerNorm(self.d_inner)
self.out_proj = nn.Linear(self.d_inner, self.d_model, bias=bias)
self.dropout = nn.Dropout(dropout) if dropout > 0. else None
@staticmethod
def dt_init(dt_rank, d_inner, dt_scale=1.0, dt_init="random", dt_min=0.001, dt_max=0.1, dt_init_floor=1e-4, **factory_kwargs):
dt_proj = nn.Linear(dt_rank, d_inner, bias=True, **factory_kwargs)
# Initialize special dt projection to preserve variance at initialization
dt_init_std = dt_rank**-0.5 * dt_scale
if dt_init == "constant":
nn.init.constant_(dt_proj.weight, dt_init_std)
elif dt_init == "random":
nn.init.uniform_(dt_proj.weight, -dt_init_std, dt_init_std)
else:
raise NotImplementedError
# Initialize dt bias so that F.softplus(dt_bias) is between dt_min and dt_max
dt = torch.exp(
torch.rand(d_inner, **factory_kwargs) * (math.log(dt_max) - math.log(dt_min))
+ math.log(dt_min)
).clamp(min=dt_init_floor)
# Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
inv_dt = dt + torch.log(-torch.expm1(-dt))
with torch.no_grad():
dt_proj.bias.copy_(inv_dt)
# Our initialization would set all Linear.bias to zero, need to mark this one as _no_reinit
dt_proj.bias._no_reinit = True
return dt_proj
@staticmethod
def A_log_init(d_state, d_inner, copies=1, device=None, merge=True):
# S4D real initialization
A = repeat(
torch.arange(1, d_state + 1, dtype=torch.float32, device=device),
"n -> d n",
d=d_inner,
).contiguous()
A_log = torch.log(A) # Keep A_log in fp32
if copies > 1:
A_log = repeat(A_log, "d n -> r d n", r=copies)
if merge:
A_log = A_log.flatten(0, 1)
A_log = nn.Parameter(A_log)
A_log._no_weight_decay = True
return A_log
@staticmethod
def D_init(d_inner, copies=1, device=None, merge=True):
# D "skip" parameter
D = torch.ones(d_inner, device=device)
if copies > 1:
D = repeat(D, "n1 -> r n1", r=copies)
if merge:
D = D.flatten(0, 1)
D = nn.Parameter(D) # Keep in fp32
D._no_weight_decay = True
return D
def forward_corev0(self, x: torch.Tensor):
self.selective_scan = selective_scan_fn
B, C, H, W = x.shape
L = H * W
K = 4
x_hwwh = torch.stack([x.view(B, -1, L), torch.transpose(x, dim0=2, dim1=3).contiguous().view(B, -1, L)], dim=1).view(B, 2, -1, L)
xs = torch.cat([x_hwwh, torch.flip(x_hwwh, dims=[-1])], dim=1) # (b, k, d, l)
x_dbl = torch.einsum("b k d l, k c d -> b k c l", xs.view(B, K, -1, L), self.x_proj_weight)
dts, Bs, Cs = torch.split(x_dbl, [self.dt_rank, self.d_state, self.d_state], dim=2)
dts = torch.einsum("b k r l, k d r -> b k d l", dts.view(B, K, -1, L), self.dt_projs_weight)
xs = xs.float().view(B, -1, L) # (b, k * d, l)
dts = dts.contiguous().float().view(B, -1, L) # (b, k * d, l)
Bs = Bs.float().view(B, K, -1, L) # (b, k, d_state, l)
Cs = Cs.float().view(B, K, -1, L) # (b, k, d_state, l)
Ds = self.Ds.float().view(-1) # (k * d)
As = -torch.exp(self.A_logs.float()).view(-1, self.d_state) # (k * d, d_state)
dt_projs_bias = self.dt_projs_bias.float().view(-1) # (k * d)
out_y = self.selective_scan(
xs, dts,
As, Bs, Cs, Ds, z=None,
delta_bias=dt_projs_bias,
delta_softplus=True,
return_last_state=False,
).view(B, K, -1, L)
assert out_y.dtype == torch.float
inv_y = torch.flip(out_y[:, 2:4], dims=[-1]).view(B, 2, -1, L)
wh_y = torch.transpose(out_y[:, 1].view(B, -1, W, H), dim0=2, dim1=3).contiguous().view(B, -1, L)
invwh_y = torch.transpose(inv_y[:, 1].view(B, -1, W, H), dim0=2, dim1=3).contiguous().view(B, -1, L)
return out_y[:, 0], inv_y[:, 0], wh_y, invwh_y
def forward(self, x: torch.Tensor):
B, H, W, C = x.shape
xz = self.in_proj(x)
x, z = xz.chunk(2, dim=-1) # (b, h, w, d) # x走的是ss2d的路径
x = x.permute(0, 3, 1, 2).contiguous()
x = self.act(self.conv2d(x)) # (b, d, h, w)
y1, y2, y3, y4 = self.forward_core(x)
assert y1.dtype == torch.float32
y = y1 + y2 + y3 + y4
y = torch.transpose(y, dim0=1, dim1=2).contiguous().view(B, H, W, -1)
y = self.out_norm(y)
y = y * F.silu(z) # 这里的z忘记了一个Linear吧
out = self.out_proj(y)
if self.dropout is not None:
out = self.dropout(out)
return out
class VSSBlock(nn.Module):
def __init__(
self,
hidden_dim: int = 0, # 96
drop_path: float = 0, # 0.2
norm_layer: Callable[..., torch.nn.Module] = partial(nn.LayerNorm, eps=1e-6), # nn.LN
attn_drop_rate: float = 0, # 0
d_state: int = 16,
):
super().__init__()
self.ln_1 = norm_layer(hidden_dim)# 96 0.2 16
self.self_attention = SS2D(d_model=hidden_dim, dropout=attn_drop_rate, d_state=d_state)
self.drop_path = DropPath(drop_path)
def forward(self, input: torch.Tensor):
# print(input.shape, "传入模块的大小")
x = input + self.drop_path(self.self_attention(self.ln_1(input)))
return x
class VSSLayer(nn.Module):
""" A basic Swin Transformer layer for one stage.
Args:
dim (int): Number of input channels.
depth (int): Number of blocks.
drop (float, optional): Dropout rate. Default: 0.0
attn_drop (float, optional): Attention dropout rate. Default: 0.0
drop_path (float | tuple[float], optional): Stochastic depth rate. Default: 0.0
norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm
downsample (nn.Module | None, optional): Downsample layer at the end of the layer. Default: None
use_checkpoint (bool): Whether to use checkpointing to save memory. Default: False.
"""
def __init__( # 以第一个为例
self,
dim, # # 96
depth, # 2
d_state=16,
drop = 0.,
attn_drop=0.,
drop_path=0., # 每一个模块都有一个drop
norm_layer=nn.LayerNorm,
downsample=None, # PatchMergin2D
use_checkpoint=False,
):
super().__init__()
self.dim = dim
self.use_checkpoint = use_checkpoint
self.blocks = nn.ModuleList([
VSSBlock(
hidden_dim=dim, # 96
drop_path=drop_path[i], # 0.2
norm_layer=norm_layer, # nn.LN
attn_drop_rate=attn_drop, # 0
d_state=d_state, # 16
)
for i in range(depth)])
if True: # is this really applied? Yes, but been overriden later in VSSM!
def _init_weights(module: nn.Module):
for name, p in module.named_parameters():
if name in ["out_proj.weight"]:
p = p.clone().detach_() # fake init, just to keep the seed ....
nn.init.kaiming_uniform_(p, a=math.sqrt(5))
self.apply(_init_weights)
if downsample is not None:
self.downsample = downsample(dim=dim, norm_layer=norm_layer)
else:
self.downsample = None
def forward(self, x):
for blk in self.blocks:
x = blk(x)
if self.downsample is not None:
x = self.downsample(x)
return x
class VSSLayer_up(nn.Module):
""" A basic Swin Transformer layer for one stage.
Args:
dim (int): Number of input channels.
depth (int): Number of blocks.
drop (float, optional): Dropout rate. Default: 0.0
attn_drop (float, optional): Attention dropout rate. Default: 0.0
drop_path (float | tuple[float], optional): Stochastic depth rate. Default: 0.0
norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm
downsample (nn.Module | None, optional): Downsample layer at the end of the layer. Default: None
use_checkpoint (bool): Whether to use checkpointing to save memory. Default: False.
"""
def __init__(
self,
dim,
depth,
attn_drop=0.,
drop_path=0.,
norm_layer=nn.LayerNorm,
upsample=None,
use_checkpoint=False,
d_state=16,
**kwargs,
):
super().__init__()
self.dim = dim
self.use_checkpoint = use_checkpoint
self.blocks = nn.ModuleList([
VSSBlock(
hidden_dim=dim,
drop_path=drop_path[i] if isinstance(drop_path, list) else drop_path,
norm_layer=norm_layer,
attn_drop_rate=attn_drop,
d_state=d_state,
)
for i in range(depth)])
if True: # is this really applied? Yes, but been overriden later in VSSM!
def _init_weights(module: nn.Module):
for name, p in module.named_parameters():
if name in ["out_proj.weight"]:
p = p.clone().detach_() # fake init, just to keep the seed ....
nn.init.kaiming_uniform_(p, a=math.sqrt(5))
self.apply(_init_weights)
if upsample is not None:
self.upsample = upsample(dim=dim, norm_layer=norm_layer)
else:
self.upsample = None
def forward(self, x):
if self.upsample is not None:
x = self.upsample(x)
for blk in self.blocks:
if self.use_checkpoint:
x = checkpoint.checkpoint(blk, x)
else:
x = blk(x)
return x
class VSSM(nn.Module):
def __init__(self, patch_size=4, in_chans=3, num_classes=1000, depths=[2, 2, 9, 2], depths_decoder=[2, 9, 2, 2],
dims=[96, 192, 384, 768], dims_decoder=[768, 384, 192, 96], d_state=16, drop_rate=0., attn_drop_rate=0., drop_path_rate=0.1,
norm_layer=nn.LayerNorm, patch_norm=True,
use_checkpoint=False):
super().__init__()
self.num_classes = num_classes # 1
self.num_layers = len(depths) # 4
if isinstance(dims, int):
dims = [int(dims * 2 ** i_layer) for i_layer in range(self.num_layers)]
self.embed_dim = dims[0] # 96
self.num_features = dims[-1] # 768
self.dims = dims # [96, 192, 384, 768]
# 4*4+LN-> b*w*h*c
self.patch_embed = PatchEmbed2D(patch_size=patch_size, in_chans=in_chans, embed_dim=self.embed_dim,
norm_layer=norm_layer if patch_norm else None)
self.pos_drop = nn.Dropout(p=drop_rate)
# 生成对应的sum(depths)随机深度衰减数值 dpr是正序,dpr_decoder是倒序(用到了[start:end:-1] 反向步长)
dpr = [x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))] # stochastic depth decay rule
dpr_decoder = [x.item() for x in torch.linspace(0, drop_path_rate, sum(depths_decoder))][::-1]
self.layers = nn.ModuleList()
for i_layer in range(self.num_layers): # 以第一个为例 num_layers = 4
layer = VSSLayer(
dim=dims[i_layer], # 96
depth=depths[i_layer], # 2
d_state=d_state, # 16
drop=drop_rate, # 0
attn_drop=attn_drop_rate, # 0
drop_path=dpr[sum(depths[:i_layer]):sum(depths[:i_layer + 1])], # ,每一个模块传一个概率值
norm_layer=norm_layer, # nn.LN
downsample=PatchMerging2D if (i_layer < self.num_layers - 1) else None,
use_checkpoint=use_checkpoint,
)
self.layers.append(layer)
self.layers_up = nn.ModuleList()
for i_layer in range(self.num_layers): # 以第一个为例,num_layers=2
layer = VSSLayer_up(
dim=dims_decoder[i_layer], # 768
depth=depths_decoder[i_layer], # 2
d_state=d_state, # 16
drop=drop_rate, # 0
attn_drop=attn_drop_rate, # 0
drop_path=dpr_decoder[sum(depths_decoder[:i_layer]):sum(depths_decoder[:i_layer + 1])],
norm_layer=norm_layer, # nn.LN
upsample=PatchExpand2D if (i_layer != 0) else None,
use_checkpoint=use_checkpoint,
)
self.layers_up.append(layer)
# 输入 64*64*96 ->linear+LN b*256*256*24 96 nn.LN
self.final_up = Final_PatchExpand2D(dim=dims_decoder[-1], dim_scale=4, norm_layer=norm_layer)
# 维度变换 输出b*1*256*256 24 1
self.final_conv = nn.Conv2d(dims_decoder[-1]//4, num_classes, 1)
self.apply(self._init_weights)
def _init_weights(self, m: nn.Module):
"""
out_proj.weight which is previously initilized in VSSBlock, would be cleared in nn.Linear
no fc.weight found in the any of the model parameters
no nn.Embedding found in the any of the model parameters
so the thing is, VSSBlock initialization is useless
Conv2D is not intialized !!!
"""
if isinstance(m, nn.Linear):
trunc_normal_(m.weight, std=.02)
if isinstance(m, nn.Linear) and m.bias is not None:
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.LayerNorm):
nn.init.constant_(m.bias, 0)
nn.init.constant_(m.weight, 1.0)
def forward_features(self, x):
skip_list = []
x = self.patch_embed(x)
x = self.pos_drop(x)
for layer in self.layers:
skip_list.append(x)
x = layer(x)
return x, skip_list
def forward_features_up(self, x, skip_list):
for inx, layer_up in enumerate(self.layers_up):
if inx == 0:
x = layer_up(x)
else:
x = layer_up(x+skip_list[-inx])
return x
def forward_final(self, x):
# input 3*64*64*96 out=3 256 256 24
x = self.final_up(x)
x = x.permute(0,3,1,2)
# out=3 24 256 256
x = self.final_conv(x)
return x
def forward(self, x):
x, skip_list = self.forward_features(x)
x = self.forward_features_up(x, skip_list)
x = self.forward_final(x)
return x
import torch
from torch import nn
class VMUNet(nn.Module):
def __init__(self,
input_channels=3,
num_classes=1,
depths=[2, 2, 9, 2],
depths_decoder=[2, 9, 2, 2],
drop_path_rate=0.2,
load_ckpt_path=None,
):
super().__init__()
self.load_ckpt_path = load_ckpt_path
self.num_classes = num_classes
self.vmunet = VSSM(in_chans=input_channels, # 3
num_classes=num_classes, # 1
depths=depths, # [2,2,9,2]
depths_decoder=depths_decoder, # [2,9,2,2]
drop_path_rate=drop_path_rate, # 0.2
)
def forward(self, x):
if x.size()[1] == 1:
x = x.repeat(1,3,1,1)
logits = self.vmunet(x)
if self.num_classes == 1: return torch.sigmoid(logits)
else: return logits
# 加载预训练权重,暂时用不到
def load_from(self):
if self.load_ckpt_path is not None:
model_dict = self.vmunet.state_dict()
modelCheckpoint = torch.load(self.load_ckpt_path)
pretrained_dict = modelCheckpoint['model']
# 过滤操作
new_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict.keys()}
model_dict.update(new_dict)
# 打印出来,更新了多少的参数
print('Total model_dict: {}, Total pretrained_dict: {}, update: {}'.format(len(model_dict), len(pretrained_dict), len(new_dict)))
self.vmunet.load_state_dict(model_dict)
not_loaded_keys = [k for k in pretrained_dict.keys() if k not in new_dict.keys()]
print('Not loaded keys:', not_loaded_keys)
print("encoder loaded finished!")
model_dict = self.vmunet.state_dict()
modelCheckpoint = torch.load(self.load_ckpt_path)
pretrained_odict = modelCheckpoint['model']
pretrained_dict = {}
for k, v in pretrained_odict.items():
if 'layers.0' in k:
new_k = k.replace('layers.0', 'layers_up.3')
pretrained_dict[new_k] = v
elif 'layers.1' in k:
new_k = k.replace('layers.1', 'layers_up.2')
pretrained_dict[new_k] = v
elif 'layers.2' in k:
new_k = k.replace('layers.2', 'layers_up.1')
pretrained_dict[new_k] = v
elif 'layers.3' in k:
new_k = k.replace('layers.3', 'layers_up.0')
pretrained_dict[new_k] = v
# 过滤操作
new_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict.keys()}
model_dict.update(new_dict)
# 打印出来,更新了多少的参数
print('Total model_dict: {}, Total pretrained_dict: {}, update: {}'.format(len(model_dict), len(pretrained_dict), len(new_dict)))
self.vmunet.load_state_dict(model_dict)
# 找到没有加载的键(keys)
not_loaded_keys = [k for k in pretrained_dict.keys() if k not in new_dict.keys()]
print('Not loaded keys:', not_loaded_keys)
print("decoder loaded finished!")
x = torch.randn(3, 3, 256, 256).to("cuda:0")
net = VMUNet(3,1).to("cuda:0")
print(net(x).shape)