Работа с данными расширенной выборки при перекрестной проверке

Реализация перекрестной проверки на Python для расширенных выборочных данных, чтобы предотвратить утечку данных и переоценку производительности вашей модели.

В этой статье рассказывается о перекрестной проверке для данных стратегии выборки с повышением частоты дискретизации и реализации на Python.

Усиленная выборка часто используется для нацеливания на метки класса меньшинства в (сильно) несбалансированных наборах данных. Если их не учитывать при перекрестной проверке, включая данные расширенной выборки в тестовом наборе, это добавит смещение в точность модели машинного обучения.

Эта история помогает нам понять эту предвзятость и обеспечивает реализацию перекрестной проверки, которая учитывает расширенные выборочные данные в Python. Затрагиваются следующие темы:

  • Несбалансированная классификация
  • Случайная и усиленная выборка
  • Утечка данных
  • Пример несбалансированных данных
  • Реализация Python
  • Заключение

Несбалансированная классификация

Проблема несбалансированной классификации — это проблема классификации, при которой целевое распределение переменных является смещенным или асимметричным, с небольшим количеством экземпляров в классе меньшинства и большим количеством экземпляров в классе большинства.

Многие проблемы реального мира не сбалансированы. Например, возвратов через Интернет значительно меньше, чем покупок, и спама меньше, чем обычных писем (хотя иногда это не так).

Несбалансированная классификация представляет собой проблему для моделей машинного обучения с учителем, поскольку может привести к тому, что модели будут иметь плохую прогностическую эффективность, особенно для класса меньшинства. Из-за несоответствия целевых классов алгоритм имеет тенденцию классифицировать класс с большим количеством экземпляров, класс большинства. Это вносит предвзятость ложного ощущения точности модели.

Случайная и усиленная выборка

Один из способов справиться с несбалансированными наборами данных — сбалансировать целевые классы во время сбора данных, обычно путем увеличения выборки меток миноритарных классов с помощью набора эвристик или бизнес-правил.

В методе случайной выборки каждая выборка имеет равную вероятность быть выбранной. Повышение или апсемплинг — это метод выборки, при котором некоторые сэмплы будут выбраны с большей вероятностью. Он может искусственно сэмплировать образцы меньшинства, чтобы сбалансировать метку класса в несбалансированных наборах данных.

Например, выборка людей моложе 30 лет, потому что они, как правило, возвращают свои покупки чаще, чем люди старшего возраста. Примерами усиленной выборки являются выборочные электронные письма, содержащие такие слова, как гарантия, доллар и цена.

Утечка данных

Мы знаем, что апсемплинг перед разделением тестового набора приводит к утечке данных, так как данные с апсэмплингом теперь также присутствуют в нашем тестовом наборе, что упрощает прогнозирование данных для нашей модели.

Однако что делать, когда у нас есть выборка данных для нашей контролируемой модели машинного обучения, содержащая данные с повышенной дискретизацией?

Мы хотим использовать эти данные для обучения нашей модели на более сбалансированном наборе данных для изучения закономерностей (даже если они по своей сути предвзяты) в выборках класса меньшинства. Но мы не хотим использовать эти данные для тестирования нашей модели или настройки гиперпараметров. Как мы тренируемся на всех данных, но проверяем только случайно выбранное подмножество наших данных?

BoostedKFold позволяет проводить обучение на всех обучающих данных в перекрестной проверке, но исключает усиленные выборочные данные, определенные -1 в параметре groups функции split. Как показано на изображении ниже, индексы обучающей складки рисуются независимо от групповых индексов, но индексы тестовой складки берутся из случайно выбранной группы, а не из группы усиленной выборки.

Пример несбалансированных данных

Давайте рассмотрим пример с использованием набора данных бинарной классификации sklearn.make_classification из 1000 образцов, распределенных по классу большинства 95% и классу меньшинства 5%. 30% меток класса меньшинств получены с помощью метода усиленной выборки, а остальные 70% выбираются случайным образом.

