Leave-One-Out Cross-Validation (LOOCV) — это особый вид перекрестной проверки, где количество фолдов равно количеству наблюдений в наборе данных.
Проще говоря: “Каждый объект по очереди становится тестовой выборкой, а все остальные объекты — обучающей выборкой”.
Основная идея
LOOCV — это крайний случай k-Fold Cross-Validation, где k = n (n — количество наблюдений в датасете).
Алгоритм:
- Из n наблюдений одно откладывается для тестирования
- Модель обучается на оставшихся (n-1) наблюдениях
- Модель тестируется на одном отложенном наблюдении
- Процесс повторяется n раз, пока каждое наблюдение не побывает в роли тестового
- Результаты всех n итераций усредняются
Формальное описание
Для датасета с n наблюдениями: LOOCV = n-Fold Cross-Validation
Количество итераций: n Размер тестовой выборки в каждой итерации: 1 наблюдение Размер обучающей выборки в каждой итерации: (n-1) наблюдений
Пример
Представьте, что у вас есть маленький датасет из 5 пациентов:
# Данные: [Пациент1, Пациент2, Пациент3, Пациент4, Пациент5]
data = [A, B, C, D, E]LOOCV выполнит 5 итераций:
| Итерация | Обучающая выборка | Тестовая выборка |
|---|---|---|
| 1 | [B, C, D, E] | A |
| 2 | [A, C, D, E] | B |
| 3 | [A, B, D, E] | C |
| 4 | [A, B, C, E] | D |
| 5 | [A, B, C, D] | E |
После всех итераций мы получаем 5 оценок качества, которые усредняем.
Преимущества LOOCV
1. Максимальное использование данных для обучения
- В каждой итерации для обучения используется (n-1) наблюдений — почти весь датасет
- Это особенно важно при очень малом количестве данных
2. Низкое смещение (Low Bias)
- Поскольку модель обучается почти на всех данных, оценка имеет очень маленькое смещение
- Модель видит практически всю генеральную совокупность при обучении
3. Отсутствие случайности
- Не зависит от случайного разбиения на фолды
- Результат детерминирован (при фиксированных данных всегда одинаковый)
4. Идеально для очень маленьких датасетов
- Когда n < 100, обычная k-Fold может быть нестабильной
- LOOCV дает наиболее надежную оценку
Недостатки и ограничения
1. Вычислительная сложность
- Нужно обучить модель n раз
- Для больших датасетов это неприемлемо
- Пример: для n = 10,000 нужно 10,000 раз обучить модель
2. Высокая дисперсия оценки
- Тестовая выборка всего из 1 наблюдения
- Выбросы сильно влияют на оценку
- Разные итерации могут давать очень разные результаты
3. Неэффективность для больших датасетов
- При большом n выигрыш в качестве оценки незначителен
- Вычислительные затраты непропорционально высоки
Сравнение с k-Fold Cross-Validation
| Параметр | LOOCV | k-Fold CV |
|---|---|---|
| Количество итераций | n | k |
| Размер тестовой выборки | 1 | n/k |
| Размер обучающей выборки | n-1 | n-(n/k) |
| Смещение | Очень низкое | Низкое |
| Дисперсия | Высокая | Средняя |
| Вычислительная стоимость | Высокая (O(n)) | Умеренная (O(k)) |
| Рекомендуется | Маленькие датасеты (n < 100) | Большинство случаев |
Когда использовать LOOCV?
✅ Рекомендуется:
- Очень маленькие датасеты (n < 100)
- Критически важные задачи, где нужна максимально точная оценка
- Когда вычислительные ресурсы не ограничены
- Для моделей с быстрым обучением
❌ Не рекомендуется:
- Большие датасеты (n > 1000)
- Медленные алгоритмы (нейросети, SVM)
- Когда важна скорость вычислений
- Для приблизительной оценки
Практический пример
Задача: Предсказание стоимости домов в маленьком городке Данные: 50 домов (очень мало данных)
from sklearn.model_selection import LeaveOneOut, cross_val_score
from sklearn.linear_model import LinearRegression
from sklearn.datasets import make_regression
import numpy as np
# Создаем синтетические данные (50 наблюдений)
X, y = make_regression(n_samples=50, n_features=3, noise=0.1, random_state=42)
# Создаем LOOCV стратегию
loo = LeaveOneOut()
# Создаем модель
model = LinearRegression()
# Запускаем перекрестную проверку
scores = cross_val_score(model, X, y, cv=loo, scoring='neg_mean_squared_error')
# Переводим отрицательную MSE в положительную
mse_scores = -scores
print(f"Количество итераций: {len(mse_scores)}")
print(f"MSE для каждой итерации (первые 5): {mse_scores[:5]}")
print(f"Средний MSE: {mse_scores.mean():.3f}")
print(f"Стандартное отклонение MSE: {mse_scores.std():.3f}")Математическая оптимизация для линейной регрессии
Для линейной регрессии существует формула, позволяющая вычислить LOOCV без фактического обучения n моделей:
PRESS (Predicted Residual Sum of Squares) statistic:
[ PRESS = \sum_{i=1}^{n} \left( \frac{y_i - \hat{y}i}{1 - h{ii}} \right)^2 ]
где ( h_{ii} ) — диагональные элементы матрицы влияния (hat matrix)
Это позволяет получить оценку LOOCV за одно вычисление!
# Быстрый расчет LOOCV для линейной регрессии
def loocv_linear_regression(X, y):
n = X.shape[0]
X_with_intercept = np.column_stack([np.ones(n), X]) # Добавляем столбец для intercept
# Вычисляем матрицу влияния
H = X_with_intercept @ np.linalg.pinv(X_with_intercept.T @ X_with_intercept) @ X_with_intercept.T
h_diag = np.diag(H) # Диагональные элементы
# Обучаем модель на всех данных
model = LinearRegression().fit(X, y)
y_pred = model.predict(X)
# Вычисляем PRESS statistic
residuals = y - y_pred
press = np.sum((residuals / (1 - h_diag)) ** 2)
return press / n # Средний MSE LOOCV
fast_loocv_mse = loocv_linear_regression(X, y)
print(f"Быстрый LOOCV MSE: {fast_loocv_mse:.3f}")Рекомендации по использованию
- n < 50: LOOCV — лучший выбор
- 50 < n < 100: LOOCV или 10-Fold в зависимости от вычислительных ресурсов
- n > 100: Используйте 5-Fold или 10-Fold
- n > 1000: 5-Fold обычно достаточно
Краткий итог
- LOOCV — это перекрестная проверка, где каждый объект по очереди становится тестовой выборкой
- Преимущества: Минимальное смещение, максимальное использование данных
- Недостатки: Высокая вычислительная стоимость, высокая дисперсия
- Идеален для очень маленьких датасетов
- Не практичен для больших датасетов и сложных моделей
LOOCV — это “золотой стандарт” для оценки моделей на маленьких датасетах, но его следует использовать с умом из-за вычислительной сложности.