Skip to content

Sink cache: crop if exceeds max cache len#32315

Closed
zucchini-nlp wants to merge 3 commits into
huggingface:mainfrom
zucchini-nlp:sink_cache-crop_length
Closed

Sink cache: crop if exceeds max cache len#32315
zucchini-nlp wants to merge 3 commits into
huggingface:mainfrom
zucchini-nlp:sink_cache-crop_length

Conversation

@zucchini-nlp

Copy link
Copy Markdown
Member

What does this PR do?

Fixes #32233. Previous PR completely broke SinkCache and now it is not working as supposed. This PR reverts the change regarding max_cache_length, because for sink cache we need to get the latest max length masks and have position ida not exceeding the max length

I am aware that SinkCache is buggy and doesn't work for some edge cases and for multi-turn dialogues. But I think we can try to keep the previous working version for one-turn generation, until we figure out how to make SinkCache happy

@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@ArthurZucker ArthurZucker left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could we add a SinkCache test to make sure this does not get broken again?
Also, SinkCache is kind of like the sliding window cache, wondering why we don;t use this:

        # SlidingWindowCache
        if using_sliding_window_cache:
            target_length = max(sequence_length, self.config.sliding_window)

which we have in mistral!

@zucchini-nlp

Copy link
Copy Markdown
Member Author

Yes, we could but we would have to also handle cropping attention masks within update_causal_mask. Otherwise we could get target_length=window_length=50 but the attention is already way beyond that. I guess this wasn't caught by sliding-window-cache users because it usually has a very high max length

Oke, I will port cropping into update_causal_mask in this PR

@ArthurZucker

Copy link
Copy Markdown
Collaborator

But the sequence length is given and we create the attention mask based off sequence_length, target_length.

            causal_mask = torch.full(
                (sequence_length, target_length), fill_value=min_dtype, dtype=dtype, device=device
            )
            exclude_mask = torch.arange(target_length, device=device) > cache_position.reshape(-1, 1)
            if self.config.sliding_window is not None:
                if not using_sliding_window_cache or sequence_length > self.config.sliding_window:
                    exclude_mask.bitwise_or_(
                        torch.arange(target_length, device=device)
                        <= (cache_position.reshape(-1, 1) - self.config.sliding_window)
                    )

@ArthurZucker

Copy link
Copy Markdown
Collaborator

now, if slicing the mask is the only thing we need to use potentially siding window attention (or SinkCache) on models, LGTM!

@ArthurZucker

Copy link
Copy Markdown
Collaborator

(It means we can refactor the mistral piece of code as well)

@zucchini-nlp

Copy link
Copy Markdown
Member Author

Yes, cropping right now is the only way to make SinkCache happy. I just realized moving the crop to update_causal_mask will cause errors in SinkCache case because we would also have to re-inii position ids. Sink Cache position ids should not go beyond max-cache-length, though I am not sure about sliding window.

I propose to leave it in prepare_for_generation as is. Otherwise we have to do the same checks in prepare_for_generation to crop position_ids which will not make code look any easier. Added a test and made some changes

@gante

gante commented Aug 2, 2024

Copy link
Copy Markdown
Contributor

Could we add a SinkCache test to make sure this does not get broken again?

We have a couple slow sink cache tests failing 🙃 I'd argue that we have so many failing slow tests that we stopped paying attention to new failures (myself included) :P

@gante gante left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the current changes will add more fuel to the fire, rather than untangle the use of the different Cache classes. Added a comment below explaining my vision for this problem, for discussion 🤗

Comment on lines +691 to +694
logger.warning_once(
"`get_max_length` for SinkCache is deprecated and will return 0 after v4.47. ",
"Use past_key_values.window_length to get the maximum window length of the cache.",
)

@gante gante Aug 2, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I dislike this change -- self.window_length is the maximum length of the cache, returning 0 (or None) would not be correct. I realize the use of the method is currently inconsistent throughout our cache classes.

We should add two new attributes to the Cache classes, to fully specify a cache:

  • a boolean attribute to determine whether the cache grows or has a static shape (discussed in other PRs, to be used in compilation checks)
  • a boolean to determine whether it uses a sliding window (to be used e.g. in this PR)

With get_max_length and these two attributes we could be able to define all types of cache, and control the modeling code accordingly 🤗

@zucchini-nlp zucchini-nlp Aug 2, 2024

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Hmm, makes sense. I used the none following SlidingwindownCache which actually is almost same as SinkCache and it does not return any max length. So we need two attributes on each cache class: _is_sliding and _is_static right? What about HybridCache, will it be sliding or not?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Those two attribute names look good 👍 (btw, probably should be done in a separate PR, for better future reference)

HybridCache is a good corner case indeed 🤔 I think we can treat it as a NON sliding cache, as common model variables (e.g. attention_mask) need to be prepared according to the non-sliding cache layers.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Got you! I will add those two attributes in a second PR.

Just so we're on the same page, even after adding attributes we'll have to crop inputs to the max sliding window length. I guess the attr will make checks look nicer, like if cache._is_static: length = cache.get_max_length()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yes, on the same page as you: we need to crop things with sliding windows :)

@zucchini-nlp

Copy link
Copy Markdown
Member Author

close as sink cache will not be maintained anymore

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.

SinkCache with Qwen1.5 broken in 4.43.0+

4 participants