Custom Benchmark Task
BenchmarkTask is a one-method plugin. Add a class, give it a name, implement run() — it’s immediately usable with any BenchmarkSuite.
Step 1: Implement the task
# dantinox/benchmarking/tasks/accuracy.py
from __future__ import annotations
from typing import Any
import jax
import jax.numpy as jnp
from dantinox.benchmarking.base import BenchmarkConfig, BenchmarkResult, BenchmarkTask
class TopKAccuracyTask(BenchmarkTask):
"""Measures top-K next-token accuracy on a held-out corpus.
Runs ``config.eval_batches`` batches of shape
``[eval_batch_size, eval_seq_len]`` through the model and computes
the fraction of positions where the ground-truth token appears in
the top-K predictions.
Args:
k: Number of candidates to consider. Default ``5``.
data_source: Path to a validation text file. If ``None``, random
token IDs are used (useful for smoke-testing).
"""
name = "top_k_accuracy"
def __init__(self, k: int = 5, data_source: str | None = None) -> None:
self.k = k
self.data_source = data_source
def run(
self,
paradigm: Any,
model: Any,
config: BenchmarkConfig,
rng: Any,
) -> BenchmarkResult:
"""Evaluate top-K accuracy over the evaluation corpus.
Args:
paradigm: Any :class:`~dantinox.paradigms.Paradigm` instance.
model: The NNX model to evaluate.
config: Suite-level benchmark configuration; ``eval_batches``,
``eval_seq_len``, and ``eval_batch_size`` are used.
rng: JAX random key.
Returns:
:class:`~dantinox.benchmarking.base.BenchmarkResult` with
``metrics = {"top_k_accuracy": <float>, "k": <int>}``.
"""
hits, total = 0, 0
B = config.eval_batch_size
T = config.eval_seq_len
V = getattr(
getattr(paradigm, "config", None) or getattr(paradigm, "model_config", None),
"vocab_size", 1000,
)
for _ in range(config.eval_batches):
rng, rng_b = jax.random.split(rng)
batch = jax.random.randint(rng_b, (B, T + 1), 0, V)
x, y = batch[:, :-1], batch[:, 1:]
try:
out = model(x)
logits = out.logits # [B, T, V]
except AttributeError:
# Fallback: paradigm.loss_fn is not the right interface here
continue
top_k = jnp.argsort(logits, axis=-1)[..., -self.k:] # [B, T, k]
hit = jnp.any(top_k == y[..., None], axis=-1) # [B, T]
hits += int(jnp.sum(hit))
total += hit.size
accuracy = hits / max(total, 1)
return BenchmarkResult(
task=self.name,
metrics={"top_k_accuracy": accuracy, "k": float(self.k)},
)
def __repr__(self) -> str:
return f"TopKAccuracyTask(k={self.k})"
Step 2: Register in dantinox/benchmarking/tasks/__init__.py
# dantinox/benchmarking/tasks/__init__.py
from dantinox.benchmarking.tasks.throughput import ThroughputTask
from dantinox.benchmarking.tasks.latency import LatencyTask
from dantinox.benchmarking.tasks.perplexity import PerplexityTask
from dantinox.benchmarking.tasks.accuracy import TopKAccuracyTask # ← new
__all__ = ["ThroughputTask", "LatencyTask", "PerplexityTask", "TopKAccuracyTask"]
Step 3: Use it
from dantinox.benchmarking import BenchmarkSuite
from dantinox.benchmarking.tasks.accuracy import TopKAccuracyTask
suite = BenchmarkSuite(tasks=[TopKAccuracyTask(k=10), PerplexityTask()])
report = suite.run(paradigm, model)
print(report.summary())
Step 4: Test it
# tests/test_topk_task.py
import jax
import jax.numpy as jnp
from flax import nnx
import dantinox as dx
from dantinox.core.config import ModelConfig
from dantinox.benchmarking import BenchmarkConfig
from dantinox.benchmarking.tasks.accuracy import TopKAccuracyTask
def test_topk_task_result_keys():
cfg = ModelConfig(paradigm="ar", dim=64, n_heads=4, head_size=16,
num_blocks=2, vocab_size=100)
paradigm = dx.Paradigm(cfg)
model = paradigm.build_model(nnx.Rngs(0))
config = BenchmarkConfig(eval_batches=2, eval_seq_len=16, eval_batch_size=2)
task = TopKAccuracyTask(k=5)
result = task.run(paradigm, model, config, jax.random.PRNGKey(0))
assert "top_k_accuracy" in result.metrics
assert 0.0 <= result.metrics["top_k_accuracy"] <= 1.0
Checklist
name: ClassVar[str]set to a unique snake_case stringrun()returns a validBenchmarkResultin all code pathsGoogle docstring on the class and
run()Exported from
dantinox/benchmarking/tasks/__init__.pyAt least one unit test
make doccheckpasses