Normalize every attention head of a batch of token vectors on its own, inside a function compiled with jax.jit.
The input x has shape (B, T, D), where B is the batch size, T is the number of tokens and D is the feature width. Let H be num_heads and Dh=D/H the head width. Head h of a token is the block of features from hDh to (h+1)Dh−1. Each head vector v with entries v1,…,vDh is normalized as
y=Dh1∑j=1Dhvj2+10−6vand the normalized heads are then merged back into vectors of width D in the original feature order.
Return a dict with the key heads, a float32 JAX array of shape (B, H, T, Dh) where entry [b, h, t] is the normalized head h of token t in sample b, and the key merged, a float32 JAX array of shape (B, T, D).
Input: x = [[[1,2,3,4],[0,0,3,-4]]], num_heads = 2
Output: {"heads":[[[[0.632455,1.264911],[0.0,0.0]],[[0.848528,1.131371],[0.848528,-1.131371]]]],"merged":[[[0.632455,1.264911,0.848528,1.131371],[0.0,0.0,0.848528,-1.131371]]]}
Explanation: Head 0 of the first token is [1,2], whose mean square is 2.5, so it becomes [0.632455,1.264911]. Head 0 of the second token is all zeros and stays zero because the 0.000001 inside the square root keeps the denominator positive.
Input: x = [[[0.5,-1,2,0,3,1]],[[1,1,1,-1,-1,-1]]], num_heads = 3
Output: {"heads":[[[[0.632455,-1.26491]],[[1.414213,0.0]],[[1.341641,0.447214]]],[[[1.0,1.0]],[[1.0,-1.0]],[[-1.0,-1.0]]]],"merged":[[[0.632455,-1.26491,1.414213,0.0,1.341641,0.447214]],[[1.0,1.0,1.0,-1.0,-1.0,-1.0]]]}
Input: x = [[[2,-2,1,0],[0.1,0.2,0.3,0.4],[0,0,0,0]]], num_heads = 1
Output: {"heads":[[[[1.333333,-1.333333,0.666667,0.0],[0.365146,0.730292,1.095438,1.460584],[0.0,0.0,0.0,0.0]]]],"merged":[[[1.333333,-1.333333,0.666667,0.0],[0.365146,0.730292,1.095438,1.460584],[0.0,0.0,0.0,0.0]]]}
jax.jit(f, static_argnames=("num_heads",)) passes num_heads as a plain Python int.
jnp.swapaxes(x, 1, 2) reorders axes, which a reshape alone never does.
jax.lax.rsqrt(v) is 1 / sqrt(v), and keepdims=True keeps a broadcastable axis.
Accepts: array
Accepts: integer
Normalize every attention head of a batch of token vectors on its own, inside a function compiled with jax.jit.
The input x has shape (B, T, D), where B is the batch size, T is the number of tokens and D is the feature width. Let H be num_heads and Dh=D/H the head width. Head h of a token is the block of features from hDh to (h+1)Dh−1. Each head vector v with entries v1,…,vDh is normalized as
y=Dh1∑j=1Dhvj2+10−6vand the normalized heads are then merged back into vectors of width D in the original feature order.
Return a dict with the key heads, a float32 JAX array of shape (B, H, T, Dh) where entry [b, h, t] is the normalized head h of token t in sample b, and the key merged, a float32 JAX array of shape (B, T, D).
Input: x = [[[1,2,3,4],[0,0,3,-4]]], num_heads = 2
Output: {"heads":[[[[0.632455,1.264911],[0.0,0.0]],[[0.848528,1.131371],[0.848528,-1.131371]]]],"merged":[[[0.632455,1.264911,0.848528,1.131371],[0.0,0.0,0.848528,-1.131371]]]}
Explanation: Head 0 of the first token is [1,2], whose mean square is 2.5, so it becomes [0.632455,1.264911]. Head 0 of the second token is all zeros and stays zero because the 0.000001 inside the square root keeps the denominator positive.
Input: x = [[[0.5,-1,2,0,3,1]],[[1,1,1,-1,-1,-1]]], num_heads = 3
Output: {"heads":[[[[0.632455,-1.26491]],[[1.414213,0.0]],[[1.341641,0.447214]]],[[[1.0,1.0]],[[1.0,-1.0]],[[-1.0,-1.0]]]],"merged":[[[0.632455,-1.26491,1.414213,0.0,1.341641,0.447214]],[[1.0,1.0,1.0,-1.0,-1.0,-1.0]]]}
Input: x = [[[2,-2,1,0],[0.1,0.2,0.3,0.4],[0,0,0,0]]], num_heads = 1
Output: {"heads":[[[[1.333333,-1.333333,0.666667,0.0],[0.365146,0.730292,1.095438,1.460584],[0.0,0.0,0.0,0.0]]]],"merged":[[[1.333333,-1.333333,0.666667,0.0],[0.365146,0.730292,1.095438,1.460584],[0.0,0.0,0.0,0.0]]]}
jax.jit(f, static_argnames=("num_heads",)) passes num_heads as a plain Python int.
jnp.swapaxes(x, 1, 2) reorders axes, which a reshape alone never does.
jax.lax.rsqrt(v) is 1 / sqrt(v), and keepdims=True keeps a broadcastable axis.
Accepts: array
Accepts: integer