HF Attention Normalizers
Utilities for experimenting with alternative attention normalizers in HuggingFace Transformers.
Package name:
pip install hf-attention-normalizers
Python import name:
import hf_attention_normalizers
This project has two integration paths:
- Register HuggingFace-style attention backend names such as
softmax1_sdpa. - Patch supported model modules directly with a surgery policy. Currently the built-in policy covers Qwen3.
Supported Normalizers
| Normalizer | Function | Notes |
|---|---|---|
softmax1 |
exp(x_i) / (1 + sum_j exp(x_j)) |
Also called Softmax-N with n=1. |
sparsemax |
sparse probability transform | Can produce exact zeros in attention weights. |
entmax15 |
1.5-entmax transform | Between softmax and sparsemax; also sparse. |
vanilla / softmax |
PyTorch softmax | Baseline. |
Backend Support Matrix
| Backend name | softmax1 |
sparsemax |
entmax15 |
|---|---|---|---|
*_eager |
supported | supported | supported |
*_sdpa |
supported via custom SDPA-like path | supported via custom SDPA-like path | supported via custom SDPA-like path |
*_flash_attention_2 |
native FA2 extension if flash-attention-softmax-n is installed |
supported as a Hopper-Triton compatibility route | supported as a Hopper-Triton compatibility route |
*_triton |
not used | fused forward and backward | fused forward and backward |
*_flash_attention_3 |
custom Hopper online kernel | custom Hopper multi-block kernel | custom Hopper multi-block kernel |
*_flex_attention |
BlockMask compatibility path | BlockMask compatibility path | BlockMask compatibility path |
| `*_paged | *` | registered only; strict mode raises | registered only; strict mode raises |
flash-attention-softmax-n implements the native FA2 Softmax-N kernel. FA2 itself cannot express sparsemax/entmax15; their *_flash_attention_2 names are HuggingFace-compatible aliases that route to the custom Hopper Triton kernels, not to official FA2 CUDA.
Install
Minimum runtime:
pip install torch transformers
Editable local install:
pip install -e .
Optional Softmax-N FlashAttention backend:
pip install "hf-attention-normalizers[flash-softmax-n]"
Optional experimental sparsemax/entmax Triton kernels:
pip install "hf-attention-normalizers[triton]"
Install every optional backend:
pip install "hf-attention-normalizers[all]"
HuggingFace-Native Usage
Register backends, then use attn_implementation just like native HuggingFace backends:
from transformers import AutoModelForCausalLM
from hf_attention_normalizers import register_softmax1_attention_backends
register_softmax1_attention_backends(mode="strict")
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen3-...",
attn_implementation="softmax1_sdpa",
)
For sparsemax:
from hf_attention_normalizers import register_sparsemax_attention_backends
register_sparsemax_attention_backends(mode="strict")
model = AutoModelForCausalLM.from_pretrained(
model_id,
attn_implementation="sparsemax_sdpa",
)
Experimental sparsemax Triton backend:
from hf_attention_normalizers import register_sparsemax_attention_backends
register_sparsemax_attention_backends(mode="strict")
model = AutoModelForCausalLM.from_pretrained(
model_id,
attn_implementation="sparsemax_triton",
)
For entmax:
from hf_attention_normalizers import register_entmax15_attention_backends
register_entmax15_attention_backends(mode="strict")
model = AutoModelForCausalLM.from_pretrained(
model_id,
attn_implementation="entmax15_sdpa",
)
Experimental entmax15 Triton backend:
from hf_attention_normalizers import register_entmax15_attention_backends
register_entmax15_attention_backends(mode="strict")
model = AutoModelForCausalLM.from_pretrained(
model_id,
attn_implementation="entmax15_triton",
)
Softmax1 FlashAttention 2
If flash-attention-softmax-n is installed, you can use:
from hf_attention_normalizers import register_softmax1_attention_backends
register_softmax1_attention_backends(mode="strict")
model = AutoModelForCausalLM.from_pretrained(
model_id,
attn_implementation="softmax1_flash_attention_2",
)
This path calls flash_attention_softmax_n.flash_attention_n with softmax_n_param=1.
Strict vs Fallback Mode
Strict mode is safest:
register_softmax1_attention_backends(mode="strict")
Unsupported fused or paged backend names raise a clear error.
Fallback mode preserves math but may not preserve the requested backend performance:
register_softmax1_attention_backends(mode="fallback")
For unsupported fused or paged names, fallback mode routes to the configured fallback backend, defaulting to custom sdpa.
Already Loaded Models
You can switch an existing model:
from hf_attention_normalizers import set_softmax_attention_backend
set_softmax_attention_backend(
model,
base_backend="sdpa",
softmax_fn="softmax1",
)
For sparsemax:
set_softmax_attention_backend(model, base_backend="sdpa", softmax_fn="sparsemax")
For entmax:
set_softmax_attention_backend(model, base_backend="sdpa", softmax_fn="entmax15")
For the experimental Triton kernels:
set_softmax_attention_backend(model, base_backend="triton", softmax_fn="sparsemax")
set_softmax_attention_backend(model, base_backend="triton", softmax_fn="entmax15")
Differentiable Paged Attention
The paged reference path supports Softmax1, sparsemax, and entmax15 with gradients accumulated back into physical K/V cache pages:
from hf_attention_normalizers import paged_attention
output = paged_attention(
query,
key_cache,
value_cache,
block_table,
sequence_lengths,
block_size=32,
normalizer="sparsemax",
)
The CUDA Triton path supports Softmax1, sparsemax, and entmax15 with fused attention forward/backward, variable sequence lengths, GQA, and physical-page gradient accumulation:
from hf_attention_normalizers import paged_triton_attention
output = paged_triton_attention(
query,
key_cache,
value_cache,
block_table,
sequence_lengths,
block_size=32,
normalizer="entmax15", # or "softmax1" / "sparsemax"
)
The three normalizers also register HuggingFace continuous-batching backends
(softmax1_paged_attention, sparsemax_paged_attention, and
entmax15_paged_attention). They follow Transformers' paged_attention
interface, update its cache, then run one packed varlen Hopper grid over the
active requests. This interface is for the standard generation cache; use
paged_triton_attention with DifferentiablePagedCache when gradients to
physical cache pages are required during training.
The Triton implementation reads physical K/V pages directly through the
block_table; it does not materialize gathered K/V tensors or the quadratic
attention matrix. Backward atomically accumulates K/V gradients directly into
their physical cache pages.
For training batches whose block_table has no shared physical pages,
paged_triton_attention(..., max_key_length=..., assume_unique_pages=True)
uses a two-stage KV-tile reduction without atomics and supports BF16
torch.compile(fullgraph=True). The flag is an explicit correctness contract:
leave it at its default (False) whenever pages can be shared.
For incremental HuggingFace training, DifferentiablePagedCache uses
functional page writes so gradients remain connected across cache updates:
from hf_attention_normalizers import DifferentiablePagedCache
cache = DifferentiablePagedCache(
num_hidden_layers=model.config.num_hidden_layers,
block_table=block_table,
block_size=32,
num_key_value_heads=model.config.num_key_value_heads,
head_dim=model.config.head_dim,
dtype=torch.float16,
device=torch.device("cuda"),
)
outputs = model(
input_ids,
past_key_values=cache,
use_cache=True,
cache_position=cache_position,
)
Unlike HuggingFace's inference-oriented paged cache, writes are out-of-place
index_copy operations. This preserves gradients to both earlier cached K/V
states and newly written K/V states.
Packed Variable-Length Attention
Softmax1, sparsemax, and entmax15 support FlashAttention-style packed Q/K/V layouts with cumulative sequence lengths:
from hf_attention_normalizers import varlen_hopper_attention
output = varlen_hopper_attention(
query, # [total_q, query_heads, head_dim]
key, # [total_k, kv_heads, head_dim]
value,
cu_seqlens_q,
cu_seqlens_k,
normalizer="softmax1",
max_seqlen_q=max_q,
max_seqlen_k=max_k,
is_causal=True,
)
The packed path supports GQA, different Q/K lengths, per-request bottom-right causal alignment, and full backward. Each normalizer launches one combined Hopper grid across the packed batch; inactive programs/tiles are masked from the cumulative sequence-length arrays, without padded Q/K/V staging.
Qwen3 Surgery Path
For models that do not use HuggingFace AttentionInterface, use the surgery path:
from transformers import AutoModelForCausalLM
from hf_attention_normalizers import apply_softmax_attention
model = AutoModelForCausalLM.from_pretrained(model_id)
model = apply_softmax_attention(
model,
softmax_fn="softmax1",
attn_implementation="sdpa",
)
Current built-in surgery policies:
from hf_attention_normalizers import supported_attention_policies
print(supported_attention_policies())
# ("qwen3",)
Additional model families can be added by registering an AttentionReplacementPolicy.
The old Qwen_attention.py module is kept as a compatibility shim, but new code should import from hf_attention_normalizers.
Important Limitations
- Native PyTorch SDPA and native FlashAttention kernels do not expose a
softmax_fnargument. softmax1_flash_attention_3uses an online tiled Hopper kernel. It treats the Softmax1 denominator term as a virtual logit-0/value-0 sink, saves row max/denominator statistics, and recomputes probabilities during fused backward.sparsemax_flash_attention_3andentmax15_flash_attention_3use this project's Hopper-oriented multi-block Triton kernels. They stream K/V tiles, solve the global normalization threshold by tiled bisection, save one threshold per query row, and recompute tiles during fused backward. They are FA3-style custom-normalizer kernels, not wrappers around the official fixed-softmax FA3 CUDA kernel.- Native FlexAttention exposes score and mask modifiers but keeps softmax fixed. Pure causal, causal sliding-window, and standard causal + right-padding HuggingFace
BlockMaskobjects route to Hopper forward/backward without dense-mask expansion. Explicit full-block CSR masks (BlockMask.from_kv_blockswithnoop_mask) also avoid dense expansion and preserve backward through direct block gathers. The right-padding path currently dispatches one fused kernel per batch row because each row has a different valid K prefix. Left padding, sliding-window + padding, and arbitrary element-levelmask_modfunctions use the differentiable dense compatibility path. *_sdpain this project is a custom SDPA-like implementation that materializes attention weights so it can apply another normalizer.softmax1_flash_attention_2usesflash-attention-softmax-n, not HuggingFace's native FlashAttention 2 kernel.sparsemax_tritonandentmax15_tritonfuse QK, normalization, and PV in the forward pass without materializing the attention matrix.- Their fused backward kernels recompute probabilities row-by-row instead of storing the quadratic attention matrix, then directly accumulate Q/K/V gradients.
- The single-block experimental Triton kernels require CUDA tensors, no dropout, no external padding mask on the fast path, matching Q/K/V head dimensions, and
key_length <= max_block_nwhere the default is 4096. The Hopper FA3-style kernels support training dropout with a saved Philox-style seed and recompute the mask in backward without storing a dense attention mask. - If a mask/dropout/CPU path is encountered through the HuggingFace wrapper, the Triton wrapper falls back to the custom SDPA implementation to preserve math.
- The Hopper multi-block kernels remove the single-block 4096-key limit. Further work remains for native BlockMask traversal and deeper Hopper-specific scheduling/autotuning.
On Hopper (H100), the FA3-style kernels are covered in BF16 with full
forward/backward and torch.compile(fullgraph=True) regression tests for all
three normalizers. Varlen and direct physical-page Paged paths are also
validated in BF16. Validation on non-Hopper GPU architectures remains pending.
- Paged Softmax1, sparsemax, and entmax15 kernels read K/V directly through block tables and scatter K/V gradients directly into physical cache pages.
Metadata
Release files for hf-attention-normalizers 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| hf_attention_normalizers-0.1.0.tar.gz | 46.4 kB | Without provenance | Details for hf_attention_normalizers-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| hf_attention_normalizers-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for hf_attention_normalizers-0.1.0-py3-none-any.whl |
Total release size: 96.7 kB
Release files / hf_attention_normalizers-0.1.0.tar.gz
| Download URL | hf_attention_normalizers-0.1.0.tar.gz |
|---|---|
| Size | 46.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
648ae5db04e15ca40abd378f1edb712413260baab7202d5b091c99861845f68f
|
|
BLAKE2b-256 checksum How to use checksums |
b9a9376a3b1df41f5bdab6fabbf1e3ed1939a475523d29b27792c4a70b654cab
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.2
|
Release files / hf_attention_normalizers-0.1.0-py3-none-any.whl
| Download URL | hf_attention_normalizers-0.1.0-py3-none-any.whl |
|---|---|
| Size | 50.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7c78c64048149a9b09bec0bc137162d73e96f38b71204908941485a65513bc3f
|
|
BLAKE2b-256 checksum How to use checksums |
da6334966c1c1d8569391da350b28bb6809079db3c249842fadb43ede17ba712
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.2
|
Security for hf-attention-normalizers 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.