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
equimo/models/mlla.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from typing import List
|
|
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 reduce
|
|
8
|
+
from jaxtyping import Array, Float, PRNGKeyArray
|
|
9
|
+
|
|
10
|
+
from equimo.layers.attention import LinearAttention, MllaBlock
|
|
11
|
+
from equimo.layers.convolution import Stem
|
|
12
|
+
from equimo.layers.ffn import Mlp
|
|
13
|
+
from equimo.layers.patch import PatchMerging
|
|
14
|
+
from equimo.models.vit import BlockChunk
|
|
15
|
+
from equimo.utils import to_list
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Mlla(eqx.Module):
|
|
19
|
+
"""Mamba-like Linear Attention (MLLA) Vision Model[1].
|
|
20
|
+
|
|
21
|
+
A vision transformer architecture that combines linear attention mechanisms
|
|
22
|
+
inspired by Mamba with hierarchical feature processing. The model processes
|
|
23
|
+
images through patches, applies position-aware dropouts, and uses a series
|
|
24
|
+
of attention blocks with progressive feature resolution reduction.
|
|
25
|
+
|
|
26
|
+
Attributes:
|
|
27
|
+
num_features: Number of features in the final layer
|
|
28
|
+
patch_embed: Patch embedding layer (Stem)
|
|
29
|
+
pos_drop: Positional dropout layer
|
|
30
|
+
blocks: List of processing blocks
|
|
31
|
+
head: Classification head
|
|
32
|
+
|
|
33
|
+
References:
|
|
34
|
+
[1]: Han, et al., 2024. https://arxiv.org/abs/2405.16605
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
num_features: int = eqx.field(static=True)
|
|
38
|
+
|
|
39
|
+
patch_embed: eqx.Module
|
|
40
|
+
pos_drop: eqx.Module
|
|
41
|
+
blocks: List[eqx.Module]
|
|
42
|
+
head: eqx.Module
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
img_size: int,
|
|
47
|
+
in_channels: int,
|
|
48
|
+
*,
|
|
49
|
+
key: PRNGKeyArray,
|
|
50
|
+
repeat: int = 1,
|
|
51
|
+
dim: int = 96,
|
|
52
|
+
patch_size: int = 4,
|
|
53
|
+
depths: List[int] = [2, 2, 6, 2],
|
|
54
|
+
num_heads: List[int] = [3, 6, 12, 24],
|
|
55
|
+
attentions_layers: List[eqx.Module] | eqx.Module = LinearAttention,
|
|
56
|
+
drop_rate: float = 0.0,
|
|
57
|
+
drop_path_rate: float = 0.0,
|
|
58
|
+
drop_path_uniform: bool = False,
|
|
59
|
+
mlp_ratio: float = 4.0,
|
|
60
|
+
num_classes: int | None = 1000,
|
|
61
|
+
**kwargs,
|
|
62
|
+
):
|
|
63
|
+
"""Initialize the MLLA model.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
img_size: Input image size
|
|
67
|
+
in_channels: Number of input channels
|
|
68
|
+
key: PRNG key for random operations
|
|
69
|
+
repeat: Number of times to repeat each block
|
|
70
|
+
dim: Initial embedding dimension
|
|
71
|
+
patch_size: Size of image patches
|
|
72
|
+
depths: Number of blocks at each stage
|
|
73
|
+
num_heads: Number of attention heads at each stage
|
|
74
|
+
attentions_layers: Type of attention layer(s) to use
|
|
75
|
+
drop_rate: Dropout rate
|
|
76
|
+
drop_path_rate: Drop path rate
|
|
77
|
+
drop_path_uniform: Whether to use uniform drop path rates
|
|
78
|
+
mlp_ratio: MLP expansion ratio
|
|
79
|
+
num_classes: Number of output classes (None for feature extraction)
|
|
80
|
+
"""
|
|
81
|
+
key_stem, key_head, *block_subkeys = jr.split(key, 2 + len(depths))
|
|
82
|
+
|
|
83
|
+
n_chunks = len(depths)
|
|
84
|
+
self.num_features = int(dim * 2 ** (n_chunks - 1))
|
|
85
|
+
|
|
86
|
+
self.patch_embed = Stem(
|
|
87
|
+
in_channels=in_channels,
|
|
88
|
+
img_size=img_size,
|
|
89
|
+
patch_size=patch_size,
|
|
90
|
+
embed_dim=dim,
|
|
91
|
+
key=key_stem,
|
|
92
|
+
)
|
|
93
|
+
patches_resolution = self.patch_embed.patches_resolution
|
|
94
|
+
|
|
95
|
+
self.pos_drop = eqx.nn.Dropout(drop_rate)
|
|
96
|
+
|
|
97
|
+
dpr = (
|
|
98
|
+
list(jnp.linspace(0.0, drop_path_rate, n_chunks))
|
|
99
|
+
if not drop_path_uniform
|
|
100
|
+
else to_list(drop_path_rate, n_chunks)
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
num_heads = to_list(num_heads, n_chunks)
|
|
104
|
+
attentions_layers = to_list(attentions_layers, n_chunks)
|
|
105
|
+
self.blocks = [
|
|
106
|
+
BlockChunk(
|
|
107
|
+
block=MllaBlock,
|
|
108
|
+
repeat=repeat,
|
|
109
|
+
depth=depth,
|
|
110
|
+
downsampler=PatchMerging if (i < n_chunks - 1) else eqx.nn.Identity,
|
|
111
|
+
downsampler_contains_dropout=False,
|
|
112
|
+
dim=int(dim * 2**i),
|
|
113
|
+
input_resolution=(
|
|
114
|
+
patches_resolution[0] // (2**i),
|
|
115
|
+
patches_resolution[1] // (2**i),
|
|
116
|
+
),
|
|
117
|
+
num_heads=num_heads[i],
|
|
118
|
+
act_layer=jax.nn.silu,
|
|
119
|
+
use_dwc=True,
|
|
120
|
+
attention_layer=attentions_layers[i],
|
|
121
|
+
drop_path=dpr[i],
|
|
122
|
+
mlp_ratio=mlp_ratio,
|
|
123
|
+
ffn_layer=Mlp,
|
|
124
|
+
key=block_subkeys[i],
|
|
125
|
+
)
|
|
126
|
+
for i, depth in enumerate(depths)
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
self.head = (
|
|
130
|
+
eqx.nn.Sequential(
|
|
131
|
+
[
|
|
132
|
+
eqx.nn.LayerNorm(self.num_features),
|
|
133
|
+
eqx.nn.Linear(self.num_features, num_classes, key=key_head),
|
|
134
|
+
]
|
|
135
|
+
)
|
|
136
|
+
if num_classes > 0
|
|
137
|
+
else eqx.nn.Identity()
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
def __call__(
|
|
141
|
+
self,
|
|
142
|
+
x: Float[Array, "..."],
|
|
143
|
+
enable_dropout: bool,
|
|
144
|
+
key: PRNGKeyArray,
|
|
145
|
+
) -> Float[Array, "..."]:
|
|
146
|
+
"""Process input through the MLLA model.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
x: Input tensor (typically an image)
|
|
150
|
+
enable_dropout: Whether to enable dropout during inference
|
|
151
|
+
key: PRNG key for random operations
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Output tensor (class logits if num_classes > 0,
|
|
155
|
+
otherwise feature representations)
|
|
156
|
+
"""
|
|
157
|
+
key_pd, *keys = jr.split(key, 1 + len(self.blocks))
|
|
158
|
+
|
|
159
|
+
x = self.patch_embed(x)
|
|
160
|
+
x = self.pos_drop(x, inference=not enable_dropout, key=key_pd)
|
|
161
|
+
for i, blk in enumerate(self.blocks):
|
|
162
|
+
x = blk(x, enable_dropout=enable_dropout, key=keys[i])
|
|
163
|
+
x = reduce(x, "s d -> d", "mean")
|
|
164
|
+
x = self.head(x)
|
|
165
|
+
|
|
166
|
+
return x
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
from typing import Callable, List, 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.attention import PartialFormerBlock
|
|
11
|
+
from equimo.layers.convolution import Stem
|
|
12
|
+
from equimo.layers.dropout import Dropout
|
|
13
|
+
from equimo.layers.ffn import Mlp
|
|
14
|
+
from equimo.layers.patch import PatchMerging
|
|
15
|
+
from equimo.layers.posemb import PosCNN
|
|
16
|
+
from equimo.layers.sharing import LayerSharing
|
|
17
|
+
from equimo.utils import to_list
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class LayerSharingWithQA(LayerSharing):
|
|
21
|
+
"""Layer sharing implementation with Query Attention (QA) token support.
|
|
22
|
+
|
|
23
|
+
Extends LayerSharing to handle query attention tokens while maintaining
|
|
24
|
+
layer sharing functionality. Processes both input features and QA tokens
|
|
25
|
+
through shared layers with optional LoRA adaptations.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __call__(
|
|
29
|
+
self,
|
|
30
|
+
x: Array,
|
|
31
|
+
qa: Array,
|
|
32
|
+
*args,
|
|
33
|
+
enable_dropout: bool,
|
|
34
|
+
key: PRNGKeyArray,
|
|
35
|
+
**kwargs,
|
|
36
|
+
):
|
|
37
|
+
if self.repeat == 1:
|
|
38
|
+
return self.f(
|
|
39
|
+
x,
|
|
40
|
+
*args,
|
|
41
|
+
enable_dropout=enable_dropout,
|
|
42
|
+
key=key,
|
|
43
|
+
**kwargs,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
keys = jr.split(key, self.repeat)
|
|
47
|
+
reshape = len(x.shape) == 3
|
|
48
|
+
|
|
49
|
+
for i in range(self.repeat):
|
|
50
|
+
if reshape:
|
|
51
|
+
_, h, w = x.shape
|
|
52
|
+
lora_x = rearrange(x, "c h w -> (h w) c")
|
|
53
|
+
else:
|
|
54
|
+
lora_x = x
|
|
55
|
+
lora_output = self.dropouts[i](
|
|
56
|
+
jax.vmap(self.loras[i])(lora_x),
|
|
57
|
+
inference=not enable_dropout,
|
|
58
|
+
key=keys[i],
|
|
59
|
+
)
|
|
60
|
+
if reshape:
|
|
61
|
+
lora_output = rearrange(
|
|
62
|
+
lora_output,
|
|
63
|
+
"(h w) c -> c h w",
|
|
64
|
+
h=h,
|
|
65
|
+
w=w,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
x, qa = self.f(
|
|
69
|
+
x,
|
|
70
|
+
qa=qa,
|
|
71
|
+
enable_dropout=enable_dropout,
|
|
72
|
+
key=key,
|
|
73
|
+
**kwargs,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
x += lora_output
|
|
77
|
+
|
|
78
|
+
return x, qa
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class BlockChunk(eqx.Module):
|
|
82
|
+
"""A chunk of processing blocks with query attention and downsampling support.
|
|
83
|
+
|
|
84
|
+
Processes input features through a sequence of attention blocks while
|
|
85
|
+
maintaining and updating a query attention token. Includes positional
|
|
86
|
+
embeddings, downsampling, and QA token projection capabilities.
|
|
87
|
+
|
|
88
|
+
Attributes:
|
|
89
|
+
reshape: Whether to reshape inputs for processing
|
|
90
|
+
downsampler_contains_dropout: If downsampler has dropout
|
|
91
|
+
posemb: Positional embedding layer
|
|
92
|
+
blocks: List of processing blocks
|
|
93
|
+
downsample: Downsampling layer
|
|
94
|
+
qa_proj: Query attention projection layer
|
|
95
|
+
qa_drop: Dropout for query attention
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
reshape: bool = eqx.field(static=True)
|
|
99
|
+
downsampler_contains_dropout: bool = eqx.field(static=True)
|
|
100
|
+
|
|
101
|
+
posemb: eqx.Module
|
|
102
|
+
blocks: List[eqx.Module]
|
|
103
|
+
downsample: eqx.Module
|
|
104
|
+
qa_proj: eqx.Module
|
|
105
|
+
qa_drop: eqx.Module
|
|
106
|
+
|
|
107
|
+
def __init__(
|
|
108
|
+
self,
|
|
109
|
+
depth: int,
|
|
110
|
+
*,
|
|
111
|
+
key: PRNGKeyArray,
|
|
112
|
+
block: eqx.Module = PartialFormerBlock,
|
|
113
|
+
use_cpe: bool = False,
|
|
114
|
+
qa_act_layer: Callable = jax.nn.relu,
|
|
115
|
+
qa_norm_layer: eqx.Module = eqx.nn.LayerNorm,
|
|
116
|
+
qa_drop: float = 0.0,
|
|
117
|
+
repeat: int = 1,
|
|
118
|
+
downsampler: eqx.Module = eqx.nn.Identity,
|
|
119
|
+
downsampler_contains_dropout: bool = False,
|
|
120
|
+
downsampler_kwargs: dict = {},
|
|
121
|
+
**kwargs,
|
|
122
|
+
):
|
|
123
|
+
key_ds, key_pos, key_qaproj, *block_subkeys = jr.split(key, depth + 3)
|
|
124
|
+
if not isinstance(downsampler, eqx.nn.Identity) or use_cpe:
|
|
125
|
+
if kwargs.get("dim") is None:
|
|
126
|
+
raise ValueError(
|
|
127
|
+
"Using a downsampler or a CPE requires passing a `dim` argument."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# self.reshape = block is not ConvBlock
|
|
131
|
+
self.reshape = True # TODO
|
|
132
|
+
self.downsampler_contains_dropout = downsampler_contains_dropout
|
|
133
|
+
|
|
134
|
+
keys_to_spread = [
|
|
135
|
+
k for k, v in kwargs.items() if isinstance(v, list) and len(v) == depth
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
dim = kwargs.get("dim")
|
|
139
|
+
self.posemb = (
|
|
140
|
+
PosCNN(
|
|
141
|
+
dim,
|
|
142
|
+
dim,
|
|
143
|
+
key=key_pos,
|
|
144
|
+
)
|
|
145
|
+
if use_cpe
|
|
146
|
+
else eqx.nn.Identity()
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
blocks = []
|
|
150
|
+
for i in range(depth):
|
|
151
|
+
config = kwargs | {k: kwargs[k][i] for k in keys_to_spread}
|
|
152
|
+
blocks.append(
|
|
153
|
+
LayerSharingWithQA(
|
|
154
|
+
dim=dim,
|
|
155
|
+
f=block(**config, key=block_subkeys[i]),
|
|
156
|
+
repeat=repeat,
|
|
157
|
+
key=block_subkeys[i],
|
|
158
|
+
),
|
|
159
|
+
)
|
|
160
|
+
self.blocks = blocks
|
|
161
|
+
|
|
162
|
+
self.downsample = downsampler(dim=dim, **downsampler_kwargs, key=key_ds)
|
|
163
|
+
self.qa_proj = (
|
|
164
|
+
eqx.nn.Sequential(
|
|
165
|
+
[
|
|
166
|
+
eqx.nn.Linear(
|
|
167
|
+
dim,
|
|
168
|
+
dim * 2 if downsampler is not eqx.nn.Identity else dim,
|
|
169
|
+
key=key_qaproj,
|
|
170
|
+
),
|
|
171
|
+
qa_norm_layer(dim * 2),
|
|
172
|
+
eqx.nn.Lambda(qa_act_layer),
|
|
173
|
+
]
|
|
174
|
+
)
|
|
175
|
+
if downsampler is not eqx.nn.Identity
|
|
176
|
+
else eqx.nn.Identity()
|
|
177
|
+
)
|
|
178
|
+
self.qa_drop = eqx.nn.Dropout(qa_drop)
|
|
179
|
+
|
|
180
|
+
def __call__(
|
|
181
|
+
self,
|
|
182
|
+
x: Float[Array, "seqlen dim"],
|
|
183
|
+
qa: Float[Array, "1 dim"],
|
|
184
|
+
*,
|
|
185
|
+
enable_dropout: bool,
|
|
186
|
+
key: PRNGKeyArray,
|
|
187
|
+
**kwargs,
|
|
188
|
+
) -> Tuple[Float[Array, "..."], Float[Array, "..."]]:
|
|
189
|
+
"""Process input features and query attention token.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
x: Input feature tensor
|
|
193
|
+
qa: Query attention token
|
|
194
|
+
enable_dropout: Whether to enable dropout
|
|
195
|
+
key: PRNG key for random operations
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
Tuple of (processed features, updated query attention token)
|
|
199
|
+
"""
|
|
200
|
+
key_qadrop, *keys = jr.split(key, len(self.blocks) + 1)
|
|
201
|
+
|
|
202
|
+
x = self.posemb(x)
|
|
203
|
+
|
|
204
|
+
for blk, key_block in zip(self.blocks, keys):
|
|
205
|
+
x, qa = blk(
|
|
206
|
+
x, qa=qa, enable_dropout=enable_dropout, key=key_block, **kwargs
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
if self.downsampler_contains_dropout:
|
|
210
|
+
x = self.downsample(x, enable_dropout, key)
|
|
211
|
+
else:
|
|
212
|
+
x = self.downsample(x)
|
|
213
|
+
qa = self.qa_drop(
|
|
214
|
+
jax.vmap(self.qa_proj)(qa),
|
|
215
|
+
inference=not enable_dropout,
|
|
216
|
+
key=key_qadrop,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
return x, qa
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class PartialFormer(eqx.Module):
|
|
223
|
+
"""PartialFormer implementation with a Partial Attention mechanism[1].
|
|
224
|
+
|
|
225
|
+
A vision transformer that processes images through patches while using
|
|
226
|
+
a query attention token to guide feature extraction. Combines hierarchical
|
|
227
|
+
feature processing with query-based attention mechanisms.
|
|
228
|
+
|
|
229
|
+
Attributes:
|
|
230
|
+
num_features: Number of features in final layer
|
|
231
|
+
qa_token: Query attention token
|
|
232
|
+
patch_embed: Patch embedding layer
|
|
233
|
+
pos_drop: Positional dropout
|
|
234
|
+
blocks: Processing blocks
|
|
235
|
+
norm: Normalization layer
|
|
236
|
+
head: Classification head
|
|
237
|
+
|
|
238
|
+
Notes:
|
|
239
|
+
WARNING, the original paper[1] does not provide an official
|
|
240
|
+
implementation. This implementation if an interpretation of the paper.
|
|
241
|
+
Although I made my best to follow what I read, it may not be a 1:1
|
|
242
|
+
replica.
|
|
243
|
+
|
|
244
|
+
References:
|
|
245
|
+
[1]: Vo, et al., 2024. https://eccv.ecva.net/virtual/2024/poster/1877
|
|
246
|
+
"""
|
|
247
|
+
|
|
248
|
+
num_features: int = eqx.field(static=True)
|
|
249
|
+
|
|
250
|
+
qa_token: jnp.ndarray
|
|
251
|
+
patch_embed: Stem
|
|
252
|
+
pos_drop: Dropout
|
|
253
|
+
blocks: List[eqx.Module]
|
|
254
|
+
norm: eqx.Module
|
|
255
|
+
head: eqx.Module
|
|
256
|
+
|
|
257
|
+
def __init__(
|
|
258
|
+
self,
|
|
259
|
+
img_size: int,
|
|
260
|
+
in_channels: int,
|
|
261
|
+
dim: int,
|
|
262
|
+
num_heads: int | List[int],
|
|
263
|
+
depths: List[int],
|
|
264
|
+
foreground_ratios: Tuple[float, float] | float,
|
|
265
|
+
*,
|
|
266
|
+
key: PRNGKeyArray,
|
|
267
|
+
patch_size: int = 7,
|
|
268
|
+
pos_drop_rate: float = 0.0,
|
|
269
|
+
drop_path_rate: float = 0.0,
|
|
270
|
+
drop_path_uniform: bool = False,
|
|
271
|
+
block: eqx.Module = PartialFormerBlock,
|
|
272
|
+
repeat: int = 1,
|
|
273
|
+
head_expand_ratio: float = 4.0,
|
|
274
|
+
mlp_ratio: float = 4.0,
|
|
275
|
+
qkv_bias: bool = True,
|
|
276
|
+
proj_bias: bool = True,
|
|
277
|
+
proj_ratio: float = 4.0,
|
|
278
|
+
qk_norm: bool = False,
|
|
279
|
+
attn_drop: float = 0.0,
|
|
280
|
+
proj_drop: float = 0.0,
|
|
281
|
+
act_layer: Callable = jax.nn.gelu,
|
|
282
|
+
ffn_layer: eqx.Module = Mlp,
|
|
283
|
+
ffn_bias: bool = True,
|
|
284
|
+
norm_layer: eqx.Module = eqx.nn.LayerNorm,
|
|
285
|
+
init_values: float | None = None,
|
|
286
|
+
num_classes: int = 1000,
|
|
287
|
+
interpolate_antialias: bool = False,
|
|
288
|
+
**kwargs,
|
|
289
|
+
):
|
|
290
|
+
depth = sum(depths)
|
|
291
|
+
key_qa, key_stem, key_head, *block_subkeys = jr.split(key, 3 + len(depths))
|
|
292
|
+
|
|
293
|
+
self.qa_token = jr.uniform(key_qa, (1, dim))
|
|
294
|
+
|
|
295
|
+
self.patch_embed = Stem(
|
|
296
|
+
in_channels=in_channels,
|
|
297
|
+
img_size=img_size,
|
|
298
|
+
patch_size=patch_size,
|
|
299
|
+
embed_dim=dim,
|
|
300
|
+
key=key_stem,
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
self.pos_drop = Dropout(pos_drop_rate)
|
|
304
|
+
|
|
305
|
+
if drop_path_uniform:
|
|
306
|
+
dpr = [drop_path_rate] * depth
|
|
307
|
+
else:
|
|
308
|
+
dpr = list(jnp.linspace(0.0, drop_path_rate, depth))
|
|
309
|
+
|
|
310
|
+
if isinstance(foreground_ratios, float):
|
|
311
|
+
f_ratios = [foreground_ratios] * depth
|
|
312
|
+
elif isinstance(foreground_ratios, Tuple[float, float]):
|
|
313
|
+
f_ratios = list(jnp.linspace(*foreground_ratios, depth))
|
|
314
|
+
else:
|
|
315
|
+
raise ValueError("Unknown type for forefround_ratios, got:")
|
|
316
|
+
|
|
317
|
+
n_chunks = len(depths)
|
|
318
|
+
num_heads = to_list(num_heads, n_chunks)
|
|
319
|
+
self.num_features = int(dim * 2 ** (n_chunks - 1))
|
|
320
|
+
self.blocks = [
|
|
321
|
+
BlockChunk(
|
|
322
|
+
block=block,
|
|
323
|
+
repeat=repeat,
|
|
324
|
+
dim=int(dim * 2**i),
|
|
325
|
+
depth=depths[i],
|
|
326
|
+
use_cpe=False,
|
|
327
|
+
downsampler=PatchMerging if (i < n_chunks - 1) else eqx.nn.Identity,
|
|
328
|
+
downsampler_contains_dropout=False,
|
|
329
|
+
foreground_ratio=f_ratios[i],
|
|
330
|
+
patch_size=patch_size,
|
|
331
|
+
num_heads=num_heads[i],
|
|
332
|
+
head_expand_ratio=head_expand_ratio,
|
|
333
|
+
mlp_ratio=mlp_ratio,
|
|
334
|
+
drop_path=dpr[sum(depths[:i]) : sum(depths[: i + 1])],
|
|
335
|
+
qkv_bias=qkv_bias,
|
|
336
|
+
proj_bias=proj_bias,
|
|
337
|
+
qk_norm=qk_norm,
|
|
338
|
+
attn_drop=attn_drop,
|
|
339
|
+
proj_drop=proj_drop,
|
|
340
|
+
proj_ratio=proj_ratio,
|
|
341
|
+
act_layer=act_layer,
|
|
342
|
+
ffn_layer=ffn_layer,
|
|
343
|
+
ffn_bias=ffn_bias,
|
|
344
|
+
norm_layer=norm_layer,
|
|
345
|
+
init_values=init_values,
|
|
346
|
+
key=block_subkeys[i],
|
|
347
|
+
)
|
|
348
|
+
for i, depth in enumerate(depths)
|
|
349
|
+
]
|
|
350
|
+
|
|
351
|
+
self.norm = norm_layer(self.num_features)
|
|
352
|
+
self.head = eqx.nn.Linear(self.num_features, num_classes, key=key_head)
|
|
353
|
+
|
|
354
|
+
def features(
|
|
355
|
+
self,
|
|
356
|
+
x: Float[Array, "channels height width"],
|
|
357
|
+
enable_dropout: bool,
|
|
358
|
+
key: PRNGKeyArray,
|
|
359
|
+
) -> Float[Array, "seqlen dim"]:
|
|
360
|
+
"""Extract features from input image using partial attention.
|
|
361
|
+
|
|
362
|
+
Args:
|
|
363
|
+
x: Input image tensor
|
|
364
|
+
enable_dropout: Whether to enable dropout during inference
|
|
365
|
+
key: PRNG key for random operations
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
Tuple of (processed features, final query attention token)
|
|
369
|
+
"""
|
|
370
|
+
key_posdrop, *block_subkeys = jr.split(key, len(self.blocks) + 1)
|
|
371
|
+
x = self.patch_embed(x)
|
|
372
|
+
x = self.pos_drop(x, inference=not enable_dropout, key=key_posdrop)
|
|
373
|
+
|
|
374
|
+
qa = self.qa_token
|
|
375
|
+
for blk, key_block in zip(self.blocks, block_subkeys):
|
|
376
|
+
x, qa = blk(
|
|
377
|
+
x,
|
|
378
|
+
qa=qa,
|
|
379
|
+
enable_dropout=enable_dropout,
|
|
380
|
+
key=key_block,
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
return x, qa
|
|
384
|
+
|
|
385
|
+
def __call__(
|
|
386
|
+
self,
|
|
387
|
+
x: Float[Array, "channels height width"],
|
|
388
|
+
enable_dropout: bool,
|
|
389
|
+
key: PRNGKeyArray,
|
|
390
|
+
) -> Float[Array, "num_classes"]:
|
|
391
|
+
"""Process input image through the full network.
|
|
392
|
+
|
|
393
|
+
Args:
|
|
394
|
+
x: Input image tensor
|
|
395
|
+
enable_dropout: Whether to enable dropout during inference
|
|
396
|
+
key: PRNG key for random operations
|
|
397
|
+
|
|
398
|
+
Returns:
|
|
399
|
+
Classification logits for each class
|
|
400
|
+
"""
|
|
401
|
+
x, qa = self.features(x, enable_dropout, key)
|
|
402
|
+
x = jax.vmap(self.norm)(x)
|
|
403
|
+
x = reduce(x, "n c -> c", "mean")
|
|
404
|
+
|
|
405
|
+
x = self.head(x)
|
|
406
|
+
|
|
407
|
+
return x
|