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

Как проводить валидацию моделей?

2.0 Middle🔥 271 комментариев
#Машинное обучение#Метрики и оценка моделей

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

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

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

Валидация моделей машинного обучения

Правильная валидация критична для оценки реальной производительности модели. Существуют различные стратегии в зависимости от типа данных и задачи.

1. Базовое разбиение: Train-Validation-Test Split

from sklearn.model_selection import train_test_split

# Базовое разбиение 60-20-20
X_train, X_temp, y_train, y_temp = train_test_split(
    X, y, 
    test_size=0.4, 
    random_state=42
)

X_val, X_test, y_val, y_test = train_test_split(
    X_temp, y_temp, 
    test_size=0.5, 
    random_state=42
)

# Для классификации со стратификацией
X_train, X_temp, y_train, y_temp = train_test_split(
    X, y, 
    test_size=0.4, 
    random_state=42,
    stratify=y
)

X_val, X_test, y_val, y_test = train_test_split(
    X_temp, y_temp, 
    test_size=0.5, 
    random_state=42,
    stratify=y_temp
)

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

Самый надёжный метод для оценки качества:

from sklearn.model_selection import KFold, StratifiedKFold, cross_val_score
from sklearn.ensemble import RandomForestClassifier

model = RandomForestClassifier(random_state=42)

# Стратифицированный K-Fold для классификации
skfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

# Расчёт метрик на всех фолдах
scores = cross_val_score(
    model, 
    X, y, 
    cv=skfold, 
    scoring="roc_auc",
    n_jobs=-1
)

print(f"Scores per fold: {scores}")
print(f"Mean: {scores.mean():.3f} (+/- {scores.std():.3f})")

# Детальное исследование
from sklearn.model_selection import cross_validate

scoring = {
    "accuracy": "accuracy",
    "f1": "f1",
    "roc_auc": "roc_auc",
    "precision": "precision"
}

cv_results = cross_validate(
    model, X, y, 
    cv=skfold, 
    scoring=scoring,
    return_train_score=True
)

results_df = pd.DataFrame(cv_results)
print(results_df[["test_accuracy", "test_f1", "test_roc_auc"]])

3. Временные ряды: Time Series Cross-Validation

Специальная стратегия для данных с временной зависимостью:

from sklearn.model_selection import TimeSeriesSplit

tscv = TimeSeriesSplit(n_splits=5)

# Визуализация разбиения
fig, ax = plt.subplots(figsize=(12, 4))
for i, (train_idx, test_idx) in enumerate(tscv.split(X)):
    ax.scatter(train_idx, [i] * len(train_idx), c="blue", label="train" if i == 0 else "")
    ax.scatter(test_idx, [i] * len(test_idx), c="red", label="test" if i == 0 else "")

ax.set_xlabel("Sample index")
ax.set_ylabel("Fold")
ax.set_title("Time Series Cross-Validation")
ax.legend()
plt.tight_layout()
plt.show()

# Обучение и валидация
scores = cross_val_score(
    model, X, y, 
    cv=tscv, 
    scoring="mse"
)
print(f"Time Series CV Scores: {scores}")

4. Метрики оценки для классификации

from sklearn.metrics import (
    accuracy_score, precision_score, recall_score, f1_score,
    roc_auc_score, confusion_matrix, classification_report,
    roc_curve, auc, precision_recall_curve
)

y_pred = model.predict(X_test)
y_pred_proba = model.predict_proba(X_test)[:, 1]

# Основные метрики
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")
print(f"Precision: {precision_score(y_test, y_pred):.3f}")
print(f"Recall: {recall_score(y_test, y_pred):.3f}")
print(f"F1-Score: {f1_score(y_test, y_pred):.3f}")
print(f"ROC-AUC: {roc_auc_score(y_test, y_pred_proba):.3f}")

# Матрица ошибок
print("\nConfusion Matrix:")
print(confusion_matrix(y_test, y_pred))

# Детальный отчёт
print("\nClassification Report:")
print(classification_report(y_test, y_pred))

# ROC-кривая
fpr, tpr, thresholds = roc_curve(y_test, y_pred_proba)
plt.figure(figsize=(8, 6))
plt.plot(fpr, tpr, label=f"ROC Curve (AUC={auc(fpr, tpr):.3f})")
plt.plot([0, 1], [0, 1], "k--", label="Random")
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.legend()
plt.show()

# Precision-Recall кривая
precision, recall, _ = precision_recall_curve(y_test, y_pred_proba)
plt.figure(figsize=(8, 6))
plt.plot(recall, precision, label="PR Curve")
plt.xlabel("Recall")
plt.ylabel("Precision")
plt.legend()
plt.show()

