Como selecionar as melhores features para seu modelo de Machine Learning

Conheça algumas técnicas para selecionar as features mais relevantes para seu modelo

Photo by Will Myers on Unsplash

Há quem diga que saber criar e selecionar as melhores features para um modelo de Machine Learning é uma arte. De fato é. Assim como a arte, você precisa saber elaborar, construir e refinar o seu modelo. Contudo, feature selection não precisa ser apenas arte, podemos utilizar de outros artifícios e técnicas para otimizar essa etapa. E o melhor: você não precisa ser um Picasso para isso.

Antes de começar, leia isso

Vale lembrar que muitas dessas técnicas dependem inicialmente do algoritmo que você quer usar. Por exemplo, uma feature pode ser considerada importante para um modelo que considera relacionamentos lineares — LinearSVC, Regressão Linear — , ao mesmo tempo em que não é importante para um modelo que consegue identificar relacionamentos não lineares — como Decision Trees, Random Forest, etc.

Se tiver dúvida sobre qual estimador utilizar para seu modelo, recomendo dar uma consultada nessa cheat sheet do scikit-learn. Dito isso, vamos para as dicas!

Matriz de Correlação

Uma das formas mais simples para começar a entender como suas variáveis se relacionam é através de uma Matriz de Correlação — do inglês Correlation Matrix. Trata-se de uma tabela que mostra os coeficientes de correlação entre as variáveis. O Pandas possui a função .corr(), que retorna os coeficientes para cada feature do seu dataset. Por exemplo, olhe o código abaixo, onde utilizei o dataset de iris:

Para cada feature X do meu dataset, ele irá mostrar como ela se relaciona com a feature Y. A partir dessa visualização, você pode saber quais features possuem uma correlação positiva — quando se movem na mesma direção, por exemplo: quanto mais alguém estuda, maior é sua nota na prova; ou quando possuem uma correlação negativa — quando se movem em direções opostas, por exemplo: quanto maior a velocidade de um carro, menor é o tempo para chegar ao destino.

É possível também gerar uma heatmap para facilitar a visualização. Graças a biblioteca Seaborn, é bem simples fazer isso:

import matplotlib.pyplot as plt
import seaborn as snsplt.figure(figsize=(10, 7))
sns.heatmap(df.corr(),
annot = True,
fmt = '.2f',
cmap='Blues')
plt.title('Correlação entre variáveis do dataset de Iris')
plt.show()

O valor mostrado para cada correlação vai de -1 — que indica uma correlação negativa perfeita — a +1 — uma correlação positiva perfeita. Vale lembrar que a função .corr() traz, por padrão, a correlação de Pearson, mostrando um relacionamento linear entre as variáveis. Em casos onde há um relacionamento não-linear, a matriz pode não ser uma boa medida.

Feature Importance

Quando você está mexendo com ensembles, como RandomForest e XGBoost, você pode contar com um aliado para identificar as features mais importantes de seu modelo: o atributo feature_importance_.

O feature_importance_ irá retornar um array onde cada elemento dele é uma feature do seu modelo. Ele irá dizer, em proporções, quão importante aquela feature é para o modelo, onde quanto maior o valor, mais importante a feature é para o modelo. Olhe abaixo um exemplo utilizando o dataset de iris:

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
import pandas as pd
import seaborn as sns# Carregando dataset
data = load_iris()
X = data.data
y = data.target# Criando conjunto de treino e teste
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)# Treinando modelo
model = RandomForestClassifier()
model.fit(X_train, y_train)# Mostrando importância de cada feature
model.feature_importances_
Out >>> array([0.12689503, 0.02375714, 0.46027916, 0.38906867])

Como o dataset de iris possui apenas quatro features (sepal length, sepal width, petal length, petal width), me é retornado um array com quatro elementos. Se você somar todos eles, verá que o resultado será 1. Ao analisar esse array, podemos ver que a feature mais importante para o algoritmo de Random Forest foi a terceira, petal lenght. Se quiser gerar uma visualização para ver as features mais importantes, o código é similar a esse:

importances = pd.Series(data=model.feature_importances_, index=data.feature_names)
sns.barplot(x=importances, y=importances.index, orient='h').set_title('Importância de cada feature')

Cuidado com esse método! As vezes, os valores mostrados pelo feature_importances_ pode ser enviesado dependendo dos parâmetros definidos na criação do objeto. O que isso quer dizer? Evite usar os parâmetros default do RandomForestClassifier(). Confira esse artigo para entender um pouco mais sobre isso.

Consulte a documentação do sklearn para saber mais

Sklearn SelectKBest

Uma outra forma de selecionar features é através de testes estatítisticos univariados — do Inglês univariate statistical test. A função SelectKBest do sklearn é um bom exemplo disso. Seu funcionamento é bem simples: você informa para a função que quer selecionar apenas as K maiores features do seu dataset com base em um teste estatístico. Por exemplo: no nosso dataset de iris, eu posso pedir ao SelectKBest apenas as 2 melhores features:

from sklearn.datasets import load_iris
from sklearn.feature_selection import chi2
from sklearn.feature_selection import SelectKBest
data = load_iris()
X = data.data
y = data.targetX = SelectKBest(chi2, k=2).fit_transform(X, y)

