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/layers/ffn.py
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
from typing import Callable
|
|
2
|
+
|
|
3
|
+
import equinox as eqx
|
|
4
|
+
import jax
|
|
5
|
+
import jax.numpy as jnp
|
|
6
|
+
import jax.random as jr
|
|
7
|
+
from jaxtyping import Array, Float, PRNGKeyArray
|
|
8
|
+
|
|
9
|
+
from equimo.layers.dropout import Dropout
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Mlp(eqx.Module):
|
|
13
|
+
"""Multi-layer Perceptron (MLP) module with dropout.
|
|
14
|
+
|
|
15
|
+
A standard MLP implementation with two fully connected layers, activation function,
|
|
16
|
+
and dropout for regularization. The architecture follows:
|
|
17
|
+
input -> fc1 -> activation -> dropout1 -> fc2 -> dropout2 -> output
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
fc1: First linear layer
|
|
21
|
+
fc2: Second linear layer
|
|
22
|
+
drop1: Dropout after first layer
|
|
23
|
+
drop2: Dropout after second layer
|
|
24
|
+
act_layer: Activation function
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
fc1: eqx.nn.Linear
|
|
28
|
+
fc2: eqx.nn.Linear
|
|
29
|
+
drop1: Dropout
|
|
30
|
+
drop2: Dropout
|
|
31
|
+
act_layer: Callable
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
in_features: int,
|
|
36
|
+
*,
|
|
37
|
+
key: PRNGKeyArray,
|
|
38
|
+
out_features: int | None = None,
|
|
39
|
+
hidden_features: int | None = None,
|
|
40
|
+
act_layer: Callable = jax.nn.gelu,
|
|
41
|
+
dropout_rate: float = 0.0,
|
|
42
|
+
bias: bool = True,
|
|
43
|
+
**kwargs,
|
|
44
|
+
):
|
|
45
|
+
"""Initialize the MLP.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
in_features: Number of input features
|
|
49
|
+
key: PRNG key for initialization
|
|
50
|
+
out_features: Number of output features (default: same as in_features)
|
|
51
|
+
hidden_features: Number of hidden features (default: same as in_features)
|
|
52
|
+
act_layer: Activation function (default: gelu)
|
|
53
|
+
dropout_rate: Dropout probability (default: 0.0)
|
|
54
|
+
bias: Whether to include bias in linear layers (default: True)
|
|
55
|
+
**kwargs: Additional arguments
|
|
56
|
+
"""
|
|
57
|
+
key_fc1, key_fc2 = jr.split(key, 2)
|
|
58
|
+
|
|
59
|
+
self.act_layer = act_layer
|
|
60
|
+
|
|
61
|
+
hidden_features = hidden_features or in_features
|
|
62
|
+
out_features = out_features or in_features
|
|
63
|
+
|
|
64
|
+
self.fc1 = eqx.nn.Linear(
|
|
65
|
+
in_features, hidden_features, use_bias=bias, key=key_fc1
|
|
66
|
+
)
|
|
67
|
+
self.fc2 = eqx.nn.Linear(
|
|
68
|
+
hidden_features, out_features, use_bias=bias, key=key_fc2
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
self.drop1 = Dropout(dropout_rate)
|
|
72
|
+
self.drop2 = Dropout(dropout_rate)
|
|
73
|
+
|
|
74
|
+
def __call__(
|
|
75
|
+
self,
|
|
76
|
+
x: Float[Array, "seqlen dim"],
|
|
77
|
+
enable_dropout: bool,
|
|
78
|
+
key: PRNGKeyArray,
|
|
79
|
+
) -> Float[Array, "seqlen dim"]:
|
|
80
|
+
key_dr1, key_dr2 = jr.split(key, 2)
|
|
81
|
+
|
|
82
|
+
x = self.drop1(
|
|
83
|
+
self.act_layer(jax.vmap(self.fc1)(x)),
|
|
84
|
+
inference=not enable_dropout,
|
|
85
|
+
key=key_dr1,
|
|
86
|
+
)
|
|
87
|
+
x = self.drop2(
|
|
88
|
+
jax.vmap(self.fc2)(x),
|
|
89
|
+
inference=not enable_dropout,
|
|
90
|
+
key=key_dr2,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
return x
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class SwiGlu(eqx.Module):
|
|
97
|
+
"""SwiGLU activation module with dropout.
|
|
98
|
+
|
|
99
|
+
Implements the SwiGLU (Swish-Gated Linear Unit) activation function with dropout,
|
|
100
|
+
as described in "GLU Variants Improve Transformer" paper [1]. The architecture uses
|
|
101
|
+
a gating mechanism where the input is transformed by two parallel paths and
|
|
102
|
+
combined multiplicatively.
|
|
103
|
+
|
|
104
|
+
The computation flow is:
|
|
105
|
+
1. Joint projection to higher dimension (w12)
|
|
106
|
+
2. Split into two paths
|
|
107
|
+
3. Apply SiLU to first path and multiply with second path
|
|
108
|
+
4. Project back to original dimension (w3)
|
|
109
|
+
|
|
110
|
+
Attributes:
|
|
111
|
+
w12: Joint projection layer for both paths
|
|
112
|
+
w3: Final projection layer
|
|
113
|
+
drop1: Dropout after gating
|
|
114
|
+
drop2: Dropout after final projection
|
|
115
|
+
|
|
116
|
+
References:
|
|
117
|
+
[1]: https://arxiv.org/pdf/2002.05202
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
w12: eqx.nn.Linear
|
|
121
|
+
w3: eqx.nn.Linear
|
|
122
|
+
drop1: Dropout
|
|
123
|
+
drop2: Dropout
|
|
124
|
+
|
|
125
|
+
def __init__(
|
|
126
|
+
self,
|
|
127
|
+
in_features: int,
|
|
128
|
+
*,
|
|
129
|
+
key: PRNGKeyArray,
|
|
130
|
+
out_features: int | None = None,
|
|
131
|
+
hidden_features: int | None = None,
|
|
132
|
+
dropout_rate: float = 0.0,
|
|
133
|
+
bias: bool = True,
|
|
134
|
+
**kwargs,
|
|
135
|
+
):
|
|
136
|
+
"""Initialize the SwiGLU module.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
in_features: Number of input features
|
|
140
|
+
key: PRNG key for initialization
|
|
141
|
+
out_features: Number of output features (default: same as in_features)
|
|
142
|
+
hidden_features: Size of hidden dimension (default: same as in_features)
|
|
143
|
+
dropout_rate: Dropout probability (default: 0.0)
|
|
144
|
+
bias: Whether to include bias in linear layers (default: True)
|
|
145
|
+
**kwargs: Additional arguments
|
|
146
|
+
"""
|
|
147
|
+
key_fc1, key_fc2 = jr.split(key, 2)
|
|
148
|
+
|
|
149
|
+
hidden_features = hidden_features or in_features
|
|
150
|
+
out_features = out_features or in_features
|
|
151
|
+
|
|
152
|
+
self.w12 = eqx.nn.Linear(
|
|
153
|
+
in_features, hidden_features, use_bias=bias, key=key_fc1
|
|
154
|
+
)
|
|
155
|
+
self.w3 = eqx.nn.Linear(
|
|
156
|
+
hidden_features // 2, out_features, use_bias=bias, key=key_fc2
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
self.drop1 = Dropout(dropout_rate)
|
|
160
|
+
self.drop2 = Dropout(dropout_rate)
|
|
161
|
+
|
|
162
|
+
def __call__(
|
|
163
|
+
self,
|
|
164
|
+
x: Float[Array, "seqlen dim"],
|
|
165
|
+
enable_dropout: bool,
|
|
166
|
+
key: PRNGKeyArray,
|
|
167
|
+
) -> Float[Array, "seqlen dim"]:
|
|
168
|
+
key_dr1, key_dr2 = jr.split(key, 2)
|
|
169
|
+
|
|
170
|
+
x12 = jax.vmap(self.w12)(x)
|
|
171
|
+
x1, x2 = jnp.split(x12, 2, axis=-1)
|
|
172
|
+
x = self.drop1(
|
|
173
|
+
jax.nn.silu(x1) * x2,
|
|
174
|
+
inference=not enable_dropout,
|
|
175
|
+
key=key_dr1,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
x = self.drop2(
|
|
179
|
+
jax.vmap(self.w3)(x),
|
|
180
|
+
inference=not enable_dropout,
|
|
181
|
+
key=key_dr2,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
return x
|
equimo/layers/generic.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import equinox as eqx
|
|
2
|
+
from jaxtyping import Array, Float, PRNGKeyArray
|
|
3
|
+
|
|
4
|
+
from equimo.layers.dropout import DropPathAdd
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Residual(eqx.Module):
|
|
8
|
+
"""A wrapper module that adds a residual connection with optional drop path.
|
|
9
|
+
|
|
10
|
+
This module wraps any other module and adds a residual (skip) connection around it.
|
|
11
|
+
It also includes drop path regularization which stochastically drops the residual
|
|
12
|
+
path during training. The computation flow is:
|
|
13
|
+
input -> [main branch: module] + [residual branch: identity with drop path] -> output
|
|
14
|
+
|
|
15
|
+
Attributes:
|
|
16
|
+
module: The module to wrap with a residual connection
|
|
17
|
+
drop_path: DropPath module for residual connection regularization
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
module: eqx.Module
|
|
21
|
+
drop_path: DropPathAdd
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
module: eqx.Module,
|
|
26
|
+
drop_path: float = 0,
|
|
27
|
+
):
|
|
28
|
+
"""Initialize the Residual wrapper.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
module: The module to wrap with a residual connection
|
|
32
|
+
drop_path: Drop path rate (probability of dropping the residual connection)
|
|
33
|
+
(default: 0)
|
|
34
|
+
"""
|
|
35
|
+
self.module = module
|
|
36
|
+
self.drop_path = DropPathAdd(drop_path)
|
|
37
|
+
|
|
38
|
+
def __call__(
|
|
39
|
+
self,
|
|
40
|
+
x: Float[Array, "..."],
|
|
41
|
+
enable_dropout: bool,
|
|
42
|
+
key: PRNGKeyArray,
|
|
43
|
+
pass_args: bool = False,
|
|
44
|
+
) -> Float[Array, "..."]:
|
|
45
|
+
"""Forward pass of the residual block.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
x: Input tensor of any shape
|
|
49
|
+
enable_dropout: Whether to enable dropout during training
|
|
50
|
+
key: PRNG key for randomness
|
|
51
|
+
pass_args: Whether to pass enable_dropout and key to the wrapped module
|
|
52
|
+
(default: False)
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Output tensor with same shape as input, combining the module output
|
|
56
|
+
with the residual connection through drop path
|
|
57
|
+
"""
|
|
58
|
+
if pass_args:
|
|
59
|
+
x2 = self.module(x, enable_dropout=enable_dropout, key=key)
|
|
60
|
+
else:
|
|
61
|
+
x2 = self.module(x)
|
|
62
|
+
|
|
63
|
+
return self.drop_path(
|
|
64
|
+
x,
|
|
65
|
+
x2,
|
|
66
|
+
inference=not enable_dropout,
|
|
67
|
+
key=key,
|
|
68
|
+
)
|
equimo/layers/mamba.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from typing import List, Tuple
|
|
3
|
+
|
|
4
|
+
import equinox as eqx
|
|
5
|
+
import jax
|
|
6
|
+
import jax.numpy as jnp
|
|
7
|
+
import jax.random as jr
|
|
8
|
+
from einops import rearrange
|
|
9
|
+
from jaxtyping import Array, Float, PRNGKeyArray
|
|
10
|
+
|
|
11
|
+
from equimo.layers.norm import RMSNormGated
|
|
12
|
+
from equimo.ops.scan import non_causal_linear_attn
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Mamba2Mixer(eqx.Module):
|
|
16
|
+
"""Mamba2 Mixer.
|
|
17
|
+
|
|
18
|
+
This class implements the a Mamba2 Mixer using State Space Duality (SSD),
|
|
19
|
+
from Mamba2 [1]. Also supports implementation details from Visual State Space
|
|
20
|
+
Duality (VSSD) [2].
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
in_proj (eqx.nn.Linear): Input projection layer.
|
|
24
|
+
conv (eqx.nn.Conv): Convolutional layer for processing input.
|
|
25
|
+
dt_bias (eqx.nn.Param): Bias for delta time.
|
|
26
|
+
A_log (eqx.nn.Param): Logarithm of the state transition matrix A.
|
|
27
|
+
D (eqx.nn.Param): Direct feedthrough matrix D.
|
|
28
|
+
norm (eqx.nn.RMSNorm): Root Mean Square Layer Normalization.
|
|
29
|
+
out_proj (eqx.nn.Linear): Output projection layer.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
config (MambaConfig): Configuration object for the Mamba2VisionMixer.
|
|
33
|
+
rngs (nnx.Rngs): Random number generators for parameter initialization.
|
|
34
|
+
|
|
35
|
+
Notes:
|
|
36
|
+
This implementation is heavily based on wlln/scratch.
|
|
37
|
+
|
|
38
|
+
References:
|
|
39
|
+
[1] Transformers are SSMs: Generalized Models and Efficient Algorithms Through Structured State Space Duality
|
|
40
|
+
(https://arxiv.org/abs/2401.04054)
|
|
41
|
+
[2] VSSD: Vision Mamba with Non-Causal State Space Duality
|
|
42
|
+
(https://arxiv.org/abs/2407.18559)
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
d_inner: int = eqx.field(static=True)
|
|
46
|
+
n_heads: int = eqx.field(static=True)
|
|
47
|
+
head_dim: int = eqx.field(static=True)
|
|
48
|
+
n_groups: int = eqx.field(static=True)
|
|
49
|
+
indices_xBC: List[int] = eqx.field(static=True)
|
|
50
|
+
|
|
51
|
+
in_proj: eqx.nn.Linear
|
|
52
|
+
conv: eqx.nn.Conv
|
|
53
|
+
dt_bias: Float[Array, "n_heads"]
|
|
54
|
+
A_log: Float[Array, "n_heads"]
|
|
55
|
+
D: Float[Array, "n_heads"]
|
|
56
|
+
norm: eqx.nn.LayerNorm
|
|
57
|
+
out_proj: eqx.nn.Linear
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
d_model: int,
|
|
62
|
+
*,
|
|
63
|
+
key: PRNGKeyArray,
|
|
64
|
+
expand: int = 2,
|
|
65
|
+
n_groups: int = 1,
|
|
66
|
+
head_dim: int = 64,
|
|
67
|
+
d_state: int = 128,
|
|
68
|
+
d_conv: int = 4,
|
|
69
|
+
dt_min: float = 0.001,
|
|
70
|
+
dt_max: float = 0.1,
|
|
71
|
+
dt_init_floor: float = 1e-4,
|
|
72
|
+
A_init_range: Tuple[int, int] = (1, 16),
|
|
73
|
+
use_bias: bool = False,
|
|
74
|
+
conv_bias: bool = True,
|
|
75
|
+
**kwargs,
|
|
76
|
+
):
|
|
77
|
+
key_inproj, key_outproj, key_conv, key_randvals, key_a = jr.split(key, 5)
|
|
78
|
+
self.d_inner = int(d_model * expand)
|
|
79
|
+
if self.d_inner % head_dim != 0:
|
|
80
|
+
raise ValueError("`d_inner` must be a multiple of `head_dim`.")
|
|
81
|
+
self.n_heads = self.d_inner // head_dim
|
|
82
|
+
self.head_dim = head_dim
|
|
83
|
+
self.n_groups = n_groups
|
|
84
|
+
self.indices_xBC = [self.d_inner, self.d_inner + n_groups * d_state]
|
|
85
|
+
|
|
86
|
+
d_in_proj = 2 * self.d_inner + 2 * n_groups * d_state + self.n_heads
|
|
87
|
+
self.in_proj = eqx.nn.Linear(
|
|
88
|
+
d_model,
|
|
89
|
+
d_in_proj,
|
|
90
|
+
use_bias=use_bias,
|
|
91
|
+
key=key_inproj,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
conv_dim = self.d_inner + 2 * n_groups * d_state
|
|
95
|
+
self.conv = eqx.nn.Conv(
|
|
96
|
+
num_spatial_dims=1,
|
|
97
|
+
in_channels=conv_dim,
|
|
98
|
+
out_channels=conv_dim,
|
|
99
|
+
kernel_size=d_conv,
|
|
100
|
+
groups=conv_dim,
|
|
101
|
+
padding="SAME",
|
|
102
|
+
use_bias=conv_bias,
|
|
103
|
+
key=key_conv,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
rand_vals = jr.uniform(key_randvals, (self.n_heads,))
|
|
107
|
+
dt = jnp.exp(
|
|
108
|
+
rand_vals * (math.log(dt_max) - math.log(dt_min)) + math.log(dt_min)
|
|
109
|
+
)
|
|
110
|
+
dt = jnp.clip(dt, a_min=dt_init_floor)
|
|
111
|
+
self.dt_bias = dt + jnp.log(-jnp.expm1(-dt))
|
|
112
|
+
|
|
113
|
+
A_min, A_max = A_init_range
|
|
114
|
+
A = jr.uniform(key_a, (self.n_heads,), minval=A_min, maxval=A_max)
|
|
115
|
+
self.A_log = jnp.log(A)
|
|
116
|
+
|
|
117
|
+
self.D = jnp.ones(self.n_heads)
|
|
118
|
+
|
|
119
|
+
self.norm = eqx.nn.LayerNorm(self.d_inner)
|
|
120
|
+
self.out_proj = eqx.nn.Linear(
|
|
121
|
+
self.d_inner, d_model, use_bias=use_bias, key=key_outproj
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def __call__(
|
|
125
|
+
self,
|
|
126
|
+
x: Float[Array, "seqlen dim"],
|
|
127
|
+
enable_dropout: bool,
|
|
128
|
+
key: PRNGKeyArray,
|
|
129
|
+
) -> Float[Array, "seqlen dim"]:
|
|
130
|
+
A = -jnp.exp(self.A_log)
|
|
131
|
+
zxbcdt = jax.vmap(self.in_proj)(x)
|
|
132
|
+
|
|
133
|
+
z, xbc, dt = jnp.split(
|
|
134
|
+
zxbcdt,
|
|
135
|
+
[self.d_inner, zxbcdt.shape[-1] - self.n_heads],
|
|
136
|
+
axis=-1,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
dt = jax.nn.softplus(dt + self.dt_bias)
|
|
140
|
+
|
|
141
|
+
# Pad or truncate the xbc tensor to match the conv kernel size
|
|
142
|
+
# xbc_rearranged = rearrange(xbc, "b l d -> b d l")
|
|
143
|
+
# conv_state = pad_or_truncate_to_length(xbc_rearranged, self.config.d_conv)
|
|
144
|
+
|
|
145
|
+
# apply 1d convolution and silu activation
|
|
146
|
+
xbc_conv = rearrange(self.conv(rearrange(xbc, "s d -> d s")), "d s -> s d")
|
|
147
|
+
xbc_silu = jax.nn.silu(xbc_conv[: x.shape[0], :])
|
|
148
|
+
|
|
149
|
+
# split the conv state into the conv kernel and the conv state
|
|
150
|
+
x, B, C = jnp.split(xbc_silu, self.indices_xBC, axis=-1)
|
|
151
|
+
|
|
152
|
+
x = rearrange(x, "l (h p) -> l h p", p=self.head_dim)
|
|
153
|
+
|
|
154
|
+
y = non_causal_linear_attn(
|
|
155
|
+
x, dt=dt, A=A, B=B, C=C, D=self.D, n_groups=self.n_groups
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
y = rearrange(y, "l h p -> l (h p)")
|
|
159
|
+
|
|
160
|
+
# apply the output projection
|
|
161
|
+
if isinstance(self.norm, RMSNormGated):
|
|
162
|
+
y = self.norm(y, jax.nn.silu(z))
|
|
163
|
+
else:
|
|
164
|
+
# Should be LayerNorm
|
|
165
|
+
y = jax.vmap(self.norm)(y) * z
|
|
166
|
+
|
|
167
|
+
y = jax.vmap(self.out_proj)(y)
|
|
168
|
+
|
|
169
|
+
return y
|
equimo/layers/norm.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import equinox as eqx
|
|
4
|
+
import jax.numpy as jnp
|
|
5
|
+
from jax import lax
|
|
6
|
+
from jaxtyping import Array, Float
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RMSNormGated(eqx.Module):
|
|
10
|
+
"""Root Mean Square (RMS) Normalization with optional gating.
|
|
11
|
+
|
|
12
|
+
Implements RMS normalization with learnable scale parameters and optional
|
|
13
|
+
gating mechanism. RMS norm is similar to Layer Norm but only normalizes by
|
|
14
|
+
the root mean square, without centering the mean.
|
|
15
|
+
|
|
16
|
+
Attributes:
|
|
17
|
+
w: Learnable scale parameter vector of size dim
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
w: Float[Array, "dim"]
|
|
21
|
+
|
|
22
|
+
def __init__(self, d: int):
|
|
23
|
+
"""Initialize RMSNormGated.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
d: Dimension of the input features
|
|
27
|
+
"""
|
|
28
|
+
self.w = jnp.ones(d)
|
|
29
|
+
|
|
30
|
+
def __call__(
|
|
31
|
+
self,
|
|
32
|
+
x: Float[Array, "dim"],
|
|
33
|
+
z: Optional[Float[Array, "dim"]] = None,
|
|
34
|
+
) -> Float[Array, "dim"]:
|
|
35
|
+
"""Apply RMS normalization with optional gating.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
x: Input tensor of shape (dim,)
|
|
39
|
+
z: Optional gating tensor of shape (dim,)
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Normalized tensor of same shape as input
|
|
43
|
+
"""
|
|
44
|
+
if z is not None:
|
|
45
|
+
x *= z
|
|
46
|
+
|
|
47
|
+
y = x.astype(jnp.float32)
|
|
48
|
+
norm = y * lax.rsqrt(jnp.mean(y * y, -1, keepdims=True) + 1e-5)
|
|
49
|
+
|
|
50
|
+
return self.w * norm.astype(x.dtype)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class LayerScale(eqx.Module):
|
|
54
|
+
"""Layer scaling module for stabilizing deep networks.
|
|
55
|
+
|
|
56
|
+
Implements learnable scaling factors for each feature dimension, initialized
|
|
57
|
+
to a small value. This helps stabilize training in deep networks by initially
|
|
58
|
+
dampening the contribution of each layer.
|
|
59
|
+
|
|
60
|
+
Attributes:
|
|
61
|
+
init_values: Initial scale value (static)
|
|
62
|
+
gamma: Learnable scale parameters of size dim
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
init_values: float = eqx.field(static=True)
|
|
66
|
+
gamma: Optional[Float[Array, "dim"]]
|
|
67
|
+
|
|
68
|
+
def __init__(self, dim: int, init_values: float):
|
|
69
|
+
"""Initialize LayerScale.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
dim: Dimension of the input features
|
|
73
|
+
init_values: Initial value for all scaling factors
|
|
74
|
+
"""
|
|
75
|
+
self.init_values = init_values
|
|
76
|
+
self.gamma = jnp.repeat(self.init_values, dim)
|
|
77
|
+
|
|
78
|
+
def __call__(self, x: Float[Array, "dim"]):
|
|
79
|
+
"""Apply layer scaling to input tensor.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
x: Input tensor of shape (dim,)
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Scaled tensor of same shape as input
|
|
86
|
+
"""
|
|
87
|
+
return x * self.gamma
|