Skip to content

Make flash_attention effective for the Whisper encoder - #2083

Open
nilsreck wants to merge 1 commit into
OpenNMT:masterfrom
nilsreck:whisper-flash-encoder
Open

Make flash_attention effective for the Whisper encoder#2083
nilsreck wants to merge 1 commit into
OpenNMT:masterfrom
nilsreck:whisper-flash-encoder

Conversation

@nilsreck

Copy link
Copy Markdown

Problem

Setting flash_attention=true on a Whisper model has no effect today, for two independent reasons:

  1. WhisperEncoderLayer constructs its TransformerEncoderLayer without forwarding model.use_flash_attention(), so the parameter always falls back to its default (false) and the encoder never takes the flash path (src/layers/whisper.cc).
  2. FlashMultiHeadAttention constructs ops::FlashAttention without is_causal, which defaults to true (src/layers/flash_attention.cc). That is correct for decoder self-attention — currently the only in-tree user of the flash path — but wrong for any encoder, which must attend bidirectionally. This latent bug blocks enabling flash attention for every encoder, not just Whisper's.

Fix

  • Pass is_causal = _is_decoder when constructing ops::FlashAttention. Behavior-neutral for decoders; makes the flash path correct for encoder self-attention.
  • Forward model.use_flash_attention() from WhisperEncoderLayer to its TransformerEncoderLayer.

The scope is deliberately limited to Whisper: TransformerEncoder (the generic one) also reads model.use_flash_attention() without forwarding it to its layers, but I only validated the Whisper encoder numerically, so I left the generic path unchanged. With the is_causal fix in place, wiring it up becomes a safe follow-up.

This does not touch wheel packaging — it only affects source builds with -DWITH_FLASH_ATTN=ON.

Validation

Measured with a WITH_FLASH_ATTN=ON source build (based on a93c2bf) on one NVIDIA RTX PRO 6000 (Blackwell), Whisper large-v3-turbo, float16:

  • Encoder forward pass: 358 ms → 116 ms (3.1×)
  • Cosine similarity of encoder output vs. the non-flash encoder: 0.999667
  • Pairwise WER on our internal test set: −0.026

Happy to share the comparison script if useful.

The flash_attention model option currently has no effect for Whisper:
WhisperEncoderLayer constructs its TransformerEncoderLayer without
forwarding model.use_flash_attention(), so the parameter always falls
back to its default (false) and the encoder never uses the flash path.

Forwarding the flag exposes a second issue: FlashMultiHeadAttention
constructs ops::FlashAttention without is_causal, which defaults to
true. That is correct for decoder self-attention (the only current
user of the flash path) but wrong for any encoder, which must attend
bidirectionally. Pass is_causal=_is_decoder instead; this is
behavior-neutral for decoders and makes the flash path usable for
encoder self-attention.

Validated with a WITH_FLASH_ATTN=ON source build on one RTX PRO 6000
(Blackwell): Whisper encoder forward pass 358 ms -> 116 ms (3.1x),
cosine similarity 0.999667 against the non-flash encoder output,
pairwise WER -0.026 on our internal test set.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant