BATCH = 128
EPOCHS = 30
steps_per_epoch = len(X_train) // BATCH
train_losses, val_losses = [], []
t0 = time.time()
for epoch in vary(EPOCHS):
key, sk = jax.random.break up(key)
perm = jax.random.permutation(sk, len(X_train))
X_s, Y_s = X_train[perm], Y_train[perm]
epoch_loss = 0.0
for step in vary(steps_per_epoch):
xb = X_s[step*BATCH:(step+1)*BATCH]
yb = Y_s[step*BATCH:(step+1)*BATCH]
mannequin, opt_state, loss = train_step(mannequin, opt_state, xb, yb)
epoch_loss += loss.merchandise()
val_loss = consider(mannequin, X_val, Y_val).merchandise()
train_losses.append(epoch_loss / steps_per_epoch)
val_losses.append(val_loss)
if (epoch + 1) % 5 == 0:
print(f"Epoch {epoch+1:3d}/{EPOCHS} "
f"train_loss={train_losses[-1]:.5f} "
f"val_loss={val_losses[-1]:.5f}")
print(f"nTotal coaching time: {time.time()-t0:.1f}s")
print("n" + "="*60)
print("SECTION 7: Save & load mannequin weights")
print("="*60)
eqx.tree_serialise_leaves("model_weights.eqx", mannequin)
key, mk2 = jax.random.break up(key)
model_skeleton = ResNetMLP(1, 64, 1, n_blocks=4, key=mk2)
model_loaded = eqx.tree_deserialise_leaves("model_weights.eqx", model_skeleton)
diff = jnp.max(jnp.abs(
jax.tree_util.tree_leaves(eqx.filter(mannequin, eqx.is_array))[0]
- jax.tree_util.tree_leaves(eqx.filter(model_loaded, eqx.is_array))[0]
))
print(f"Max weight distinction after reload: {diff:.2e} (needs to be 0.0)")
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
axes[0].plot(train_losses, label="Prepare MSE", coloration="#4C72B0")
axes[0].plot(val_losses, label="Val MSE", coloration="#DD8452", linestyle="--")
axes[0].set_xlabel("Epoch")
axes[0].set_ylabel("MSE")
axes[0].set_title("Coaching curves")
axes[0].legend()
axes[0].grid(True, alpha=0.3)
x_plot = jnp.linspace(-1, 1, 300).reshape(-1, 1)
y_true = jnp.sin(2 * jnp.pi * x_plot)
y_pred = jax.vmap(mannequin)(x_plot)
axes[1].scatter(X_val[:100], Y_val[:100], s=10, alpha=0.4, coloration="grey", label="Information")
axes[1].plot(x_plot, y_true, coloration="#4C72B0", linewidth=2, label="True f(x)")
axes[1].plot(x_plot, y_pred, coloration="#DD8452", linewidth=2, linestyle="--", label="Predicted")
axes[1].set_xlabel("x")
axes[1].set_ylabel("y")
axes[1].set_title("Sine regression match")
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("equinox_tutorial.png", dpi=150)
plt.present()
print("nDone! Plot saved to equinox_tutorial.png")
print("n" + "="*60)
print("BONUS: eqx.filter_jit + form inference debug tip")
print("="*60)
jaxpr = jax.make_jaxpr(jax.vmap(mannequin))(x_plot)
n_eqns = len(jaxpr.jaxpr.eqns)
print(f"Compiled ResNetMLP jaxpr has {n_eqns} equations (ops) for batch enter {x_plot.form}")
BATCH = 128
EPOCHS = 30
steps_per_epoch = len(X_train) // BATCH
train_losses, val_losses = [], []
t0 = time.time()
for epoch in vary(EPOCHS):
key, sk = jax.random.break up(key)
perm = jax.random.permutation(sk, len(X_train))
X_s, Y_s = X_train[perm], Y_train[perm]
epoch_loss = 0.0
for step in vary(steps_per_epoch):
xb = X_s[step*BATCH:(step+1)*BATCH]
yb = Y_s[step*BATCH:(step+1)*BATCH]
mannequin, opt_state, loss = train_step(mannequin, opt_state, xb, yb)
epoch_loss += loss.merchandise()
val_loss = consider(mannequin, X_val, Y_val).merchandise()
train_losses.append(epoch_loss / steps_per_epoch)
val_losses.append(val_loss)
if (epoch + 1) % 5 == 0:
print(f"Epoch {epoch+1:3d}/{EPOCHS} "
f"train_loss={train_losses[-1]:.5f} "
f"val_loss={val_losses[-1]:.5f}")
print(f"nTotal coaching time: {time.time()-t0:.1f}s")
print("n" + "="*60)
print("SECTION 7: Save & load mannequin weights")
print("="*60)
eqx.tree_serialise_leaves("model_weights.eqx", mannequin)
key, mk2 = jax.random.break up(key)
model_skeleton = ResNetMLP(1, 64, 1, n_blocks=4, key=mk2)
model_loaded = eqx.tree_deserialise_leaves("model_weights.eqx", model_skeleton)
diff = jnp.max(jnp.abs(
jax.tree_util.tree_leaves(eqx.filter(mannequin, eqx.is_array))[0]
- jax.tree_util.tree_leaves(eqx.filter(model_loaded, eqx.is_array))[0]
))
print(f"Max weight distinction after reload: {diff:.2e} (needs to be 0.0)")
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
axes[0].plot(train_losses, label="Prepare MSE", coloration="#4C72B0")
axes[0].plot(val_losses, label="Val MSE", coloration="#DD8452", linestyle="--")
axes[0].set_xlabel("Epoch")
axes[0].set_ylabel("MSE")
axes[0].set_title("Coaching curves")
axes[0].legend()
axes[0].grid(True, alpha=0.3)
x_plot = jnp.linspace(-1, 1, 300).reshape(-1, 1)
y_true = jnp.sin(2 * jnp.pi * x_plot)
y_pred = jax.vmap(mannequin)(x_plot)
axes[1].scatter(X_val[:100], Y_val[:100], s=10, alpha=0.4, coloration="grey", label="Information")
axes[1].plot(x_plot, y_true, coloration="#4C72B0", linewidth=2, label="True f(x)")
axes[1].plot(x_plot, y_pred, coloration="#DD8452", linewidth=2, linestyle="--", label="Predicted")
axes[1].set_xlabel("x")
axes[1].set_ylabel("y")
axes[1].set_title("Sine regression match")
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("equinox_tutorial.png", dpi=150)
plt.present()
print("nDone! Plot saved to equinox_tutorial.png")
print("n" + "="*60)
print("BONUS: eqx.filter_jit + form inference debug tip")
print("="*60)
jaxpr = jax.make_jaxpr(jax.vmap(mannequin))(x_plot)
n_eqns = len(jaxpr.jaxpr.eqns)
print(f"Compiled ResNetMLP jaxpr has {n_eqns} equations (ops) for batch enter {x_plot.form}")
