The problem
While generating text, a model keeps the keys and values of all previous tokens in memory, and that memory is what limits how much text it can handle at once. Grouped-query attention shrinks it: there are still query heads, but only key/value heads, and each key/value head is shared by a group of query heads. With this is ordinary multi-head attention; with it is multi-query attention, where all query heads share one key/value head.
Which query head uses which key/value head
Consecutive query heads form a group. Query head uses key/value head
where rounds down, and is the size of a group.
Example. query heads and key/value heads: groups of .
query head key/value head 0 0 0 1 0.5 0 2 1 1 3 1.5 1 So query heads 0 and 1 share key/value head 0, and query heads 2 and 3 share head 1. The memory holds 2 key/value heads instead of 4: half the size.
The simplest way to compute it: copy each key/value head times, so that there is one per query head, in the order of the table, and then run ordinary causal attention.
What to implement
gqa_attention(q, k, v), with causal masking:
qhas shape ;kandvhave shape ;- return the output, shape .
The attention and causal_mask functions are given.
Requirements
- Raise
ValueErrorif is not divisible by . - Query heads share key/value head 0, the next share head 1, and so on.
- No loops over heads.
Hint
Along the head axis, np.repeat(k, 2, axis=1) turns the heads [k0, k1] into [k0, k0, k1, k1]: the order of the table. np.tile would give [k0, k1, k0, k1], which pairs query head 1 with key/value head 1: the wrong grouping. (Real implementations avoid making the copies, but the result is the same.)