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,329 @@
|
|
|
1
|
+
from typing import Callable, Optional
|
|
2
|
+
|
|
3
|
+
import equinox as eqx
|
|
4
|
+
import jax
|
|
5
|
+
import jax.random as jr
|
|
6
|
+
from einops import rearrange
|
|
7
|
+
from jaxtyping import Array, Float, PRNGKeyArray
|
|
8
|
+
|
|
9
|
+
from equimo.layers.dropout import DropPathAdd
|
|
10
|
+
from equimo.layers.norm import LayerScale
|
|
11
|
+
from equimo.utils import nearest_power_of_2_divisor
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ConvBlock(eqx.Module):
|
|
15
|
+
"""A residual convolutional block with normalization and regularization.
|
|
16
|
+
|
|
17
|
+
This block implements a residual connection with two convolution layers,
|
|
18
|
+
group normalization, activation, layer scaling, and drop path regularization.
|
|
19
|
+
The block maintains the input dimension while allowing for an optional
|
|
20
|
+
intermediate hidden dimension.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
conv1: First convolution layer
|
|
24
|
+
conv2: Second convolution layer
|
|
25
|
+
norm1: Group normalization after first conv
|
|
26
|
+
norm2: Group normalization after second conv
|
|
27
|
+
drop_path1: Drop path regularization for residual connection
|
|
28
|
+
act: Activation function
|
|
29
|
+
ls1: Layer scaling module
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
conv1: eqx.nn.Conv
|
|
33
|
+
conv2: eqx.nn.Conv
|
|
34
|
+
norm1: eqx.Module
|
|
35
|
+
norm2: eqx.Module
|
|
36
|
+
drop_path1: DropPathAdd
|
|
37
|
+
act: Callable
|
|
38
|
+
ls1: LayerScale
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
dim: int,
|
|
43
|
+
*,
|
|
44
|
+
key: PRNGKeyArray,
|
|
45
|
+
hidden_dim: int | None = None,
|
|
46
|
+
kernel_size: int = 3,
|
|
47
|
+
stride: int = 1,
|
|
48
|
+
padding: int = 1,
|
|
49
|
+
act_layer: Callable = jax.nn.gelu,
|
|
50
|
+
norm_max_group: int = 32,
|
|
51
|
+
drop_path: float = 0.0,
|
|
52
|
+
init_values: float | None = None,
|
|
53
|
+
**kwargs,
|
|
54
|
+
):
|
|
55
|
+
"""Initialize the ConvBlock.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
dim: Input and output channel dimension
|
|
59
|
+
key: PRNG key for initialization
|
|
60
|
+
hidden_dim: Optional intermediate channel dimension (defaults to dim)
|
|
61
|
+
kernel_size: Size of the convolutional kernel (default: 3)
|
|
62
|
+
stride: Stride of the convolution (default: 1)
|
|
63
|
+
padding: Padding size for convolution (default: 1)
|
|
64
|
+
act_layer: Activation function (default: gelu)
|
|
65
|
+
norm_max_group: Maximum number of groups for GroupNorm (default: 32)
|
|
66
|
+
drop_path: Drop path rate (default: 0.0)
|
|
67
|
+
init_values: Initial value for layer scaling (default: None)
|
|
68
|
+
**kwargs: Additional arguments passed to Conv layers
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
key_conv1, key_conv2 = jr.split(key, 2)
|
|
72
|
+
hidden_dim = hidden_dim or dim
|
|
73
|
+
num_groups = nearest_power_of_2_divisor(dim, norm_max_group)
|
|
74
|
+
self.conv1 = eqx.nn.Conv(
|
|
75
|
+
num_spatial_dims=2,
|
|
76
|
+
in_channels=dim,
|
|
77
|
+
out_channels=hidden_dim,
|
|
78
|
+
kernel_size=kernel_size,
|
|
79
|
+
stride=stride,
|
|
80
|
+
padding=padding,
|
|
81
|
+
use_bias=True,
|
|
82
|
+
key=key_conv1,
|
|
83
|
+
)
|
|
84
|
+
self.norm1 = eqx.nn.GroupNorm(num_groups, hidden_dim)
|
|
85
|
+
self.act = act_layer
|
|
86
|
+
self.conv2 = eqx.nn.Conv(
|
|
87
|
+
num_spatial_dims=2,
|
|
88
|
+
in_channels=hidden_dim,
|
|
89
|
+
out_channels=dim,
|
|
90
|
+
kernel_size=kernel_size,
|
|
91
|
+
stride=stride,
|
|
92
|
+
padding=padding,
|
|
93
|
+
use_bias=True,
|
|
94
|
+
key=key_conv2,
|
|
95
|
+
)
|
|
96
|
+
self.norm2 = eqx.nn.GroupNorm(num_groups, dim)
|
|
97
|
+
|
|
98
|
+
dpr = drop_path[0] if isinstance(drop_path, list) else float(drop_path)
|
|
99
|
+
self.drop_path1 = DropPathAdd(dpr)
|
|
100
|
+
|
|
101
|
+
self.ls1 = (
|
|
102
|
+
LayerScale(dim, init_values=init_values)
|
|
103
|
+
if init_values
|
|
104
|
+
else eqx.nn.Identity()
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def permute(
|
|
108
|
+
self, x: Float[Array, "channels height width"]
|
|
109
|
+
) -> Float[Array, "height width channels"]:
|
|
110
|
+
return rearrange(x, "c h w -> h w c")
|
|
111
|
+
|
|
112
|
+
def depermute(
|
|
113
|
+
self,
|
|
114
|
+
x: Float[Array, "height width channels"],
|
|
115
|
+
) -> Float[Array, "channels height width"]:
|
|
116
|
+
return rearrange(x, "h w c -> c h w")
|
|
117
|
+
|
|
118
|
+
def __call__(
|
|
119
|
+
self,
|
|
120
|
+
x: Float[Array, "channels height width"],
|
|
121
|
+
enable_dropout: bool,
|
|
122
|
+
key: PRNGKeyArray,
|
|
123
|
+
) -> Float[Array, "channels height width"]:
|
|
124
|
+
_, h, w = x.shape
|
|
125
|
+
x2 = self.act(self.norm1(self.conv1(x)))
|
|
126
|
+
x2 = self.norm2(self.conv2(x2))
|
|
127
|
+
x2 = self.depermute(jax.vmap(jax.vmap(self.ls1))(self.permute(x2)))
|
|
128
|
+
|
|
129
|
+
return self.drop_path1(x, x2, inference=not enable_dropout, key=key)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class SingleConvBlock(eqx.Module):
|
|
133
|
+
"""A basic convolution block combining convolution, normalization and activation.
|
|
134
|
+
|
|
135
|
+
This block provides a streamlined combination of convolution, optional group
|
|
136
|
+
normalization, and optional activation in a single unit. It's designed to be
|
|
137
|
+
a fundamental building block for larger architectures.
|
|
138
|
+
|
|
139
|
+
Attributes:
|
|
140
|
+
conv: Convolution layer
|
|
141
|
+
norm: Normalization layer (GroupNorm or Identity)
|
|
142
|
+
act: Activation layer (Lambda or Identity)
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
conv: eqx.nn.Conv
|
|
146
|
+
norm: eqx.Module
|
|
147
|
+
act: eqx.Module
|
|
148
|
+
|
|
149
|
+
def __init__(
|
|
150
|
+
self,
|
|
151
|
+
in_channels: int,
|
|
152
|
+
out_channels: int,
|
|
153
|
+
*,
|
|
154
|
+
key: PRNGKeyArray,
|
|
155
|
+
use_norm: bool = True,
|
|
156
|
+
norm_max_group: int = 32,
|
|
157
|
+
act_layer: Callable | None = None,
|
|
158
|
+
**kwargs,
|
|
159
|
+
):
|
|
160
|
+
"""Initialize the SingleConvBlock.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
in_channels: Number of input channels
|
|
164
|
+
out_channels: Number of output channels
|
|
165
|
+
key: PRNG key for initialization
|
|
166
|
+
use_norm: Whether to use group normalization (default: True)
|
|
167
|
+
norm_max_group: Maximum number of groups for GroupNorm (default: 32)
|
|
168
|
+
act_layer: Optional activation function (default: None)
|
|
169
|
+
**kwargs: Additional arguments passed to Conv layer
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
num_groups = nearest_power_of_2_divisor(out_channels, norm_max_group)
|
|
173
|
+
self.conv = eqx.nn.Conv(
|
|
174
|
+
num_spatial_dims=2,
|
|
175
|
+
in_channels=in_channels,
|
|
176
|
+
out_channels=out_channels,
|
|
177
|
+
key=key,
|
|
178
|
+
**kwargs,
|
|
179
|
+
)
|
|
180
|
+
self.norm = (
|
|
181
|
+
eqx.nn.GroupNorm(num_groups, out_channels)
|
|
182
|
+
if use_norm
|
|
183
|
+
else eqx.nn.Identity()
|
|
184
|
+
)
|
|
185
|
+
self.act = eqx.nn.Lambda(act_layer) if act_layer else eqx.nn.Identity()
|
|
186
|
+
|
|
187
|
+
def __call__(
|
|
188
|
+
self,
|
|
189
|
+
x: Float[Array, "channels height width"],
|
|
190
|
+
*,
|
|
191
|
+
key: Optional[PRNGKeyArray] = None,
|
|
192
|
+
) -> Float[Array, "dim height width"]:
|
|
193
|
+
return self.act(self.norm(self.conv(x)))
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class Stem(eqx.Module):
|
|
197
|
+
"""Image-to-embedding stem network for vision transformers.
|
|
198
|
+
|
|
199
|
+
This module processes raw input images into patch embeddings through a series
|
|
200
|
+
of convolutional stages. It includes three main components:
|
|
201
|
+
1. Initial downsampling with conv + norm + activation
|
|
202
|
+
2. Residual block with two convolutions
|
|
203
|
+
3. Final downsampling and channel projection
|
|
204
|
+
|
|
205
|
+
The output is reshaped into a sequence of patch embeddings suitable for
|
|
206
|
+
transformer processing.
|
|
207
|
+
|
|
208
|
+
Attributes:
|
|
209
|
+
num_patches: Total number of patches (static)
|
|
210
|
+
patches_resolution: Spatial resolution of patches (static)
|
|
211
|
+
conv1: Initial convolution block
|
|
212
|
+
conv2: Middle residual convolution blocks
|
|
213
|
+
conv3: Final convolution blocks
|
|
214
|
+
"""
|
|
215
|
+
|
|
216
|
+
num_patches: int = eqx.field(static=True)
|
|
217
|
+
patches_resolution: int = eqx.field(static=True)
|
|
218
|
+
|
|
219
|
+
conv1: eqx.nn.Conv
|
|
220
|
+
conv2: eqx.nn.Conv
|
|
221
|
+
conv3: eqx.nn.Conv
|
|
222
|
+
|
|
223
|
+
def __init__(
|
|
224
|
+
self,
|
|
225
|
+
in_channels: int,
|
|
226
|
+
*,
|
|
227
|
+
key: PRNGKeyArray,
|
|
228
|
+
img_size: int = 224,
|
|
229
|
+
patch_size: int = 4,
|
|
230
|
+
embed_dim=96,
|
|
231
|
+
**kwargs,
|
|
232
|
+
):
|
|
233
|
+
"""Initialize the Stem network.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
in_channels: Number of input image channels
|
|
237
|
+
key: PRNG key for initialization
|
|
238
|
+
img_size: Input image size (default: 224)
|
|
239
|
+
patch_size: Size of each patch (default: 4)
|
|
240
|
+
embed_dim: Final embedding dimension (default: 96)
|
|
241
|
+
**kwargs: Additional arguments passed to convolution blocks
|
|
242
|
+
"""
|
|
243
|
+
self.num_patches = (img_size // patch_size) ** 2
|
|
244
|
+
self.patches_resolution = [img_size // patch_size] * 2
|
|
245
|
+
(
|
|
246
|
+
key_conv1,
|
|
247
|
+
key_conv2,
|
|
248
|
+
key_conv3,
|
|
249
|
+
key_conv4,
|
|
250
|
+
key_conv5,
|
|
251
|
+
) = jr.split(key, 5)
|
|
252
|
+
|
|
253
|
+
self.conv1 = SingleConvBlock(
|
|
254
|
+
in_channels=in_channels,
|
|
255
|
+
out_channels=embed_dim // 2,
|
|
256
|
+
kernel_size=3,
|
|
257
|
+
stride=2,
|
|
258
|
+
padding=1,
|
|
259
|
+
use_bias=False,
|
|
260
|
+
use_norm=True,
|
|
261
|
+
act_layer=jax.nn.relu,
|
|
262
|
+
key=key_conv1,
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
self.conv2 = eqx.nn.Sequential(
|
|
266
|
+
[
|
|
267
|
+
SingleConvBlock(
|
|
268
|
+
in_channels=embed_dim // 2,
|
|
269
|
+
out_channels=embed_dim // 2,
|
|
270
|
+
kernel_size=3,
|
|
271
|
+
stride=1,
|
|
272
|
+
padding=1,
|
|
273
|
+
use_bias=False,
|
|
274
|
+
use_norm=True,
|
|
275
|
+
act_layer=jax.nn.relu,
|
|
276
|
+
key=key_conv2,
|
|
277
|
+
),
|
|
278
|
+
SingleConvBlock(
|
|
279
|
+
in_channels=embed_dim // 2,
|
|
280
|
+
out_channels=embed_dim // 2,
|
|
281
|
+
kernel_size=3,
|
|
282
|
+
stride=1,
|
|
283
|
+
padding=1,
|
|
284
|
+
use_bias=False,
|
|
285
|
+
use_norm=True,
|
|
286
|
+
act_layer=None,
|
|
287
|
+
key=key_conv3,
|
|
288
|
+
),
|
|
289
|
+
]
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
self.conv3 = eqx.nn.Sequential(
|
|
293
|
+
[
|
|
294
|
+
SingleConvBlock(
|
|
295
|
+
in_channels=embed_dim // 2,
|
|
296
|
+
out_channels=embed_dim * 4,
|
|
297
|
+
kernel_size=3,
|
|
298
|
+
stride=2,
|
|
299
|
+
padding=1,
|
|
300
|
+
use_bias=False,
|
|
301
|
+
use_norm=True,
|
|
302
|
+
act_layer=jax.nn.relu,
|
|
303
|
+
key=key_conv4,
|
|
304
|
+
),
|
|
305
|
+
SingleConvBlock(
|
|
306
|
+
in_channels=embed_dim * 4,
|
|
307
|
+
out_channels=embed_dim,
|
|
308
|
+
kernel_size=1,
|
|
309
|
+
stride=1,
|
|
310
|
+
padding=0,
|
|
311
|
+
use_bias=False,
|
|
312
|
+
use_norm=True,
|
|
313
|
+
act_layer=None,
|
|
314
|
+
key=key_conv5,
|
|
315
|
+
),
|
|
316
|
+
]
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
def __call__(
|
|
320
|
+
self,
|
|
321
|
+
x: Float[Array, "channels height width"],
|
|
322
|
+
*,
|
|
323
|
+
key: Optional[PRNGKeyArray] = None,
|
|
324
|
+
) -> Float[Array, "seqlen dim"]:
|
|
325
|
+
x = self.conv1(x)
|
|
326
|
+
x = self.conv2(x) + x
|
|
327
|
+
x = self.conv3(x)
|
|
328
|
+
|
|
329
|
+
return rearrange(x, "c h w -> (h w) c")
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import equinox as eqx
|
|
2
|
+
from jaxtyping import Array, Float, PRNGKeyArray
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Downsampler(eqx.Module):
|
|
6
|
+
"""A module that performs spatial downsampling using strided convolution.
|
|
7
|
+
|
|
8
|
+
This module reduces spatial dimensions (height and width) by a factor of 2
|
|
9
|
+
while optionally increasing the channel dimension. Uses a 3x3 strided
|
|
10
|
+
convolution for downsampling.
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
reduction: Convolutional layer that performs the downsampling
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
reduction: eqx.Module
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
dim: int,
|
|
21
|
+
*,
|
|
22
|
+
key=PRNGKeyArray,
|
|
23
|
+
keep_dim: bool = False,
|
|
24
|
+
):
|
|
25
|
+
"""Initialize the Downsampler.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
dim: Number of input channels
|
|
29
|
+
key: PRNG key for initialization
|
|
30
|
+
keep_dim: If True, maintains channel dimension; if False, doubles it
|
|
31
|
+
(default: False)
|
|
32
|
+
"""
|
|
33
|
+
dim_out = dim if keep_dim else 2 * dim
|
|
34
|
+
self.reduction = eqx.nn.Conv(
|
|
35
|
+
num_spatial_dims=2,
|
|
36
|
+
in_channels=dim,
|
|
37
|
+
out_channels=dim_out,
|
|
38
|
+
kernel_size=(3, 3),
|
|
39
|
+
stride=(2, 2),
|
|
40
|
+
padding=(1, 1),
|
|
41
|
+
use_bias=False,
|
|
42
|
+
key=key,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def __call__(
|
|
46
|
+
self,
|
|
47
|
+
x: Float[Array, "channels height width"],
|
|
48
|
+
) -> Float[Array, "new_channels new_height new_width"]:
|
|
49
|
+
"""Apply downsampling to the input tensor.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
x: Input tensor of shape (channels, height, width)
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Downsampled tensor with halved spatial dimensions and potentially
|
|
56
|
+
doubled channels depending on keep_dim parameter
|
|
57
|
+
"""
|
|
58
|
+
return self.reduction(x)
|
equimo/layers/dropout.py
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
import equinox as eqx
|
|
5
|
+
import jax
|
|
6
|
+
import jax.lax as lax
|
|
7
|
+
import jax.numpy as jnp
|
|
8
|
+
import jax.random as jrandom
|
|
9
|
+
from jaxtyping import Array, PRNGKeyArray
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Dropout(eqx.Module, strict=True):
|
|
13
|
+
"""Applies dropout.
|
|
14
|
+
|
|
15
|
+
Note that this layer behaves differently during training and inference. During
|
|
16
|
+
training then dropout is randomly applied; during inference this layer does nothing.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
# Let's make them static fields, just to avoid possible filtering issues
|
|
20
|
+
p: float = eqx.field(static=True)
|
|
21
|
+
inference: bool = eqx.field(static=True)
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
p: float = 0.5,
|
|
26
|
+
inference: bool = False,
|
|
27
|
+
*,
|
|
28
|
+
deterministic: Optional[bool] = None,
|
|
29
|
+
):
|
|
30
|
+
"""**Arguments:**
|
|
31
|
+
|
|
32
|
+
- `p`: The fraction of entries to set to zero. (On average.)
|
|
33
|
+
- `inference`: Whether to actually apply dropout at all. If `True` then dropout
|
|
34
|
+
is *not* applied. If `False` then dropout is applied. This may be toggled
|
|
35
|
+
with [`equinox.nn.inference_mode`][] or overridden during
|
|
36
|
+
[`equinox.nn.Dropout.__call__`][].
|
|
37
|
+
- `deterministic`: Deprecated alternative to `inference`.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
if deterministic is not None:
|
|
41
|
+
inference = deterministic
|
|
42
|
+
warnings.warn(
|
|
43
|
+
"Dropout(deterministic=...) is deprecated "
|
|
44
|
+
"in favour of Dropout(inference=...)"
|
|
45
|
+
)
|
|
46
|
+
self.p = p
|
|
47
|
+
self.inference = inference
|
|
48
|
+
|
|
49
|
+
# Backward compatibility
|
|
50
|
+
@property
|
|
51
|
+
def deterministic(self):
|
|
52
|
+
return self.inference
|
|
53
|
+
|
|
54
|
+
@jax.named_scope("eqx.nn.Dropout")
|
|
55
|
+
def __call__(
|
|
56
|
+
self,
|
|
57
|
+
x: Array,
|
|
58
|
+
*,
|
|
59
|
+
key: Optional[PRNGKeyArray] = None,
|
|
60
|
+
inference: Optional[bool] = None,
|
|
61
|
+
deterministic: Optional[bool] = None,
|
|
62
|
+
) -> Array:
|
|
63
|
+
"""**Arguments:**
|
|
64
|
+
|
|
65
|
+
- `x`: An any-dimensional JAX array to dropout.
|
|
66
|
+
- `key`: A `jax.random.PRNGKey` used to provide randomness for calculating
|
|
67
|
+
which elements to dropout. (Keyword only argument.)
|
|
68
|
+
- `inference`: As per [`equinox.nn.Dropout.__init__`][]. If `True` or
|
|
69
|
+
`False` then it will take priority over `self.inference`. If `None`
|
|
70
|
+
then the value from `self.inference` will be used.
|
|
71
|
+
- `deterministic`: Deprecated alternative to `inference`.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
if deterministic is not None:
|
|
75
|
+
inference = deterministic
|
|
76
|
+
warnings.warn(
|
|
77
|
+
"Dropout()(deterministic=...) is deprecated "
|
|
78
|
+
"in favour of Dropout()(inference=...)"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
if inference is None:
|
|
82
|
+
inference = self.inference
|
|
83
|
+
if isinstance(self.p, (int, float)) and self.p == 0:
|
|
84
|
+
inference = True
|
|
85
|
+
if inference:
|
|
86
|
+
return x
|
|
87
|
+
elif key is None:
|
|
88
|
+
raise RuntimeError(
|
|
89
|
+
"Dropout requires a key when running in non-deterministic mode."
|
|
90
|
+
)
|
|
91
|
+
else:
|
|
92
|
+
q = 1 - lax.stop_gradient(self.p)
|
|
93
|
+
mask = jrandom.bernoulli(key, q, x.shape)
|
|
94
|
+
return jnp.where(mask, x / q, 0)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class DropPath(eqx.Module, strict=True):
|
|
98
|
+
"""Applies drop path (stochastic depth).
|
|
99
|
+
|
|
100
|
+
Note that this layer behaves differently during training and inference. During
|
|
101
|
+
training then dropout is randomly applied; during inference this layer does nothing.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
# Let's make them static fields, just to avoid possible filtering issues
|
|
105
|
+
p: float = eqx.field(static=True)
|
|
106
|
+
inference: bool = eqx.field(static=True)
|
|
107
|
+
|
|
108
|
+
def __init__(
|
|
109
|
+
self,
|
|
110
|
+
p: float = 0.5,
|
|
111
|
+
inference: bool = False,
|
|
112
|
+
):
|
|
113
|
+
"""**Arguments:**
|
|
114
|
+
|
|
115
|
+
- `p`: The fraction of entries to set to zero. (On average.)
|
|
116
|
+
- `inference`: Whether to actually apply dropout at all. If `True` then dropout
|
|
117
|
+
is *not* applied. If `False` then dropout is applied. This may be toggled
|
|
118
|
+
with overridden during [`DropPath.__call__`][].
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
self.p = p
|
|
122
|
+
self.inference = inference
|
|
123
|
+
|
|
124
|
+
def __call__(
|
|
125
|
+
self,
|
|
126
|
+
x: Array,
|
|
127
|
+
*,
|
|
128
|
+
key: Optional[PRNGKeyArray] = None,
|
|
129
|
+
inference: Optional[bool] = None,
|
|
130
|
+
) -> Array:
|
|
131
|
+
"""**Arguments:**
|
|
132
|
+
|
|
133
|
+
- `x`: An any-dimensional JAX array to dropout.
|
|
134
|
+
- `key`: A `jax.random.PRNGKey` used to provide randomness for calculating
|
|
135
|
+
which elements to dropout. (Keyword only argument.)
|
|
136
|
+
- `inference`: As per [`DropPath.__init__`][]. If `True` or
|
|
137
|
+
`False` then it will take priority over `self.inference`. If `None`
|
|
138
|
+
then the value from `self.inference` will be used.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
if inference is None:
|
|
142
|
+
inference = self.inference
|
|
143
|
+
if isinstance(self.p, (int, float)) and self.p == 0:
|
|
144
|
+
inference = True
|
|
145
|
+
if inference:
|
|
146
|
+
return x
|
|
147
|
+
elif key is None:
|
|
148
|
+
raise RuntimeError(
|
|
149
|
+
"DropPath requires a key when running in non-deterministic mode."
|
|
150
|
+
)
|
|
151
|
+
else:
|
|
152
|
+
q = 1 - lax.stop_gradient(self.p)
|
|
153
|
+
shape = (x.shape[0],) + (1,) * (x.ndim - 1)
|
|
154
|
+
mask = jrandom.bernoulli(key, q, shape)
|
|
155
|
+
return x * mask / q
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class DropPathAdd(eqx.Module, strict=True):
|
|
159
|
+
"""Applies drop path (stochastic depth), by adding the second input to the first.
|
|
160
|
+
|
|
161
|
+
Note that this layer behaves differently during training and inference. During
|
|
162
|
+
training then dropout is randomly applied; during inference this layer does nothing.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
# Let's make them static fields, just to avoid possible filtering issues
|
|
166
|
+
p: float = eqx.field(static=True)
|
|
167
|
+
inference: bool = eqx.field(static=True)
|
|
168
|
+
|
|
169
|
+
def __init__(
|
|
170
|
+
self,
|
|
171
|
+
p: float = 0.5,
|
|
172
|
+
inference: bool = False,
|
|
173
|
+
):
|
|
174
|
+
"""**Arguments:**
|
|
175
|
+
|
|
176
|
+
- `p`: The fraction of entries to set to zero. (On average.)
|
|
177
|
+
- `inference`: Whether to actually apply dropout at all. If `True` then dropout
|
|
178
|
+
is *not* applied. If `False` then dropout is applied. This may be toggled
|
|
179
|
+
with overridden during [`DropPath.__call__`][].
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
self.p = p
|
|
183
|
+
self.inference = inference
|
|
184
|
+
|
|
185
|
+
def __call__(
|
|
186
|
+
self,
|
|
187
|
+
x1: Array,
|
|
188
|
+
x2: Array,
|
|
189
|
+
*,
|
|
190
|
+
key: Optional[PRNGKeyArray] = None,
|
|
191
|
+
inference: Optional[bool] = None,
|
|
192
|
+
) -> Array:
|
|
193
|
+
"""**Arguments:**
|
|
194
|
+
|
|
195
|
+
- `x1`: An any-dimensional JAX array.
|
|
196
|
+
- `x2`: A x1-dimensional JAX array to stochastically add to x1.
|
|
197
|
+
- `key`: A `jax.random.PRNGKey` used to provide randomness for calculating
|
|
198
|
+
which elements to dropout. (Keyword only argument.)
|
|
199
|
+
- `inference`: As per [`DropPath.__init__`][]. If `True` or
|
|
200
|
+
`False` then it will take priority over `self.inference`. If `None`
|
|
201
|
+
then the value from `self.inference` will be used.
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
if inference is None:
|
|
205
|
+
inference = self.inference
|
|
206
|
+
if isinstance(self.p, (int, float)) and self.p == 0:
|
|
207
|
+
inference = True
|
|
208
|
+
if inference:
|
|
209
|
+
return x1 + x2
|
|
210
|
+
elif key is None:
|
|
211
|
+
raise RuntimeError(
|
|
212
|
+
"DropPathAdd requires a key when running in non-deterministic mode."
|
|
213
|
+
)
|
|
214
|
+
else:
|
|
215
|
+
q = 1 - lax.stop_gradient(self.p)
|
|
216
|
+
add = jrandom.bernouilli(key, q)
|
|
217
|
+
return jax.lax.cond(add, lambda x, y: x + y, lambda x, y: x, x1, x2)
|