keras: Deep Learning em R - Construindo uma MLP (Multi-Layer Perceptron)

Este tutorial é um resumo do tutorial Keras: Deep Learning in R.

Esse pequeno guia representa uma atividade para a disciplina Analise de Dados 2 da Universidade Federal de Campina Grande. De forma nenhuma representa uma cópia ou algo do gênero, apenas um facilitador para pessoas que não sabem inglês ou que tiveram algum problema em seguir os passos do tutorial original.

Instalando a package Keras

Na primeira parte deste tutorial deveremos instalar a package keras, para isto precisaremos utilizar estes comandos:

# devtools::install_github("rstudio/keras")
# install_keras()

Talvez você necessite instalar o package reticulate antes. Você pode fazer isto utilizando este comando:

# install.package("reticulate")

Após a instalação do keras, é necessário que você instale o tensorflow. Para fazer isso basta executar os comandos:

# install.packages("tensorflow")
# library(tensorflow)
# install_tensorflow()

Uma observação: Para poder utilizar o tensorflow e o keras será necessário ter o anaconda (versão 64 bits) instalado previamente.

Carregando dados:

Após todos esse processo de instalações, você estará pronto para carregar seus dados. A principio existem três tipos de opções em relação ao seus dados: Você pode utilizar algum dataset que vem junto do keras packages, você pode carregar seu próprio dataset de um CSV, por exemplo, ou você pode criar dados fictícios

Utilizando datasets do Keras:

Para fazer isto basta simplesmente utilizar:

# Lendo dados MNIST 
mnist <- dataset_mnist()

# Lendo dados CIFAR10
cifar10 <- dataset_cifar10()

# Lendo dados IMDB
imdb <- dataset_imdb()

Dados Ficticios

Alternativamente você poderá criar dados fictícios. Isto é possível através da função matrix()

# Criar dados ficticios
data <- matrix(rexp(1000*784), nrow = 1000, ncol = 784)

# Criar valores de alvo ficitios para os seus dados ficticios
labels <- matrix(round(runif(1000*10, min = 0, max = 9)), nrow = 1000, ncol = 10)

Lendo dados de arquivos

Mais uma opção é ler dados de arquivos. Para este tutorial vamos focar em carregar dados de arquivos CSV. Caso você não saiba muito bem como importar arquivos do R, aqui está um link disponível no tutorial original: R Data Import Tutorial.

# Lê e coloca em iris os dados
set.seed(500)
iris <- read.csv(url("http://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"), header = FALSE) 

# Retornar a primeira parte de "iris"
head(iris)
##    V1  V2  V3  V4          V5
## 1 5.1 3.5 1.4 0.2 Iris-setosa
## 2 4.9 3.0 1.4 0.2 Iris-setosa
## 3 4.7 3.2 1.3 0.2 Iris-setosa
## 4 4.6 3.1 1.5 0.2 Iris-setosa
## 5 5.0 3.6 1.4 0.2 Iris-setosa
## 6 5.4 3.9 1.7 0.4 Iris-setosa
# Inspecione a estrutura
str(iris)
## 'data.frame':    150 obs. of  5 variables:
##  $ V1: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
##  $ V2: num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
##  $ V3: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ V4: num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
##  $ V5: Factor w/ 3 levels "Iris-setosa",..: 1 1 1 1 1 1 1 1 1 1 ...
# Obtendo as dimensões
dim(iris)
## [1] 150   5

O resultado das três funções apresentadas, head, str e dim, não apresentam nada de especial no momento. Contudo, ao olhar a saída da função str você poderá ver que as strings da coluna species são lidas como fatores. Isso não é um problema, contudo é algo que será bom saber para os próximos passos apresentados, onde os dados serão explorados e pre-processados.

Exploração de dados

Neste tutorial será utilizado o conjunto de dados iris importado previamente no tópico anterior.

O dataset iris em questão, devido ao import, terá nome de colunas como: “V1, V2, V3, V4 e V5”. Iremos alterar esses valores para algo que faça mais sentido:

set.seed(500)
names(iris) <- c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width", "Species")

A seguir, vamos usar a função plot para verificar a correlação entre as variáveis petal .length e petal.width

plot(iris$Petal.Length, 
     iris$Petal.Width, 
     pch=21, bg=c("red","green3","blue")[unclass(iris$Species)], 
     xlab="Petal Length", 
     ylab="Petal Width")

