Skip to content

Implement filtering press (#257) - #258

Open
fax4ever wants to merge 2 commits into
NVIDIA:mainfrom
fax4ever:filtering
Open

Implement filtering press (#257)#258
fax4ever wants to merge 2 commits into
NVIDIA:mainfrom
fax4ever:filtering

Conversation

@fax4ever

@fax4ever fax4ever commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

PR description

Description of your PR. Fixes #257

ruler_heatmaps ruler_score_vs_ratio

Checklist

Before submitting a PR, please make sure:

  • Tests are working (make test)

  • Code is formatted correctly (make style, on errors try fix with make format)

  • Copyright header is included

  • All commits are signed-off using git commit -s

  • (new press) mypress_press.py is in the presses directory

  • (new press) MyPress is in __init__.py

  • (new press) README.md is updated with a 1 liner about the new press in the Available presses section

  • (new press) New press is in the default_presses list in tests/default_presses.py

  • (new press) A docstring is provided that follows the same structure as the existing ones

Signed-off-by: Fabio Massimo Ercoli <fabiomassimo.ercoli@gmail.com>
@copy-pr-bot

copy-pr-bot Bot commented Aug 4, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

Signed-off-by: Fabio Massimo Ercoli <fabiomassimo.ercoli@gmail.com>
@alessiodevoto

Copy link
Copy Markdown
Collaborator

Hi @fax4ever ! Thanks for opening this PR 🙂

General comment

I think I need some clarification here to understand whether this is in scope for KVPress. KVPress is currently a research-friendly library for comparing KV cache compression methods. We have not tried to provide direct compatibility with inference engines such as vLLM. I think this can fit the repo if it is framed as an experimental proxy for the question:

What would the accuracy look like if this press were constrained to an append-only, vLLM-like cache update pattern?

But then I think we should be explicit about what this is not:

  • It is not real vLLM compatibility. That would require a larger refactor, since KVPress currently relies on Hugging Face hooks and cache mutations in place, and even these new presses still rewrite/shrink/update cache tensors in ways that are not directly compatible with vLLM.
  • It is not expected to compete fairly with ordinary prefill compression presses in accuracy, because it is deliberately constrained by append-only-style filtering.
  • It should not claim generic compatibility with all scorer presses unless each scorer has been checked. For example, KeyDiff was updated to understand valid_mask, but most other scorers were not.

What do you think of this framing ?

Evaluation

About the proposed evaluation on RULER. RULER is mainly a long-context/prefill compression benchmark. These presses are decoding-only and are no-ops during prefill. That does not seem like a meaningful comparison against standard KVPress methods that compress the context during prefill. For decoding, please consider using other datasets.

Also, if you have other results or tests that seem meaningful to you, please feel free to share here, so we can use them for future reference. Having broader evaluation of this press would also be helpful.

Code

I left some comments in the code, but something important: there are two new presses in this PR, uniform and non uniform. For the non uniform (so per head filtering), I have two observations:

  1. Is it actually possible to do per-head ops in vLLM's paged attention, or are all KV pairs for one token stored in the same block ? If that is the case, having a per-head eviction mechanism would not be (ever) compatible with vLLM. I am not familiar enough with vLLM to answer this question, but we should make sure this non uniform actually makes sense.
  2. We already have a way to handle head masking, that is also an approximation, but it is used by a lot of presses. If we decide to move on with this PR, it would be nice to use that one and avoid the new tensor class altogether.

Comment thread kvpress/__init__.py
from kvpress.presses.dms_press import DMSPress
from kvpress.presses.duo_attention_press import DuoAttentionPress
from kvpress.presses.expected_attention_press import ExpectedAttentionPress
from kvpress.presses.filtering_press import FilteringPress

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.

nit: maybe we can keep the order nicer here and move this one after expected attention (alphab order)

this press decides for each new decode token whether to keep it in the cache.
Only the newest token can be removed — existing cache entries are never modified.

This makes the press compatible with append-only cache architectures (e.g. vLLM's

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.

DecodingPress.forward_hook still does cache_layer.keys = keys . Please reword to something like: "The decision model is compatible with append-only caches - each step's decision depends only on the newest token - but this reference implementation still writes back a full tensor. Integrating with a real paged-cache backend (e.g. vLLM) is out of scope for this PR."

paged KV cache). During prefill, this press is a no-op — filtering only applies
to the decode phase, where tokens arrive one at a time and the cache is append-only.

The decision is made per head: each head independently scores all tokens

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.

Are we sure this one is correct and could actually become compatible with vLLM ? I mean, does vLLM allow per-head block allocation or do all heads for a token live in one block. ? Just want to get some context about how vLLM works here :)


kt.accept_last(~rejected)
vt.accept_last(~rejected)
if self.fill_padding:

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.

kvpress already has infrastructure for head-wise masking without probability leakage: kvpress/attention_patch.py patches every attention function to substitute a fake key at positions listed in module.masked_key_indices, for example AdaKVPress uses this pattern.

Zeroing padded rows still lets attention assign non-zero softmax mass to them, so fill_padding=True silently degrades quality and fill_padding=False is worse.

attentions: torch.Tensor,
kwargs: dict,
) -> tuple[torch.Tensor, torch.Tensor]:
total_tokens_seen = int(kwargs["position_ids"].max().item()) + 1

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.

FilteringPress uses total_tokens_seen but UniformFilteringPress uses k_len for the same purpose. Should we align them, or document why they intentionally differ ?

kwargs,
) -> torch.Tensor:
anchor = F.normalize(keys, p=2, dim=-1).mean(dim=2, keepdim=True)
normalized = F.normalize(keys, p=2, dim=-1)

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.

What about other scorer presses ? They will silently produce biased scores under FilteringPress ?

@@ -0,0 +1,111 @@
# SPDX-FileCopyrightText: Copyright (c) 1993-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.

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.

This non uniform FilteringPress could probably be implemented through the existing masked_key_indices mechanism instead of introducing PaddedTensor and per-head ragged cache lengths ? KVPress already has an attention patch that supports head-wise masking through module.masked_key_indices, and several presses use that path (leaving a comment later here)

@@ -0,0 +1,75 @@
# SPDX-FileCopyrightText: Copyright (c) 1993-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.

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.

If we use masked_key_indices as suggested in the other comment we can cleanup this file and make this PR slimmer

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.

Decoding compression compatible with append-only KV cache architectures

2 participants