← Назад к вопросам

Что такое переобучение? Какие есть способы борьбы с ним?

2.0 Middle🔥 282 комментариев
#Машинное обучение

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Переобучение (Overfitting): диагностика и методы борьбы

Переобучение — это ситуация, когда модель слишком хорошо подгоняется к тренировочным данным и теряет способность обобщаться на новые данные. Модель запоминает частности тренировочного набора вместо того, чтобы изучать общие закономерности.

Признаки переобучения

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.model_selection import train_test_split

# Создание данных с истинной зависимостью + шум
np.random.seed(42)
X = np.linspace(0, 10, 50).reshape(-1, 1)
y_true = np.sin(X.ravel())
y = y_true + np.random.normal(0, 0.3, len(y_true))  # добавляем шум

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

fig, axes = plt.subplots(1, 3, figsize=(15, 4))

# 1. Недообучение (Underfitting)
model_under = LinearRegression()
model_under.fit(X_train, y_train)
y_pred_train_under = model_under.predict(X_train)
y_pred_test_under = model_under.predict(X_test)

train_error_under = np.mean((y_train - y_pred_train_under)**2)
test_error_under = np.mean((y_test - y_pred_test_under)**2)

axes[0].scatter(X_train, y_train, alpha=0.5, label='Тренировка')
axes[0].scatter(X_test, y_test, alpha=0.5, label='Тест')
axes[0].plot(X, model_under.predict(X), 'r-', label='Модель', linewidth=2)
axes[0].set_title(f'UNDERFITTING\nTrain MSE={train_error_under:.3f}, Test MSE={test_error_under:.3f}')
axes[0].legend()

# 2. Хорошее обучение (Good Fit)
poly = PolynomialFeatures(degree=3)
X_train_poly = poly.fit_transform(X_train)
X_test_poly = poly.transform(X_test)
X_poly = poly.transform(X)

model_good = LinearRegression()
model_good.fit(X_train_poly, y_train)
y_pred_train_good = model_good.predict(X_train_poly)
y_pred_test_good = model_good.predict(X_test_poly)

train_error_good = np.mean((y_train - y_pred_train_good)**2)
test_error_good = np.mean((y_test - y_pred_test_good)**2)

axes[1].scatter(X_train, y_train, alpha=0.5, label='Тренировка')
axes[1].scatter(X_test, y_test, alpha=0.5, label='Тест')
X_sorted = np.sort(X, axis=0)
axes[1].plot(X_sorted, model_good.predict(poly.transform(X_sorted)), 'g-', label='Модель', linewidth=2)
axes[1].set_title(f'GOOD FIT\nTrain MSE={train_error_good:.3f}, Test MSE={test_error_good:.3f}')
axes[1].legend()

# 3. Переобучение (Overfitting)
poly_over = PolynomialFeatures(degree=15)  # очень высокая степень!
X_train_poly_over = poly_over.fit_transform(X_train)
X_test_poly_over = poly_over.transform(X_test)
X_poly_over = poly_over.transform(X)

model_over = LinearRegression()
model_over.fit(X_train_poly_over, y_train)
y_pred_train_over = model_over.predict(X_train_poly_over)
y_pred_test_over = model_over.predict(X_test_poly_over)

train_error_over = np.mean((y_train - y_pred_train_over)**2)
test_error_over = np.mean((y_test - y_pred_test_over)**2)

axes[2].scatter(X_train, y_train, alpha=0.5, label='Тренировка')
axes[2].scatter(X_test, y_test, alpha=0.5, label='Тест')
X_sorted = np.sort(X, axis=0)
axes[2].plot(X_sorted, model_over.predict(poly_over.transform(X_sorted)), 'r-', label='Модель', linewidth=2)
axes[2].set_title(f'OVERFITTING\nTrain MSE={train_error_over:.3f}, Test MSE={test_error_over:.3f}')
axes[2].legend()

plt.tight_layout()
plt.show()

print("Анализ:")
print(f"Underfitting: Train ошибка большая, Test ошибка большая")
print(f"Good Fit: Train ошибка маленькая, Test ошибка маленькая (примерно равны)")
print(f"Overfitting: Train ошибка очень маленькая, Test ошибка БОЛЬШАЯ (gap!)")
print(f"\nГап между Train и Test:")
print(f"  Underfitting: {test_error_under - train_error_under:.3f}")
print(f"  Good Fit: {test_error_good - train_error_good:.3f}")
print(f"  Overfitting: {test_error_over - train_error_over:.3f}")

