Temperon
Pay for sharpness-aware minimization only where it pays you back.
Tempering is the controlled heat treatment that follows quenching and sets a metal's final toughness. This package governs the training equivalent: what happens during the last anneal.
SAM improves generalization but doubles the cost of every training step, which is why most people skip it. Temperon runs SAM for a contiguous tail aligned to your learning-rate decay instead of the whole run. In our experiments that reaches full-time-SAM quality for roughly a third less wall-clock — and spreading the same SAM budget uniformly across training does worse than either extreme.
Status: pre-release (
0.1.0.dev0), published alongside a paper in preparation. The API may still change. Single-GPU, bf16/fp32 tested; DDP and gradient accumulation are not supported yet.
pip install temperon
Usage
import torch
from temperon import Temperon, wsd
base = torch.optim.AdamW(model.parameters(), lr=2e-5)
total = len(loader) * epochs
opt = Temperon(base, total_steps=total, tail_frac=0.3, rho=0.05)
sched = torch.optim.lr_scheduler.LambdaLR(base, wsd(total, 250, decay_frac=0.3))
for batch in loader:
def closure():
opt.zero_grad()
loss = loss_fn(model(batch.x), batch.y)
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
return loss
loss = opt.step(closure) # 1 pass in the cheap phase, 2 in the tail
sched.step()
The closure follows the torch.optim.LBFGS convention: zero the gradients,
compute the loss, call backward(), return the loss. Temperon calls it once per
step during the cheap phase and twice during the tail. Nothing else in your
training code changes.
The one thing that matters
tail_frac must be aligned to your learning-rate decay, so the tail owns a
complete anneal. This is not a detail: in our ablations a SAM tail bolted onto
the middle of an ongoing cycle gained nothing at all (flat in tail length,
~1pp below full SAM), while a tail owning a fresh anneal matched full-time SAM.
Pass the same fraction to wsd(..., decay_frac=f) and Temperon(tail_frac=f)
and the alignment is exact.
Choosing tail_frac
The switch point is a measured quality/cost dial, not a magic number. On CIFAR-100 (5 seeds, same 100-epoch budget, SGD explorer → SAM+Muon tail):
tail_frac |
final accuracy | total cost (calibrated) |
|---|---|---|
| 0.57 | 0.8295 ± 0.0034 — ties full-time SAM+Muon | 5038s |
| 0.20 | 0.8249 ± 0.0022 — −0.42pp vs full SAM+Muon (p=0.015), level with the published full SAM+SGD recipe (+0.17pp, p=0.35) | 2862s, −43% |
A longer tail buys the expensive method's full ceiling at its full cost; a
shorter one gives up a measured slice of that ceiling for a much cheaper run.
The default 0.3 is the value that matched full-time SAM quality on GPT-2
pretraining (−29% wall-clock) and GLUE fine-tuning (−36%). Below 0.2 we have
no measurements; as tail_frac → 1 you are simply paying for full-time SAM.
Cheap explorer, expensive refiner
You can also switch optimizers at the same boundary — a cheap explorer for most of training, then a stronger optimizer owning the SAM tail:
from temperon import cosine_tail
explorer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
refiner = Muon(model.parameters(), lr=0.01, weight_decay=0.2) # any Muon impl.
opt = Temperon(explorer, total_steps=total, tail_frac=0.57,
tail_optimizer=refiner, transfer="momentum")
sched = torch.optim.lr_scheduler.LambdaLR(explorer, cosine_tail(total, 0.57))
transfer="momentum" carries momentum_buffer across the switch.
When not to use this
Measured limits, stated because they define where the method applies:
- A cheap optimizer wins below its own ceiling. Temperon never accelerates an accuracy target that plain SGD or Adam can already reach; it accelerates targets that only expensive methods reach at all. If your target is modest, use the cheap optimizer.
- Saturated tasks gain nothing. Where the frontier is reached early, the late anneal is not what is limiting you.
- Language-model pretraining is out of scope. In near-single-epoch pretraining there is little overfitting for SAM to prevent, and we measured no useful gain; under heavy data repetition SAM was actively worse. SAM's established language gains are in fine-tuning, which is where this package is aimed for LMs.
Citing
If you use Temperon, please cite the preprint, arXiv:2609.17575:
@article{mastromichalakis2026temperon,
title = {Temperon: Full-Time {SAM} Quality at a Third Less Wall-Clock},
author = {Mastromichalakis, Stamatis},
journal = {arXiv preprint arXiv:2609.17575},
year = {2026},
doi = {10.48550/arXiv.2609.17575}
}
The research code and the full experimental record, including the negative controls this claim rests on, live in the same repository.
License
MIT.
Metadata
Release files for temperon 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| temperon-0.1.1.tar.gz | 10.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| temperon-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 19.6 kB
Release files / temperon-0.1.1.tar.gz
| Download URL | temperon-0.1.1.tar.gz |
|---|---|
| Size | 10.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bb33f4ebd3a7ccc3042985d52c96e9cbfb2951188ee16d9a093448180b34318a
|
|
BLAKE2b-256 checksum How to use checksums |
53d2db829b0454eed2127dac7f0aa57f59bf69d12f6bb5463e49e0e640f80ae9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.4
|
Release files / temperon-0.1.1-py3-none-any.whl
| Download URL | temperon-0.1.1-py3-none-any.whl |
|---|---|
| Size | 9.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2a1b10af50355edbbe9f0cf45c082cf259373b3eb23afa7f3e529a3207271a5b
|
|
BLAKE2b-256 checksum How to use checksums |
b962fbe720ec73e4daa5802596b10c6a878367527c622cd271acc824a8f334e4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.4
|