O objetivo aqui é mostrar noções básicas de machine learning, mais especificamente mostrar como utilizar o algoritmo KNN no software R. Não será abordado aqui os detalhes matemáticos, mas se quiser saber mais sobre isso será muito legal trocar ideias sobreo o assunto. Se quiser mais detalhes sobre machine learning há o excelente livro “Statistical Learning” do Trevor Hastie.
O algoritmo KNN ou k-vizinhos mais próximos é um algoritmo bem simples de machine learning. Ele usa algum tipo de medida de similaridade para dizer em qual classe o novo dado se classifica. Essa medida de similaridade é normalmente uma medida de distância como a distância Euclidiana (obrigado Pitágoras!) ou a distância de Manhattan(métrica do táxi) por exemplo. Existe todo uma área da matemática que estuda o conceito de distância de forma mais geral, caso tenha interesse e realmente gostar de matemática pode consultar/estudar o excelente livro do saudoso prof. Elon Lages Lima chamado “Espaços Métricos” da editora IMPA. Agora se tiver interessado na matemática e nos algoritmos de machine learning e além disso as aplicações no R você poderá consultar, como eu disse antes , o também excelente livro “Statistical Learning” do Trevor Hastie.
Livro:
Para ver como o KNN funciona e como usar no R será utilizada a seguinte sequência:
Problemas que pode-se usar o KNN
Quando usar o algoritmo KNN?
Como funciona(intuição) o algoritmo KNN?
Adquirindo dados
Limpeza de dados
Preparação dos dados
Usando KNN
Escolhendo valor de k
O KNN é usado numa extensa variedade de aplicações como finanças, saúde, ciência política, detecção de manuscritos, reconhecimento de imagem e reconhecimento de vídeo.
Por exemplo,no desembolso de empréstimos, os institutos bancários irão prever se o empréstimo é seguro ou arriscado. Na ciência política, classificar eleitores em potencial em duas classes votará ou não votará. Na saúde dado os sintomas do paciente na base de dados o algoritmo pode classificá-lo em doente ou não doente.
O KNN pode ser usado para problemas preditivos de classificação(saída discreta) e regressão(saída contínua). No entanto, parece ser mais utilizado em problemas de classificação.
No KNN, K é o número de vizinhos mais próximos. O número de vizinhos é o principal fator decisivo. Quando K = 1, o algoritmo é conhecido como o algoritmo do vizinho mais próximo. Este é o caso mais simples. Suponha a situação abaixo e P( que aparece como interrogação), seja a classe(ou rótulo) que se quer prever. Primeiro, você encontra o ponto mais próximo de P e depois o rótulo desse ponto mais próximo. P terá a mesma classificação do seuvizinho mais próximo. No exemplo abaixo será classificado como estrela vermelha.
Se k > 1 a classe a ser escolhida é pela maioria dos vizinhos mais próximos. Se caso houver impate pode ser feito um sorteio aleatório para escolher qual classe o novo rótulo será classificado.
Vamos aplicar a abordagem KNN ao conjunto de dados Caravan, que faz parte da biblioteca ISLR. Esse conjunto de dados inclui 86 variáveis onde há dados sociodemográficos (variáveis nas colunas de 1-43) e propriedade do produto (variáveis nas colunas de 44-86)
A variável de resposta é Compra( variável 86), que indica se um determinado indivíduo compra ou não uma apólice de seguro de Caravan. Neste conjunto de dados, apenas 6% das pessoas adquiriram seguro da caravana.
Vamos verificar a estrutura desses dados
library(ISLR)
library(dplyr)
glimpse(Caravan)
## Observations: 5,822
## Variables: 86
## $ MOSTYPE <dbl> 33, 37, 37, 9, 40, 23, 39, 33, 33, 11, 10, 9, 33, 41,...
## $ MAANTHUI <dbl> 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2,...
## $ MGEMOMV <dbl> 3, 2, 2, 3, 4, 2, 3, 2, 2, 3, 4, 3, 2, 3, 1, 2, 2, 3,...
## $ MGEMLEEF <dbl> 2, 2, 2, 3, 2, 1, 2, 3, 4, 3, 3, 3, 3, 3, 2, 3, 3, 3,...
## $ MOSHOOFD <dbl> 8, 8, 8, 3, 10, 5, 9, 8, 8, 3, 3, 3, 8, 10, 5, 8, 9, ...
## $ MGODRK <dbl> 0, 1, 0, 2, 1, 0, 2, 0, 0, 3, 1, 1, 1, 0, 0, 0, 0, 0,...
## $ MGODPR <dbl> 5, 4, 4, 3, 4, 5, 2, 7, 1, 5, 4, 3, 4, 5, 6, 7, 6, 5,...
## $ MGODOV <dbl> 1, 1, 2, 2, 1, 0, 0, 0, 3, 0, 1, 2, 1, 0, 1, 0, 0, 0,...
## $ MGODGE <dbl> 3, 4, 4, 4, 4, 5, 5, 2, 6, 2, 4, 4, 4, 4, 2, 2, 3, 4,...
## $ MRELGE <dbl> 7, 6, 3, 5, 7, 0, 7, 7, 6, 7, 7, 7, 6, 7, 1, 7, 7, 7,...
## $ MRELSA <dbl> 0, 2, 2, 2, 1, 6, 2, 2, 0, 0, 1, 1, 2, 1, 2, 2, 0, 0,...
## $ MRELOV <dbl> 2, 2, 4, 2, 2, 3, 0, 0, 3, 2, 2, 2, 3, 1, 6, 0, 2, 2,...
## $ MFALLEEN <dbl> 1, 0, 4, 2, 2, 3, 0, 0, 3, 2, 0, 2, 3, 1, 5, 0, 0, 0,...
## $ MFGEKIND <dbl> 2, 4, 4, 3, 4, 5, 3, 5, 3, 2, 3, 3, 4, 4, 3, 5, 6, 2,...
## $ MFWEKIND <dbl> 6, 5, 2, 4, 4, 2, 6, 4, 3, 6, 6, 5, 3, 5, 1, 4, 3, 7,...
## $ MOPLHOOG <dbl> 1, 0, 0, 3, 5, 0, 0, 0, 0, 0, 4, 1, 1, 2, 2, 0, 2, 2,...
## $ MOPLMIDD <dbl> 2, 5, 5, 4, 4, 5, 4, 3, 1, 4, 3, 7, 4, 4, 6, 3, 6, 1,...
## $ MOPLLAAG <dbl> 7, 4, 4, 2, 0, 4, 5, 6, 8, 5, 3, 1, 5, 4, 2, 6, 2, 7,...
## $ MBERHOOG <dbl> 1, 0, 0, 4, 0, 2, 0, 2, 1, 2, 0, 4, 1, 3, 1, 2, 2, 0,...
## $ MBERZELF <dbl> 0, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 2,...
## $ MBERBOER <dbl> 1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,...
## $ MBERMIDD <dbl> 2, 5, 7, 3, 0, 4, 4, 2, 1, 3, 9, 5, 3, 2, 4, 2, 4, 1,...
## $ MBERARBG <dbl> 5, 0, 0, 1, 0, 2, 1, 5, 8, 3, 0, 1, 2, 2, 3, 5, 0, 1,...
## $ MBERARBO <dbl> 2, 4, 2, 2, 0, 2, 5, 2, 1, 3, 0, 1, 4, 2, 2, 2, 4, 5,...
## $ MSKA <dbl> 1, 0, 0, 3, 9, 2, 0, 2, 1, 1, 3, 2, 1, 4, 1, 2, 2, 2,...
## $ MSKB1 <dbl> 1, 2, 5, 2, 0, 2, 1, 1, 1, 2, 0, 3, 2, 2, 3, 1, 2, 0,...
## $ MSKB2 <dbl> 2, 3, 0, 1, 0, 2, 4, 2, 0, 1, 6, 4, 2, 1, 2, 2, 4, 0,...
## $ MSKC <dbl> 6, 5, 4, 4, 0, 4, 5, 5, 8, 4, 0, 1, 5, 4, 4, 5, 2, 7,...
## $ MSKD <dbl> 1, 0, 0, 0, 0, 2, 0, 2, 1, 2, 0, 0, 1, 0, 0, 2, 0, 0,...
## $ MHHUUR <dbl> 1, 2, 7, 5, 4, 9, 6, 0, 9, 0, 0, 6, 5, 5, 9, 0, 6, 4,...
## $ MHKOOP <dbl> 8, 7, 2, 4, 5, 0, 3, 9, 0, 9, 9, 3, 4, 4, 0, 9, 3, 5,...
## $ MAUT1 <dbl> 8, 7, 7, 9, 6, 5, 8, 4, 5, 6, 6, 7, 6, 7, 5, 4, 7, 6,...
## $ MAUT2 <dbl> 0, 1, 0, 0, 2, 3, 0, 4, 2, 1, 2, 1, 1, 2, 1, 4, 2, 1,...
## $ MAUT0 <dbl> 1, 2, 2, 0, 1, 3, 1, 2, 3, 2, 1, 2, 3, 0, 3, 2, 0, 2,...
## $ MZFONDS <dbl> 8, 6, 9, 7, 5, 9, 9, 6, 7, 6, 5, 4, 7, 8, 5, 6, 4, 7,...
## $ MZPART <dbl> 1, 3, 0, 2, 4, 0, 0, 3, 2, 3, 4, 5, 2, 1, 4, 3, 5, 2,...
## $ MINKM30 <dbl> 0, 2, 4, 1, 0, 5, 4, 2, 7, 2, 0, 3, 3, 3, 4, 2, 0, 0,...
## $ MINK3045 <dbl> 4, 0, 5, 5, 0, 2, 3, 5, 2, 3, 3, 4, 4, 2, 3, 5, 1, 6,...
## $ MINK4575 <dbl> 5, 5, 0, 3, 9, 3, 3, 3, 1, 3, 2, 3, 3, 4, 3, 3, 6, 3,...
## $ MINK7512 <dbl> 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 1, 1, 0, 0, 2, 0,...
## $ MINK123M <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0,...
## $ MINKGEM <dbl> 4, 5, 3, 4, 6, 3, 3, 3, 2, 4, 8, 3, 3, 4, 3, 3, 5, 4,...
## $ MKOOPKLA <dbl> 3, 4, 4, 4, 3, 3, 5, 3, 3, 7, 7, 4, 3, 4, 3, 3, 4, 2,...
## $ PWAPART <dbl> 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 2, 2, 0, 2,...
## $ PWABEDR <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ PWALAND <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ PPERSAUT <dbl> 6, 0, 6, 6, 0, 6, 6, 0, 5, 0, 6, 5, 6, 0, 5, 0, 0, 6,...
## $ PBESAUT <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ PMOTSCO <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ PVRAAUT <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ PAANHANG <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ PTRACTOR <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ PWERKT <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ PBROM <dbl> 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ PLEVEN <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ PPERSONG <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ PGEZONG <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ PWAOREG <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ PBRAND <dbl> 5, 2, 2, 2, 6, 0, 0, 0, 0, 3, 0, 2, 0, 0, 2, 4, 0, 3,...
## $ PZEILPL <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ PPLEZIER <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ PFIETS <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ PINBOED <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ PBYSTAND <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ AWAPART <dbl> 0, 2, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1,...
## $ AWABEDR <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ AWALAND <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ APERSAUT <dbl> 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1,...
## $ ABESAUT <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ AMOTSCO <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ AVRAAUT <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ AAANHANG <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ ATRACTOR <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ AWERKT <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ ABROM <dbl> 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ ALEVEN <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ APERSONG <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ AGEZONG <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ AWAOREG <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ ABRAND <dbl> 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1,...
## $ AZEILPL <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ APLEZIER <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ AFIETS <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ AINBOED <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ ABYSTAND <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ Purchase <fct> No, No, No, No, No, No, No, No, No, No, No, No, No, N...
Foi afirmado que no conjunto de dados, apenas 6% das pessoas compraram apólice de seguro Caravan.
Veja:
table(Caravan$Purchase)/nrow(Caravan)
##
## No Yes
## 0.94022673 0.05977327
Verificado!
Para verificar se há algum dado faltante basta aplicar o código abaixo:
any(is.na(Caravan))
## [1] FALSE
Perceba que resultou em “False”, logo não há dado faltante.
Cada algoritmo de Machine Learning usado recebe os dados de uma forma, portanto os dados devem ser preparados para serem imputados no algoritmo. Além disso pode-se fazer transformações nos dados para que o algoritmo atue de forma mais performática. No caso do Knn, que é baseado em distância faz-se necessário colocar os dados numa mesma escala. Isso se deve ao fato de que quaisquer variáveis que estiverem em escala muito alta terão um efeito muito maior na distância entre as observações e, portanto, no classificador KNN, do que as variáveis que estão em pequena escala, enviesando assim nossos resultados.
Para dar um exemplo vamos verificar a variância de duas variáveis preditoras
var(Caravan[,1])
## [1] 165.0378
var(Caravan[,2])
## [1] 0.1647078
Claramente as escalas são diferentes! Vamos agora usar a função scale em todas as variáveis preditoras exceto a variavel Purchase(Compra) para colocá-las numa mesma escala:
Caravan[,1:85]<- scale(Caravan[,1:85])
Vamos checar a variancia novamente:
var(Caravan[,1])
## [1] 1
# Standarize the dataset using "scale()" R function
var(Caravan[,2])
## [1] 1
Agora que os dados estão numa mesma escala, o conjunto de dados será dividido em teste e treinamento.
# dados de treinamento e teste
library(caTools)
set.seed(1)
divisao <- sample.split(Caravan$MOSTYPE, SplitRatio = 0.75)
#test.index <- 1:1000
Caravan_treinamento <- subset(Caravan, divisao==TRUE) #treinamento
Caravan_teste <- subset(Caravan, divisao==FALSE) # teste
Lembre-se de que o objetivo é elaborar um modelo para prever se alguém comprará ou não. Será usado a função knn () para isso faz-se necessário especificar 4 de seus argumentos. No primeiro argumento é colocado os dados de treinamento(sem a variábel resposta) , o segundo argumento o conjunto de dados de teste (novamente sem variável resposta), o terceiro argumento a coluna com os dados da variável resposta no conjunto de treinamento e o quarto argumento é o k (quantos vizinhos). A função knn () retorna um vetor de Ys previstos.
Começando com k = 1:
library(class)
set.seed(1)
previsoes <- knn(train = Caravan_treinamento[,-86], test= Caravan_teste[,-86],cl= Caravan_treinamento[,86],k=1)
head(previsoes)
## [1] No No Yes No No No
## Levels: No Yes
Avaliando o modelo e taxa de erro( % de classificação errada):
mean(Caravan_teste[,86] != previsoes)
## [1] 0.1175258
Observe que com k=1 obteve-se 12% de erro.
Mudando o k, para k=5:
previsoes <- knn(train = Caravan_treinamento[,-86], test= Caravan_teste[,-86],cl= Caravan_treinamento[,86],k=7)
mean(Caravan_teste[,86] != previsoes)
## [1] 0.06185567
Obteve-se 6% de erro.
No lugar de ficar “chutando” valores,pode-se automatizar o processo com um loop. Uma forma de fazer isso é a seguinte:
library(class)
previsoes = NULL
perc.erro = NULL
for(i in 1:20){
set.seed(1)
previsoes = knn(train = Caravan_treinamento[,-86], test= Caravan_teste[,-86],cl= Caravan_treinamento[,86],k=i)
perc.erro[i] =mean(Caravan_teste[,86] != previsoes)
}
Perceba que o loop acima cria a variável perc.erro que armazena os erros de previsão para valores de k de 1 até 20.
Veja:
print(perc.erro)
## [1] 0.11752577 0.11752577 0.07353952 0.07560137 0.06735395 0.06391753
## [7] 0.06185567 0.06048110 0.06116838 0.05979381 0.06048110 0.06048110
## [13] 0.06116838 0.06116838 0.06116838 0.06116838 0.06116838 0.06116838
## [19] 0.06116838 0.06116838
Quando colocado num gráfico k x perc.erro fica melhor a visualização:
library(ggplot2)
k.values <- 1:20
error.df <- data.frame(perc.erro,k.values)
ggplot(error.df,aes(x=k.values,y=perc.erro)) + geom_point()+ geom_line(lty="dotted",color='red')
Observe que para k=10 teve-se o menor erro global
Depois de olhar o erro global ainda seria interessante analisar a matriz de confusão já que na base atual 6% das pessoas apenas compraram apólice de seguro Caravan, dificultando a acurácia na previsão de compradores. Mas isso fica para uma outra oportunidade quando for tratado sobre esse assunto específico.
Era isso que eu queria mostrar. Espero que eu tenha conseguido passar um pouco da ideia de Machine Learning, do Knn e de sua aplicação via R!
Keep calm and analysing data!