Rotary Positional Embeddings (RoPE) - Transformer Architecture
Implement Rotary Positional Embeddings (RoPE) in Python with NumPy. medium exercise with examples, hints, and instant feedback. No setup or signup required.
Rotary Positional Embeddings (RoPE)
What you'll build
Apply RoPE to queries and keys: rotate each pair of dimensions by an angle proportional to the token's position. LLaMA, Mistral, Qwen, GPT-NeoX and most modern LLMs use it.
Background
Sinusoidal encodings are added to the embeddings once, at the input. RoPE instead rotates q and k inside every attention layer. Rotating q at position m by m·θ and k at position n by n·θ makes their dot product depend only on the angle difference (m - n)·θ, so attention scores see relative positions, with no learned parameters and no change to the vectors' norms. Values V are not rotated.
Formula
For position pos (row index, starting at 0), d = d_model and pair index i = 0 .. d/2 - 1:
theta_i = 1 / 10000^(2i / d)
a = pos * theta_i
x_rot[2i] = x[2i] * cos(a) - x[2i+1] * sin(a)
x_rot[2i + 1] = x[2i] * sin(a) + x[2i+1] * cos(a)
This is the 2-D rotation [[cos a, -sin a], [sin a, cos a]] applied to the pair (x[2i], x[2i+1]). Apply it to every row of Q and of K.
Inputs
Q: float array(seq_len, d_model),d_modelevenK: float array(seq_len, d_model)
Expected Return Format
A tuple (Q_rot, K_rot), each with the shape of its input.
Edge cases and conventions
- Interleaved pairs
(0,1), (2,3), ...as in the RoFormer paper. Hugging Face LLaMA code pairs dimensioniwithi + d/2("rotate_half"); it is the same idea with permuted dimensions, but it gives different numbers, so use the interleaved layout here. - Position 0 is unchanged (angle 0). Don't modify the inputs in place.
Constraints
NumPy only, vectorised over positions and pairs (strided slicing x[:, 0::2], x[:, 1::2]).
Follow-up
- Prove
<R(m) q, R(n) k> = <q, R(n - m) k>. - Context extension (NTK scaling, YaRN) changes the base 10000. Which pairs are affected most, and why?
Examples
Input: apply_rope(np.array([[1.0, 0.0, 1.0, 0.0]] * 3), K)[0] Output: [[1.0, 0.0, 1.0, 0.0], [0.5403, 0.8415, 1.0, 0.01], [-0.4161, 0.9093, 0.9998, 0.02]]
theta = [1, 0.01]: pair 0 rotates by pos radians, pair 1 by pos / 100 radians.
Input: apply_rope(np.ones((2, 2)), np.ones((2, 2)))[0] Output: [[1.0, 1.0], [-0.3012, 1.3818]]
Row 1: (cos 1 - sin 1, sin 1 + cos 1); the norm stays sqrt(2).
Practice in Python
import numpy as np
def apply_rope(Q, K):
"""
Apply Rotary Positional Embeddings to Q and K (interleaved pairs).
Args:
Q: Queries (seq_len, d_model), d_model even
K: Keys (seq_len, d_model)
Returns:
(Q_rot, K_rot): pair (2i, 2i+1) of row pos rotated by pos * theta_i,
theta_i = 1 / 10000^(2i / d_model)
"""
# Your code here
pass
The interactive editor loads with Python and NumPy. Run your implementation against 7 tests, or use the progressive hints.