Problemas de classificação representam uma ampla categoria de problemas de machine learning que envolvem a previsão de valores dentro de um conjunto finito e discreto de casos.

Neste exemplo, construiremos um classificador para prever a qual espécie uma flor pertence.

Leitura dos dados


In [2]:
import pandas as pd

iris = pd.read_csv('../datasets/iris.csv')

In [3]:
# Exiba informações sobre o dataset
iris.info()


<class 'pandas.core.frame.DataFrame'>
RangeIndex: 150 entries, 0 to 149
Data columns (total 5 columns):
Sepal_length    150 non-null float64
Sepal_width     150 non-null float64
Petal_length    150 non-null float64
Petal_width     150 non-null float64
Class           150 non-null object
dtypes: float64(4), object(1)
memory usage: 5.9+ KB

In [3]:
# Exiba as classes presentes nesse dataset usando o método unique() na coluna "Class"
iris['Class'].unique()


Out[3]:
array(['Iris-setosa', 'Iris-versicolor', 'Iris-virginica'], dtype=object)

In [4]:
# Use o método describe() para exibir estatísticas sobre o dataset
iris.describe()


Out[4]:
Sepal_length Sepal_width Petal_length Petal_width
count 150.000000 150.000000 150.000000 150.000000
mean 5.843333 3.054000 3.758667 1.198667
std 0.828066 0.433594 1.764420 0.763161
min 4.300000 2.000000 1.000000 0.100000
25% 5.100000 2.800000 1.600000 0.300000
50% 5.800000 3.000000 4.350000 1.300000
75% 6.400000 3.300000 5.100000 1.800000
max 7.900000 4.400000 6.900000 2.500000

Visualização dos dados


In [15]:
# Criação de um scatterplot dos valores as colunas "Sepal_length" e "Sepal_width"
import matplotlib.pyplot as plt
%matplotlib inline

sl = iris['Sepal_length']
sw = iris['Sepal_width']

# Crie um scatterplot dessas duas propriedades usando a função plt.scatter()
# Atribua cores diferentes a cada exemplo do dataset de acordo com a classe à qual ele pertence
plt.scatter(sl[iris['Class'] == 'Iris-setosa'], sw[iris['Class'] == 'Iris-setosa'], color='red')
plt.scatter(sl[iris['Class'] == 'Iris-versicolor'], sw[iris['Class'] == 'Iris-versicolor'], color='green')
plt.scatter(sl[iris['Class'] == 'Iris-virginica'], sw[iris['Class'] == 'Iris-virginica'], color='blue')
# Atribua labels aos eixos X e Y
plt.xlabel('Sepal Length')
plt.ylabel('Sepal Width')
# Exiba o gráfico
plt.show()



In [16]:
# Criação de um scatterplot dos valores as colunas "Petal_length" e "Pepal_width"
pl = iris['Petal_length']
pw = iris['Petal_width']

# Crie um scatterplot dessas duas propriedades usando a função plt.scatter()
# Atribua cores diferentes a cada exemplo do dataset de acordo com a classe à qual ele pertence
plt.scatter(pl[iris['Class'] == 'Iris-setosa'], pw[iris['Class'] == 'Iris-setosa'], color='red')
plt.scatter(pl[iris['Class'] == 'Iris-versicolor'], pw[iris['Class'] == 'Iris-versicolor'], color='green')
plt.scatter(pl[iris['Class'] == 'Iris-virginica'], pw[iris['Class'] == 'Iris-virginica'], color='blue')
# Atribua labels aos eixos X e Y
plt.xlabel('Petal Length')
plt.ylabel('Petal Width')
# Exiba o gráfico
plt.show()


Classificação de espécies

Usaremos a classe LogisticRegression do scikit-learn para construir o classificador.


In [9]:
X = iris.drop('Class', axis=1) # Crie um DataFrame com todas as features através da remoção da coluna "Class"
t = iris['Class'].values # Pegue os valores da coluna "Class"
RANDOM_STATE = 4321

# Use o método train_test_plit() para dividir os dados em dois conjuntos
from sklearn.model_selection import train_test_split

Xtr, Xts, ytr, yts = train_test_split(X, t, random_state=RANDOM_STATE)

