lab-x-nome-sobrenome.ipynb
.# IMPLEMENTE O SEU CÓDIGO AQUI
.Ctrl + Enter
para finalizar a edição.Ctrl + Enter
ou clique em Run
no menu do Jupyter.import os # operational system para manipulação de arquivos.
import cv2 # opencv para manipulação de imagens.
import random # trabalhar com aleatoriedade
import numpy as np # numpy para manipulação de matrizes e arrays.
import matplotlib.pyplot as plt # pyplot para plotagem de gráficos e imagens.
from sklearn.model_selection import train_test_split # função para particionamento dos dados
from tensorflow.keras.models import Sequential # classe de modelos sequenciais para construir as redes neurais.
from tensorflow.keras.utils import to_categorical # função para preprocessamento dos gabaritos.
from tensorflow.keras.datasets import mnist # dataset utilizado nesse experimento.
from tensorflow.keras import layers # módulo de camadas do keras
from tensorflow.keras import callbacks # módulo de callbacks do keras
from tensorflow.keras import optimizers # módulo de otimizadores do keras
Vamos utilizar o MNIST, outro banco de dados presente no catálogo de datasets do Keras, que é disponibilizado como uma função pronta;
Cada instância do banco de dados corresponde a uma imagem rotulada de um dígito manuscrito;
As imagens do banco de dados são monocromáticas e de dimensões 28 x 28;
Os gabaritos correspondem ao número manuscrito, sendo um inteiro entre 0 e 9;
O banco de dados contém 60.000 imagens para treino e 10.000 imagens para teste;
Como vimos, a primeira dimensão dos arrays do banco de dados é reservada para controlar a amostra e as demais correspondem às demais dimensões do tipo de dados utilizado.
Nesse caso, os nossos dados são imagens monocromáticas (2D), de modo que são organizados em tensores tridimensionais (3D) com formato: dados.shape = (amostras, altura, largura)
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
# escolhe exemplos aleatórios
indices = np.random.randint(0, 60000 - 1, 10)
# plots
fig, axs = plt.subplots(nrows = 2, ncols = 5, figsize=(16, 8))
for i, idx in enumerate(indices):
# plota a imagem
axs[i // 5][i % 5].imshow(train_images[idx], vmin = 0, vmax = 255, cmap="gray")
# adiciona o gabarito como título
axs[i//5][i%5].set_title("Gabarito: {}".format(train_labels[idx]), fontsize = 16)
# adiciona o shape como subtítulo
axs[i//5][i%5].set_xlabel("Shape: {}".format(train_images[idx].shape), fontsize = 16)
Como estamos utilizando Redes Neurais Convolucionais 2D, precisamos que a entrada seja seja um array tetradimensional (4D), sendo necessário adicionar uma dimensão para os canais.
Para isso, podemos utilizar a função reshape(shape) que reorganiza um array para um determinado formato especificado desde que seja possível alocar todos os valores do array original para o novo formato.
Além disso, como em uma imagem todos os pixels são valores entre 0 e 255, podemos pre-processar os dados de entrada a partir de uma simples divisão por 255, que garante que todos os dados estarão entre 0 e 1.
def preprocess_data(data):
# desfaz adiciona uma dimensão para os canais da imagem
data = data.reshape((len(data), 28, 28, 1))
# normaliza os valores ao dividir pelo maior valor possível dentro das imagens.
data = data.astype("float32")/255
return data
preprocessed_train_images = preprocess_data(train_images)
preprocessed_test_images = preprocess_data(test_images)
# IMPLEMENTE SEU CÓDIGO AQUI --> QUESTÃO 1
Agora vamos criar uma partição de validação a partir do conjunto de treino para realizar uma validação cruzada.
Novamente vamos utilizar a função train_test_split, que separa dados e os seus respectivos gabaritos segundo uma fração especificada.
Contudo, dessa vez utilizaremos o parâmetro stratify, que indica os rótulos associados aos dados fornecidos. Quando esse parâmetro é fornecido a função realiza o particionamento dos dados e mantém a proporção entre exemplos de uma mesma classe com relação aos dados originais.
# fração escolhida para separar o mesmo número de instâncias do conjunto de testes
data_frac = preprocessed_test_images.shape[0] / preprocessed_train_images.shape[0]
# criação do conjunto de validação
preprocessed_train_images, preprocessed_val_images, train_labels, val_labels = train_test_split(preprocessed_train_images, # dados de treino
train_labels, # gabaritos de treino
test_size = data_frac, # proporção de dados p/ validação
stratify = train_labels, # dados de referência
random_state = 42) # semente de geração
print("Treino:", preprocessed_train_images.shape, train_labels.shape)
print("Validação:", preprocessed_val_images.shape, val_labels.shape)
Nesse caso temos um problema multiclasse com 10 classes, sendo elas correspondentes aos números entre 0 e 9. Consequentemente, o modelo produzido terá 10 unidades de saída, uma para cada classe, e utilizará a função de ativação softmax.
Para fornecer esses gabaritos para a rede durante o treinamento é necessário categorizar as saídas, para limitá-las ao intervalo [0, 1]. Para isso vamos utilizar a função to_categorical() disponível no próprio Keras.
categorical_train_labels = to_categorical(train_labels)
categorical_val_labels = to_categorical(val_labels)
categorical_test_labels = to_categorical(test_labels)
Para construir o modelo usaremos a classe Sequential, que possibilita a construção de modelos sequenciais de forma bastante simples.
A construção do modelo é feita a partir do seu instanciamento como objeto da classe seguido de chamadas à função add() para adicionar camadas.
Como estamos construindo Redes Neurais Convolucionais, vamos utilizar as camadas Input, Conv2D, MaxPooling2D, Dropout, Flatten e Dense.
Algumas funções de ativação disponíveis são:
Mais informações sobre a camada dense podem ser vistas em https://keras.io/api/layers/core_layers/dense/
Mais informações sobre as ativações disponíveis podem ser vistas em https://keras.io/api/layers/activations/
# COMENTE AS LINHAS DE CÓDIGO E MODOFIQUE OS PARÂMETROS AQUI --> QUESTÃO 2 - letra (a, b)
def build_model( input_shape, n_outputs ):
''''construindo o modelo baseado em redes neurais convolucionais'''
rede = Sequential()
rede.add(layers.Input((input_shape)))
rede.add(layers.Conv2D(filters=32, kernel_size=(3, 3), activation='relu'))
rede.add(layers.MaxPooling2D(pool_size=(2, 2)))
rede.add(layers.Dropout(rate=0.30))
rede.add(layers.Conv2D(filters=64, kernel_size=(3, 3), activation='relu'))
rede.add(layers.MaxPooling2D(pool_size=(2, 2)))
rede.add(layers.Dropout(rate=0.25))
rede.add(layers.Flatten())
rede.add(layers.Dense(units=128, activation="relu"))
rede.add(layers.Dense(units=n_outputs, activation = "softmax"))
return rede
model = build_model( (28, 28, 1), 10 )
model.summary()
model = build_model((28, 28, 1), 10)
model.compile(optimizer=optimizers.Adam(learning_rate = 1e-3), loss="categorical_crossentropy", metrics=["acc"])
model.summary()
model_checkpoint = callbacks.ModelCheckpoint("model.hdf5", monitor = "val_acc", save_best_only = True, verbose = 1)
O treinamento é realizado a partir da função fit, que recebe dados de treino e de validação além de hiperparâmetros como o número de épocas e o tamanho dos lotes de dados (batchsize).
hist = model.fit( x = None, y = None, epochs = 1, batchsize = None, callbacks = [], validation_data = None, verbose = "auto")
Lista de callbacks disponíveis: https://keras.io/api/callbacks/
history = model.fit(preprocessed_train_images, categorical_train_labels,
epochs=10, batch_size=128, callbacks = [model_checkpoint],
validation_data = (preprocessed_val_images, categorical_val_labels))
history_dict = history.history
fig, axes = plt.subplots(1, 2, squeeze = False, figsize = (16,8))
# Loss
train_loss_values = history_dict['loss']
val_loss_values = history_dict['val_loss']
# Epochs
epochs = range(1, len(train_loss_values) + 1)
# Accuracy
train_acc_values = history_dict['acc']
val_acc_values = history_dict['val_acc']
ax = axes.flat[0]
ax.plot(epochs, train_loss_values, 'r', label='Training loss')
ax.plot(epochs, val_loss_values, 'b', label='Validation loss')
ax.set_title('Training and validation Loss')
ax.set_xlabel('Epochs')
ax.set_ylabel('Loss')
ax.legend()
ax = axes.flat[1]
ax.plot(epochs, train_acc_values, 'r', label='Training acc')
ax.plot(epochs, val_acc_values, 'b', label='Validation acc')
ax.set_title('Training and validation Accuracy')
ax.set_xlabel('Epochs')
ax.set_ylabel('Accuracy')
ax.legend()
O teste do modelo pode ser realizado a partir da função evaluate, que recebe os dados de treino e retorna o valor de loss calculado para esse conjunto e os valores de cada métrica da lista fornecida durante a compilação do modelo.
test_loss, test_acc = model.evaluate(preprocessed_test_images, categorical_test_labels)
print("Test Accuracy:", 100*test_acc, "%")
print("Acertos: {} - Erros: {}".format(round(len(preprocessed_test_images)*test_acc),
round(len(preprocessed_test_images)*(1-test_acc))))
model.load_weights('model.hdf5')
test_loss, test_acc = model.evaluate(preprocessed_test_images, categorical_test_labels)
print("Test Accuracy:", 100*test_acc, "%")
print("Acertos: {} - Erros: {}".format(round(len(preprocessed_test_images)*test_acc),
round(len(preprocessed_test_images)*(1-test_acc))))
def show_results(xtest, ytest, ypred, num = 25, tipo = "rand"):
if tipo == "acertos":
fltr_idx = [i for i in range(xtest.shape[0]) if ypred[i] == ytest[i]]
else:
fltr_idx = [i for i in range(xtest.shape[0]) if ypred[i] != ytest[i]]
indices = np.random.choice(fltr_idx, min(num, len(fltr_idx)), replace=False)
rows = int(num/5)
fig, axs = plt.subplots(nrows = rows, ncols = 5, figsize=(20, 4*rows))
for i, idx in enumerate(indices):
img = xtest[idx]
if ypred[idx] == ytest[idx]:
axs[i//5][i%5].set_title(str(ytest[idx]), color = "green", fontsize = 20)
else:
axs[i//5][i%5].set_title("Pred: {} - Gabarito: {}".format(ypred[idx], ytest[idx]), color = "red", fontsize = 20)
axs[i//5][i%5].imshow(img, vmin=0, vmax=255, cmap = "gray")
return
pred_labels = model.predict(preprocessed_test_images, verbose=1)
preds = np.argmax(pred_labels, axis = -1)
show_results(test_images, test_labels, preds, tipo = "acertos")
show_results(test_images, test_labels, preds, tipo = "erros")