Transforms in the Training Loop¶
Every augmentation ships with two backends that share the same names and parameters:
NumPy (
audiotree.transforms) — for CPU Grain data pipelines. You chain these onto a dataset with.map()/.random_map(); that is the previous chapter, Chaining Transforms with Datasets. They take anp.random.Generator.JAX (
audiotree.transforms.jax) — the same transforms built from JAX ops, so they run on the accelerator and compose insidejax.jit. They take ajax.random.key.
Reach for the JAX backend when you want to augment a batch inside the training
step, on-device, instead of in CPU data-loader workers. A transform’s
random_map is a plain function of (tree, key), so it traces cleanly under
@jax.jit right next to your model:
import jax
import jax.numpy as jnp
from audiotree import AudioTree
from audiotree.transforms import jax as jax_transforms
# The same transform as the NumPy backend, but JAX-native (takes a jax.random.key).
augment = jax_transforms.volume_change(min_db=-6, max_db=6)
@jax.jit
def train_step(batch: AudioTree, key):
# Augment on-device, then run your model. A trivial energy statistic
# stands in for the model and loss here.
batch = augment.random_map(batch, key)
return jnp.mean(batch.waveform**2)
batch = AudioTree(jnp.full((8, 2, 16_000), 0.5), 16_000)
# Split a fresh key each step so every step augments differently.
key = jax.random.key(0)
for _ in range(3):
key, subkey = jax.random.split(key)
loss = train_step(batch, subkey)
print(loss.shape) # a scalar
print(bool(loss > 0))
()
True
Chain several augmentations by splitting a key per random transform; map
transforms such as trim and resample need no key and compose the same way:
gain = jax_transforms.volume_change(min_db=-6, max_db=6)
phase = jax_transforms.invert_phase(prob=0.5)
resize = jax_transforms.trim(length=0.5) # a map transform
@jax.jit
def augment_batch(batch: AudioTree, key) -> AudioTree:
k1, k2 = jax.random.split(key)
batch = gain.random_map(batch, k1)
batch = phase.random_map(batch, k2)
batch = resize.map(batch)
return batch
out = augment_batch(batch, jax.random.key(1))
print(out.waveform.shape)
(8, 2, 8000)
Pair this with grain.experimental.device_put() (see
Prefetching Batches onto an Accelerator) to stream host batches onto the accelerator and
augment them in the same jitted step that trains your model.
Note
The full catalog of transforms — their shared parameters (prob,
split_seed, scope, output_key), which ones invalidate the cached
lufs, and the decorators for writing your own — lives in the
audiotree.transforms API reference. Both backends expose the same names
(choose is the one exception — it branches in Python, so it cannot be
traced), so anything you configure for a Grain pipeline works here by importing
it from audiotree.transforms.jax.
encode_with_codec and encode_latents are a special case in the other
direction: the two namespaces export the same objects, because the
arithmetic lives in the codec you supply rather than in audiotree. Neither
wraps the codec in jax.jit, so a JAX codec traces into a step like the one
above. See Neural Codecs.
Note
Same names, same semantics, not bit-identical results. The two backends
agree on what a transform means — and tests/transforms/test_backend_parity.py
pins that agreement — but two implementations of the same DSP do not produce
identical floats:
resampleuses librosa/soxr on NumPy and a Julius-style sinc filter on JAX. On band-limited content they agree to about8.5e-5absolute; on broadband noise only to ~24 dB SNR, essentially all of it in the anti-aliasing filter’s transition band, where the two filter designs roll off differently.volume_normmeasures with the exact IIR K-weighting meter on NumPy andjaxloudnorm’s FIR-approximated K-weighting on JAX. Measured loudness differs by up to 0.031 dB, so the output differs from the other backend’s by a pure scalar gain of about 0.036 dB.
Neither is a bug, but do not expect a NumPy-augmented run and a JAX-augmented run to reproduce each other sample-for-sample.
Next¶
With data loaded, augmented, and ready for training, the final Getting-started chapter, Writing Datasets, shows how to write prepared datasets back to disk.