Вы кодируете предвзятость в своих обучающих данных?

Если кодировщик подходит для всего набора данных, то на кодирование обучающих данных влияет распределение данных проверки и тестирования.

Вы когда-нибудь задумывались, почему библиотеки обработки данных, такие как scikit-learn, разделяют процесс кодирования на два этапа: fit() и transform()? В этом посте мы рассмотрим причины, лежащие в основе этого шаблона.

Утечка определена

Цель контролируемого алгоритма состоит в том, чтобы делать прогнозы относительно данных, которые алгоритм ранее не видел. Следовательно, при обучении алгоритма делать прогнозы в отношении разбиений при проверке и тестировании специалисты-практики должны убедиться, что алгоритм непреднамеренно не получит доступ к информации об этих разбиениях. Когда происходит это злоупотребление служебным положением, это называется «утечкой данных».

Как происходит утечка

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

Фактическим кодировщиком для обработки числовых данных в scikit-learn является StandardScaler. Он масштабирует значения от -1 до 1 около среднего значения 0.

Например, предположим, что мы нормализуем дневную температуру для города Бостона в течение года:

  • Средняя температура (например, 52 по Фаренгейту) будет закодирована как ноль.
  • При высоких температурах (например, 98 по Фаренгейту) около 1
  • И низкие температуры (например, -5 по Фаренгейту) около -1

Теперь мы могли бы закодировать весь наш набор данных за один шаг, например так:

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
encoded_dataset = scaler.fit_transform(dataset)

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

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

Так зачем кодировать?

Если процесс стандартизации числовых данных подвержен утечкам, то почему его нельзя пропустить? Кодирование дает два основных преимущества:

Равная важность функций. Допустим, у нас есть две функции: exam_score и SAT_score [подготовительный тест в колледже США]. С одной стороны, максимальный балл за экзамен — 100, а с другой стороны, максимальное количество баллов за SAT — 1600. Если мы не нормализуем эти две характеристики на основе диапазона их возможных значений, то алгоритм изначально склонны отдавать приоритет функции SAT_score из-за ее больших значений. Однако, если мы нормализуем обе функции между 0 и 1, то в начале обучения они будут обрабатываться одинаково.

Помогите предотвратить взрыв градиента. Нейронные сети лучше учатся, когда входные значения близки к нулю. Если вы мне не верите, попробуйте сначала обучить CNN как с масштабированием данных изображения, так и без него. Причина в том, что многие функции активации (например, сигмовидная кривая, как показано на рисунке ниже) являются асимптотическими (насыщенными) при 1 и -1. Из-за асимптот более высокие и более низкие значения не оказывают существенного влияния на выходное значение. Удобно, что большинство функций стандартизации выводят значения, близкие к 0.

Разделить перед кодированием

Так как же предотвратить утечку данных во время кодирования?

Ключевым моментом является сегментация набора данных на обучение, проверку и тестирование перед выполнением стандартизации. Однако мы не разделяем каждую fit_transform() отдельно. Сначала мы используем fit() для нашего тренировочного сплита, а затем transform() для каждого из остальных сплитов следующим образом:

scaler = StandardScaler()
scaler.fit(split_training)
encoded_training   = scaler.transform(split_training)
encoded_validation = scaler.transform(split_validation)
encoded_test       = scaler.transform(split_test)

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

Введите AIQC

К счастью, автоматизированная Pipelines() AIQC, библиотеки с открытым исходным кодом, созданной автором этой записи в блоге, решает эти проблемы следующим образом:

  • Обнаружение использования числовых препроцессоров sklearn
  • fit() для обучающих выборок перед раздельным transform() для каждого разделения и/или сворачивания
  • Сохранение каждого fit() для последующего декодирования прогнозов
splitset = aiqc.Pipeline.Tabular.make(
    # --- Data source ---
    df_or_path = df

    # --- Label preprocessing ---
    , label_column = 'SurfaceTempK'
    , label_encoder = dict(sklearn_preprocess =   StandardScaler(copy=False))

    # --- Feature preprocessing ---
    , feature_cols_excluded = 'SurfaceTempK'
    , feature_encoders = [
        dict(dtypes=['float64'], sklearn_preprocess=RobustScaler(copy=False)),
        dict(dtypes=['int64'], sklearn_preprocess=OneHotEncoder(sparse=False))
    ]
    , feature_reshape_indices = None

    # --- Stratification ---
    , size_test = 0.12
    , size_validation = 0.22
    , fold_count = None
    , bin_count = 4
)

Как видно из вышеизложенного, все, что нужно сделать практикующему врачу, это:

  • Определите кодировщик(и), которые будут использоваться для их меток и функций.
  • Укажите размер их проверочных и тестовых подмножеств
  • Укажите количество складок, если таковые имеются, для использования

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

https://github.com/aiqc/aiqc

Таким образом, мы узнали, что утечка данных происходит, когда кодировщик подходит для данных, не предназначенных для обучения. Эта проблема может быть решена путем отдельного кодирования каждого разделения/сгиба, но при этом вручную вводится еще одно измерение обработки данных, за которым практикующие специалисты должны следить. К счастью, aiqc.Pipelines() можно использовать для автоматизации этой и других задач предварительной и последующей обработки.