Emanuel Fontelles
3/8/2020

Como melhor tratar variáveis categóricas para modelos de machine learning — Parte 1

Sim, dados da vida real frequentemente contém variáveis categóricas. E sim, muita das vezes elas são mais importantes que o algoritmo de aprendizado escolhido.

Essa é a primeira parte de um conjunto de três posts sobre encoders categóricos. Neles vamos cobrir de uma forma simples e prática o uso de diversos encoders e o impacto da sua performance.

Introdução

Dados categóricos são bem comuns em problemas de Data Science e Machine Learning e na maioria das vezes podem ser mais desafiadores de extrair informações do que dados numéricos. Do ponto de vista de algoritmos de aprendizado, esses dados deverão ser transformado em dados numéricos para que possamos incluí-los na nossa etapa de treinamento. Assim, uma melhor representação dos dados categóricos afetam diretamente a performance do treinamento como também introduz melhores formas de explicarmos sua contribuição para a predição.

Representar dados categóricos não é tão óbvio, variáveis como estado, genero, signo (por mais inocente que isso seja, signos trazem a data de nascimento embutidas) dados como gênero não requerem ordenação, podemos assumir uma representação numérica qualquer. No entanto, dados de nascimento levam em conta à ação do tempo, necessitam de uma ordenação para que sua representação numérica seja coerente.

Nos próximos instantes, iremos passar por alguns dos métodos de transformação usados no dia-a-dia dos projetos de Data Science, comumente conhecidos com encoders (codificador, transformador), ou nesse caso especifico, encoders categóricos.

Traremos alguns exemplos práticos usando o pacote Category Encoders, um conjunto de transformadores capaz de transformar variáveis categóricas com diferentes técnicas.

Sobre os dados

Na tentativa de trabalharmos com os diferentes encoders possíveis, vamos usar o Breast Cancer Dataset (conjunto de dados de câncer de mama), disponível no UCI Machine Learning Repository, obtido a partir da University Medical Centre, Institute of Oncology, Ljubljana, Yugoslavia.

O dataset contém 201 registros de uma classe e 85 registros de uma segunda classe, além de conter 9 variáveis, algumas lineares e outras categóricas.

Descrição das variáveis:

  • Class: no-recurrence-events, recurrence-events
  • age: 10–19, 20–29, 30–39, 40–49, 50–59, 60–69, 70–79, 80–89, 90–99.
  • menopause: lt40, ge40, premeno.
  • tumor-size: 0–4, 5–9, 10–14, 15–19, 20–24, 25–29, 30–34, 35–39, 40–44, 45–49, 50–54, 55–59.
  • inv-nodes: 0–2, 3–5, 6–8, 9–11, 12–14, 15–17, 18–20, 21–23, 24–26, 27–29, 30–32, 33–35, 36–39.
  • node-caps: yes, no.
  • deg-malig: 1, 2, 3.
  • breast: left, right.
  • breast-quad: left-up, left-low, right-up, right-low, central.
  • irradiat: yes, no.

Carregando os dados

import pandas as pd
breast_cancer_url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer/'
dataset_url = breast_cancer_url + 'breast-cancer.data'
data = pd.read_csv(filepath_or_buffer=dataset_url,
                  names=['Class', 'age', 'menopause', 'tumor_size',
                         'inv_nodes', 'node-caps', 'deg-malig',
                         'breast', 'breast_quad', 'irradiat']
                  )
data

png


Os encoders

Para nos organizarmos na definição dos diferentes encoders à seguir, vamos criar uma pequena tabela onde separamos diferentes encoders em clássicos, de contraste e bayesianos. Ao longo da nossa série de posts, iremos passar por cada um deles mostrando suas aplicações e casos de uso.


Clássicos

Começaremos pelos os encoders clássicos, um conjunto de encoders que são frequentemente usados em uma primeira aproximação nos problemas de machine learning, pois oferecem basicamente uma representação numérica da categoria. Os mais comuns são Ordinal e One Hot Encoding.