In [10]:
# Use o conjunto de treinamento para construir um modelo LogisticRegression
from sklearn.linear_model import LogisticRegression

lr = LogisticRegression().fit(Xtr, ytr) # Crie e treine um objeto LogisticRegression aqui

In [11]:
# Use o método score() do objeto LogisticRegression para avaliar a acurácia do modelo
lr.score(Xtr, ytr)


Out[11]:
0.9553571428571429

In [12]:
# Use o método score() do objeto LogisticRegression para avaliar a acurácia
# do modelo no conjunto de teste
lr.score(Xts, yts)


Out[12]:
0.92105263157894735

Inspeção dos resultados

Cálculos como o realizado acima geralmente não representam bem aquilo que queremos avaliar quando estamos resolvendo um problema de classificação. Ele apenas retorna o erro médio obtido entre as previsões e as classes reais do dataset de treinamento.

Pense, por exemplo, no que aconteceria se você estivesse treinando um modelo para classificar se uma pessoa possui ou não uma doença em um contexto onde sabe-se que, normalmente, 99% ads pessoas não têm essa doença. O que poderia dar errado se calculássemos a taxa de erros e acertos do modelo como uma forma de avaliá-lo? Dica: qual seria o valor dessa taxa de erros/acertos para um classificador "hardcoded" que sempre retorna 0 (isto é, ele sempre diz que a pessoa não tem a doença)?

Métricas simples de acurácia geralmente não são recomendadas para problemas de classificação. Existem pelo menos três métricas que costumam ser usadas dependendo do contexto:

  • Precisão: esse número responde à seguinte pergunta: dentre os exemplos que o classificador disse que pertencem a uma classe, quantos de fato pertencem a ela?
  • Recall: esse número responde a uma pergunta levemente diferente da mostrada na Precisão: dentre os exemplos que realmente pertencem a uma classe, quantos o classificador conseguiu identificar?
  • F1-Score: essa métrica representa uma soma ponderada de precisão e recall - ela não apresenta uma interpretação intuitiva, mas a ideia é que o f1-score representa um meio-termo entre precisão e recall.

Source: https://en.wikipedia.org/wiki/Precision_and_recall

Outras métodos de avaliação para mdodelos de classificação incluem análise de curva ROC e, relacionada a essa técnica, o conceito de área sob a curva ROC.

Qual dessas métricas você priorizaria para o exemplo do classificador de doença descrito no parágrafo anterior? Quais são os custos para falsos positivos e falsos negativos nesse caso?


In [13]:
# scikit-learn implementa uma função chamada "classification_report" que calcula as três métricas acima
# para um dado classificador.
from sklearn.metrics import classification_report

# Use essa função para exibir as métricas de classificação no modelo treinado anteriormente
# http://scikit-learn.org/stable/modules/generated/sklearn.metrics.classification_report.html
print(classification_report(yts, lr.predict(Xts)))


                 precision    recall  f1-score   support

    Iris-setosa       1.00      1.00      1.00        13
Iris-versicolor       0.83      0.91      0.87        11
 Iris-virginica       0.92      0.86      0.89        14

    avg / total       0.92      0.92      0.92        38

Outra técnica útil para inspecionar os resultados gerados por um modelo de classificação é a checagem da matriz de confusão. A matriz de confusão é uma matriz de dimensões K x K (onde K é o número de classes que o classificador pode identificar) que mostra, na posição (i,j), quantos exemplos pertencentes à classe i foram classificados como pertencentes à classe j.

Isso pode trazer insights a respeito de quais classes possuem a maior quantidade de classificações incorretas, por exemplo, e que portanto poderiam receber uma maior atenção por parte da pessoa cientista de dados.


In [14]:
from sklearn.metrics import confusion_matrix

# Use a função confusion_matrix para entender quais classes estão sendo classificadas incorretamente
# http://scikit-learn.org/stable/modules/generated/sklearn.metrics.confusion_matrix.html
confusion_matrix(yts, lr.predict(Xts))


Out[14]:
array([[13,  0,  0],
       [ 0, 10,  1],
       [ 0,  2, 12]])

No exemplo acima, o que você investigaria mais a fundo? Quais classes o classificador tem mais dificuldade de identificar corretamente?