Como podemos ver, aparentemente o gráfico acima indica uma certa correlação entre petal.length e petal.width. Para checar isto em termos mais concretos podemos utilizar a função cor(). A função cor() indica o valor da correlação entre petal.length e petal.width de forma numérica.

# Valor da correlação entre petal.length e petal.width de forma númerica.
cor(iris$Petal.Length, iris$Petal.Width)
## [1] 0.9627571

Adicionalmente, você poderá usar a função corrplot() em combinação com a função cor() para gerar as correlações entre os atributos do nosso conjunto de dados:

# Guarda o valor da correlação em M
M <- cor(iris[,1:4])

# Gera o gráfico de correlação a partir de M
corrplot(M, method="circle")

Obs: Para utilizar a função corrplot, você precisará da biblioteca “corrplot” instalada.

Processamento de dados

Nesta parte do tutorial verificaremos se os dados estão limpos, normalizados e divididos entre conjuntos de treinos e conjuntos de test.

Para verificar se os dados estão limpos, podemos utilizar as funções summary e str()

# Nos dá um resumo dos dados de iris
summary(iris)
##   Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
##  Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
##  1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300  
##  Median :5.800   Median :3.000   Median :4.350   Median :1.300  
##  Mean   :5.843   Mean   :3.054   Mean   :3.759   Mean   :1.199  
##  3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800  
##  Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500  
##             Species  
##  Iris-setosa    :50  
##  Iris-versicolor:50  
##  Iris-virginica :50  
##                      
##                      
## 
# Inspeciona a estrutura do dataset iris
str(iris)
## 'data.frame':    150 obs. of  5 variables:
##  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
##  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
##  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
##  $ Species     : Factor w/ 3 levels "Iris-setosa",..: 1 1 1 1 1 1 1 1 1 1 ...

Aparentemente, pode-se ver que os dados estão limpos.

Recapitulação: Dados limpos são dados que não possuem dados aparentemente incorretos ou faltando(valores em linhas faltando), por exemplo.

Normalizando os dados

Está parte do tutorial é um extra, pode ser pulado caso o leitor quiser.

Analisando os dados o que se nota é que não é preciso normalizar os dados. Isso acontece pelo intervalo de valores(ver função summary) que vão de 0.1 a 7.9, que é, em tese, aceitável. Apesar disso, estudaremos um pouco de normalização de dados. Para isso utilizaremos dois métodos de normalização. O primeiro será criando uma função própria e a outra é usando o keras.

Criando uma função própria:

# Criando uma função própria de normalização
norm <- function(x) {
  num <- x - min(x)
  denom <- max(x) - min(x)
  return (num/denom)
}

# Normalizando os dados
iris_norm <- as.data.frame(lapply(iris[1:4], norm))

# Retornando alguns dados do dataset iris
head(iris)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width     Species
## 1          5.1         3.5          1.4         0.2 Iris-setosa
## 2          4.9         3.0          1.4         0.2 Iris-setosa
## 3          4.7         3.2          1.3         0.2 Iris-setosa
## 4          4.6         3.1          1.5         0.2 Iris-setosa
## 5          5.0         3.6          1.4         0.2 Iris-setosa
## 6          5.4         3.9          1.7         0.4 Iris-setosa

Utilizando a função de normalização do keras:

Observação: A função de normalização do keras necessita que o dado em questão esteja na forma de matrix. Por isso a função as.matrix() é utilizada.

iris_keras_norm <- iris
iris_keras_norm[,5] <- as.numeric(iris[,5]) -1

# Transforma iris em uma matrix
iris_keras_norm <- as.matrix(iris_keras_norm)

dimnames(iris_keras_norm) <- NULL

# Normaliza 
iris_keras_norm <- normalize(iris_keras_norm[,1:4])

# Return the summary of `iris`
summary(iris_keras_norm)
##        V1               V2               V3               V4         
##  Min.   :0.6539   Min.   :0.2384   Min.   :0.1678   Min.   :0.01473  
##  1st Qu.:0.7153   1st Qu.:0.3267   1st Qu.:0.2509   1st Qu.:0.04873  
##  Median :0.7549   Median :0.3544   Median :0.5364   Median :0.16415  
##  Mean   :0.7516   Mean   :0.4048   Mean   :0.4550   Mean   :0.14096  
##  3rd Qu.:0.7884   3rd Qu.:0.5252   3rd Qu.:0.5800   3rd Qu.:0.19753  
##  Max.   :0.8609   Max.   :0.6071   Max.   :0.6370   Max.   :0.28042

Obs: Normalização aqui significa ajustar os valores que poderiam ter sido medidos em diferentes escalas para uma escala comum.

