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,99 @@
|
|
|
1
|
+
# Draws inspiration from Timm's ModelEmaV2
|
|
2
|
+
# <https://github.com/huggingface/pytorch-image-models/blob/main/timm/utils/model_ema.py#L83>
|
|
3
|
+
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
|
|
6
|
+
import equinox as eqx
|
|
7
|
+
import jax
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class EmaModel:
|
|
11
|
+
"""
|
|
12
|
+
Exponential Moving Average (EMA) model wrapper.
|
|
13
|
+
|
|
14
|
+
This class implements an EMA model, which maintains a moving average of the weights
|
|
15
|
+
of another model. This can be useful for model smoothing and improving generalization.
|
|
16
|
+
|
|
17
|
+
Example usage can be Sharpness-Aware Training for Free[1].
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
model (nnx.Module): The underlying model to apply EMA to.
|
|
21
|
+
decay (float): The decay rate for the moving average. Default is 0.9999.
|
|
22
|
+
|
|
23
|
+
References:
|
|
24
|
+
[1] Du, et al. Sharpness-Aware Training for Free (2022). https://arxiv.org/abs/2205.14083
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
model: eqx.Module
|
|
28
|
+
decay: float
|
|
29
|
+
|
|
30
|
+
def __init__(self, model: eqx.Module, decay: float = 0.9999):
|
|
31
|
+
self.model = deepcopy(model)
|
|
32
|
+
self.decay = decay
|
|
33
|
+
self.ema_params = None
|
|
34
|
+
|
|
35
|
+
def __call__(self, *args, **kwargs):
|
|
36
|
+
"""
|
|
37
|
+
Call the underlying model.
|
|
38
|
+
|
|
39
|
+
This method allows the EMA model to be used as a drop-in replacement for the original model.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
*args: Positional arguments to pass to the underlying model.
|
|
43
|
+
**kwargs: Keyword arguments to pass to the underlying model.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
The output of the underlying model.
|
|
47
|
+
"""
|
|
48
|
+
return self.model(*args, **kwargs)
|
|
49
|
+
|
|
50
|
+
def initialize_ema(self, params):
|
|
51
|
+
"""
|
|
52
|
+
Initialize the EMA parameters.
|
|
53
|
+
|
|
54
|
+
This method creates a copy of the initial model parameters to start the EMA process.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
params: The initial parameters of the model.
|
|
58
|
+
"""
|
|
59
|
+
self.ema_params = jax.tree.map(lambda x: x.copy(), params)
|
|
60
|
+
|
|
61
|
+
def update(self, params):
|
|
62
|
+
"""
|
|
63
|
+
Update the EMA parameters.
|
|
64
|
+
|
|
65
|
+
This method updates the EMA parameters based on the current model parameters.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
params: The current parameters of the model.
|
|
69
|
+
"""
|
|
70
|
+
if self.ema_params is None:
|
|
71
|
+
self.initialize_ema(params)
|
|
72
|
+
else:
|
|
73
|
+
self.ema_params = jax.tree.map(
|
|
74
|
+
lambda ema, new: self.decay * ema + (1.0 - self.decay) * new,
|
|
75
|
+
self.ema_params,
|
|
76
|
+
params,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
self.model = eqx.apply_updates(self.model, self.ema_params)
|
|
80
|
+
|
|
81
|
+
def set(self, params):
|
|
82
|
+
"""
|
|
83
|
+
Set the EMA parameters directly.
|
|
84
|
+
|
|
85
|
+
This method allows for manual setting of the EMA parameters.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
params: The parameters to set as the new EMA parameters.
|
|
89
|
+
"""
|
|
90
|
+
self.ema_params = jax.tree.map(lambda x: x.copy(), params)
|
|
91
|
+
|
|
92
|
+
def get_ema_params(self):
|
|
93
|
+
"""
|
|
94
|
+
Get the current EMA parameters.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
The current EMA parameters.
|
|
98
|
+
"""
|
|
99
|
+
return self.ema_params
|
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
from typing import Callable, List, Literal, Optional
|
|
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
|
|
8
|
+
from jaxtyping import Array, Float, PRNGKeyArray
|
|
9
|
+
|
|
10
|
+
from equimo.layers.attention import HATBlock, WindowedAttention
|
|
11
|
+
from equimo.layers.convolution import ConvBlock
|
|
12
|
+
from equimo.layers.downsample import Downsampler
|
|
13
|
+
from equimo.layers.ffn import Mlp
|
|
14
|
+
from equimo.layers.patch import ConvPatchEmbed
|
|
15
|
+
from equimo.layers.sharing import LayerSharing
|
|
16
|
+
from equimo.utils import pool_sd, to_list
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class LayerSharingWithCT(LayerSharing):
|
|
20
|
+
"""Layer sharing implementation that handles carrier tokens (CT).
|
|
21
|
+
|
|
22
|
+
Extends LayerSharing to support passing and updating carrier tokens through
|
|
23
|
+
the network layers while maintaining layer sharing functionality.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __call__(
|
|
27
|
+
self,
|
|
28
|
+
x: Array,
|
|
29
|
+
ct: Array,
|
|
30
|
+
# *args,
|
|
31
|
+
enable_dropout: bool,
|
|
32
|
+
key: PRNGKeyArray,
|
|
33
|
+
**kwargs,
|
|
34
|
+
):
|
|
35
|
+
"""Apply layer sharing with carrier token support.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
x: Input tensor
|
|
39
|
+
ct: carrier token tensor
|
|
40
|
+
enable_dropout: Whether to enable dropout during inference
|
|
41
|
+
key: PRNG key for random operations
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Tuple of (processed tensor, updated carrier token)
|
|
45
|
+
"""
|
|
46
|
+
if self.repeat == 1:
|
|
47
|
+
return self.f(
|
|
48
|
+
x,
|
|
49
|
+
ct,
|
|
50
|
+
# *args,
|
|
51
|
+
enable_dropout=enable_dropout,
|
|
52
|
+
key=key,
|
|
53
|
+
**kwargs,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
keys = jr.split(key, self.repeat)
|
|
57
|
+
reshape = len(x.shape) == 3
|
|
58
|
+
|
|
59
|
+
for i in range(self.repeat):
|
|
60
|
+
if reshape:
|
|
61
|
+
_, h, w = x.shape
|
|
62
|
+
lora_x = rearrange(x, "c h w -> (h w) c")
|
|
63
|
+
else:
|
|
64
|
+
lora_x = x
|
|
65
|
+
lora_output = self.dropouts[i](
|
|
66
|
+
jax.vmap(self.loras[i])(lora_x),
|
|
67
|
+
inference=not enable_dropout,
|
|
68
|
+
key=keys[i],
|
|
69
|
+
)
|
|
70
|
+
if reshape:
|
|
71
|
+
lora_output = rearrange(
|
|
72
|
+
lora_output,
|
|
73
|
+
"(h w) c -> c h w",
|
|
74
|
+
h=h,
|
|
75
|
+
w=w,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
x, ct = self.f(
|
|
79
|
+
x,
|
|
80
|
+
ct,
|
|
81
|
+
enable_dropout=enable_dropout,
|
|
82
|
+
key=key,
|
|
83
|
+
**kwargs,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
x += lora_output
|
|
87
|
+
|
|
88
|
+
return x, ct
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class TokenInitializer(eqx.Module):
|
|
92
|
+
"""Initializes and processes carrier tokens for the network.
|
|
93
|
+
|
|
94
|
+
Creates carrier tokens by applying positional embeddings and pooling
|
|
95
|
+
to the input features. Used to generate global tokens for attention.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
ct_size: int = eqx.field(static=True)
|
|
99
|
+
stride: int = eqx.field(static=True)
|
|
100
|
+
kernel: int = eqx.field(static=True)
|
|
101
|
+
|
|
102
|
+
pos_embed: eqx.nn.Conv
|
|
103
|
+
pool: eqx.nn.AvgPool2d
|
|
104
|
+
|
|
105
|
+
def __init__(
|
|
106
|
+
self,
|
|
107
|
+
dim: int,
|
|
108
|
+
input_resolution: int,
|
|
109
|
+
window_size: int,
|
|
110
|
+
*,
|
|
111
|
+
key: PRNGKeyArray,
|
|
112
|
+
ct_size: int = 1,
|
|
113
|
+
**kwargs,
|
|
114
|
+
):
|
|
115
|
+
self.ct_size = ct_size
|
|
116
|
+
output_size = int(ct_size * input_resolution / window_size)
|
|
117
|
+
self.stride = int(input_resolution / output_size)
|
|
118
|
+
self.kernel = int(input_resolution - (output_size - 1) * self.stride)
|
|
119
|
+
|
|
120
|
+
self.pos_embed = eqx.nn.Conv(
|
|
121
|
+
num_spatial_dims=2,
|
|
122
|
+
in_channels=dim,
|
|
123
|
+
out_channels=dim,
|
|
124
|
+
kernel_size=3,
|
|
125
|
+
stride=1,
|
|
126
|
+
padding=1,
|
|
127
|
+
groups=dim,
|
|
128
|
+
key=key,
|
|
129
|
+
)
|
|
130
|
+
self.pool = eqx.nn.AvgPool2d(kernel_size=self.kernel, stride=self.stride)
|
|
131
|
+
|
|
132
|
+
def __call__(
|
|
133
|
+
self,
|
|
134
|
+
x: Float[Array, "channels height weight"],
|
|
135
|
+
) -> Float[Array, "new_channels seqlen"]:
|
|
136
|
+
x = self.pos_embed(x)
|
|
137
|
+
x = self.pool(x)
|
|
138
|
+
|
|
139
|
+
ct = rearrange(
|
|
140
|
+
x, "c (h h1) (w w1) -> (h w h1 w1) c", h1=self.ct_size, w1=self.ct_size
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
return ct
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class BlockChunk(eqx.Module):
|
|
147
|
+
"""A chunk of processing blocks with optional downsampling and global tokenization.
|
|
148
|
+
|
|
149
|
+
Handles a sequence of processing blocks (either ConvBlock or HATBlock),
|
|
150
|
+
with support for window partitioning, global tokenization, and downsampling.
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
reshape: bool = eqx.field(static=True)
|
|
154
|
+
downsampler_contains_dropout: bool = eqx.field(static=True)
|
|
155
|
+
is_hat: bool = eqx.field(static=True)
|
|
156
|
+
ct_size: int = eqx.field(static=True)
|
|
157
|
+
window_size: bool = eqx.field(static=True)
|
|
158
|
+
do_gt: bool = eqx.field(static=True)
|
|
159
|
+
|
|
160
|
+
blocks: List[eqx.Module]
|
|
161
|
+
downsample: eqx.Module
|
|
162
|
+
global_tokenizer: Optional[TokenInitializer]
|
|
163
|
+
|
|
164
|
+
def __init__(
|
|
165
|
+
self,
|
|
166
|
+
input_resolution: int,
|
|
167
|
+
window_size: int,
|
|
168
|
+
depth: int,
|
|
169
|
+
*,
|
|
170
|
+
key: PRNGKeyArray,
|
|
171
|
+
block: eqx.Module = HATBlock,
|
|
172
|
+
repeat: int = 1,
|
|
173
|
+
downsampler: eqx.Module = Downsampler,
|
|
174
|
+
downsampler_contains_dropout: bool = False,
|
|
175
|
+
only_local: bool = False,
|
|
176
|
+
hierarchy: bool = True,
|
|
177
|
+
ct_size: int = 1,
|
|
178
|
+
**kwargs,
|
|
179
|
+
):
|
|
180
|
+
key_ds, key_gt, *block_subkeys = jr.split(key, depth + 2)
|
|
181
|
+
if not isinstance(downsampler, eqx.nn.Identity):
|
|
182
|
+
if kwargs.get("dim") is None:
|
|
183
|
+
raise ValueError(
|
|
184
|
+
"Using a downsampler requires passing a `dim` argument."
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# self.reshape = block is not ConvBlock
|
|
188
|
+
self.reshape = True # TODO
|
|
189
|
+
self.downsampler_contains_dropout = downsampler_contains_dropout
|
|
190
|
+
self.is_hat = block is HATBlock
|
|
191
|
+
self.ct_size = ct_size
|
|
192
|
+
self.window_size = window_size
|
|
193
|
+
|
|
194
|
+
keys_to_spread = [
|
|
195
|
+
k for k, v in kwargs.items() if isinstance(v, list) and len(v) == depth
|
|
196
|
+
]
|
|
197
|
+
|
|
198
|
+
self.blocks = []
|
|
199
|
+
for i in range(depth):
|
|
200
|
+
config = kwargs | {k: kwargs[k][i] for k in keys_to_spread}
|
|
201
|
+
|
|
202
|
+
if self.is_hat:
|
|
203
|
+
config = config | {
|
|
204
|
+
"sr_ratio": (
|
|
205
|
+
input_resolution // window_size if not only_local else 1
|
|
206
|
+
),
|
|
207
|
+
"window_size": window_size,
|
|
208
|
+
"last": i == depth - 1,
|
|
209
|
+
"ct_size": self.ct_size,
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
wrapper = LayerSharingWithCT if self.is_hat else LayerSharing
|
|
213
|
+
self.blocks.append(
|
|
214
|
+
wrapper(
|
|
215
|
+
dim=kwargs.get("dim"),
|
|
216
|
+
f=block(**config, key=block_subkeys[i]),
|
|
217
|
+
repeat=repeat,
|
|
218
|
+
key=block_subkeys[i],
|
|
219
|
+
),
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
self.downsample = downsampler(dim=kwargs.get("dim"), key=key_ds)
|
|
223
|
+
|
|
224
|
+
if (
|
|
225
|
+
len(self.blocks)
|
|
226
|
+
and not only_local
|
|
227
|
+
and input_resolution // window_size > 1
|
|
228
|
+
and hierarchy
|
|
229
|
+
and self.is_hat
|
|
230
|
+
):
|
|
231
|
+
self.do_gt = True
|
|
232
|
+
self.global_tokenizer = TokenInitializer(
|
|
233
|
+
kwargs.get("dim"),
|
|
234
|
+
input_resolution,
|
|
235
|
+
window_size,
|
|
236
|
+
ct_size=self.ct_size,
|
|
237
|
+
key=key_gt,
|
|
238
|
+
)
|
|
239
|
+
else:
|
|
240
|
+
self.do_gt = False
|
|
241
|
+
self.global_tokenizer = eqx.nn.Identity()
|
|
242
|
+
|
|
243
|
+
def window_partition(
|
|
244
|
+
self,
|
|
245
|
+
x: Float[Array, "channels height width"],
|
|
246
|
+
window_size: int,
|
|
247
|
+
) -> Float[Array, "patches window_size channels"]:
|
|
248
|
+
"""Partition input tensor into windows.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
x: Input tensor of shape (channels, height, width)
|
|
252
|
+
window_size: Size of each window
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
Tensor partitioned into windows of specified size
|
|
256
|
+
"""
|
|
257
|
+
return rearrange(
|
|
258
|
+
x,
|
|
259
|
+
"c (h h1) (w w1) -> (h w) (h1 w1) c",
|
|
260
|
+
h1=window_size,
|
|
261
|
+
w1=window_size,
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
def window_reverse(
|
|
265
|
+
self,
|
|
266
|
+
x: Float[Array, "patches window_size channels"],
|
|
267
|
+
window_size: int,
|
|
268
|
+
h: int,
|
|
269
|
+
w: int,
|
|
270
|
+
) -> Float[Array, "channels height width"]:
|
|
271
|
+
"""Reverse window partitioning to original tensor shape.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
x: Window partitioned tensor
|
|
275
|
+
window_size: Size of each window
|
|
276
|
+
h: Original height
|
|
277
|
+
w: Original width
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
Tensor restored to original spatial dimensions
|
|
281
|
+
"""
|
|
282
|
+
return rearrange(
|
|
283
|
+
x,
|
|
284
|
+
"(h w) (h1 w1) c -> c (h h1) (w w1)",
|
|
285
|
+
h=h // window_size,
|
|
286
|
+
w=w // window_size,
|
|
287
|
+
h1=window_size,
|
|
288
|
+
w1=window_size,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
def __call__(
|
|
292
|
+
self,
|
|
293
|
+
x: Float[Array, "channels height width"],
|
|
294
|
+
enable_dropout: bool,
|
|
295
|
+
key: PRNGKeyArray,
|
|
296
|
+
) -> Float[Array, "..."]:
|
|
297
|
+
keys = jr.split(key, len(self.blocks))
|
|
298
|
+
ct = self.global_tokenizer(x) if self.do_gt else None
|
|
299
|
+
c, h, w = x.shape
|
|
300
|
+
|
|
301
|
+
if self.is_hat:
|
|
302
|
+
x = self.window_partition(x, self.window_size)
|
|
303
|
+
for blk, key_block in zip(self.blocks, keys):
|
|
304
|
+
x, ct = jax.vmap(
|
|
305
|
+
blk,
|
|
306
|
+
in_axes=(0, None, None, None),
|
|
307
|
+
out_axes=(0, None),
|
|
308
|
+
)(x, ct, enable_dropout, key_block)
|
|
309
|
+
x = self.window_reverse(x, self.window_size, h, w)
|
|
310
|
+
else:
|
|
311
|
+
for blk, key_block in zip(self.blocks, keys):
|
|
312
|
+
x = blk(x, enable_dropout=enable_dropout, key=key_block)
|
|
313
|
+
|
|
314
|
+
if self.downsampler_contains_dropout:
|
|
315
|
+
x = self.downsample(x, enable_dropout, key)
|
|
316
|
+
else:
|
|
317
|
+
x = self.downsample(x)
|
|
318
|
+
|
|
319
|
+
return x
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
class FasterViT(eqx.Module):
|
|
323
|
+
"""FasterViT: Fast Vision Transformers with Hierarchical Attention[1]
|
|
324
|
+
|
|
325
|
+
Implements a vision transformer architecture that combines convolution and
|
|
326
|
+
hierarchical attention mechanisms for efficient image processing. Features
|
|
327
|
+
carrier tokens, window attention, and progressive feature resolution reduction.
|
|
328
|
+
|
|
329
|
+
References:
|
|
330
|
+
[1]: Hatamizadeh, et al., 2023. https://arxiv.org/abs/2306.06189
|
|
331
|
+
"""
|
|
332
|
+
|
|
333
|
+
patch_embed: ConvPatchEmbed
|
|
334
|
+
blocks: List[eqx.Module]
|
|
335
|
+
norm: eqx.Module
|
|
336
|
+
head: eqx.Module
|
|
337
|
+
|
|
338
|
+
dim: int = eqx.field(static=True)
|
|
339
|
+
global_pool: str = eqx.field(static=True)
|
|
340
|
+
|
|
341
|
+
def __init__(
|
|
342
|
+
self,
|
|
343
|
+
img_size: int,
|
|
344
|
+
in_channels: int,
|
|
345
|
+
dim: int,
|
|
346
|
+
in_dim: int,
|
|
347
|
+
num_heads: int | List[int],
|
|
348
|
+
hat: bool | List[bool],
|
|
349
|
+
depths: List[int],
|
|
350
|
+
window_size: int | List[int],
|
|
351
|
+
ct_size: int,
|
|
352
|
+
*,
|
|
353
|
+
key: PRNGKeyArray,
|
|
354
|
+
drop_path_rate: float = 0.0,
|
|
355
|
+
drop_path_uniform: bool = False,
|
|
356
|
+
block: eqx.Module = HATBlock,
|
|
357
|
+
repeat: int = 1,
|
|
358
|
+
mlp_ratio: float = 4.0,
|
|
359
|
+
qkv_bias: bool = True,
|
|
360
|
+
proj_bias: bool = True,
|
|
361
|
+
qk_norm: bool = False,
|
|
362
|
+
attn_drop: float = 0.0,
|
|
363
|
+
proj_drop: float = 0.0,
|
|
364
|
+
act_layer: Callable = jax.nn.gelu,
|
|
365
|
+
attn_layer: eqx.Module = WindowedAttention,
|
|
366
|
+
ffn_layer: eqx.Module = Mlp,
|
|
367
|
+
ffn_bias: bool = True,
|
|
368
|
+
norm_layer: eqx.Module = eqx.nn.LayerNorm,
|
|
369
|
+
init_values: float | None = 1e-5,
|
|
370
|
+
ls_convblock: bool = False,
|
|
371
|
+
do_propagation: bool = False,
|
|
372
|
+
global_pool: Literal["", "token", "avg", "avgmax", "max"] = "avg",
|
|
373
|
+
num_classes: int = 1000,
|
|
374
|
+
interpolate_antialias: bool = False,
|
|
375
|
+
**kwargs,
|
|
376
|
+
):
|
|
377
|
+
depth = sum(depths)
|
|
378
|
+
key_patchemb, key_posemb, key_cls, key_reg, key_head, *block_subkeys = jr.split(
|
|
379
|
+
key, 5 + len(depths)
|
|
380
|
+
)
|
|
381
|
+
self.dim = dim
|
|
382
|
+
self.global_pool = global_pool
|
|
383
|
+
|
|
384
|
+
self.patch_embed = ConvPatchEmbed(
|
|
385
|
+
in_channels,
|
|
386
|
+
in_dim,
|
|
387
|
+
dim,
|
|
388
|
+
key=key_patchemb,
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
if drop_path_uniform:
|
|
392
|
+
dpr = [drop_path_rate] * depth
|
|
393
|
+
else:
|
|
394
|
+
dpr = list(jnp.linspace(0.0, drop_path_rate, depth))
|
|
395
|
+
|
|
396
|
+
n_chunks = len(depths)
|
|
397
|
+
num_heads = to_list(num_heads, n_chunks)
|
|
398
|
+
hat = to_list(hat, n_chunks)
|
|
399
|
+
attn_layer = to_list(attn_layer, n_chunks)
|
|
400
|
+
window_size = to_list(window_size, n_chunks)
|
|
401
|
+
self.blocks = [
|
|
402
|
+
BlockChunk(
|
|
403
|
+
block=ConvBlock if i < 2 else HATBlock,
|
|
404
|
+
repeat=repeat,
|
|
405
|
+
dim=int(dim * 2**i),
|
|
406
|
+
depth=depths[i],
|
|
407
|
+
input_resolution=int(2 ** (-2 - i) * img_size),
|
|
408
|
+
window_size=window_size[i],
|
|
409
|
+
ct_size=ct_size,
|
|
410
|
+
num_heads=num_heads[i],
|
|
411
|
+
mlp_ratio=mlp_ratio,
|
|
412
|
+
drop_path=dpr[sum(depths[:i]) : sum(depths[: i + 1])],
|
|
413
|
+
qkv_bias=qkv_bias,
|
|
414
|
+
proj_bias=proj_bias,
|
|
415
|
+
qk_norm=qk_norm,
|
|
416
|
+
attn_drop=attn_drop,
|
|
417
|
+
proj_drop=proj_drop,
|
|
418
|
+
act_layer=act_layer,
|
|
419
|
+
attn_layer=attn_layer[i],
|
|
420
|
+
ffn_layer=ffn_layer,
|
|
421
|
+
ffn_bias=ffn_bias,
|
|
422
|
+
norm_layer=norm_layer,
|
|
423
|
+
init_values=None if i < 2 and not ls_convblock else init_values,
|
|
424
|
+
downsampler=Downsampler if i < len(depths) - 1 else eqx.nn.Identity,
|
|
425
|
+
only_local=not hat[i],
|
|
426
|
+
do_propagation=do_propagation,
|
|
427
|
+
key=block_subkeys[i],
|
|
428
|
+
)
|
|
429
|
+
for i, depth in enumerate(depths)
|
|
430
|
+
]
|
|
431
|
+
|
|
432
|
+
num_features = int(dim * 2 ** (len(depths) - 1))
|
|
433
|
+
self.norm = norm_layer(num_features)
|
|
434
|
+
self.head = eqx.nn.Linear(num_features, num_classes, key=key_head)
|
|
435
|
+
|
|
436
|
+
def features(
|
|
437
|
+
self,
|
|
438
|
+
x: Float[Array, "channels height width"],
|
|
439
|
+
enable_dropout: bool,
|
|
440
|
+
key: PRNGKeyArray,
|
|
441
|
+
) -> Float[Array, "seqlen dim"]:
|
|
442
|
+
"""Extract features from input image.
|
|
443
|
+
|
|
444
|
+
Args:
|
|
445
|
+
x: Input image tensor
|
|
446
|
+
enable_dropout: Whether to enable dropout during inference
|
|
447
|
+
key: PRNG key for random operations
|
|
448
|
+
|
|
449
|
+
Returns:
|
|
450
|
+
Processed feature tensor
|
|
451
|
+
"""
|
|
452
|
+
key_posdrop, *block_subkeys = jr.split(key, len(self.blocks) + 1)
|
|
453
|
+
x = self.patch_embed(x)
|
|
454
|
+
|
|
455
|
+
for blk, key_block in zip(self.blocks, block_subkeys):
|
|
456
|
+
x = blk(x, enable_dropout=enable_dropout, key=key_block)
|
|
457
|
+
|
|
458
|
+
return x
|
|
459
|
+
|
|
460
|
+
def __call__(
|
|
461
|
+
self,
|
|
462
|
+
x: Float[Array, "channels height width"],
|
|
463
|
+
enable_dropout: bool,
|
|
464
|
+
key: PRNGKeyArray,
|
|
465
|
+
) -> Float[Array, "num_classes"]:
|
|
466
|
+
"""Process input image through the full network.
|
|
467
|
+
|
|
468
|
+
Args:
|
|
469
|
+
x: Input image tensor
|
|
470
|
+
enable_dropout: Whether to enable dropout during inference
|
|
471
|
+
key: PRNG key for random operations
|
|
472
|
+
|
|
473
|
+
Returns:
|
|
474
|
+
Classification logits for each class
|
|
475
|
+
"""
|
|
476
|
+
x = self.features(x, enable_dropout, key)
|
|
477
|
+
x = rearrange(x, "c h w -> (h w) c")
|
|
478
|
+
x = jax.vmap(self.norm)(x)
|
|
479
|
+
x = pool_sd(
|
|
480
|
+
x,
|
|
481
|
+
num_prefix_tokens=0,
|
|
482
|
+
pool_type=self.global_pool,
|
|
483
|
+
reduce_include_prefix=False,
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
x = self.head(x)
|
|
487
|
+
|
|
488
|
+
return x
|