Este é um trabalho baseado no tutorial do DataCamp

Machine Learning, é uma sub área da Ciência da Computação, e suas aplicações são cada vez mais comuns no mundo contemporâneo. Uma das principais novidades em técnicas de machine learning, é o deep learning, um conjunto de técnicas e algoritmos, inspirados pelo funcionamento e estrutura do cérebro. Deep Learning, é especialmente eficiente em algumas áreas como robótica e reconhecimento de imagens.

Neste tutorial, daremos nossos primeiros passos no deep learning, fazendo uso da linguagem R e do pacote Keras.

Keras

Na área da ciência de dados em geral, as duas linguagens de programação mais utilizadas são o R e o Python, portanto, é comum que profissionais desenvolvam ferramentas para as suas linguagens de preferência. Neste caso, keras é um pacote de algoritmos de deep learning, originalmente desenvolvido na linguagem Python, entretanto, o pacote R keras, nos fornece uma tecnologia facil de instalar e usar, que nos permite fazer uso das implementações no pacote original, através de uma interface R familiar.

# Instalando o pacote
#devtools::install_github("rstudio/keras")

# Instalando o tensorflow
#install.packages('tensorflow')
library(tensorflow)
#install_tensorflow()


library(keras)
#install_keras()

Instalamos também o pacote tensorflow, que faz interface ao serviço homônimo, de computação em grafos.

Os Dados

Neste tutorial, faremos uso do famoso dataset iris, que inclui dados de flores

iris <- read.csv(url("http://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"), header = FALSE)

View(head(iris))

Observando o dataset que importamos, percebemos que as colunas do mesmo não são nomeadas. Apenas para propósitos de exploração, vamos incluir estes nomes. Após isto, veremos em uma visualização rápida, como o comprimento e a largura das pétalas se correlacionam.

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

View(head(iris))

library(ggplot2)

ggplot(iris, aes(x = Petal.Length, y = Petal.Width, colour = Species)) +
  geom_point() +
  labs(x = "Comprimento da pétala", y = "Largura da pétala", colour = "Espécie")

O gráfico indica uma correlação positiva entre as duas variáveis analizadas, entretanto, este tipo de relação deve ser testada fazendo uso da função cor(), neste caso temos uma correlação de 0.9627571, o que confirma a nossa observação.

Podemos generalizar esta análise de correlação, e explorar as relações entre todas as variáveis dos nossos dados, fazendo uso do corrplot.

#install.packages('corrplot')
library(corrplot)
## corrplot 0.84 loaded
cors <- cor(iris[,1:4])

corrplot(cors, method = "circle")

Pré Processamento dos Dados

Antes de gerar algum modelo, precisamos ter certeza de que os dados estão limpos, completos e até as vezes normalizados e balanceados.

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  
##                      
##                      
## 
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 ...

A partir do sumário acima, percebemos que os dados não precisam ser normalizados, pois os valores estão em um intervalo próximo e aceitável, entre 0.1 e 7.9. Entretanto, podemos estudar o efeito da normalização no nosso modelo, para isso, usamos a função normalize() do keras. Agora é um bom momento para saber de uma propriedade importante do pacote. Boa parte de suas funções (como por exemplo a função normalize) funciona com dados no formato de matriz, portanto, precisamos converter o nosso data frame para este formato, para então realizar a normalização.

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

iris <- as.matrix(iris, dimnames = FALSE)

Agora antes de construirmos nossos modelos, precisamos dividir os dados em conjuntos de teste e treino, desta forma podemos testar o poder preditivo dos nossos modelos em dados não vistos antes.

set.seed(42)

ind <- sample(2, nrow(iris), replace=TRUE, prob=c(0.67, 0.33))

iris.training <- iris[ind==1, 1:4]
iris.test <- iris[ind==2, 1:4]

# Separamos agora as variáveis objeto da análise
iris.trainingtarget <- iris[ind==1, 5]
iris.testtarget <- iris[ind==2, 5]

Por fim, é uma boa prática, transformar a variável objeto da análise, em uma matriz cujas colunas são os valores possíveis das classes, e os elementos são booleanos que indicam se aquela instância em particular faz parte daquela classe. Esta sintaxe é chamada de One-Hot Encoding e é muito útil para evitar que o modelo interprete diferentes valores para diferentes classes.

O pacote keras possui uma função que facilita este trabalho, to_categorical(). Usamos a função na nossa variável objeto.

iris.trainLabels <- to_categorical(iris.trainingtarget)[,2:4]

iris.testLabels <- to_categorical(iris.testtarget)[,2:4]

Construindo o Modelo

Antes de iniciar qualquer análise, precisamos saber de forma muito clara, qual o nosso objetivo. Neste caso, que pergunta buscamos a resposta nos dados?

Podemos prever a espécie de uma determinada flor?

Após pré processarmos os dados, sabemos que temos features numéricas e uma variável de saída no formato de One-Hot Encoding. Um tipo de rede neural que performa muito bem nestas condições é a multi-layer perceptron.

Iniciamos usando a função keras_model_sequential() para gerar o nosso modelo sequencial. Como o propósito deste tutorial é apenas de dar nossos primeiros passos com deep learning em keras, vamos usar a função de ativação mais comum relu e usaremos a função softmax para a camada de saída, pois desejamos que os valores de saída estejam entre 0 e 1, para poderem ser interpretados como probabilidades.

# Antes de mais nada criamos o modelo sequencial
model <- keras_model_sequential()

# Então adicionamos camadas no nosso modelo
# A camada de entrada, possui um input shape de 4 pois são 4 colunas na entrada
# A camada de saída por sua vez possui 3 neurons pois a mesma possui 3 classes possíveis
model %>% 
    layer_dense(units = 8, activation = 'relu', input_shape = c(4)) %>% 
    layer_dense(units = 3, activation = 'softmax')

Agora, compilamos o nosso modelo.

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

Os três argumentos passados para a função compile, são essenciais para o funcionamento do modelo, metrics especifica a métrica que queremos maximizar, optimizer indica o algoritmo de otimização e loss representa a função que calcula o erro nas predições.

Os valores atribuídos à loss, são dependentes do domínio do problema, entretanto no nosso caso (problema de classificação com mais de duas classes) fazemos uso de categorical_crossentropy.

Agora treinamos o modelo usando nossos dados.

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

plot(history)

Usamos 200 epochs (iterações) e 5 de batch_size (número de valores que são propagados pela rede). E na visualização acima, podemos observar os valores de acurácia e perda, para os dados de treino e teste.

Predizendo Novos Valores

Por fim, vamos fazer uso do nosso modelo, para prever as classes dos elementos separados para teste.

classes <- model %>% predict_classes(iris.test, batch_size = 128)

table(iris.testtarget, classes)
##                classes
## iris.testtarget  0  1  2
##               1 23  0  0
##               2  0 15  0
##               3  0  5 15

Os comandos acima usam o modelo para realizar as predições dos dados de teste e mostram a tabela com os resultados, entretanto, podemos usar o comando evaluate() para sumarizar estes valores.

score <- model %>% evaluate(iris.test, iris.testLabels, batch_size = 128)

print(score)
## $loss
## [1] 0.2290177
## 
## $acc
## [1] 0.9137931

Tunando o modelo

Se quisermos melhorar a avaliação do nosso modelo, precisaremos reconfigurá-lo de formas diferentes. Nossas opções são:

Adicionando camadas

model <- keras_model_sequential() 

# Quando especificamos o modelo, adicionamos as camadas
model %>% 
    layer_dense(units = 8, activation = 'relu', input_shape = c(4)) %>% 
    layer_dense(units = 5, activation = 'relu') %>% 
    layer_dense(units = 3, activation = 'softmax')

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

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

score <- model %>% evaluate(iris.test, iris.testLabels, batch_size = 128)

print(score)
## $loss
## [1] 0.1507161
## 
## $acc
## [1] 0.9137931

Adicionando unidades às camadas

model <- keras_model_sequential() 

# Aqui também especificamos o número adicional de unidades
model %>% 
    layer_dense(units = 28, activation = 'relu', input_shape = c(4)) %>% 
    layer_dense(units = 3, activation = 'softmax')

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

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

score <- model %>% evaluate(iris.test, iris.testLabels, batch_size = 128)

print(score)
## $loss
## [1] 0.1697371
## 
## $acc
## [1] 0.9137931
Modificando os parâmetros de otimização

Além das opções supracitadas, podemos tentar alterar os parâmetros do algoritmo de otimização que passamos para a função compile(). podemos usar por exemplo, o algoritmo de gradiente descendente através da função optimizer_sgd(), este algoritmo recebe como parâmetro uma taxa de aprendizado que pode ser modificada para fins específicos.

Salvando, carregando e exportando modelos

Dependendo dos dados que usamos de entrada, as computações realizadas para gerar os modelos podem ser custosas. Por este motivo, é desejável a habilidade de salvar o trabalho realizado e retomá-lo futuramente. Keras oferece algumas funções que facilitam este trabalho.