Skip to content

← Position Encodings step 5 of 5

Hard End-to-End

Attention Block with RoPE

This block applies rotary position embeddings (RoPE) to projected queries and keys before computing multi-head attention. It then projects the joined head outputs, adds the original input and normalizes each token.

RoPE rotates adjacent feature pairs within each head. Applying position-dependent rotations to both queries and keys makes their dot products sensitive to relative position. The scores still depend on the contents of the queries and keys; position differences alone do not determine them.

Inputs and output

Implement transformer_block_with_rope(x, w_q, w_k, w_v, w_o, num_heads, freqs_cos, freqs_sin). Keep the supplied function name.

  • x has shape (N, T, d_model).
  • All four weight matrices have shape (d_model, d_model).
  • num_heads is positive and divides d_model.
  • d_head = d_model // num_heads is even.
  • freqs_cos and freqs_sin each have shape (T, d_head // 2).

All arrays are floating-point, and all dimensions are positive. Return shape (N, T, d_model). The tables already contain the cosine and sine coefficients for each position and pair. Use them directly, broadcasting across batches and heads. Do not calculate new frequencies or apply sine/cosine to the tables.

This is an unmasked attention block with a residual and post-layer normalization. It has no feed-forward layer, projection biases, dropout or learned normalization parameters. Each query can attend to every position in its own sequence.

Project and rotate

Compute Q = x @ w_q, K = x @ w_k and V = x @ w_v. Reshape each to (N, T, num_heads, d_head), then swap the sequence and head axes to obtain (N, num_heads, T, d_head).

For each query or key vector at position t, pair adjacent coordinates (2*k, 2*k+1). Using the original coordinates a and b, compute:

c = freqs_cos[t, k]
s = freqs_sin[t, k]
rotated_even = a * c - b * s
rotated_odd  = a * s + b * c

Interleave the rotated even and odd coordinates back into their original pair order. Rotate Q and K only. This block combines the unrotated V vectors; do not apply the same rotation to V.

For example, coefficients c = 0, s = 1 rotate [2, 3] into [-3, 2]. Coefficients c = 1, s = 0 leave the pair unchanged. With all identity coefficients, the block reduces to the corresponding unrotated attention block.

Attend, add and normalize

  1. Multiply rotated Q by rotated K with only K's last two axes swapped. Divide by sqrt(d_head) to get shape (N, num_heads, T, T).
  2. Apply a stable softmax over the final axis, which contains key positions. Multiply these weights by the unrotated V.
  3. Swap the head and sequence axes back, then reshape to (N, T, d_model). Keep the original head order when joining features.
  4. Compute z = joined @ w_o + x, then normalize over the last feature axis:
mean = mean(z, axis=-1, keepdims=True)
variance = mean((z - mean)**2, axis=-1, keepdims=True)
output = (z - mean) / sqrt(variance + 1e-5)

Use population variance, with divisor d_model, and add epsilon inside the square root. Normalization follows the residual addition and does not mix batch items or sequence positions.

The visualization shows how position can generate rotation angles. In this exercise that work is already done: the supplied tables are authoritative. Its four pairs are an illustration, not a required head width.

Loading visualization…