Ordinal Encoding

Também chamado de Label Encoder (usado quando queremos transformar a classe da variável resposta), esse é o encoder mais simples para variáveis categóricas. Sua transformação busca um mapeamento entre cada categoria da variável e um valor numérico único.

Matematicamente, tomemos à variável x que contém as seguintes categorias {categoria₁, categoria₂, …, categoriaₙ}, para cada categoriaᵢ tomemos o mapeamento:

∀ categoriaᵢ, categoriaᵢ = kᵢ, onde kᵢ ∈ {0, … , (n-1)}.

Lê se: para toda categoriaᵢ, categoriaᵢ igual a kᵢ, onde kᵢ está contido no conjunto dos números naturais até (n-1) classes.

A definição matemática parece um pouco demais, mas nos dá o formalismo necessário para não cometermos duplicidade, garantindo que estamos mapeando uma classe para um único valor inteiro. Por conta desse último detalhe, é também chamado de encoder inteiro (integer encoding).

Para exemplificar, tomemos do nosso dado, data, a feature que contém as informações sobre qual seio foi diagnosticado o câncer de mama, esquerdo ou direito (breast).

import seaborn as sns
import matplotlib.pyplot as plt
plt.style.use("ggplot")

fig, _ = plt.subplots(1, figsize=(14,8))
ax = sns.countplot(x="breast", data=data)
fig.tight_layout()

png

Para a variável breast (seio), temos duas categorias, left (esquerdo) e right (direito), cada uma será mapeada como left=0 e right=1, respeitando a ordem alfabética entre as categorias.

from sklearn.preprocessing import OrdinalEncoder

ordinal_encoder = OrdinalEncoder()
data["breast_encoded"] =
ordinal_encoder.fit_transform(data.breast.values.reshape(-1,1))

fig, _ = plt.subplots(1, figsize=(14,8))
ax = sns.countplot(x="breast_encoded", data=data)
fig.tight_layout()

png

Aqui está o nosso resultado final. Devemos usar o Ordinal Encoder quando tivermos uma ordenação clara entre as categorias. No nosso exemplo, estamos um dado que não requer ordem entre suas categorias, e isso deve ser levado em conta na hora que formos aplicar esse encoder.


data[["breast","breast_encoded"]]

png

One Hot Encoding

No exemplo anterior usamos um encoder que requer ordem entre as categorias, daí o seu nome se referir a ordinal, pois à ordem entre as categorias é levada em conta na sua representação numérica.

Uma forma de contornar a ordem entre as categorias é buscar uma transformação vetorial, nesse caso um vetor esparso (na verdade é um tensor). O que iremos fazer é mapear cada categoria em uma entrada desse vetor, onde cada categoria terá sua própria entrada assinalada com um valor de 1 (daí o nome One Hot) e o restante com 0.

Matematicamente, o que estamos buscando é uma representação matricial (na verdade é um tensor), onde cada linha i representa uma amostra e cada coluna j representa uma categoria da variável. Vamos fazer por partes:

  • Primeiro criamos para cada categoria um índice, que representa a entrada do vetor, assim como fizemos no Ordinal Encoding (∀ categoriaᵢ, categoriaᵢ = kᵢ, onde kᵢ ∈ {0, … , (n-1)}).
  • Cada valor k representa uma entrada no nosso vetor v que contém (n-1) entradas.

  • Por último, para a categoriaᵢ, com o índice k, preenchemos o vetor v na k-ésima entrada (vₖ) com 1 e as demais entradas com 0.

Todos os passos acima são representados na figura abaixo.

One hot encoding passo-a-passo.

Para exemplificar, tomemos do nosso dado, data, a feature que contém as informações sobre o período antes, depois ou pré-menopausa (menopause) e como isso pode está relacionado com o cancêr de mama (nossa target).


