Equimo 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Equimo-0.1.0.dist-info/LICENSE.md +21 -0
- Equimo-0.1.0.dist-info/METADATA +104 -0
- Equimo-0.1.0.dist-info/RECORD +29 -0
- Equimo-0.1.0.dist-info/WHEEL +5 -0
- Equimo-0.1.0.dist-info/top_level.txt +1 -0
- equimo/__init__.py +0 -0
- equimo/layers/__init__.py +0 -0
- equimo/layers/attention.py +1526 -0
- equimo/layers/convolution.py +329 -0
- equimo/layers/downsample.py +58 -0
- equimo/layers/dropout.py +217 -0
- equimo/layers/ffn.py +184 -0
- equimo/layers/generic.py +68 -0
- equimo/layers/mamba.py +169 -0
- equimo/layers/norm.py +87 -0
- equimo/layers/patch.py +399 -0
- equimo/layers/posemb.py +416 -0
- equimo/layers/sharing.py +160 -0
- equimo/layers/squeeze_excite.py +143 -0
- equimo/models/__init__.py +7 -0
- equimo/models/emamodel.py +99 -0
- equimo/models/fastervit.py +488 -0
- equimo/models/mlla.py +166 -0
- equimo/models/partialformer.py +407 -0
- equimo/models/shvit.py +376 -0
- equimo/models/vit.py +472 -0
- equimo/models/vssd.py +189 -0
- equimo/ops/scan.py +246 -0
- equimo/utils.py +127 -0
|
@@ -0,0 +1,1526 @@
|
|
|
1
|
+
from typing import Callable, List, Optional, Tuple
|
|
2
|
+
|
|
3
|
+
import equinox as eqx
|
|
4
|
+
import jax
|
|
5
|
+
import jax.numpy as jnp
|
|
6
|
+
import jax.random as jr
|
|
7
|
+
from einops import rearrange, reduce
|
|
8
|
+
from jaxtyping import Array, Float, PRNGKeyArray
|
|
9
|
+
|
|
10
|
+
from equimo.layers.dropout import Dropout, DropPathAdd
|
|
11
|
+
from equimo.layers.ffn import Mlp
|
|
12
|
+
from equimo.layers.mamba import Mamba2Mixer
|
|
13
|
+
from equimo.layers.norm import LayerScale
|
|
14
|
+
from equimo.layers.posemb import PosCNN2D, PosEmbMLPSwinv1D, PosEmbMLPSwinv2D, RoPE
|
|
15
|
+
from equimo.utils import nearest_power_of_2_divisor
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Attention(eqx.Module):
|
|
19
|
+
"""Multi-head self attention module.
|
|
20
|
+
|
|
21
|
+
A standard transformer-style attention implementation with query, key and value
|
|
22
|
+
projections followed by scaled dot-product attention.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
dim: Total dimension of the input/output
|
|
26
|
+
num_heads: Number of attention heads
|
|
27
|
+
head_dim: Dimension of each attention head (dim // num_heads)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
dim: int = eqx.field(static=True)
|
|
31
|
+
num_heads: int = eqx.field(static=True)
|
|
32
|
+
head_dim: int = eqx.field(static=True)
|
|
33
|
+
|
|
34
|
+
qkv: eqx.nn.Linear
|
|
35
|
+
proj: eqx.nn.Linear
|
|
36
|
+
q_norm: eqx.Module
|
|
37
|
+
k_norm: eqx.Module
|
|
38
|
+
attn_drop: Dropout
|
|
39
|
+
proj_drop: Dropout
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
dim: int,
|
|
44
|
+
num_heads: int,
|
|
45
|
+
*,
|
|
46
|
+
key: PRNGKeyArray,
|
|
47
|
+
qkv_bias: bool = True,
|
|
48
|
+
proj_bias: bool = True,
|
|
49
|
+
qk_norm: bool = False,
|
|
50
|
+
attn_drop: float = 0.0,
|
|
51
|
+
proj_drop: float = 0.0,
|
|
52
|
+
norm_layer: eqx.Module = eqx.nn.LayerNorm,
|
|
53
|
+
**kwargs,
|
|
54
|
+
):
|
|
55
|
+
self.num_heads = num_heads
|
|
56
|
+
self.head_dim = dim // num_heads
|
|
57
|
+
self.dim = dim
|
|
58
|
+
|
|
59
|
+
assert self.head_dim * num_heads == dim, "dim must be divisible by num_heads"
|
|
60
|
+
|
|
61
|
+
key_qkv, key_proj = jr.split(key, 2)
|
|
62
|
+
self.qkv = eqx.nn.Linear(dim, dim * 3, use_bias=qkv_bias, key=key_qkv)
|
|
63
|
+
self.proj = eqx.nn.Linear(dim, dim, use_bias=proj_bias, key=key_proj)
|
|
64
|
+
|
|
65
|
+
self.q_norm = norm_layer(dim) if qk_norm else eqx.nn.Identity()
|
|
66
|
+
self.k_norm = norm_layer(dim) if qk_norm else eqx.nn.Identity()
|
|
67
|
+
|
|
68
|
+
self.attn_drop = Dropout(attn_drop)
|
|
69
|
+
self.proj_drop = Dropout(proj_drop)
|
|
70
|
+
|
|
71
|
+
def __call__(
|
|
72
|
+
self,
|
|
73
|
+
x: Float[Array, "seqlen dim"],
|
|
74
|
+
enable_dropout: bool,
|
|
75
|
+
key: PRNGKeyArray,
|
|
76
|
+
) -> Float[Array, "seqlen dim"]:
|
|
77
|
+
key1, key2 = jr.split(key, 2)
|
|
78
|
+
|
|
79
|
+
qkv = jax.vmap(self.qkv)(x)
|
|
80
|
+
qkv = rearrange(
|
|
81
|
+
qkv,
|
|
82
|
+
"s (n h d) -> n h s d",
|
|
83
|
+
n=3,
|
|
84
|
+
h=self.num_heads,
|
|
85
|
+
d=self.dim // self.num_heads,
|
|
86
|
+
)
|
|
87
|
+
q, k, v = qkv
|
|
88
|
+
q = jax.vmap(jax.vmap(self.q_norm))(q)
|
|
89
|
+
k = jax.vmap(jax.vmap(self.k_norm))(k)
|
|
90
|
+
|
|
91
|
+
attn = jnp.einsum("hqd,hkd->hqk", q, k) / jnp.sqrt(self.head_dim)
|
|
92
|
+
attn = jax.nn.softmax(attn, axis=-1)
|
|
93
|
+
attn = self.attn_drop(attn, inference=not enable_dropout, key=key1)
|
|
94
|
+
|
|
95
|
+
x = jnp.einsum("hqk,hvd->hqd", attn, v)
|
|
96
|
+
x = rearrange(x, "h s d -> s (h d)")
|
|
97
|
+
x = jax.vmap(self.proj)(x)
|
|
98
|
+
x = self.proj_drop(x, inference=not enable_dropout, key=key2)
|
|
99
|
+
|
|
100
|
+
return x
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class WindowedAttention(eqx.Module):
|
|
104
|
+
"""Windowed multi-head self attention module.
|
|
105
|
+
|
|
106
|
+
Applies self-attention within local windows of the input sequence.
|
|
107
|
+
Includes relative position embeddings within each window.
|
|
108
|
+
|
|
109
|
+
Attributes:
|
|
110
|
+
dim: Total dimension of the input/output
|
|
111
|
+
num_heads: Number of attention heads
|
|
112
|
+
head_dim: Dimension of each attention head (dim // num_heads)
|
|
113
|
+
resolution: Size of each attention window (window_size)
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
dim: int = eqx.field(static=True)
|
|
117
|
+
num_heads: int = eqx.field(static=True)
|
|
118
|
+
head_dim: int = eqx.field(static=True)
|
|
119
|
+
resolution: int = eqx.field(static=True)
|
|
120
|
+
|
|
121
|
+
qkv: eqx.nn.Linear
|
|
122
|
+
proj: eqx.nn.Linear
|
|
123
|
+
q_norm: eqx.Module
|
|
124
|
+
k_norm: eqx.Module
|
|
125
|
+
attn_drop: Dropout
|
|
126
|
+
proj_drop: Dropout
|
|
127
|
+
pos_emb_funct: PosEmbMLPSwinv2D
|
|
128
|
+
|
|
129
|
+
def __init__(
|
|
130
|
+
self,
|
|
131
|
+
dim: int,
|
|
132
|
+
num_heads: int,
|
|
133
|
+
resolution: int,
|
|
134
|
+
seq_len: int,
|
|
135
|
+
*,
|
|
136
|
+
key: PRNGKeyArray,
|
|
137
|
+
qkv_bias: bool = True,
|
|
138
|
+
proj_bias: bool = True,
|
|
139
|
+
qk_norm: bool = False,
|
|
140
|
+
attn_drop: float = 0.0,
|
|
141
|
+
proj_drop: float = 0.0,
|
|
142
|
+
norm_layer: eqx.Module = eqx.nn.LayerNorm,
|
|
143
|
+
**kwargs,
|
|
144
|
+
):
|
|
145
|
+
self.num_heads = num_heads
|
|
146
|
+
self.head_dim = dim // num_heads
|
|
147
|
+
self.dim = dim
|
|
148
|
+
self.resolution = resolution
|
|
149
|
+
|
|
150
|
+
assert self.head_dim * num_heads == dim, "dim must be divisible by num_heads"
|
|
151
|
+
|
|
152
|
+
key_qkv, key_proj, key_posemb = jr.split(key, 3)
|
|
153
|
+
self.qkv = eqx.nn.Linear(dim, dim * 3, use_bias=qkv_bias, key=key_qkv)
|
|
154
|
+
self.proj = eqx.nn.Linear(dim, dim, use_bias=proj_bias, key=key_proj)
|
|
155
|
+
|
|
156
|
+
self.pos_emb_funct = PosEmbMLPSwinv2D(
|
|
157
|
+
window_size=[resolution, resolution],
|
|
158
|
+
pretrained_window_size=[resolution, resolution],
|
|
159
|
+
num_heads=num_heads,
|
|
160
|
+
seq_len=seq_len,
|
|
161
|
+
key=key_posemb,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
self.q_norm = norm_layer(dim) if qk_norm else eqx.nn.Identity()
|
|
165
|
+
self.k_norm = norm_layer(dim) if qk_norm else eqx.nn.Identity()
|
|
166
|
+
|
|
167
|
+
self.attn_drop = Dropout(attn_drop)
|
|
168
|
+
self.proj_drop = Dropout(proj_drop)
|
|
169
|
+
|
|
170
|
+
def __call__(
|
|
171
|
+
self,
|
|
172
|
+
x: Float[Array, "seqlen dim"],
|
|
173
|
+
enable_dropout: bool,
|
|
174
|
+
key: PRNGKeyArray,
|
|
175
|
+
) -> Float[Array, "seqlen dim"]:
|
|
176
|
+
key1, key2 = jr.split(key, 2)
|
|
177
|
+
|
|
178
|
+
qkv = jax.vmap(self.qkv)(x)
|
|
179
|
+
qkv = rearrange(
|
|
180
|
+
qkv,
|
|
181
|
+
"s (n h d) -> n h s d",
|
|
182
|
+
n=3,
|
|
183
|
+
h=self.num_heads,
|
|
184
|
+
d=self.dim // self.num_heads,
|
|
185
|
+
)
|
|
186
|
+
q, k, v = qkv
|
|
187
|
+
q = jax.vmap(jax.vmap(self.q_norm))(q)
|
|
188
|
+
k = jax.vmap(jax.vmap(self.k_norm))(k)
|
|
189
|
+
|
|
190
|
+
attn = jnp.einsum("hqd,hkd->hqk", q, k) / jnp.sqrt(self.head_dim)
|
|
191
|
+
attn = self.pos_emb_funct(attn, self.resolution**2)
|
|
192
|
+
attn = jax.nn.softmax(attn, axis=-1)
|
|
193
|
+
attn = self.attn_drop(attn, inference=not enable_dropout, key=key1)
|
|
194
|
+
|
|
195
|
+
x = jnp.einsum("hqk,hvd->hqd", attn, v)
|
|
196
|
+
x = rearrange(x, "h s d -> s (h d)")
|
|
197
|
+
x = jax.vmap(self.proj)(x)
|
|
198
|
+
x = self.proj_drop(x, inference=not enable_dropout, key=key2)
|
|
199
|
+
|
|
200
|
+
return x
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class AttentionBlock(eqx.Module):
|
|
204
|
+
"""Standard transformer block with attention and MLP.
|
|
205
|
+
|
|
206
|
+
Implements a full transformer block with:
|
|
207
|
+
- Multi-head self attention
|
|
208
|
+
- Layer normalization
|
|
209
|
+
- MLP feed-forward network
|
|
210
|
+
- Residual connections
|
|
211
|
+
- Optional layer scaling
|
|
212
|
+
- Dropout paths
|
|
213
|
+
|
|
214
|
+
Attributes:
|
|
215
|
+
norm1: First layer normalization
|
|
216
|
+
norm2: Second layer normalization
|
|
217
|
+
ls1: First layer scale (optional)
|
|
218
|
+
ls2: Second layer scale (optional)
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
norm1: eqx.Module
|
|
222
|
+
norm2: eqx.Module
|
|
223
|
+
ls1: LayerScale
|
|
224
|
+
ls2: LayerScale
|
|
225
|
+
attn: eqx.Module
|
|
226
|
+
mlp: eqx.Module
|
|
227
|
+
drop_path1: DropPathAdd
|
|
228
|
+
drop_path2: DropPathAdd
|
|
229
|
+
|
|
230
|
+
def __init__(
|
|
231
|
+
self,
|
|
232
|
+
dim: int,
|
|
233
|
+
num_heads: int,
|
|
234
|
+
*,
|
|
235
|
+
key: PRNGKeyArray,
|
|
236
|
+
mlp_ratio: float = 4.0,
|
|
237
|
+
drop_path: float | List[float] = 0.0,
|
|
238
|
+
qkv_bias: bool = True,
|
|
239
|
+
proj_bias: bool = True,
|
|
240
|
+
qk_norm: bool = False,
|
|
241
|
+
attn_drop: float = 0.0,
|
|
242
|
+
proj_drop: float = 0.0,
|
|
243
|
+
act_layer: Callable = jax.nn.gelu,
|
|
244
|
+
attn_layer: eqx.Module = Attention,
|
|
245
|
+
ffn_layer: eqx.Module = Mlp,
|
|
246
|
+
ffn_bias: bool = True,
|
|
247
|
+
norm_layer: eqx.Module = eqx.nn.LayerNorm,
|
|
248
|
+
init_values: float | None = None,
|
|
249
|
+
**kwargs,
|
|
250
|
+
):
|
|
251
|
+
key_attn, key_mlp = jr.split(key, 2)
|
|
252
|
+
|
|
253
|
+
if isinstance(drop_path, list):
|
|
254
|
+
if len(drop_path) != 2:
|
|
255
|
+
raise AssertionError(
|
|
256
|
+
f"`drop_path` needs to have 2 elements, got {len(drop_path)} ({drop_path})."
|
|
257
|
+
)
|
|
258
|
+
dr1, dr2 = drop_path
|
|
259
|
+
dr1 = float(dr1)
|
|
260
|
+
dr2 = float(dr2)
|
|
261
|
+
else:
|
|
262
|
+
dr1 = dr2 = float(drop_path)
|
|
263
|
+
|
|
264
|
+
self.norm1 = norm_layer(dim)
|
|
265
|
+
self.norm2 = norm_layer(dim)
|
|
266
|
+
|
|
267
|
+
if init_values:
|
|
268
|
+
self.ls1 = LayerScale(dim, init_values=init_values)
|
|
269
|
+
self.ls2 = LayerScale(dim, init_values=init_values)
|
|
270
|
+
else:
|
|
271
|
+
self.ls1 = self.ls2 = eqx.nn.Identity()
|
|
272
|
+
|
|
273
|
+
self.attn = attn_layer(
|
|
274
|
+
dim=dim,
|
|
275
|
+
num_heads=num_heads,
|
|
276
|
+
qkv_bias=qkv_bias,
|
|
277
|
+
proj_bias=proj_bias,
|
|
278
|
+
qk_norm=qk_norm,
|
|
279
|
+
attn_drop=attn_drop,
|
|
280
|
+
proj_drop=proj_drop,
|
|
281
|
+
norm_layer=norm_layer,
|
|
282
|
+
key=key_attn,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
self.mlp = ffn_layer(
|
|
286
|
+
in_features=dim,
|
|
287
|
+
hidden_features=int(dim * mlp_ratio),
|
|
288
|
+
act_layer=act_layer,
|
|
289
|
+
dropout_rate=proj_drop,
|
|
290
|
+
bias=ffn_bias,
|
|
291
|
+
key=key_mlp,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
self.drop_path1 = DropPathAdd(dr1)
|
|
295
|
+
self.drop_path2 = DropPathAdd(dr2)
|
|
296
|
+
|
|
297
|
+
def __call__(
|
|
298
|
+
self,
|
|
299
|
+
x: Float[Array, "seqlen dim"],
|
|
300
|
+
enable_dropout: bool,
|
|
301
|
+
key: PRNGKeyArray,
|
|
302
|
+
) -> Float[Array, "seqlen dim"]:
|
|
303
|
+
key_attn, key_mlp, key_dr1, key_dr2 = jr.split(key, 4)
|
|
304
|
+
|
|
305
|
+
x = self.drop_path1(
|
|
306
|
+
x,
|
|
307
|
+
self.ls1(
|
|
308
|
+
self.attn(
|
|
309
|
+
jax.vmap(self.norm1)(x),
|
|
310
|
+
enable_dropout,
|
|
311
|
+
key=key_attn,
|
|
312
|
+
)
|
|
313
|
+
),
|
|
314
|
+
inference=not enable_dropout,
|
|
315
|
+
key=key_dr1,
|
|
316
|
+
)
|
|
317
|
+
x = self.drop_path2(
|
|
318
|
+
x,
|
|
319
|
+
self.ls2(
|
|
320
|
+
self.mlp(
|
|
321
|
+
jax.vmap(self.norm2)(x),
|
|
322
|
+
enable_dropout,
|
|
323
|
+
key=key_mlp,
|
|
324
|
+
)
|
|
325
|
+
),
|
|
326
|
+
inference=not enable_dropout,
|
|
327
|
+
key=key_dr2,
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
return x
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
class HATBlock(eqx.Module):
|
|
334
|
+
"""Hierarchical Attention Transformer block.
|
|
335
|
+
|
|
336
|
+
Implements hierarchical attention with:
|
|
337
|
+
- Local window attention
|
|
338
|
+
- Carrier tokens for global interaction
|
|
339
|
+
- Optional token propagation in final layer
|
|
340
|
+
- Hierarchical structure with different spatial resolutions
|
|
341
|
+
|
|
342
|
+
Attributes:
|
|
343
|
+
do_propagation: Whether to propagate carrier token info in last layer
|
|
344
|
+
sr_ratio: Spatial reduction ratio for hierarchical structure
|
|
345
|
+
window_size: Size of local attention windows
|
|
346
|
+
ct_size: Size of carrier token grid
|
|
347
|
+
last: Whether this is the last block in the network
|
|
348
|
+
"""
|
|
349
|
+
|
|
350
|
+
do_propagation: bool = eqx.field(static=True)
|
|
351
|
+
sr_ratio: float = eqx.field(static=True)
|
|
352
|
+
window_size: int = eqx.field(static=True)
|
|
353
|
+
ct_size: int = eqx.field(static=True)
|
|
354
|
+
last: bool = eqx.field(static=True)
|
|
355
|
+
|
|
356
|
+
norm1: eqx.Module
|
|
357
|
+
norm2: eqx.Module
|
|
358
|
+
ls1: LayerScale
|
|
359
|
+
ls2: LayerScale
|
|
360
|
+
attn: eqx.Module
|
|
361
|
+
mlp: eqx.Module
|
|
362
|
+
drop_path1: DropPathAdd
|
|
363
|
+
drop_path2: DropPathAdd
|
|
364
|
+
pos_embed: PosEmbMLPSwinv1D
|
|
365
|
+
|
|
366
|
+
hat_norm1: Optional[eqx.Module] = eqx.field(default=None)
|
|
367
|
+
hat_norm2: Optional[eqx.Module] = eqx.field(default=None)
|
|
368
|
+
hat_norm3: Optional[eqx.Module] = eqx.field(default=None)
|
|
369
|
+
hat_attn: Optional[eqx.Module] = eqx.field(default=None)
|
|
370
|
+
hat_mlp: Optional[eqx.Module] = eqx.field(default=None)
|
|
371
|
+
hat_drop_path: Optional[DropPathAdd] = eqx.field(default=None)
|
|
372
|
+
hat_pos_embed: Optional[PosEmbMLPSwinv1D] = eqx.field(default=None)
|
|
373
|
+
hat_ls1: Optional[LayerScale] = eqx.field(default=None)
|
|
374
|
+
hat_ls2: Optional[LayerScale] = eqx.field(default=None)
|
|
375
|
+
hat_ls3: Optional[LayerScale] = eqx.field(default=None)
|
|
376
|
+
|
|
377
|
+
def __init__(
|
|
378
|
+
self,
|
|
379
|
+
dim: int,
|
|
380
|
+
num_heads: int,
|
|
381
|
+
window_size: int,
|
|
382
|
+
*,
|
|
383
|
+
key: PRNGKeyArray,
|
|
384
|
+
mlp_ratio: float = 4.0,
|
|
385
|
+
drop_path: float | List[float] = 0.0,
|
|
386
|
+
qkv_bias: bool = True,
|
|
387
|
+
proj_bias: bool = True,
|
|
388
|
+
qk_norm: bool = False,
|
|
389
|
+
attn_drop: float = 0.0,
|
|
390
|
+
proj_drop: float = 0.0,
|
|
391
|
+
act_layer: Callable = jax.nn.gelu,
|
|
392
|
+
attn_layer: eqx.Module = Attention,
|
|
393
|
+
ffn_layer: eqx.Module = Mlp,
|
|
394
|
+
ffn_bias: bool = True,
|
|
395
|
+
norm_layer: eqx.Module = eqx.nn.LayerNorm,
|
|
396
|
+
init_values: float | None = None,
|
|
397
|
+
sr_ratio: float = 1.0,
|
|
398
|
+
ct_size: int = 1,
|
|
399
|
+
last: bool = False,
|
|
400
|
+
do_propagation: bool = False,
|
|
401
|
+
**kwargs,
|
|
402
|
+
):
|
|
403
|
+
key_posemb, key_hatposemb, key_attn, key_hatattn, key_mlp, key_hatmlp = (
|
|
404
|
+
jr.split(key, 6)
|
|
405
|
+
)
|
|
406
|
+
self.do_propagation = do_propagation
|
|
407
|
+
self.sr_ratio = sr_ratio
|
|
408
|
+
self.window_size = window_size
|
|
409
|
+
self.ct_size = ct_size
|
|
410
|
+
self.last = last
|
|
411
|
+
|
|
412
|
+
if isinstance(drop_path, list):
|
|
413
|
+
if len(drop_path) != 2:
|
|
414
|
+
raise AssertionError(
|
|
415
|
+
f"`drop_path` needs to have 2 elements, got {len(drop_path)} ({drop_path})."
|
|
416
|
+
)
|
|
417
|
+
dr1, dr2 = drop_path
|
|
418
|
+
dr1 = float(dr1)
|
|
419
|
+
dr2 = float(dr2)
|
|
420
|
+
else:
|
|
421
|
+
dr1 = dr2 = float(drop_path)
|
|
422
|
+
|
|
423
|
+
self.pos_embed = PosEmbMLPSwinv1D(
|
|
424
|
+
dim, rank=2, seq_len=window_size**2, key=key_posemb
|
|
425
|
+
)
|
|
426
|
+
self.norm1 = norm_layer(dim)
|
|
427
|
+
self.norm2 = norm_layer(dim)
|
|
428
|
+
|
|
429
|
+
if init_values:
|
|
430
|
+
self.ls1 = LayerScale(dim, init_values=init_values)
|
|
431
|
+
self.ls2 = LayerScale(dim, init_values=init_values)
|
|
432
|
+
else:
|
|
433
|
+
self.ls1 = self.ls2 = eqx.nn.Identity()
|
|
434
|
+
|
|
435
|
+
# number of carrier tokens per every window
|
|
436
|
+
cr_tokens_per_window = self.ct_size**2 if self.sr_ratio > 1 else 0
|
|
437
|
+
cr_tokens_total = cr_tokens_per_window * self.sr_ratio * self.sr_ratio
|
|
438
|
+
|
|
439
|
+
self.attn = WindowedAttention(
|
|
440
|
+
resolution=self.window_size,
|
|
441
|
+
seq_len=self.window_size**2 + cr_tokens_per_window,
|
|
442
|
+
dim=dim,
|
|
443
|
+
num_heads=num_heads,
|
|
444
|
+
qkv_bias=qkv_bias,
|
|
445
|
+
proj_bias=proj_bias,
|
|
446
|
+
qk_norm=qk_norm,
|
|
447
|
+
attn_drop=attn_drop,
|
|
448
|
+
proj_drop=proj_drop,
|
|
449
|
+
norm_layer=norm_layer,
|
|
450
|
+
key=key_attn,
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
self.mlp = ffn_layer(
|
|
454
|
+
in_features=dim,
|
|
455
|
+
hidden_features=int(dim * mlp_ratio),
|
|
456
|
+
act_layer=act_layer,
|
|
457
|
+
dropout_rate=proj_drop,
|
|
458
|
+
bias=ffn_bias,
|
|
459
|
+
key=key_mlp,
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
self.drop_path1 = DropPathAdd(dr1)
|
|
463
|
+
self.drop_path2 = DropPathAdd(dr2)
|
|
464
|
+
|
|
465
|
+
if self.sr_ratio > 1:
|
|
466
|
+
# If hierarchical attention, this part is for carrier tokens
|
|
467
|
+
self.hat_norm1 = norm_layer(dim)
|
|
468
|
+
self.hat_norm2 = norm_layer(dim)
|
|
469
|
+
|
|
470
|
+
self.hat_attn = WindowedAttention(
|
|
471
|
+
resolution=int(cr_tokens_total**0.5),
|
|
472
|
+
seq_len=cr_tokens_total,
|
|
473
|
+
dim=dim,
|
|
474
|
+
num_heads=num_heads,
|
|
475
|
+
qkv_bias=qkv_bias,
|
|
476
|
+
proj_bias=proj_bias,
|
|
477
|
+
qk_norm=qk_norm,
|
|
478
|
+
attn_drop=attn_drop,
|
|
479
|
+
proj_drop=proj_drop,
|
|
480
|
+
norm_layer=norm_layer,
|
|
481
|
+
key=key_hatattn,
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
self.hat_mlp = ffn_layer(
|
|
485
|
+
in_features=dim,
|
|
486
|
+
hidden_features=int(dim * mlp_ratio),
|
|
487
|
+
act_layer=act_layer,
|
|
488
|
+
dropout_rate=proj_drop,
|
|
489
|
+
bias=ffn_bias,
|
|
490
|
+
key=key_hatmlp,
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
self.hat_drop_path = DropPathAdd(dr2)
|
|
494
|
+
|
|
495
|
+
self.hat_pos_embed = PosEmbMLPSwinv1D(
|
|
496
|
+
dim, rank=2, seq_len=cr_tokens_total, key=key_hatposemb
|
|
497
|
+
)
|
|
498
|
+
self.hat_ls1 = (
|
|
499
|
+
LayerScale(dim, init_values=init_values)
|
|
500
|
+
if init_values
|
|
501
|
+
else eqx.nn.Identity()
|
|
502
|
+
)
|
|
503
|
+
self.hat_ls2 = (
|
|
504
|
+
LayerScale(dim, init_values=init_values)
|
|
505
|
+
if init_values
|
|
506
|
+
else eqx.nn.Identity()
|
|
507
|
+
)
|
|
508
|
+
self.hat_ls3 = (
|
|
509
|
+
LayerScale(dim, init_values=init_values)
|
|
510
|
+
if init_values
|
|
511
|
+
else eqx.nn.Identity()
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
def ct_window(self, ct, H, W, window_size):
|
|
515
|
+
return rearrange(
|
|
516
|
+
ct,
|
|
517
|
+
"(h h1 w w1) d -> (h w h1 w1) d",
|
|
518
|
+
h=H,
|
|
519
|
+
w=W,
|
|
520
|
+
h1=window_size,
|
|
521
|
+
w1=window_size,
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
def ct_dewindow(self, ct, H, W, window_size):
|
|
525
|
+
return rearrange(
|
|
526
|
+
ct,
|
|
527
|
+
"(h w h1 w1) d -> (h h1 w w1) d",
|
|
528
|
+
h=H,
|
|
529
|
+
w=W,
|
|
530
|
+
h1=window_size,
|
|
531
|
+
w1=window_size,
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
def __call__(
|
|
535
|
+
self,
|
|
536
|
+
x: Float[Array, "seqlen dim"],
|
|
537
|
+
carrier_tokens: Float[Array, "..."],
|
|
538
|
+
enable_dropout: bool,
|
|
539
|
+
key: PRNGKeyArray,
|
|
540
|
+
) -> Float[Array, "seqlen dim"]:
|
|
541
|
+
key_attn, key_hattn, key_dr1, key_hdr1, key_dr2, key_hdr2, key_mlp, key_hmlp = (
|
|
542
|
+
jr.split(key, 8)
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
s, n = x.shape
|
|
546
|
+
x = self.pos_embed(x)
|
|
547
|
+
ct = carrier_tokens
|
|
548
|
+
|
|
549
|
+
if self.sr_ratio > 1:
|
|
550
|
+
# do hierarchical attention via carrier tokens
|
|
551
|
+
# first do attention for carrier tokens
|
|
552
|
+
ng, hg = ct.shape
|
|
553
|
+
|
|
554
|
+
# ct are located quite differently
|
|
555
|
+
ct = self.ct_dewindow(
|
|
556
|
+
ct,
|
|
557
|
+
self.sr_ratio,
|
|
558
|
+
self.sr_ratio,
|
|
559
|
+
self.ct_size,
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
# positional bias for carrier tokens
|
|
563
|
+
ct = self.hat_pos_embed(ct)
|
|
564
|
+
|
|
565
|
+
# attention plus mlp
|
|
566
|
+
ct = self.hat_drop_path(
|
|
567
|
+
ct,
|
|
568
|
+
self.hat_ls1(
|
|
569
|
+
self.hat_attn(
|
|
570
|
+
jax.vmap(self.hat_norm1)(ct),
|
|
571
|
+
enable_dropout,
|
|
572
|
+
key_hattn,
|
|
573
|
+
)
|
|
574
|
+
),
|
|
575
|
+
inference=not enable_dropout,
|
|
576
|
+
key=key_hdr1,
|
|
577
|
+
)
|
|
578
|
+
ct = self.hat_drop_path(
|
|
579
|
+
ct,
|
|
580
|
+
self.hat_ls2(
|
|
581
|
+
self.hat_mlp(
|
|
582
|
+
jax.vmap(self.hat_norm2)(ct),
|
|
583
|
+
enable_dropout,
|
|
584
|
+
key_hmlp,
|
|
585
|
+
)
|
|
586
|
+
),
|
|
587
|
+
inference=not enable_dropout,
|
|
588
|
+
key=key_hdr2,
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
# ct are put back to windows
|
|
592
|
+
ct = self.ct_window(
|
|
593
|
+
ct,
|
|
594
|
+
self.sr_ratio,
|
|
595
|
+
self.sr_ratio,
|
|
596
|
+
self.ct_size,
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
# concatenate carrier_tokens to the windowed tokens
|
|
600
|
+
x = jnp.concatenate((x, ct), axis=0)
|
|
601
|
+
|
|
602
|
+
x = self.drop_path1(
|
|
603
|
+
x,
|
|
604
|
+
self.ls1(
|
|
605
|
+
self.attn(
|
|
606
|
+
jax.vmap(self.norm1)(x),
|
|
607
|
+
enable_dropout,
|
|
608
|
+
key_attn,
|
|
609
|
+
)
|
|
610
|
+
),
|
|
611
|
+
inference=not enable_dropout,
|
|
612
|
+
key=key_dr1,
|
|
613
|
+
)
|
|
614
|
+
x = self.drop_path2(
|
|
615
|
+
x,
|
|
616
|
+
self.ls2(
|
|
617
|
+
self.mlp(
|
|
618
|
+
jax.vmap(self.norm2)(x),
|
|
619
|
+
enable_dropout,
|
|
620
|
+
key_mlp,
|
|
621
|
+
)
|
|
622
|
+
),
|
|
623
|
+
inference=not enable_dropout,
|
|
624
|
+
key=key_dr2,
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
if self.sr_ratio > 1:
|
|
628
|
+
# for hierarchical attention we need to split carrier tokens and window tokens back
|
|
629
|
+
split_index = self.window_size * self.window_size
|
|
630
|
+
x, ctr = jnp.split(x, [split_index], axis=0)
|
|
631
|
+
|
|
632
|
+
if self.last and self.do_propagation:
|
|
633
|
+
# propagate carrier token information into the image
|
|
634
|
+
ctr_image_space = rearrange(
|
|
635
|
+
ctr,
|
|
636
|
+
"(h w) c -> c h w",
|
|
637
|
+
h=self.ct_size * self.sr_ratio,
|
|
638
|
+
w=self.ct_size * self.sr_ratio,
|
|
639
|
+
)
|
|
640
|
+
upsampled = jax.image.resize(
|
|
641
|
+
ctr_image_space,
|
|
642
|
+
(n, self.window_size, self.window_size),
|
|
643
|
+
method="nearest",
|
|
644
|
+
)
|
|
645
|
+
upsampled = rearrange(upsampled, "c h w -> (h w) c")
|
|
646
|
+
|
|
647
|
+
x = x + self.hat_ls3(upsampled)
|
|
648
|
+
|
|
649
|
+
return x, ct
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
class SHSA(eqx.Module):
|
|
653
|
+
"""Signe-Head Self Attention module from the SHViT paper.
|
|
654
|
+
|
|
655
|
+
"In SHSA, self-attention with a single head is applied to just a subset of
|
|
656
|
+
the input channels, while the others remain unchanged. SHSA layer not only
|
|
657
|
+
eliminates the computational redundancy derived from multi-head mechanism
|
|
658
|
+
but also reduces memory access cost by processing partial channels"
|
|
659
|
+
|
|
660
|
+
Attributes:
|
|
661
|
+
scale: Scaling factor for attention scores
|
|
662
|
+
qk_dim: Dimension of query/key projections
|
|
663
|
+
pdim: Dimension of primary features
|
|
664
|
+
"""
|
|
665
|
+
|
|
666
|
+
scale: eqx.field(static=True)
|
|
667
|
+
qk_dim: eqx.field(static=True)
|
|
668
|
+
pdim: eqx.field(static=True)
|
|
669
|
+
|
|
670
|
+
pre_norm: eqx.Module
|
|
671
|
+
qkv: eqx.Module
|
|
672
|
+
proj: eqx.Module
|
|
673
|
+
|
|
674
|
+
def __init__(
|
|
675
|
+
self,
|
|
676
|
+
dim: int,
|
|
677
|
+
qk_dim: int,
|
|
678
|
+
pdim: int,
|
|
679
|
+
*,
|
|
680
|
+
key: PRNGKeyArray,
|
|
681
|
+
norm_max_group: int = 32,
|
|
682
|
+
**kwargs,
|
|
683
|
+
):
|
|
684
|
+
key_conv1, key_conv2 = jr.split(key, 2)
|
|
685
|
+
|
|
686
|
+
self.scale = qk_dim**-0.5
|
|
687
|
+
self.qk_dim = qk_dim
|
|
688
|
+
self.pdim = pdim
|
|
689
|
+
|
|
690
|
+
self.pre_norm = eqx.nn.GroupNorm(1, pdim)
|
|
691
|
+
self.qkv = eqx.nn.Conv(
|
|
692
|
+
num_spatial_dims=2,
|
|
693
|
+
in_channels=pdim,
|
|
694
|
+
out_channels=qk_dim * 2 + pdim,
|
|
695
|
+
kernel_size=1,
|
|
696
|
+
stride=1,
|
|
697
|
+
padding=0,
|
|
698
|
+
key=key_conv1,
|
|
699
|
+
)
|
|
700
|
+
num_groups = nearest_power_of_2_divisor(dim, norm_max_group)
|
|
701
|
+
self.proj = eqx.nn.Sequential(
|
|
702
|
+
[
|
|
703
|
+
eqx.nn.Lambda(jax.nn.relu),
|
|
704
|
+
eqx.nn.Conv(
|
|
705
|
+
num_spatial_dims=2,
|
|
706
|
+
in_channels=dim,
|
|
707
|
+
out_channels=dim,
|
|
708
|
+
kernel_size=1,
|
|
709
|
+
stride=1,
|
|
710
|
+
padding=0,
|
|
711
|
+
key=key_conv2,
|
|
712
|
+
),
|
|
713
|
+
eqx.nn.GroupNorm(num_groups, dim),
|
|
714
|
+
]
|
|
715
|
+
)
|
|
716
|
+
|
|
717
|
+
def flatten(self, x: Float[Array, "channels height width"]):
|
|
718
|
+
return rearrange(x, "c h w -> c (h w)")
|
|
719
|
+
|
|
720
|
+
def __call__(
|
|
721
|
+
self,
|
|
722
|
+
x: Float[Array, "channels height width"],
|
|
723
|
+
enable_dropout: Optional[bool] = None,
|
|
724
|
+
key: Optional[PRNGKeyArray] = None,
|
|
725
|
+
) -> Float[Array, "channels height width"]:
|
|
726
|
+
C, H, W = x.shape
|
|
727
|
+
x1, x2 = jnp.split(x, [self.pdim], axis=0)
|
|
728
|
+
x1 = self.pre_norm(x1)
|
|
729
|
+
qkv = self.qkv(x1)
|
|
730
|
+
q, k, v = jnp.split(qkv, [self.qk_dim, self.qk_dim * 2], axis=0)
|
|
731
|
+
q, k, v = self.flatten(q), self.flatten(k), self.flatten(v)
|
|
732
|
+
|
|
733
|
+
attn = jnp.einsum("dq,dk->qk", q, k) * self.scale
|
|
734
|
+
attn = jax.nn.softmax(attn, axis=-1)
|
|
735
|
+
|
|
736
|
+
x1 = jnp.einsum("qk,dv->qd", attn, v)
|
|
737
|
+
x1 = rearrange(x1, "(h w) d -> d h w", h=H, w=W, d=self.pdim)
|
|
738
|
+
|
|
739
|
+
x = jnp.concat([x1, x2], axis=0)
|
|
740
|
+
x = self.proj(x)
|
|
741
|
+
|
|
742
|
+
return x
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
class LinearAttention(eqx.Module):
|
|
746
|
+
"""Linear attention with rotary position encoding.
|
|
747
|
+
|
|
748
|
+
Implements efficient linear attention with:
|
|
749
|
+
- Linear complexity in sequence length
|
|
750
|
+
- Rotary position embeddings
|
|
751
|
+
- Learnable position encoding (LePE)
|
|
752
|
+
- Multi-head structure
|
|
753
|
+
|
|
754
|
+
Attributes:
|
|
755
|
+
num_heads: Number of attention heads
|
|
756
|
+
"""
|
|
757
|
+
|
|
758
|
+
num_heads: int = eqx.field(static=True)
|
|
759
|
+
|
|
760
|
+
qk: eqx.Module
|
|
761
|
+
lepe: eqx.Module
|
|
762
|
+
rope: eqx.Module
|
|
763
|
+
|
|
764
|
+
def __init__(
|
|
765
|
+
self,
|
|
766
|
+
input_resolution: Tuple[int, int],
|
|
767
|
+
dim: int,
|
|
768
|
+
num_heads: int,
|
|
769
|
+
*,
|
|
770
|
+
key: PRNGKeyArray,
|
|
771
|
+
**kwargs,
|
|
772
|
+
):
|
|
773
|
+
key_fc1, key_conv = jr.split(key, 2)
|
|
774
|
+
self.num_heads = num_heads
|
|
775
|
+
|
|
776
|
+
self.qk = eqx.nn.Linear(dim, dim * 2, key=key_fc1)
|
|
777
|
+
self.lepe = eqx.nn.Conv(
|
|
778
|
+
num_spatial_dims=2,
|
|
779
|
+
in_channels=dim,
|
|
780
|
+
out_channels=dim,
|
|
781
|
+
kernel_size=3,
|
|
782
|
+
stride=1,
|
|
783
|
+
padding=1,
|
|
784
|
+
groups=dim,
|
|
785
|
+
key=key_conv,
|
|
786
|
+
)
|
|
787
|
+
self.rope = RoPE((input_resolution[0], input_resolution[1], dim))
|
|
788
|
+
|
|
789
|
+
def __call__(
|
|
790
|
+
self,
|
|
791
|
+
x: Float[Array, "seqlen dim"],
|
|
792
|
+
enable_dropout: Optional[bool] = None,
|
|
793
|
+
key: Optional[PRNGKeyArray] = None,
|
|
794
|
+
) -> Float[Array, "seqlen dim"]:
|
|
795
|
+
n, c = x.shape
|
|
796
|
+
h = w = int(n**0.5)
|
|
797
|
+
|
|
798
|
+
q, k = rearrange(
|
|
799
|
+
jax.vmap(self.qk)(x),
|
|
800
|
+
"n (qk h d) -> qk h n d",
|
|
801
|
+
qk=2,
|
|
802
|
+
h=self.num_heads,
|
|
803
|
+
)
|
|
804
|
+
v = rearrange(x, "n (h d) -> h n d", h=self.num_heads)
|
|
805
|
+
|
|
806
|
+
q = jax.nn.elu(q) + 1.0
|
|
807
|
+
k = jax.nn.elu(k) + 1.0
|
|
808
|
+
|
|
809
|
+
q_2d = rearrange(q, "h (x y) d -> x y (h d)", x=h, y=w)
|
|
810
|
+
k_2d = rearrange(k, "h (x y) d -> x y (h d)", x=h, y=w)
|
|
811
|
+
|
|
812
|
+
q_rope = rearrange(self.rope(q_2d), "x y (h d) -> h (x y) d", h=self.num_heads)
|
|
813
|
+
k_rope = rearrange(self.rope(k_2d), "x y (h d) -> h (x y) d", h=self.num_heads)
|
|
814
|
+
|
|
815
|
+
# Compute attention
|
|
816
|
+
z = 1 / (jnp.einsum("hnd,hd->hn", q, reduce(k, "h n d -> h d", "mean")) + 1e-6)
|
|
817
|
+
kv = jnp.einsum("hnd,hne->hde", k_rope * (n**-0.5), v * (n**-0.5))
|
|
818
|
+
x = jnp.einsum("hnd,hde->hne", q_rope, kv) * z[..., None]
|
|
819
|
+
|
|
820
|
+
# Reshape output
|
|
821
|
+
x = rearrange(x, "h n d -> n (h d)")
|
|
822
|
+
|
|
823
|
+
# Apply LePE
|
|
824
|
+
v_2d = rearrange(v, "h (x y) d -> (h d) x y", x=h, y=w)
|
|
825
|
+
lepe_out = self.lepe(v_2d)
|
|
826
|
+
|
|
827
|
+
lepe_out = rearrange(lepe_out, "(h d) x y -> (x y) (h d)", h=self.num_heads)
|
|
828
|
+
|
|
829
|
+
# Combine attention output and LePE
|
|
830
|
+
x = x + lepe_out
|
|
831
|
+
|
|
832
|
+
return x
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
class MllaBlock(eqx.Module):
|
|
836
|
+
"""Mamba-like Linear Attention block.
|
|
837
|
+
|
|
838
|
+
Implements a transformer block using linear attention that:
|
|
839
|
+
- Uses convolutional position encoding
|
|
840
|
+
- Optionally includes depthwise convolution
|
|
841
|
+
- Has residual connections and layer norms
|
|
842
|
+
- Includes MLP feed-forward network
|
|
843
|
+
|
|
844
|
+
Attributes:
|
|
845
|
+
use_dwc: Whether to use depthwise convolution
|
|
846
|
+
"""
|
|
847
|
+
|
|
848
|
+
use_dwc: bool = eqx.field(static=True)
|
|
849
|
+
|
|
850
|
+
act: Callable
|
|
851
|
+
cpe1: eqx.Module
|
|
852
|
+
cpe2: eqx.Module
|
|
853
|
+
norm1: eqx.Module
|
|
854
|
+
norm2: eqx.Module
|
|
855
|
+
in_proj: eqx.Module
|
|
856
|
+
act_proj: eqx.Module
|
|
857
|
+
out_proj: eqx.Module
|
|
858
|
+
dwc: eqx.Module
|
|
859
|
+
attn: eqx.Module
|
|
860
|
+
drop_path1: eqx.Module
|
|
861
|
+
drop_path2: eqx.Module
|
|
862
|
+
mlp: eqx.Module
|
|
863
|
+
|
|
864
|
+
def __init__(
|
|
865
|
+
self,
|
|
866
|
+
dim: int,
|
|
867
|
+
*,
|
|
868
|
+
key: PRNGKeyArray,
|
|
869
|
+
act_layer: Callable = jax.nn.silu, # gelu in VSSD
|
|
870
|
+
norm_layer: eqx.Module = eqx.nn.LayerNorm,
|
|
871
|
+
use_dwc: bool = True, # For Mlla but not VMamba-2
|
|
872
|
+
attention_layer: eqx.Module = LinearAttention,
|
|
873
|
+
drop_path: List[float] | float = 0.0,
|
|
874
|
+
mlp_ratio: float = 4.0,
|
|
875
|
+
ffn_layer: eqx.Module = Mlp,
|
|
876
|
+
ffn_bias: bool = True,
|
|
877
|
+
proj_drop: float = 0.0,
|
|
878
|
+
**kwargs,
|
|
879
|
+
):
|
|
880
|
+
if attention_layer not in [Attention, LinearAttention, Mamba2Mixer]:
|
|
881
|
+
raise ValueError(
|
|
882
|
+
"Unsupported `attention_layer`, got:",
|
|
883
|
+
attention_layer,
|
|
884
|
+
)
|
|
885
|
+
|
|
886
|
+
(
|
|
887
|
+
key_conv1,
|
|
888
|
+
key_conv2,
|
|
889
|
+
key_conv3,
|
|
890
|
+
key_fc1,
|
|
891
|
+
key_fc2,
|
|
892
|
+
key_fc3,
|
|
893
|
+
key_attn,
|
|
894
|
+
key_mlp,
|
|
895
|
+
) = jr.split(key, 8)
|
|
896
|
+
self.use_dwc = use_dwc
|
|
897
|
+
self.act = act_layer
|
|
898
|
+
|
|
899
|
+
self.cpe1 = eqx.nn.Conv(
|
|
900
|
+
num_spatial_dims=2,
|
|
901
|
+
in_channels=dim,
|
|
902
|
+
out_channels=dim,
|
|
903
|
+
kernel_size=3,
|
|
904
|
+
stride=1,
|
|
905
|
+
padding=1,
|
|
906
|
+
groups=dim,
|
|
907
|
+
use_bias=True,
|
|
908
|
+
key=key_conv1,
|
|
909
|
+
)
|
|
910
|
+
self.norm1 = norm_layer(dim)
|
|
911
|
+
|
|
912
|
+
if use_dwc:
|
|
913
|
+
self.in_proj = eqx.nn.Linear(dim, dim, key=key_fc1)
|
|
914
|
+
self.act_proj = eqx.nn.Linear(dim, dim, key=key_fc2)
|
|
915
|
+
self.dwc = eqx.nn.Conv(
|
|
916
|
+
num_spatial_dims=2,
|
|
917
|
+
in_channels=dim,
|
|
918
|
+
out_channels=dim,
|
|
919
|
+
kernel_size=3,
|
|
920
|
+
stride=1,
|
|
921
|
+
padding=1,
|
|
922
|
+
groups=dim,
|
|
923
|
+
use_bias=True,
|
|
924
|
+
key=key_conv2,
|
|
925
|
+
)
|
|
926
|
+
self.out_proj = eqx.nn.Linear(dim, dim, key=key_fc3)
|
|
927
|
+
else:
|
|
928
|
+
self.in_proj = self.act_proj = self.dwc = self.out_proj = eqx.nn.Identity()
|
|
929
|
+
|
|
930
|
+
config = {"d_model": dim} if attention_layer is Mamba2Mixer else {"dim": dim}
|
|
931
|
+
|
|
932
|
+
self.attn = attention_layer(
|
|
933
|
+
**(kwargs | config),
|
|
934
|
+
key=key_attn,
|
|
935
|
+
)
|
|
936
|
+
|
|
937
|
+
if isinstance(drop_path, list):
|
|
938
|
+
if len(drop_path) != 2:
|
|
939
|
+
raise AssertionError(
|
|
940
|
+
f"`drop_path` needs to have 2 elements, got {len(drop_path)} ({drop_path})."
|
|
941
|
+
)
|
|
942
|
+
dr1, dr2 = drop_path
|
|
943
|
+
dr1 = float(dr1)
|
|
944
|
+
dr2 = float(dr2)
|
|
945
|
+
else:
|
|
946
|
+
dr1 = dr2 = float(drop_path)
|
|
947
|
+
|
|
948
|
+
self.drop_path1 = DropPathAdd(dr1)
|
|
949
|
+
|
|
950
|
+
self.cpe2 = eqx.nn.Conv(
|
|
951
|
+
num_spatial_dims=2,
|
|
952
|
+
in_channels=dim,
|
|
953
|
+
out_channels=dim,
|
|
954
|
+
kernel_size=3,
|
|
955
|
+
stride=1,
|
|
956
|
+
padding=1,
|
|
957
|
+
groups=dim,
|
|
958
|
+
use_bias=True,
|
|
959
|
+
key=key_conv3,
|
|
960
|
+
)
|
|
961
|
+
|
|
962
|
+
self.norm2 = norm_layer(dim)
|
|
963
|
+
self.mlp = ffn_layer(
|
|
964
|
+
in_features=dim,
|
|
965
|
+
hidden_features=int(dim * mlp_ratio),
|
|
966
|
+
act_layer=act_layer,
|
|
967
|
+
dropout_rate=proj_drop,
|
|
968
|
+
bias=ffn_bias,
|
|
969
|
+
key=key_mlp,
|
|
970
|
+
)
|
|
971
|
+
|
|
972
|
+
self.drop_path2 = DropPathAdd(dr2)
|
|
973
|
+
|
|
974
|
+
def __call__(
|
|
975
|
+
self,
|
|
976
|
+
x: Float[Array, "seqlen dim"],
|
|
977
|
+
enable_dropout: Optional[bool] = None,
|
|
978
|
+
key: Optional[PRNGKeyArray] = None,
|
|
979
|
+
) -> Float[Array, "seqlen dim"]:
|
|
980
|
+
key_attn, key_dr1, key_dr2, key_mlp = jr.split(key, 4)
|
|
981
|
+
l, _ = x.shape
|
|
982
|
+
h = w = int(l**0.5)
|
|
983
|
+
|
|
984
|
+
x1 = x + rearrange(
|
|
985
|
+
self.cpe1(rearrange(x, "(h w) c -> c h w", h=h, w=w)),
|
|
986
|
+
"c h w -> (h w) c",
|
|
987
|
+
)
|
|
988
|
+
x1 = jax.vmap(self.norm1)(x1)
|
|
989
|
+
|
|
990
|
+
if self.use_dwc:
|
|
991
|
+
act_res = self.act(jax.vmap(self.act_proj)(x1))
|
|
992
|
+
|
|
993
|
+
x1 = rearrange(jax.vmap(self.in_proj)(x1), "(h w) c -> c h w", h=h, w=w)
|
|
994
|
+
x1 = self.act(rearrange(self.dwc(x1), "c h w -> (h w) c"))
|
|
995
|
+
|
|
996
|
+
x1 = self.attn(x1, enable_dropout, key_attn)
|
|
997
|
+
|
|
998
|
+
if self.use_dwc:
|
|
999
|
+
x1 = jax.vmap(self.out_proj)(x * act_res)
|
|
1000
|
+
|
|
1001
|
+
x = self.drop_path1(x, x1, inference=not enable_dropout, key=key_dr1)
|
|
1002
|
+
|
|
1003
|
+
x += rearrange(
|
|
1004
|
+
self.cpe2(rearrange(x, "(h w) c -> c h w", h=h, w=w)),
|
|
1005
|
+
"c h w -> (h w) c",
|
|
1006
|
+
)
|
|
1007
|
+
|
|
1008
|
+
return self.drop_path2(
|
|
1009
|
+
x,
|
|
1010
|
+
self.mlp(jax.vmap(self.norm2)(x), enable_dropout, key_mlp),
|
|
1011
|
+
inference=not enable_dropout,
|
|
1012
|
+
key=key_dr2,
|
|
1013
|
+
)
|
|
1014
|
+
|
|
1015
|
+
|
|
1016
|
+
class MMSA(eqx.Module):
|
|
1017
|
+
"""Mixed Multi-head Self Attention.
|
|
1018
|
+
|
|
1019
|
+
Implements attention with:
|
|
1020
|
+
- Multi-head structure
|
|
1021
|
+
- Attention score projection for multi-scale interaction
|
|
1022
|
+
- Normalized query/key processing
|
|
1023
|
+
- Dropout regularization
|
|
1024
|
+
|
|
1025
|
+
Attributes:
|
|
1026
|
+
dim: Total dimension of input/output
|
|
1027
|
+
num_heads: Number of attention heads
|
|
1028
|
+
head_dim: Dimension per attention head
|
|
1029
|
+
"""
|
|
1030
|
+
|
|
1031
|
+
dim: int = eqx.field(static=True)
|
|
1032
|
+
num_heads: int = eqx.field(static=True)
|
|
1033
|
+
head_dim: int = eqx.field(static=True)
|
|
1034
|
+
|
|
1035
|
+
qkv: eqx.nn.Linear
|
|
1036
|
+
proj: eqx.nn.Linear
|
|
1037
|
+
attn_proj1: eqx.nn.Linear
|
|
1038
|
+
attn_proj2: eqx.nn.Linear
|
|
1039
|
+
q_norm: eqx.Module
|
|
1040
|
+
k_norm: eqx.Module
|
|
1041
|
+
attn_drop: Dropout
|
|
1042
|
+
proj_drop: Dropout
|
|
1043
|
+
|
|
1044
|
+
def __init__(
|
|
1045
|
+
self,
|
|
1046
|
+
dim: int,
|
|
1047
|
+
num_heads: int,
|
|
1048
|
+
*,
|
|
1049
|
+
key: PRNGKeyArray,
|
|
1050
|
+
head_expand_ratio: float = 4.0,
|
|
1051
|
+
qkv_bias: bool = True,
|
|
1052
|
+
proj_bias: bool = True,
|
|
1053
|
+
qk_norm: bool = False,
|
|
1054
|
+
attn_drop: float = 0.0,
|
|
1055
|
+
proj_drop: float = 0.0,
|
|
1056
|
+
norm_layer: eqx.Module = eqx.nn.LayerNorm,
|
|
1057
|
+
**kwargs,
|
|
1058
|
+
):
|
|
1059
|
+
self.num_heads = num_heads
|
|
1060
|
+
self.head_dim = dim // num_heads
|
|
1061
|
+
self.dim = dim
|
|
1062
|
+
|
|
1063
|
+
assert self.head_dim * num_heads == dim, "dim must be divisible by num_heads"
|
|
1064
|
+
|
|
1065
|
+
key_qkv, key_proj, key_attnproj1, key_attnproj2 = jr.split(key, 4)
|
|
1066
|
+
self.qkv = eqx.nn.Linear(dim, dim * 3, use_bias=qkv_bias, key=key_qkv)
|
|
1067
|
+
self.proj = eqx.nn.Linear(dim, dim, use_bias=proj_bias, key=key_proj)
|
|
1068
|
+
self.attn_proj1 = eqx.nn.Linear(
|
|
1069
|
+
num_heads,
|
|
1070
|
+
int(num_heads * head_expand_ratio),
|
|
1071
|
+
key=key_attnproj1,
|
|
1072
|
+
)
|
|
1073
|
+
self.attn_proj2 = eqx.nn.Linear(
|
|
1074
|
+
int(num_heads * head_expand_ratio),
|
|
1075
|
+
num_heads,
|
|
1076
|
+
key=key_attnproj2,
|
|
1077
|
+
)
|
|
1078
|
+
|
|
1079
|
+
self.q_norm = norm_layer(dim) if qk_norm else eqx.nn.Identity()
|
|
1080
|
+
self.k_norm = norm_layer(dim) if qk_norm else eqx.nn.Identity()
|
|
1081
|
+
|
|
1082
|
+
self.attn_drop = Dropout(attn_drop)
|
|
1083
|
+
self.proj_drop = Dropout(proj_drop)
|
|
1084
|
+
|
|
1085
|
+
def __call__(
|
|
1086
|
+
self,
|
|
1087
|
+
x: Float[Array, "seqlen dim"],
|
|
1088
|
+
enable_dropout: bool,
|
|
1089
|
+
key: PRNGKeyArray,
|
|
1090
|
+
) -> Float[Array, "seqlen dim"]:
|
|
1091
|
+
key1, key2 = jr.split(key, 2)
|
|
1092
|
+
|
|
1093
|
+
qkv = jax.vmap(self.qkv)(x)
|
|
1094
|
+
qkv = rearrange(
|
|
1095
|
+
qkv,
|
|
1096
|
+
"s (n h d) -> n h s d",
|
|
1097
|
+
n=3,
|
|
1098
|
+
h=self.num_heads,
|
|
1099
|
+
d=self.dim // self.num_heads,
|
|
1100
|
+
)
|
|
1101
|
+
q, k, v = qkv
|
|
1102
|
+
q = jax.vmap(jax.vmap(self.q_norm))(q)
|
|
1103
|
+
k = jax.vmap(jax.vmap(self.k_norm))(k)
|
|
1104
|
+
|
|
1105
|
+
attn = jnp.einsum("hqd,hkd->hqk", q, k) / jnp.sqrt(self.head_dim)
|
|
1106
|
+
attn = jax.vmap(jax.vmap(self.attn_proj1))(
|
|
1107
|
+
rearrange(attn, "h q k -> q k h"),
|
|
1108
|
+
)
|
|
1109
|
+
attn = jax.nn.softmax(attn, axis=1)
|
|
1110
|
+
attn = rearrange(
|
|
1111
|
+
jax.vmap(jax.vmap(self.attn_proj2))(attn),
|
|
1112
|
+
"q k h -> h q k",
|
|
1113
|
+
)
|
|
1114
|
+
attn = self.attn_drop(attn, inference=not enable_dropout, key=key1)
|
|
1115
|
+
|
|
1116
|
+
x = jnp.einsum("hqk,hvd->hqd", attn, v)
|
|
1117
|
+
x = rearrange(x, "h s d -> s (h d)")
|
|
1118
|
+
x = jax.vmap(self.proj)(x)
|
|
1119
|
+
x = self.proj_drop(x, inference=not enable_dropout, key=key2)
|
|
1120
|
+
|
|
1121
|
+
return x
|
|
1122
|
+
|
|
1123
|
+
|
|
1124
|
+
class SQA(eqx.Module):
|
|
1125
|
+
"""Single Query Attention module.
|
|
1126
|
+
|
|
1127
|
+
Implements efficient attention where:
|
|
1128
|
+
- Only one query is used against many keys/values
|
|
1129
|
+
- Includes projection layers for dimension reduction
|
|
1130
|
+
- Uses normalized attention computation
|
|
1131
|
+
- Includes residual connection
|
|
1132
|
+
|
|
1133
|
+
Attributes:
|
|
1134
|
+
dim: Total dimension of input/output
|
|
1135
|
+
num_heads: Number of attention heads
|
|
1136
|
+
head_dim: Dimension per attention head
|
|
1137
|
+
"""
|
|
1138
|
+
|
|
1139
|
+
dim: int = eqx.field(static=True)
|
|
1140
|
+
num_heads: int = eqx.field(static=True)
|
|
1141
|
+
head_dim: int = eqx.field(static=True)
|
|
1142
|
+
|
|
1143
|
+
kv: eqx.nn.Linear
|
|
1144
|
+
proj1: eqx.nn.Linear
|
|
1145
|
+
proj2: eqx.nn.Linear
|
|
1146
|
+
proj_norm: eqx.Module
|
|
1147
|
+
q_norm: eqx.Module
|
|
1148
|
+
k_norm: eqx.Module
|
|
1149
|
+
attn_drop: Dropout
|
|
1150
|
+
proj_drop: Dropout
|
|
1151
|
+
|
|
1152
|
+
def __init__(
|
|
1153
|
+
self,
|
|
1154
|
+
dim: int,
|
|
1155
|
+
num_heads: int,
|
|
1156
|
+
*,
|
|
1157
|
+
key: PRNGKeyArray,
|
|
1158
|
+
kv_bias: bool = True,
|
|
1159
|
+
proj_bias: bool = True,
|
|
1160
|
+
proj_ratio: float = 4.0,
|
|
1161
|
+
qk_norm: bool = False,
|
|
1162
|
+
attn_drop: float = 0.0,
|
|
1163
|
+
proj_drop: float = 0.0,
|
|
1164
|
+
norm_layer: eqx.Module = eqx.nn.LayerNorm,
|
|
1165
|
+
**kwargs,
|
|
1166
|
+
):
|
|
1167
|
+
self.num_heads = num_heads
|
|
1168
|
+
self.head_dim = dim // num_heads
|
|
1169
|
+
self.dim = dim
|
|
1170
|
+
|
|
1171
|
+
assert self.head_dim * num_heads == dim, "dim must be divisible by num_heads"
|
|
1172
|
+
|
|
1173
|
+
key_kv, key_proj = jr.split(key, 2)
|
|
1174
|
+
self.kv = eqx.nn.Linear(dim, dim * 2, use_bias=kv_bias, key=key_kv)
|
|
1175
|
+
self.proj1 = eqx.nn.Linear(
|
|
1176
|
+
dim, int(dim // proj_ratio), use_bias=proj_bias, key=key_proj
|
|
1177
|
+
)
|
|
1178
|
+
self.proj2 = eqx.nn.Linear(
|
|
1179
|
+
int(dim // proj_ratio), dim, use_bias=proj_bias, key=key_proj
|
|
1180
|
+
)
|
|
1181
|
+
self.proj_norm = norm_layer(int(dim // proj_ratio))
|
|
1182
|
+
|
|
1183
|
+
self.q_norm = norm_layer(dim) if qk_norm else eqx.nn.Identity()
|
|
1184
|
+
self.k_norm = norm_layer(dim) if qk_norm else eqx.nn.Identity()
|
|
1185
|
+
|
|
1186
|
+
self.attn_drop = Dropout(attn_drop)
|
|
1187
|
+
self.proj_drop = Dropout(proj_drop)
|
|
1188
|
+
|
|
1189
|
+
def __call__(
|
|
1190
|
+
self,
|
|
1191
|
+
x: Float[Array, "seqlen dim"],
|
|
1192
|
+
q: Float[Array, "1 dim"],
|
|
1193
|
+
enable_dropout: bool,
|
|
1194
|
+
key: PRNGKeyArray,
|
|
1195
|
+
) -> Float[Array, "seqlen_x dim"]:
|
|
1196
|
+
key1, key2 = jr.split(key, 2)
|
|
1197
|
+
|
|
1198
|
+
q = rearrange(
|
|
1199
|
+
q,
|
|
1200
|
+
"1 (h d) -> h 1 d",
|
|
1201
|
+
h=self.num_heads,
|
|
1202
|
+
d=self.dim // self.num_heads,
|
|
1203
|
+
)
|
|
1204
|
+
kv = jax.vmap(self.kv)(x)
|
|
1205
|
+
kv = rearrange(
|
|
1206
|
+
kv,
|
|
1207
|
+
"s (n h d) -> n h s d",
|
|
1208
|
+
n=2,
|
|
1209
|
+
h=self.num_heads,
|
|
1210
|
+
d=self.dim // self.num_heads,
|
|
1211
|
+
)
|
|
1212
|
+
k, v = kv
|
|
1213
|
+
q = jax.vmap(jax.vmap(self.q_norm))(q)
|
|
1214
|
+
k = jax.vmap(jax.vmap(self.k_norm))(k)
|
|
1215
|
+
|
|
1216
|
+
attn = jnp.einsum("hqd,hkd->hqk", q, k) / jnp.sqrt(self.head_dim)
|
|
1217
|
+
attn = jax.nn.softmax(attn, axis=-1)
|
|
1218
|
+
attn = self.attn_drop(attn, inference=not enable_dropout, key=key1)
|
|
1219
|
+
|
|
1220
|
+
x1 = jnp.einsum("hqk,hvd->hqd", attn, v)
|
|
1221
|
+
x1 = rearrange(x1, "h s d -> s (h d)")
|
|
1222
|
+
x1 = jax.vmap(self.proj_norm)(jax.vmap(self.proj1)(x1))
|
|
1223
|
+
x1 = jax.vmap(self.proj2)(jax.nn.relu(x1))
|
|
1224
|
+
|
|
1225
|
+
x = self.proj_drop(x + x1, inference=not enable_dropout, key=key2)
|
|
1226
|
+
|
|
1227
|
+
return x
|
|
1228
|
+
|
|
1229
|
+
|
|
1230
|
+
class PartialFormerBlock(eqx.Module):
|
|
1231
|
+
"""Partial Transformer block with foreground/background separation.
|
|
1232
|
+
|
|
1233
|
+
Implements a specialized transformer that:
|
|
1234
|
+
- Separates input into foreground/background regions
|
|
1235
|
+
- Uses different attention mechanisms for each region
|
|
1236
|
+
- Includes positional encoding and MLP layers
|
|
1237
|
+
- Has layer scaling and dropout paths
|
|
1238
|
+
|
|
1239
|
+
Attributes:
|
|
1240
|
+
foreground_ratio: Ratio of tokens treated as foreground
|
|
1241
|
+
patch_size: Size of input patches
|
|
1242
|
+
"""
|
|
1243
|
+
|
|
1244
|
+
foreground_ratio: float = eqx.field(static=True)
|
|
1245
|
+
patch_size: float = eqx.field(static=True)
|
|
1246
|
+
|
|
1247
|
+
act: Callable
|
|
1248
|
+
posemb: eqx.Module
|
|
1249
|
+
norm1: eqx.Module
|
|
1250
|
+
norm2: eqx.Module
|
|
1251
|
+
mmsa: eqx.Module
|
|
1252
|
+
sqa: eqx.Module
|
|
1253
|
+
ls1: eqx.Module
|
|
1254
|
+
ls2: eqx.Module
|
|
1255
|
+
drop_path1: eqx.Module
|
|
1256
|
+
drop_path2: eqx.Module
|
|
1257
|
+
mlp: eqx.Module
|
|
1258
|
+
|
|
1259
|
+
def __init__(
|
|
1260
|
+
self,
|
|
1261
|
+
dim: int,
|
|
1262
|
+
num_heads: int,
|
|
1263
|
+
foreground_ratio: int,
|
|
1264
|
+
patch_size: int,
|
|
1265
|
+
*,
|
|
1266
|
+
key: PRNGKeyArray,
|
|
1267
|
+
head_expand_ratio: float = 4.0,
|
|
1268
|
+
qkv_bias: bool = True,
|
|
1269
|
+
proj_bias: bool = True,
|
|
1270
|
+
qk_norm: bool = False,
|
|
1271
|
+
attn_drop: float = 0.0,
|
|
1272
|
+
proj_drop: float = 0.0,
|
|
1273
|
+
proj_ratio: float = 4.0,
|
|
1274
|
+
act_layer: Callable = jax.nn.silu, # gelu in VSSD
|
|
1275
|
+
norm_layer: eqx.Module = eqx.nn.LayerNorm,
|
|
1276
|
+
drop_path: List[float] | float = 0.0,
|
|
1277
|
+
mlp_ratio: float = 4.0,
|
|
1278
|
+
ffn_layer: eqx.Module = Mlp,
|
|
1279
|
+
ffn_bias: bool = True,
|
|
1280
|
+
init_values: float | None = None,
|
|
1281
|
+
**kwargs,
|
|
1282
|
+
):
|
|
1283
|
+
(
|
|
1284
|
+
key_posemb,
|
|
1285
|
+
key_mmsa,
|
|
1286
|
+
key_sqa,
|
|
1287
|
+
key_mlp,
|
|
1288
|
+
) = jr.split(key, 4)
|
|
1289
|
+
self.act = act_layer
|
|
1290
|
+
self.foreground_ratio = foreground_ratio
|
|
1291
|
+
self.patch_size = patch_size
|
|
1292
|
+
|
|
1293
|
+
if isinstance(drop_path, list):
|
|
1294
|
+
if len(drop_path) != 2:
|
|
1295
|
+
raise AssertionError(
|
|
1296
|
+
f"`drop_path` needs to have 2 elements, got {len(drop_path)} ({drop_path})."
|
|
1297
|
+
)
|
|
1298
|
+
dr1, dr2 = drop_path
|
|
1299
|
+
dr1 = float(dr1)
|
|
1300
|
+
dr2 = float(dr2)
|
|
1301
|
+
else:
|
|
1302
|
+
dr1 = dr2 = float(drop_path)
|
|
1303
|
+
|
|
1304
|
+
self.norm1 = norm_layer(dim)
|
|
1305
|
+
self.norm2 = norm_layer(dim)
|
|
1306
|
+
|
|
1307
|
+
if init_values:
|
|
1308
|
+
self.ls1 = LayerScale(dim, init_values=init_values)
|
|
1309
|
+
self.ls2 = LayerScale(dim, init_values=init_values)
|
|
1310
|
+
else:
|
|
1311
|
+
self.ls1 = self.ls2 = eqx.nn.Identity()
|
|
1312
|
+
|
|
1313
|
+
self.posemb = PosCNN2D(dim, dim, key=key_posemb)
|
|
1314
|
+
|
|
1315
|
+
self.mmsa = MMSA(
|
|
1316
|
+
dim=dim,
|
|
1317
|
+
num_heads=num_heads,
|
|
1318
|
+
head_expand_ratio=head_expand_ratio,
|
|
1319
|
+
qkv_bias=qkv_bias,
|
|
1320
|
+
proj_bias=proj_bias,
|
|
1321
|
+
qk_norm=qk_norm,
|
|
1322
|
+
attn_drop=attn_drop,
|
|
1323
|
+
proj_drop=proj_drop,
|
|
1324
|
+
norm_layer=norm_layer,
|
|
1325
|
+
key=key_mmsa,
|
|
1326
|
+
)
|
|
1327
|
+
self.sqa = SQA(
|
|
1328
|
+
dim=dim,
|
|
1329
|
+
num_heads=num_heads,
|
|
1330
|
+
kv_bias=qkv_bias,
|
|
1331
|
+
qk_norm=qk_norm,
|
|
1332
|
+
attn_drop=attn_drop,
|
|
1333
|
+
proj_bias=proj_bias,
|
|
1334
|
+
proj_ratio=proj_ratio,
|
|
1335
|
+
proj_drop=proj_drop,
|
|
1336
|
+
norm_layer=norm_layer,
|
|
1337
|
+
key=key_sqa,
|
|
1338
|
+
)
|
|
1339
|
+
|
|
1340
|
+
self.mlp = ffn_layer(
|
|
1341
|
+
in_features=dim,
|
|
1342
|
+
hidden_features=int(dim * mlp_ratio),
|
|
1343
|
+
act_layer=act_layer,
|
|
1344
|
+
dropout_rate=proj_drop,
|
|
1345
|
+
bias=ffn_bias,
|
|
1346
|
+
key=key_mlp,
|
|
1347
|
+
)
|
|
1348
|
+
|
|
1349
|
+
self.drop_path1 = DropPathAdd(dr1)
|
|
1350
|
+
self.drop_path2 = DropPathAdd(dr2)
|
|
1351
|
+
|
|
1352
|
+
def __call__(
|
|
1353
|
+
self,
|
|
1354
|
+
x: Float[Array, "seqlen dim"],
|
|
1355
|
+
qa: Float[Array, "1 dim"],
|
|
1356
|
+
enable_dropout: Optional[bool] = None,
|
|
1357
|
+
key: Optional[PRNGKeyArray] = None,
|
|
1358
|
+
) -> Tuple[Float[Array, "seqlen dim"], Float[Array, "1 dim"]]:
|
|
1359
|
+
key_mmsa, key_sqa, key_dr1, key_dr2, key_mlp = jr.split(key, 5)
|
|
1360
|
+
l, _ = x.shape
|
|
1361
|
+
h = w = int(l**0.5)
|
|
1362
|
+
|
|
1363
|
+
x1 = rearrange(x, "(h w) c -> c h w", h=h, w=w)
|
|
1364
|
+
x1 = self.posemb(x1)
|
|
1365
|
+
x1 = rearrange(
|
|
1366
|
+
x1,
|
|
1367
|
+
"c (h h1) (w w1) -> (h w) (h1 w1) c",
|
|
1368
|
+
h1=self.patch_size,
|
|
1369
|
+
w1=self.patch_size,
|
|
1370
|
+
)
|
|
1371
|
+
|
|
1372
|
+
n = x1.shape[0]
|
|
1373
|
+
nf = int(n * self.foreground_ratio)
|
|
1374
|
+
|
|
1375
|
+
idx = jnp.argsort(reduce(x1, "n s c -> n", "mean"), descending=True)
|
|
1376
|
+
nf_idx, nb_idx = jnp.split(idx, [nf])
|
|
1377
|
+
|
|
1378
|
+
f, b = x1[nf_idx], x1[nb_idx]
|
|
1379
|
+
|
|
1380
|
+
f = rearrange(f, "n s c -> (n s) c")
|
|
1381
|
+
b = rearrange(b, "n s c -> (n s) c")
|
|
1382
|
+
|
|
1383
|
+
qf = self.mmsa(jnp.concat([qa, f], axis=0), enable_dropout, key=key_mmsa)
|
|
1384
|
+
qa, f = jnp.split(qf, [1])
|
|
1385
|
+
b = self.sqa(b, qa, enable_dropout, key=key_sqa)
|
|
1386
|
+
|
|
1387
|
+
x1 = self.ls1(jnp.concat([f, b], axis=0))
|
|
1388
|
+
x = self.drop_path1(x, x1, inference=not enable_dropout, key=key_dr1)
|
|
1389
|
+
|
|
1390
|
+
qa, x1 = jnp.split(
|
|
1391
|
+
self.mlp(
|
|
1392
|
+
jax.vmap(self.norm2)(jnp.concat([qa, x])),
|
|
1393
|
+
enable_dropout,
|
|
1394
|
+
key=key_mlp,
|
|
1395
|
+
)[1],
|
|
1396
|
+
)
|
|
1397
|
+
x = self.drop_path2(
|
|
1398
|
+
x,
|
|
1399
|
+
self.ls2(x1),
|
|
1400
|
+
inference=not enable_dropout,
|
|
1401
|
+
key=key_dr2,
|
|
1402
|
+
)
|
|
1403
|
+
|
|
1404
|
+
return x, qa
|
|
1405
|
+
|
|
1406
|
+
|
|
1407
|
+
class LinearAngularAttention(eqx.Module):
|
|
1408
|
+
"""Linear Angular Attention with optional sparsity.
|
|
1409
|
+
|
|
1410
|
+
This is the base attention module to be included in a ViT to replicate
|
|
1411
|
+
Castling-ViT[1].
|
|
1412
|
+
Implements an efficient attention variant that:
|
|
1413
|
+
- Uses normalized vectors for computing attention
|
|
1414
|
+
- Applies optional sparsity regularization
|
|
1415
|
+
- Includes depthwise convolution for local mixing
|
|
1416
|
+
- Has linear complexity in sequence length
|
|
1417
|
+
|
|
1418
|
+
Attributes:
|
|
1419
|
+
dim: Total dimension of input/output
|
|
1420
|
+
num_heads: Number of attention heads
|
|
1421
|
+
head_dim: Dimension per attention head
|
|
1422
|
+
sparse_reg: Whether to use sparsity regularization
|
|
1423
|
+
sparsity_threshold: Threshold for sparse attention
|
|
1424
|
+
|
|
1425
|
+
References:
|
|
1426
|
+
[1]: You, et al., 2024. https://arxiv.org/abs/2211.10526
|
|
1427
|
+
"""
|
|
1428
|
+
|
|
1429
|
+
dim: int = eqx.field(static=True)
|
|
1430
|
+
num_heads: int = eqx.field(static=True)
|
|
1431
|
+
head_dim: int = eqx.field(static=True)
|
|
1432
|
+
sparse_reg: bool = eqx.field(static=True)
|
|
1433
|
+
sparsity_threshold: float = eqx.field(static=True)
|
|
1434
|
+
|
|
1435
|
+
qkv: eqx.nn.Linear
|
|
1436
|
+
proj: eqx.nn.Linear
|
|
1437
|
+
attn_drop: Dropout
|
|
1438
|
+
proj_drop: Dropout
|
|
1439
|
+
dconv: eqx.nn.Conv
|
|
1440
|
+
|
|
1441
|
+
def __init__(
|
|
1442
|
+
self,
|
|
1443
|
+
dim: int,
|
|
1444
|
+
num_heads: int,
|
|
1445
|
+
sparse_reg: bool = False,
|
|
1446
|
+
*,
|
|
1447
|
+
key: PRNGKeyArray,
|
|
1448
|
+
sparsity_threshold: float = 0.2,
|
|
1449
|
+
qkv_bias: bool = True,
|
|
1450
|
+
proj_bias: bool = True,
|
|
1451
|
+
qk_norm: bool = False,
|
|
1452
|
+
attn_drop: float = 0.0,
|
|
1453
|
+
proj_drop: float = 0.0,
|
|
1454
|
+
res_kernel_size: int = 9,
|
|
1455
|
+
**kwargs,
|
|
1456
|
+
):
|
|
1457
|
+
self.sparse_reg = sparse_reg
|
|
1458
|
+
self.sparsity_threshold = sparsity_threshold
|
|
1459
|
+
self.num_heads = num_heads
|
|
1460
|
+
self.head_dim = dim // num_heads
|
|
1461
|
+
self.dim = dim
|
|
1462
|
+
|
|
1463
|
+
assert self.head_dim * num_heads == dim, "dim must be divisible by num_heads"
|
|
1464
|
+
|
|
1465
|
+
key_qkv, key_proj, key_dconv = jr.split(key, 3)
|
|
1466
|
+
self.qkv = eqx.nn.Linear(dim, dim * 3, use_bias=qkv_bias, key=key_qkv)
|
|
1467
|
+
self.proj = eqx.nn.Linear(dim, dim, use_bias=proj_bias, key=key_proj)
|
|
1468
|
+
|
|
1469
|
+
self.dconv = eqx.nn.Conv(
|
|
1470
|
+
num_spatial_dims=2,
|
|
1471
|
+
in_channels=num_heads,
|
|
1472
|
+
out_channels=num_heads,
|
|
1473
|
+
kernel_size=(res_kernel_size, 1),
|
|
1474
|
+
stride=1,
|
|
1475
|
+
padding=(res_kernel_size // 2, 0),
|
|
1476
|
+
groups=num_heads,
|
|
1477
|
+
use_bias=False,
|
|
1478
|
+
key=key_dconv,
|
|
1479
|
+
)
|
|
1480
|
+
|
|
1481
|
+
self.attn_drop = Dropout(attn_drop)
|
|
1482
|
+
self.proj_drop = Dropout(proj_drop)
|
|
1483
|
+
|
|
1484
|
+
def __call__(
|
|
1485
|
+
self,
|
|
1486
|
+
x: Float[Array, "seqlen dim"],
|
|
1487
|
+
enable_dropout: bool,
|
|
1488
|
+
key: PRNGKeyArray,
|
|
1489
|
+
) -> Float[Array, "seqlen dim"]:
|
|
1490
|
+
key1, key2 = jr.split(key, 2)
|
|
1491
|
+
|
|
1492
|
+
qkv = jax.vmap(self.qkv)(x)
|
|
1493
|
+
qkv = rearrange(
|
|
1494
|
+
qkv,
|
|
1495
|
+
"s (n h d) -> n h s d",
|
|
1496
|
+
n=3,
|
|
1497
|
+
h=self.num_heads,
|
|
1498
|
+
d=self.dim // self.num_heads,
|
|
1499
|
+
)
|
|
1500
|
+
q, k, v = qkv
|
|
1501
|
+
|
|
1502
|
+
if self.sparse_reg:
|
|
1503
|
+
attn = jnp.einsum("hqd,hkd->hqk", q, k) / jnp.sqrt(self.head_dim)
|
|
1504
|
+
attn = jax.nn.softmax(attn, axis=-1)
|
|
1505
|
+
attn = self.attn_drop(attn, inference=not enable_dropout, key=key1)
|
|
1506
|
+
sparse = jnp.where(attn > self.sparsity_threshold, attn, 0)
|
|
1507
|
+
|
|
1508
|
+
q = q / jnp.linalg.norm(q, axis=-1, keepdims=True)
|
|
1509
|
+
k = k / jnp.linalg.norm(k, axis=-1, keepdims=True)
|
|
1510
|
+
dconv_v = self.dconv(v)
|
|
1511
|
+
|
|
1512
|
+
attn = jnp.einsum("hqk,hqv->hkv", k, v)
|
|
1513
|
+
|
|
1514
|
+
if self.sparse_reg:
|
|
1515
|
+
x = (sparse @ v) + 0.5 * v + 1.0 / jnp.pi * (q @ attn)
|
|
1516
|
+
else:
|
|
1517
|
+
x = 0.5 * v + 1.0 / jnp.pi * (q @ attn)
|
|
1518
|
+
|
|
1519
|
+
x = x / jnp.linalg.norm(x, axis=-1, keepdims=True)
|
|
1520
|
+
x += dconv_v
|
|
1521
|
+
|
|
1522
|
+
x = rearrange(x, "h s d -> s (h d)")
|
|
1523
|
+
x = jax.vmap(self.proj)(x)
|
|
1524
|
+
x = self.proj_drop(x, inference=not enable_dropout, key=key2)
|
|
1525
|
+
|
|
1526
|
+
return x
|