Thanks to visit codestin.com
Credit goes to github.com

Skip to content

New Feature: Enabling Speculative Decoding with Batch Size > 1 (If draft and target model share tokenizer)#42655

Open
YanivDorGalron wants to merge 4 commits into
huggingface:mainfrom
YanivDorGalron:batch_spec_dec
Open

New Feature: Enabling Speculative Decoding with Batch Size > 1 (If draft and target model share tokenizer)#42655
YanivDorGalron wants to merge 4 commits into
huggingface:mainfrom
YanivDorGalron:batch_spec_dec

Conversation

@YanivDorGalron

@YanivDorGalron YanivDorGalron commented Dec 5, 2025

Copy link
Copy Markdown

What does this PR do?

Many discussions focused on speculative decoding with 'batch_size > 1' (#26875, #29769, #32165, #32189), but none were fully implemented. This PR does that.

Summary of Changes: Enable Batched Speculative Decoding

Until now, invoking generate() with an assistant_model raised a ValueError if batch_size > 1. This update modifies the codebase to support batched speculative decoding.

Key Features & Limitations

  • Batch Support: Enabled batch_size > 1 for standard speculative decoding.
  • Tokenizer Constraint: Batched execution is currently restricted to scenarios where the draft (assistant) and target (main) models share the same tokenizer.
    • Universal Assisted Generation (UAG)—where models use different tokenizers—remains restricted to batch_size = 1 and will still raise a ValueError if used with a batch.

Technical Implementation Details

Supporting batches requires dealing with "ragged tensors", where different sequences in a batch accept a different number of speculative tokens, leading to misalignment in tensor shapes and KV caches. The implementation addresses this via the following mechanisms:

1. Assistant Cache Alignment

  • When reusing the assistant model for the next draft generation, the previous KV cache must match the current state of the sequences.
  • align_cache function was implemented in candidate_generator.py. It identify which parts of the old cache match the new inputs and removing the irrelevant keys and values from the cache. This ensures the assistant's cache correctly reflects the accepted tokens.

2. Repadding Inputs and Main Model Cache

  • After verification, if Batch Item A accepts 2 tokens and Batch Item B accepts 4, the resulting input tensor becomes ragged.
  • The rejected tokens and the padding tokens are removed from each item and the entire batch is repadded. The same is happening fo the main model KV cache

3. Vectorized Verification & Sampling

  • Greedy Search: The acceptance logic (checking if candidate tokens match target output) now uses vectorized cumsum operations to identify the index of the first mismatch for every item in the batch simultaneously.
  • Sampling: _speculative_sampling has been updated to calculate acceptance probabilities ($p$ vs $q$) and handle token rejection/resampling for the entire batch in parallel.

4. Dynamic Heuristics (update_candidate_strategy)

  • The heuristic that adjusts the number of assistant tokens (num_assistant_tokens) now aggregates statistics across the batch (e.g., using the average number of matches) to determine if the draft model step size should increase or decrease.
  • For the dynamic confidence threshold (ROC curve calculation), matches and probabilities are collected per batch item, flattened, and then used to update the threshold.

Note on max_new_tokens

When using speculative decoding with batch_size > 1, the behavior regarding max_new_tokens requires nuance. Because the generation loop serves the entire batch, the process continues until one items satisfy the stopping criteria. However, due to the repadding and varying acceptance rates, other items in the batch may effectively stop generating before reaching the global max_new_tokens limit.

Batch Size = 3: Assistant vs Standard Decoding

WITHOUT assistant_model: 9.2215 seconds
WITH assistant_model: 7.4744 seconds

Sequence # Tokens w Assistant # Tokens w/o Assistant
1 50 50
2 41 50
3 40 50

I look forward to your response and comments to ensure this can be included in the next version.

- Add batch speculative decoding functionality for batch_size > 1
- Update candidate generator to handle batch processing
- Enhance generation utils with batch speculative decoding support
- Add cache utilities for batch speculative decoding
- Update tests for batch speculative decoding
@YanivDorGalron

Copy link
Copy Markdown
Author

Hi!
Before I spend more time resolving the remaining failing tests, I’d like to confirm whether adding batched speculative decoding is something the team is open to including in the library.
If you have any guidance or a preferred direction for how this should integrate with the existing generation code, I’d be happy to align with it.
@zucchini-nlp @gante

Thanks!

@zucchini-nlp

Copy link
Copy Markdown
Member

@YanivDorGalron hey! Thanks for the PR

It is a nice-to-have feature for sure. We have been delaying it since an implementation would be complicated unless we can use a ragged_tensor natively from torch. As you might have already noticed, there are several edge cases with assisted decoding and we don't want to increase maintaining costs even more

I will definitely take a closer look around this week

padding_mask = cache_input_ids[:, :-1] == pad_token_id
past_key_values.compress_and_repad_cache(padding_mask)
# 1. Filter out current padding and repad to minimum length.
next_input_ids_clean = [row[row != pad_token_id] for row in next_input_ids]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!

@zucchini-nlp zucchini-nlp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey again, sorry for the delay

I get the idea behind and I think it is in a good direction. I liked that we don't pad infinitely and the next input ids are re-padded back. Though the cache logic look more involved as we need to basically re-create a new padded cache each time. I personally prefer to not have more code in cache_utils. Since it is creating new cache from an old, we can separate it out to a helper fn

Given the complexities of the feature and since it wasn't being requested much recently, I suggest us to move it to hub via remote code. Here is more info on custom generate. We can add it in transformers-community org and communicate to users. If the feature gets traction and interest from community, we will consider adding it in core library.

That will help us to make sure that the feature is shipped fast bypassing the review cycle, we can get feedback from community fast. Also it helps to not put maintenance burden for a rarely-used feature. LMK if that sounds good to you and I can help with making a custom generate repo

@YanivDorGalron

Copy link
Copy Markdown
Author

Thanks for your response, that sounds great.

If you can create a repo under transformers-community and add me as a collaborator, I’m happy to take ownership of the implementation. In the meantime, I’ll start refactoring the cache logic into a helper and prepare a minimal structure so we can plug it in once the repo is ready.

Looking forward to it!

@zucchini-nlp

Copy link
Copy Markdown
Member

@YanivDorGalron sure, what is you HF handle?

@YanivDorGalron

Copy link
Copy Markdown
Author

My HF handle is yaniv-galron

@YanivDorGalron sure, what is you HF handle?

@zucchini-nlp

zucchini-nlp commented Dec 22, 2025

Copy link
Copy Markdown
Member

I will ask @ArthurZucker to add, just noticed I am not admin 😄

Most maintainer are now in winter vacations so feel free in the meantime experiment with a repo under your own profile/org.

@WindChimeRan

Copy link
Copy Markdown

Relevant paper using unpad-append-repad for batch speculative decoding: https://arxiv.org/pdf/2510.22876

The code is open sourced.

May I ask what's the current progress for this PR?

@YanivDorGalron

Copy link
Copy Markdown
Author

Relevant paper using unpad-append-repad for batch speculative decoding: https://arxiv.org/pdf/2510.22876

The code is open sourced.

May I ask what's the current progress for this PR?

Hey @WindChimeRan, we have decided to move this code to a custom generate which we will publish on the hub.
This is still not done and your help will be appreciated.
@zucchini-nlp @ArthurZucker can you create a repo under transformers-community and add me as a collaborator?

@WindChimeRan

Copy link
Copy Markdown

Relevant paper using unpad-append-repad for batch speculative decoding: https://arxiv.org/pdf/2510.22876
The code is open sourced.
May I ask what's the current progress for this PR?

Hey @WindChimeRan, we have decided to move this code to a custom generate which we will publish on the hub. This is still not done and your help will be appreciated. @zucchini-nlp @ArthurZucker can you create a repo under transformers-community and add me as a collaborator?

@YanivDorGalron Sure! I'm glad to help!

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.

4 participants