Классический training loop

Собираем всё изученное в главный паттерн PyTorch — цикл обучения.

Training loop — повторяющаяся последовательность: предсказать, измерить ошибку, посчитать градиенты, сделать шаг по весам.

Пять шагов одной итерации

Любое обучение в PyTorch — это один и тот же ритуал из пяти действий, повторяемый много раз. Запомните его наизусть, дальше вы будете писать его постоянно:

import torch
import torch.nn as nn

model = nn.Linear(3, 1)
criterion = nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

for epoch in range(100):
    # 1. forward — модель делает предсказание
    pred = model(X)

    # 2. loss — измеряем ошибку
    loss = criterion(pred, y)

    # 3. zero_grad — чистим старые градиенты
    optimizer.zero_grad()

    # 4. backward — autograd считает новые градиенты
    loss.backward()

    # 5. step — оптимизатор двигает веса
    optimizer.step()

Каждый шаг — это кусочек, который мы уже разбирали по отдельности. Теперь они работают вместе.

Разбор по шагам

Шаг 1, forward. model(X) прогоняет данные через слои и строит граф вычислений (помните: динамически, на лету). На выходе — предсказания.

Шаг 2, loss. criterion(pred, y) сравнивает предсказание с правильным ответом и возвращает одно число — насколько мы ошиблись. Это вершина графа.

Шаг 3, zero_grad. Обнуляем .grad всех параметров. Без этого градиенты с прошлой итерации сложатся с новыми (мы разбирали это в уроке про zero_grad) — и обучение сломается.

Шаг 4, backward. loss.backward() запускает backprop: autograd проходит граф назад и заполняет .grad каждого веса. Производные считаются автоматически.

Шаг 5, step. optimizer.step() применяет правило обновления (для SGD это w = w - lr * w.grad) и сдвигает все веса в сторону уменьшения ошибки. Это и есть градиентный спуск.

Почему именно такой порядок

Порядок не случаен. Сначала нужно посчитать loss (нельзя дифференцировать то, чего нет). zero_grad ставят перед backward, чтобы расчистить место для свежих градиентов. step — строго после backward, ведь он использует только что посчитанные .grad. Перепутаете порядок backward и step — оптимизатор сдвинет веса по устаревшим (или нулевым) градиентам.

ШагКомандаЧто меняет
forwardmodel(X)строит предсказание и граф
losscriterion(pred, y)число ошибки
zero_gradoptimizer.zero_grad()чистит .grad
backwardloss.backward()заполняет .grad
stepoptimizer.step()двигает веса

Что такое эпоха

Один проход по всем обучающим данным называют эпохой. В примере мы повторяем шаги 100 эпох — модель 100 раз увидит весь датасет и каждый раз чуть улучшит веса. Loss с эпохами должен падать; если он растёт или скачет — частые причины — слишком большой lr или забытый zero_grad.

Итог

  • Цикл обучения — это ритуал из 5 шагов: forward → loss → zero_grad → backward → step.
  • Порядок важен: zero_grad перед backward, step строго после backward.
  • step двигает веса по только что посчитанным градиентам — это градиентный спуск.
  • Эпоха — один проход по всем данным; за эпохи loss должен снижаться.
Проверьте себя
1. Каков правильный порядок шагов в одной итерации обучения?
Abackward, step, forward, loss, zero_grad
Bforward, loss, zero_grad, backward, step
Czero_grad, step, forward, backward, loss
Dloss, forward, backward, zero_grad, step
2. Почему optimizer.step() вызывают строго после loss.backward()?
AИначе программа упадёт с ошибкой
Bstep использует градиенты, которые заполняет backward
CПорядок не важен, можно как угодно
Dstep сам вызывает backward внутри
3. Что делает optimizer.step() для оптимизатора SGD?
AСчитает градиенты весов
BОбнуляет градиенты
CСдвигает веса по правилу w = w - lr * w.grad
DДелает предсказание модели
Поддержать проект