fig, _ = plt.subplots(1, figsize=(14,8))
ax = sns.countplot(x="menopause", data=data)
fig.tight_layout()

png


Para a variável menopause que contém as seguintes classes menopause = {premeno, ge40, lt40} o resultado é mostrado abaixo:


Em suma, conseguimos uma representação numérica para nossa categoria, mas em contraponto, imaginemos que determinada variável possua um número muito grande de categorias, a nossa transformação vetorial cresce com o aumento dessas categorias. Nesse ponto, One Hot Encoding deve ser evitado, pois com o aumento de dimensões nosso algoritmo de aprendizado pode perder sua sensibilidade para determinadas informações contidas na categoria.

Estatísticos ou Bayesianos

Até aqui tratamos os principais encoders categóricos que basicamente procuram um representação numérica para variáveis categóricas.

Por outro lado, existe um seleto grupo de encoders capazes de transformar determinadas features baseando-se na informação prévia da variável resposta (target) que desejamos modelar. Esses tais, são conhecidos como encoders estatísticos, bayesianos ou ainda target encoding. Uma aproximação mais formal pode ser obtida nesse artigo, A preprocessing scheme for high-cardinality categorical attributes in classification and prediction problems. Nesse artigo, o autor traz um conjunto de técnicas para encoders categóricos, alguns relativamente antigos (Probability and the Weighting of Evidence), que ganharam notoriedade com o avanço dos estudos de análise de crédito, por exemplo.

O problema com encoders clássicos se dá ao fato de não trazerem dependências da variável resposta para a variável que estamos modelando. Por exemplo, One Hot encoding pode crescer rapidamente se tivermos várias categorias dentro de determinada variável, o que pode nos trazer problemas de performance e uma pobre previsão no conjunto de teste (The curse of Dimensionality).

O que apresentaremos à seguir são encoders que buscam substituir a categoria por sua relação direta com a variável resposta, e nesse primeiro post apresentaremos três, Mean Target Encoding, James-Stein Encoding e Weight of Evidence Encoding.

(Mean) Target Encoding

Mean Target Encoding, encoder da variável resposta pela média, em tradução livre, nos permite reter informações diretas entre determinada categoria e a média da variável resposta. Diferentemente do One Hot Encoding, aqui mantemos a dimensão das categorias (não buscamos uma representação vetorial). Estamos interessados na proporção de determinada categoria com a variável resposta, e é isso que nos interessa.

A proporção que buscamos é a média da variável resposta para a categoria que estamos trabalhando. Matematicamente, tomemos nossa variável resposta, representada por 0 (classe negativa) e 1 (classe positiva), estamos buscando para cada categoriaᵢ a seguinte representação:


Parece um pouco confuso, primeiro vamos entender a variável menopause e como ela está associada com nossa variável resposta.

from sklearn.preprocessing import LabelEncoder

menopause_encoder = OrdinalEncoder()
data["menopause_oe"] = menopause_encoder.fit_transform(
   data.menopause.values.reshape(-1,1))

class_encoder = LabelEncoder()
data["Class_encoded"] = class_encoder.fit_transform(
   data.Class.values.reshape(-1,1))

/home/emanuel/.cache/pypoetry/virtualenvs/posts-z4DaRnQO-py3.8/lib/python3.8/site-packages/sklearn/utils/validation.py:73: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().  
return f(**kwargs)


Para conseguirmos fazer o cálculo da média, transformamos nossa variável resposta em sua representação inteira, onde no-recurrence-events = 0 e recurrence-events = 1. O resultado é visto abaixo.

data[['menopause', 'Class', 'Class_encoded']]

png


Queremos entender qual o valor médio da nossa variável resposta para cada categoria contida em menopause = {premeno, ge40, lt40}. Por exemplo, tomamos a categoria premeno e somamos quantos da classe positiva temos ($\sum (classe\ positiva|premeno)$) e divididos pela quantidade de amostras da categoria premeno, ou seja, a média da categoria para nossa variável resposta.

