Leave-One-Out Cross-Validation (LOOCV) — это особый вид перекрестной проверки, где количество фолдов равно количеству наблюдений в наборе данных.

Проще говоря: “Каждый объект по очереди становится тестовой выборкой, а все остальные объекты — обучающей выборкой”.


Основная идея

LOOCV — это крайний случай k-Fold Cross-Validation, где k = n (n — количество наблюдений в датасете).

Алгоритм:

  1. Из n наблюдений одно откладывается для тестирования
  2. Модель обучается на оставшихся (n-1) наблюдениях
  3. Модель тестируется на одном отложенном наблюдении
  4. Процесс повторяется n раз, пока каждое наблюдение не побывает в роли тестового
  5. Результаты всех 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

ПараметрLOOCVk-Fold CV
Количество итерацийnk
Размер тестовой выборки1n/k
Размер обучающей выборкиn-1n-(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}")

Рекомендации по использованию

  1. n < 50: LOOCV — лучший выбор
  2. 50 < n < 100: LOOCV или 10-Fold в зависимости от вычислительных ресурсов
  3. n > 100: Используйте 5-Fold или 10-Fold
  4. n > 1000: 5-Fold обычно достаточно

Краткий итог

  • LOOCV — это перекрестная проверка, где каждый объект по очереди становится тестовой выборкой
  • Преимущества: Минимальное смещение, максимальное использование данных
  • Недостатки: Высокая вычислительная стоимость, высокая дисперсия
  • Идеален для очень маленьких датасетов
  • Не практичен для больших датасетов и сложных моделей

LOOCV — это “золотой стандарт” для оценки моделей на маленьких датасетах, но его следует использовать с умом из-за вычислительной сложности.