Что не так с TargetEncoder из библиотеки category_encoders?

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

1. Когда ошибается TargetEncoder?

Посмотрите на эти данные. Цвет - это особенность, а цель - это… цель. Наша цель - кодировать цвет на основе Target.

Давайте сделаем для этого обычную целе-вероятностную кодировку.

!pip install category_encoders
import category_encoders as ce
ce.TargetEncoder(smoothing=0).fit_transform(df.Color,df.Target)

Хмм… выглядит не очень, не так ли? Все цвета были заменены на 1. Почему? Так происходит потому, что TargetEncoder принимает среднее значение всех целевых значений для каждого цвета, а не вероятность.

Хотя TargetEncoder корректно работает в случае, когда у вас есть двоичная цель, имеющая 0 и 1, он будет давать сбой в двух случаях:

  • Когда цель двоичная, но не 0/1 (хотя бы, например 1 и 2).                               

  • Когда цель - мультикласс, как в приведенном выше примере.

Так что же делать?!

Теория

В оригинальном документе Daniele Micci-Barreca, который вводит средне-целевую кодировку говориться про мульти-классовые целевые переменные.

Допустим, мы имеем n классов. Теория гласит, что первым делом нужно закодировать значения класса в бинарные переменные. Это даст n двоичных столбцов, по одному на каждый класс цели. Однако только n-1 двоичных столбцов будут линейно независимы, так что в принципе, любой из них можно отбросить. После чего применим обычное целе-вероятностное кодирование для наших новых категорий, используя каждую из двоичных меток, по одной за раз.

Давайте разберемся на примере.

Пример

Продолжим с предыдущими данными.

Шаг 1: Бинарное кодирование мульти-классовой категории.

enc=ce.OneHotEncoder().fit(df.Target.astype(str))
y_onehot=enc.transform(df.Target.astype(str))
y_onehot

Обратите внимание, что столбец Target_1 показывает наличие либо отсутствие значения 0 в исходном столбце Target. Он принимает значение 1 если в Target есть 0, либо 0 в противном случае. Точно так же столбец Target_2 показывает наличие или отсутствие 1 в Target.

Шаг 2: Кодируем цвет, используя каждую из бинарных категорий.

class_names = y_onehot.columns

for class_ in class_names: 

    enc = ce.TargetEncoder(smoothing = 0)
    print(enc.fit_transform(X,y_onehot[class_]))

Для класса 0

Для класса 1

Для класса 2

Шаг 3: Если есть другие категории, кроме цвета, повторяем шаги 1 и 2 для них.

Готово!

Таким образом, на выходе получаем такой набор данных:

Обратите внимание, что для ясности я закодировал здесь все три столбца Color_Target. Если вы знаете бинарное кодирование, то слышали, что один из столбцов можно удалить без потери информации. Следовательно, здесь мы можем безопасно удалить, например, столбец Color_Target_3 (либо какой-то другой) без потери информации.

Практика

Вы здесь за кодом, не так ли?!

Ниже представлена функция, которая принимает на вход таблицу данных и объект целевой метки типа Series. Функция df может иметь как числовые, так и категориальные переменные.

def target_encode_multiclass(X,y): #X,y are pandas df and series

    y=y.astype(str)  #convert to string to onehot encode
    enc=ce.OneHotEncoder().fit(y)
    y_onehot=enc.transform(y) 

    class_names=y_onehot.columns  #names of onehot encoded columns

    X_obj=X.select_dtypes('object') #separate categorical columns
    X=X.select_dtypes(exclude='object')

    for class_ in class_names:

        enc=ce.TargetEncoder()
        enc.fit(X_obj,y_onehot[class_]) #convert all categorical
        temp=enc.transform(X_obj)       #columns for class_
        temp.columns=[str(x)+'_'+str(class_) for x in temp.columns]
        X=pd.concat([X,temp],axis=1)    #add to original dataset

    return X

Резюме

В этой статье я показал, что не так с TargetEncoder из библиотеки category_encoder, объяснил, что говорится в оригинальной статье о целевом кодировании мультиклассовых переменных, продемонстрировал всё это на примере и предоставил рабочий модульный код, который вы можете подключить к своему приложению.