Checkpoints and randomness#
Save and restore model, optimizer, and random-stream state together. Verify the next update is reproduced, rather than checking only that weights load.
These outputs were saved from local execution. The documentation renderer does not execute notebook cells.
JAXsaw 0.1.0 · JAX 0.5.3
[CudaDevice(id=0)]
Own random streams explicitly#
Initialization and runtime randomness use separate streams. The random stream is an explicit step argument and part of the checkpoint.
model = nn.Linear(2,1,rngs=js.Rngs(params=15))
optimizer = optim.Adam(model.parameters(),lr=.01)
runtime = js.Rngs(sample=16)
@js.jit
def step(model,optimizer,rngs):
x = js.random.normal(rngs.next('sample'),(8,2))
target = x.sum(-1,keepdims=True)
loss,grads = js.value_and_grad(lambda m:F.mse_loss(m(x),target))(model)
optimizer.step(grads)
return loss
print('Initial update:',float(step(model,optimizer,runtime)))
Initial update: 6.502008438110352
Save a complete snapshot#
NPZ checkpoints contain arrays and a JSON manifest; loading does not execute embedded Python. Reconstruct the same architecture and optimizer configuration before restoration.
directory = ROOT / 'runs' / 'api-checkpoint'
directory.mkdir(parents=True,exist_ok=True)
path = directory / 'state.npz'
checkpoint.save(path,model=model,optimizer=optimizer,rngs=runtime,
progress={'next_update':int(optimizer.step_count)},metadata={'seed':15})
expected_loss = step(model,optimizer,runtime)
expected_weights = model.state_dict()
print('Next update before restore:',float(expected_loss))
Next update before restore: 2.25146222114563
Verify exact continuation on this backend#
Restoring RNG counters and optimizer moments is necessary to reproduce the next update. Array comparisons below verify actual continuation on this GPU/runtime.
restored = checkpoint.restore(path,model=model,optimizer=optimizer,rngs=runtime)
actual_loss = step(model,optimizer,runtime)
np.testing.assert_allclose(actual_loss,expected_loss,atol=1e-7)
for name,value in model.state_dict().items():
np.testing.assert_allclose(value,expected_weights[name],atol=1e-7)
print('Next update after restore:',float(actual_loss))
print('Saved progress:',restored.progress)
print('Continuation verified.')
Next update after restore: 2.25146222114563
Saved progress: {'next_update': 1}
Continuation verified.
Weights-only snapshots#
Use state_dict when transferring weights into an already constructed matching model. It does not contain optimizer or RNG state, so it is insufficient for exact training continuation.
weights = model.state_dict()
copy = nn.Linear(2,1,rngs=js.Rngs(params=99))
copy.load_state_dict(weights)
probe = jnp.ones((2,2))
np.testing.assert_allclose(model(probe),copy(probe))
print('Matching inference:',np.asarray(copy(probe)))
Matching inference: [[-0.7157132]
[-0.7157132]]