Методы борьбы с переобучением

1. Regularization (Регуляризация)

from sklearn.linear_model import Ridge, Lasso, ElasticNet

# L2 регуляризация (Ridge)
model_ridge = Ridge(alpha=1.0)  # alpha контролирует силу регуляризации
model_ridge.fit(X_train_poly_over, y_train)

# L1 регуляризация (Lasso)
model_lasso = Lasso(alpha=0.01)
model_lasso.fit(X_train_poly_over, y_train)

# ElasticNet (комбинация L1 и L2)
model_elastic = ElasticNet(alpha=0.1, l1_ratio=0.5)
model_elastic.fit(X_train_poly_over, y_train)

print("Регуляризация:")
print(f"OLS Train MSE: {np.mean((y_train - y_pred_train_over)**2):.4f}")
print(f"OLS Test MSE: {np.mean((y_test - y_pred_test_over)**2):.4f}")
print(f"Ridge Test MSE: {model_ridge.score(X_test_poly_over, y_test):.4f}")
print(f"Lasso Test MSE: {model_lasso.score(X_test_poly_over, y_test):.4f}")

2. Cross-Validation (Кросс-валидация)

from sklearn.model_selection import cross_val_score, KFold

kf = KFold(n_splits=5, shuffle=True, random_state=42)
X_all = np.vstack([X_train, X_test])
y_all = np.concatenate([y_train, y_test])

# Трансформация для полиномиальной модели
poly = PolynomialFeatures(degree=15)
X_all_poly = poly.fit_transform(X_all)

scores = cross_val_score(
    Ridge(alpha=1.0),
    X_all_poly, y_all,
    cv=kf,
    scoring='neg_mean_squared_error'
)

print(f"CV MSE scores: {-scores}")
print(f"Mean CV MSE: {-scores.mean():.4f} (+/- {scores.std():.4f})")

3. Early Stopping (Ранняя остановка)

from tensorflow.keras.callbacks import EarlyStopping
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense

# Создание модели
model = Sequential([
    Dense(64, activation='relu', input_shape=(X_train_poly_over.shape[1],)),
    Dense(32, activation='relu'),
    Dense(1)
])

model.compile(optimizer='adam', loss='mse')

# Early Stopping
early_stop = EarlyStopping(
    monitor='val_loss',
    patience=10,  # остановка, если val_loss не улучшается 10 эпох
    restore_best_weights=True
)

history = model.fit(
    X_train_poly_over, y_train,
    validation_data=(X_test_poly_over, y_test),
    epochs=100,
    callbacks=[early_stop],
    verbose=0
)

print(f"Обучение остановилось на эпохе {len(history.history['loss'])}")
print(f"Final Train Loss: {history.history['loss'][-1]:.4f}")
print(f"Final Val Loss: {history.history['val_loss'][-1]:.4f}")

4. Dropout (Выключение нейронов)

from tensorflow.keras.layers import Dropout

model_with_dropout = Sequential([
    Dense(128, activation='relu', input_shape=(X_train_poly_over.shape[1],)),
    Dropout(0.3),  # выключаем 30% нейронов во время обучения
    Dense(64, activation='relu'),
    Dropout(0.2),
    Dense(32, activation='relu'),
    Dense(1)
])

model_with_dropout.compile(optimizer='adam', loss='mse')
model_with_dropout.fit(X_train_poly_over, y_train, epochs=50, verbose=0)

print(f"Model with Dropout Test MSE: {model_with_dropout.evaluate(X_test_poly_over, y_test)}")

5. Data Augmentation (Увеличение датасета)

from sklearn.utils import resample

# Создание синтетических примеров через бутстрэп
X_augmented = X_train.copy()
y_augmented = y_train.copy()

for _ in range(5):  #增加 5 раз
    indices = np.random.choice(len(X_train), size=len(X_train), replace=True)
    X_augmented = np.vstack([X_augmented, X_train[indices]])
    y_augmented = np.concatenate([y_augmented, y_train[indices]])

