IV Semana Acadêmica Integrada da UFT Gurupi: MiniCurso: Machine Learning com R
IV Semana Acadêmica Integrada da UFT Gurupi
Câmpus de Gurupi da UFT, Gurupi - Tocantins
De 04/11 a 07/11 de 2024
Veremos uma introdução prática e objetiva, cobrindo os principais conceitos e aplicações reais relevântes para as áreas de Agronomia, Engenharia Florestal, Biotecnologia, Química e Agroindústria.
Objetivos Gerais:
- Introduzir os conceitos básicos de Machine Learning (ML).
- Demonstrar o uso de R para modelagem preditiva.
- Aplicar algoritmos de ML em conjuntos de dados.
- Incentivar o pensamento crítico sobre a aplicação de ML em problemas práticos.
Introdução ao Machine Learning (15 minutos)
Apresentar os principais conceitos de Machine Learning, supervisionado e não supervisionado.
Tópicos: Definição de ML: O que é e por que é importante?
Tipos de ML
Supervisionado: Regressão e classificação.
Não supervisionado: Agrupamento (clustering).
Conceitos fundamentais: Features (variáveis preditoras), target (variável de resposta), treino, validação e teste.
Material de Apoio:
Slides simples com exemplos visuais aplicados às áreas de interesse dos alunos (ex.: previsões de produtividade agrícola, classificação de espécies, etc.).
Introdução ao R e ao RStudio aplicado ao Machine Learning
Definindo a pasta de trabalho.
Code
setwd("~/Ciência de Dados/MC Machine Learning com R")Pacotes básicos: * readr e readxl para importação de dados
* tidyverse para manipulação de dados
* caret para machine learning
Code
#install.packages("tidyverse")
library(tidyverse)── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.5
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ ggplot2 3.5.1 ✔ tibble 3.2.1
✔ lubridate 1.9.3 ✔ tidyr 1.3.1
✔ purrr 1.0.2
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
Importação dos Dados
Importação de dados com readr.
Code
library(readxl)
tabela <- readxl::read_xlsx("soja.xlsx")Tarefas comuns de pré-processamento de dados
Limpeza e tratamento de dados (ex.: valores faltantes, variáveis categóricas).
Seleção de variáveis relevantes para o modelo.
Resumos Estatísticos e Visualizações dos Dados
Code
glimpse(tabela)Rows: 200
Columns: 6
$ pH <dbl> 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.1, 6.1, 6.1, 6.1, 6.1, 6.1, 6.1…
$ MO <dbl> 3.8, 3.6, 3.6, 3.5, 3.5, 3.5, 3.4, 3.4, 3.4, 3.3, 3.3, 3.3, 3.3…
$ P <dbl> 33.4, 32.7, 32.1, 31.8, 31.4, 30.9, 30.5, 30.5, 30.4, 30.2, 30.…
$ K <dbl> 34.3, 34.1, 33.9, 33.8, 33.6, 33.6, 33.4, 33.3, 33.3, 33.2, 33.…
$ Arg <dbl> 40.3, 39.3, 37.3, 36.1, 35.9, 34.8, 34.2, 34.0, 33.7, 33.4, 33.…
$ Classe <chr> "alta", "alta", "alta", "alta", "alta", "alta", "alta", "alta",…
Code
summary(tabela) pH MO P K Arg
Min. :3.60 Min. :0.900 Min. :14.10 Min. :25.30 Min. :10.30
1st Qu.:5.90 1st Qu.:2.175 1st Qu.:22.70 1st Qu.:29.25 1st Qu.:22.00
Median :6.55 Median :2.500 Median :25.10 Median :30.40 Median :25.10
Mean :6.50 Mean :2.494 Mean :24.86 Mean :30.28 Mean :25.28
3rd Qu.:7.20 3rd Qu.:2.800 3rd Qu.:26.90 3rd Qu.:31.60 3rd Qu.:28.32
Max. :8.90 Max. :3.800 Max. :33.40 Max. :34.30 Max. :40.30
Classe
Length:200
Class :character
Mode :character
Code
#if(!require(skimr)) install.packages("skimr")
library(skimr)
skim(tabela)| Name | tabela |
| Number of rows | 200 |
| Number of columns | 6 |
| _______________________ | |
| Column type frequency: | |
| character | 1 |
| numeric | 5 |
| ________________________ | |
| Group variables | None |
Variable type: character
| skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
|---|---|---|---|---|---|---|---|
| Classe | 0 | 1 | 4 | 5 | 0 | 3 | 0 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| pH | 0 | 1 | 6.50 | 0.95 | 3.6 | 5.90 | 6.55 | 7.20 | 8.9 | ▁▃▇▆▂ |
| MO | 0 | 1 | 2.49 | 0.51 | 0.9 | 2.18 | 2.50 | 2.80 | 3.8 | ▁▃▇▆▂ |
| P | 0 | 1 | 24.86 | 3.31 | 14.1 | 22.70 | 25.10 | 26.90 | 33.4 | ▁▃▇▇▂ |
| K | 0 | 1 | 30.28 | 1.84 | 25.3 | 29.25 | 30.40 | 31.60 | 34.3 | ▁▃▇▆▂ |
| Arg | 0 | 1 | 25.28 | 4.83 | 10.3 | 22.00 | 25.10 | 28.33 | 40.3 | ▁▃▇▃▁ |
Code
library(visdat)
vis_dat(tabela)Code
vis_miss(tabela)Classificação de Dados com Machine Learning
Nesta aula veremos alguns algoritmos de classificação, começando com o K-Nearest Neighbors (KNN), que é um algoritmo de aprendizado supervisionado. O KNN é um algoritmo simples que armazena todos os casos disponíveis e classifica novos casos com base em uma medida de similaridade (por exemplo, distância Euclidiana). O KNN tem sido usado em estatística e reconhecimento de padrões por muitos anos.
As técnicas de classificação são utilizadas para a identificação do rótulo de determinadas observações com base em características e informações previamente conhecidas (Lantz 2013). A classificação é uma técnica de aprendizado supervisionado, onde o objetivo é identificar a qual classe pertence uma determinada observação.
A capacidade do modelo de gerar uma predição satisfatória é denominada capacidade de generalização, ou seja, quão bom o modelo é na predição de classe ou valor dos novos registros ainda não rotulados.
Neste tutorial exploramento o algoritmo K vizinhos mais próximos (K Nearest Neighbor - KNN).
Classificação com Algortimo KNN
O algoritmo KNN é um dos algoritmos mais simples de classificação e é baseado na ideia de que os objetos semelhantes tendem a estar próximos uns dos outros. O algoritmo KNN é um tipo de aprendizado supervisionado, onde o resultado é classificado por maioria de votos dos vizinhos mais próximos.
O algoritmo K-Nearest Neighbors (KNN) é um dos métodos clássicos de classificação, conhecido por sua simplicidade e eficiência. Ele é amplamente utilizado para classificar objetos com base em exemplos de treinamento que estão mais próximos no espaço de características, ou seja, ele toma como base a proximidade entre pontos de dados para inferir a classe de novos exemplos.
Para seu funcionamento, o KNN necessita inicialmente de uma métrica de distância que permita calcular o quão próximos dois exemplos estão um do outro. A métrica mais comum utilizada é a distância Euclidiana, mas outras métricas, como a distância de Manhattan ou de Minkowski, também podem ser aplicadas, dependendo da natureza dos dados.
Outro passo importante no uso do KNN é a definição do valor de K, que indica quantos vizinhos mais próximos serão considerados pelo algoritmo na classificação. A escolha de K é crucial, pois um valor muito pequeno pode tornar o modelo sensível a ruídos, enquanto um valor muito grande pode diluir a influência dos vizinhos mais relevantes.
Finalmente, a classificação de um exemplo desconhecido é feita por meio de uma votação majoritária entre os K vizinhos mais próximos. Ou seja, o rótulo de classe que for mais frequente entre os vizinhos próximos será atribuído ao exemplo a ser classificado. Este processo simples e intuitivo faz do KNN uma escolha comum em problemas de classificação, especialmente quando a interpretação da decisão é importante.
A imagem acima ilustra o funcionamento do algoritmo KNN. Neste exemplo, temos um conjunto de dados com duas classes distintas (amarelo e roxo) e um novo exemplo a ser classificado (ponto vermelho). O algoritmo KNN calcula a distância entre o novo exemplo e os exemplos de treinamento, selecionando os K vizinhos mais próximos. Neste caso, K = 3, então os três vizinhos mais próximos são selecionados (dois da classe roxo e um da classe amarelo). Como a maioria dos vizinhos é da classe roxo, o novo exemplo é classificado como roxo. Para o K = 6, o novo exemplo seria classificado como amarelo, pois são quatro amarelos e dois roxos.
Vantagens e Desvantagens do KNN
Vantagens
- Simplicidade: O KNN é um algoritmo simples e fácil de entender, o que o torna uma boa escolha para problemas de classificação.
- Não paramétrico: O KNN não faz suposições sobre a distribuição dos dados, o que o torna útil para dados não lineares e complexos.
- Interpretabilidade: A classificação do KNN é baseada na proximidade dos exemplos, o que torna fácil interpretar as decisões do modelo.
- Robustez: O KNN é robusto a outliers e ruídos nos dados, pois considera vários vizinhos próximos em vez de depender de um único exemplo.
Desvantagens
- Sensibilidade à escala: O KNN é sensível à escala dos dados, o que pode levar a resultados distorcidos se as variáveis tiverem escalas diferentes.
- Custo computacional: O KNN precisa calcular a distância entre o novo exemplo e todos os exemplos de treinamento, o que pode ser computacionalmente caro para grandes conjuntos de dados.
- Escolha de K: A escolha do valor de K é crucial para o desempenho do modelo, e um valor inadequado pode levar a resultados subótimos.
Exemplo de Classificação com Algortimo KNN
Code
dados <- tibble(
a1 = c(0,10,2,6,4,1,8,10,6),
a2 = c(250,150,90,78,20,170,160,180, 200),
a3 = c(36,34,10,8,1,70,41,38,45))
classe <- factor(c(rep(c("A", "B"), 4), NA))
dados <- cbind(dados, classe)Vamos visualizar os dados:
| a1 | a2 | a3 | classe |
|---|---|---|---|
| 0 | 250 | 36 | A |
| 10 | 150 | 34 | B |
| 2 | 90 | 10 | A |
| 6 | 78 | 8 | B |
| 4 | 20 | 1 | A |
| 1 | 170 | 70 | B |
| 8 | 160 | 41 | A |
| 10 | 180 | 38 | B |
| 6 | 200 | 45 | NA |
Padronizar os dados
A padronização dos dados é uma etapa importante para garantir que todas as variáveis tenham a mesma escala e não influenciem indevidamente o algoritmo.
O cálculo do valor padronizado é feito subtraindo a média da variável e dividindo pelo desvio padrão:
\[x^* = \frac{x_i - \bar x}{s} \] Onde o \(x^*\) é o valor padronizado, \(x_i\) é o valor original, \(\bar x\) é a média da variável e \(s\) é o desvio padrão.
Para isso, vamos padronizar os dados utilizando a função scale() do R.
Code
# Padronizando
df <- scale(dados[,1:3],
center = T)Vamos visualizar os dados padronizados:
| a1 | a2 | a3 |
|---|---|---|
| -1.3984751 | 1.50950320 | 0.2099027 |
| 1.2794559 | 0.08245186 | 0.1177503 |
| -0.8628889 | -0.77377895 | -0.9880787 |
| 0.2082835 | -0.94502511 | -1.0802311 |
| -0.3273027 | -1.77271489 | -1.4027646 |
| -1.1306820 | 0.36786212 | 1.7764938 |
| 0.7438697 | 0.22515699 | 0.4402838 |
| 1.2794559 | 0.51056726 | 0.3020551 |
| 0.2082835 | 0.79597753 | 0.6245886 |
Calculando a distância entre dois pontos.
Existem várias formas diferentes de calcular essa distância. A mais simples é a distância euclidiana. É a distância entre pontos, que pode ser provada pela aplicação repetida do teorema de Pitágoras.
A distância euclidiana entre dois pontos, p e q, em um espaço n-dimensional é calculada da seguinte forma:
\[d(p, q) = \sqrt{(p_1 - q_1)^2 + (p_2 - q_2)^2 + \ldots + (p_n - q_n)^2}\]
Onde: - \(p_1, p_2, \ldots, p_n\) são as coordenadas do ponto p; - \(q_1, q_2, \ldots, q_n\) são as coordenadas do ponto q; - \(n\) é o número de dimensões do espaço.
Essencialmente, a distância Euclidiana é a raiz quadrada da soma dos quadrados das diferenças entre as coordenadas correspondentes de 𝑝 e 𝑞.
Vamos calcular a distância entre os pontos:
Code
distancias <- philentropy::distance(x = df,
method = "euclidean") |>
round(digits = 3)Metric: 'euclidean'; comparing: 9 vectors.
Code
distancias v1 v2 v3 v4 v5 v6 v7 v8 v9
v1 0.000 3.036 2.634 3.205 3.811 1.957 2.508 2.860 1.806
v2 3.036 0.000 2.558 1.907 2.887 2.940 0.641 0.466 1.383
v3 2.634 2.558 0.000 1.089 1.207 3.003 2.371 2.811 2.492
v4 3.205 1.907 1.089 0.000 1.037 3.417 1.992 2.275 2.437
v5 3.811 2.887 1.207 1.037 0.000 3.916 2.922 3.271 3.316
v6 1.957 2.940 3.003 3.417 3.916 0.000 2.306 2.829 1.817
v7 2.508 0.641 2.371 1.992 2.922 2.306 0.000 0.622 0.804
v8 2.860 0.466 2.811 2.275 3.271 2.829 0.622 0.000 1.155
v9 1.806 1.383 2.492 2.437 3.316 1.817 0.804 1.155 0.000
Observe que o resultado é uma matriz de distâncias entre os pontos. Cada elemento da matriz representa a distância entre os pontos correspondentes nas linhas e colunas. Para o nosso exemplo em estudo, o elemento na linha 9 é o que desejamos verificar as distâncias.
Desta forma, a distância entre o ponto 9 e os demais pontos é observada na última linha da matriz. Sendo a distância do ponto 9 para o ponto 1 de 1.806, para o ponto 2 de 1.383, para o ponto 3 de 2.492, para o ponto 4 de 2.437, para o ponto 5 de 3.316, para o ponto 6 de 1.817, para o ponto 7 de 0.804 e para o ponto 8 de 1.155.
Outra função que pode ser utilizada para calcular a distância entre os pontos é a função dist() do pacote {stats} R.
Code
dist2 <- dist(df, method = "euclidean") |>
round(digits = 3)
dist2 1 2 3 4 5 6 7 8
2 3.036
3 2.634 2.558
4 3.205 1.907 1.089
5 3.811 2.887 1.207 1.037
6 1.957 2.940 3.003 3.417 3.916
7 2.508 0.641 2.371 1.992 2.922 2.306
8 2.860 0.466 2.811 2.275 3.271 2.829 0.622
9 1.806 1.383 2.492 2.437 3.316 1.817 0.804 1.155
O resultado é a distância entre os pontos, de forma semelhante a função philentropy::distance().
Classificação do ponto 9
Vamos classificar o ponto 9 utilizando a lógica por trás do algoritmo KNN. Para isso, vamos considerar inicialmente os 3 vizinhos mais próximos (k = 3). E por votação majoritária atribui-se à amostra desconhecida a classe mais frequente entre os K vizinhos. Cada vizinho vota com a sua classe, e a classe com mais votos é atribuída ao novo ponto. Por isso a importância de escolher um valor de K ímpar.
Voltando a nossa base de dados originais.
Code
dados a1 a2 a3 classe
1 0 250 36 A
2 10 150 34 B
3 2 90 10 A
4 6 78 8 B
5 4 20 1 A
6 1 170 70 B
7 8 160 41 A
8 10 180 38 B
9 6 200 45 <NA>
Vamos analisar as distâncias do ponto 9 para os demais pontos em ordem crescente.
Code
distancias[9,] |>
sort() v9 v7 v8 v2 v1 v6 v4 v3 v5
0.000 0.804 1.155 1.383 1.806 1.817 2.437 2.492 3.316
Agora vamos considerar os 3 vizinhos mais próximos para o ponto 9. Que são os pontos 7 (distância de 0.804), 8 (distância de 1.155) e 2 (distância de 1.383). Agora obervando a classe desses pontos, temos que a classe do ponto 7 é \(A\), do ponto 8 é \(B\) e do ponto 2 é \(B\), sendo a classe mais frequente a classe B. Portando, o ponto 9 é classificado como \(B\).
Se quisermos classificar o ponto 9 para um valor de \(K = 5\), temos que os pontos mais próximos são os pontos 7, 8, 2, 1 e 6. E a classe mais frequente é a classe \(B\). Portanto, o ponto 9 é classificado como \(B\) também para k = 5.
Para \(k = 7\), temos que os pontos mais próximos são os pontos 7, 8, 2, 1, 6, 4 e 3. E a classe mais frequente é a classe \(B\). Portanto, o ponto 9 é classificado como \(B\) para k = 7.
Implementação do KNN
Para isso, vamos utilizar a função knn() do pacote {class} R.
Como é um exemplo simples, não dividimos em treino e teste, no entanto o equivalente a nossos conjunto de dados teste são os oito primeiros pontos e o ponto 9 é o ponto a ser classificado.
Code
treino <- df[1:8,]
classe <- dados[1:8,4]
prever <- df[9,1:3] # Elemento da linha 9Agora vamos classificar o ponto 9 para \(k = 3\) utilizando a função knn() do pacote {class} R.
Code
if(!require(class)) install.packages("class")Carregando pacotes exigidos: class
Code
library(class)
previsão <- knn(treino,
prever,
cl = classe,
k = 3)
previsão[1] B
Levels: A B
Observamos que a previsão para o ponto 9 é a classe \(B\).
Mudando o valor do hiperparâmetro \(k\) para 5 e 7, temos:
Code
previsão <- knn(treino,
prever,
cl = classe,
k = 5)
previsão[1] B
Levels: A B
A classificação do ponto 9 para \(k = 5\) é a classe \(B\). E para \(k = 7\) temos:
Code
previsão <- knn(treino,
prever,
cl = classe,
k = 7)
previsão[1] B
Levels: A B
Para os três valores de \(k\) testados, a classificação do ponto 9 foi a classe \(B\). Podemos concluir que o ponto 9 foi classificado como \(B\) para os valores de \(k = 3\), \(k = 5\) e \(k = 7\), sendo razoável aceitas esta classificação.
Exemplo 2 de Aplicação do KNN
Vejamos outro exemplo de aplicação do KNN.
O exemplo a seguir é um exemplo de classificação de sementes de soja.
Code
rm(list = ls())Carregando os dados
Code
# Classes de sementes de soja
df <- read_excel("soja.xlsx")
glimpse(df)Rows: 200
Columns: 6
$ pH <dbl> 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.1, 6.1, 6.1, 6.1, 6.1, 6.1, 6.1…
$ MO <dbl> 3.8, 3.6, 3.6, 3.5, 3.5, 3.5, 3.4, 3.4, 3.4, 3.3, 3.3, 3.3, 3.3…
$ P <dbl> 33.4, 32.7, 32.1, 31.8, 31.4, 30.9, 30.5, 30.5, 30.4, 30.2, 30.…
$ K <dbl> 34.3, 34.1, 33.9, 33.8, 33.6, 33.6, 33.4, 33.3, 33.3, 33.2, 33.…
$ Arg <dbl> 40.3, 39.3, 37.3, 36.1, 35.9, 34.8, 34.2, 34.0, 33.7, 33.4, 33.…
$ Classe <chr> "alta", "alta", "alta", "alta", "alta", "alta", "alta", "alta",…
Code
# Transformar a classe em fator na sequêcia: Alta, Média e Baixa.
df$Classe <- factor(df$Classe,
levels = c("alta", "media", "baixa"))
df$Classe [1] alta alta alta alta alta alta alta alta alta alta alta alta
[13] alta alta alta alta alta alta alta alta alta alta alta alta
[25] alta alta alta alta alta alta alta alta alta alta alta alta
[37] alta alta alta alta alta alta alta alta alta alta alta alta
[49] alta alta alta alta alta alta alta alta alta alta alta alta
[61] alta alta alta alta alta alta alta alta alta alta alta alta
[73] alta alta alta alta alta alta alta alta alta media media media
[85] media media media media media media media media media media media media
[97] media media media media media media media media media media media media
[109] media media media media media media media media media media media media
[121] media media media media media media media media media media media media
[133] media media media media media media media media media media baixa baixa
[145] baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa
[157] baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa
[169] baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa
[181] baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa
[193] baixa baixa baixa baixa baixa baixa baixa baixa
Levels: alta media baixa
Vamos visualizar os dados:
Code
plot(df[,1:5])Visualizar os dados usando o pacote {GGally}
Code
#install.packages("GGally")
library(GGally)Registered S3 method overwritten by 'GGally':
method from
+.gg ggplot2
Code
ggpairs(df,
columns = 1:5,
mapping = aes(color = Classe))A função skimr::skim() é uma função que fornece um resumo estatístico dos dados.
Code
# Verificação de escala.
skimr::skim(df)| Name | df |
| Number of rows | 200 |
| Number of columns | 6 |
| _______________________ | |
| Column type frequency: | |
| factor | 1 |
| numeric | 5 |
| ________________________ | |
| Group variables | None |
Variable type: factor
| skim_variable | n_missing | complete_rate | ordered | n_unique | top_counts |
|---|---|---|---|---|---|
| Classe | 0 | 1 | FALSE | 3 | alt: 81, med: 61, bai: 58 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| pH | 0 | 1 | 6.50 | 0.95 | 3.6 | 5.90 | 6.55 | 7.20 | 8.9 | ▁▃▇▆▂ |
| MO | 0 | 1 | 2.49 | 0.51 | 0.9 | 2.18 | 2.50 | 2.80 | 3.8 | ▁▃▇▆▂ |
| P | 0 | 1 | 24.86 | 3.31 | 14.1 | 22.70 | 25.10 | 26.90 | 33.4 | ▁▃▇▇▂ |
| K | 0 | 1 | 30.28 | 1.84 | 25.3 | 29.25 | 30.40 | 31.60 | 34.3 | ▁▃▇▆▂ |
| Arg | 0 | 1 | 25.28 | 4.83 | 10.3 | 22.00 | 25.10 | 28.33 | 40.3 | ▁▃▇▃▁ |
Podendo ser realizada por categoria.
Code
# Resumo por Classe
df |>
group_by(Classe) |>
skimr::skim()| Name | group_by(df, Classe) |
| Number of rows | 200 |
| Number of columns | 6 |
| _______________________ | |
| Column type frequency: | |
| numeric | 5 |
| ________________________ | |
| Group variables | Classe |
Variable type: numeric
| skim_variable | Classe | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|---|
| pH | alta | 0 | 1 | 6.50 | 0.29 | 6.0 | 6.3 | 6.50 | 6.70 | 7.0 | ▆▅▇▅▃ |
| pH | media | 0 | 1 | 6.53 | 0.75 | 5.5 | 5.8 | 7.10 | 7.20 | 7.4 | ▅▂▁▁▇ |
| pH | baixa | 0 | 1 | 6.46 | 1.57 | 3.6 | 5.1 | 6.50 | 7.88 | 8.9 | ▂▇▁▅▆ |
| MO | alta | 0 | 1 | 2.99 | 0.28 | 2.6 | 2.7 | 3.00 | 3.20 | 3.8 | ▇▅▆▂▁ |
| MO | media | 0 | 1 | 2.40 | 0.14 | 2.2 | 2.3 | 2.40 | 2.50 | 2.6 | ▅▇▆▇▅ |
| MO | baixa | 0 | 1 | 1.90 | 0.28 | 0.9 | 1.8 | 2.00 | 2.10 | 2.2 | ▁▁▁▅▇ |
| P | alta | 0 | 1 | 27.90 | 1.76 | 25.7 | 26.6 | 27.50 | 28.70 | 33.4 | ▇▆▂▁▁ |
| P | media | 0 | 1 | 24.59 | 0.79 | 23.2 | 23.9 | 24.60 | 25.40 | 25.7 | ▅▅▅▃▇ |
| P | baixa | 0 | 1 | 20.90 | 2.04 | 14.1 | 20.2 | 21.30 | 22.20 | 23.2 | ▁▁▂▆▇ |
| K | alta | 0 | 1 | 32.01 | 0.91 | 30.7 | 31.4 | 31.80 | 32.60 | 34.3 | ▆▇▃▃▂ |
| K | media | 0 | 1 | 30.08 | 0.41 | 29.4 | 29.7 | 30.10 | 30.50 | 30.7 | ▆▅▃▆▇ |
| K | baixa | 0 | 1 | 28.06 | 1.07 | 25.3 | 27.7 | 28.20 | 28.80 | 29.4 | ▂▁▃▇▇ |
| Arg | alta | 0 | 1 | 29.87 | 2.97 | 26.5 | 27.7 | 29.30 | 31.30 | 40.3 | ▇▆▂▁▁ |
| Arg | media | 0 | 1 | 24.51 | 1.07 | 22.9 | 23.7 | 24.50 | 25.30 | 26.5 | ▇▇▆▆▅ |
| Arg | baixa | 0 | 1 | 19.69 | 2.32 | 10.3 | 18.3 | 19.85 | 21.30 | 22.8 | ▁▁▂▆▇ |
Outra forma de visualizar os dados é através de uma tabela de frequência.
Code
xtabs(~Classe, data = df)Classe
alta media baixa
81 61 58
Vamos padronizar os dados.
Code
# Padronizando
df_p <- data.frame(scale(df[,1:5]),
as.factor(df$Classe)) |>
rename(Classe = as.factor.df.Classe.)
summary(df_p) pH MO P K
Min. :-3.04415 Min. :-3.10779 Min. :-3.24583 Min. :-2.70948
1st Qu.:-0.62982 1st Qu.:-0.62273 1st Qu.:-0.65134 1st Qu.:-0.55867
Median : 0.05249 Median : 0.01072 Median : 0.07271 Median : 0.06752
Mean : 0.00000 Mean : 0.00000 Mean : 0.00000 Mean : 0.00000
3rd Qu.: 0.73479 3rd Qu.: 0.59544 3rd Qu.: 0.61574 3rd Qu.: 0.72093
Max. : 2.51929 Max. : 2.54451 Max. : 2.57669 Max. : 2.19111
Arg Classe
Min. :-3.1023 alta :81
1st Qu.:-0.6799 media:61
Median :-0.0381 baixa:58
Mean : 0.0000
3rd Qu.: 0.6296
Max. : 3.1089
Observe que os valores foram padronizados, para média igual a zero e desvio padrão igual a 1.
A padronização dos dados é uma etapa fundamental em diversos processos de análise estatística, aprendizado de máquina e modelagem preditiva. Ela envolve transformar variáveis para que tenham uma escala comum, o que pode ser feito subtraindo a média e dividindo pelo desvio padrão, ou reescalando os valores para uma faixa específica, como [0, 1]. A padronização é particularmente importante em algoritmos que dependem de medições de distância ou variáveis com diferentes escalas.
Algoritmos que dependem de medições de distância, como KNN (K-vizinhos mais próximos) e clustering (K-means, por exemplo), podem ser severamente influenciados por variáveis com escalas maiores. Nesse caso, variáveis com valores numéricos maiores podem influenciar fortemente as distâncias calculadas, distorcendo os resultados. Padronizar os dados assegura que todas as variáveis tenham um impacto comparável nas medidas de distância.
Agora vamos dividir os dados em treino e teste.
Code
# Separando em treino e teste.
n <- nrow(df_p)
set.seed(134) # Para reprodutibilidade dos resultados.
i <- sample(x = c(TRUE, FALSE),
size = n,
replace = TRUE,
prob = c(0.70, 1 - 0.70))
df_train <- df_p[i, ]
nrow(df_train)[1] 139
Code
df_test <- df_p[!i, ]
nrow(df_test)[1] 61
Vamos verificar a proporção de treino e teste.
Code
# Proporção treino e teste
round(c(nrow(df_train),
nrow(df_test))/n,
digits = 3)*100[1] 69.5 30.5
Muito próximo de 70% para treino e 30% para teste.
Obtendo as predições para o conjunto de teste via conjunto de treino.
- K = 3
Code
library(class)
m0 <- knn(train = df_train[, -6],
test = df_test[, -6],
cl = df_train[, 6],
k = 3)Avaliação do modelo
Code
# Tabela de confusão.
ct <- table(df_test[, 6], m0)
# Renomeando as linhas e colunas
rownames(ct) <- c("Alta_V", "Média_V", "Baixa_V")
colnames(ct) <- c("Alta_Prev", "Média_Prev", "Baixa_Prev")
ct m0
Alta_Prev Média_Prev Baixa_Prev
Alta_V 21 3 0
Média_V 0 18 1
Baixa_V 0 0 18
Code
# Conferindo a Tabela do teste
xtabs(~Classe, data = df_test)Classe
alta media baixa
24 19 18
Visualizando os erros e acertos.
Code
library(gt)
# Juntando a base de teste com a previsão
df_comp <- cbind(df_test, Previssao = m0)
df_comp |>
gt()| pH | MO | P | K | Arg | Classe | Previssao |
|---|---|---|---|---|---|---|
| -0.5248530 | 2.15469678 | 2.18449952 | 1.97330252 | 2.48780137 | alta | alta |
| -0.4198824 | 1.76488280 | 1.70180387 | 1.64659681 | 1.80456697 | alta | alta |
| -0.4198824 | 1.56997581 | 1.61129843 | 1.59214585 | 1.68034254 | alta | alta |
| -0.4198824 | 1.56997581 | 1.58112995 | 1.59214585 | 1.68034254 | alta | alta |
| -0.4198824 | 1.56997581 | 1.46045604 | 1.59214585 | 1.59752625 | alta | alta |
| -0.4198824 | 1.56997581 | 1.46045604 | 1.59214585 | 1.59752625 | alta | alta |
| -0.2099412 | 1.37506882 | 1.33978213 | 1.37434204 | 1.28696516 | alta | alta |
| -0.2099412 | 1.37506882 | 1.30961365 | 1.31989109 | 1.26626108 | alta | alta |
| -0.2099412 | 1.37506882 | 1.15877126 | 1.26544014 | 1.24555701 | alta | alta |
| -0.1049706 | 1.18016183 | 1.06826582 | 1.15653823 | 1.07992443 | alta | alta |
| -0.1049706 | 1.18016183 | 1.00792886 | 0.99318538 | 0.99710814 | alta | alta |
| -0.1049706 | 1.18016183 | 0.97776038 | 0.93873442 | 0.99710814 | alta | alta |
| 0.0000000 | 0.98525484 | 0.79674951 | 0.82983252 | 0.83147556 | alta | alta |
| 0.1049706 | 0.79034785 | 0.70624408 | 0.82983252 | 0.72795520 | alta | alta |
| 0.1049706 | 0.59544086 | 0.61573864 | 0.72093061 | 0.58302669 | alta | alta |
| 0.1049706 | 0.59544086 | 0.58557017 | 0.66647966 | 0.56232261 | alta | alta |
| 0.2099412 | 0.40053387 | 0.52523321 | 0.61202871 | 0.52091447 | alta | alta |
| 0.2099412 | 0.40053387 | 0.49506473 | 0.55757775 | 0.47950632 | alta | alta |
| 0.2099412 | 0.40053387 | 0.49506473 | 0.50312680 | 0.45880225 | alta | alta |
| 0.3149118 | 0.40053387 | 0.43472777 | 0.39422490 | 0.43809818 | alta | alta |
| 0.3149118 | 0.40053387 | 0.37439082 | 0.39422490 | 0.33457781 | alta | alta |
| 0.5248530 | 0.20562688 | 0.28388538 | 0.28532299 | 0.27246560 | alta | media |
| 0.5248530 | 0.20562688 | 0.28388538 | 0.28532299 | 0.27246560 | alta | media |
| 0.5248530 | 0.20562688 | 0.25371690 | 0.23087204 | 0.25176152 | alta | media |
| 0.5248530 | 0.20562688 | 0.25371690 | 0.23087204 | 0.25176152 | media | media |
| 0.6298236 | 0.20562688 | 0.25371690 | 0.23087204 | 0.23105745 | media | media |
| 0.7347942 | 0.01071988 | 0.19337995 | 0.12197013 | 0.08612894 | media | media |
| 0.7347942 | 0.01071988 | 0.19337995 | 0.12197013 | 0.08612894 | media | media |
| 0.7347942 | 0.01071988 | 0.16321147 | 0.12197013 | 0.04472080 | media | media |
| 0.8397648 | 0.01071988 | 0.01236908 | 0.01306823 | -0.07950364 | media | media |
| 0.8397648 | 0.01071988 | 0.01236908 | 0.01306823 | -0.12091178 | media | media |
| 0.8397648 | -0.18418711 | -0.01779940 | -0.04138272 | -0.12091178 | media | media |
| -0.6298236 | -0.18418711 | -0.10830484 | -0.15028463 | -0.20372808 | media | media |
| -0.6298236 | -0.18418711 | -0.13847332 | -0.15028463 | -0.22443215 | media | media |
| -0.6298236 | -0.37909410 | -0.16864179 | -0.15028463 | -0.24513622 | media | media |
| -0.6298236 | -0.37909410 | -0.25914723 | -0.20473558 | -0.30724844 | media | media |
| -0.7347942 | -0.37909410 | -0.28931571 | -0.25918653 | -0.32795251 | media | media |
| -0.7347942 | -0.37909410 | -0.28931571 | -0.25918653 | -0.32795251 | media | media |
| -0.7347942 | -0.37909410 | -0.28931571 | -0.31363749 | -0.32795251 | media | media |
| -0.7347942 | -0.37909410 | -0.31948419 | -0.31363749 | -0.34865658 | media | media |
| -0.9447354 | -0.57400109 | -0.40998962 | -0.42253939 | -0.49358509 | media | media |
| -0.9447354 | -0.57400109 | -0.44015810 | -0.47699034 | -0.49358509 | media | media |
| -1.0497060 | -0.57400109 | -0.50049506 | -0.47699034 | -0.49358509 | media | baixa |
| -1.1546766 | -0.57400109 | -0.50049506 | -0.47699034 | -0.55569731 | baixa | baixa |
| -1.1546766 | -0.57400109 | -0.50049506 | -0.53144130 | -0.55569731 | baixa | baixa |
| -1.3646178 | -0.76890808 | -0.65133745 | -0.64034320 | -0.74203396 | baixa | baixa |
| -1.3646178 | -0.76890808 | -0.65133745 | -0.64034320 | -0.74203396 | baixa | baixa |
| -1.4695884 | -0.76890808 | -0.77201136 | -0.69479415 | -0.80414618 | baixa | baixa |
| -1.6795295 | -0.76890808 | -0.86251680 | -0.85814701 | -0.84555433 | baixa | baixa |
| -1.6795295 | -0.76890808 | -0.89268528 | -0.91259797 | -0.86625840 | baixa | baixa |
| -1.8894707 | -0.76890808 | -0.92285375 | -0.91259797 | -0.90766655 | baixa | baixa |
| -2.2043825 | -0.96381507 | -0.98319071 | -1.02149987 | -1.01118691 | baixa | baixa |
| 1.0497060 | -0.96381507 | -1.07369615 | -1.13040178 | -1.13541135 | baixa | baixa |
| 1.0497060 | -1.15872206 | -1.16420158 | -1.23930368 | -1.19752356 | baixa | baixa |
| 1.2596472 | -1.15872206 | -1.16420158 | -1.23930368 | -1.25963578 | baixa | baixa |
| 1.4695884 | -1.35362905 | -1.40554941 | -1.40265654 | -1.44597244 | baixa | baixa |
| 1.4695884 | -1.54853604 | -1.52622332 | -1.56600940 | -1.44597244 | baixa | baixa |
| 1.8894707 | -1.74344303 | -1.88824506 | -1.94716606 | -1.71512538 | baixa | baixa |
| 2.0994119 | -2.13325702 | -1.88824506 | -2.00161702 | -1.77723760 | baixa | baixa |
| 2.2043825 | -2.32816401 | -2.70279398 | -2.60057750 | -2.00498240 | baixa | baixa |
| 2.5192943 | -2.52307100 | -2.79329941 | -2.70947940 | -2.08779869 | baixa | baixa |
Acurácia do modelo.
Code
# Acurária = Fração de acertos totais.
A <- sum(diag(ct))/sum(ct)*100
paste0(round(A, 1), "%")[1] "93.4%"
O modelo obteve uma acurácia de 93.4%, ou seja, acertou 57 de 61 observações da base de teste.
Comparar diferentes k’s
Vamos considerar o critério da Acuária para escolher o melhor valor de \(k\), fazendo o ajuste do modelo para diferentes valores de \(k\) e comparando a acurácia obtida.
Code
# cria uma lista para receber as predicoes
Knn_Testes = list()
# cria variavel para receber acuracia
acuracia = numeric()
# cria loop para testar de k=1 ate k=20
for(k in 1:20){
Knn_Testes[[k]] = knn(train = df_train[,-6],
df_test[,-6],
cl = df_train[,6],
k)
acuracia[k] = sum(Knn_Testes[[k]]==df_test[,6])/length(df_test[,6])*100
}
acuracia [1] 96.72131 95.08197 93.44262 93.44262 93.44262 93.44262 93.44262 93.44262
[9] 93.44262 95.08197 98.36066 96.72131 95.08197 95.08197 91.80328 96.72131
[17] 95.08197 95.08197 95.08197 95.08197
Verificando graficmente
Code
# Comparacao grafica das acuracias
# Criando um data frame com os dados de k e acuracia
dt <- data.frame(k = 1:k, acuracia = acuracia)
# Gerando o gráfico
dt |>
ggplot(aes(x = k,
y = acuracia)) +
geom_line(color = "blue") + # Linha azul
geom_point(color = "blue", size = 3) + # Pontos azuis
labs(title = "Acurácia para cada k",
x = "k",
y = "Acurácia") +
theme_minimal()Observemos que a acurácia é maior para o valor de \(k = 11\). Portanto, o melhor valor de \(k\) para este problema é 11. Podemos então colocar o modelo definitivo com \(k = 11\) em produção.
Previsão da classe para novos valores
Vamos considerar um novo conjunto com cinco novas amostras de solo para prever a classe de qualidade de sementes esperada.
Code
# Novos valores
novos_valores <- data.frame(pH = c(4.2, 7.4, 5.7, 5.8, 6.9),
MO = c(1.5, 3.6, 2.7, 4.8, 3.9),
P = c(18.5, 13.6, 22.7, 18.8, 35.9),
K = c(26.5, 28.6, 26.7, 30.8, 37.9),
Arg = c(17.5, 20.6, 30.7, 40.8, 41.9))
novos_valores pH MO P K Arg
1 4.2 1.5 18.5 26.5 17.5
2 7.4 3.6 13.6 28.6 20.6
3 5.7 2.7 22.7 26.7 30.7
4 5.8 4.8 18.8 30.8 40.8
5 6.9 3.9 35.9 37.9 41.9
Com os valores estão em escalas diferentes é necessário a transformação de escala para uma padronizada. Vamos então escalonar os novos valores, para média igual a zero e desvio padrão igual a 1 em relação aos dados da base original.
Code
# calculando as médias e desvios padrão de cada variável
medias <- colMeans(df[,1:5])
desv_pad <- apply(df[,1:5], 2, sd)
# padronizando os novos valores em relação aos dados da base original
novos_valores_p <- scale(novos_valores,
center = medias,
scale = desv_pad)
novos_valores_p pH MO P K Arg
[1,] -2.4143237 -1.9383500 -1.9184135 -2.056068 -1.6116050
[2,] 0.9447354 2.1546968 -3.3966690 -0.912598 -0.9697788
[3,] -0.8397648 0.4005339 -0.6513374 -1.947166 1.1213326
[4,] -0.7347942 4.4935807 -1.8279081 0.285323 3.2124439
[5,] 0.4198824 2.7394178 3.3309017 4.151341 3.4401887
attr(,"scaled:center")
pH MO P K Arg
6.5000 2.4945 24.8590 30.2760 25.2840
attr(,"scaled:scale")
pH MO P K Arg
0.9526477 0.5130652 3.3147181 1.8365152 4.8299676
Como valor do \(K\) já foi otimizado, vamos considerar o valor de \(k = 11\) para prever a classe dos novos valores. No entanto, podemos utilizar todos os dados disponíveis para treinar o modelo, já que o modelos já foi validado e o parâmetro \(K\) otimizado.
Utilizando o valor de \(k = 11\) para prever a classe dos novos valores.
Code
Knn_K11_Predicao = knn(train = df_p[,-6],
test = novos_valores_p,
cl = df_p[, 6],
k = 11)
Knn_K11_Predicao[1] baixa baixa media alta alta
Levels: alta media baixa
Agora temos a previsão da classe para os novos valores.
Algoritmo de Classificação - KNN
Neste tutorial vamos dar ênfase no processo de valiação cruzada, utilizando as funções do pacote caret KUHN, M. The caret Package. 2019 para treinar modelos de classificação usando o algoritmo K-Nearest Neighbors (KNN).
O pacote contém ferramentas para:
* divisão de dados
* pré-processamento
* seleção de recursos
* ajuste de modelo usando reamostragem
* estimativa de importância variável
bem como outras funcionalidades.
Carregando pacotes necessários para a execução do código.
Code
library(tidyverse)
library(caret)Carregando pacotes exigidos: lattice
Anexando pacote: 'caret'
O seguinte objeto é mascarado por 'package:purrr':
lift
Code
library(readxl)Limpando a memória do R
Code
rm(list = ls())Carregando os dados
Code
# Classes de sementes de soja
df <- read_excel("soja.xlsx")
glimpse(df)Rows: 200
Columns: 6
$ pH <dbl> 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.1, 6.1, 6.1, 6.1, 6.1, 6.1, 6.1…
$ MO <dbl> 3.8, 3.6, 3.6, 3.5, 3.5, 3.5, 3.4, 3.4, 3.4, 3.3, 3.3, 3.3, 3.3…
$ P <dbl> 33.4, 32.7, 32.1, 31.8, 31.4, 30.9, 30.5, 30.5, 30.4, 30.2, 30.…
$ K <dbl> 34.3, 34.1, 33.9, 33.8, 33.6, 33.6, 33.4, 33.3, 33.3, 33.2, 33.…
$ Arg <dbl> 40.3, 39.3, 37.3, 36.1, 35.9, 34.8, 34.2, 34.0, 33.7, 33.4, 33.…
$ Classe <chr> "alta", "alta", "alta", "alta", "alta", "alta", "alta", "alta",…
Code
# Transformar a classe em fator na sequêcia: Alta, Média e Baixa.
df$Classe <- factor(df$Classe,
levels = c("alta", "media", "baixa"))
df$Classe [1] alta alta alta alta alta alta alta alta alta alta alta alta
[13] alta alta alta alta alta alta alta alta alta alta alta alta
[25] alta alta alta alta alta alta alta alta alta alta alta alta
[37] alta alta alta alta alta alta alta alta alta alta alta alta
[49] alta alta alta alta alta alta alta alta alta alta alta alta
[61] alta alta alta alta alta alta alta alta alta alta alta alta
[73] alta alta alta alta alta alta alta alta alta media media media
[85] media media media media media media media media media media media media
[97] media media media media media media media media media media media media
[109] media media media media media media media media media media media media
[121] media media media media media media media media media media media media
[133] media media media media media media media media media media baixa baixa
[145] baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa
[157] baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa
[169] baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa
[181] baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa baixa
[193] baixa baixa baixa baixa baixa baixa baixa baixa
Levels: alta media baixa
Exemplo 2: KNN com pacote {caret}
Vejamos outro exemplo de aplicação do KNN.
Vamos dividir os dados em treino e teste, para isso vamos utilizar o pacote {caret}, usando a função createDataPartition.
Code
set.seed(678)
treino <- createDataPartition(y = df$Classe,
p = 0.7,
list = FALSE)
df_treino <- df[treino, ]
df_teste <- df[-treino, ]Vamos verificar a proporção de treino e teste em cada classe.
Code
# Proporções das classes no treino e teste.
rbind(treino = prop.table(xtabs(~Classe, df_treino))*100,
teste = prop.table(xtabs(~Classe, df_teste))*100,
base = prop.table(xtabs(~Classe, df))*100) |>
round(1) |>
print() alta media baixa
treino 40.4 30.5 29.1
teste 40.7 30.5 28.8
base 40.5 30.5 29.0
Validação Cruzada
Code
# Parametriza a validação cruzada com 10 folds
trctrl <- trainControl(method = "cv",
number = 10)Define-se os parâmetros para a validação cruzada, onde:
* method = "cv" indica o tipo de validação cruzada que será realizada.
* number = 10 indica que a validação cruzada será realizada com 10 folds. Isso significa que o conjunto de dados será dividido em 10 partes (ou folds) e o modelo será treinado 10 vezes, em cada uma delas usando 9 folds para treinamento e 1 fold para teste. No final, as métricas de desempenho do modelo são calculadas como a média dos resultados das 10 iterações.
Treinando o modelo
Code
set.seed(678)
knn_fit <- train(Classe ~ .,
data = df_treino,
method = "knn",
preProcess = c("center", "scale"), # normalização dos dados
trControl = trctrl,
metric = "Accuracy",
tuneLength = 15)Usando a função train do pacote {caret} para treinar o modelo, onde:
* Classe ~ . indica que a variável resposta é Classe e as variáveis preditoras são todas as outras variáveis do conjunto de dados, (\(.\)) indica todas as demais.
* data = df_treino indica o conjunto de dados de treino.
* method = "knn" indica que o algoritmo a ser utilizado é o KNN. Valores possíveis são encontrados usando names(getModelInfo()).
* preProcess = c("center", "scale") indica que os dados serão normalizados.
* trControl = trctrl indica que a validação cruzada será realizada com 10 folds.
* metric = "Accuracy" indica que a métrica de avaliação será a acurácia.
* tuneLength = 15 indica que o modelo será treinado com 15 valores de k diferentes. O melhor valor de k será escolhido com base no desempenho. O pacote caret tentará automaticamente diferentes valores de k (no caso do K-NN) e selecionará o que resulta na melhor métrica de desempenho.
Avaliando o modelo
Code
# Gráfico do desempenho dos modelos testados
plot(knn_fit) |> print()Nota-se que o modelo com k = 9 obteve o melhor desempenho.
Uma variação do metodo “cv” é o method = "repeatedcv", que permite realizar validação cruzada repetida, o que pode aumentar a robustez do modelo, repetindo a validação cruzada várias vezes.
Code
# Parametriza a validação cruzada com 10 folds e 5 repetições
trctrl <- trainControl(method = "repeatedcv",
number = 10,
repeats = 5)Treinando o modelo com repetição da validação cruzada
Code
set.seed(678)
knn_fit <- train(Classe ~ .,
data = df_treino,
method = "knn",
preProcess = c("center", "scale"), # normalização dos dados
trControl = trctrl,
metric = "Accuracy",
tuneLength = 15)Isso realiza 10 folds de validação cruzada repetidos 5 vezes.
Avaliando o resultado do modelo
Code
print(knn_fit)k-Nearest Neighbors
141 samples
5 predictor
3 classes: 'alta', 'media', 'baixa'
Pre-processing: centered (5), scaled (5)
Resampling: Cross-Validated (10 fold, repeated 5 times)
Summary of sample sizes: 126, 128, 127, 127, 126, 127, ...
Resampling results across tuning parameters:
k Accuracy Kappa
5 0.9826081 0.9736544
7 0.9852601 0.9777430
9 0.9797363 0.9693889
11 0.9812601 0.9717025
13 0.9725788 0.9582380
15 0.9713407 0.9563901
17 0.9660220 0.9477806
19 0.9544542 0.9301976
21 0.9587546 0.9370853
23 0.9469817 0.9191544
25 0.9468718 0.9192541
27 0.9339853 0.8998631
29 0.9184469 0.8761842
31 0.8818462 0.8202297
33 0.8370403 0.7501879
Accuracy was used to select the optimal model using the largest value.
The final value used for the model was k = 7.
Mostra o desempenho do modelo (Acurácia e Kappa) para diferentes valores de k. No exemplo, foram testados k = 5 até k = 33 variando de 2 em 2, onde o k = 7 obtendo a maior acurácia.
O modelo com k = 7 teve a maior Acurácia (98%), a Acurácia (Accuracy): Mede a porcentagem de previsões corretas (verdadeiras positivas + verdadeiras negativas) em relação ao total de previsões. Uma acurácia de 0.98 significa que o modelo classificou corretamente 98% dos exemplos. O k = 7 também apresentou o maior valor de Kappa (97%), o Kappa é uma métrica que corrige a acurácia levando em consideração as previsões corretas feitas ao acaso. Um valor de Kappa próximo de 1 indica que o modelo está muito acima do acaso, enquanto valores próximos de 0 indicam que ele não é muito melhor que uma classificação aleatória.
Avaliando o modelo
Code
# Gráfico do desempenho dos modelos testados
plot(knn_fit) |> print()Nota-se que o modelo com k = 7 obteve o melhor desempenho.
A interpretação dos resultados de um modelo K-NN treinado com validação cruzada usando o pacote caret envolve analisar as métricas de desempenho que são fornecidas ao final do treinamento.
Dado que já treinamos e validamos o modelo, podemos agora fazer previsões nos dados de teste.
Predição nos dados deixados de fora (dados de Teste).
Code
m0 <- predict(knn_fit,
newdata = df_teste)Matriz de confusão.
Code
confusionMatrix(m0, factor(df_teste$Classe)) |> print()Confusion Matrix and Statistics
Reference
Prediction alta media baixa
alta 23 0 0
media 1 17 0
baixa 0 1 17
Overall Statistics
Accuracy : 0.9661
95% CI : (0.8829, 0.9959)
No Information Rate : 0.4068
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.9487
Mcnemar's Test P-Value : NA
Statistics by Class:
Class: alta Class: media Class: baixa
Sensitivity 0.9583 0.9444 1.0000
Specificity 1.0000 0.9756 0.9762
Pos Pred Value 1.0000 0.9444 0.9444
Neg Pred Value 0.9722 0.9756 1.0000
Prevalence 0.4068 0.3051 0.2881
Detection Rate 0.3898 0.2881 0.2881
Detection Prevalence 0.3898 0.3051 0.3051
Balanced Accuracy 0.9792 0.9600 0.9881
Ao utilizar a função confusionMatrix() do pacote {caret} para avaliar um modelo com a base de teste, você obterá várias métricas que são essenciais para entender o desempenho do seu classificador. A matriz de confusão é uma tabela que mostra o desempenho do modelo em termos de previsões corretas e incorretas.
Como interpretar as métricas:
Acurácia: 0,9661 de acurácia significa que o modelo classificou corretamente 96,6% dos exemplos no conjunto de teste.
Kappa: Um valor de 0.9487 para o Kappa indica que o modelo está 94,87% melhor do que uma classificação feita ao acaso.
Sensibilidade ou Recall: 0.9583 significa que 95,83% dos exemplos positivos foram corretamente identificados para a classe alta.
Especificidade: 1.0000 significa que 100% dos exemplos negativos foram corretamente identificados.
Essas métricas são úteis para avaliar o desempenho do modelo e identificar áreas onde o modelo pode estar falhando, como baixa sensibilidade ou alta taxa de falsos positivos. Isso ajuda a ajustar o modelo e melhorar seu desempenho geral.
Conclusão
Neste tutorial, aprendemos a usar a classificação K-Nearest Neighbors (KNN) com o R. Abordamos o conceito básico de KNN e como ele funciona. Em seguida, aplicamos o algoritmo KNN a um conjunto de dados de exemplo para classificar sementes de soja com base em suas características de solo.
Você precisa entender que o algoritmo KNN não é perfeito, ele também tem algumas desvantagens, e é preciso levar em conta vários aspectos antes de selecioná-lo como modelo principal.
Neste tutorial, aprendemos a usar o pacote {caret} para treinar modelos de classificação usando o algoritmo K-Nearest Neighbors (KNN). Destacamos também, a importância da validação cruzada para avaliar a capacidade de generalização do modelo.
Atividade de fixação
Atividade: Classificação de Espécies de Flores com K-Nearest Neighbors (KNN)
Objetivo: Utilizar a base de dados iris para construir um modelo de classificação que seja capaz de prever a espécie de uma flor com base em suas características morfológicas. O algoritmo KNN será utilizado como modelo preditivo.
Descrição da Atividade:
Exploração da Base de Dados:
* Carregar e inspecionar a base de dados iris para entender suas variáveis.
* Verificar a distribuição das variáveis numéricas (Sepal.Length, Sepal.Width, Petal.Length, Petal.Width) e analisar a variável alvo Species.
* Realizar visualizações, como gráficos de dispersão, para investigar a separabilidade das espécies com base nas características.
Divisão dos Dados em Conjunto de Treinamento e Teste
- Dividir a base de dados em conjunto de treinamento (70%) e conjunto de teste (30%) de forma aleatória.
- Verificar se as proporções das espécies são semelhantes nos conjuntos de treinamento e teste.
Normalização das Variáveis
- Aplicar normalização (ou padronização) nas variáveis numéricas, pois o KNN é sensível a escalas diferentes.
- Explicar por que essa etapa é importante no contexto do KNN.
Ajuste do Modelo KNN
- Treinar o modelo KNN utilizando o conjunto de treinamento, ajustando o valor de k (número de vizinhos).
- Testar diferentes valores de k e observar o impacto na acurácia do modelo. (Sugere-se testar ao menos os valores k = 3, k = 5, e k = 7).
Avaliação do Modelo
- Avaliar o modelo com o conjunto de teste, utilizando métricas como acurácia, matriz de confusão e f1-score.
- Discutir possíveis erros de classificação e como o valor de k afeta o desempenho do modelo.
Interpretação dos Resultados
Elabore um breve relatório em RMarkdown, incluindo as seguintes seções:
* Introdução: Contextualização do problema de classificação.
* Metodologia: Explicação sobre o algoritmo KNN e a importância da normalização.
* Resultados: Apresentação das métricas de desempenho, gráficos e tabelas relevantes.
* Discussão e Conclusão: Interpretação dos resultados e possíveis melhorias para o modelo.