← Назад к вопросам
Как проводить валидацию моделей?
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:
- Раздели данные со стратификацией (если нужна)
- Используй K-Fold CV для надёжной оценки
- Проверяй на переобучение
- Выбирай метрику под задачу
- Документируй результаты
Частые ошибки:
- Утечка данных (использование информации из тестового набора)
- Отсутствие стратификации при дисбалансе
- Оценка на одном только тесте (нужна CV)
- Игнорирование временной зависимости в рядах