Em código, temos o seguinte,

data.groupby('menopause')['Class_encoded'].mean()

menopause
ge40       0.271318
lt40       0.285714
premeno    0.320000
Name: Class_encoded, dtype: float64


Basta substituirmos cada classe pelo valor médio da variável resposta. Podemos fazer isso rapidamente usando category_encoders

from category_encoders.target_encoder import TargetEncoder

menopause_te = TargetEncoder()
data["menopause_te_encoded"] = menopause_te.fit_transform(
   X=data.menopause, y=data.Class_encoded)

data[['menopause', 'menopause_te_encoded', 'Class', 'Class_encoded']]

png


Atenção!

Nem tudo é simples como parece, observe que a média pode incluir uma tendência nos dados, dada a quantidade de amostras para cada categoria ou desbalanceamento entre as classes. Além disso, à aplicação de encoders como esse levam a vazamento de informações para o treinamento do algoritmo de aprendizado, esse problema é conhecido como data leakage e para contornamos isso devemos aplicar esse encoder com uma técnica de validação cruzada e removermos o vazamento.

Do ponto de vista de enviesamento pela quantidade de amostras, a figura abaixo mostra a distribuição da média da variável resposta dada a quantidade de registros por categoria.

from pdpbox import info_plots

fig, axes, summary_df = info_plots.target_plot(
   df=data, feature='menopause_oe', feature_name='Menopause Age Categories', target='Class_encoded', num_grid_points=100
)

_ = axes['bar_ax'].set_xticklabels(['ge40', 'lt40', 'premeno'])

png


O Mean Target Encoding nos permitiu representar um dado categórico em uma representação numérica contínua, mas após um olhar minucioso, percebemos que o seu valor é altamente influenciado pela quantidade de amostras de cada categoria, por exemplo, na figura acima temos apenas 7 amostras para a categoria lt40.


Novamente, esse é um dos encoders mais poderosos, mas deixamos aqui um lembrete que deve-se usar a melhor forma de validação para determinados casos, como desbalanceamento da variável resposta junto com uma validação cruzada, por exemplo. Para maiores de detalhes validação como Cross-Fold Target Encoding e Leave-one-out Target Encoding visitar Representing Categorical Data with Target Encoding.

James-Stein Encoding

Por um lado, Mean Target Encoding permite trazermos a média da variável resposta para cada categoria dentro de determinada variável, notamos suas falhas quando tratamos variáveis onde as categorias são desbalanceadas, a média apresenta uma variância bem maior com poucas amostras.

Na tentativa de tratar variáveis onde há um desbalanceamento entre as categorias, surge James-Stein Encoding. Trata-se de um encoder bayesiano que permite-nos trazer informações da variável resposta, assim como o Mean Target Encoding, mas agora consideramos uma média ponderada para as categorias.

James-Stein Encoding busca uma representação pela média da variável resposta, no entanto, consideramos a média global da classe positiva e a média local (que seria a média para cada categoria, como no Mean Target Encoding). Assim, ajustamos as duas informações com um hiperparâmetro $B$ que por enquanto será descrito como o peso entre as classes.

Matematicamente, temos:


Onde mean(y) é a média global da variável resposta (sobre todas as amostras), mean(y_i) é a média da variável resposta para a categoriaᵢ.

Nossa nova representação é uma média ponderada para cada categoria, onde conseguimos diminuir o efeito do desbalanceamento entre as categorias de determinada variável. Ganhamos também um hiperparâmetro B que atua como peso para à média das categoria. Vale ressaltar que cada categoria possui seu próprio valor de B.

O hiperparâmetro B aqui apresentado possui uma fundamentação estatística baseada na estimativa por máxima verossimilhança que pode ser um pouco avançado para esse post, mas pode ser encontrado nas referências abaixo:

A definição que usaremos para B


Substituindo,