Treinando e testando conjunto de dados

Foi verificado previamente que a data era de boa qualidade, assim como não havia a necessidade de normalizar. Neste parte então nós separaremos os dados em dados de treinos e dados de teste.

Antes de tudo você deverá utilizar a função set.seed. Isso serve para garantir que a mesma sequencia de números randômicos será gerado pela geração de números randômicos.

A ideia aqui é a seguinte: Cria se um vetor de 150 elementos(mesmo tamanho de linhas ) com probabilidade 67% de um valor desse vetor ser 1 e 33% de ser 2. Isso servirá para separa nossos dados em dados de treino e dados de teste. Onde se agrupa os valores comparando os valores dos indexes de iris com os indexes do vetor criado.

Por exemplo, se ind[1] for igual a 1, isso significa que a primeira linha de iris representará um dado de treino. Caso contrário, será um dado de teste.

Após isso, será criado as variáveis iris.trainingtarget e iris.testtarget que servirão para separar as especies(setosa, virginica e versicolor) mencionadas previamente

set.seed(500)

# Determinando o tamanho da amostra
ind <- sample(2, nrow(iris), replace=TRUE, prob=c(0.67, 0.33))

# Separando os dados "iris" 
iris.training <- iris[ind==1, 1:4]
iris.test <- iris[ind==2, 1:4]

# Separando as classes de atributos 
iris.trainingtarget <- iris[ind==1, 5]
iris.testtarget <- iris[ind==2, 5]

One-Hot Encoding

Aqui faremos o One-hot encoding. Porém, o que é isso? Basicamente temos a variável iris.trainingtarget e iris.testtarget que possuem valores categóricos informando os valores das “species”. A partir disso utilizaremos a função to_categorical presente no package keras para transformar essas variáveis em um dataset com quatro colunas, onde cada linha representará a qual classe representa aquela informação.

Ex:

Linha 1: 0 1 0 0. Logo, o dado 1 representa um dado do tipo de classe B Linha 2: 0 0 1 0. Logo, o dado 2 representa um dado do tipo de classe C

E assim sucessivamente. Note que nunca uma linha conterá mais de um valor 1.

# One hot encode valores de treino
iris.trainLabels <- to_categorical(iris.trainingtarget)

# One hot encode valores de teste 
iris.testLabels <- to_categorical(iris.testtarget)

# Printa o valor de iris.testLabels
print(iris.testLabels)
##       [,1] [,2] [,3] [,4]
##  [1,]    0    1    0    0
##  [2,]    0    1    0    0
##  [3,]    0    1    0    0
##  [4,]    0    1    0    0
##  [5,]    0    1    0    0
##  [6,]    0    1    0    0
##  [7,]    0    1    0    0
##  [8,]    0    1    0    0
##  [9,]    0    1    0    0
## [10,]    0    1    0    0
## [11,]    0    1    0    0
## [12,]    0    1    0    0
## [13,]    0    1    0    0
## [14,]    0    1    0    0
## [15,]    0    1    0    0
## [16,]    0    1    0    0
## [17,]    0    1    0    0
## [18,]    0    1    0    0
## [19,]    0    1    0    0
## [20,]    0    0    1    0
## [21,]    0    0    1    0
## [22,]    0    0    1    0
## [23,]    0    0    1    0
## [24,]    0    0    1    0
## [25,]    0    0    1    0
## [26,]    0    0    1    0
## [27,]    0    0    1    0
## [28,]    0    0    1    0
## [29,]    0    0    1    0
## [30,]    0    0    1    0
## [31,]    0    0    1    0
## [32,]    0    0    1    0
## [33,]    0    0    1    0
## [34,]    0    0    1    0
## [35,]    0    0    0    1
## [36,]    0    0    0    1
## [37,]    0    0    0    1
## [38,]    0    0    0    1
## [39,]    0    0    0    1
## [40,]    0    0    0    1
## [41,]    0    0    0    1
## [42,]    0    0    0    1
## [43,]    0    0    0    1
## [44,]    0    0    0    1
## [45,]    0    0    0    1
## [46,]    0    0    0    1
## [47,]    0    0    0    1
## [48,]    0    0    0    1
## [49,]    0    0    0    1
## [50,]    0    0    0    1
## [51,]    0    0    0    1
## [52,]    0    0    0    1
## [53,]    0    0    0    1
## [54,]    0    0    0    1
## [55,]    0    0    0    1
## [56,]    0    0    0    1

Construindo o modelo

