Standard metrics
metrana.log is the workhorse for standard, ML-step metrics:
metrana.log(metric_name, value, *, step=None, timestamp=None, scale=None, labels=None, evaluation=False, dtype=None)It logs to the default ML_STEP scale. For how step, scale, labels, and evaluation shape a series,
see Steps, scales & labels; this page covers values and call
shapes.
Values: scalar or array
Section titled “Values: scalar or array”A value may be a scalar or an array — a Python list, or any NumPy / PyTorch / JAX / TensorFlow tensor (any float dtype, on any device). Arrays are converted to a contiguous NumPy array once before crossing into the engine.
metrana.log("loss", 0.5) # one pointmetrana.log("loss", [0.51, 0.49, 0.48]) # three points (bulk)metrana.log("grad_norm", grad_norm_tensor) # torch / np / jax / tf tensorLogging an array is the bulk path: it records many points in one call. This is far cheaper than a Python
loop of scalar log calls — see Logging at scale.
Storage precision (dtype)
Section titled “Storage precision (dtype)”Float values are stored at float32 by default: metric signals don’t carry more precision than that,
and it halves the wire and storage footprint. Values arriving wider are rounded with IEEE
round-to-nearest-even. Integer-typed values — Python ints, int arrays/tensors, typically counters like
tokens seen or cumulative samples — are exempt from the default and stored as float64, which keeps them
exact up to 253 (float32 would silently round anything past ~16.7M, 224).
Choose a precision explicitly per call with dtype, or run-wide with metrana.init(..., default_dtype=...):
metrana.log("loss", loss) # float32 (the float default)metrana.log("tokens_seen", tokens) # int input → float64, stays exactmetrana.log("reward", rewards, dtype="float16") # half precision, half the wire againAccepted values are "float64", "float32", "float16", "int64" and "int32" — numpy spellings
(np.float32, "f4", …) work too; for torch/JAX dtypes pass their string name. "float16" rounds to half
precision but travels as float32 (the widening is exact). "int64"/"int32" declare an integer metric:
use them when a counter reaches the log call as a float (after a division, a mean, or a metrics dict that
stores plain floats — where the automatic exemption can’t see it) — values are truncated toward zero and
travel as float64, exact to 253; NaN/inf raise a validation error (a NaN is meaningful in a
float metric, but always a bug in a counter). An explicit dtype
always wins, including over the integer exemption: dtype="float32" narrows an int input too.
Multiple metrics at once
Section titled “Multiple metrics at once”Pass a mapping to log several metrics in a single call. Each value may itself be a scalar or an array:
metrana.log({"loss": loss, "accuracy": acc})
metrana.log({ "loss": loss, "lr": current_lr, "grad_norm": grad_norm_tensor,})This is purely a convenience over calling log once per metric — each entry becomes its own series.
A typical training loop
Section titled “A typical training loop”import metrana
metrana.init(workspace_name="ws", project_name="proj", run_name="run-001")
try: for step in range(num_steps): loss = train_step() metrics = evaluate() # {"accuracy": ..., "f1": ...}
metrana.log("loss", loss) # logged every step → auto-increment is fine metrana.log(metrics) # several at once if step % 100 == 0: # logged only every 100 steps → pass the real training step explicitly, # otherwise this series' auto-incremented axis (0, 1, 2, …) wouldn't line # up with `loss` on the same chart. metrana.log("lr", scheduler.get_last_lr()[0], step=step)finally: metrana.close()Steps must increase
Section titled “Steps must increase”A series logged with log is ordered: every new point’s step must be greater than the last step
already logged for that series. Auto-incremented steps satisfy this by construction; with explicit step=
it’s on you.
Two situations look like backward steps and have proper solutions instead:
- Several processes writing one series legitimately interleave steps — use
log_distributed, whose merge semantics accept out-of-order steps. - A restarted process must not re-number from 0: resume from the run’s real last step
(
metrana.get_last_step) or your checkpoint’s step counter. See Resuming & forking.
logis non-blocking: it validates the value and enqueues it for the background engine, then returns. It does not wait for the network. To wait for durability, callflush.- An invalid value (bad name, mismatched lengths, non-finite where not allowed, …) raises
MetranaValidationErrorsynchronously fromlog— validation is the enqueue boundary. See Errors. - For series written by more than one process, use
log_distributedinstead — it uses merge semantics so concurrent writers don’t conflict.
- Steps, scales & labels — series identity and the step axis.
- RL metrics — the two-level RL step model.