Skip to content

Commit 0aa321a

Browse files
committed
Fix Falcon ALiBi bias when using the KV cache
The ALiBi bias was built from the query window instead of the cache, so it was shorter than the attention scores whenever a cache was in use: - With more than one token per step it fails to broadcast, raising an error. - With a single token it broadcasts silently and evaluates to all zeros, because `cumsum(ones(1)) - 1 == 0`. Since `generate()` decodes one token at a time, `FalconCausalLM.generate()` has been running with ALiBi effectively disabled at every decode step. Build the bias over the cache length when a cache is present. Unused cache positions are already masked out by the causal attention mask, so an all-ones mask yields the correct absolute positions. Verified: cached decoding now matches a full forward pass (max abs diff 2.98e-07), and the no-cache path is bit-identical before and after, both with and without a padding mask.
1 parent d512b28 commit 0aa321a

2 files changed

Lines changed: 58 additions & 4 deletions

File tree

keras_hub/src/models/falcon/falcon_causal_lm_test.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,48 @@ def wrapper(*args, **kwargs):
148148
# We should immediately abort and output the prompt.
149149
self.assertEqual(prompt, output)
150150

151+
def test_cached_decoding_matches_full_forward(self):
152+
# The ALiBi bias must span the cache, not just the query window.
153+
# Building it from the query length breaks cached decoding two ways:
154+
# it fails to broadcast when more than one token is passed, and it
155+
# collapses to all zeros for a single token, silently disabling ALiBi.
156+
causal_lm = FalconCausalLM(**self.init_kwargs)
157+
token_ids = self.input_data["token_ids"]
158+
batch_size, seq_length = token_ids.shape
159+
num_layers = self.backbone.num_layers
160+
num_heads = self.backbone.num_attention_heads
161+
head_dim = self.backbone.hidden_dim // num_heads
162+
cache_shape = [
163+
batch_size,
164+
num_layers,
165+
2,
166+
seq_length,
167+
num_heads,
168+
head_dim,
169+
]
170+
dtype = causal_lm.compute_dtype
171+
172+
# One pass over the whole sequence, seeding an empty cache.
173+
full_logits, _, _ = causal_lm.call_with_cache(
174+
token_ids, ops.zeros(cache_shape, dtype=dtype), 0
175+
)
176+
177+
# Prefill half the sequence, then decode a token at a time.
178+
cache = ops.zeros(cache_shape, dtype=dtype)
179+
prefill = seq_length // 2
180+
logits, _, cache = causal_lm.call_with_cache(
181+
token_ids[:, :prefill], cache, 0
182+
)
183+
pieces = [logits]
184+
for i in range(prefill, seq_length):
185+
logits, _, cache = causal_lm.call_with_cache(
186+
token_ids[:, i : i + 1], cache, i
187+
)
188+
pieces.append(logits)
189+
stepwise_logits = ops.concatenate(pieces, axis=1)
190+
191+
self.assertAllClose(stepwise_logits, full_logits, atol=1e-5, rtol=1e-5)
192+
151193
def test_generate_compilation(self):
152194
causal_lm = FalconCausalLM(**self.init_kwargs)
153195
# Assert we do not recompile with successive calls.

keras_hub/src/models/falcon/falcon_transformer_decoder.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,22 @@ def call(
118118

119119
x = self.input_layernorm(inputs)
120120

121-
mask = decoder_padding_mask
122-
if mask is None:
123-
batch_size, seq_length = ops.shape(inputs)[:2]
124-
mask = ops.ones((batch_size, seq_length), dtype="int32")
121+
if attention_cache is not None:
122+
# Keys and values span the whole cache, so the ALiBi bias must
123+
# too. Building it from the query window instead leaves the bias
124+
# shorter than the attention scores: it fails to broadcast when
125+
# more than one token is passed, and collapses to all zeros
126+
# during single-token decoding, silently disabling ALiBi.
127+
# Unused cache positions are already masked out by the causal
128+
# attention mask, so an all-ones mask gives absolute positions.
129+
batch_size = ops.shape(inputs)[0]
130+
kv_length = ops.shape(attention_cache)[2]
131+
mask = ops.ones((batch_size, kv_length), dtype="int32")
132+
else:
133+
mask = decoder_padding_mask
134+
if mask is None:
135+
batch_size, seq_length = ops.shape(inputs)[:2]
136+
mask = ops.ones((batch_size, seq_length), dtype="int32")
125137
alibi = self._build_alibi_tensor(self.num_attention_heads, mask)
126138

127139
# Attention block.

0 commit comments

Comments
 (0)