Tudo isso parece complicado, mas vamos entender o que essa equação acima quer dizer. Quando uma categoriaᵢ possui uma variância muito alta, o que ocorre quando temos poucas amostras, diminuímos o efeito da média local (não temos certeza no valor da média nossa categoria) e aumentamos a influência da média global. Para o caso onde a variância da categoriaᵢ é baixa (temos certeza que a média é próxima da média esperada) consideramos o efeito da média local e diminuímos a média global. Vale ressaltar que quando B=0, James Stein Encoding tende Mean Target Encoding (JS ⇒ MTE).

Para entendermos o efeito real do James-Stein Encoding vamos tomar a variável tumor_size, que possui uma maior quantidade de categorias.

def string_to_interval(string: str):
   return pd.Interval(int(string.split('-')[0]), int(string.split('-')[1]))

data['count'] = 1
data["tumor_size"] = data["tumor_size"].apply(string_to_interval)

fig, ax = plt.subplots(1, figsize=(14,8))

tumor_size_by_class = data.groupby(['tumor_size', 'Class'])
[['count']].count().reset_index()
sns.barplot(x='tumor_size',
           y='count',
           hue='Class',
           data=tumor_size_by_class,
            ax = ax)

fig.tight_layout()

png


Uma implementação do James-Stein Encoding está disponível no pacote category_encoders e pode ser implementada como à seguir:

from category_encoders import JamesSteinEncoder

tumor_size_js_encoder = JamesSteinEncoder(cols=['tumor_size'])
data['tumor_size_js_encoded'] = tumor_size_js_encoder.fit_transform(
   X = data["tumor_size"].astype(str), y = data['Class_encoded'])

Para título de comparação, façamos também o Mean Target Encoding da feature tumor_size:

tumor_size_te_encoder = TargetEncoder(cols=['tumor_size'])
data['tumor_size_te_encoded'] = tumor_size_te_encoder.fit_transform(
   X = data["tumor_size"].astype(str), y = data['Class_encoded'])


Certo, agora podemos comparar os dois encoders,

data.groupby(['tumor_size'])\
   [['tumor_size_js_encoded', 'tumor_size_te_encoded', 'count']]\
   .agg(numero_registros=('count', 'count'),
        tumor_size_te_encoded=('tumor_size_te_encoded', 'max'),
        tumor_size_js_encoded=('tumor_size_js_encoded', 'max'),)

png


Ambos os encoders são bem parecidos no resultado apresentado, no entanto, James-Stein Encoding apresenta uma robustez quando estamos tratando categorias com baixas amostras. Lembramos novamente que devemos fazer uma validação cruzada para garantir o uso correto desses encoders, o que apresentaremos mais à frente.

WOE (Weight of Evidence) Encoding

Por último, trataremos do Weight of Evidence Encoding, primeiramente introduzido por I. J. Good em 1950 e baseado primordialmente no Teorema de Bayes, o teorema de Bayes define o grau de crença em uma hipótese (H) antes e depois de se considerar as evidências (E).

Sua derivação matemática pode ser encontrada Weight of Evidence: A Brief Survey, mas o que devemos ter em mente é como interpretar sua formulação. Para isso tomemos sua definição:


onde, (% targetᵢ +) é a proporção da target positiva, para a categoriaᵢ. Similarmente, (% targetᵢ -) é a proporção da target negativa para categoriaᵢ.

Diferentemente do encoders anteriores onde usamos a média para representar uma determinada categoria, aqui estamos interessados em encontrar a proporção entre a quantidade de amostras para a classe positiva e quantidade de amostras para a classe negativa.

O termo evidência se relaciona ao fato de estarmos interessados em quanta informação (evidência) a proporção entre as classes positivas e negativas tem para determinada categoria.

WOE encoding é comumente usado no contexto de crédito, onde a variável resposta que estamos tentando prever é a inadimplência, mas é facilmente expandido para outros contextos. Sua função é medir a força de cada categoria presente na feature usando a proporção entre as classe positiva e negativa.

