Одинаковые строки в train и test: почему модель выглядит точнее

Выгрузка заказов содержит повторные строки после нескольких загрузок. Команда случайно разделила её на train и test; модель почти безошибочна. Причина может быть простой: копия проверочного заказа уже лежит в обучении с тем же ответом. Модель узнаёт объект, а отчёт называет это качеством на новых заказах.

Найдите повтор до обучения

Сначала определите, что считается одним объектом. Точный повтор всех колонок — очевидный случай, но два заказа могут иметь разные служебные timestamps и один устойчивый ID. Если цель — предсказывать для нового заказа, записи одного заказа должны остаться по одну сторону границы. Для новых клиентов границу иногда нужно поднять до клиента; разбор GroupKFold объясняет этот более широкий случай.

Учебный набор ниже специально состоит из пар одинаковых ID и меток. Однососедняя модель легко запоминает ответ для ID. Сначала мы делаем обычный случайный split, затем разделяем группы ID через GroupShuffleSplit. Код печатает число общих ID и оценку на каждом тесте.

import numpy as np
from sklearn.model_selection import GroupShuffleSplit, train_test_split
from sklearn.neighbors import KNeighborsClassifier

rng = np.random.default_rng(3)
ids = np.repeat(np.arange(30), 2)
X = ids.reshape(-1, 1)
y = np.repeat(rng.integers(0, 2, 30), 2)
train, test = train_test_split(np.arange(len(y)), test_size=0.3, random_state=1)
model = KNeighborsClassifier(n_neighbors=1).fit(X[train], y[train])
overlap = set(ids[train]) & set(ids[test])
print("случайный split: общих id", len(overlap), "accuracy", round(model.score(X[test], y[test]), 3))
group_train, group_test = next(GroupShuffleSplit(test_size=0.3, random_state=1).split(X, y, groups=ids))
group_model = KNeighborsClassifier(n_neighbors=1).fit(X[group_train], y[group_train])
print("group split: общих id", len(set(ids[group_train]) & set(ids[group_test])),
      "accuracy", round(group_model.score(X[group_test], y[group_test]), 3))
assert overlap
assert not (set(ids[group_train]) & set(ids[group_test]))

В этом прогоне у случайного split двенадцать общих ID и accuracy 1,0; у группового пересечений нет и accuracy 0,667. Разница показывает эффект запоминания конкретных объектов в искусственной задаче. Не переносите величину 0,333 на свои данные: там могут быть реальные общие закономерности между новыми ID. Сначала проверьте пересечение, затем интерпретируйте изменение метрики.

Не удаляйте дубликаты вслепую

Иногда повторные записи отражают реальные повторные события. Клиент мог действительно купить два одинаковых товара в разные дни. Удалив одну строку, можно изменить частоту явления и исказить задачу. Отделяйте технические дубли выгрузки от самостоятельных событий по бизнес-ключу, времени и источнику. Если источник прислал одну и ту же транзакцию дважды, устраняйте повтор до расчёта признаков и разбиения. Если события разные, сохраняйте их, но задавайте границу по сущности, которую модель не должна запоминать.

Проверьте пересечение после каждого обновления датасета. Посчитайте не только число общих ID, но и долю тестовых строк с копией в train. Для текстов и изображений возможны почти точные дубли, которые обычное равенство не найдёт; там понадобятся отпечатки или поиск похожих файлов с ручной проверкой. Но начните с точных бизнес-ключей: они дешевле и легче объясняются.

На курсе «Data Scientist: от данных до деплоя моделей» вы проверяете качество источников, выбираете стратегию валидации, сравниваете модели и анализируете ложное улучшение. Это даёт процесс, в котором дубликат обнаруживается до выбора победителя эксперимента. Откройте программу курса.

Продолжить на практике

  • Data Scientist: от данных до деплоя моделей ↗

    Практический курс по Data Science: от постановки ML-задачи и работы с данными до inference API, CI/CD и мониторинга моделей в продакшне. Вы пройдёте EDA, feature engineering, обучение и оценку моделей, experiment tracking, error analysis и deployment — чтобы собирать полноценный рабочий контур вокруг ML-решения.