train

notebooks/train.py

#0 jc.step

Tiny training loop

A one-parameter linear regression fit with gradient descent, plus a loss curve and final metrics. Every run dumps:

  • artifacts/checkpoint.json — the final weight + full loss history.
  • artifacts/metrics.json — end-of-training summary (for the tearsheet table).
  • artifacts/loss_curve.png — training loss vs. epoch.
#1 config jc.setup ok 131 ms
EPOCHS = 40
LR = 0.02
SEED = 0
TRUE_W = 1.7
NOISE = 0.25
#2 toy_dataset jc.load ok 56 ms
# Noisy linear data: y ≈ TRUE_W * x + ε.
import numpy as np

rng = np.random.default_rng(SEED)
x = rng.uniform(-1.0, 1.0, size=128)
noise = rng.normal(0.0, NOISE, size=x.shape)
y = TRUE_W * x + noise
print(f"dataset: {len(x)} points   target w: {TRUE_W}")
dataset: 128 points   target w: 1.7
#3 train deps: toy_dataset config ok 6 ms
# Vanilla full-batch gradient descent on MSE.
w = 0.0
loss_history: list[float] = []
for _ in range(EPOCHS):
    pred = w * x
    grad = float(np.mean(2.0 * (pred - y) * x))
    w -= LR * grad
    loss_history.append(float(np.mean((pred - y) ** 2)))
print(f"fitted w: {w:.4f}   target w: {TRUE_W}")
print(f"final loss: {loss_history[-1]:.4f}")
fitted w: 0.7903   target w: 1.7
final loss: 0.4149
#4 loss_curve jc.figure deps: train ok 263 ms
import matplotlib.pyplot as plt

import jellycell.api as jc

fig, ax = plt.subplots(figsize=(7, 3.2))
ax.plot(range(1, EPOCHS + 1), loss_history, color="#4f46e5", linewidth=1.6)
ax.set_xlabel("Epoch")
ax.set_ylabel("MSE loss")
ax.set_title("Training loss per epoch")
ax.grid(alpha=0.3)
fig.tight_layout()
jc.figure(
    path="artifacts/loss_curve.png",
    fig=fig,
    caption="Figure 1: training loss (MSE) per epoch",
    notes=(
        "Monotone decrease, no oscillation, no divergence — LR and batch "
        "size are in a stable regime for this dataset. 40 epochs not quite "
        "enough for convergence (see model-card.md)."
    ),
    tags=["training", "diagnostic"],
)
PosixPath('/Users/blaise/Desktop/blaise-oss/jellycell/examples/ml-experiment/artifacts/loss_curve.png')
<Figure size 700x320 with 1 Axes>
#5 metrics deps: train ok 5 ms
metrics = {
    "epochs": EPOCHS,
    "learning_rate": LR,
    "final_weight": round(w, 4),
    "target_weight": TRUE_W,
    "final_loss": round(loss_history[-1], 4),
    "initial_loss": round(loss_history[0], 4),
    "weight_error_abs": round(abs(w - TRUE_W), 4),
}
jc.save(
    metrics,
    "artifacts/metrics.json",
    caption="Table 1: end-of-training metrics",
    notes=(
        "final_weight vs target_weight shows how far the optimizer got. "
        "weight_error_abs = |target − fitted|."
    ),
    tags=["training", "result"],
)
print(metrics)
{'epochs': 40, 'learning_rate': 0.02, 'final_weight': 0.7903, 'target_weight': 1.7, 'final_loss': 0.4149, 'initial_loss': 1.2033, 'weight_error_abs': 0.9097}
#6 checkpoint deps: train ok 5 ms
# Full loss history lives in the checkpoint alongside the weight. Real
# projects would write tensors here; jellycell's cache is content-
# addressed so identical checkpoints dedup on disk.
jc.save({"weight": w, "history": loss_history}, "artifacts/checkpoint.json")
PosixPath('/Users/blaise/Desktop/blaise-oss/jellycell/examples/ml-experiment/artifacts/checkpoint.json')