Фаза 02 · урок 10
Компромисс между смещением и дисперсией
Цель урока: Вы обучили модель. У неё есть некоторая ошибка на тестовых данных. Откуда она берётся?
Текущий релиз AlexBred.com: первые 100 уроков русскоязычной программы.
Содержание урока
- Цели обучения
- Проблема
- Концепция
- Смещение: систематическая ошибка
- Дисперсия: чувствительность к обучающим данным
- Разложение
- Сложность модели и ошибка
- Регуляризация как управление смещением и дисперсией
- Двойной спуск: современный взгляд
- Диагностика модели
- Практические стратегии
- Ансамблевые методы и уменьшение дисперсии
- Кривые обучения
- Как строить кривые обучения
- Соберите это
- Шаг 1: сгенерируйте синтетические данные из известной функции
- Шаг 2: бутстреп-выборка и подгонка полинома
- Шаг 3: вычислите разложение на bias^2 и дисперсию
- Шаг 4: кривые обучения
- Шаг 5: перебор регуляризации
- Используйте это
- Кривая валидации: перебор сложности модели
- Кривая обучения: перебор размера обучающего набора
- Кросс-валидация с перебором регуляризации
- Собираем всё вместе: полный рабочий процесс диагностики
- Внедрите это
- Упражнения
- Ключевые термины
- Дополнительное чтение
Каждая ошибка модели происходит из одного из трёх источников: смещения, дисперсии или шума. Управлять можно лишь первыми двумя.
Тип: Изучение Язык: Python Предварительные требования: Фаза 2, уроки 01–09 (основы ML, регрессия, классификация, оценивание) Время: ~75 минут
Цели обучения
- Вывести разложение ожидаемой ошибки предсказания на смещение и дисперсию и объяснить роль неустранимого шума
- Диагностировать высокое смещение или высокую дисперсию модели по закономерностям ошибок на обучении и тесте
- Объяснить, как методы регуляризации (L1, L2, dropout, ранняя остановка) обменивают смещение на дисперсию
- Реализовать эксперименты, визуализирующие компромисс между смещением и дисперсией для моделей возрастающей сложности
Проблема
Вы обучили модель. У неё есть некоторая ошибка на тестовых данных. Откуда она берётся?
Если модель слишком проста (линейная регрессия на изогнутом наборе данных), она будет систематически упускать истинную закономерность. Это смещение. Если модель слишком сложна (полином степени 20 на 15 точках данных), она идеально подгонит обучающие данные, но будет давать совершенно разные предсказания на новых данных. Это дисперсия.
Нельзя одновременно минимизировать и то, и другое при фиксированной ёмкости модели. Уменьшаете смещение — растёт дисперсия. Уменьшаете дисперсию — растёт смещение. Понимание этого компромисса — самый полезный диагностический навык в машинном обучении. Он говорит, сделать ли модель сложнее или проще, получить больше данных или создать более качественные признаки, усилить или ослабить регуляризацию.
Концепция
Смещение: систематическая ошибка
Смещение показывает, насколько среднее предсказание модели отклоняется от истинного значения. Если обучить одну и ту же модель на множестве разных обучающих наборов из одного распределения и усреднить предсказания, смещение — это разрыв между этим средним и истиной.
Высокое смещение означает, что модель слишком жёсткая, чтобы уловить реальную закономерность. Прямая, подогнанная к параболе, всегда будет промахиваться мимо кривой, сколько бы данных вы ни дали. Это недообучение.
High bias (underfitting):
Model always predicts roughly the same wrong thing.
Training error: HIGH
Test error: HIGH
Gap between them: SMALL
Дисперсия: чувствительность к обучающим данным
Дисперсия измеряет, насколько меняются предсказания при обучении на разных подмножествах данных. Если небольшие изменения обучающего набора вызывают большие изменения модели, дисперсия высока.
Высокая дисперсия означает, что модель подгоняет шум в обучающих данных, а не лежащий в основе сигнал. Полином степени 20 пройдёт через каждую обучающую точку, но будет резко колебаться между ними. Это переобучение.
High variance (overfitting):
Model fits training data perfectly but fails on new data.
Training error: LOW
Test error: HIGH
Gap between them: LARGE
Разложение
Для любой точки x ожидаемая ошибка предсказания при квадратичной функции потерь в точности раскладывается так:
Expected Error = Bias^2 + Variance + Irreducible Noise
where:
Bias^2 = (E[f_hat(x)] - f(x))^2
Variance = E[(f_hat(x) - E[f_hat(x)])^2]
Noise = E[(y - f(x))^2] (sigma^2)
f(x)— истинная функцияf_hat(x)— предсказание вашей моделиE[...]— математическое ожидание по разным обучающим наборамy— наблюдаемая метка (истинная функция плюс шум)
Слагаемое шума неустранимо. Ни одна модель не может быть лучше sigma^2 на зашумлённых данных. Ваша задача — найти правильный баланс между bias^2 и дисперсией.
Сложность модели и ошибка
Классическая U-образная кривая:
| Сложность | Смещение | Дисперсия | Суммарная ошибка |
|---|---|---|---|
| Слишком низкая | ВЫСОКОЕ | НИЗКАЯ | ВЫСОКАЯ (недообучение) |
| В самый раз | УМЕРЕННОЕ | УМЕРЕННАЯ | НАИМЕНЬШАЯ |
| Слишком высокая | НИЗКОЕ | ВЫСОКАЯ | ВЫСОКАЯ (переобучение) |
Регуляризация как управление смещением и дисперсией
Регуляризация намеренно увеличивает смещение, чтобы уменьшить дисперсию. Она ограничивает модель, не позволяя ей преследовать шум.
- L2 (Ridge): сжимает все веса в сторону нуля. Сохраняет все признаки, но уменьшает их влияние.
- L1 (Lasso): выталкивает некоторые веса ровно к нулю. Выполняет отбор признаков.
- Dropout: случайно отключает нейроны во время обучения. Заставляет создавать избыточные представления.
- Ранняя остановка: прекращает обучение до того, как модель полностью подгонит обучающие данные.
Сила регуляризации (lambda, доля dropout, число эпох) напрямую управляет вашим положением на кривой смещения–дисперсии. Больше регуляризации означает больше смещения и меньше дисперсии.
Двойной спуск: современный взгляд
Классическая теория говорит: после оптимальной точки большая сложность всегда вредит. Но исследования с 2019 года показали нечто неожиданное. Если продолжать увеличивать ёмкость модели далеко за порог интерполяции (где у модели достаточно параметров, чтобы идеально подогнать обучающие данные), тестовая ошибка может снова уменьшиться.
Феномен «двойного спуска» объясняет, почему сильно переопределённые нейронные сети (с параметров гораздо больше, чем обучающих примеров) всё ещё хорошо обобщают. Классический компромисс смещения–дисперсии не ошибочен, но неполон для современного режима.
Ключевые наблюдения о двойном спуске:
- Он возникает в линейных моделях, деревьях решений и нейронных сетях
- Большее количество данных может даже навредить в области интерполяции (двойной спуск по числу примеров)
- Его может вызвать и большее количество эпох обучения (двойной спуск по эпохам)
- Регуляризация сглаживает пик, но не устраняет его
Почему это происходит? На пороге интерполяции у модели ровно достаточно ёмкости, чтобы подогнать все обучающие точки. Она вынуждена попасть в очень специфическое решение, проходящее через каждую точку, и малые возмущения данных вызывают большие изменения подгонки. Здесь дисперсия достигает пика. За порогом у модели есть много возможных решений, идеально подгоняющих данные. Алгоритм обучения (например, градиентный спуск с неявной регуляризацией) обычно выбирает среди них самое простое. Это неявное смещение в сторону простых решений и объясняет, почему переопределённые модели обобщают.
| Режим | Параметры и примеры | Поведение |
|---|---|---|
| Недоопределённая модель | p << n | Применим классический компромисс |
| Порог интерполяции | p ~ n | Дисперсия достигает пика, тестовая ошибка резко возрастает |
| Переопределённая модель | p >> n | Включается неявная регуляризация, тестовая ошибка падает |
Для практики: если вы используете нейронные сети или большие ансамбли деревьев, не останавливайтесь на пороге интерполяции. Либо оставайтесь значительно ниже него (с явной регуляризацией), либо уходите далеко за него. Худшее место — прямо на пороге.
Диагностика модели
| Симптом | Диагноз | Исправление |
|---|---|---|
| Высокая ошибка на обучении, высокая ошибка на тесте | Смещение | Больше признаков, сложная модель, меньше регуляризации |
| Низкая ошибка на обучении, высокая ошибка на тесте | Дисперсия | Больше данных, регуляризация, более простая модель, dropout |
| Низкая ошибка на обучении, низкая ошибка на тесте | Хорошая подгонка | Выпускайте в продакшен |
| Ошибка обучения уменьшается, тестовая ошибка растёт | Идёт переобучение | Ранняя остановка |
Практические стратегии
Когда проблема — смещение:
- Добавляйте полиномиальные признаки или признаки взаимодействия
- Используйте более гибкую модель (ансамбль деревьев вместо линейной модели)
- Уменьшайте силу регуляризации
- Обучайте дольше (если сходимость ещё не достигнута)
Когда проблема — дисперсия:
- Получите больше обучающих данных
- Используйте бэггинг (случайные леса)
- Усиливайте регуляризацию (больший lambda, больше dropout)
- Выполняйте отбор признаков (удаляйте шумные признаки)
- Используйте кросс-валидацию, чтобы выявить проблему рано
Ансамблевые методы и уменьшение дисперсии
Ансамблевые методы — наиболее практичный инструмент борьбы с дисперсией.
Бэггинг (Bootstrap Aggregating) обучает несколько моделей на разных бутстреп-выборках обучающих данных, а затем усредняет их предсказания. У каждой отдельной модели высокая дисперсия, но у среднего она гораздо ниже. Случайные леса — это бэггинг, применённый к деревьям решений.
Почему это работает математически: если усреднить N независимых предсказаний, каждое с дисперсией sigma^2, дисперсия среднего равна sigma^2 / N. Модели не по-настоящему независимы (они видят похожие данные), поэтому снижение меньше, чем в 1/N раз, но всё равно существенно.
Бустинг уменьшает смещение, строя модели последовательно: каждая новая модель сосредотачивается на ошибках ансамбля к этому моменту. Основные примеры — градиентный бустинг и AdaBoost. Бустинг может переобучиться, если добавить слишком много моделей, поэтому нужны ранняя остановка или регуляризация.
| Метод | Основной эффект | Изменение смещения | Изменение дисперсии |
|---|---|---|---|
| Бэггинг | Уменьшает дисперсию | Без изменения | Уменьшается |
| Бустинг | Уменьшает смещение | Уменьшается | Может увеличиться |
| Стекинг | Уменьшает оба | Зависит от мета-модели | Зависит от базовых моделей |
| Dropout | Неявный бэггинг | Небольшое увеличение | Уменьшается |
Практическое правило: если базовая модель имеет высокую дисперсию (глубокие деревья, полиномы высокой степени), используйте бэггинг. Если у базовой модели высокое смещение (неглубокие пни, простые линейные модели), используйте бустинг.
Кривые обучения
Кривые обучения отображают ошибку обучения и валидации как функцию размера обучающего набора. Это самый практичный диагностический инструмент. В отличие от одиночного сравнения train/test, они показывают траекторию модели и говорят, помогут ли дополнительные данные.
Как их читать:
| Сценарий | Ошибка обучения | Ошибка валидации | Разрыв | Что это значит | Что делать |
|---|---|---|---|---|---|
| Высокое смещение | Высокая | Высокая | Малый | Модель не может уловить закономерность | Больше признаков, сложная модель, меньше регуляризации |
| Высокая дисперсия | Низкая | Высокая | Большой | Модель запоминает обучающие данные | Больше данных, регуляризация, более простая модель |
| Хорошая подгонка | Умеренная | Умеренная | Малый | Модель хорошо обобщает | Выпускайте в продакшен |
| Высокая дисперсия, улучшается | Низкая | Уменьшается с новыми данными | Сокращается | Проблему дисперсии могут исправить данные | Соберите больше данных |
| Высокое смещение, плато | Высокая | Высокая и плоская | Малый и плоский | Больше данных НЕ поможет | Измените архитектуру модели |
Критическое наблюдение: если обе кривые вышли на плато, разрыв мал, но обе ошибки высоки, больше данных бесполезно. Нужна модель лучше. Если разрыв большой и всё ещё сокращается, больше данных поможет.
Как строить кривые обучения
Есть два подхода:
Подход 1: меняйте размер обучающего набора при фиксированной модели. Сохраняйте модель и гиперпараметры постоянными. Обучайте на всё больших подмножествах обучающих данных. Измеряйте ошибку обучения и валидации при каждом размере. Это стандартная кривая обучения.
Подход 2: меняйте сложность модели при фиксированных данных. Сохраняйте данные постоянными. Перебирайте параметр сложности (степень полинома, глубину дерева, число слоёв). Измеряйте ошибку обучения и валидации для каждой сложности. Это кривая валидации, которая напрямую показывает компромисс смещения–дисперсии.
Оба подхода дополняют друг друга. Первый говорит, помогут ли дополнительные данные. Второй — поможет ли другая модель. Запускайте оба, прежде чем решать следующий шаг.
bias-variance
Соберите это
Код в code/bias_variance.py запускает полный эксперимент по разложению на смещение и дисперсию. Вот пошаговый подход.
Шаг 1: сгенерируйте синтетические данные из известной функции
Мы используем f(x) = sin(1.5x) + 0.5x с гауссовским шумом. Знание истинной функции позволяет вычислить точные смещение и дисперсию.
def true_function(x):
return np.sin(1.5 * x) + 0.5 * x
def generate_data(n_samples=30, noise_std=0.5, x_range=(-3, 3), seed=None):
rng = np.random.RandomState(seed)
x = rng.uniform(x_range[0], x_range[1], n_samples)
y = true_function(x) + rng.normal(0, noise_std, n_samples)
return x, y
Шаг 2: бутстреп-выборка и подгонка полинома
Для каждой степени полинома мы извлекаем много бутстреп-обучающих наборов, подгоняем полином и записываем предсказания на фиксированной тестовой сетке. Это даёт распределение предсказаний в каждой тестовой точке.
def fit_polynomial(x_train, y_train, degree, lam=0.0):
X = np.column_stack([x_train ** d for d in range(degree + 1)])
if lam > 0:
penalty = lam * np.eye(X.shape[1])
penalty[0, 0] = 0
w = np.linalg.solve(X.T @ X + penalty, X.T @ y_train)
else:
w = np.linalg.lstsq(X, y_train, rcond=None)[0]
return w
Мы подгоняем модель на 200 разных бутстреп-выборках. Каждая бутстреп-выборка взята из одного базового распределения, но содержит разные точки.
Шаг 3: вычислите разложение на bias^2 и дисперсию
Имея 200 наборов предсказаний в каждой тестовой точке, можно вычислить разложение прямо по определению:
mean_pred = predictions.mean(axis=0)
bias_sq = np.mean((mean_pred - y_true) ** 2)
variance = np.mean(predictions.var(axis=0))
total_error = np.mean(np.mean((predictions - y_true) ** 2, axis=1))
mean_pred— оценка E[f_hat(x)] по бутстреп-выборкамbias_sq— квадрат разрыва между средним предсказанием и истинойvariance— средний разброс предсказаний по бутстреп-выборкамtotal_errorдолжен приблизительно равнятьсяbias^2 + variance + noise
Шаг 4: кривые обучения
Кривые обучения перебирают размер обучающего набора при фиксированной сложности модели. Они показывают, ограничена ли ваша модель данными или ёмкостью.
def demo_learning_curves():
sizes = [10, 15, 20, 30, 50, 75, 100, 150, 200, 300]
degree = 5
for n in sizes:
train_errors = []
test_errors = []
for seed in range(50):
x_train, y_train = generate_data(n_samples=n, seed=seed * 100)
w = fit_polynomial(x_train, y_train, degree)
train_pred = predict_polynomial(x_train, w)
train_mse = np.mean((train_pred - y_train) ** 2)
test_pred = predict_polynomial(x_test, w)
test_mse = np.mean((test_pred - y_test) ** 2)
train_errors.append(train_mse)
test_errors.append(test_mse)
# Average over runs gives the learning curve point
Для модели с высокой дисперсией (степень 5 при малом количестве данных) вы увидите:
- Ошибка обучения начинает низко и растёт, так как больше данных затрудняет запоминание
- Тестовая ошибка начинает высоко и снижается, когда модель получает больше сигнала
- Разрыв сокращается с добавлением данных
Для модели с высоким смещением (степень 1) обе ошибки быстро сходятся к одному высокому значению, и больше данных не помогает.
Шаг 5: перебор регуляризации
Код также содержит demo_regularization_sweep(), которая фиксирует полином высокой степени (степени 15) и перебирает силу Ridge-регуляризации от 0.001 до 100. Это показывает компромисс с другого угла: вместо варьирования сложности модели мы меняем силу ограничения.
def demo_regularization_sweep():
alphas = [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 50.0, 100.0]
for alpha in alphas:
results = bias_variance_decomposition([15], lam=alpha)
r = results[15]
print(f"alpha={alpha:.3f} bias={r['bias_sq']:.4f} var={r['variance']:.4f}")
При малом alpha полином степени 15 почти не ограничен. Дисперсия доминирует, потому что модель преследует шум в каждой бутстреп-выборке. При большом alpha штраф настолько силён, что модель фактически становится почти постоянной функцией. Доминирует смещение. Оптимальный alpha находится между этими крайностями.
Это та же U-кривая, что и при изменении степени полинома, но управляемая непрерывной ручкой вместо дискретной. На практике регуляризация — предпочтительный способ контролировать компромисс, потому что она допускает тонкую настройку, не меняя набор признаков.
Используйте это
sklearn предоставляет learning_curve и validation_curve, чтобы автоматизировать эти диагностики без написания бутстреп-циклов.
Кривая валидации: перебор сложности модели
from sklearn.model_selection import validation_curve
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import Ridge
degrees = list(range(1, 16))
train_scores_all = []
val_scores_all = []
for d in degrees:
pipe = make_pipeline(PolynomialFeatures(d), Ridge(alpha=0.01))
train_scores, val_scores = validation_curve(
pipe, X, y, param_name="polynomialfeatures__degree",
param_range=[d], cv=5, scoring="neg_mean_squared_error"
)
train_scores_all.append(-train_scores.mean())
val_scores_all.append(-val_scores.mean())
Это напрямую даёт кривую компромисса смещения–дисперсии. Там, где оценка валидации особенно плоха относительно оценки обучения, доминирует дисперсия. Там, где плохи обе, доминирует смещение.
Кривая обучения: перебор размера обучающего набора
from sklearn.model_selection import learning_curve
pipe = make_pipeline(PolynomialFeatures(5), Ridge(alpha=0.01))
train_sizes, train_scores, val_scores = learning_curve(
pipe, X, y, train_sizes=np.linspace(0.1, 1.0, 10),
cv=5, scoring="neg_mean_squared_error"
)
train_mse = -train_scores.mean(axis=1)
val_mse = -val_scores.mean(axis=1)
Постройте график train_mse и val_mse относительно train_sizes. Его форма расскажет всё о вашей модели.
Кросс-валидация с перебором регуляризации
from sklearn.model_selection import cross_val_score
alphas = [0.001, 0.01, 0.1, 1.0, 10.0, 100.0]
for alpha in alphas:
pipe = make_pipeline(PolynomialFeatures(10), Ridge(alpha=alpha))
scores = cross_val_score(pipe, X, y, cv=5, scoring="neg_mean_squared_error")
print(f"alpha={alpha:>7.3f} MSE={-scores.mean():.4f} +/- {scores.std():.4f}")
Здесь перебирается сила регуляризации для фиксированной сложности модели. Вы увидите тот же компромисс: малый alpha означает высокую дисперсию, большой alpha — высокое смещение.
Собираем всё вместе: полный рабочий процесс диагностики
На практике эти диагностики выполняют по очереди:
- Обучите модель. Вычислите ошибку обучения и тестовую ошибку.
- Если обе высоки, у вас проблема смещения. Переходите к шагу 4.
- Если ошибка обучения низка, а тестовая высока, у вас проблема дисперсии. Постройте кривую обучения, чтобы увидеть, помогут ли дополнительные данные. Если нет — регуляризируйте.
- Постройте кривую валидации, перебирая главный параметр сложности. Найдите оптимальную точку.
- В оптимальной точке постройте кривую обучения. Если разрыв всё ещё велик, вам нужны больше данных или регуляризация.
- Попробуйте Ridge/Lasso с разными значениями alpha с помощью
cross_val_score. Выберите alpha, при котором ошибка кросс-валидации минимальна.
Для большинства табличных наборов данных это занимает 10–15 минут вычислений и экономит часы угадывания.
Внедрите это
Этот урок создаёт: outputs/prompt-model-diagnostics.md
Упражнения
-
Запустите разложение с
noise_std=0(без шума). Что происходит с неустранимым слагаемым ошибки? Меняется ли оптимальная сложность? -
Увеличьте размер обучающего набора с 30 до 300. Как это влияет на компоненту дисперсии? Сдвигается ли оптимальная степень полинома?
-
Добавьте в эксперимент L2-регуляризацию (Ridge-регрессию). Для фиксированного полинома высокой степени (степень 15) переберите lambda от 0 до 100. Постройте
bias^2и дисперсию как функции lambda. -
Измените истинную функцию с полинома на
sin(x). Как меняется разложение на смещение и дисперсию? Остаётся ли чёткая оптимальная степень? -
Реализуйте простую обёртку bootstrap aggregating (бэггинг): обучите 10 моделей на бутстреп-выборках и усредните предсказания. Покажите, что это уменьшает дисперсию, почти не увеличивая смещение.
Ключевые термины
| Термин | Как обычно говорят | Что это действительно означает |
|---|---|---|
| Смещение | «Модель слишком проста» | Систематическая ошибка из-за неверных предположений. Разрыв между средним предсказанием модели и истиной. |
| Дисперсия | «Модель переобучается» | Ошибка из-за чувствительности к обучающим данным. Насколько предсказания меняются между разными обучающими наборами. |
| Неустранимая ошибка | «Шум в данных» | Ошибка от случайности в истинном процессе генерации данных. Ни одна модель не может её устранить. |
| Недообучение | «Учится недостаточно» | У модели высокое смещение. Она упускает реальную закономерность даже на обучающих данных. |
| Переобучение | «Запоминает данные» | У модели высокая дисперсия. Она подгоняет шум обучающих данных, который не обобщается. |
| Регуляризация | «Ограничение модели» | Добавление штрафа для снижения сложности модели: обмен смещения на меньшую дисперсию. |
| Двойной спуск | «Больше параметров может помочь» | Тестовая ошибка снова уменьшается, когда ёмкость модели далеко превышает порог интерполяции. |
| Сложность модели | «Насколько модель гибка» | Способность модели подгонять произвольные закономерности; задаётся архитектурой, признаками или регуляризацией. |
Дополнительное чтение
- Hastie, Tibshirani, Friedman: Elements of Statistical Learning, гл. 7 — исчерпывающее изложение разложения на смещение и дисперсию
- Belkin et al., Reconciling modern machine learning practice and the bias-variance trade-off (2019) — статья о двойном спуске
- Nakkiran et al., Deep Double Descent (2019) — двойной спуск по эпохам и числу примеров
- Scott Fortmann-Roe: Understanding the Bias-Variance Tradeoff — наглядное объяснение
Источник: Bias-Variance Tradeoff 02.09 — Оценка моделей · Фаза 2 — Основы машинного обучения · Полный каталог · 02.11 — Ансамблевые методы