Lab|208

Clusterizando conhecimento.


Aplicando KNN no Banco de Dados Iris

16 May 2020 - Anthony João Bet

Agora que já conhecemos o funcionamento do KNN iremos investigar os dados do Iris, aplicar o algoritmo e testar os resultados. Tudo isso será feito utilizando a biblioteca pandas para ler os dados e sklearn para aplicar o modelo.

Baixando os dados

Você pode encontra os dados para o projeto no UCI Machine Learning Repository ou diretamente pelo link, o nome do arquivo é iris.data. O UCI foi criado em 1987 e hoje agrega mais de 490 datasets para diversos tipos de projetos.

Descrição dos dados encontrada no UCI:

  • sepal length in cm
  • sepal width in cm
  • petal length in cm
  • petal width in cm
  • class:
    • Iris Setosa
    • Iris Versicolour
    • Iris Virginica

Se abrirmos o arquivo de dados é isso que encontraremos:

dados iris

As quatro primeiras colunas representam as características de uma planta e a última coluna, sua espécie. Então a pergunta que queremos responder é: Dado um novo conjunto de características ['sepal_length','sepal_width','petal_length','petal_width'], qual é a espécie dessa planta?

Bora pro codigo

Para ler o dataset nós iremos utilizar a biblioteca pandas;

import pandas as pd
 
def carregar_dados(path):
   """Função para ler os dados"""
   df = pd.read_csv(path, names=['sepal_length','sepal_width','petal_length','petal_width','class'])
   return df
 
df = carregar_dados('iris.data')

Em geral, em problemas reais muitos dados podem estar faltando no datset, isso pode acontecer por diversos motivos como a falha em algum sensor, erro humano, aleatoriedade no sistema e por aí vai. Os dados que estamos utilizando não possuem nem um erro desse tipo, então não iremos nos preocupar com isso agora. Tratar os dados antes de aplicar qualquer modelos é uma parte essencial do processo, mas iremos falar sobre isso quando chegar a hora.

No UCI podemos encontrar informações estatísticas sobre os dados:

Summary Statistics:
           Min  Max   Mean    SD   Class Correlation
   sepal length: 4.3  7.9   5.84  0.83    0.7826  
   sepal width:  2.0  4.4   3.05  0.43   -0.4194
   petal length: 1.0  6.9   3.76  1.76    0.9490  (high!)
   petal width:  0.1  2.5   1.20  0.76    0.9565  (high!)
 
 
Distribuição das classes: 33.3% para cada uma das 3 classes.

Isso nos diz que o tamanho das pétalas está altamente correlacionado com sua classe e que número de amostras está igualmente dividido entre o total dos dados, isso é muito importante, pois significa, de forma grosseira, que cada classe possui a mesma importância.

Nossos dados estão todos em um dataframe, precisamos separá-los em dois grupos X e Y, onde X será um novo conjunto apenas com as características (input) e Y um conjunto com as classes:

X = df[['sepal_length','sepal_width','petal_length','petal_width']]
Y = df['class']

Outra coisa que precisamos mudar é a forma como identificamos nossas classes, no momento cada classe é identificada por uma string, e queremos que cada classe seja identificada por um número inteiro. Essa transformação pode ser feita facilmente com a biblioteca sklearn, dessa forma:

from sklearn.preprocessing import LabelEncoder
 
encoder = LabelEncoder()
encoder.fit(Y)
Y = encoder.transform(Y)

Se printarmos novamente, é isso que temos:

print(Y)
  iris python3 classification.py
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2]

Meio feio, mas é isso mesmo que queremos.

Agora precisamos preparar os dados para o treinamento. Para fazer isso os dados devem embaralhados e separados em grupo de treino e grupo de deste. O grupo de treino será utilizado para treinar o algoritmo, e o grupo de teste para validar este treino, ou seja, testar o algoritmo em dados que ele nunca viu, para garantir sua eficácia.

