C.W.K.
Stream
Lesson 04 of 08 · published

One Train Step

~18 min · train-step, zero-grad, step

Level 0Curious
0 XP0/73 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

The five-line ritual

The unit of training is one step. It always has the same five lines, in the same order:

  1. opt.zero_grad() — clear gradients from the previous step.
  2. logits = model(xb) — forward pass.
  3. loss = loss_fn(logits, yb) — scalar loss.
  4. loss.backward() — compute gradients via autograd.
  5. opt.step() — apply gradients to parameters.

Optionally insert clip_grad_norm_ between backward and step, and scheduler.step() after step. That's the whole loop body. Everything else is plumbing around it.

Tip: If you can't draw the five-line ritual on a whiteboard from memory, you don't yet have the muscle. Write it twenty times. Eventually you will type opt.zero_grad() before you finish thinking the sentence.

The two famous bugs of step 1

Forgotten zero_grad() — gradients accumulate across steps, so each step sees the sum of every previous gradient. Loss stays high, training collapses. The fix is one line.

Calling opt.step() before loss.backward() — you step on stale gradients (the previous batch's). Loss curve looks vaguely like training but the model isn't actually learning the current batch.

Gradient accumulation (the right way to skip zero_grad)

Sometimes you want a larger effective batch size than fits in VRAM. The trick: skip zero_grad() for several batches, divide the loss by the number of accumulation steps, then opt.step() + opt.zero_grad() at the end. This produces gradients equivalent to a single batch accum_steps times bigger.

Principle: The five-line ritual is the smallest unit of training. Memorize it. Then learn one variation at a time (gradient accumulation, mixed precision, gradient clipping). Do not skip the basic version.

Code

One train step, with all the optional pieces·python
from torch.nn.utils import clip_grad_norm_

opt.zero_grad()
logits = model(xb)
loss = loss_fn(logits, yb)
loss.backward()
clip_grad_norm_(model.parameters(), max_norm=1.0)   # optional
opt.step()
scheduler.step()                                    # optional
Gradient accumulation for big effective batch·python
ACCUM = 4
opt.zero_grad()
for micro_idx, (xb, yb) in enumerate(loader):
    logits = model(xb)
    loss = loss_fn(logits, yb) / ACCUM        # rescale for averaging
    loss.backward()                            # accumulates into .grad
    if (micro_idx + 1) % ACCUM == 0:
        clip_grad_norm_(model.parameters(), max_norm=1.0)
        opt.step()
        opt.zero_grad()

External links

Exercise

Comment out opt.zero_grad() in your training loop and watch the loss curve. Now try gradient accumulation with ACCUM=4 — verify the result matches a single 4x batch run.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.