Вывод показывает переход от предсказания 0.5 к 0.75. Обратите внимание на два отдельных вычисления ошибки — до обновления и после него. Переменная loss хранит результат уже выполненного вычисления; она не пересчитывается сама, когда оптимизатор изменил параметры. Чтобы узнать качество новой версии модели, нужно снова вызвать модель и функцию ошибки. Этот простой факт особенно важен при построении графиков: иначе легко подписать прежнее значение как результат нового шага.
Также стоит заметить, что оптимизатору переданы model.parameters(), а не входные данные. Во время обучения меняется модель, тогда как правильные ответы служат ориентиром. Команда zero_grad() убирает прежнее накопление, backward() рассчитывает новые производные, а step() использует их. Если пропустить первый вызов, вклады разных шагов начнут складываться. Если пропустить последний, можно многократно вычислять ошибку, так и не научив сеть ничему новому.
Почему обучение повторяется много раз
Один шаг лишь немного изменяет параметры. После него зависимость ошибки от весов уже может быть другой, поэтому градиенты следует вычислить заново. Повторение этой последовательности и образует цикл обучения. Полный проход по обучающему набору называют эпохой. Пока в нашем наборе только один объект и мы используем его целиком за один шаг, одна эпоха совпадает с одним обновлением. Позже, когда появятся небольшие порции данных, внутри эпохи будет несколько обновлений.
Для следующего примера возьмём готовую функцию nn.MSELoss() — среднюю квадратичную ошибку. По умолчанию она усредняет квадраты отклонений по всем элементам и не добавляет множитель 0.5. Для одного и того же предсказания её значение и градиенты вдвое больше, чем у формулы, использованной выше. Поэтому при сравнении двух экспериментов важно сравнивать не только название «квадратичная ошибка», но и точную формулу. Здесь мы выберем небольшой шаг 0.01 и повторим обучение сто раз.