We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Flax NNX step 40 of 100
NNX RoPE Attention
Rotate queries and keys by position
Rotary position embeddings (RoPE) add position information by rotating pairs of features in Q and K before computing attention scores. The rotation angle depends on the token position and the feature pair. V is left unchanged in this exercise.
A rotation preserves the length of each pair. Rotating Q and K by different angles changes their dot product, which changes the attention weights and therefore the weighted average of values.
Pair features and choose frequencies
Let Dh = d_model // num_heads. It must be even. This exercise pairs
adjacent features: (0, 1), (2, 3), and so on. For pair index r:
frequency[r] = base ** (-2 * r / Dh)
angle[position, r] = position * frequency[r]
Angles are in radians. Positions are the row indices 0, ..., T - 1.
With Dh=4 and base=10000, the two frequencies are 1 and 0.01 radians
per position. Pair zero always has frequency one; changing base affects
only the later pairs. For base greater than one, later pairs rotate
more slowly. Base is a supplied positive number, not a learned parameter.
Some implementations pair the first half of a head with the second half. That convention can represent the same rotations after a matching feature permutation, but swapping conventions without also reordering the relevant projection features changes the model. Use adjacent pairs here.
A complete rotation example
Rotate (a, b) by angle t as (a*cos(t) - b*sin(t), a*sin(t) + b*cos(t)).
Stack the two results together before flattening, so they remain interleaved.
import jax.numpy as jnp
def rotate(x, cos, sin):
a, b = x[..., 0::2], x[..., 1::2]
return jnp.stack((a * cos - b * sin,
a * sin + b * cos), axis=-1).reshape(x.shape)
x = jnp.array([[1.0, 0.0, 0.0, 2.0],
[1.0, 0.0, 0.0, 2.0]])
frequencies = jnp.array([1.0, 0.01])
angles = jnp.arange(2)[:, None] * frequencies[None, :]
y = rotate(x, jnp.cos(angles), jnp.sin(angles))
assert jnp.allclose(y[0], x[0])
expected = jnp.array([jnp.cos(1.0), jnp.sin(1.0),
-2 * jnp.sin(0.01), 2 * jnp.cos(0.01)])
assert jnp.allclose(y[1], expected)
assert jnp.allclose(jnp.sum(y * y, axis=-1), jnp.sum(x * x, axis=-1))
Position zero leaves both pairs unchanged. At position one, the first pair rotates by one radian and the second by 0.01 radians. Neither pair changes length. The attention module applies this operation separately to the projected query and key arrays for every head.
Why the dot product uses relative position
Write R(t) for a 2-D rotation. For fixed content vectors q and k and frequency f, the rotated dot product satisfies:
(R(i*f) q) dot (R(j*f) k) = q dot (R((j-i)*f) k)
The rotations' position contribution depends on j - i. Shifting both
positions by the same offset leaves this dot product unchanged. The
result still depends on q and k; it is not a function of distance alone.
The property holds pair by pair, with each pair's own frequency.
RoPE is not a mask or a monotonic distance penalty. A more distant key can have a larger score. The formula can be evaluated beyond a training sequence length, but good performance at those lengths is not guaranteed. Changing frequency scales also changes scores and needs evaluation.
The diagram uses four feature pairs (head width eight), base 10000, and
initial vectors (1, 0). Move the position slider to compare their
angles. Actual Q/K pairs can start at different lengths and directions.
Loading visualization…
Problem
Write mha_rope(seed, x, num_heads, d_model, base) using a
RopeMHA(nnx.Module) and an adjacent-pair rotate helper.
- Convert seed and dimensions to Python integers. Set H to num_heads,
D to d_model, and Dh to D // H. Create one
nnx.Rngs(int(seed)). - Construct four
nnx.Linear(D, D, rngs=rngs)layers in this order:q_proj,k_proj,v_proj,out_proj. Keep default initializers and biases. Store base as an ordinary attribute, not annnx.Param. - Project x to Q, K and V. Reshape each to
(T, H, Dh)and transpose to(H, T, Dh). - Build the
(T, Dh // 2)angle, cosine and sine tables using the supplied base. Apply them to Q and K only. These tables broadcast over heads; a leading singleton axis can make that explicit. - Compute rotated
Q @ K.transpose(0, 2, 1) / sqrt(Dh). Apply softmax over key positions on the last axis, then multiply by V. - Transpose the result to
(T, H, Dh), reshape to(T, D), applyout_proj, and return a flat array of shape(T * D,).
Inputs use finite float32 x shaped (T, D) with positive dimensions.
H is positive, divides D, and gives an even Dh. Base is finite and
positive. Seed and dimensions arrive as floats representing integers;
keep them static under JIT. Base can remain an array scalar: do not
force it through Python float inside a traced call.
Each entrypoint call constructs a fresh model. A retained model's
parameters should not change during its forward call. Use no mask,
dropout or additional position embeddings; all positions may attend
to all positions. Scores still occupy (H, T, T) storage.
Understand the ideas behind this exercise. The Problem tab contains the exact implementation requirements.
Rotate queries and keys by position
Rotary position embeddings (RoPE) add position information by rotating pairs of features in Q and K before computing attention scores. The rotation angle depends on the token position and the feature pair. V is left unchanged in this exercise.
A rotation preserves the length of each pair. Rotating Q and K by different angles changes their dot product, which changes the attention weights and therefore the weighted average of values.
Pair features and choose frequencies
Let Dh = d_model // num_heads. It must be even. This exercise pairs
adjacent features: (0, 1), (2, 3), and so on. For pair index r:
frequency[r] = base ** (-2 * r / Dh)
angle[position, r] = position * frequency[r]
Angles are in radians. Positions are the row indices 0, ..., T - 1.
With Dh=4 and base=10000, the two frequencies are 1 and 0.01 radians
per position. Pair zero always has frequency one; changing base affects
only the later pairs. For base greater than one, later pairs rotate
more slowly. Base is a supplied positive number, not a learned parameter.
Some implementations pair the first half of a head with the second half. That convention can represent the same rotations after a matching feature permutation, but swapping conventions without also reordering the relevant projection features changes the model. Use adjacent pairs here.
A complete rotation example
Rotate (a, b) by angle t as (a*cos(t) - b*sin(t), a*sin(t) + b*cos(t)).
Stack the two results together before flattening, so they remain interleaved.
import jax.numpy as jnp
def rotate(x, cos, sin):
a, b = x[..., 0::2], x[..., 1::2]
return jnp.stack((a * cos - b * sin,
a * sin + b * cos), axis=-1).reshape(x.shape)
x = jnp.array([[1.0, 0.0, 0.0, 2.0],
[1.0, 0.0, 0.0, 2.0]])
frequencies = jnp.array([1.0, 0.01])
angles = jnp.arange(2)[:, None] * frequencies[None, :]
y = rotate(x, jnp.cos(angles), jnp.sin(angles))
assert jnp.allclose(y[0], x[0])
expected = jnp.array([jnp.cos(1.0), jnp.sin(1.0),
-2 * jnp.sin(0.01), 2 * jnp.cos(0.01)])
assert jnp.allclose(y[1], expected)
assert jnp.allclose(jnp.sum(y * y, axis=-1), jnp.sum(x * x, axis=-1))
Position zero leaves both pairs unchanged. At position one, the first pair rotates by one radian and the second by 0.01 radians. Neither pair changes length. The attention module applies this operation separately to the projected query and key arrays for every head.
Why the dot product uses relative position
Write R(t) for a 2-D rotation. For fixed content vectors q and k and frequency f, the rotated dot product satisfies:
(R(i*f) q) dot (R(j*f) k) = q dot (R((j-i)*f) k)
The rotations' position contribution depends on j - i. Shifting both
positions by the same offset leaves this dot product unchanged. The
result still depends on q and k; it is not a function of distance alone.
The property holds pair by pair, with each pair's own frequency.
RoPE is not a mask or a monotonic distance penalty. A more distant key can have a larger score. The formula can be evaluated beyond a training sequence length, but good performance at those lengths is not guaranteed. Changing frequency scales also changes scores and needs evaluation.
The diagram uses four feature pairs (head width eight), base 10000, and
initial vectors (1, 0). Move the position slider to compare their
angles. Actual Q/K pairs can start at different lengths and directions.
Loading visualization…
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
JAX reference solution
Sign in to track your attempts toward unlocking the reference solution.