Primeiramente devemos inicializar um sequenciador com a ajuda da função keras_model_sequential() :

# Inicializa o modelo sequencial 
model <- keras_model_sequential() 

model %>% 
    layer_dense(units = 8, activation = 'relu', input_shape = c(4)) %>% 
    layer_dense(units = 4, activation = 'softmax')

Obs: Aqui uma pequena observação, o número 4 do input_shape é inserido pois temos 4 colunas na nossa variável iris.training. O 3 presente na função acima é devido as nossas três “species” de iris (versicolor, virginica or setosa).

Abaixo estão algumas funções interessantes que podem ajuda-lo a entender um pouco mais do model

# Printa um sumário do modelo
summary(model)
## ___________________________________________________________________________
## Layer (type)                     Output Shape                  Param #     
## ===========================================================================
## dense_1 (Dense)                  (None, 8)                     40          
## ___________________________________________________________________________
## dense_2 (Dense)                  (None, 4)                     36          
## ===========================================================================
## Total params: 76
## Trainable params: 76
## Non-trainable params: 0
## ___________________________________________________________________________
# Retorna a configuração do modelo
get_config(model)
## [{'class_name': 'Dense', 'config': {'name': 'dense_1', 'trainable': True, 'batch_input_shape': (None, 4), 'dtype': 'float32', 'units': 8, 'activation': 'relu', 'use_bias': True, 'kernel_initializer': {'class_name': 'VarianceScaling', 'config': {'scale': 1.0, 'mode': 'fan_avg', 'distribution': 'uniform', 'seed': None}}, 'bias_initializer': {'class_name': 'Zeros', 'config': {}}, 'kernel_regularizer': None, 'bias_regularizer': None, 'activity_regularizer': None, 'kernel_constraint': None, 'bias_constraint': None}}, {'class_name': 'Dense', 'config': {'name': 'dense_2', 'trainable': True, 'units': 4, 'activation': 'softmax', 'use_bias': True, 'kernel_initializer': {'class_name': 'VarianceScaling', 'config': {'scale': 1.0, 'mode': 'fan_avg', 'distribution': 'uniform', 'seed': None}}, 'bias_initializer': {'class_name': 'Zeros', 'config': {}}, 'kernel_regularizer': None, 'bias_regularizer': None, 'activity_regularizer': None, 'kernel_constraint': None, 'bias_constraint': None}}]
# Retorna a configuração da camada
get_layer(model, index = 1)
## <keras.layers.core.Dense>
# Lista as camadas do modelo
model$layers
## [[1]]
## <keras.layers.core.Dense>
## 
## [[2]]
## <keras.layers.core.Dense>
# Lista the input tensors
model$inputs
## [[1]]
## Tensor("dense_1_input:0", shape=(?, 4), dtype=float32)
# Lista the output tensors
model$outputs
## [[1]]
## Tensor("dense_2/Softmax:0", shape=(?, 4), dtype=float32)

Compilando e adequando o modelo

Após a configuração inicial do modelo feito na seção anterior, agora é hora de compilar e adequar o modelo. Para isto utilizaremos o otimizador adam e a função de perda categorical_crossentropy. A precisão nos é disponibilizada através do parâmetro ‘accuracy’(precisão em inglês)

# Compilando o model
model %>% compile(
     loss = 'categorical_crossentropy',
     optimizer = 'adam',
     metrics = 'accuracy'
 )

Após isso será ajustado o modelo para os dados. O número abaixo representa o número de iterações em todas as amostras dos modelos de treino e nos labels de treino em um lote de 5 exemplo. O tamanho do lote é importante por causa da eficiência, no caso, evita que um número grande de entradas seja carregada na memoria de uma vez só.

# Ajustando o modelo
model %>% fit(
     as.matrix(iris.training), 
     iris.trainLabels, 
     epochs = 200, 
     batch_size = 5, 
     validation_split = 0.2
 )

Visualizando a história do modelo de treino

history <- model %>% fit(
     as.matrix(iris.training), 
     iris.trainLabels, 
     epochs = 200,
     batch_size = 5, 
     validation_split = 0.2
 )

# Plota a historia
plot(history)

A função acima serve para visualizar a historia do ajustamento do modelo de treino. As variáveis loss e acc que aparecem nos gráficos acima representam a perda e a precisão do modelo de treino. Já as variáveis val_loss e val_acc são as mesmas métricas supracitas, porém, para os dados de teste

Contudo, podemos melhorar essa visualizações através dos seguintes comandos:

