Кросс-валидация
В практике машинного обучения мы часто сталкиваемся с ключевой проблемой: как оценить качество модели? Вы можете подумать об использовании части данных для обучения модели, а затем другой части невиданных данных для проверки её производительности. Эта идея совершенно верна, но как мы можем оценить модель более надёжно и стабильно? ЭтоПерекрёстная проверка— основная проблема, которую нужно решить.
Проще говоря, перекрёстная проверка — это статистический метод, который оценивает способность модели к обобщению (то есть её способность работать с новыми данными) путём многократного разбиения набора данных. Это как устроить для модели пробный экзамен, используя несколько различных пробных вариантов (подмножеств данных), чтобы проверить её истинный уровень, избегая ошибочной оценки из-за случайности одного экзамена.
Эта статья поможет вам глубоко понять принципы, распространённые методы и ключевую роль перекрёстной проверки в оптимизации моделей и инженерии.
Зачем нужна перекрёстная проверка?
Прежде чем углубляться в технические детали, давайте сначала поймём её необходимость с помощью аналогии.
Представьте, что вы студент, которому предстоит сдавать важный экзамен по математике. Существуют два метода оценки вашего уровня:
- Метод A (простое разбиение): Учитель случайным образом выбирает 10 вопросов из банка заданий, чтобы вы прошли пробный экзамен, а затем использует этот результат для прогнозирования вашей итоговой оценки за экзамен.
- Метод B (перекрёстная проверка): Учитель делит банк заданий на 5 частей. В первый раз используйте части 2, 3, 4, 5 для вашего обучения, а часть 1 — для тестирования; во второй раз используйте части 1, 3, 4, 5 для вашего обучения, а часть 2 — для тестирования... Повторите это 5 раз. Наконец, возьмите среднее из 5 результатов тестирования, чтобы оценить вас.
Какой метод более надёжен? Очевидно,Метод B。
- Метод AРиск метода A заключается в следующем: если выбранные 10 вопросов окажутся типами, в которых вы сильны, результат пробного экзамена будет завышен, что приведёт к излишнему оптимизму относительно вашего истинного уровня; напротив, если выбранные вопросы окажутся вашими слепыми зонами, результат будет слишком низким, что приведёт к излишнему пессимизму. Результаты оценки сильно колеблются и нестабильны.
- Метод BБлагодаря множеству различных комбинаций обучения/тестирования он позволяет вам познакомиться с различными типами вопросов в банке вопросов, а полученная средняя оценка более репрезентативна для вашего общего, стабильного уровня и позволяет точнее предсказать результат итогового экзамена.
В машинном обучении:
- банк вопросов— это нашполный набор данных。
- студент— это модель, которую мы хотим обучить,модель машинного обучения。
- оценка за пробный экзамен— этометрика оценки модели(например, точность, среднеквадратичная ошибка и т. д.).
- итоговый экзамен— это будущаяпроизводительность модели на реальных, не встречавшихся ранее данных.
Основная цель кросс-валидации — обеспечить для способности модели к обобщениюболее устойчивую и более несмещённую оценку, тем самым помогая нам выполнять более надёжный выбор модели, настройку параметров и оценку производительности.
Распространённые методы кросс-валидации
Кросс-валидация имеет несколько методов реализации, подходящих для разных данных и сценариев. Ниже представлены наиболее распространённые из них.
1. Отложенная валидация
Это самый простой и интуитивно понятный метод.
Пример
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
# Создаём данные для запуска
# 200 образцов, 4 признака, бинарная классификация
np.random.seed(42)
X = np.random.randn(200, 4)
y = (X[:, 0] + X[:, 1] * 0.7 - X[:, 2] * 0.4 > 0).astype(int)
# 1. Разделение обучающей и тестовой выборок (7:3)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42
)
# 2. Обучение модели
model = RandomForestClassifier(random_state=42)
model.fit(X_train, y_train)
# 3. Оценка на тестовой выборке
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(f"Точность модели: {accuracy:.4f}")
Результат вывода:
模型准确率: 0.8833
Описание процесса:

-
Advantages:Простота и скорость, низкие вычислительные затраты.
- Недостатки:Результат оценки сильно зависит от одного случайного разбиения. Если разбиение неудачное, результат оценки может быть нерепрезентативным. При этом, поскольку тестовый набор используется только один раз, данные используются недостаточно эффективно.
2. K-фолдовая кросс-валидация
В настоящее время это наиболее часто используемый и стандартный метод кросс-валидации.
Принцип:Разделить набор данныхравномернослучайным образом на K взаимоисключающих подмножеств (называемых фолдами).
В каждом эксперименте по очереди используйте одно подмножество в качестве тестового набора, а остальные K-1 подмножеств — в качестве обучающего набора. Этот процесс повторяется K раз, гарантируя, что каждое подмножество один раз используется как тестовый набор. В итоге мы получаем K оценок и берём их среднее как финальную оценку производительности модели.
Пример
from sklearn.model_selection import cross_val_score, KFold
from sklearn.linear_model import LogisticRegression
# Создать примерные данные для запуска
# 100 образцов, 4 признака, метки бинарной классификации
np.random.seed(42)
X = np.random.randn(100, 4)
y = (X[:, 0] + X[:, 1] * 0.5 > 0).astype(int)
# 1. Инициализация модели
model = LogisticRegression(max_iter=1000)
# 2. Определить разделитель K-фолдовой кросс-валидации (K=5)
kfold = KFold(n_splits=5, shuffle=True, random_state=42)
# 3. Выполнить кросс-валидацию
scores = cross_val_score(model, X, y, cv=kfold, scoring='accuracy')
print(f"Точность по фолдам: {scores}")
print(f"Средняя точность: {scores.mean():.4f} (+/- {scores.std() * 2:.4f})")
每次折叠的准确率: [0.9 0.95 1. 0.95 1. ] 平均准确率: 0.9600 (+/- 0.0748)
Схема процесса при K=5:

Как выбрать значение K?
- Распространённые значения: 5 или 10. Это эмпирический компромисс.
- Меньшее значение K (например, 3): обучающая выборка больше, но число оценок невелико, и дисперсия оценки может быть большой.
- Большее значение K (например, 10 или 20): оценка более стабильна (малая дисперсия), но обучающая выборка каждый раз ближе к исходному набору данных, что может внести более оптимистичное смещение оценки, а вычислительные затраты значительно возрастают.
- Крайний случай K = N (число примеров): это leave-one-out, тестирование каждый раз только на одном примере. Оценка наиболее несмещённая, но вычислительные затраты чрезвычайно высоки; обычно используется только для очень малых наборов данных.
Преимущества:данные используются полностью, а результаты оценки стабильны и надёжны.
Недостатки:вычислительные затраты в K раз выше, чем у метода hold-out.
3. Стратифицированная K-фолдовая кросс-валидация
Это важный вариант K-фолдовой кросс-валидации, особенно подходящий длязадач классификацииСреднийс несбалансированным распределением классовнаборов данных.
Решаемая проблема:В обычной K-фолдовой кросс-валидации случайное разбиение может привести к тому, что доля определённого класса в некоторых фолдах будет сильно отличаться от исходного набора данных. Например, если в наборе данных 90% положительного класса и 10% отрицательного класса, случайное разбиение на 5 фолдов может дать фолд, полностью состоящий из положительного класса, без отрицательного класса, что делает оценку на этом фолде бессмысленной.
Стратифицированная K-фолдовая кросс-валидация гарантирует, что при разбиении доля образцов каждого класса в каждом фолде остаётся согласованной с общей долей в исходном наборе данных.
Пример
# Использование почти такое же, как у KFold, только замените разделитель
stratified_kfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=stratified_kfold, scoring='accuracy')
Для задач классификации, особенно когда классы несбалансированы,отдавайте предпочтение использованиюStratifiedKFold。
4. Разделение временных рядов
Дляданных временных рядов, порядок данных критически важен (данные завтрашнего дня зависят от сегодняшних и вчерашних). Мы не можем случайно перемешивать данные; мы должны сохранять временной порядок.
Принцип таков: обучающая выборка всегда состоит из более ранних по времени данных, а тестовая выборка — из непосредственно следующих за ними данных. По мере увеличения числа фолдов окно обучающей выборки постоянно расширяется.
Пример
from sklearn.model_selection import TimeSeriesSplit
# Создание запускаемых данных временного ряда
# 100 временных точек, 2 признака
np.random.seed(42)
X = np.random.randn(100, 2)
# Пример TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5)
for train_index, test_index in tscv.split(X):
print(f"Диапазон индексов обучающего набора: {train_index)
print(f"Диапазон индексов тестового набора: {test_index)
print("---")
Вывод:
训练集索引范围: 0 到 19 测试集索引范围: 20 到 35 --- 训练集索引范围: 0 到 35 测试集索引范围: 36 到 51 --- 训练集索引范围: 0 到 51 测试集索引范围: 52 到 67 --- 训练集索引范围: 0 到 67 测试集索引范围: 68 到 83 --- 训练集索引范围: 0 到 83 测试集索引范围: 84 到 99 ---
Применение кросс-валидации в инженерии моделей
Кросс-валидация — это не только инструмент оценки, но и ключевая часть процесса оптимизации и инженерии моделей.
Применение 1: Выбор и сравнение моделей
Когда нам нужно выбрать среди нескольких моделей-кандидатов (таких как линейная регрессия, дерево решений, машина опорных векторов), мы не можем использовать тестовый набор для выбора (иначе тестовый набор становится частью процесса обучения и происходит "утечка" информации). Правильный подход таков:
- Для каждой модели-кандидата наобучающем набореиспользуйте кросс-валидацию, чтобы получить оценку её производительности.
- Сравните средние оценки этих результатов кросс-валидации и выберите модель с наивысшей оценкой.
- Наконец, переобучите выбранную модель на всём обучающем наборе и используйтенезависимый тестовый набор, чтобы выполнить финальную однократную оценку, указав эту оценку в качестве итоговой производительности модели.
Пример
from sklearn.model_selection import cross_val_score, StratifiedKFold, train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
# Создайте запускаемые данные для классификации
# 200 образцов, 4 признака, бинарная классификация
np.random.seed(42)
X = np.random.randn(200, 4)
y = (X[:, 0] + X[:, 1] * 0.8 - X[:, 2] * 0.3 > 0).astype(int)
# Разделите обучающий набор / тестовый набор
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42, stratify=y
)
# Определите модели
models = {
'Logistic Regression': LogisticRegression(max_iter=1000),
'SVM': SVC(),
'Decision Tree': DecisionTreeClassifier()
}
# Стратифицированная K-кратная кросс-валидация
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
results = {}
# Оценка с помощью кросс-валидации
for name, model in models.items():
scores = cross_val_score(model, X_train, y_train, cv=cv, scoring='accuracy')
results[name] = scores.mean()
print(f"{name} средняя точность: {scores.mean():.4f}")
# Выбор лучшей модели
best_model_name = max(results, key=results.get)
print(f"\nПо результатам кросс-валидации лучшая модель: {best_model_name}")
# Финальное обучение и оценка на тестовой выборке
best_model = models[best_model_name]
best_model.fit(X_train, y_train)
final_score = best_model.score(X_test, y_test)
print(f"Итоговая точность лучшей модели на независимом тестовом наборе: {final_score:.4f}")
Вывод:
Logistic Regression 平均准确率: 0.9533 SVM 平均准确率: 0.9400 Decision Tree 平均准确率: 0.8467 根据交叉验证,最佳模型是: Logistic Regression 最佳模型在独立测试集上的最终准确率: 1.0000
Приложение 2: Настройка гиперпараметров
Гиперпараметры — это параметры, которые необходимо задать перед обучением модели (например, количество деревьев в случайном лесуn_estimators, штрафной коэффициент SVMC). Процесс поиска наилучшей комбинации гиперпараметров называетсянастройкой гиперпараметров, а кросс-валидация — его стандартным методом оценки.
Наиболее часто используемый метод — этокросс-валидация с поиском по сетке。
Пример
from sklearn.model_selection import GridSearchCV, train_test_split
from sklearn.ensemble import RandomForestClassifier
# Создайте запускаемые данные для классификации
# 300 образцов, 5 признаков, бинарная классификация
np.random.seed(42)
X = np.random.randn(300, 5)
y = (X[:, 0] * 0.6 + X[:, 1] * 0.4 - X[:, 2] > 0).astype(int)
# Разделите обучающий набор / тестовый набор
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42, stratify=y
)
# 1. Сетка параметров
param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [None, 10, 20],
'min_samples_split': [2, 5, 10]
}
# 2. Базовая модель
rf = RandomForestClassifier(random_state=42)
# 3. GridSearchCV
grid_search = GridSearchCV(
estimator=rf,
param_grid=param_grid,
cv=5,
scoring='accuracy',
n_jobs=-1
)
# 4. Поиск по сетке (только обучающая выборка)
grid_search.fit(X_train, y_train)
# 5. Оптимальные параметры и оценка
print(f"Лучшие параметры: {grid_search.best_params_}")
print(f"Лучшая оценка кросс-валидации: {grid_search.best_score_:.4f}")
# 6. Оценка на тестовой выборке
best_rf_model = grid_search.best_estimator_
test_accuracy = best_rf_model.score(X_test, y_test)
print(f"Точность настроенной модели на тестовой выборке: {test_accuracy:.4f}")
Вывод:
最佳参数: {'max_depth': None, 'min_samples_split': 2, 'n_estimators': 100}
最佳交叉验证分数: 0.9067
调优后模型在测试集上的准确率: 0.9467
Ключевые моменты: GridSearchCVВнутри кросс-валидация уже завершена. Она дополнительно разбивает обучающую выборкуX_trainна меньшие "обучающие подмножества" и "валидационные подмножества", чтобы оценить параметры, поэтому то, что мы передаём,X_trainэквивалентно всему "банку вопросов", тогда какX_test— это «окончательный экзаменационный вопрос», который никогда не участвовал в процессе настройки и используется для финальной проверки.
Практические упражнения и резюме
Практическое упражнение
- Базовая реализация: Используйте
sklearnвстроенный набор данных Iris, соответственно используяtrain_test_splitиcross_val_score(K=5) для обучения и оценкиKNeighborsClassifier, и сравнить показатели, полученные двумя методами оценки. - Сравнение моделей: На одном и том же наборе данных используйте кросс-валидацию, чтобы сравнить
SVC、RandomForestClassifierиGradientBoostingClassifierпроизводительность моделей. - Настройка параметров: Для
SVCмодели составьте сетку параметров (включаяCиgamma), используйтеGridSearchCVчтобы найти оптимальные параметры.
Сводка ключевых моментов
- Цель кросс-валидации: получить более устойчивую и надёжную оценку способности модели к обобщению.
- Основной метод:K-кратная кросс-валидацияявляется золотым стандартом; для задач классификации отдавайте предпочтение использованиюстратифицированной K-кратной кросс-валидации。
- Ключевое различие:
- Обучающая выборка/валидационная выборка: Используется для обучения модели иоценки в процессе разработки(например, выбора моделей, настройки параметров). Кросс-валидация происходит на этом этапе.
- Тестовый набор: Только после завершения всей разработки используется дляитогового однократного отчёта о производительности. Он должен быть строго изолирован и никогда не использоваться для принятия каких-либо решений.
- Инженерная роль: Кросс-валидация — это мост, соединяющий разработку модели (обучение, настройку) и оценку модели (тестирование), и краеугольный камень для обеспечения качества модели, предотвращения переобучения и достижения надёжного выбора и оптимизации модели.