Что не так с 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, объяснил, что говорится в оригинальной статье о целевом кодировании мультиклассовых переменных, продемонстрировал всё это на примере и предоставил рабочий модульный код, который вы можете подключить к своему приложению.