5. Метрики для регрессии

from sklearn.metrics import (
    mean_squared_error, mean_absolute_error,
    r2_score, mean_absolute_percentage_error
)

y_pred = model.predict(X_test)

mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
mae = mean_absolute_error(y_test, y_pred)
mape = mean_absolute_percentage_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)

print(f"MSE: {mse:.4f}")
print(f"RMSE: {rmse:.4f}")
print(f"MAE: {mae:.4f}")
print(f"MAPE: {mape:.4f}")
print(f"R2-Score: {r2:.4f}")

# Анализ остатков
residuals = y_test - y_pred
plt.figure(figsize=(10, 4))

plt.subplot(1, 2, 1)
plt.scatter(y_pred, residuals)
plt.axhline(y=0, c="r", linestyle="--")
plt.xlabel("Predicted values")
plt.ylabel("Residuals")

plt.subplot(1, 2, 2)
plt.hist(residuals, bins=30, edgecolor="black")
plt.xlabel("Residuals")
plt.ylabel("Frequency")

plt.tight_layout()
plt.show()

6. Проверка переобучения (Overfitting)

def detect_overfitting(model, X_train, X_val, y_train, y_val):
    """Проверить переобучение"""
    
    train_score = model.score(X_train, y_train)
    val_score = model.score(X_val, y_val)
    
    overfitting_gap = train_score - val_score
    
    print(f"Train Score: {train_score:.3f}")
    print(f"Val Score: {val_score:.3f}")
    print(f"Overfitting Gap: {overfitting_gap:.3f}")
    
    if overfitting_gap > 0.1:
        print("WARNING: Strong overfitting detected!")
    elif overfitting_gap > 0.05:
        print("CAUTION: Mild overfitting detected")
    else:
        print("OK: No significant overfitting")
    
    return overfitting_gap

# Визуализация кривых обучения
def plot_learning_curves(model, X_train, X_val, y_train, y_val):
    """Плот кривых обучения"""
    train_sizes = np.linspace(0.1, 1.0, 10)
    train_scores = []
    val_scores = []
    
    for size in train_sizes:
        split_idx = int(len(X_train) * size)
        model.fit(X_train[:split_idx], y_train[:split_idx])
        
        train_scores.append(model.score(X_train[:split_idx], y_train[:split_idx]))
        val_scores.append(model.score(X_val, y_val))
    
    plt.figure(figsize=(10, 6))
    plt.plot(train_sizes, train_scores, "o-", label="Train Score")
    plt.plot(train_sizes, val_scores, "s-", label="Val Score")
    plt.xlabel("Training Set Size")
    plt.ylabel("Score")
    plt.legend()
    plt.grid()
    plt.show()

plot_learning_curves(model, X_train, X_val, y_train, y_val)

7. Nested Cross-Validation (для подбора параметров)

from sklearn.model_selection import GridSearchCV

outer_cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
inner_cv = StratifiedKFold(n_splits=3, shuffle=True, random_state=42)

param_grid = {
    "max_depth": [5, 10, 15],
    "n_estimators": [100, 200]
}

nested_scores = []

for train_idx, test_idx in outer_cv.split(X, y):
    X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
    y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]
    
    grid_search = GridSearchCV(
        RandomForestClassifier(random_state=42),
        param_grid,
        cv=inner_cv,
        scoring="roc_auc"
    )
    grid_search.fit(X_train, y_train)
    
    score = grid_search.score(X_test, y_test)
    nested_scores.append(score)

print(f"Nested CV Scores: {nested_scores}")
print(f"Mean: {np.mean(nested_scores):.3f} (+/- {np.std(nested_scores):.3f})")

Ключевые рекомендации

Выбор стратегии валидации:

  • Малый датасет (<1000): 5-10 Fold CV
  • Средний датасет (1K-100K): Stratified K-Fold
  • Большой датасет (>100K): Train-Val-Test split
  • Временные ряды: Time Series CV
  • Дисбаланс классов: Stratified K-Fold

Типичный pipeline:

  1. Раздели данные со стратификацией (если нужна)
  2. Используй K-Fold CV для надёжной оценки
  3. Проверяй на переобучение
  4. Выбирай метрику под задачу
  5. Документируй результаты

Частые ошибки:

  • Утечка данных (использование информации из тестового набора)
  • Отсутствие стратификации при дисбалансе
  • Оценка на одном только тесте (нужна CV)
  • Игнорирование временной зависимости в рядах
Как проводить валидацию моделей? | PrepBro