Кросс-валидация
Как оценить модель надёжнее, когда одного разбиения train/test мало.
Кросс-валидация (cross-validation) — способ оценки, при котором данные несколько раз по-разному делят на обучение и проверку, а результаты усредняют.
Проблема единственного разбиения
Один train/test split даёт одну оценку — и она зависит от того, какие именно примеры попали в тест. Не повезло с разбиением — оценка получится случайно завышенной или заниженной. На небольших данных это особенно болезненно: 20% теста — это совсем немного примеров.
Идея k-fold
Кросс-валидация решает проблему так. Данные делят на k равных частей (folds, «складок»). Затем проводят k экспериментов: в каждом одна часть служит тестом, остальные k−1 — обучением. Так каждая часть ровно один раз побывает тестом. Итоговую оценку усредняют по всем k экспериментам.
При k = 5: учим на 80%, проверяем на 20% — но пять раз, каждый раз на другой пятой части. Затем берём среднее пяти оценок — оно гораздо устойчивее одиночного замера.
# 5-fold кросс-валидация: смотрим, какие части идут в тест
data = list(range(1, 11)) # 10 объектов
k = 5
fold_size = len(data) // k
for i in range(k):
start = i * fold_size
end = start + fold_size
test = data[start:end]
train = data[:start] + data[end:]
print(f"Эксперимент {i+1}: test={test}, train={train}")
Вывод:
Эксперимент 1: test=[1, 2], train=[3, 4, 5, 6, 7, 8, 9, 10] Эксперимент 2: test=[3, 4], train=[1, 2, 5, 6, 7, 8, 9, 10] Эксперимент 3: test=[5, 6], train=[1, 2, 3, 4, 7, 8, 9, 10] Эксперимент 4: test=[7, 8], train=[1, 2, 3, 4, 5, 6, 9, 10] Эксперимент 5: test=[9, 10], train=[1, 2, 3, 4, 5, 6, 7, 8]
Видно: каждый объект ровно один раз оказался в тесте. Модель обучают и оценивают пять раз, а финальное качество — среднее пяти оценок.
Зачем это нужно
- Надёжнее. Среднее по нескольким разбиениям меньше зависит от удачи.
- Экономит данные. Каждый пример успевает побывать и в обучении, и в проверке — ценно, когда данных мало.
- Удобно для подбора настроек. Сравнивая модели или значения
kв kNN, по кросс-валидации выбирают устойчиво лучший вариант.
Плата за надёжность — время: модель обучается k раз вместо одного.
На практике
# Иллюстрация (scikit-learn в браузере не запустится)
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
model = LogisticRegression()
scores = cross_val_score(model, X, y, cv=5) # 5 складок
print("Оценки по складкам:", scores)
print("Среднее качество:", scores.mean())
Итог
- Кросс-валидация делит данные на k частей и k раз меняет, какая часть — тест.
- Итоговая оценка — среднее по всем k экспериментам, она устойчивее одиночной.
- Каждый пример успевает побывать и в обучении, и в проверке — экономно на малых данных.
- Цена — обучение модели k раз вместо одного.