Как правильно создать отложенную (hold-out) тестовую выборку?

«Как правильно создать отложенную (hold-out) тестовую выборку?» — вопрос из категории Классическое ML, который задают на 26% собеседований Data Scientist / ML Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Отложенная тестовая выборка (hold-out test set) — это часть данных, которая изолируется на самом начале и не используется ни для обучения, ни для настройки гиперпараметров. Её единственная цель — финальная оценка качества уже обученной модели. Стандартный инструмент — train_test_split из sklearn.model_selection.

Базовый пример:

from sklearn.model_selection import train_test_split

# Исходные данные и целевая переменная
X, y = load_data()

# Разделение: 80% на обучение/валидацию, 20% на финальный тест
X_train_val, X_test, y_train_val, y_test = train_test_split(
    X, y,
    test_size=0.2,
    random_state=42,  # Для воспроизводимости результата
    shuffle=True      # Перемешивать данные перед разбиением
)

Критически важные практики:

  1. Разделение до любых преобразований: Тестовая выборка должна имизировать "новые, невиданные" данные. Любая утечка информации из теста в train (например, через масштабирование) исказит оценку.

    # НЕПРАВИЛЬНО: Сначала масштабировать всю выборку, потом делить.
    # ПРАВИЛЬНО:
    X_train_val, X_test, y_train_val, y_test = train_test_split(X, y, test_size=0.2)
    scaler = StandardScaler().fit(X_train_val) # Обучаем scaler ТОЛЬКО на train
    X_train_val_scaled = scaler.transform(X_train_val)
    X_test_scaled = scaler.transform(X_test) # Трансформируем тест тем же scaler'ом
  2. Стратификация (для классификации): Если классы несбалансированы, используйте stratify=y. Это сохранит пропорции классов в train и test наборах.

    X_train, X_test, y_train, y_test = train_test_split(
        X, y,
        test_size=0.2,
        random_state=42,
        stratify=y  # Ключевой параметр для классификации
    )
  3. Для временных рядов: Простое случайное разбиение неприменимо, так как нарушает временной порядок. Тестовой выборкой должны быть последние наблюдения.

    split_index = int(len(X) * 0.8)
    X_train, X_test = X[:split_index], X[split_index:]
    y_train, y_test = y[:split_index], y[split_index:]

Итог: Отложенная выборка — это "священный грааль" для финальной проверки. Все этапы обучения и валидации (включая кросс-валидацию) должны происходить только на X_train_val/y_train_val. К X_test/y_test обращаемся один раз в самом конце.