Градиентный спуск для линейной регрессии
Соберём всё вместе: обучим настоящую линейную регрессию градиентным спуском — модель с двумя параметрами, которые подгоняются под данные.
Линейная регрессия ищет прямую
y = w·x + b, наилучшим образом приближающую данные. Параметры w (наклон) и b (сдвиг) подбираются минимизацией MSE градиентным спуском.
Модель и потеря
У нас есть пары (x, y), и мы хотим прямую предсказание = w·x + b. Качество меряем средним квадратом ошибки: MSE = среднее((w·x + b − y)²). Теперь параметров два — w и b, — значит, нужен градиент из двух частных производных.
Градиенты по w и b
Применим правило цепочки к (w·x + b − y)². Производная по w: 2·(w·x + b − y)·x. Производная по b: 2·(w·x + b − y). Усредняем по всем точкам — получаем градиент потери. Дальше — знакомое правило: шаг против градиента.
# Данные: примерно y = 2x + 1 с лёгким шумом
xs = [1, 2, 3, 4, 5]
ys = [3.1, 4.9, 7.2, 8.8, 11.1]
w, b = 0.0, 0.0 # стартовые параметры
lr = 0.01
n = len(xs)
for epoch in range(1, 2001):
# считаем градиенты по w и b
dw, db = 0.0, 0.0
for x, y in zip(xs, ys):
pred = w * x + b
err = pred - y
dw += 2 * err * x / n
db += 2 * err / n
# шаг против градиента
w -= lr * dw
b -= lr * db
if epoch in (1, 100, 500, 2000):
mse = sum((w * x + b - y) ** 2 for x, y in zip(xs, ys)) / n
print(f"эпоха {epoch:4}: w={round(w, 3):5} b={round(b, 3):6} MSE={round(mse, 4)}")
Вывод:
эпоха 1: w=0.501 b= 0.14 MSE=33.3711 эпоха 100: w=2.081 b= 0.72 MSE=0.0412 эпоха 500: w=2.014 b= 0.965 MSE=0.0227 эпоха 2000: w= 1.99 b= 1.049 MSE=0.0214
Спуск сам нашёл w ≈ 2 и b ≈ 1 — почти точную прямую y = 2x + 1 (данные были с шумом, поэтому совпадение не идеальное). MSE упала с 33 до 0.02. Никакого перебора — только наклон и шаги.
Используем обученную модель
Обучив параметры, модель умеет предсказывать для новых x, которых не было в данных. Это и есть цель: обобщение на новые объекты.
xs = [1, 2, 3, 4, 5]
ys = [3.1, 4.9, 7.2, 8.8, 11.1]
w, b = 0.0, 0.0
lr = 0.01
n = len(xs)
for _ in range(5000):
dw = sum(2 * (w * x + b - y) * x for x, y in zip(xs, ys)) / n
db = sum(2 * (w * x + b - y) for x, y in zip(xs, ys)) / n
w -= lr * dw
b -= lr * db
print("Обученная модель: y =", round(w, 3), "* x +", round(b, 3))
for x_new in [6, 10]:
print(f"Предсказание для x={x_new}: y =", round(w * x_new + b, 3))
Вывод:
Обученная модель: y = 1.99 * x + 1.05 Предсказание для x=6: y = 12.99 Предсказание для x=10: y = 20.95
Что мы только что сделали
Это полный цикл машинного обучения в миниатюре, и в нём узнаются все темы курса:
| Шаг | Какая математика |
| Предсказание w·x + b | линейная комбинация (векторы) |
| Функция потерь MSE | функция многих переменных (анализ) |
| Градиент dw, db | частные производные, правило цепочки |
| Шаг w −= lr·dw | градиентный спуск (оптимизация) |
Итог
- Линейная регрессия подбирает w и b прямой y = w·x + b под данные.
- Градиенты: по w — среднее
2·err·x, по b — среднее2·err. - Градиентный спуск шагает против градиента и сам находит лучшие параметры.
- Обученная модель предсказывает y для новых x — это и есть машинное обучение.