JustAGeek commited on
Commit
08306d3
·
verified ·
1 Parent(s): f4a53ad

Delete duplicate/old file: \/mamba_model.py

Browse files
Files changed (1) hide show
  1. ///mamba_model.py +0 -273
///mamba_model.py DELETED
@@ -1,273 +0,0 @@
1
- #!/usr/bin/env python
2
- """
3
- mamba_model.py
4
- Mamba-DLoc: Replaces the ResNet bottleneck in DLoc with Mamba selective
5
- state-space (S6) blocks for efficient long-range sequence modelling.
6
-
7
- Architecture overview
8
- ---------------------
9
- CNN Stem (4 -> 128 channels, 2x spatial downsample)
10
- -> Tokenize (flatten spatial -> sequence)
11
- -> 4x MambaResidualBlock (LayerNorm -> Mamba -> residual)
12
- -> Reshape back to spatial
13
- -> CNN Decoder (upsample back to original resolution)
14
-
15
- Input: [B, 4, 161, 361] AoA heatmaps
16
- Output: [B, 1, 161, 361] location heatmap
17
- """
18
-
19
- import math
20
- import torch
21
- import torch.nn as nn
22
- import torch.nn.functional as F
23
-
24
- # ---------------------------------------------------------------------------
25
- # Try to import the CUDA-optimised Mamba implementation (Linux + CUDA only).
26
- # Fall back to a pure-PyTorch implementation on Windows / CPU.
27
- # ---------------------------------------------------------------------------
28
- HAS_MAMBA_SSM = False
29
- try:
30
- from mamba_ssm import Mamba as CudaMamba # type: ignore
31
- HAS_MAMBA_SSM = True
32
- except ImportError:
33
- CudaMamba = None
34
-
35
-
36
- # ===================================================================
37
- # Pure-PyTorch Mamba (S6 selective scan) - works on any backend
38
- # ===================================================================
39
- class PurePytorchMamba(nn.Module):
40
- """
41
- Minimal re-implementation of the Mamba selective-scan block (S6).
42
-
43
- Interface: [B, L, D] -> [B, L, D]
44
-
45
- Parameters
46
- ----------
47
- d_model : int - input / output dimension (D)
48
- d_state : int - SSM hidden state dimension (N)
49
- d_conv : int - local convolution kernel width
50
- expand : int - expansion factor for inner dimension (E = expand * D)
51
- """
52
-
53
- def __init__(self, d_model: int = 128, d_state: int = 16,
54
- d_conv: int = 4, expand: int = 2):
55
- super().__init__()
56
- self.d_model = d_model
57
- self.d_state = d_state
58
- self.d_conv = d_conv
59
- self.expand = expand
60
- self.d_inner = d_model * expand # E
61
- self.dt_rank = math.ceil(d_model / 16) # rank for delta-t projection
62
-
63
- # --- input projection: D -> 2*E (main path + gate) ---
64
- self.in_proj = nn.Linear(d_model, 2 * self.d_inner, bias=False)
65
-
66
- # --- causal depthwise conv on main path ---
67
- self.conv1d = nn.Conv1d(
68
- self.d_inner, self.d_inner,
69
- kernel_size=d_conv, padding=d_conv - 1, # causal: trim later
70
- groups=self.d_inner, bias=True,
71
- )
72
-
73
- # --- SSM parameter projections ---
74
- # from the convolved main path (E) -> dt_rank + 2*N
75
- self.x_proj = nn.Linear(self.d_inner, self.dt_rank + 2 * d_state, bias=False)
76
-
77
- # dt_rank -> E (broadcast delta across inner dim)
78
- self.dt_proj = nn.Linear(self.dt_rank, self.d_inner, bias=True)
79
-
80
- # --- learnable SSM parameters ---
81
- # A_log: [E, N] initialised as log(1..N) broadcast to each inner dim
82
- A = torch.arange(1, d_state + 1, dtype=torch.float32).unsqueeze(0).expand(self.d_inner, -1)
83
- self.A_log = nn.Parameter(torch.log(A)) # learned in log-space
84
-
85
- # D (skip connection scalar per inner dim)
86
- self.D = nn.Parameter(torch.ones(self.d_inner))
87
-
88
- # --- output projection: E -> D ---
89
- self.out_proj = nn.Linear(self.d_inner, d_model, bias=False)
90
-
91
- # ---------------------------------------------------------------
92
- def forward(self, x: torch.Tensor) -> torch.Tensor:
93
- """
94
- x : [B, L, D]
95
- returns : [B, L, D]
96
- """
97
- B, L, _ = x.shape
98
-
99
- # --- input projection & split ---
100
- xz = self.in_proj(x) # [B, L, 2E]
101
- x_main, z = xz.chunk(2, dim=-1) # each [B, L, E]
102
-
103
- # --- causal conv1d on main path ---
104
- # conv1d expects [B, E, L]
105
- x_main = x_main.transpose(1, 2) # [B, E, L]
106
- x_main = self.conv1d(x_main)[:, :, :L] # causal trim
107
- x_main = x_main.transpose(1, 2) # [B, L, E]
108
- x_main = F.silu(x_main)
109
-
110
- # --- SSM parameters ---
111
- x_ssm = self.x_proj(x_main) # [B, L, dt_rank + 2N]
112
- dt, B_param, C_param = x_ssm.split(
113
- [self.dt_rank, self.d_state, self.d_state], dim=-1
114
- )
115
- # dt: [B, L, dt_rank] -> [B, L, E]
116
- dt = F.softplus(self.dt_proj(dt)) # [B, L, E]
117
-
118
- # A: [E, N] (negative for stability)
119
- A = -torch.exp(self.A_log) # [E, N]
120
-
121
- # --- discretize ---
122
- # dA: [B, L, E, N]
123
- dA = torch.exp(dt.unsqueeze(-1) * A.unsqueeze(0).unsqueeze(0))
124
- # dB: [B, L, E, N]
125
- dB = dt.unsqueeze(-1) * B_param.unsqueeze(2) # broadcast E
126
-
127
- # --- selective scan (sequential, kept simple for correctness) ---
128
- # x_main: [B, L, E] -> weight input
129
- # h: [B, E, N] SSM hidden state
130
- h = torch.zeros(B, self.d_inner, self.d_state,
131
- device=x.device, dtype=x.dtype)
132
- ys = []
133
- for t in range(L):
134
- h = dA[:, t, :, :] * h + dB[:, t, :, :] * x_main[:, t, :].unsqueeze(-1)
135
- y_t = (h * C_param[:, t, :].unsqueeze(1)).sum(dim=-1) # [B, E]
136
- ys.append(y_t)
137
- y = torch.stack(ys, dim=1) # [B, L, E]
138
-
139
- # --- skip connection ---
140
- y = y + x_main * self.D.unsqueeze(0).unsqueeze(0)
141
-
142
- # --- gate ---
143
- y = y * F.silu(z)
144
-
145
- # --- output projection ---
146
- return self.out_proj(y) # [B, L, D]
147
-
148
-
149
- # ===================================================================
150
- # Mamba residual block: LayerNorm -> Mamba -> residual add
151
- # ===================================================================
152
- class MambaResidualBlock(nn.Module):
153
- """
154
- Pre-norm residual block wrapping a Mamba layer.
155
-
156
- Interface: [B, L, D] -> [B, L, D]
157
- Uses the CUDA Mamba kernel when available, otherwise falls back to
158
- PurePytorchMamba.
159
- """
160
-
161
- def __init__(self, d_model: int = 128, d_state: int = 16,
162
- d_conv: int = 4, expand: int = 2):
163
- super().__init__()
164
- self.norm = nn.LayerNorm(d_model)
165
- if HAS_MAMBA_SSM and CudaMamba is not None:
166
- self.mamba = CudaMamba(
167
- d_model=d_model, d_state=d_state,
168
- d_conv=d_conv, expand=expand,
169
- )
170
- else:
171
- self.mamba = PurePytorchMamba(
172
- d_model=d_model, d_state=d_state,
173
- d_conv=d_conv, expand=expand,
174
- )
175
-
176
- def forward(self, x: torch.Tensor) -> torch.Tensor:
177
- return x + self.mamba(self.norm(x))
178
-
179
-
180
- # ===================================================================
181
- # MambaDLocNet - full end-to-end model
182
- # ===================================================================
183
- class MambaDLocNet(nn.Module):
184
- """
185
- Mamba-DLoc: CNN stem -> Mamba sequence blocks -> CNN decoder.
186
-
187
- Input: [B, 4, 161, 361]
188
- Output: [B, 1, 161, 361]
189
- """
190
-
191
- # Spatial sizes at each resolution level (full, mid, low)
192
- SPATIAL_SIZES = [(161, 361), (81, 181), (41, 91)]
193
-
194
- def __init__(self, input_nc: int = 4, d_model: int = 128,
195
- n_mamba_blocks: int = 4,
196
- d_state: int = 16, d_conv: int = 4, expand: int = 2):
197
- super().__init__()
198
- self.d_model = d_model
199
-
200
- # ------ CNN Stem (encoder) ------
201
- self.stem = nn.Sequential(
202
- # [B,input_nc,161,361] -> [B,32,161,361]
203
- nn.Conv2d(input_nc, 32, kernel_size=3, stride=1, padding=1, bias=False),
204
- nn.BatchNorm2d(32),
205
- nn.ReLU(inplace=True),
206
- # [B,32,161,361] -> [B,64,81,181]
207
- nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1, bias=False),
208
- nn.BatchNorm2d(64),
209
- nn.ReLU(inplace=True),
210
- # [B,64,81,181] -> [B,128,41,91]
211
- nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=1, bias=False),
212
- nn.BatchNorm2d(128),
213
- nn.ReLU(inplace=True),
214
- )
215
-
216
- # ------ Mamba sequence blocks ------
217
- self.mamba_blocks = nn.Sequential(*[
218
- MambaResidualBlock(d_model=d_model, d_state=d_state,
219
- d_conv=d_conv, expand=expand)
220
- for _ in range(n_mamba_blocks)
221
- ])
222
-
223
- # ------ CNN Decoder ------
224
- # Stage 1: [B,128,41,91] -> interpolate to (81,181) -> conv 128->64
225
- self.dec_conv1 = nn.Sequential(
226
- nn.Conv2d(128, 64, kernel_size=3, stride=1, padding=1, bias=False),
227
- nn.BatchNorm2d(64),
228
- nn.ReLU(inplace=True),
229
- )
230
- # Stage 2: [B,64,81,181] -> interpolate to (161,361) -> conv 64->32
231
- self.dec_conv2 = nn.Sequential(
232
- nn.Conv2d(64, 32, kernel_size=3, stride=1, padding=1, bias=False),
233
- nn.BatchNorm2d(32),
234
- nn.ReLU(inplace=True),
235
- )
236
- # Stage 3: 1x1 head -> sigmoid
237
- self.dec_head = nn.Sequential(
238
- nn.Conv2d(32, 1, kernel_size=1, bias=True),
239
- nn.Sigmoid(),
240
- )
241
-
242
- # ---------------------------------------------------------------
243
- def forward(self, x: torch.Tensor) -> torch.Tensor:
244
- """
245
- x : [B, 4, 161, 361]
246
- returns : [B, 1, 161, 361]
247
- """
248
- B = x.size(0)
249
-
250
- # --- CNN Stem ---
251
- feat = self.stem(x) # [B, 128, 41, 91]
252
-
253
- # --- Tokenize: flatten spatial dims into sequence ---
254
- _, C, H, W = feat.shape
255
- tokens = feat.reshape(B, C, H * W).permute(0, 2, 1) # [B, 3731, 128]
256
-
257
- # --- Mamba blocks ---
258
- tokens = self.mamba_blocks(tokens) # [B, 3731, 128]
259
-
260
- # --- Reshape back to spatial ---
261
- feat = tokens.permute(0, 2, 1).reshape(B, C, H, W) # [B, 128, 41, 91]
262
-
263
- # --- CNN Decoder ---
264
- feat = F.interpolate(feat, size=self.SPATIAL_SIZES[1],
265
- mode='bilinear', align_corners=False) # [B,128,81,181]
266
- feat = self.dec_conv1(feat) # [B,64,81,181]
267
-
268
- feat = F.interpolate(feat, size=self.SPATIAL_SIZES[0],
269
- mode='bilinear', align_corners=False) # [B,64,161,361]
270
- feat = self.dec_conv2(feat) # [B,32,161,361]
271
-
272
- out = self.dec_head(feat) # [B,1,161,361]
273
- return out