Введение

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

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

Основное различие между ними заключается в сравнении. В Порядковые категориальные переменные мы можем сравнивать категории друг с другом; например, если у нас есть обзоры фильмов, разделенные на пять категорий: «Прекрасно», «Хорошо», «Нейтрально», «Плохо», «Тяжело смотреть», мы знаем, что Великолепно › Плохо или Хорошо › Трудно смотреть; однако Номинальные категориальные переменные нельзя напрямую сравнивать друг с другом; например, мы не можем сказать, что Красный › Синий или Зеленый › Желтый без определенного контекста.

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

Набор данных

Мы будем проводить наши эксперименты на наборе данных с конкурса Porto Seguro Sage Driver, который проводится на Kaggle. Цель конкурса состояла в том, чтобы предсказать вероятность того, что водитель инициирует иск по автострахованию в следующем году.

Набор данных содержит:

  • 14 номинальных категориальных переменных
  • 17 бинарных переменных
  • 26 числовых переменных

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

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

На следующем рисунке вы можете увидеть кардинальность каждой функции в наборе данных.

Из предыдущей таблицы мы можем вывести, что:

  • Переменная ps_car_11_cat имеет наибольшее количество элементов и содержит 104 уникальные категории.
  • Большинство мощностей меньше 10.
  • Хотя ps_car_08_cat имеет только 2 уникальных значения, которые можно рассматривать как двоичные, в рамках этого блога мы будем рассматривать его как номинальную категориальную переменную.

Кодировка этикетки

При кодировании меток каждой категории присваивается уникальное целое число от 0 до C-1, где C представляет кардинальность переменной. Целые числа присваиваются каждой категории в любом конкретном порядке.

Преимущества

  • Не увеличивайте пространство входных признаков и сложность модели
  • Простота реализации и удобство памяти 🙂

Недостатки

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

Выполнение

Организатор конкурса уже закодировал категории в целые числа для нас, однако трансформатор LabelEncoder от sklearn может сделать эту работу за нас 🙂.

Одно горячее кодирование

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

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

# categories = [Red, Green, Blue], cardinality = 3
colors = ['Red', 'Red', 'Green', 'Red', 'Blue']
ohe_colors =(
       # R, G, B
       [[1, 0, 0], # Red
	[1, 0, 0], # Red
	[0, 1, 0], # Green
	[1, 0, 0], # Red
	[0, 0, 1]] # Blue
)

Преимущества

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

Недостатки

  • Этот метод увеличивает пространство входных признаков, что приводит к более сложным моделям и потенциальному переоснащению обучающих данных.
  • Сложность памяти составляет O(NC), где N — количество выборок в наборе данных, а C — мощность переменной. становится дорогим по мере увеличения N или C.

Выполнение

Преобразователь OneHotEncoder от sklearn делает за нас тяжелую работу.

def one_hot_encoding(train_data: pd.DataFrame, valid_data: pd.DataFrame):
    """
    encode categorical variables using one hot encoding strategy
    cfg.CATEGORICAL_VARIABLES is a list object that stores all the categorical variables present in the dataset.
    """

    ohe_tmf = OneHotEncoder(sparse=False, dtype=np.float32)    
    ohe_tmf.fit(train_data[cfg.CATEGORICAL_VARIABLES])

    ohe_columns = [
        f'{cat_name}_{cat_value}'
        for cat_name, categories_ in zip(cfg.CATEGORICAL_VARIABLES, ohe_tmf.categories_)
        for cat_value in categories_
    ]

    # process train data
    processed_train_data = train_data.drop(cfg.CATEGORICAL_VARIABLES, axis=1)
    _np_ohe_train = ohe_tmf.transform(train_data[cfg.CATEGORICAL_VARIABLES])    
    ohe_train = pd.DataFrame(_np_ohe_train, columns=ohe_columns)
    processed_train_data = pd.concat((processed_train_data, ohe_train), axis=1)

    # process valid data
    processed_valid_data = valid_data.drop(cfg.CATEGORICAL_VARIABLES, axis=1)
    _np_ohe_valid = ohe_tmf.transform(valid_data[cfg.CATEGORICAL_VARIABLES])    
    ohe_valid = pd.DataFrame(_np_ohe_valid, columns=ohe_columns)
    processed_valid_data = pd.concat((processed_valid_data, ohe_valid), axis=1)

    return processed_train_data, processed_valid_data