В визуализации ниже вы можете увидеть матрицу путаницы и отчет о классификации для 5-кратной перекрестной проверки в StratifiedKFold. Перекрестная проверка выполняется для всех тестовых меток через cross_val_predict. Метка класса положительного меньшинства 1 насчитывала 50 выборок, включая данные усиленной выборки и данные случайной выборки.

Теперь сравните это с реализацией BoostKFold ниже. Перекрестная проверка выполняется для всех тестовых меток с помощью пользовательской функции _cross_val_predict, поскольку cross_val_predict не может обрабатывать различия в размерах тестового набора в истинных метках Y. Метка класса положительного меньшинства 1 насчитывала 35 выборок, включая данные случайно выбранной выборки и исключая данные усиленной выборки.

Показатели оценки для положительного класса меньшинства значительно ниже, чем в предыдущей реализации StratifiedKFold для класса меньшинства, что дает нам более реалистичное представление о производительности модели на невидимых (производственных) данных. Отчасти это связано с меньшим количеством экземпляров (35 против 50), что еще больше затрудняет прогнозирование класса меньшинства.

Однако снижение производительности нельзя полностью объяснить этим. Оценка F1 падает вдвое, тогда как экземпляры класса меньшинства падают только на 30%. Оставшееся падение производительности можно объяснить удалением легко предсказуемых экземпляров с повышенной частотой дискретизации, поскольку модель уже видела аналогичные данные с повышенной частотой дискретизации на этапе обучения.

Реализация Python

BoostedKFold использует StratifiedKFold и PredefinedSplit под капотом, реализованным в методе .split(). Я также добавил метод .plot(), который визуализирует разделение изображения, показанного в виде миниатюры этой истории.

def split(self, X: np.array, y: np.array, groups: np.array):
    """Generate indices to split data into training and test set, excluding data in groups with value '-1'.

    boosted sample data == '-1' in the ``groups`` parameter
    random sample data != '-1' in the ``groups`` parameter

    Args:
        X (ndarray): array-like of shape (n_samples, n_features)
            Training data, where `n_samples` is the number of samples and `n_features` is the number of features.
        y (ndarray): array-like of shape (n_samples,),
            The target variable for supervised learning problems.
        groups (ndarray): array-like of shape 1d: '-1' for elements to be excluded

    Yields:
        train (ndarray): The training set indices for that split.
        test (ndarray): The testing set indices for that split.
    """
    # separate boosted sample data that have group ``-1``, from random sample data
    boosted_indices = np.where(groups == -1)[0]
    random_indices = np.where(groups != -1)[0]

    skf = StratifiedKFold(n_splits=self.n_splits, shuffle=self.shuffle)
    # split the randomly sampled indices that are to be included in the test-set in ``n_splits`` splits
    stratified_random_splits = skf.split(X[random_indices], y[random_indices])

    random_sampled = [0] * len(random_indices)
    boosted_sampled = [-1] * len(boosted_indices)

    # converts the random stratified split test-set indices to the ``n_splits`` enumeration
    for split_nr, (_, testing_indices) in enumerate(stratified_random_splits):
        # defines which random sample datapoint is in which test-fold
        for test_idx in testing_indices:
            random_sampled[test_idx] = split_nr

    # concatenate the randomly sampled split numbers and the boosted sampling split numbers
    predefined_splits = random_sampled + boosted_sampled
    # boosted samples are not accounted for in the test-fold splits
    ps = PredefinedSplit(test_fold=predefined_splits)

    return ps.split(X)

Реализация может использоваться в sklearn.GridSearchCV или sklearn.Pipeline, как и любой другой класс перекрестной проверки в scikit-learn. Полный сценарий можно найти на моей странице GitHub.

Заключение

Повышение дискретизации данных может быть хорошим способом повысить метки класса меньшинства, что может быть очень актуально в случае несбалансированных наборов данных. В этой истории вы узнали о потенциальном уклоне в утечке данных и предотвращении переоценки производительности вашего классификатора.

Удачного кодирования! Не стесняйтесь задавать мне любые вопросы, которые у вас есть.