# Plota a perda do modelo de treino
plot(history$metrics$loss, main="Perda do Modelo", xlab = "iterações", ylab="perda", col="blue", type="l", ylim=range( c(history$metrics$loss, history$metrics$val_loss) ))

# Plota a perda do modelo dos dados de teste
lines(history$metrics$val_loss, col="green")

# Adiciona uma legenda
legend("topright", c("train","test"), col=c("blue", "green"), lty=c(1,1))

# Plota a precisão do modelo de treino

plot(history$metrics$acc, main="Precisão do modelo", xlab = "iterações", ylab="precisão", col="blue", type="l", ylim=range( c(history$metrics$acc, history$metrics$val_acc) ) )

lines(history$metrics$val_acc, type='l', col='red')

# Adiciona uma legenda
legend("bottomright", c("train","test"), col=c("blue", "red"), lty=c(1,1))

Aqui coloca-se algumas observações, tais quais:

1 - Se a precisão continua aumentando nas ultimas iterações, isso significa o que? Significa que o modelo não superou o conjunto de dados de treino.

2 - Outro ponto, se a precisão do modelo de treino continuar melhorando enquanto a precisão da sua validação piora, o modelo simplesmente estará memorizando os dados ao invés de aprender sobre ele(overfitting).

Prevendo valores

Depois de todos os passos feitos anteriormente, finalmente poderemos prever valores para nossas classes de iris. Faremos isso através dos seguintes comandos:

# Preve as classes para os dados de teste
classes <- model %>% predict_classes(as.matrix(iris.test), batch_size = 128)


table(iris.testtarget, classes)
##                  classes
## iris.testtarget    1  2  3
##   Iris-setosa     19  0  0
##   Iris-versicolor  0 15  0
##   Iris-virginica   0  2 20

Avaliando o modelo

Agora que prevemos o modelo, como podemos avaliar? Para isso poderemos utilizar a função evaluate:

# Evaluate on test data and labels
score <- model %>% evaluate(as.matrix(iris.test), iris.testLabels, batch_size = 128)

# Print the score
print(score)
## $loss
## [1] 0.1122993
## 
## $acc
## [1] 0.9642857

Pode-se notar que tivemos uma boa acurácia: ~=93%

Melhorando o modelo

Nesta parte falaremos um pouco de como melhorar o modelo

Algumas formas bastante claras é tentar alterar os valores de epoch e do batch size. Contudo, apresentaremos algumas outras formas abaixo:

Adicionando Layers

A primeira forma é adicionando uma nova camada(layer)

add_layer_model <- keras_model_sequential() 

add_layer_model %>% 
    layer_dense(units = 8, activation = 'relu', input_shape = c(4)) %>% 
    layer_dense(units = 5, activation = 'relu') %>% 
    layer_dense(units = 4, activation = 'softmax')

add_layer_model %>% compile(
     loss = 'categorical_crossentropy',
     optimizer = 'adam',
     metrics = 'accuracy'
 )


add_layer_model %>% fit(
     as.matrix(iris.training), iris.trainLabels, 
     epochs = 200, batch_size = 5, 
     validation_split = 0.2
 )

score_add_layer <- add_layer_model %>% evaluate(as.matrix(iris.test), iris.testLabels, batch_size = 128)

print(score_add_layer)
## $loss
## [1] 0.1504154
## 
## $acc
## [1] 0.9285714

Como podemos ver, para o caso em questão, tivemos um aumento de acurácia

Nós escondidos

Agora iremos mais uma vez usar a mesma estrutura inicial mas desa vez iremos adicionar mais nós a camada escondida.(Ver o layer_dense abaixo)

Porém, aqui temos uma observação: Geralmente fazer isso não é uma boa otimização se você não tem muitos dados. Isso se deve por causa do overfitting(citando anteriormente/deverá ser pior). Por está questão é preferível usar menos nós quando temos poucos dados(nosso caso).

nos_model <- keras_model_sequential() 

nos_model %>% 
    layer_dense(units = 28, activation = 'relu', input_shape = c(4)) %>% 
    layer_dense(units = 4, activation = 'softmax')

nos_model %>% compile(
     loss = 'categorical_crossentropy',
     optimizer = 'adam',
     metrics = 'accuracy'
 )

nos_model_history <- nos_model %>%  fit(
     as.matrix(iris.training), iris.trainLabels, 
     epochs = 200, batch_size = 5, 
     validation_split = 0.2
 )

