PAJAMA / LEARN
← Course overview

Learn from observations / 09 OF 12

Watch a model actually learn

After this lessonRun genuine updates, distinguish forward/backward/optimizer stages, and read what the loss does and does not prove.

Now replace the single hand-written gradient with the existing two-Gaussian experiment. We keep targets fixed and watch three parameters adapt across three timestamps.

Build the idea, one step at a time

Need a math refresher?

Open only the tool you need; return here to use it in the next step.

Derivatives as local ratesThe chain rule

STEP 1 / 5

Freeze the question before fitting the answer

The teacher creates 12 × 10 target pixels at each of three times: 360 RGB samples. The student starts from a different record. Only Gaussian A’s initial x, linear x-motion and opacity logit change. Gaussian B, camera, shape, other motion terms and target images remain fixed. This bounded task makes the cause of each improvement inspectable.

PUT NUMBERS IN

At learning rate 5, the deterministic initial MSE is about 0.004031. After 50 full-batch updates it is about 0.001795. These are recomputed from the renderer; the curve is not a prerecorded animation. Different rates can behave differently.

FORMULA → RUNNING CODE

Find the same operation in the code

These excerpts are read from the current repository when the site is built. Expand one to inspect it; the full snapshot opens in a separate tab.

public/math/training.mjsL20–41

The batch computes actual image loss and accumulated gradients.

export function evaluateTraining(records, data, withGradient=false) {
  let loss=0; const gradients=[0,0,0],frames=[];
  const count=data.reduce((n,frame)=>n+frame.samples.length,0);
  for(const frame of data) {
    const pixels=[];
    for(const sample of frame.samples) {
      const options={target:sample.target,finite:false};
      const position=withGradient?gradient(records,0,frame.time,sample.pixel,options):null;
      const result=position?.result??forward(records,frame.time,sample.pixel,options);
      loss+=result.loss/count;
      if(withGradient) {
        gradients[0]+=position.analytic/count;
        gradients[1]+=position.analytic*(frame.time-records[0][3])/count;
        gradients[2]+=gradient(records,20,frame.time,sample.pixel,options).analytic/count;
      }
      pixels.push(result);
    }
    frames.push(pixels);
  }
  return {loss,gradients,frames};
}
Open the deployed source snapshot ↗
public/math/training.mjsL42–50

The update is simultaneous and uses pre-update gradients.

export function trainingIteration(records, data, rate=1) {
  if(!Number.isFinite(rate)||rate<=0)throw new RangeError('Learning rate must be positive');
  const beforeRecords=copyRecords(records),before=evaluateTraining(beforeRecords,data,true);
  const afterRecords=copyRecords(beforeRecords);
  // All gradients come from the same pre-update batch; commit simultaneously.
  trainingParameters.forEach((field,j)=>{afterRecords[0][field]-=rate*before.gradients[j];});
  const after=evaluateTraining(afterRecords,data);
  if(!Number.isFinite(after.loss)||!afterRecords.flat().every(Number.isFinite))throw new Error('Non-finite training state');
  return {beforeRecords,before,afterRecords,after,rate};
Open the deployed source snapshot ↗
scripts/train_toy_stg.pyL80–89

The separate Python experiment uses PyTorch autograd and Adam.

    optimizer = torch.optim.Adam(fitted, lr=0.025)
    history = []
    started = time.perf_counter()
    for step in range(args.steps):
        optimizer.zero_grad(set_to_none=True)
        loss = (render(*fitted, times, cameras)-truth).square().mean()
        if not torch.isfinite(loss):
            raise RuntimeError("Non-finite training loss")
        loss.backward()
        optimizer.step()
Open the deployed source snapshot ↗

CHECK YOUR UNDERSTANDING

Can you predict the result?

Which data must stay fixed while fitting this synthetic task?

This is your own review mark. You can remove it at any time.

Go deeper with primary sources

Adam · original optimizer paper ↗3DGS · training implementation ↗Full mathematical derivation ↗