Para exemplificarmos, vamos seguir os seguintes passos para uma categoriaᵢ:

  • Calculamos o número de vezes que a target positiva acontece, (nᵢ +)
  • Calculamos o número de vezes que a target negativa acontece, (nᵢ -)
  • Calculamos a porcentagem para (% targetᵢ +) e (% targetᵢ -)
  • Calculamos o WOEᵢ tomando o logaritmo entre as proporções entre as classes.

Para exemplificarmos, tomemos a variável age.

fig, ax = plt.subplots(1, figsize=(14,8))

age_by_class = data.groupby(['age', 'Class'])
[['count']].count().reset_index()
sns.barplot(x='age',
           y='count',
           hue='Class',
           data=age_by_class,
            ax = ax)

fig.tight_layout()

png


Por exemplo, o WOE para a categoria [30–39] é:

  • n + = 15 (y=recurrence-events)
  • n - = 21 (y=no-recurrence-events)
  • recurrence-events = 85
  • no-recurrence-events = 201


from category_encoders import WOEEncoder

age_woe_encoder = WOEEncoder(cols=['age'],
                             sigma=0.0,
                            regularization=0.0,)

data['age_woe_encoded'] = age_woe_encoder.fit_transform(
   X = data["age"], y = data['Class_encoded'])

/home/emanuel/.cache/pypoetry/virtualenvs/posts-z4DaRnQO-py3.8/lib/python3.8/site-packages/pandas/core/series.py:679: RuntimeWarning: divide by zero encountered in log
 result = getattr(ufunc, method)(*inputs, **kwargs)

data.groupby(['age'])\
   ['age_woe_encoded']\
   .agg(age_woe_encoded=('age_woe_encoded', 'max'))

png


Weight of Evidence Encoding nesse contexto pode ser usado para transformarmos nossas variáveis categóricas em variáveis contínuas, mas suas aplicações vão além, podendo ser usado com seletor de variáveis importantes tomando o conceito de Valor da Informação (IV, information value).

James-Stein Encoding versus Mean Target Encoding versus WOE Encoding

Para exemplificarmos o correto uso dos encoders bayesianos, selecionamos algumas variáveis para construirmos um modelo de base, exemplificando o correto uso das validações para esses tipos de encoder. Nosso objetivo aqui não é encontrar o melhor modelo possível, mas sim exemplificar as aplicações.

data['tumor_size'] = data['tumor_size'].astype(str)
columns_to_model = ['tumor_size',
                    'age',
                    'breast',
                    'breast_quad',
                    'irradiat',
                    'menopause_oe',
                    'menopause_te_encoded',
                    'Class_encoded'
]
def train_and_validator(data: pd.DataFrame,
                        encoder,
                        columns_to_encoder: list = ['tumor_size'],
                        target_variable: str = 'Class'):

   from sklearn.ensemble import RandomForestClassifier
   from sklearn.model_selection import (GridSearchCV,
                                        train_test_split)
   from sklearn.pipeline import Pipeline
   from sklearn.metrics import classification_report

   X_train, X_test, y_train, y_test = train_test_split(
       data[columns_to_encoder],
        data[target_variable],
        stratify=data[target_variable],
        random_state=42)

   model = Pipeline([
       ('encode_categorical', encoder(cols=columns_to_encoder)),
       ('classifier', RandomForestClassifier())
   ])

   params = {
     'classifier__n_estimators': [50, 100, 200],
     'classifier__max_depth': [4, 6, 8]
   }

   grid = GridSearchCV(model, param_grid=params, cv=5).fit(X_train, y_train)
   predict_test = grid.predict(X_test)
       return grid, print(classification_report(
       y_true = y_test,
       y_pred = predict_test)
   )

