Este tutorial é inspirado neste tutorial do Data Camp em inglês.
Deep Learning é uma sub área de Aprendizado de máquina dentro do campo de Ciência da Computação. O aprendizado de máquina explora o estudo e construção de algoritmos que podem aprender de seus erros e fazer previsões sobre dados. Tais algoritmos são inspirados em como o cérebro funciona e é estruturado e geralmente são chamados de Redes Neurais Artificiais. Atualmente é uma das áreas mais em alta, e é utilizada para resolver problemas na área de robótica, reconhecimento de imagem e Inteligência Artificial.
A rede neural mais simples, de uma camada, é chamada de Perceptron e é inspirado nos neurônios. É constituídos de nós de entrada, os pesos de cada entrada, função de ativação e os nós de saída. Funciona basicamente da seguinte maneira: é feita uma soma ponderada dos dados de entrada (que sempre são numéricos) com seus pesos, o resultado dessa soma passa pela função de ativação que vai gerar o resultado na saída.
Observe que o perceptron só trabalha com dados numéricos, e você deve converter qualquer dado nominal em um formato numérico.
Uma rede de perceptrons é uma MLP e é o que vamos implementar em R neste tutorial. Multi-layer perceptrons é uma rede de perceptrons organizadas em camadas. As saídas de uma camada serve de entrada para a camada seguinte. Geralmente podemos dividir em camada de entrada (input layer), camada “escondida” (hidden layer, que é onde acontece a “mágica”) e a camada de saída (output layer). Frequentemente as MLP possuem os perceptrons completamente conectados, o que significa que todos estão ligados com todos.
Outra observaçao a se fazer, é que com uma Rede Neural de uma camada só, o perceptron, só é possível reprentar bem problemas lineares, e essa limitação é inexistente numa MLP que pode representar problemas complexos.
O pacote do Keras em R é uma interface para o pacote Keras de Python. É uma api para redes neurais que utiliza TensorFlow, Microsoft Cognitive Toolkit (CNTK) or Theano. Basicamente o keras facilita a criação de uma rede neural, oferencendo suporte para que você se preocupe apenas com a construção do modelo.
Vamos começar a prática.
Execute os comandos abaixo para instalar o keras.
# Se não tiver o devtools no seu RStudio, é necessário instalá-lo.
install.packages("devtools")
# Instalar Keras
devtools::install_github("rstudio/keras")
# Carregar o Keras
library(keras)
# Instalar o TensorFlow e todo ambiente necessário para utilizar o Keras.
install_keras()
Depois de ter instaldo o keras com sucesso, o próximo passo é carregar os dados. Existem 3 opções principais.
1. O Keras já vem com 3 datasets imbutidos. É só usar qualquer umas dessas três funções para carregar os dados.
# Carregar MNIST data
mnist <- dataset_mnist()
# Carregar CIFAR10 data
cifar10 <- dataset_cifar10()
# Carregar IMDB data
imdb <- dataset_imdb()
# Criar matriz de dados
data <- matrix(rexp(1000*784), nrow = 1000, ncol = 784)
# Criar valores desejados na saída.
labels <- matrix(round(runif(1000*10, min = 0, max = 9)), nrow = 1000, ncol = 10)
# Carregar arquivos iris
iris <- read.csv(url("http://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"), header = FALSE)
É utilizada a opcão de header = FALSE
pois não precisaremos dos nomes das colunas para modelagem da MLP. A função read.csv
cria um data frame, que é uma estrutura de dados do R`
É sempre bom dar uma olhada nos dados, procurar entender o dominío do problema para que a manipulação do modelo seja mais eficiente. Para tal temos algumas funções do R que possibilitam analisar os dataframes. Já que entender o dominío do data set é importante, aqui vai algumas informações sobre os dados. Toda flor tem pétalas e sépalas, as pétalas geralmente são coloridas e as sépalas são mais parecidas com folhas. Na éspecie de lofr Iris, elas podem ser classificadas em 3 categorias(iris-setosa, iris-versicolor, iris-virginica) de acordo com as caracteristicas da sépala e da pétala.
Voltando a exploração dos dados, vamos lá.
# A função head retorna as primeiras linhas do dataframe.
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
#A função str mostra a estrutura dos dados.
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 ...
Temos um data frame com 150 objetos (linhas) e 5 variáveis (colunas). 4 variáveis são numéricas e 1 é um fator de 3 níveis, ou seja 3 classificações diferentes.
Como já foi dito acima, o data frame foi carregado sem nome para as colunas. Mas iremos adicioná-los para uma melhor compreensão nessa etapa de exploração. Ainda sobre explorar os dados, será que há alguma correlação entre o tamanho e a largura das Petalas? Vamos plotar o gráfico para observar melhor.
# Adicionar nomes para colunas
names(iris) <- c("Sepala.Tamanho", "Sepala.Largura", "Petala.Tamanho", "Petala.Largura", "Especies")
# Gráfico para visualizar a correlação entre o Tamanho e a Largura da pétala nas espécies.
plot(iris$Petala.Tamanho,
iris$Petala.Largura,
pch=21, bg=c("red","green3","blue")[unclass(iris$Especies)],
xlab="Petal Tamanho",
ylab="Petal Largura")
cor()
e também o pacote corrplot para visualizar as correlações entre as variáveis.cor(iris$Petala.Tamanho, iris$Petala.Largura)
## [1] 0.9627571
M <- cor(iris[,1:4])
corrplot(M, method="circle")
Antes de construir o modelo, é necessário garantir algumas propriedades dos dados. Os dados devem estar limpos (sem presença de N/A), normalizados (se aplicável) e para que possamos validar nosso modelo, é necessário dividir os dados em TREINO e TESTE.
Uma função para observar algumas características dos dados é a summary()
.
summary(iris)
## Sepala.Tamanho Sepala.Largura Petala.Tamanho Petala.Largura
## 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
## Especies
## Iris-setosa :50
## Iris-versicolor:50
## Iris-virginica :50
##
##
##
É possível observar algumas informações estatíticas como max, min e média (entre outros), e nas variáveis que sáo do tipo Fator é possível observar a quantidade de cada fator. O summary tbm te mostra se tem N/A, caso aplicável. Analisando os dados, não parece necessário uma normalização, mas vamos normalizá-los a fim de aprender como faz.
É possível criar a sua própria função de normalização e aplicar aos dados, seguindo os comandos abaixo.
# Crie sua prórpia função de nomralização
minhaNormalizacao <- function(x) {
num <- x - min(x)
denom <- max(x) - min(x)
return (num/denom)
}
# Utilize a função para normalizar os dados
iris_norm <- as.data.frame(lapply(iris[1:4], minhaNormalizacao))
# Observe os valores novamente
summary(iris_norm)
## Sepala.Tamanho Sepala.Largura Petala.Tamanho Petala.Largura
## Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.00000
## 1st Qu.:0.2222 1st Qu.:0.3333 1st Qu.:0.1017 1st Qu.:0.08333
## Median :0.4167 Median :0.4167 Median :0.5678 Median :0.50000
## Mean :0.4287 Mean :0.4392 Mean :0.4676 Mean :0.45778
## 3rd Qu.:0.5833 3rd Qu.:0.5417 3rd Qu.:0.6949 3rd Qu.:0.70833
## Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.00000
Mas, já que estamos utilizando o keras, e este pacote possui uma função de normalização. Essa função só funciona com matrizes, então precisamos tranformar nossos dados em matrizes. E antes disso temos que transformar os dados nominais em numéricos. É só seguir os comandos abaixo.
# Modifica a coluna de Espécies para variáveis numéricas.
iris[,5] <- as.numeric(iris[,5]) -1
# Tranforma o data frame em matriz
iris <- as.matrix(iris)
# Seta os nomes das dimensões para NULL
dimnames(iris) <- NULL
# Normaliza os dados
iris[,1:4] <- normalize(iris[,1:4])
Para fazer essa separação, iremos utilizar a função sample()
. Basicamente iremos criar um vetor com 150 elementos (número de linhas dos dados), com os valores 1 e 2 aleatoriamente distribuidos com a probabilidade de 0.67 e 0.33. Ou seja nossos dados de treinos terão 67% dos dados originais, enquanto os dados de testes terão 33%.
# Determina a divisão dos "pedaços"
indice <- sample(2, nrow(iris), replace=TRUE, prob=c(0.67, 0.33))
# Divide os dados em teste e treino
iris.training <- iris[indice==1, 1:4]
iris.test <- iris[indice==2, 1:4]
# Separa os rótulos do modelo de teste e treino
iris.trainingtarget <- iris[indice==1, 5]
iris.testtarget <- iris[indice==2, 5]
Para que o gerador de números aleatórios de R sigam uma mesma sequência, para que a reprodutibilidade seja garantida, iremos utilizar a função set.seed()
passando qualquer inteiro como parâmetro. Neste tutorial utilizamos o número 100 É utilizado o replace=TRUE
para que depois de atribuir um número a linha, seja possível utilizar esse número novamente
Só mais um passo antes de começar a construir o modelo, que é adequar os rótulos de saída para um formato que melhor funcione para a redes neurais. Para fazer isso, é necessário transformar o vetor de saída, que tem um valor para cada classe, para uma matriz de boolean que indica se a classe está presente ou não. Por exemplo, neste caso, saíremos de um vetor com valores [1,2,3] para [0,0,1], [0,1,0], [1,0,0]. O pacote do keras tem uma função que oferce essa tranformação.
# Modifica os tórulo do treino para one-hot enconding
iris.trainLabels <- to_categorical(iris.trainingtarget)
# Modifica os tórulo do teste para one-hot enconding
iris.testLabels <- to_categorical(iris.testtarget)
# Mostra o valor dos rótulos de teste
print(head(iris.testLabels))
## [,1] [,2] [,3]
## [1,] 1 0 0
## [2,] 1 0 0
## [3,] 1 0 0
## [4,] 1 0 0
## [5,] 1 0 0
## [6,] 1 0 0
Para iniciar a construção do modelo, contaremos com a ajuda da função keras_model_sequential()
. E então vamos adicionar as camadas da rede neural. Para função de ativação, utilizaremos a função relu, que é uma das mais comuns utilizadas e é ideal para quem está começando a aprender. Essa função de ativação é utilizada na “hidden layer”. Para camada de saída (output layer) utilizaremos a função softmax que mantém os valores de saída entre 0 e 1.
# Inicializa o modelo
model <- keras_model_sequential()
# Adiciona as camadas
model %>%
layer_dense(units = 8, activation = 'relu', input_shape = c(4)) %>%
layer_dense(units = 3, activation = 'softmax')
A camda de saída tem 3 unidades, que é uma pra cada classe de classificação (versicolor, virginica or setosa). Na camada anterior, temos 8 nós escondidos e um input_shape
de 4, que é um pra cada coluna de variável dos dados.
Para analisar o modelo, podemos usar as seguintes funções:
# Resumo do modelo
summary(model)
## ___________________________________________________________________________
## Layer (type) Output Shape Param #
## ===========================================================================
## dense_1 (Dense) (None, 8) 40
## ___________________________________________________________________________
## dense_2 (Dense) (None, 3) 27
## ===========================================================================
## Total params: 67
## Trainable params: 67
## Non-trainable params: 0
## ___________________________________________________________________________
# 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': 3, '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}}]
# Configuração da camada
get_layer(model, index = 1)
# Lista das camadas
model$layers
# Lista das entradas
model$input
# Lista das saídas
model$outputs
Depois de configurar a arquiterura do modelo, vamos compilar e ajustá-lo para uma melhor perfomance nos dados. Para compilar, iremos utilizar a função compile()
. Nessa função é necesário setar alguns parametros obrigatórios, o loss
e o optimizer
. Se desejar monitorar a acurácia durante o treino, é so setar o parametro metrics
para accuracy
.
# Compila o modelo
model %>% compile(
loss = 'categorical_crossentropy',
optimizer = 'adam',
metrics = 'accuracy'
)
Escolhemos a função categorical_crossentropy
para função de perda, porque desejamos classificar os dados em 3 classes, se fosse em apenas duas classes, seria mais recomendado utilizar a função binary_crossentropy
. Para a função de otimização temos várias opções, as mais populares são Stochastic Gradient Descent (SGD), ADAM e RMSprop, cada um tem alguns parametros específicos a ser tunado.
Para ajustar o modelo, iremos usar a função fit()
com 200 iterações em 5 lotes. Definindo as iterações e os lotes ajuda a melhorar eficiência pois evitar de carregar todas as possíveis entrada de uma vez só. Armazenaremos numa variável para algumas visualizações futuras.
# Ajusta o modelo
history <-model %>% fit(
iris.training,
iris.trainLabels,
epochs = 200,
batch_size = 5,
validation_split = 0.2
)
É possível observar o comportamento do treino a partir dos seguintes gráficos:
# Gráfico do treinamento do modelo
plot(history)
O gráfico de cima é relacionado a função de perda (loss), onde a cor verde representa os dados de validação e a vermelha, os dados de treino. O mesmo acontece para o gráfico de baixo, só que representa a acurácia (acc).
É importante observar que:
* Se a acurácia do treino está crescendo, mas a acurácia da validação está decrescendo é um sinal de overfitting (quando o modelo memoriza os dados ao invés de aprendê-los)
* Se a acurácia do treino e da validação são sempre crescentes, siginifca que o modelo está sendo bem treinado.
O “último” passo agora é verificar a predição da rede neural para novos valores.
# Predição das classes para os dados de teste
classes <- model %>% predict_classes(iris.test, batch_size = 128)
# Avaliando rótulos dos testes
score <- model %>% evaluate(iris.test, iris.testLabels, batch_size = 128)
# Pontuação de acerto
print(score)
## $loss
## [1] 0.4489071
##
## $acc
## [1] 0.6530612
Esse é realmente o “último” passo e um dos mais trabalhosos. Modificar os parametros do modelo para aumentar o índice da métrica utilizada, que no nosso caso foi a acurácia. Além de modificar os valores dos epochs e dos lotes batch, tem outros três modos de otimizar o modelo.
1. Adicionando camadas
2. Aumentando o número de nós “escondidos”
3. Mudando o parametro de otimização da função de compile
.
Vamos observar o que acontece se adicionarmos mais uma camadas?
# Inicializa o modelo
model <- keras_model_sequential()
# Adiciona camadas
model %>%
layer_dense(units = 8, activation = 'relu', input_shape = c(4)) %>%
layer_dense(units = 5, activation = 'relu') %>%
layer_dense(units = 3, activation = 'softmax')
# Compila o modelo
model %>% compile(
loss = 'categorical_crossentropy',
optimizer = 'adam',
metrics = 'accuracy'
)
# Salva o histórico do treino
history <- model %>% fit(
iris.training, iris.trainLabels,
epochs = 200, batch_size = 5,
validation_split = 0.2
)
# Avalia o modelo
score <- model %>% evaluate(iris.test, iris.testLabels, batch_size = 128)
# Pontuação do modelo
print(score)
## $loss
## [1] 0.5102868
##
## $acc
## [1] 0.6530612
# Gráfico do histórico
plot(history)
Vamos adicionar mais nós na camada escondida.
# Inicializa o modelo
model <- keras_model_sequential()
# Adiciona camadas
model %>%
layer_dense(units = 28, activation = 'relu', input_shape = c(4)) %>%
layer_dense(units = 3, activation = 'softmax')
# Compila o modelo
model %>% compile(
loss = 'categorical_crossentropy',
optimizer = 'adam',
metrics = 'accuracy'
)
# Salva o histórico do treino
history <- model %>% fit(
iris.training, iris.trainLabels,
epochs = 200, batch_size = 5,
validation_split = 0.2
)
# Avalia o modelo
score <- model %>% evaluate(iris.test, iris.testLabels, batch_size = 128)
# Pontuação do modelo
print(score)
## $loss
## [1] 0.2907182
##
## $acc
## [1] 0.877551
#Gráfico do histórico do modelo
plot(history)
Se mudarmos a função de otimização, o que acontece? Utilizaremos a função de otimização por gradiente descentendente e ela requer um parametro extra, a taxa de aprendizagem (learning rate)
# Inicializa o modelo
model <- keras_model_sequential()
# Adiciona camadas
model %>%
layer_dense(units = 8, activation = 'relu', input_shape = c(4)) %>%
layer_dense(units = 3, activation = 'softmax')
# Inicializa uma funcão de otimização
sgd <- optimizer_sgd(lr = 0.01)
# Compila o modelo
model %>% compile(optimizer=sgd,
loss='categorical_crossentropy',
metrics='accuracy')
# Salva o histórico do treino
history <- model %>% fit(
iris.training, iris.trainLabels,
epochs = 200, batch_size = 5,
validation_split = 0.2
)
# Avalia o modelo
score <- model %>% evaluate(iris.test, iris.testLabels, batch_size = 128)
# Pontuação do modelo
print(score)
## $loss
## [1] 0.6216444
##
## $acc
## [1] 0.6530612
#Gráfico do histórico do modelo
plot(history)
O keras oferece algumas opções para salvar e exportar seu modelo. Utilizando as funções save_model_hdf5()
e load_model_hdf5()
é possível salvar e recuperar o modelo, respectivamente.
Também é possível salvar e recuperar os pesos através das funções save_model_weights_hdf5()
e load_model_weights_hdf5()
.
Também é possível salvar o modelo em arquivos JSON ou YAML, através das funções model_to_json()
e model_to_yaml()
para salvar, e model_from_json()
e model_from yaml()
.
Parabéns, você completou este tutorial, mas é só o começo. Escolha um dataset diferente e aplique seus conhecimentos sobre ele. Só mais uma coisa, aqui está a documentação do keras.