Memory-efficient fused linear cross-entropy loss for JAX. Computes cross_entropy(x @ w.T, labels) without materializing the full logits tensor.
Project description
jax-cce
Memory-efficient fused linear cross-entropy loss for JAX. Instead of the standard two-step approach that first materializes the full [N, V] logit matrix and then computes the loss, jax-cce fuses the linear projection and cross-entropy into a single pass that processes the vocabulary in small chunks via lax.scan. For large-vocabulary models (128K+ tokens), this reduces peak activation memory from O(N·V) to O(N·D + V·D), eliminating the multi-GB logit buffer that typically dominates memory usage during LLM training. The implementation is fully compatible with jax.jit, jax.grad, and jax.value_and_grad, supports both float32 and bfloat16, and includes optional features for causal sequence shifting, vocabulary reordering for better cache locality, and gradient filtering to skip negligible chunks in the backward pass.
Install
pip install jax-cce
Quick usage
import jax
import jax.numpy as jnp
from jax_cce import linear_cross_entropy
# Typical LLM shapes: N tokens, D hidden dim, V vocab size
N, D, V = 4096, 4096, 128_000
key = jax.random.key(0)
x = jax.random.normal(key, (N, D), dtype=jnp.bfloat16) # hidden states
w = jax.random.normal(key, (V, D), dtype=jnp.bfloat16) # lm_head weights
labels = jax.random.randint(key, (N,), 0, V) # target token ids
# Naive approach — allocates ~4 GB for bfloat16 logits at N=4096, V=128K
# logits = x @ w.T # [N, V] <-- this is what we avoid
# loss = cross_entropy(logits, labels)
# jax-cce — never allocates the [N, V] tensor
loss = linear_cross_entropy(x, w, labels, chunk_size=4096)
# Gradients work the same way
loss, (dx, dw) = jax.value_and_grad(
lambda x_, w_: linear_cross_entropy(x_, w_, labels),
argnums=(0, 1),
)(x, w)
# Next-token prediction (shift=1): x[t] predicts labels[t+1]
# Works on batched sequences [B, T, D] / [B, T] too
loss = linear_cross_entropy(x, w, labels, shift=1)
Memory savings at bfloat16 with V=128K vocab:
| N (tokens) | Naive logits | jax-cce (chunk=4096) |
|---|---|---|
| 1,024 | 256 MB | ~32 MB |
| 4,096 | 1 GB | ~32 MB |
| 16,384 | 4 GB | ~32 MB |
API
jax_cce.linear_cross_entropy(
x,
w,
labels,
*,
shift=0,
chunk_size=4096,
vocab_sort_indices=None,
filter_eps=None,
)
Parameters
- x
jax.Array— Input activations, shape[N, D]. For sequence models withshift > 0, accepts[..., T, D]. - w
jax.Array— Vocabulary weight matrix, shape[V, D]. - labels
jax.Array— Integer class labels in[0, V), shape[N]or[..., T]. - shift
int(default0) — When > 0, applies a causal sequence shift:x[..., :-shift, :]predictslabels[..., shift:]. Setshift=1for standard next-token prediction. - chunk_size
int(default4096) — Vocabulary entries per scan step. Larger values use more memory but fewer kernel launches. Must be a positive integer. - vocab_sort_indices
jax.Array | None(defaultNone) — Integer array of shape[V]. When provided, reorders weight rows asw[vocab_sort_indices]before chunking, grouping related tokens for better cache locality. Labels are remapped internally so the loss is identical. Gradients are correctly unsorded back to original row order via JAX's gather VJP. - filter_eps
float | None(defaultNone) — Gradient filtering threshold. Chunks whose per-sample max logit is more thanfilter_epsbelow the global max are zeroed out in the backward pass (their softmax contribution is negligible). The forward loss is always exact. Usefloat('inf')to enable the filter code path without actually filtering (useful for benchmarking).
Returns — Scalar mean cross-entropy loss in float32.
Reference
This package implements the algorithm from:
"Cut Your Losses in Large-Vocabulary Language Models" arxiv.org/abs/2411.09009
The implementation was contributed to JAX as part of JAX issue #35906 and reviewed by a JAX maintainer. See the upstream PR for additional context on the design and memory analysis.
The original reference implementation in Triton is available at github.com/apple/ml-cross-entropy.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jax_cce-0.1.0.tar.gz.
File metadata
- Download URL: jax_cce-0.1.0.tar.gz
- Upload date:
- Size: 14.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
02aa90045b6d4f24e3a94e9ba454c8c95fa3449bf97e80d644f06f1b337f2593
|
|
| MD5 |
0443305a34b9cb375fa8d97bba6f9d26
|
|
| BLAKE2b-256 |
1c23de1961f8e64c9769f584bfb9fb43437587bbfd84bb07cc6db80a6e787a1e
|
File details
Details for the file jax_cce-0.1.0-py3-none-any.whl.
File metadata
- Download URL: jax_cce-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9c79c2ff3393f48a1fbe732a9c151a9fb9afe1b0b231fc1d2ad6d555c2bd5199
|
|
| MD5 |
8b43efe56b3e0f57687a1ee69fab6b34
|
|
| BLAKE2b-256 |
d1fc441d75d839da2c8b811760b8a6aa7d161a88d4f35fb7a82108537ccf1622
|