Como disse anteriormente, o dataset possui quatro features. Após esse processo, ele terá apenas dois, que serão aqueles que atingiram um melhor score no teste estatístico que escolhi, que no caso foi chi2. A parte boa do SelectKBest é que ele funciona para problemas de regressão também, com testes estatísticos como f_regression e mutual_info_regression. A parte ruim é que escolher o número K ideal é difícil. Muitas das vezes, o valor atribuído é bem empírico, mas você pode tentar contornar isso com o primo legal do SelectKBest: SelectPercentile, que irá selecionar X% das melhores features.

Saiba mais sobre SelectKBest na documentação do sklearn

Sklearn RFE

Particulamente, eu curto bastante o RFE. O resultado dele é bem consistente, e seu principal trade-off — o tempo — não chega a ser uma fator tão negativo em meus projetos.

Assim como seu nome diz — Recursive Feature Elimination — , o RFE funciona da seguinte forma: ele irá treinar seu modelo utilizando todo seu conjunto inicial, com todas as features e data points que vierem nele. Após o primeiro treino, o RFE irá verificar a importância das features — utilizando atributos como coef_ ou feature_importances_e, recursivamente, irá remover as features menos importantes do dataset e treinar o modelo novamente. Ele fará isso até chegar a um número ideal de features. Veja abaixo uma aplicação do RFE, onde informo que quero remover uma feature de cada vez. Ou seja, cada vez que o modelo for treinado, ele irá remover uma feature. O parêmetro n_features_to_selectpode ser passado para informar a quantidade de features que quer selecionar. Se ele for nulo, o RFE escolherá metade do total de features.

from sklearn.datasets import load_iris
from sklearn.svm import LinearSVC
from sklearn.feature_selection import RFEdata = load_iris()
X = data.data
y = data.target#
model = LinearSVC()
rfe = RFE(model, step=1).fit(X, y)

Eu gosto de utilizar o RFE em modelos que possuem atributos coef_, como SVM, mas ele também funciona bem com Ensembles — atente-se aos pontos que falei no tópico de Feature Importance. Como disse anteriormente, o principal trade-off dessa função é o tempo: se você tiver um dataset com alta dimensionalidade — muitas features e/ou muitos data points — esse processo tende a demorar muito.

Você pode diminuí-lo um pouco ao informar um número maior de steps, ou informar uma proporção de features para remover. Por exemplo: ao invés de remover uma feature de cada vez — step=1 — , você pode remover 5% das features — step=0.05.

Sklearn SelectFromModel

O SelectFromModel é uma outra função do sklearn que funciona da seguinte forma: a partir de um modelo (fittado ou não), o SFM irá remover todas as features que não passem do threshold que você informa em seus argumentos. Essa função soou familiar? De fato, o funcionamento do SelectFromModel é bem parecido com o RFE, contudo, o SFM é menos robusto, já que ele baseia sua seleção apenas no threshold informado, enquanto o RFE recursivamente remove as features através de iterações.

Coefficients

Para finalizar nossa lista de hoje eu quero falar sobre coeficientes. Em Álgebra Linear, um coeficiente é o fator multiplicativo em uma expressão algébrica. Em Machine Learning, eles são um amigão da vizinhança quando o papo é entender quais features de nosso modelo tem maior impacto. Isso porque o peso de um coeficiente é capaz de nos indicar qual direção nosso algoritmo está seguindo para fazer uma previsão.

Eu curto utilizar visualização de coeficientes em projetos de NLP. Recentemente trabalhei na construção de um atendente virtual e, utilizar tal visualização me permitiu identificar que meu modelo possuía coeficientes altos para palavras como “atenciosamente”, o que me ajudou a melhorar etapas de pré-processamento.

Visualização de coeficientes

Para te ajudar a criar e entender suas próprias visualizações de coeficientes, vou deixar a dica desse post bem completo que fala um pouco mais sobre o assunto. Abaixo, você pode conferir o código que gera a visualização, mostrado no post que indiquei:

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.svm import LinearSVC
import matplotlib.pyplot as pltdef plot_coefficients(classifier, feature_names, top_features=20):
coef = classifier.coef_.ravel()
top_positive_coefficients = np.argsort(coef)[-top_features:]
top_negative_coefficients = np.argsort(coef)[:top_features]
top_coefficients = np.hstack([top_negative_coefficients, top_positive_coefficients])
# create plot
plt.figure(figsize=(15, 5))
colors = [‘red’ if c < 0 else ‘blue’ for c in coef[top_coefficients]]
plt.bar(np.arange(2 * top_features), coef[top_coefficients], color=colors)
feature_names = np.array(feature_names)
plt.xticks(np.arange(1, 1 + 2 * top_features), feature_names[top_coefficients], rotation=60, ha=’right’)
plt.show()
cv = CountVectorizer()
cv.fit(data)
print(len(cv.vocabulary_))
print(cv.get_feature_names())
X_train = cv.transform(data)svm = LinearSVC()
svm.fit(X_train, target)
plot_coefficients(svm, cv.get_feature_names())

E aí, curtiu as técnicas que você pode utilizar para identificar as melhores features de seu dataset? Caso esteja na dúvida sobre qual utilizar, minha dica é tentar todas elas e ver qual se aplica melhor ao seu problema. Caso queira ficar sabendo sobre mais assuntos relacionados a ML e DS, me segue lá no Twitter (onde eu falo besteiras) e no Linkedin (onde eu falo besteiras de forma mais séria). Não esquece de falar que veio pelo blog, tá bom? :)

--

--