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

Категорические признаки

В машинном обучении функции можно разделить на две основные категории:

  • Числовые характеристики (возраст, цена, площадь и т. д.)
  • Категориальные признаки (пол, семейное положение, профессия и т.д.)

Все те признаки, которые состоят из определенного количества категорий, известны как категориальные признаки. Категориальные признаки можно разделить на два основных типа:

  1. Номинальный
  2. Порядковый номер

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

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

Обработка категориальных признаков

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

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

  1. Кодировка этикетки
  2. Одно горячее кодирование

Кодировка ярлыка

Кодирование метки — это процесс присвоения числовой метки каждой категории объекта. Если N — количество категорий, всем значениям категории будет присвоен уникальный номер от 0 до N-1.

Если у нас есть функция с именем «Цвета», имеющая значения красный, синий, зеленый и желтый, ее можно преобразовать в числовое отображение следующим образом.

Category : Label
"red"    : 0
"blue"   : 1
"green"  : 2
"yellow" : 3

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

  • Деревья решений
  • Случайный лес
  • XGBoost
  • СветGBM

Мы можем реализовать кодировку меток, используя класс LabelEncoder scikit-learn. Мы увидим реализацию в следующем разделе.

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

Ограничение кодирования меток можно преодолеть путем бинаризации категорий, т. е. представления категорий, использующих только 0 и 1. Здесь мы представляем каждую категорию вектором размера N, где N — количество категорий в этой функции. Каждый вектор имеет один 1, а все остальные значения равны 0. Следовательно, это называется горячим кодированием.

Предположим, у нас есть столбец с именем температуры. Он имеет четыре значения: «Заморозка», «Холодный», «Теплый» и «Горячий». Каждая категория будет представлена ​​следующим образом:

Category        Encoded vector
Freezing        0  0  0  1
Cold            0  0  1  0
Warm            0  1  0  0
Hot             1  0  0  0

Как вы можете видеть здесь, каждая категория представлена ​​вектором длины 4, поскольку 4 — это количество уникальных категорий в функции. Каждый вектор имеет одну 1, а все остальные значения равны 0.

Поскольку горячее кодирование генерирует нормализованные функции, его можно использовать с линейными моделями, такими как:

  • Линейная регрессия
  • Логистическая регрессия

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

Реализация на Python

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

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

  1. Заполните значения NaN новыми категориями (например, NONE)
  2. Преобразование категорий в числовые значения с помощью кодирования меток для древовидных моделей и горячего кодирования для линейных моделей.
  3. Постройте модель, используя числовые и закодированные функции.

Мы будем использовать общедоступный набор данных под названием Cat in the Dat на kaggle. Ссылка здесь. Это проблема бинарной классификации, которая состоит из множества категориальных признаков.

Сначала мы создадим 5 складок для проверки, используя класс StratifiedKFold в scikit-learn. Этот вариант KFold используется для обеспечения одинакового соотношения целевых переменных в каждой кратности.

import pandas as pd
from sklearn import model_selection
#read training data
df = pd.read_csv('../input/train.csv')
#create column for kfolds and fill it with -1
df['kfold'] = -1
#randomize the rows
df = df.sample(frac=1).reset_index(drop=True)
#fetch the targets
y = df['target'].values
#initiatre StratifiedKFold class from model_selection
kf = model_selection.StratifiedKFold(n_splits=5)
#fill the new kfold column 
for f,(t_,v_) in enumerate(kf.split(X=df,y=y)):
    df.loc[v_,'kfold'] = f
#save the new csv with kfold column
df.to_csv('../input/train_folds.csv',index=False)

Кодировка ярлыка

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

