Линейная регрессия, Ridge и Lasso
ЧерновикМатериалы дорабатываются и будут дополняться.
Чему научимся
Представьте, что у вас есть два завода,
Сейчас мы увидели очень упрощённый принцип работы линейной модели. Нам повезло, что кто-то уже сказал, какие коэффициенты стоит использовать. А что, если коэффициенты неизвестны? Как подобрать их так, чтобы предсказания были точными, и где вам поможет линейная модель? Именно об этом мы поговорим в этой главе.
После занятия вы сможете:
- Вычислять прогноз линейной модели, остатки и MSE и находить прямую по нескольким точкам.
- Объяснять, как Ridge и Lasso изменяют веса и почему только Lasso обнуляет их.
- Выбирать силу штрафа по проверочной части, масштабируя признаки внутри конвейера.
Время и подготовка: ориентир 8–10 часов с практикой. Нужны график прямой, среднее значение, квадрат числа и основы Python. Основной маршрут — прямая, остатки, MSE и библиотечное обучение; выводы формул и опыт с 12 признаками можно пройти как углубление после первого запуска кода.
План: линейная комбинация → функция потерь MSE → метод наименьших квадратов → где модель ломается → Ridge и Lasso → выбор силы штрафа → код.
Все данные занятия — учебные синтетические: мы придумали их или сгенерировали случайным образом. Выводы о реальном мире по ним делать нельзя.
Регрессия и классификация
В занятии 1.2 мы угадывали класс героя: «воин» или «маг». Ответ выбирался из короткого списка — это задача классификации. Число стульев, цена квартиры или балл за тест — числа, и между любыми двумя ответами есть промежуточные. Предсказание числа называют задачей регрессии. Линейная регрессия решает именно её; к классификации линейные модели вернутся в занятии 1.6.
Линейная комбинация
Линейные модели лучше всего использовать тогда, когда целевая переменная
где
В нашем игрушечном примере нам уже выдали правильные коэффициенты: мы просто подставляли признаки в уравнение и получали ответ.
Но в реальной жизни подобрать правильные коэффициенты — отдельная задача. Обучение линейной модели — это процесс подбора таких коэффициентов, чтобы модель как можно точнее предсказывала целевое значение.
Дальше будем называть коэффициенты при признаках весами и обозначать
Начнём с одного признака. Школьник готовился к пробным тестам и записал, сколько часов занимался (
Почему предсказать значение точно невозможно
Мы хотим определять значение как можно точнее, но почему же нельзя предсказать его абсолютно точно? В реальном мире огромную роль играют погрешности: неточность датчиков, ошибки людей при разметке данных, а также всё, что влияет на ответ, но не попало в признаки. Балл за тест зависит не только от часов подготовки, но и от сна, настроения и вопросов в варианте. Чем больше данных мы собираем, тем точнее можно оценить коэффициенты, но случайный разброс отдельного ответа никуда не исчезнет.
Как же оценить, насколько хорошо модель справляется с задачей? Для этого существуют функции потерь (loss-функции), которые показывают, насколько сильно модель ошиблась в своих предсказаниях. Обучая линейную модель, мы подбираем коэффициенты так, чтобы функция потерь стала минимальной.
Функция потерь MSE
Ошибку на одном объекте называют остатком:
Сложить остатки нельзя:
Квадрат делает две вещи. Во-первых, все слагаемые становятся неотрицательными. Во-вторых, он наказывает за большие проступки гораздо сильнее, чем за маленькие. Представьте учителя, который за опоздание на 1 минуту даёт 1 штрафное очко, на 2 минуты — 4 очка, а на 5 минут — уже 25. Пять опозданий по минуте обойдутся в 5 очков, а одно на пять минут — в 25. Так и MSE: модели выгоднее немного ошибиться на многих объектах, чем сильно — на одном.
Слева — лучшая прямая
Метод наименьших квадратов
Основной смысл: мы подбираем прямую, чтобы сумма квадратов вертикальных ошибок была минимальной. Формулу для одного признака ниже полезно проверить по таблице; её вывод не нужен для применения LinearRegression в библиотеке.
На уроках физики в лабораторных работах вы наносили измерения на график и проводили прямую «на глаз» так, чтобы точки лежали как можно ближе к ней. Метод наименьших квадратов (МНК) делает то же самое строго: ищет
Для одного признака ответ даёт готовая формула. Через
Посчитаем для наших точек:
| произведение | |||||
|---|---|---|---|---|---|
| 1 | 3 | −1,5 | −2 | 3 | 2,25 |
| 2 | 5 | −0,5 | 0 | 0 | 0,25 |
| 3 | 4 | 0,5 | −1 | −0,5 | 0,25 |
| 4 | 8 | 1,5 | 3 | 4,5 | 2,25 |
Сумма произведений
Для многих признаков веса находят из системы
Сделаем один шаг из
Соберём всё вместе. Обучение линейной модели — это круг из четырёх шагов: прогноз, остаток, потери, поправка весов. На схеме он показан для точки
Градиентный спуск проходит этот круг много раз, понемногу меняя
Где линейная регрессия ломается
Выбросы. Вспомните штраф за опоздание: одна большая ошибка весит как много маленьких. Поэтому одна странная точка заметно поворачивает прямую. На рисунке ниже слева наклон падает с
Почти одинаковые признаки (мультиколлинеарность). Пусть
Зачем нужны Ridge и Lasso
Здесь достаточно сначала понять общую идею: штраф не добавляют к готовому прогнозу, а учитывают при подборе весов. Ridge обычно сжимает веса, Lasso может сделать некоторые ровно нулевыми. Формулы для одного признака и мягкий порог ниже — углубление, которое объясняет разницу точнее.
Обе проблемы связаны с весами: выброс тянет их в свою сторону, а при похожих признаках веса неустойчивы и могут вырасти до больших значений разных знаков. Идея регуляризации — добавить к ошибке плату за большие веса. Тогда модель увеличит вес, только если это действительно заметно уменьшает ошибку.
Ridge: штраф на квадраты весов
Ridge-регрессия (штраф
Число
Для нашего примера с одним признаком получается простая формула
Lasso: штраф на модули весов
Lasso (штраф
Для нашего примера
Слева по горизонтали отложен вес
Сведём три способа подбора весов в одну шпаргалку. Столбики на ней схематичные: они показывают характер изменений, а не веса из наших данных.
Как применять Lasso и выбирать силу штрафа
Следующий опыт с 12 признаками показывает, как работает регуляризация на более широкой таблице. Для первого прохода можно читать выводы и выполнить готовый листинг; самостоятельный разбор всех коэффициентов оставьте после освоения конвейера StandardScaler + модель.
Сравним модели на учебных данных с известной истиной: 12 признаков, ответ
Пути коэффициентов показывают, как веса меняются при росте силы штрафа (в scikit-learn она называется alpha). У Ridge все линии плавно стремятся к нулю. У Lasso лишние признаки исчезают раньше полезных, а последними остаются
В этом и состоит главное применение Lasso — отбор признаков. Нулевой вес означает, что признак не участвует в прогнозе. Его можно не собирать и не хранить, а модель становится короче и понятнее. Ненулевые веса при стандартизованных признаках можно сравнивать по модулю: чем больше модуль, тем сильнее признак влияет на прогноз. Но при почти одинаковых признаках Lasso часто оставляет только один из них, и какой именно — зависит от выборки. Поэтому нулевой вес не доказывает, что признак бесполезен сам по себе.
Силу штрафа подбирают по проверочной части: пробуют сетку значений и выбирают то, при котором ошибка на validation минимальна. Тест используют один раз, в самом конце. Учтите, что в scikit-learn alpha определён у двух моделей по-разному: для Ridge alpha у Ridge и Lasso нельзя.
Масштаб. Штраф одинаково давит на все веса, а вес зависит от единиц измерения: признак в граммах вместо килограммов получит вес в 1000 раз меньше и почти не будет оштрафован. Поэтому числовые признаки стандартизуют по формуле make_pipeline): при обучении он находит
МНК почти весь вес alpha; у обеих моделей со штрафом ошибка на проверке ниже, чем у МНК.
Повторим на Python
Начнём с ручного примера из четырёх точек: формулы МНК, один шаг градиентного спуска и формулы Ridge и Lasso для одного признака.
import numpy as np
x = np.array([1., 2., 3., 4.])
y = np.array([3., 5., 4., 8.])
xc, yc = x - x.mean(), y - y.mean()
sxy, sxx = (xc * yc).sum(), (xc ** 2).sum()
w = sxy / sxx
b = y.mean() - w * x.mean()
e = y - (w * x + b)
print("w, b:", w, b)
print("остатки:", e.round(2), "сумма:", e.sum().round(10))
print("MSE прямой:", (e ** 2).mean(), " MSE среднего:", ((y - y.mean()) ** 2).mean())
# один шаг градиентного спуска из w = b = 0
d = 0 * x + 0 - y
w1 = 0 - 0.05 * 2 * np.mean(d * x)
b1 = 0 - 0.05 * 2 * np.mean(d)
print("после шага:", w1, b1)
# один признак: Ridge и Lasso по формулам этого занятия
print("Ridge, lambda = 5:", sxy / (sxx + 5))
for lam in (4, 14):
print("Lasso, lambda =", lam, ":", np.sign(sxy) * max(abs(sxy) - lam / 2, 0) / sxx)
Получим
Теперь учебный набор с 12 признаками. Генератор случайных чисел зафиксирован числом 7, поэтому результаты повторяются. Делим строки на train, validation и test по порядку.
from sklearn.linear_model import LinearRegression, Ridge, Lasso
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error
rng = np.random.default_rng(7)
X = rng.normal(size=(90, 12))
X[:, 3] = X[:, 0] + 0.2 * rng.normal(size=90) # почти копия первого признака
true_w = np.array([3, -2, 1.5, 0, 0, 0, 0, 0, 0, 0, 0, 0.0])
y = 5 + X @ true_w + 2.0 * rng.normal(size=90)
X_train, X_val, X_test = X[:30], X[30:60], X[60:]
y_train, y_val, y_test = y[:30], y[30:60], y[60:]
def rmse(y_true, y_pred):
return mean_squared_error(y_true, y_pred) ** 0.5
Для каждой модели строим конвейер «масштаб → модель». Для Ridge и Lasso перебираем значения alpha и оставляем то, при котором ошибка на validation минимальна. Тест в выборе не участвует.
alphas = np.logspace(-3, 3, 40)
best = {"МНК": make_pipeline(StandardScaler(), LinearRegression()).fit(X_train, y_train)}
for name, model_class in (("Ridge", Ridge), ("Lasso", Lasso)):
scores = []
for alpha in alphas:
model = make_pipeline(StandardScaler(), model_class(alpha=alpha))
model.fit(X_train, y_train)
scores.append(rmse(y_val, model.predict(X_val)))
best_alpha = alphas[int(np.argmin(scores))]
best[name] = make_pipeline(StandardScaler(), model_class(alpha=best_alpha)).fit(X_train, y_train)
print(name, "alpha =", round(best_alpha, 3), " validation RMSE =", round(min(scores), 2))
for name, model in best.items():
print(name, "test RMSE =", round(rmse(y_test, model.predict(X_test)), 2))
После выбора считаем ошибку на тесте один раз. В этом учебном наборе получим примерно
for name, model in best.items():
coef = model[-1].coef_
print(f"{name:6}", coef.round(2), " нулей:", int((coef == 0).sum()))
print("истина", true_w)
У Lasso 7 нулей из 12: ненулевыми остались
X_big = X_train.copy()
X_big[:, 2] *= 100
X_val_big = X_val.copy()
X_val_big[:, 2] *= 100
plain = Ridge(alpha=5).fit(X_train, y_train)
plain_big = Ridge(alpha=5).fit(X_big, y_train)
plain_gap = np.abs(plain.predict(X_val) - plain_big.predict(X_val_big)).max()
print("без масштаба, разница прогнозов:", round(plain_gap, 2))
scaled = make_pipeline(StandardScaler(), Ridge(alpha=5)).fit(X_train, y_train)
scaled_big = make_pipeline(StandardScaler(), Ridge(alpha=5)).fit(X_big, y_train)
scaled_gap = np.abs(scaled.predict(X_val) - scaled_big.predict(X_val_big)).max()
print("с масштабом, разница прогнозов:", round(scaled_gap, 2))
Без стандартизации смена единиц меняет прогнозы Ridge примерно на
Попробуйте сами
- В ручном примере замените
на и вычислите по формулам вес Ridge. Затем найдите наименьшее , при котором Lasso даёт . Проверьте кодом. Для самопроверки: и . - В листинге с 12 признаками обучите конвейер со
StandardScalerиLasso(alpha=3)и посчитайте ненулевые веса. Затем уменьшитеalphaдо0.01. Объясните, как меняется число нулей. Для самопроверки: ненулевых весов 0 и 12. - Постройте свой путь коэффициентов: пройдите по
alphas, обучите Ridge и сохранитеcoef_в список; затем нарисуйте линии черезmatplotlib. Сравните с рисунком выше. Объясните, почему тест нельзя использовать для выбораalpha.
Что стоит унести с собой
Линейная модель — взвешенная сумма признаков, а обучение — подбор весов. МНК выбирает веса с наименьшей MSE; квадрат сильно наказывает большие ошибки. Поэтому модель чувствительна к выбросам, а при похожих признаках её веса неустойчивы. Ridge добавляет штраф на квадраты весов и сжимает их; Lasso штрафует модули и обнуляет часть весов, отбирая признаки. Силу штрафа выбирают по validation, а признаки масштабируют внутри конвейера.
Это могло бы пригодиться и для времени 3D-печати из занятия 1.1: вместо одного постоянного ответа линейная модель связала бы прогноз с высотой и числом цветов. Но такая связь не обязана описывать все особенности фигурки; сравнивать её с другими методами будем на одинаковых новых заказах. Проверьте себя: что показывает остаток? Почему выброс особенно влияет на MSE? Где подбирают силу штрафа и почему не на тесте? Подсказки и ответы — ниже.
Самопроверка
Проверено 0 из 10
Подсказка
От настоящего ответа отнимаем прогноз.
Решение
B.
Подсказка
Сложите остатки
Решение
C. Сумма остатков не отличает точную прямую от прямой с большими ошибками разных знаков. Квадрат делает каждую ошибку неотрицательной и сильнее наказывает большие.
Подсказка
Прогнозы
Решение
A. Остатки
Подсказка
Идём против производной.
Решение
D. Новое
Подсказка
Сравните вклад ошибки
Решение
B. Ошибка
Подсказка
Какая комбинация весов влияет на прогноз, когда
Решение
D. Облако весов вытянуто вдоль линии
Подсказка
Формула
Решение
C.
Подсказка
Сравните
Решение
A. Порог
Подсказка
Вспомните, как Lasso поступает с почти одинаковыми признаками.
Решение
A. Нулевой вес исключает признак из прогноза. Но связь с ответом у признака может быть: из двух почти одинаковых признаков Lasso часто оставляет один, а второй обнуляет, хотя он так же связан с ответом. Ridge веса до нуля не доводит.
Подсказка
Какие части данных можно использовать для настройки?
Решение
C. Подбор по тесту делает итоговую оценку оптимистичной, а масштаб, найденный на всём наборе, переносит сведения из validation и test в обучение. Слишком большой alpha обнуляет почти все веса.