O aprendizado supervisionado depende de dados rotulados para ajuste do modelo estatístico capaz de realizar predições ou classificações.
Muitos métodos clássicos de aprendizado estatístico, como regressão linear e regressão logística, bem como abordagens mais modernas como GAM, boosting e Support vector machines, operam no domínio do aprendizado supervisionado.1
Neste artigo, discutiremos algumas técnicas de classificação amplamente utilizadas, tais como Análise Discriminante Linear (em inglês LDA - Linear Discriminant Analysis), Análise Discriminante Quadrática (em inglês QDA - Quadratic Discriminant Analysis) e Árvores de Decisão.
Novamente, vamos utilizar o dataset seed2 para ajustar e avaliar todos os modelos de classificação.
O conjunto de dados seeds_dataset se refere a medidas geométricas de sementes (area, perimeter, compactness, legth, width, asymmetry, length_grove) de três variedades de trigo, a saber: Kama, Rosa e Canadian.
Carregamos as seguintes bibliotecas do R:
Definimos o diretório onde se encontra o arquivo a ser importado:
setwd("/home/gf/Scripts/Moyses")
Carregamos o arquivo seeds_dataset2.txt, com a função read.table():
dados <- read.table('seeds_dataset2.txt', header=FALSE)
Constatamos que o conjunto de dados da amostra é constituido por 210 observações, com sete variáveis contínuas, e uma, a última, categórica:
str(dados)
'data.frame': 210 obs. of 8 variables:
$ V1: num 15.3 14.9 14.3 13.8 16.1 ...
$ V2: num 14.8 14.6 14.1 13.9 15 ...
$ V3: num 0.871 0.881 0.905 0.895 0.903 ...
$ V4: num 5.76 5.55 5.29 5.32 5.66 ...
$ V5: num 3.31 3.33 3.34 3.38 3.56 ...
$ V6: num 2.22 1.02 2.7 2.26 1.35 ...
$ V7: num 5.22 4.96 4.83 4.8 5.17 ...
$ V8: int 1 1 1 1 1 1 1 1 1 1 ...
Atribuímos nomes às sete primeiras variáveis, consideradas variáveis preditoras ou variáveis independentes, conforme as medidas geométricas das sementes, adicionando o rótulo “type” à variável dependente ou variável de resposta.
Verificamos a quantidade de itens da amostra por variedade:
table(dados$type)
1 2 3
70 70 70
No R, as variáveis categóricas são armazendas como fator, mediante uso da função factor():
dados$type <- as.factor(dados$type)
Executamos uma função para verificar a existência de dados nulos:
area perimeter compactness length width
0 0 0 0 0
asymmetry length_grove type
0 0 0
Inspecionamos novamente o dataset, agora com a função glimpse():
glimpse(dados)
Rows: 210
Columns: 8
$ area <dbl> 15.26, 14.88, 14.29, 13.84, 16.14, 14.38, 14.69…
$ perimeter <dbl> 14.84, 14.57, 14.09, 13.94, 14.99, 14.21, 14.49…
$ compactness <dbl> 0.8710, 0.8811, 0.9050, 0.8955, 0.9034, 0.8951,…
$ length <dbl> 5.763, 5.554, 5.291, 5.324, 5.658, 5.386, 5.563…
$ width <dbl> 3.312, 3.333, 3.337, 3.379, 3.562, 3.312, 3.259…
$ asymmetry <dbl> 2.2210, 1.0180, 2.6990, 2.2590, 1.3550, 2.4620,…
$ length_grove <dbl> 5.220, 4.956, 4.825, 4.805, 5.175, 4.956, 5.219…
$ type <fct> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
Segmentamos o dataset em dados de treinamento e dados de validação, na proporção 70/30.
Dados de treinamento:
Dados de treinamento são utilizados para ajustar o modelo.
dados_tr <- dados[tr,]
Dados de validação:
Subamostra utilizada para validar o modelo.
dados_val<-dados[-tr,]
Após a partição dos dados, assim fica a distribuição por variedade, para treinamento e validação:
Análise Discriminante Linear(em inglês LDA - Linear Discriminant Analysis) é uma metodologia utilizada para a classificação de elementos de uma amostra ou população sob o enfoque estatístico, integrante do grupo de técnicas de aprendizado supervisionado, pois a aprendizagem se dá através de treinamento com dados rotulados.
A LDA presume que as observações dentro de cada classe são retiradas de uma distribuição multivariada gaussiana e que a covariância das variáveis preditoras são comuns através de todos os níveis da variável resposta Y.
lda.m1 <- lda(type ~ ., data = dados_tr, prior = c(1,1,1)/3)
list("Prior probabilities of groups" = lda.m1$prior,
"SVD - Singular values" = lda.m1$svd)
$`Prior probabilities of groups`
1 2 3
0.3333333 0.3333333 0.3333333
$`SVD - Singular values`
[1] 20.23893 16.11946
A QDA também presume que as observações dentro de cada classe são retiradas de uma distribuição multivariada gaussiana, mas, diferentemente da LDA, presupõe que cada classe da variável resposta Y possui sua própria matriz de covariância.
Call:
qda(type ~ ., data = dados_tr, prior = c(1, 1, 1)/3)
Prior probabilities of groups:
1 2 3
0.3333333 0.3333333 0.3333333
Group means:
area perimeter compactness length width asymmetry
1 14.45942 14.35519 0.8801904 5.528096 3.259288 2.538215
2 18.41609 16.18196 0.8820391 6.187500 3.676761 3.760783
3 11.92204 13.28163 0.8485449 5.246224 2.857980 4.911816
length_grove
1 5.103615
2 6.058804
3 5.134020
Para amostras pequenas, que torna inviável a separação em grupos de treinamento e validação, a literatura sugere o uso da abordagem LOOCV, que exclui uma observação da amostra, e aplica a função discriminante nos dados restantes.
Para medir o desempenho conjunto, os dois modelos serão aplicados aos dados de validação.
Calculamos a tabela de confusão para ambos os modelos, para avaliar qual deles apresentou melhor desempenho. Recordem que a repartição amostral dos dados, na proporção 70/30, alocou 18, 24 e 21 observações para as variedades 1, 2 e 3, respectivamente, para validação do modelo.
lda.mc <- table(dados_val$type, preditos.lda$class)
lda.mc
1 2 3
1 18 0 0
2 1 23 0
3 3 0 18
qda.mc <- table(dados_val$type, preditos.qda$class)
qda.mc
1 2 3
1 17 0 1
2 1 23 0
3 3 0 18
ac.lda <- mean(preditos.lda$class==dados_val$type)
ac.qda <- mean(preditos.qda$class==dados_val$type)
list("Acurácia Modelo LDA" = ac.lda,
"Acurácia Modelo QDA" = ac.qda)
$`Acurácia Modelo LDA`
[1] 0.9365079
$`Acurácia Modelo QDA`
[1] 0.9206349
dados_val %>%
mutate(pred.lda = (preditos.lda$class),
pred.qda = (preditos.qda$class)) %>%
summarise(lda.error = mean(type != pred.lda),
qda.error = mean(type != pred.qda))
lda.error qda.error
1 0.06349206 0.07936508
Sensibilidade mede o percentual de verdadeiros positivos. Especificidade mede o percentual de verdadeiros negativos.
sens.lda <- lda.mc[2,2]/ sum(lda.mc[2,])
sens.qda <- qda.mc[2,2]/ sum(qda.mc[2,])
list("Sensibilidade do Modelo LDA" = sens.lda,
"Sensibilidade do Modelo QDA" = sens.qda)
$`Sensibilidade do Modelo LDA`
[1] 0.9583333
$`Sensibilidade do Modelo QDA`
[1] 0.9583333
esp.lda <- lda.mc[1,1]/ sum(lda.mc[1,])
esp.qda <- qda.mc[1,1]/ sum(qda.mc[1,])
list("Especificidade do Modelo LDA" = esp.lda,
"Especificidade do Modelo QDA" = esp.qda)
$`Especificidade do Modelo LDA`
[1] 1
$`Especificidade do Modelo QDA`
[1] 0.9444444
Ao comparar os indicadores de qualidade dos modelos, concluímos que o modelo linear de Análise Discriminante apresentou desempenho ligeiramente superior ao modelo Quadrático.
Finalmente, representamos graficamente o modo como as duas funções discriminantes separam as variedades: a LD1, que responde por 61,18% da variância entre os grupos, desmembra as variedades 2 e 3 de maneira indiscutível. Contudo, a LD2, que responde por apenas 38,81% da variância entre os grupos, não aparta as variedades 1 e 2 e tampouco as 1 e 3 de forma inequívoca.
lda.data <- cbind(dados_tr, predict(lda.m1)$x)
ggplot(lda.data, aes(LD1, LD2)) +
geom_point(aes(color = type))
Árvore de Decisão é um algoritmo de aprendizagem supervisionada, que pode ser utilizado para resolver problemas de classificação, adequado para variáveis dependentes categóricas.
arvore1 <- tree(type ~ ., data = dados_train)
Quatro variáveis foram utilizadas na construção da árvore.
summary(arvore1)
Classification tree:
tree(formula = type ~ ., data = dados_train)
Variables actually used in tree construction:
[1] "length_grove" "area" "compactness" "asymmetry"
Number of terminal nodes: 6
Residual mean deviance: 0.1691 = 16.74 / 99
Misclassification error rate: 0.0381 = 4 / 105
mod_poda=prune.tree(arvore1,best=3)
plot(mod_poda)
text(mod_poda, pretty =0)
[1] 1 1 1 1 1 1
Levels: 1 2 3
confusionMatrix(pred_arv, dados_test$type)
Confusion Matrix and Statistics
Reference
Prediction 1 2 3
1 31 2 6
2 0 33 0
3 4 0 29
Overall Statistics
Accuracy : 0.8857
95% CI : (0.8089, 0.9395)
No Information Rate : 0.3333
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.8286
Mcnemar's Test P-Value : NA
Statistics by Class:
Class: 1 Class: 2 Class: 3
Sensitivity 0.8857 0.9429 0.8286
Specificity 0.8857 1.0000 0.9429
Pos Pred Value 0.7949 1.0000 0.8788
Neg Pred Value 0.9394 0.9722 0.9167
Prevalence 0.3333 0.3333 0.3333
Detection Rate 0.2952 0.3143 0.2762
Detection Prevalence 0.3714 0.3143 0.3143
Balanced Accuracy 0.8857 0.9714 0.8857
bagging <- randomForest(type ~ ., data=dados_train, mtry = 7)
bagging
Call:
randomForest(formula = type ~ ., data = dados_train, mtry = 7)
Type of random forest: classification
Number of trees: 500
No. of variables tried at each split: 7
OOB estimate of error rate: 5.71%
Confusion matrix:
1 2 3 class.error
1 32 1 2 0.08571429
2 1 34 0 0.02857143
3 2 0 33 0.05714286
pred_bagg <- predict(bagging, dados_test)
confusionMatrix(pred_bagg, dados_test$type)
Confusion Matrix and Statistics
Reference
Prediction 1 2 3
1 32 6 6
2 0 29 0
3 3 0 29
Overall Statistics
Accuracy : 0.8571
95% CI : (0.7753, 0.9178)
No Information Rate : 0.3333
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.7857
Mcnemar's Test P-Value : NA
Statistics by Class:
Class: 1 Class: 2 Class: 3
Sensitivity 0.9143 0.8286 0.8286
Specificity 0.8286 1.0000 0.9571
Pos Pred Value 0.7273 1.0000 0.9063
Neg Pred Value 0.9508 0.9211 0.9178
Prevalence 0.3333 0.3333 0.3333
Detection Rate 0.3048 0.2762 0.2762
Detection Prevalence 0.4190 0.2762 0.3048
Balanced Accuracy 0.8714 0.9143 0.8929
rf<- randomForest(type ~area+perimeter+compactness+length
+width+asymmetry+length_grove,
data=dados_train, mtry = 2)
rf
Call:
randomForest(formula = type ~ area + perimeter + compactness + length + width + asymmetry + length_grove, data = dados_train, mtry = 2)
Type of random forest: classification
Number of trees: 500
No. of variables tried at each split: 2
OOB estimate of error rate: 6.67%
Confusion matrix:
1 2 3 class.error
1 31 2 2 0.11428571
2 2 33 0 0.05714286
3 1 0 34 0.02857143
pred_rf <- predict(rf, dados_test)
confusionMatrix(pred_rf, dados_test$type)
Confusion Matrix and Statistics
Reference
Prediction 1 2 3
1 31 7 7
2 0 28 0
3 4 0 28
Overall Statistics
Accuracy : 0.8286
95% CI : (0.7427, 0.8951)
No Information Rate : 0.3333
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.7429
Mcnemar's Test P-Value : NA
Statistics by Class:
Class: 1 Class: 2 Class: 3
Sensitivity 0.8857 0.8000 0.8000
Specificity 0.8000 1.0000 0.9429
Pos Pred Value 0.6889 1.0000 0.8750
Neg Pred Value 0.9333 0.9091 0.9041
Prevalence 0.3333 0.3333 0.3333
Detection Rate 0.2952 0.2667 0.2667
Detection Prevalence 0.4286 0.2667 0.3048
Balanced Accuracy 0.8429 0.9000 0.8714
i_mod_rf <-importance(rf)
i_mod_rf
MeanDecreaseGini
area 16.718417
perimeter 13.211895
compactness 7.373120
length 5.775742
width 10.845447
asymmetry 3.405758
length_grove 11.996439
varImpPlot (rf)
Kuhn, M. (2008). Building Predictive Models in R Using the caret Package. Journal of Statistical Software, 28(5), 1–26. https://doi.org/10.18637/jss.v028.i05
Kuhn, M. (2019). The caret Package. Disponível em: https://topepo.github.io/caret/
Veja o capítulo 4 do livro “An Introduction to Statistical Learning: with Applications in R”↩︎
Dataset criado por Magorzata Charytanowicz, Jerzy Niewczas, Piotr Kulczycki, Piotr Kowalski e Szymon Lukasik, pesquisadores do Instituto de Agrofísica da Academia Polonesa de Ciências em Lublin, disponível sob a licença Creative Commons Attribution 4.0 International (CC BY 4.0)↩︎