Nossa rede é uma sequência de camadas e podemos usar o modelo sequencial oferecido pelo Keras, que possui as funções necessárias para construir cada camada de uma rede neural convolucional.
O primeiro passo é carregar os pacotes necessários, o que é feito nas células abaixo.
O Keras utiliza o TensorFlow como backend, pois na prática o Keras é apenas uma biblioteca para simplificar a complexidade do TensorFlow. Aqui estão as versões utilizadas:
import tensorflow as tf
print("Versão do TensorFlow:", tf.__version__)
import keras as K
print("Versão do Keras:", K.__version__)
# Imports
from keras.models import Sequential
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import Flatten
from keras.layers import Dense
Nós então inicializamos a nossa rede:
# Inicializando a Rede Neural Convolucional
classifier = Sequential()
Agora nós definimos os parâmetros para o shape dos dados de entrada e a função de ativação. Usaremos 32 features para um array 2D e definiremos nosso array como o formato 3x3.
Converteremos todas as nossas imagens 64x64 pixels em um array 3D (pois as imagens são coloridas com 3 canais de cores).
# Passo 1 - Primeira Camada de Convolução
classifier.add(Conv2D(32, (3, 3), input_shape = (64, 64, 3), activation = 'relu'))
Agora aplicamos o agrupamento (pooling) para reduzir o tamanho do mapa de features resultado da primeira camada de convolução (dividido por 2):
# Passo 2 - Pooling
classifier.add(MaxPooling2D(pool_size = (2, 2)))
Adicionamos então a Segunda Camada de Convolução, tornando nossa rede um pouco mais profunda:
# Adicionando a Segunda Camada de Convolução
classifier.add(Conv2D(32, (3, 3), activation = 'relu'))
Mais uma vez, aplicamos a camada de pooling à saída da camada de convolução anterior.
classifier.add(MaxPooling2D(pool_size = (2, 2)))
Agora aplicamos o "achatamento" ou apenas Flatten para converter a estrutura de dados 2D resultado da camada anterior em uma estrutura 1D, ou seja, um vetor.
# Passo 3 - Flattening
classifier.add(Flatten())
No próximo passo conectamos todas as camadas. Usamos uma função de ativação retificadora (relu) e então uma função de ativação sigmóide para obter as probabilidades de cada imagem conter um cachorro ou um gato. O modelo raramente terá 100% de certeza e o que ele gera como um resultado é uma probabilidade.
# Passo 4 - Full connection
classifier.add(Dense(units = 128, activation = 'relu'))
classifier.add(Dense(units = 1, activation = 'sigmoid'))
Finalmente nós compilamos nossa rede neural. Para compilar a rede, usamos o otimizador "Adam", um excelente algoritmo de primeira ordem para otimização baseada em gradiente de funções objetivas estocásticas, que toma como base uma estimativa adaptada de momentos de baixa ordem.
Usamos uma função log loss com "entropia binária cruzada", pois ela funciona bem com funções sigmóides. Nossa métrica será a acurácia, pois essa é nossa maior preocupação no treinamento deste tipo de modelo.
# Compilando a rede
classifier.compile(optimizer = 'adam', loss = 'binary_crossentropy', metrics = ['accuracy'])
Neste ponto temos nossa rede construída. Precisamos agora treiná-la.
Com a rede criada, precisamos agora realizar o treinamento. Antes, porém, precisamos fazer algum pré-processamento nos dados, em nosso caso as imagens. Para essa tarefa, vamos usar a função ImageDataGenerator() do Keras e ajustar escala e zoom das imagens de treino e a escala das imagens de validação.
O pré-processamento dos dados é etapa crucial em qualquer projeto de Machine Learning e muitas técnicas podem ser usadas, sempre de acordo com os dados em mãos e o problema que estamos tentando resolver. Nos cursos da Data Science Academy ensinamos aos alunos uma variedade de técnicas.
# Criando os objetos train_datagen e validation_datagen com as regras de pré-processamento das imagens
from keras.preprocessing.image import ImageDataGenerator
train_datagen = ImageDataGenerator(rescale = 1./255,
shear_range = 0.2,
zoom_range = 0.2,
horizontal_flip = True)
validation_datagen = ImageDataGenerator(rescale = 1./255)
Aplicamos então os dois objetos criados anteriormente para pré-processar os dados de treino e de validação. Lembre-se: o tratamento aplicado aos dados de validação deve ser o mesmo tratamento aplicado aos dados de treino.
# Pré-processamento das imagens de treino e validação
training_set = train_datagen.flow_from_directory('dataset_treino',
target_size = (64, 64),
batch_size = 32,
class_mode = 'binary')
validation_set = validation_datagen.flow_from_directory('dataset_validation',
target_size = (64, 64),
batch_size = 32,
class_mode = 'binary')
Usaremos 8000 passos em nosso conjunto de treinamento para cada época. Escolhemos 2000 etapas de validação para as imagens de validação. Esses hiperparâmetros são definidos por você.
Em uma máquina apenas com CPU o processo de treinamento pode ser bem demorado. Com GPU podemos reduzir o tempo total em até 10x. O treinamento desse modelo foi feito no super computador da DSA com 3 GPUs. Os alunos da Formação Inteligência Artificial tem acesso remoto gratuito ao super computador para treinar os modelos estudados nos cursos da DSA.
Foram usadas 2 GPUs para este experimento, conforme informações abaixo:
I tensorflow/core/common_runtime/gpu/gpu_device.cc:1405] Found device 0 with properties: name: TITAN X (Pascal) major: 6 minor: 1 memoryClockRate(GHz): 1.531 pciBusID: 0000:05:00.0 totalMemory: 11.91GiB freeMemory: 11.75GiB
Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 11375 MB memory) -> physical GPU (device: 0, name: TITAN X (Pascal), pci bus id: 0000:05:00.0, compute capability: 6.1)
I tensorflow/core/common_runtime/gpu/gpu_device.cc:1405] Found device 1 with properties: name: GeForce GTX 1080 Ti major: 6 minor: 1 memoryClockRate(GHz): 1.582 pciBusID: 0000:09:00.0 totalMemory: 10.92GiB freeMemory: 10.76GiB
Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:1 with 10409 MB memory) -> physical GPU (device: 1, name: GeForce GTX 1080 Ti, pci bus id: 0000:09:00.0, compute capability: 6.1)
# Executando o treinamento (esse processo pode levar bastante tempo, dependendo do seu computador)
classifier.fit_generator(training_set,
steps_per_epoch = 8000,
epochs = 5,
validation_data = validation_set,
validation_steps = 2000)
Treinamento concluído com sucesso! Observe que ao final de cada época a acurácia aumenta, ou seja, nosso modelo está aprendendo e cometendo cada vez menos erros! Essa é a magia por trás de Machine Learning, a pura aplicação de Matemática e Estatística, através de Programação e Ciência da Computação. Isso não é maravilhoso? Machine Learning está transformando o nosso mundo, à medida que treinamos as máquinas para realizar tarefas até então exclusivas dos seres humanos.
Vamos agora testar nosso modelo treinado com imagens que ele ainda não viu e que estão nos dados de teste.
Para cada imagem de teste, carregamos as imagens com as mesmas dimensões usadas nas imagens de treino. Na sequência convertemos as imagens em um array e expandimos as dimensões. Então apresentamos as imagens ao classificador treinado nos passos anteriores. Por fim, verificamos o resultado da previsão e emitimos a informação se a imagem é de um gato ou cachorro.
# Primeira Imagem
import numpy as np
from keras.preprocessing import image
test_image = image.load_img('dataset_teste/2216.jpg', target_size = (64, 64))
test_image = image.img_to_array(test_image)
test_image = np.expand_dims(test_image, axis = 0)
result = classifier.predict(test_image)
training_set.class_indices
if result[0][0] == 1:
prediction = 'Cachorro'
else:
prediction = 'Gato'
Image(filename='dataset_teste/2216.jpg')
# Previsão da primeira imagem
prediction
Perfeito! Nosso modelo recebeu uma imagem que nunca tinha visto antes e com base no que aprendeu durante o treinamento, foi capaz de classificar a imagem como sendo de um cachorro! Na prática, isso é o que acontece:
Não há mágica. Há Matemática e Estatística, através de Programação e Ciência da Computação!
# Segunda Imagem
test_image = image.load_img('dataset_teste/2897.jpg', target_size = (64, 64))
test_image = image.img_to_array(test_image)
test_image = np.expand_dims(test_image, axis = 0)
result = classifier.predict(test_image)
training_set.class_indices
if result[0][0] == 1:
prediction = 'Cachorro'
else:
prediction = 'Gato'
Image(filename='dataset_teste/2897.jpg')
# Previsão da segunda imagem
prediction
Nosso modelo acertou mais uma! Veja que a imagem possui apenas a face de um gato, mas os detalhes estão bem definidos e nosso modelo não teve trabalho nesta classificação.
# Terceira Imagem
test_image = image.load_img('dataset_teste/2891.jpg', target_size = (64, 64))
test_image = image.img_to_array(test_image)
test_image = np.expand_dims(test_image, axis = 0)
result = classifier.predict(test_image)
training_set.class_indices
if result[0][0] == 1:
prediction = 'Cachorro'
else:
prediction = 'cat'
Image(filename='dataset_teste/2891.jpg')
# Previsão da terceira imagem
prediction
Nosso modelo está impossível. Acertou novamente! :-)
# Quarta Imagem
test_image = image.load_img('dataset_teste/2892.jpg', target_size = (64, 64))
test_image = image.img_to_array(test_image)
test_image = np.expand_dims(test_image, axis = 0)
result = classifier.predict(test_image)
training_set.class_indices
if result[0][0] == 1:
prediction = 'Cachorro'
else:
prediction = 'Gato'
Image(filename='dataset_teste/2892.jpg')
# Previsão da quarta imagem
prediction
Veja que esse modelo está realmente bem treinado, pois ele é capaz de classificar corretamente apenas a face de um gato ou mesmo a foto do corpo inteiro de um cachorro! Para que isso aconteça, temos que apresentar muitas imagens ao modelo durante o treinamento.
# Quinta Imagem
test_image = image.load_img('dataset_teste/2524.jpg', target_size = (64, 64))
test_image = image.img_to_array(test_image)
test_image = np.expand_dims(test_image, axis = 0)
result = classifier.predict(test_image)
training_set.class_indices
if result[0][0] == 1:
prediction = 'Cachorro'
else:
prediction = 'Gato'
Image(filename='dataset_teste/2524.jpg')
# Previsão da quinta imagem
prediction
Ops. Nosso modelo falhou! Por que isso ocorreu? Nosso modelo não conseguiu detectar na imagem as características aprendidas sobre gatos e classificou como cachorro. Para resolver isso, teríamos que retornar ao treinamento e apresentar mais imagens ao modelo, eventualmente usando técnicas como Dataset Augmentation e treinar por mais tempo, até atingir uma acurácia maior. Ensinamos tudo isso aos alunos nos cursos da Formação Inteligência Artificial, aqui na DSA.
# Sexta Imagem
test_image = image.load_img('dataset_teste/2551.jpg', target_size = (64, 64))
test_image = image.img_to_array(test_image)
test_image = np.expand_dims(test_image, axis = 0)
result = classifier.predict(test_image)
training_set.class_indices
if result[0][0] == 1:
prediction = 'Cachorro'
else:
prediction = 'Gato'
Image(filename='dataset_teste/2551.jpg')
# Previsão da sexta imagem
prediction
E para encerrar, mais uma classificação correta do nosso modelo!
Obteve-se um resultado final de 95,82% de precisão para o nosso conjunto de treino e 83% para o nosso conjunto de testes. Para nossas 6 imagens de amostra, nosso modelo acertou 5. Obviamente mais testes poderiam ser realizados.
Melhorias adicionais para este modelo: