Classificação Multiclasses
Suponha um problema de classificação em que, em vez de cada instância (observação) pertencer a uma de duas possíveis classes, ela pertence a uma entre \(k\) possíveis classes.
Este problema também pode ser resolvido com uma rede neural Perseptron. A rede que vai atender a esse tipo de problema será muito parecida com aquelas vistas até o momento, a diferença é que as redes para esse tipo de problema são definidas com mais neurônios na camada de saída.
Arquitetura do Perseptron para problemas de classificação Multiclasses
Se a variável resposta do problema de classificação puder assumir 1 entre \(k\) possíveis classes, o número de neurônios na camada de saída será igual a \(k\). Dessa forma em vez de uma saída teremos \(k\) saídas, que são previsões para cada classe.
A figura apresenta uma rede com 3 covariáveis de entrada, 1 camada oculta com 4 neurônios e uma camada de saída com 3 neurônios. Isso indica que esta rede foi construída para um problema de classificação com 3 classes. A saída retorna um número entre 0 e 1 e este número indica a probabilidade da instância pertencer a classe correspondente.
A função de Atrivação Softmax
É esperado que, para uma instância qualquer, a soma das saídas para cada classe seja iguak a 1. Isso pode ser feito, as vezes manualmente, usando a função softmax.
Seja \(K\) o número de classes possíveis. Vamos chamar de \(v_{i,k}\) o valor que saí do somatório do \(k\)-ésimo neurônio da camada de saída ara a \(i\)-ésima observação. A função de ativação softmax é definda por:
\[ \hat{y}_{i,k} = \dfrac{e^{v_{i,k}}}{\sum_{k=1}^{K} e^{v_{i,k}}} \]
Nesse caso, a rede neural terá mais um neurônio na camada oculta com função de ativação softmax e \(\hat{y}_{i,k}\) será a saída desse neurônio.
EC para multiclasse
O que muda no treinamento para o problema multicalsse é a função de custo, que agora deverá considerar um problema de mais de uma classe.
A expressão da entropia cruzada pode ser generalizada para o caso do problema com \(K\) classes, \(K > 2\)
\[ EC = - \dfrac{1}{N} \sum_{i=1}^N \sum_{k=1}^K y_{k,i}\ln(\hat{y}_{k,i}) \]
Essa será a função de custo para o treinamento dos problemas multiclasses, sendo eles com ou sem a função softmax.
Exemplo
Vamos usar o exemplo da base de seguros e tentar prever se um cliente é considerado de alto, médio ou baixo custo.
Primeiro, carregamos os pacotes.
library(tidyverse)
library(caret)
library(neuralnet)Depois, carregamos a base de dados já tratada.
base_treino_sem_NA = readRDS("base_treino_sem_NA.RDS")
base_treino_final = readRDS("base_treino_final_log.RDS")O objeto base_treino_sem_NA guarda a base de treino nas unidades originais, sem a padronização das variáveis. Já o objeto base_treino_final guarda a base padroniada.
Criação da variável alvo
Os clientes de baixo custo serão aqueles com charges\(< 10.000\), os de custo médio serão aqueles com $10.000 $ charges\(\le 20.000\), e clientes de alto custo serão aqueles com charges\(> 20.000\).
hist(base_treino_sem_NA$charges)summary(base_treino_sem_NA$charges) Min. 1st Qu. Median Mean 3rd Qu. Max.
1132 4843 9565 13396 17082 63770
custobaixo = ifelse(base_treino_sem_NA$charges<5000,1,0)
customedio = ifelse(base_treino_sem_NA$charges>=5000 & base_treino_sem_NA$charges<=20000,1,0)
custoalto = ifelse(base_treino_sem_NA$charges>20000,1,0)
base_treino_final = base_treino_final |> cbind(custobaixo,customedio,custoalto)Vamos tirar da matriz de treinamento as variáveis de região e charges para simplificar o código.
colnames(base_treino_final) [1] "age" "sexmale" "bmi" "children"
[5] "smokeryes" "regionnorthwest" "regionsoutheast" "regionsouthwest"
[9] "charges" "custobaixo" "customedio" "custoalto"
base_treino_final = base_treino_final[,-c(6,7,8,9)]
colnames(base_treino_final)[1] "age" "sexmale" "bmi" "children" "smokeryes"
[6] "custobaixo" "customedio" "custoalto"
Treinamento e previsão com o neuralnet
modelo_1 = neuralnet(
custobaixo + customedio + custoalto ~ .,
data = base_treino_final,
hidden = 1,
linear.output = FALSE,
err.fct = "ce")
plot(modelo_1,rep="best")modelo_2 = neuralnet(
custobaixo + customedio + custoalto + custoalto ~ .,
data = base_treino_final,
hidden = 2,
linear.output = FALSE,
err.fct = "ce")
plot(modelo_2,rep="best")modelo_3 = neuralnet(
custobaixo + customedio + custoalto ~ .,
data = base_treino_final,
hidden = 3,
linear.output = FALSE,
err.fct = "ce")
plot(modelo_3,rep="best")Agora a previsão não será mais um vetor de uma única coluna, e sim uma matriz com o número de colunas igual ao número de classes.
prev_treino_1 = modelo_1$net.result[[1]]
head(prev_treino_1) [,1] [,2] [,3]
1 0.516771665 0.1571478 0.32971149
2 0.516771665 0.1571478 0.32971149
3 0.516771665 0.1571478 0.32971149
4 0.516771665 0.1571478 0.32971149
5 0.516771665 0.1571478 0.32971149
6 0.001000017 0.9227626 0.07798269
head(rowSums(prev_treino_1)) 1 2 3 4 5 6
1.003631 1.003631 1.003631 1.003631 1.003631 1.001745
A função neuralnet não implementa a softmax e por isso as saidas não somam necessariamente 1. Mas isso pode ser corrigido com uma simples transformação, que não é igual a considerar o softmax no treinamento.
prev_treino_1 = prev_treino_1 / rowSums(prev_treino_1)
head(prev_treino_1) [,1] [,2] [,3]
1 0.5149020740 0.1565793 0.32851865
2 0.5149020740 0.1565793 0.32851865
3 0.5149020740 0.1565793 0.32851865
4 0.5149020740 0.1565793 0.32851865
5 0.5149020740 0.1565793 0.32851865
6 0.0009982743 0.9211549 0.07784682
head(rowSums(prev_treino_1))1 2 3 4 5 6
1 1 1 1 1 1
prev_treino_2 = modelo_2$net.result[[1]]
head(prev_treino_2) [,1] [,2] [,3]
1 0.002297474 0.90570010 0.09757296
2 0.878021795 0.09659256 0.04587041
3 0.831185484 0.11828337 0.04707040
4 0.877506163 0.09684338 0.04588541
5 0.876848110 0.09716301 0.04590448
6 0.002381368 0.92760958 0.07844518
prev_treino_2 = prev_treino_2 / rowSums(prev_treino_2)
head(prev_treino_2) [,1] [,2] [,3]
1 0.002284747 0.90068281 0.09703244
2 0.860396777 0.09465360 0.04494962
3 0.834072000 0.11869414 0.04723386
4 0.860102040 0.09492263 0.04497533
5 0.859726150 0.09526573 0.04500812
6 0.002361446 0.91984961 0.07778894
prev_treino_3 = modelo_3$net.result[[1]]
head(prev_treino_3) [,1] [,2] [,3]
1 0.000000e+00 0.66122585 0.1673541996
2 8.806445e-01 0.08158596 0.0544018677
3 7.840406e-01 0.15063377 0.0436653657
4 8.717443e-01 0.08778565 0.0530420551
5 8.663019e-01 0.09159128 0.0522646450
6 3.099963e-08 0.99999992 0.0001070966
prev_treino_3 = prev_treino_3 / rowSums(prev_treino_2)
head(prev_treino_3) [,1] [,2] [,3]
1 0.000000e+00 0.66122585 0.1673541996
2 8.806445e-01 0.08158596 0.0544018677
3 7.840406e-01 0.15063377 0.0436653657
4 8.717443e-01 0.08778565 0.0530420551
5 8.663019e-01 0.09159128 0.0522646450
6 3.099963e-08 0.99999992 0.0001070966
Treinamento e previsão com o nnet
O nnet é um outro pacote que implementa redes neurais Perceptrons. Nele a função softymax é implementada, mas tem desvantagens em relação ao neuralnet. Por exemplo, só permite 1 camada oculta.
Neste pacote, diferente do neuralnet, a base de dados deve ser um data.frame e não uma matriz.
base_nnet = tibble(
sex = base_treino_sem_NA$sex,
smoker = base_treino_sem_NA$smoker,
age = scale(base_treino_sem_NA$age),
bmi = scale(base_treino_sem_NA$bmi),
children = scale(base_treino_sem_NA$children),
custo = as.factor(ifelse(base_treino_sem_NA$charges < 5000,"baixo",ifelse(base_treino_sem_NA$charges > 20000,"alto","medio")))
)library(nnet)
modelo_nnet_2 = nnet(custo ~ ., data = base_nnet,size = 2)# weights: 21
initial value 993.326461
iter 10 value 540.840443
iter 20 value 376.211058
iter 30 value 307.326849
iter 40 value 304.186813
iter 50 value 304.154936
iter 60 value 304.133905
iter 70 value 303.750198
iter 80 value 303.716184
iter 90 value 303.386665
iter 100 value 303.365268
final value 303.365268
stopped after 100 iterations
names(modelo_nnet_2) [1] "n" "nunits" "nconn" "conn"
[5] "nsunits" "decay" "entropy" "softmax"
[9] "censored" "value" "wts" "convergence"
[13] "fitted.values" "residuals" "lev" "call"
[17] "terms" "coefnames" "contrasts" "xlevels"
modelo_nnet_2$softmax[1] TRUE
head(modelo_nnet_2$fitted.values) alto baixo medio
1 0.09453284 0.0001718783 0.90529528
2 0.03764167 0.8703341593 0.09202417
3 0.04615683 0.8360622986 0.11778087
4 0.03775797 0.8698749936 0.09236704
5 0.03790625 0.8692891808 0.09280457
6 0.07637451 0.0001955817 0.92342991
head(rowSums(modelo_nnet_2$fitted.values))1 2 3 4 5 6
1 1 1 1 1 1
Para a previsão, temos que tomar cuidado. Para os modelos neuralnet a ordem das colunas de previsão é igual a ordem que colocamos no argumento custobaixo + customedio + custoalto ~ ., ou seja, a primeira coluna é a previsão para baixo custo, a segunda para custo médio e a terceira para alto custo.
Já na saída nnet temos que ver a ordem que ele retonar no objeto que guardamos com o comando modelo_nnet_2$fitted.values.
prev_treino_2_nnet = modelo_nnet_2$fitted.values
head(prev_treino_2_nnet) alto baixo medio
1 0.09453284 0.0001718783 0.90529528
2 0.03764167 0.8703341593 0.09202417
3 0.04615683 0.8360622986 0.11778087
4 0.03775797 0.8698749936 0.09236704
5 0.03790625 0.8692891808 0.09280457
6 0.07637451 0.0001955817 0.92342991
para facilitar as contas de medida de qualidade, vamos reordenar as colunas.
prev_treino_2_nnet = prev_treino_2_nnet[,c("baixo","medio","alto")]
head(prev_treino_2_nnet) baixo medio alto
1 0.0001718783 0.90529528 0.09453284
2 0.8703341593 0.09202417 0.03764167
3 0.8360622986 0.11778087 0.04615683
4 0.8698749936 0.09236704 0.03775797
5 0.8692891808 0.09280457 0.03790625
6 0.0001955817 0.92342991 0.07637451
EC na base de treino
Para o caso do problema com \(K\) classes, \(K > 2\), a expressão da entropia cruzada precisa ser generalizada.
\[ EC = - \dfrac{1}{N} \sum_{i=1}^N \sum_{k=1}^K y_{k,i}\ln(\hat{y}_{k,i}) \]
sendo, \(N\) o número de instâncias (observações), \(K\) o número de classes, \(y_{k,i}\) a variável indicadora da classe \(k\), isto é, \(y_{k,i} = 1\) se a isntância \(i\) pertence a classe \(k\) e 0 caso contrário, e \(\hat{y}_{k,i}\) o valor de saída para a classe \(k\).
clip = function(x,eps=0.00001){
return(pmax(pmin(x,1-eps),eps))
}
#real = uma matriz com N linhas e K colunas
#real(i,j)=1 se a observacao i é da classe j
#previsao = uma matriz com N linhas e K colunas
#previsao eh uma matriz de numeros reais entre 0 e 1 de forma que a soma das linhas tem que ser 1.
EC = function(real,previsao){
N = dim(real)[1]
K = dim(real)[2]
ec = -sum(rowSums(real*log(clip(previsao))))/N
return(ec)
}classe_real_treino = matrix(
c(custobaixo,customedio,custoalto),
ncol = 3,byrow = F)
head(classe_real_treino) [,1] [,2] [,3]
[1,] 0 1 0
[2,] 1 0 0
[3,] 1 0 0
[4,] 1 0 0
[5,] 1 0 0
[6,] 0 1 0
(EC_treino_1 = EC(classe_real_treino,prev_treino_1))[1] 0.6466046
(EC_treino_2 = EC(classe_real_treino,prev_treino_2))[1] 0.3240459
(EC_treino_3 = EC(classe_real_treino,prev_treino_3))[1] 0.3100071
(EC_treino_2_nnet = EC(classe_real_treino,prev_treino_2_nnet))[1] 0.3241082
Matriz de Confusão para a base de treino
Primeiro definimos a variável custo que deve ser do tipo factor e conter a classe para cada observação da base de teste.
custo = as.factor(ifelse(base_treino_sem_NA$charges < 5000,"baixo",ifelse(base_treino_sem_NA$charges > 20000,"alto","medio")))Vamos usar como a classe prevista aquela de maior valor estimado:
coluna = apply(prev_treino_1,MARGIN = 1,FUN = "which.max")
prev_custo_1 = ifelse(coluna==1,"baixo",ifelse(coluna==2,"medio","alto"))
prev_custo_1 = factor(prev_custo_1)(CM_1 = confusionMatrix(
factor(prev_custo_1),
factor(custo)
))Confusion Matrix and Statistics
Reference
Prediction alto baixo medio
alto 0 1 0
baixo 157 242 74
medio 36 2 424
Overall Statistics
Accuracy : 0.7115
95% CI : (0.6813, 0.7404)
No Information Rate : 0.5321
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.5231
Mcnemar's Test P-Value : < 2.2e-16
Statistics by Class:
Class: alto Class: baixo Class: medio
Sensitivity 0.000000 0.9878 0.8514
Specificity 0.998654 0.6657 0.9132
Pos Pred Value 0.000000 0.5116 0.9177
Neg Pred Value 0.793583 0.9935 0.8439
Prevalence 0.206197 0.2618 0.5321
Detection Rate 0.000000 0.2585 0.4530
Detection Prevalence 0.001068 0.5053 0.4936
Balanced Accuracy 0.499327 0.8267 0.8823
coluna = apply(prev_treino_2,MARGIN = 1,FUN = "which.max")
prev_custo_2 = ifelse(coluna==1,"baixo",ifelse(coluna==2,"medio","alto"))
prev_custo_2 = factor(prev_custo_2)(CM_2 = confusionMatrix(
factor(prev_custo_2),
factor(custo)
))Confusion Matrix and Statistics
Reference
Prediction alto baixo medio
alto 137 0 4
baixo 13 238 28
medio 43 7 466
Overall Statistics
Accuracy : 0.8985
95% CI : (0.8774, 0.9171)
No Information Rate : 0.5321
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.8302
Mcnemar's Test P-Value : 1.602e-12
Statistics by Class:
Class: alto Class: baixo Class: medio
Sensitivity 0.7098 0.9714 0.9357
Specificity 0.9946 0.9407 0.8858
Pos Pred Value 0.9716 0.8530 0.9031
Neg Pred Value 0.9296 0.9893 0.9238
Prevalence 0.2062 0.2618 0.5321
Detection Rate 0.1464 0.2543 0.4979
Detection Prevalence 0.1506 0.2981 0.5513
Balanced Accuracy 0.8522 0.9560 0.9108
coluna = apply(prev_treino_3,MARGIN = 1,FUN = "which.max")
prev_custo_3 = ifelse(coluna==1,"baixo",ifelse(coluna==2,"medio","alto"))
prev_custo_3 = factor(prev_custo_3)(CM_3 = confusionMatrix(
factor(prev_custo_3),
factor(custo)
))Confusion Matrix and Statistics
Reference
Prediction alto baixo medio
alto 138 0 4
baixo 13 238 28
medio 42 7 466
Overall Statistics
Accuracy : 0.8996
95% CI : (0.8785, 0.9181)
No Information Rate : 0.5321
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.832
Mcnemar's Test P-Value : 2.581e-12
Statistics by Class:
Class: alto Class: baixo Class: medio
Sensitivity 0.7150 0.9714 0.9357
Specificity 0.9946 0.9407 0.8881
Pos Pred Value 0.9718 0.8530 0.9049
Neg Pred Value 0.9307 0.9893 0.9240
Prevalence 0.2062 0.2618 0.5321
Detection Rate 0.1474 0.2543 0.4979
Detection Prevalence 0.1517 0.2981 0.5502
Balanced Accuracy 0.8548 0.9560 0.9119
coluna = apply(prev_treino_2_nnet,MARGIN = 1,FUN = "which.max")
prev_custo_2_nnet = ifelse(coluna==1,"baixo",ifelse(coluna==2,"medio","alto"))
prev_custo_2_nnet = factor(prev_custo_2_nnet)(CM_2_nnet = confusionMatrix(
factor(prev_custo_2_nnet),
factor(custo)
))Confusion Matrix and Statistics
Reference
Prediction alto baixo medio
alto 136 0 4
baixo 13 238 28
medio 44 7 466
Overall Statistics
Accuracy : 0.8974
95% CI : (0.8762, 0.9161)
No Information Rate : 0.5321
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.8283
Mcnemar's Test P-Value : 9.933e-13
Statistics by Class:
Class: alto Class: baixo Class: medio
Sensitivity 0.7047 0.9714 0.9357
Specificity 0.9946 0.9407 0.8836
Pos Pred Value 0.9714 0.8530 0.9014
Neg Pred Value 0.9284 0.9893 0.9236
Prevalence 0.2062 0.2618 0.5321
Detection Rate 0.1453 0.2543 0.4979
Detection Prevalence 0.1496 0.2981 0.5524
Balanced Accuracy 0.8496 0.9560 0.9097
Leitura da base de teste
Vamos carregar a base de teste, que já está sem NA e com as variáveis independentes quantitativas padronizadas e as qualitativas transformadas em indicadoras.
base_teste = readRDS("base_teste_final.RDS")
head(base_teste) age sexmale bmi children smokeryes regionnorthwest
1 -0.4405096 1 -1.2931960 -0.92254085 0 1
2 0.4752807 0 0.4404965 -0.08337833 0 0
3 -1.1449637 1 0.5955356 -0.92254085 0 0
4 -1.1449637 1 -1.1090870 -0.92254085 0 0
5 1.1797348 1 1.5483800 -0.92254085 0 0
6 -0.1587280 1 -0.4340210 0.75578419 0 1
regionsoutheast regionsouthwest
1 0 0
2 1 0
3 0 1
4 0 0
5 0 1
6 0 0
Previsão na base de teste
prev_teste_1 = predict(modelo_1,newdata = base_teste)
prev_teste_2 = predict(modelo_2,newdata = base_teste)
prev_teste_3 = predict(modelo_3,newdata = base_teste)EC na base de teste
base_teste_bruta = readRDS("base_teste_bruta.RDS")
base_teste_bruta = base_teste_bruta |> select(-married)
base_teste_bruta = na.omit(base_teste_bruta)custobaixo_teste = ifelse(base_teste_bruta$charges<5000,1,0)
customedio_teste = ifelse(base_teste_bruta$charges>=5000 & base_teste_bruta$charges<=20000,1,0)
custoalto_teste = ifelse(base_teste_bruta$charges>20000,1,0)
classe_real_teste = matrix(
c(custobaixo_teste,customedio_teste,custoalto_teste),
ncol = 3,byrow = F)
head(classe_real_teste) [,1] [,2] [,3]
[1,] 0 0 1
[2,] 0 1 0
[3,] 1 0 0
[4,] 1 0 0
[5,] 0 1 0
[6,] 0 1 0
(EC_teste_1 = EC(classe_real_teste,prev_teste_1))[1] 0.6131556
(EC_teste_2 = EC(classe_real_teste,prev_teste_2))[1] 0.2743206
(EC_teste_3 = EC(classe_real_teste,prev_teste_3))[1] 0.2948038
Matriz de Confusão para a base de teste
Primeiro definimos a variável custo_teste que deve ser do tipo factor e conter a classe para cada observação da base de teste.
custo_teste = as.factor(ifelse(base_teste_bruta$charges < 5000,"baixo",ifelse(base_teste_bruta$charges > 20000,"alto","medio")))Vamos usar como a classe prevista aquela de maior valor estimado:
coluna = apply(prev_teste_1,MARGIN = 1,FUN = "which.max")
prev_custo_1_teste = ifelse(coluna==1,"baixo",ifelse(coluna==2,"medio","alto"))
prev_custo_1_teste = factor(prev_custo_1_teste)Os levels de prev_custo_1_teste e custo_teste devem ser os mesmos.
levels(prev_custo_1_teste)[1] "alto" "baixo" "medio"
levels(custo_teste)[1] "alto" "baixo" "medio"
Pelo que percebemos, o modelo_1 não realiza previsão do tipo altocusto, mas precisamos inlcuir essa classe para que a função de confusão seja construída.
levels(prev_custo_1_teste) = c(levels(prev_custo_1_teste),"alto")
levels(prev_custo_1_teste)[1] "alto" "baixo" "medio"
(CM_1_teste = confusionMatrix(prev_custo_1_teste,custo_teste))Confusion Matrix and Statistics
Reference
Prediction alto baixo medio
alto 1 0 0
baixo 67 113 29
medio 10 0 177
Overall Statistics
Accuracy : 0.733
95% CI : (0.6866, 0.7759)
No Information Rate : 0.5189
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.5589
Mcnemar's Test P-Value : < 2.2e-16
Statistics by Class:
Class: alto Class: baixo Class: medio
Sensitivity 0.012821 1.0000 0.8592
Specificity 1.000000 0.6620 0.9476
Pos Pred Value 1.000000 0.5407 0.9465
Neg Pred Value 0.805556 1.0000 0.8619
Prevalence 0.196474 0.2846 0.5189
Detection Rate 0.002519 0.2846 0.4458
Detection Prevalence 0.002519 0.5264 0.4710
Balanced Accuracy 0.506410 0.8310 0.9034
Repetimos o mesmo processo para os demais modelos.
coluna = apply(prev_teste_2,MARGIN = 1,FUN = "which.max")
prev_custo_2_teste = ifelse(coluna==1,"baixo",ifelse(coluna==2,"medio","alto"))
prev_custo_2_teste = factor(prev_custo_2_teste)Os levels de prev_custo_2_teste e custo_teste devem ser os mesmos.
levels(prev_custo_2_teste)[1] "alto" "baixo" "medio"
levels(custo_teste)[1] "alto" "baixo" "medio"
(CM_2_teste = confusionMatrix(prev_custo_2_teste,custo_teste))Confusion Matrix and Statistics
Reference
Prediction alto baixo medio
alto 64 0 6
baixo 2 110 7
medio 12 3 193
Overall Statistics
Accuracy : 0.9244
95% CI : (0.8939, 0.9484)
No Information Rate : 0.5189
P-Value [Acc > NIR] : <2e-16
Kappa : 0.8757
Mcnemar's Test P-Value : 0.1328
Statistics by Class:
Class: alto Class: baixo Class: medio
Sensitivity 0.8205 0.9735 0.9369
Specificity 0.9812 0.9683 0.9215
Pos Pred Value 0.9143 0.9244 0.9279
Neg Pred Value 0.9572 0.9892 0.9312
Prevalence 0.1965 0.2846 0.5189
Detection Rate 0.1612 0.2771 0.4861
Detection Prevalence 0.1763 0.2997 0.5239
Balanced Accuracy 0.9009 0.9709 0.9292
coluna = apply(prev_teste_3,MARGIN = 1,FUN = "which.max")
prev_custo_3_teste = ifelse(coluna==1,"baixo",ifelse(coluna==2,"medio","alto"))
prev_custo_3_teste = factor(prev_custo_3_teste)Os levels de prev_custo_23_teste e custo_teste devem ser os mesmos.
levels(prev_custo_3_teste)[1] "alto" "baixo" "medio"
levels(custo_teste)[1] "alto" "baixo" "medio"
(CM_3_teste = confusionMatrix(prev_custo_3_teste,custo_teste))Confusion Matrix and Statistics
Reference
Prediction alto baixo medio
alto 63 0 5
baixo 2 109 7
medio 13 4 194
Overall Statistics
Accuracy : 0.9219
95% CI : (0.891, 0.9463)
No Information Rate : 0.5189
P-Value [Acc > NIR] : < 2e-16
Kappa : 0.8711
Mcnemar's Test P-Value : 0.09478
Statistics by Class:
Class: alto Class: baixo Class: medio
Sensitivity 0.8077 0.9646 0.9417
Specificity 0.9843 0.9683 0.9110
Pos Pred Value 0.9265 0.9237 0.9194
Neg Pred Value 0.9544 0.9857 0.9355
Prevalence 0.1965 0.2846 0.5189
Detection Rate 0.1587 0.2746 0.4887
Detection Prevalence 0.1713 0.2972 0.5315
Balanced Accuracy 0.8960 0.9665 0.9264
Comparação de resultados
Vamos comparar os resultados de todos os modelos treinados considerando os valores de R\(^2\) tanto na base de treino quanto na base de teste.
Precisamos escolher uma ou mais medidas de comparação. Vamos começar pela acurácia.
Acuracia = matrix(
c(CM_1$overall["Accuracy"],CM_2$overall["Accuracy"],CM_3$overall["Accuracy"],CM_1_teste$overall["Accuracy"],CM_2_teste$overall["Accuracy"],CM_3_teste$overall["Accuracy"]),
nrow = 2, byrow = T)barplot(Acuracia,
beside = T,
names.arg = c("Modelo 1","Modelo 2","Modelo 3"),
ylim = c(0,1),
col=c("tomato","blue4"),
args.legend = list(x = "bottomright"),
legend.text = c("treino","teste"),main="Acurácia")Mas podemos usar outra medida, como por exemplo, a sensibilidade da classe alto custo, que indica a taxa de acerto para a classe altocusto, ou seja, a proporção de clientes de alto custo que foram corretamente classificados como alto custo.
S_alto =
matrix(
c(CM_1$byClass["Class: alto","Sensitivity"],CM_2$byClass["Class: alto","Sensitivity"],CM_3$byClass["Class: alto","Sensitivity"],CM_1_teste$byClass["Class: alto","Sensitivity"],CM_2_teste$byClass["Class: alto","Sensitivity"],CM_3_teste$byClass["Class: alto","Sensitivity"]),
nrow = 2, byrow = T)barplot(S_alto,
beside = T,
names.arg = c("Modelo 1","Modelo 2","Modelo 3"),
ylim = c(0,1),
col=c("tomato","blue4"),
args.legend = list(x = "bottomright"),
legend.text = c("treino","teste"),main="Sensibilidade para Alto Custo")Vamos fazer mais um exemplo de comparação, considerando a sensibilidade da classe baixo custo.
S_baixo =
matrix(
c(CM_1$byClass["Class: baixo","Sensitivity"],CM_2$byClass["Class: baixo","Sensitivity"],CM_3$byClass["Class: baixo","Sensitivity"],CM_1_teste$byClass["Class: baixo","Sensitivity"],CM_2_teste$byClass["Class: baixo","Sensitivity"],CM_3_teste$byClass["Class: baixo","Sensitivity"]),
nrow = 2, byrow = T)barplot(S_baixo,
beside = T,
names.arg = c("Modelo 1","Modelo 2","Modelo 3"),
ylim = c(0,1),
col=c("tomato","blue4"),
args.legend = list(x = "bottomright"),
legend.text = c("treino","teste"),main="Sensibilidade para Baixo Custo")