Полный пример обучения построчно
Финальная сборка раздела: рабочий пример обучения от начала до конца, строка за строкой.
Цель урока — увидеть все детали вместе на одной маленькой, но реальной задаче: научить линию проходить через данные.
Задача
Сгенерируем точки вокруг прямой y = 2x + 1 с шумом и попросим модель восстановить эту зависимость. Это регрессия: модель nn.Linear(1, 1) должна сама подобрать наклон около 2 и сдвиг около 1.
Шаг 1. Данные
import torch
import torch.nn as nn
torch.manual_seed(0) # воспроизводимость
# 100 точек: x случайные, y = 2x + 1 + шум
X = torch.rand(100, 1) * 10 # форма (100, 1), значения 0..10
y = 2 * X + 1 + torch.randn(100, 1) # истинная зависимость + шум
Формы (100, 1) — это 100 примеров по одному признаку. torch.manual_seed(0) фиксирует случайность, чтобы результат повторялся.
Шаг 2. Модель, loss, оптимизатор
model = nn.Linear(1, 1) # один вход, один выход
criterion = nn.MSELoss() # регрессия -> MSE
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
Три кирпича из прошлых уроков: линейный слой (он и есть наша прямая y = wx + b), среднеквадратичный loss и оптимизатор SGD, которому отдали параметры модели.
Шаг 3. Цикл обучения
for epoch in range(200):
pred = model(X) # 1. forward
loss = criterion(pred, y) # 2. loss
optimizer.zero_grad() # 3. чистим градиенты
loss.backward() # 4. backprop
optimizer.step() # 5. шаг по весам
if (epoch + 1) % 50 == 0:
print(f"epoch {epoch+1:3d} loss {loss.item():.3f}")
Это ровно тот ритуал из пяти шагов. Каждые 50 эпох печатаем loss — он должен снижаться. Обратите внимание на loss.item(): достаём число из тензора для печати (без удержания графа).
Вывод (примерно):
epoch 50 loss 1.421 epoch 100 loss 1.045 epoch 150 loss 0.987 epoch 200 loss 0.971
Loss упал и вышел на плато около 1.0 — это уровень шума, который мы сами добавили. Идеального нуля и не должно быть: модель не может предсказать случайный шум, и это правильно.
Шаг 4. Смотрим, что выучила модель
w = model.weight.item() # выученный наклон
b = model.bias.item() # выученный сдвиг
print(f"выучено: y = {w:.2f} * x + {b:.2f}")
# выучено: y = 2.01 * x + 0.95 (близко к истинным 2 и 1)
# предсказание на новой точке — режим оценки, без графа
model.eval()
with torch.no_grad():
x_new = torch.tensor([[5.0]])
print("предсказание для x=5:", model(x_new).item())
# около 11.0 (2*5 + 1)
Модель восстановила почти точные коэффициенты: наклон ≈ 2.01, сдвиг ≈ 0.95 — мы их не задавали, она нашла их сама, минимизируя loss. На предсказании мы аккуратно использовали eval() и no_grad(), как учились в прошлом уроке.
Чек-лист рабочего цикла
| Есть в коде? | Что именно |
| да | пять шагов в правильном порядке |
| да | zero_grad() перед backward |
| да | loss подходит задаче (MSE для регрессии) |
| да | формы предсказания и цели совпадают (100, 1) |
| да | eval() + no_grad() на предсказании |
Итог
- Полное обучение = данные → (модель, loss, оптимизатор) → цикл из 5 шагов → проверка.
- Loss выходит на плато уровня шума в данных — нулём он быть не обязан.
- Модель сама подобрала коэффициенты, минимизируя loss; мы их не задавали.
- Один и тот же шаблон масштабируется от линии до огромных сетей — меняются лишь модель и данные.