library(caret)
library(tidyverse)
library(neuralnet)Base de Seguros
um exemplo de RNA para regressão
O Problema
Considere a base de dados insurance.csv, composta pelas variáveis: age, sex, bmi, children, smoker, region, married, charges.
Vamos ajustar diferentes modelos de regressão para prever o custo (charge) de um cliente a partir das suas características como idade, gênero, índice de massa corporal, número de filhos, hábito de fumar, região de residência e se é casado ou não.
Pacotes necessários
O primeiro passo será carregar os pacotes necessários.
Leitura da base de dados
Em seguida faremos a leitura da base de dados. Nesse momento é preciso verificar o tipo das variáveis e todas as variáveis categóricas precisam ser transformadas em factor.
base = read_csv("insurance.csv")
glimpse(base)Rows: 1,338
Columns: 9
$ age <dbl> 19, 18, 28, 33, 32, 31, 46, 37, 37, 60, 25, 62, 23, 56, 27, 1…
$ sex <chr> "female", "male", "male", "male", "male", "female", "female",…
$ bmi <dbl> 27.900, 33.770, 33.000, 22.705, 28.880, 25.740, 33.440, 27.74…
$ children <dbl> 0, 1, 3, 0, 0, 0, 1, 3, 2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0…
$ smoker <chr> "yes", "no", "no", "no", "no", "no", "no", "no", "no", "no", …
$ region <chr> "southwest", "southeast", "southeast", "northwest", "northwes…
$ charges <dbl> 16884.924, 1725.552, 4449.462, 21984.471, 3866.855, 3756.622,…
$ married <chr> "yes", NA, "yes", "no", "yes", "yes", "yes", "yes", "yes", NA…
$ country <chr> "usa", "usa", "usa", "usa", "usa", "usa", "usa", "usa", "usa"…
base$sex = factor(base$sex)
base$smoker = factor(base$smoker)
base$region = factor(base$region)
base$married = factor(base$married)
base$country = factor(base$country)
glimpse(base)Rows: 1,338
Columns: 9
$ age <dbl> 19, 18, 28, 33, 32, 31, 46, 37, 37, 60, 25, 62, 23, 56, 27, 1…
$ sex <fct> female, male, male, male, male, female, female, female, male,…
$ bmi <dbl> 27.900, 33.770, 33.000, 22.705, 28.880, 25.740, 33.440, 27.74…
$ children <dbl> 0, 1, 3, 0, 0, 0, 1, 3, 2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0…
$ smoker <fct> yes, no, no, no, no, no, no, no, no, no, no, yes, no, no, yes…
$ region <fct> southwest, southeast, southeast, northwest, northwest, southe…
$ charges <dbl> 16884.924, 1725.552, 4449.462, 21984.471, 3866.855, 3756.622,…
$ married <fct> yes, NA, yes, no, yes, yes, yes, yes, yes, NA, NA, yes, NA, N…
$ country <fct> usa, usa, usa, usa, usa, usa, usa, usa, usa, usa, usa, usa, u…
Divisão da base em treino e teste
Em seguida dividimos a base em treino e teste de forma que 70% ficará no treino e 30% no teste. Para garantir a reprodutibilidade do código devemos fixar a semente antes de realizar a partição.
set.seed(123456789)
N = dim(base)[1]
indices_treino = createDataPartition(1:N,p=0.7)[[1]]
base_treino = base[indices_treino,]
base_teste = base[-indices_treino,]Tratamento das NAs
A partir de agora vamos usar somente a base de treino. A base de teste só volta ao uso quando quisermos medir a qualidade das previsões fora da amostra.
Sobre o tratamento das NAs, temos a alternativa de eliminar as linhas, de imputar valores ou eliminar colunas. Neste exemplo, vamos simplesmente eliminar as linhas que contém algum NA de forma a simplificar esta etapa, mas nem sempre está é a alternativa mais interessante.
sum(is.na(base_treino)) #num total de NAs na base[1] 384
base_treino = na.omit(base_treino)Padronização das variáveis independentes numéricas
As variáveis independentes (aquelas que não são a variável resposta) numéricas devem ser padronizadas e podem ser padronizadas pela forma \[ \tilde{X} = \dfrac{X - \bar{X}}{sd(X)} \] E esta padronização pode ser realizada com a função scale.
age_ = scale(base_treino$age)
attr(age_,"scaled:center")[1] 40.09009
attr(age_,"scaled:scale")[1] 14.32938
bmi_ = scale(base_treino$bmi)
children_ = scale(base_treino$children)Padronização das variáveis dependente numérica
A padronização da variável dependente tem que ser feita de forma que so valores padronizados estejam contidos na imagem da função de ativação escolhida para o problema. no caso de usarmos a função de ativação logistica, vamos padronizar a variável resposta de forma que seus valores fiquem dentro do intervalo \([0,1]\). A operação realizada será: \[ \tilde{Y} = \dfrac{Y - \min\{Y\}}{\max\{Y\}- \min\{Y\}} \]
charges_ = (base_treino$charges - min(base_treino$charges))/(
max(base_treino$charges) - min(base_treino$charges)
)Tratamento nas variáveis categóricas
As variáveis categóricas precisam ser transformadas em indicadoras antes do ajuste de uma rede neural pela função neuralnet. Para isso vamos primeiro criar uma base de dados com as variáveis numéricas padronizadas e as variáveis categóricas como factor. Depois usamos a função modelmatrix e por último eliminamos a coluna intersept.
base_treino_ = tibble(
age = as.numeric(age_),
bmi = as.numeric(bmi_),
children = as.numeric(children_),
charges = as.numeric(charges_),
sex = base_treino$sex,
smoker = base_treino$smoker,
region = base_treino$region,
married = base_treino$married
)
glimpse(base_treino_)Rows: 555
Columns: 8
$ age <dbl> -1.471807263, -0.843727188, -0.564580488, -0.634367163, -0.21…
$ bmi <dbl> -0.4625470253, 0.3568875837, -0.3050870416, -0.8096016832, -0…
$ children <dbl> -0.9223682, 1.4999394, -0.9223682, -0.9223682, 1.4999394, 0.6…
$ charges <dbl> 2.514957e-01, 5.296955e-02, 4.366851e-02, 4.190869e-02, 9.818…
$ sex <fct> female, male, male, female, female, male, female, female, mal…
$ smoker <fct> yes, no, no, no, no, no, yes, no, yes, no, no, yes, no, no, y…
$ region <fct> southwest, southeast, northwest, southeast, northwest, northe…
$ married <fct> yes, yes, yes, yes, yes, yes, yes, no, yes, yes, no, yes, yes…
base_treino_final =
model.matrix( ~ age + sex + bmi +
children + smoker +
region + charges + married,
data = base_treino_)
head(base_treino_final) (Intercept) age sexmale bmi children smokeryes
1 1 -1.4718073 0 -0.4625470 -0.9223682 1
2 1 -0.8437272 1 0.3568876 1.4999394 0
3 1 -0.5645805 1 -0.3050870 -0.9223682 0
4 1 -0.6343672 0 -0.8096017 -0.9223682 0
5 1 -0.2156471 0 -0.4882548 1.4999394 0
6 1 -0.2156471 1 -0.1524473 0.6925035 0
regionnorthwest regionsoutheast regionsouthwest charges marriedyes
1 0 0 1 0.25149567 1
2 0 1 0 0.05296955 1
3 1 0 0 0.04366851 1
4 0 1 0 0.04190869 1
5 1 0 0 0.09818175 1
6 0 0 0 0.08421129 1
base_treino_final = base_treino_final[,-1]
head(base_treino_final) age sexmale bmi children smokeryes regionnorthwest
1 -1.4718073 0 -0.4625470 -0.9223682 1 0
2 -0.8437272 1 0.3568876 1.4999394 0 0
3 -0.5645805 1 -0.3050870 -0.9223682 0 1
4 -0.6343672 0 -0.8096017 -0.9223682 0 0
5 -0.2156471 0 -0.4882548 1.4999394 0 1
6 -0.2156471 1 -0.1524473 0.6925035 0 0
regionsoutheast regionsouthwest charges marriedyes
1 0 1 0.25149567 1
2 1 0 0.05296955 1
3 0 0 0.04366851 1
4 1 0 0.04190869 1
5 0 0 0.09818175 1
6 0 0 0.08421129 1
Ajuste do Modelo de Rede Neural com um único neurônio
A função usada para isso será a neuralnet do pacote neuralnet.
modelo_completo = neuralnet(
formula = charges ~ age + sexmale + bmi + children + smokeryes + regionnorthwest + regionsoutheast + regionsouthwest + marriedyes,
data = base_treino_final,
hidden = 0,
linear.output = FALSE)Podemos visualizar o modelo criado com a função plot:
plot(modelo_completo)