На изображении ниже показана переменная ps_car_09_cat до и после применения одного горячего кодирования.

Частотное кодирование

Этот метод заменяет каждую категорию в зависимости от того, сколько раз она появляется в обучающем наборе. Например, если «Мужчина» встречается 5000 раз, а «Женщина» — 20 000 раз, то категория «Мужчина» заменяется на 5000/(объем обучающих данных) и «Женщина» заменяется на 20000/(объем обучающих данных). Частоты нормализованы, чтобы не смещать размер набора данных.

Преимущества

  • Не увеличивайте пространство входных признаков и сложность модели
  • Простота реализации и удобство памяти 🙂
  • Хорошо работает с моделями на основе дерева.

Недостатки

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

Выполнение

def freq_encoding(train_data: pd.DataFrame, valid_data: pd.DataFrame):
    """
    encode categorical variables by the frequencies of each category
    cfg.CATEGORICAL_VARIABLES is a list object that stores all the categorical variables present in the dataset.
    """
    # becareful, this implementation does not handle unknown categorical values
    category_freq_mapper = {}

    for cat_name in cfg.CATEGORICAL_VARIABLES:
        # normalize so values are between 0 and 1!
        category_freq_mapper[cat_name] = train_data[cat_name].value_counts(normalize=True)

    processed_train_data = train_data.drop(cfg.CATEGORICAL_VARIABLES, axis=1)
    for cat_name in cfg.CATEGORICAL_VARIABLES:
        cat_freqs = category_freq_mapper[cat_name]
        processed_train_data[cat_name] = train_data[cat_name].map(cat_freqs).astype(np.float32)

    processed_valid_data = valid_data.drop(cfg.CATEGORICAL_VARIABLES, axis=1)
    for cat_name in cfg.CATEGORICAL_VARIABLES:
        cat_freqs = category_freq_mapper[cat_name]
        processed_valid_data[cat_name] = valid_data[cat_name].map(cat_freqs).astype(np.float32)
    
    return processed_train_data, processed_valid_data

На изображении ниже показана переменная ps_car_09_cat до и после применения частотного кодирования.

Эксперименты

Мы будем использовать линейные и древовидные модели для оценки эффективности упомянутых выше методов кодирования. Наши представители:

Линейная модель: логистическая регрессия

Модель на основе дерева: классификатор XGBOOST

Мы выбрали XGBOOST, потому что он более мощный, чем RandomForest, но он не обрабатывает категориальные переменные, отличные от непрерывных переменных, тогда как реализация LGBM и CatBoost GBM поддерживает категориальные переменные.

Полученные результаты

В таблице ниже сравниваются результаты всех экспериментов.

Основываясь на этих выводах, мы можем сделать вывод, что для этого набора данных

  • One Hot Encoding и Label Encoding — лучший и худший методы соответственно среди исследованных решений.
  • Частотное кодирование работает так же, как Горячее кодирование с использованием модели XGBOOST.
  • Относительное ранжирование методов поддерживается в обеих моделях: Одно горячее кодирование › Частотное кодирование › Кодирование меток.
  • Несмотря на то, что One Hot Encoding является лучшей стратегией для логистической регрессии, линейная модель с этим методом лучше подходит для обучающей выборки, чем другие методы, как показано в таблице. выше разрыв между логарифмом обучения и проверки в 2 раза больше, чем при других подходах.

Все эксперименты, связанные с проектом, хранятся в этом репозитории GitHub.

Заключение

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

Эй!, Ты сделал это! , Вы закончили первое руководство по работе с категориальными переменными; если вам понравилось, нажмите кнопку «Нравится» и поделитесь публикацией с коллегами. Это первая часть серии, так что следите за обновлениями для следующего чтения; скоро увидимся 😎