O KNN é um algoritmo muito simples no qual cada observação é prevista com base em sua “semelhança” com outras observações. Ao contrário da maioria dos métodos, KNN é um algoritmo baseado na memória e não pode ser resumido por um modelo de forma fechada. Isso significa que as amostras de treinamento são necessárias no tempo de execução e as previsões são feitas diretamente das relações amostrais. Consequentemente, os KNNs também são conhecidos como aprendizes preguiçosos
#> default student balance income
#> 1 No No 729.5265 44361.625
#> 2 No Yes 817.1804 12106.135
#> 3 No No 1073.5492 31767.139
#> 4 No No 529.2506 35704.494
#> 5 No No 785.6559 38463.496
#> 6 No Yes 919.5885 7491.559
#> inadimplente estudante balanco receita
#> Nao:9667 Min. :0.0000 Min. : 0.0 Min. : 772
#> Sim: 333 1st Qu.:0.0000 1st Qu.: 481.7 1st Qu.:21340
#> Median :0.0000 Median : 823.6 Median :34553
#> Mean :0.2944 Mean : 835.4 Mean :33517
#> 3rd Qu.:1.0000 3rd Qu.:1166.3 3rd Qu.:43808
#> Max. :1.0000 Max. :2654.3 Max. :73554
Matriz de dispersão
Vamos agora explorar os dados originais para termos algum visão do comportamento das variáveis explicativas e a variável dependente.
library(psych)pairs.panels(credito, method ="pearson", # metodo de correlação hist.col ="#00AFBB", density =TRUE, # mostra graficos de densidade ellipses =FALSE# mostra elipses de correlação)
Avaliando o comportamento das variáveis em função do status (inadimplente / estudante)
ggplot(data =credito, aes(x=balanco, y =receita, col =inadimplente))+geom_point()
KNN
Vamos usar a função knn da biblioteca caret que tem ótimas funcionalidades. Observem que a saída pode ser as classes ou as probabilidades de pertencer a uma classe
Como o KNN usa as distancias entre os pontos ele é afetado pela escala dos dados, portanto, é necessário que os dados sejam normalizados (padronizados) para eliminar este efeito.
Quando temos diversas variáveis explicativas em diferentes escalas, em geral, elas devem ser transformadas para ter media zero e desvio padrão 1
Criando conjuntos de treino e teste e normalizando variáveis
library(caret)set.seed(2024)y<-credito$inadimplentecredito_split<-createDataPartition(y, times =1, p =0.80, list =FALSE)conj_treino<-credito[credito_split,]conj_treino[,3:4]<-scale(conj_treino[,3:4])# scale normalizaconj_teste<-credito[-credito_split,]conj_teste[,3:4]<-scale(conj_teste[, 3:4])summary(conj_treino)
#> inadimplente estudante balanco receita
#> Nao:7734 Min. :0.0000 Min. :-1.72131 Min. :-2.45922
#> Sim: 267 1st Qu.:0.0000 1st Qu.:-0.73380 1st Qu.:-0.91305
#> Median :0.0000 Median :-0.02068 Median : 0.07759
#> Mean :0.2945 Mean : 0.00000 Mean : 0.00000
#> 3rd Qu.:1.0000 3rd Qu.: 0.68805 3rd Qu.: 0.77310
#> Max. :1.0000 Max. : 3.74460 Max. : 2.93799
set.seed(23)t_knn1<-knn3(inadimplente~balanco+receita+estudante, data =conj_treino, k =90)t_knn1
#> 90-nearest neighbor model
#> Training set outcome distribution:
#>
#> Nao Sim
#> 7734 267
Avaliando o modelo
Através da função matriz de confusão do pacote caret conseguimos obter as principais medidas de avaliação de um modelo de classificação.
Veja que a acurácia deu um valor alto, mas isto não é suficiente para considerarmos que temos um bom modelo. Veja que a sensibilidade está muito baixa e que o ideal é que tenhamos valores altos de sensibilidade e especificidade.
Observar que a prevalência é muito baixa o que está afetando os resultados do modelo.
y_chapeu_knn1<-predict(t_knn1, conj_teste, type ="class")confusionMatrix(y_chapeu_knn1, conj_teste$inadimplente, positive="Sim")
# Aqui gera o curva e salvo numa variávelroc_knn1<-roc(conj_teste$inadimplente~p_chapeu_knn1[,2], plot =TRUE, print.auc=FALSE, col="black", legacy.axes=TRUE)legend("bottomright",legend=c("KNN1"), col=c("black"),lwd=4)
Area embaixo da curva ROC
# Area abaixo da Curva (AUC)as.numeric(roc_knn1$auc)
#> [1] 0.9468286
Variando K
Anteriormente usamos k=90. Este parametro deve ser ajustado para melhoramos os modelo KNN. Para isto vamos usar a função train da biblioteca caret
Observe que a otimização de k é feita através de acurácia.
set.seed(2024)# Usando validação cruzada para obter o valor de k através da função train da biblioteca caret e o controle do treino e fazendo um gride de valores para k.ctrl<-trainControl(method ="repeatedcv", number =10, repeats =5)t_knn2<-train(inadimplente~balanco+receita+estudante, method ="knn", trControl=ctrl, tuneGrid =data.frame(k =seq(5,100, by=5)), metric ="Accuracy", data =conj_treino)## Resultados do treinot_knn2
#> k-Nearest Neighbors
#>
#> 8001 samples
#> 3 predictor
#> 2 classes: 'Nao', 'Sim'
#>
#> No pre-processing
#> Resampling: Cross-Validated (10 fold, repeated 5 times)
#> Summary of sample sizes: 7200, 7202, 7200, 7202, 7201, 7200, ...
#> Resampling results across tuning parameters:
#>
#> k Accuracy Kappa
#> 5 0.9687539 0.3713723
#> 10 0.9710787 0.4074849
#> 15 0.9719035 0.3949430
#> 20 0.9714038 0.3650255
#> 25 0.9716285 0.3568810
#> 30 0.9716288 0.3532068
#> 35 0.9715038 0.3445969
#> 40 0.9715037 0.3418931
#> 45 0.9714537 0.3369602
#> 50 0.9715037 0.3314707
#> 55 0.9711037 0.3129172
#> 60 0.9707038 0.2893980
#> 65 0.9705289 0.2761428
#> 70 0.9704536 0.2663185
#> 75 0.9699037 0.2364880
#> 80 0.9696287 0.2176720
#> 85 0.9693037 0.1946375
#> 90 0.9688291 0.1614589
#> 95 0.9685543 0.1362015
#> 100 0.9680543 0.1070964
#>
#> Accuracy was used to select the optimal model using the largest value.
#> The final value used for the model was k = 15.
Observe que os resultados de área abaixo da ROC não são suficientes para a escolha do k, pois precisamos estar atentos a sensibilidade e especificidade!