print("James-Stein Encoding")
_ = train_and_validator(data=data[columns_to_model],
                        encoder=JamesSteinEncoder,
                        columns_to_encoder=['age',
                                            'breast',
                                            'breast_quad',
                                            'irradiat',
                                            'tumor_size'],
                        target_variable='Class_encoded')James-Stein Encoding
             precision    recall  f1-score   support
          0       0.78      0.88      0.83        51
           1       0.57      0.38      0.46        21
accuracy                              0.74        72
macro avg  0.67     0.63      0.64        72
weighted avg0.72     0.74     0.72        72

print("Mean-Target Encoding")
_ = train_and_validator(data=data[columns_to_model],
                        encoder=TargetEncoder,
                        columns_to_encoder=['age',
                                            'breast',
                                            'breast_quad',
                                            'irradiat',
                                            'tumor_size'],
                        target_variable='Class_encoded')

Mean-Target Encoding
             precision    recall  f1-score   support
          0       0.74      0.88      0.80        51
          1       0.45      0.24      0.31        21    
   accuracy                           0.69        72  
macro avg  0.60      0.56      0.56        72
weighted avg0.66   0.69      0.66        72

print("WOE Encoding")
_ = train_and_validator(data=data[columns_to_model],
                        encoder=WOEEncoder,
                        columns_to_encoder=['age',
                                            'breast',
                                            'breast_quad',
                                            'irradiat',
                                            'tumor_size'],
                        target_variable='Class_encoded')
WOE Encoding
             precision    recall  f1-score   support
          0       0.75      0.88      0.81        51
          1       0.50      0.29      0.36        21    
accuracy                                0.71        72  
macro avg   0.62      0.58      0.59        72
weighted avg0.68      0.71      0.68        72


Certo, até o presente momento temos a comparação entre os três encoders acima, e para esse determinado problema o Mean Target Encoding performa melhor, no entanto, para cada determinado problema de modelagem, devemos escolher aquele que apresenta mais ganho para nossa solução.

Conclusão

Nessa primeira parte desse texto, focamos em analisar alguns dos principais encoders e como podemos utilizá-lós no nosso dia a dia. Encoders clássicos nos permitem uma representação rápida e simples que funciona para quase todos os problemas de classificação e regressão em machine learning. No entanto, casos como Ordinal Encoding levam em conta a ordem das classes pré-definidas o que pode levar à viés nos nossos dados. Por outro lado, One Hot Encoding permite expressar com maior granularidade as categorias dentro de determinada feature, mas com o aumento da quantidade de categorias nossa transformação torna-se altamente custosa com o aumento de novas features encodadas.

A grande classe aqui tratada, foram de encoders que usam a variável resposta como estimação para a transformação. No entanto, tais transformadores devem levar em conta uma validação cruzada para evitar overfitting no nosso conjunto de treino. A função train_and_validator nos fornece uma forma de calcular e validar nosso modelo usando o encoder de nossa preferência.

Na próxima parte do nosso texto, iremos focar em encoders não tão comuns mas que podem apresentar bastante ganho dependendo da nossa variável resposta. Iremos tratar também de encoders que usam classificadores como forma definir as melhores transformações para cada categoria. Além disso, iremos tratar o CatBoost Encoding que é o que torna o estimadores do CatBoost bastantes eficazes.

Bônus

Uma vez que passamos pelo principais encoders, gostaríamos de entender o quão performáticos eles podem ser dado a quantidade de categorias em cada feature do nosso dado. Para tal, Denis Vorotyntsev construiu um repositório para testar os principais encoders e sua performance em diferentes datasets.

Outro ponto que devemos ter atenção que encoders bayesianos são os que nos fornecem melhores performance em problemas de machine learning, pois conhecem o comportamento da variável resposta para determinado subset dos dados de treinamento (fold), para isso devemos no atentar para boas estratégias de validação para cada encoder aqui citado, mas vamos deixar isso para uma próxima conversa.



Referências

Mais artigos da Datarisk

Ver todos os artigos
© Copyright 2017 - 2022 | Datarisk.io | Todos os direitos reservados