import pandas as pd
from sklearn import ensemble
from sklearn import metrics
from sklearn import preprocessing
def run(fold):
    #read training data with folds
    df = pd.read_csv('../input/train_folds.csv')
    #get all relevant features excluding id, target and kfold columns
    features = [feature for feature in df.columns if feature not in ['id','target','kfold']]
    #fill all nan values with NONE
    for feature in features:
        df.loc[:,feature] = df[feature].astype(str).fillna('NONE')
    #Label encoding the features
    for feature in features:
        #initiate LabelEncoder for each feature
        lbl = preprocessing.LabelEncoder()
        #fit the label encoder
        lbl.fit(df[feature])
        #transform data
        df.loc[:,feature] = lbl.transform(df[feature])
    #get training data using folds
    df_train = df[df['kfold']!=fold].reset_index(drop=True)
    
    #get validation data using folds
    df_valid = df[df['kfold']==fold].reset_index(drop=True)
    #get training features
    X_train = df_train[features].values
    
    #get validation features
    X_valid = df_valid[features].values
    #initiate Random forest model
    model = ensemble.RandomForestClassifier(n_jobs=-1)
    #fit the model on train data
    model.fit(X_train,df_train['target'].values)
    #predict the probabilities on validation data
    valid_preds = model.predict_proba(X_valid)[:,1]
    #get auc-roc score
    auc = metrics.roc_auc_score(df_valid['target'].values,valid_preds)
    #print AUC score for each fold
    print(f'Fold ={fold}, AUC = {auc}')

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

if __name__=='__main__':
    for fold_ in range(5):
        run(fold_)

Выполнение этого кода даст результат, как показано ниже.

Fold =0, AUC = 0.7163772816343564
Fold =1, AUC = 0.7136206487083182
Fold =2, AUC = 0.7171801474337066
Fold =3, AUC = 0.7158938474390842
Fold =4, AUC = 0.7186004462481813

Здесь следует отметить, что мы не выполняли настройку гиперпараметров модели случайного леса. Вы можете настроить параметры, чтобы повысить точность проверки. Еще одна вещь, которую следует упомянуть в приведенном выше коде, заключается в том, что мы используем показатель AUC ROC в качестве метрики для проверки. Это связано с тем, что целевые значения искажены, и такие показатели, как Точность, не дадут нам правильных результатов.

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

Теперь давайте посмотрим на реализацию одного горячего кодирования с логистической регрессией.

Ниже приведена модифицированная версия метода run для этого подхода.

import pandas as pd
from sklearn import linear_model
from sklearn import metrics
from sklearn import preprocessing
def run(fold):
    #read training data with folds
    df = pd.read_csv('../input/train_folds.csv')
    #get all relevant features excluding id, target and folds columns
    features = [feature for feature in df.columns if feature not in ['id','target','kfold']]
    #fill all nan values with NONE
    for feature in features:
        df.loc[:,feature] = df[feature].astype(str).fillna('NONE')
    #get training data using folds
    df_train = df[df['kfold']!=fold].reset_index(drop=True)
    
    #get validation data using folds
    df_valid = df[df['kfold']==fold].reset_index(drop=True)
    #initiate OneHotEncoder from sklearn
    ohe = preprocessing.OneHotEncoder()
    #fit ohe on training+validation features
    full_data = pd.concat([df_train[features],df_valid[features]],axis=0)
    ohe.fit(full_data[features])
    #transform training data
    X_train = ohe.transform(df_train[features])
    
    #transform validation data
    X_valid = ohe.transform(df_valid[features])
    #initiate logistic regression
    model = linear_model.LogisticRegression()
    #fit the model on train data
    model.fit(X_train,df_train['target'].values)
    #predict the probabilities on validation data
    valid_preds = model.predict_proba(X_valid)[:,1]
    #get auc-roc score
    auc = metrics.roc_auc_score(df_valid['target'].values,valid_preds)
    #print AUC score for each fold
    print(f'Fold ={fold}, AUC = {auc}')

Метод зацикливания всех складок остается прежним.

if __name__=='__main__':
    for fold_ in range(5):
        run(fold_)

Вывод этого кода будет таким, как показано ниже:

Fold =0, AUC = 0.7872262099199782
Fold =1, AUC = 0.7856877416085041
Fold =2, AUC = 0.7850910855093067
Fold =3, AUC = 0.7842966593706009
Fold =4, AUC = 0.7887711592194284

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

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

Вывод

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

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

Спасибо за чтение.:)