print(f"Original train size: {len(X_train)}")
print(f"Augmented train size: {len(X_augmented)}")

6. Feature Selection (Отбор признаков)

from sklearn.feature_selection import SelectKBest, f_regression

# Оставляем только самые важные признаки
selector = SelectKBest(score_func=f_regression, k=5)
X_train_selected = selector.fit_transform(X_train_poly_over, y_train)
X_test_selected = selector.transform(X_test_poly_over)

model_selected = Ridge(alpha=1.0)
model_selected.fit(X_train_selected, y_train)

print(f"Selected features MSE: {model_selected.score(X_test_selected, y_test):.4f}")
print(f"Number of features: {X_train_selected.shape[1]}")

7. Ensemble Methods (Ансамблевые методы)

from sklearn.ensemble import BaggingRegressor, RandomForestRegressor, GradientBoostingRegressor

# Bagging
model_bagging = BaggingRegressor(n_estimators=10, random_state=42)
model_bagging.fit(X_train_poly_over, y_train)
bagging_score = model_bagging.score(X_test_poly_over, y_test)

# Random Forest
model_rf = RandomForestRegressor(n_estimators=100, max_depth=5, random_state=42)
model_rf.fit(X_train[:, 0:1], y_train)  # используем исходный X
rf_score = model_rf.score(X_test[:, 0:1], y_test)

# Gradient Boosting
model_gb = GradientBoostingRegressor(learning_rate=0.1, max_depth=3, random_state=42)
model_gb.fit(X_train[:, 0:1], y_train)
gb_score = model_gb.score(X_test[:, 0:1], y_test)

print(f"Bagging Score: {bagging_score:.4f}")
print(f"Random Forest Score: {rf_score:.4f}")
print(f"Gradient Boosting Score: {gb_score:.4f}")

Чеклист для диагностики переобучения

def check_overfitting(y_train, y_pred_train, y_test, y_pred_test):
    from sklearn.metrics import mean_squared_error
    
    train_mse = mean_squared_error(y_train, y_pred_train)
    test_mse = mean_squared_error(y_test, y_pred_test)
    
    gap = test_mse - train_mse
    gap_ratio = test_mse / train_mse if train_mse > 0 else float('inf')
    
    print(f"Train MSE: {train_mse:.4f}")
    print(f"Test MSE: {test_mse:.4f}")
    print(f"Gap: {gap:.4f}")
    print(f"Test/Train Ratio: {gap_ratio:.2f}")
    
    if gap < 0.01 and gap_ratio < 1.05:
        print("\nВывод: Хорошее обучение (Good Fit)")
    elif gap > 0.1 and gap_ratio > 1.5:
        print("\nВывод: ПЕРЕОБУЧЕНИЕ (Overfitting)")
    elif train_mse > 0.1 and test_mse > 0.1:
        print("\nВывод: НЕДООБУЧЕНИЕ (Underfitting)")
    else:
        print("\nВывод: Модель в норме")

check_overfitting(y_train, y_pred_train_over, y_test, y_pred_test_over)

Практическая стратегия

print("СТРАТЕГИЯ БОРЬБЫ С ПЕРЕОБУЧЕНИЕМ:")
print("="*50)
print("\n1. ДИАГНОСТИКА:")
print("   - Строй learning curves (train vs val loss)")
print("   - Смотри гап между train и test ошибками")
print("\n2. ЕСЛИ ПЕРЕОБУЧЕНИЕ:")
print("   Сначала:")
print("   - Добавь больше тренировочных данных")
print("   - Упрости модель (уменьши количество параметров)")
print("   Потом:")
print("   - Добавь регуляризацию (L1/L2)")
print("   - Используй Dropout или Early Stopping")
print("   - Применяй ensemble методы")
print("\n3. ВАЖНО:")
print("   - Всегда используй отдельное test множество!")
print("   - Не подстраивай гиперпараметры на test")
print("   - Используй кросс-валидацию для надежной оценки")

Переобучение — одна из главных проблем в ML. Правильная диагностика и применение методов регуляризации — критические навыки для успешного построения моделей.

Что такое переобучение? Какие есть способы борьбы с ним? | PrepBro