Novamente iremos utilizar a biblioteca sklearn. Existem dois parâmetros que precisamos entender nessa função, o parâmetro test_size regula a porcentagem do dataset que será separado como teste e com shuffle escolhemos se queremos que os dados sejam embaralhados antes de serem separados, se olharmos para a figura anterior veremos que as classes estão dispostas em sequência, sendo assim precisamos embaralhar nossos dados;

from sklearn.model_selection import train_test_split
 
X_train, X_test, Y_train, Y_test = train_test_split( X,
                                                     Y,
                                                     test_size = 0.3,
                                                     shuffle = True
                                                    )

Tudo pronto! Agora podemos finalmente aplicar o algoritmo nos dados;

from sklearn.neighbors import KNeighborsClassifier
 
modelo = KNeighborsClassifier(n_neighbors = 5,
                              algorithm = 'brute'
                              )
 
modelo.fit(X_train, Y_train)

Não é necessário informar que estamos utilizando distância euclidiana, isso já é escolhido por padrão. O parâmetro n_neighbors define quantos vizinhos vamos utilizar para contagem, devemos escolher um número que não gere empates e algorithm = 'brute' significa que o modelo irá calcular a distância entre o novo ponto e todos os outros pontos dos dados de treino, este é o jeito mais pedestre e menos eficiente, mas como nosso banco de dados é pequeno isso não será um problema.

É isso aí, após aplicar o função fit nos dados o modelo estará “treinado”, agora podemos utilizar o modelo para fazer predições. Vamos escolher um ponto do conjunto de teste e ver qual a classificação:

#novo ponto será o último ponto em X_test
novo_ponto = X_test.values[-1]
print(f'Novo ponto: {novo_ponto}')
 
prediction = modelo.predict([novo_ponto])
print(f'Predição: {prediction}, Classe correta: {Y_test[-1]}')

Que nos retorna:

  iris python3 classification.py

Novo ponto: [6.3 3.3 4.7 1.6]
 
Predição: [1], Classe correta: 1

Um ponto é muito pouco para sabermos se a classificação é boa ou não, a acuracia do modelo é dada pela razão entre o número de acertos e o número de tentativas.

Podemos fazer o seguinte, vamos comparar o conjunto de predição com o conjunto de teste (prediction == Y_test) , isso irá comparar os dois arrays entrada por entrada, em ordem, e irá retornar um array de verdadeiro ou falso
([True, True, False, True, ... , False]), podemos então somar esse array, isso nos dará o número de acertos já que verdadeiros são representados por 1 e falsos por 0, depois basta dividir isso pelo tamanho da conjunto.

prediction = modelo.predict(X_test)
acertos = (prediction == Y_test).sum()
erros = len(X_test) - acertos
acuracia = acertos / len(prediction)
 
print(f'Acuracia: {acuracia}')
print(f'Acertos:  {acertos}')
print(f'Erros:    {erros}')

Ao rodarmos o programa, esse é o resultado que obtemos:

  iris python3 classification.py

Acuracia: 0.9555555555555556
Acertos:  43
Erros:    2

Note a que a cada vez que o programa for executado, o resultado será um pouco diferente, isso ocorre porque estamos escolhendo aleatoriamente os grupos de treino e teste a cada vez que rodamos o programa, para obtermos uma estimativa mais precisa da acurácia seria necessário fazer uma média, rodando varias vezes o programa. Depois de fazer isso você pode variar o número de K-vizinhos e observar como a acurácia reage a isso.

Eu não me preocupei em explicar como funciona cada biblioteca e não mostrei todas as suas ferramenta, mas deixei linkado as paginas com as documentações. É super difícil entender o funcionamento de cada função dessa na primeira vez, o que eu mostrei aqui tem o intuito se ser um primeiro passo, então não deixe de ler a documentação e aplicar em outros problemas.

Obrigado pela leitura e até a próxima.

FIM!