# Plota a perda do modelo de treino
plot(nos_model_history$metrics$loss, main="Perda do Modelo", xlab = "iterações", ylab="perda", col="blue", type="l", ylim=range( c(nos_model_history$metrics$loss, nos_model_history$metrics$val_loss) ))

# Plota a perda do modelo dos dados de teste
lines(nos_model_history$metrics$val_loss, col="green")

# Adiciona uma legenda
legend("topright", c("train","test"), col=c("blue", "green"), lty=c(1,1))

# Plota a precisão do modelo de treino
plot(nos_model_history$metrics$acc, main="Precisão do modelo", xlab = "iterações", ylab="precisão", col="blue", type="l", ylim=range( c(nos_model_history$metrics$acc, nos_model_history$metrics$val_acc) ) )

lines(nos_model_history$metrics$val_acc, type='l', col='red')

# Adiciona uma legenda
legend("bottomright", c("train","test"), col=c("blue", "red"), lty=c(1,1))

score_nos <- nos_model %>% evaluate(as.matrix(iris.test), iris.testLabels, batch_size = 128)


print(score_nos)
## $loss
## [1] 0.1067775
## 
## $acc
## [1] 0.9642857

Optimização de parâmetros

Além dos métodos citados acima, temos ainda mais algumas alternativas para sugerir. A primeira é ajustar alguns parâmetros passados para o compilador. A primeira coisa que faremos é adicionar um novo algoritmo(utilizaremos o Stochastic Gradient Descent (SGD)). Além disso, diminuiremos a taxa de aprendizado. Inclusive, diminuir a taxa de aprendizado é uma pratica bastante comum em experimentos.

optimize_model <- keras_model_sequential() 

optimize_model %>% 
    layer_dense(units = 8, activation = 'relu', input_shape = c(4)) %>% 
    layer_dense(units = 4, activation = 'softmax')


# Definindo o optmizador e diminuindo a taxa de aprendizado
sgd <- optimizer_sgd(lr = 0.01)

# Usando o optimizador para compilar o modelo
optimize_model %>% compile(
     loss = 'categorical_crossentropy',
     optimizer = sgd,
     metrics = 'accuracy'
 )

optimize_model %>% fit(
     as.matrix(iris.training), iris.trainLabels, 
     epochs = 200, batch_size = 5, 
     validation_split = 0.2
 )


optimize_model_history <- optimize_model %>%  fit(
     as.matrix(iris.training), iris.trainLabels, 
     epochs = 200, batch_size = 5, 
     validation_split = 0.2
 )


# Plota a perda do modelo de treino
plot(optimize_model_history$metrics$loss, main="Perda do Modelo", xlab = "iterações", ylab="perda", col="blue", type="l", ylim=range( c(optimize_model_history$metrics$loss, optimize_model_history$metrics$val_loss) ))

# Plota a perda do modelo dos dados de teste
lines(optimize_model_history$metrics$val_loss, col="green")

# Adiciona uma legenda
legend("topright", c("train","test"), col=c("blue", "green"), lty=c(1,1))

# Plota a precisão do modelo de treino
plot(optimize_model_history$metrics$acc, main="Precisão do modelo", xlab = "iterações", ylab="precisão", col="blue", type="l", ylim=range( c(optimize_model_history$metrics$acc, optimize_model_history$metrics$val_acc) ) )

lines(optimize_model_history$metrics$val_acc, type='l', col='red')

# Adiciona uma legenda
legend("bottomright", c("train","test"), col=c("blue", "red"), lty=c(1,1))

# Avaliando o modelo
score_opt <- optimize_model %>% evaluate(as.matrix(iris.test), iris.testLabels, batch_size = 128)


print(score_opt)
## $loss
## [1] 0.08220977
## 
## $acc
## [1] 0.9642857

Notamos que aqui encontrado o maior score das três tecnicas com cerca de 98 de score.

Salvando, exportando e carregando seus modelos

Caso você chegou até aqui acredito que notou uma coisa: Algumas funções demoram muito para executarem. Por isso, neste último tópico do tutorial, apresentarei algumas funções para facilitar trabalhos futuros:

# Salvar modelo no workspace 
# -> save_model_hdf5(model, "my_model.h5")
# Carregando modelo do workspace 
# - > model <- load_model_hdf5("my_model.h5")

# Exportando o modelo para um json
# json_string <- model_to_json(model)
# Carregando o modelo de um json
# model <- model_from_json(json_string)

# Exportando o modelo para um YAML
# yaml_string <- model_to_yaml(model)
# Carregando o modelo de um YAML
# model <- model_from_yaml(yaml_string)