Pesquisa de site

Reconhecimento de dígitos manuscritos usando redes neurais convolucionais em Python com Keras


Uma demonstração popular da capacidade das técnicas de aprendizagem profunda é o reconhecimento de objetos em dados de imagem.

O “olá mundo” do reconhecimento de objetos para aprendizado de máquina e aprendizado profundo é o conjunto de dados MNIST para reconhecimento de dígitos manuscritos.

Nesta postagem, você descobrirá como desenvolver um modelo de aprendizado profundo para obter um desempenho próximo do estado da arte na tarefa de reconhecimento de dígitos manuscritos MNIST em Python usando a biblioteca de aprendizado profundo Keras.

Depois de concluir este tutorial, você saberá:

  • Como carregar o conjunto de dados MNIST em Keras
  • Como desenvolver e avaliar um modelo básico de rede neural para o problema MNIST
  • Como implementar e avaliar uma rede neural convolucional simples para MNIST
  • Como implementar um modelo de aprendizagem profunda próximo do estado da arte para MNIST

Inicie seu projeto com meu novo livro Deep Learning With Python, incluindo tutoriais passo a passo e os arquivos de código-fonte Python para todos exemplos.

Vamos começar.

  • Junho/2016: publicado pela primeira vez
  • Atualização outubro/2016: atualizado para Keras 1.1.0, TensorFlow 0.10.0 e scikit-learn v0.18
  • Atualização março/2017: atualizado para Keras 2.0.2, TensorFlow 1.0.1 e Theano 0.9.0
  • Atualização de setembro/2019: atualização para API Keras 2.2.5
  • Atualização de julho/2022: atualização para API TensorFlow 2.x

Observe que para uma versão estendida deste tutorial, consulte:

  • Como desenvolver uma Deep CNN para classificação de dígitos MNIST

Descrição do problema de reconhecimento de dígitos manuscritos do MNIST

O problema MNIST é um conjunto de dados desenvolvido por Yann LeCun, Corinna Cortes e Christopher Burges para avaliar modelos de aprendizado de máquina no problema de classificação de dígitos manuscritos.

O conjunto de dados foi construído a partir de vários conjuntos de dados de documentos digitalizados disponíveis no Instituto Nacional de Padrões e Tecnologia (NIST). É daí que vem o nome do conjunto de dados, conjunto de dados NIST ou MNIST modificado.

Imagens de dígitos foram tiradas de uma variedade de documentos digitalizados, normalizadas em tamanho e centralizadas. Isso o torna um excelente conjunto de dados para avaliação de modelos, permitindo que o desenvolvedor se concentre no aprendizado de máquina com o mínimo de limpeza ou preparação de dados necessária.

Cada imagem é um quadrado de 28×28 pixels (784 pixels no total). Uma divisão padrão do conjunto de dados é usada para avaliar e comparar modelos, onde 60.000 imagens são usadas para treinar um modelo e um conjunto separado de 10.000 imagens é usado para testá-lo.

É uma tarefa de reconhecimento de dígitos. Como tal, existem dez dígitos (0 a 9) ou dez classes para prever. Os resultados são relatados usando o erro de predição, que nada mais é do que a precisão da classificação invertida.

Excelentes resultados alcançam um erro de previsão inferior a 1%. Um erro de previsão de última geração de aproximadamente 0,2% pode ser alcançado com grandes redes neurais convolucionais. Há uma listagem dos resultados mais recentes e links para os artigos relevantes sobre o MNIST e outros conjuntos de dados na página de Rodrigo Benenson.

Carregando o conjunto de dados MNIST em Keras

A biblioteca de aprendizagem profunda Keras fornece um método conveniente para carregar o conjunto de dados MNIST.

O conjunto de dados é baixado automaticamente na primeira vez que esta função é chamada e armazenado em seu diretório inicial em ~/.keras/datasets/mnist.npz como um arquivo de 11 MB.

Isso é muito útil para desenvolver e testar modelos de aprendizado profundo.

Para demonstrar como é fácil carregar o conjunto de dados MNIST, primeiro escreva um pequeno script para baixar e visualizar as primeiras quatro imagens no conjunto de dados de treinamento.

# Plot ad hoc mnist instances
from tensorflow.keras.datasets import mnist
import matplotlib.pyplot as plt
# load (downloaded if needed) the MNIST dataset
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# plot 4 images as gray scale
plt.subplot(221)
plt.imshow(X_train[0], cmap=plt.get_cmap('gray'))
plt.subplot(222)
plt.imshow(X_train[1], cmap=plt.get_cmap('gray'))
plt.subplot(223)
plt.imshow(X_train[2], cmap=plt.get_cmap('gray'))
plt.subplot(224)
plt.imshow(X_train[3], cmap=plt.get_cmap('gray'))
# show the plot
plt.show()

Você pode ver que baixar e carregar o conjunto de dados MNIST é tão fácil quanto chamar a função mnist.load_data(). Executando o exemplo acima, você deverá ver a imagem abaixo.

Modelo de linha de base com perceptrons multicamadas

Você realmente precisa de um modelo complexo como uma rede neural convolucional para obter os melhores resultados com MNIST?

Você pode obter resultados muito bons usando um modelo de rede neural muito simples com uma única camada oculta. Nesta seção, você criará um modelo perceptron multicamadas simples que atinge uma taxa de erro de 1,74%. Você usará isso como base para comparar modelos de redes neurais convolucionais mais complexos.

Vamos começar importando as classes e funções necessárias.

from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Dropout
from tensorflow.keras.utils import to_categorical
...

Agora, você pode carregar o conjunto de dados MNIST usando a função auxiliar Keras.

...
# load data
(X_train, y_train), (X_test, y_test) = mnist.load_data()

O conjunto de dados de treinamento é estruturado como uma matriz tridimensional de instância, largura e altura da imagem. Para um modelo perceptron multicamadas, você deve reduzir as imagens em um vetor de pixels. Nesse caso, as imagens de tamanho 28×28 terão valores de entrada de 784 pixels.

Você pode fazer essa transformação facilmente usando a função reshape() no array NumPy. Você também pode reduzir os requisitos de memória forçando a precisão dos valores de pixel para 32 bits, a precisão padrão usada pelo Keras de qualquer maneira.

...
# flatten 28*28 images to a 784 vector for each image
num_pixels = X_train.shape[1] * X_train.shape[2]
X_train = X_train.reshape((X_train.shape[0], num_pixels)).astype('float32')
X_test = X_test.reshape((X_test.shape[0], num_pixels)).astype('float32')

Os valores de pixel estão em tons de cinza entre 0 e 255. Quase sempre é uma boa ideia realizar algum escalonamento dos valores de entrada ao usar modelos de redes neurais. Como a escala é bem conhecida e bem comportada, você pode normalizar rapidamente os valores dos pixels para o intervalo 0 e 1 dividindo cada valor pelo máximo de 255.

...
# normalize inputs from 0-255 to 0-1
X_train = X_train / 255
X_test = X_test / 255

Finalmente, a variável de saída é um número inteiro de 0 a 9. Este é um problema de classificação multiclasse. Como tal, é uma boa prática usar uma codificação one-hot dos valores da classe, transformando o vetor de inteiros da classe em uma matriz binária.

Você pode fazer isso facilmente usando a função auxiliar tf.keras.utils.to_categorical() integrada no Keras.

...
# one hot encode outputs
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
num_classes = y_test.shape[1]

Agora você está pronto para criar seu modelo simples de rede neural. Você definirá seu modelo em uma função. Isso é útil se você quiser estender o exemplo posteriormente e tentar obter uma pontuação melhor.

...
# define baseline model
def baseline_model():
	# create model
	model = Sequential()
	model.add(Dense(num_pixels, input_shape=(num_pixels,), kernel_initializer='normal', activation='relu'))
	model.add(Dense(num_classes, kernel_initializer='normal', activation='softmax'))
	# Compile model
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	return model

O modelo é uma rede neural simples com uma camada oculta com o mesmo número de neurônios que entradas (784). Uma função de ativação retificadora é usada para os neurônios da camada oculta.

Uma função de ativação softmax é usada na camada de saída para transformar as saídas em valores semelhantes a probabilidade e permitir que uma classe das dez seja selecionada como a previsão de saída do modelo. A perda logarítmica é usada como função de perda (chamada categorical_crossentropy em Keras), e o algoritmo eficiente de descida de gradiente ADAM é usado para aprender os pesos.

Agora você pode ajustar e avaliar o modelo. O modelo é adequado ao longo de dez épocas com atualizações a cada 200 imagens. Os dados de teste são usados como conjunto de dados de validação, permitindo que você veja a habilidade do modelo durante o treinamento. Um valor detalhado de 2 é usado para reduzir a saída para uma linha para cada época de treinamento.

Finalmente, o conjunto de dados de teste é usado para avaliar o modelo e uma taxa de erro de classificação é impressa.

...
# build the model
model = baseline_model()
# Fit the model
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=200, verbose=2)
# Final evaluation of the model
scores = model.evaluate(X_test, y_test, verbose=0)
print("Baseline Error: %.2f%%" % (100-scores[1]*100))

Depois de juntar tudo isso, a listagem completa do código é fornecida abaixo.

# Baseline MLP for MNIST dataset
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.utils import to_categorical
# load data
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# flatten 28*28 images to a 784 vector for each image
num_pixels = X_train.shape[1] * X_train.shape[2]
X_train = X_train.reshape((X_train.shape[0], num_pixels)).astype('float32')
X_test = X_test.reshape((X_test.shape[0], num_pixels)).astype('float32')
# normalize inputs from 0-255 to 0-1
X_train = X_train / 255
X_test = X_test / 255
# one hot encode outputs
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
num_classes = y_test.shape[1]
# define baseline model
def baseline_model():
	# create model
	model = Sequential()
	model.add(Dense(num_pixels, input_shape=(num_pixels,), kernel_initializer='normal', activation='relu'))
	model.add(Dense(num_classes, kernel_initializer='normal', activation='softmax'))
	# Compile model
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	return model
# build the model
model = baseline_model()
# Fit the model
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=200, verbose=2)
# Final evaluation of the model
scores = model.evaluate(X_test, y_test, verbose=0)
print("Baseline Error: %.2f%%" % (100-scores[1]*100))

A execução do exemplo pode levar alguns minutos quando você o executa em uma CPU.

Observação: seus resultados podem variar de acordo com a natureza estocástica do algoritmo ou procedimento de avaliação, ou com diferenças na precisão numérica. Considere executar o exemplo algumas vezes e compare o resultado médio.

Você deve ver a saída abaixo. Esta rede muito simples definida em poucas linhas de código atinge uma taxa de erro respeitável de 2,3%.

Epoch 1/10
300/300 - 1s - loss: 0.2792 - accuracy: 0.9215 - val_loss: 0.1387 - val_accuracy: 0.9590 - 1s/epoch - 4ms/step
Epoch 2/10
300/300 - 1s - loss: 0.1113 - accuracy: 0.9676 - val_loss: 0.0923 - val_accuracy: 0.9709 - 929ms/epoch - 3ms/step
Epoch 3/10
300/300 - 1s - loss: 0.0704 - accuracy: 0.9799 - val_loss: 0.0728 - val_accuracy: 0.9787 - 912ms/epoch - 3ms/step
Epoch 4/10
300/300 - 1s - loss: 0.0502 - accuracy: 0.9859 - val_loss: 0.0664 - val_accuracy: 0.9808 - 904ms/epoch - 3ms/step
Epoch 5/10
300/300 - 1s - loss: 0.0356 - accuracy: 0.9897 - val_loss: 0.0636 - val_accuracy: 0.9803 - 905ms/epoch - 3ms/step
Epoch 6/10
300/300 - 1s - loss: 0.0261 - accuracy: 0.9932 - val_loss: 0.0591 - val_accuracy: 0.9813 - 907ms/epoch - 3ms/step
Epoch 7/10
300/300 - 1s - loss: 0.0195 - accuracy: 0.9953 - val_loss: 0.0564 - val_accuracy: 0.9828 - 910ms/epoch - 3ms/step
Epoch 8/10
300/300 - 1s - loss: 0.0145 - accuracy: 0.9969 - val_loss: 0.0580 - val_accuracy: 0.9810 - 954ms/epoch - 3ms/step
Epoch 9/10
300/300 - 1s - loss: 0.0116 - accuracy: 0.9973 - val_loss: 0.0594 - val_accuracy: 0.9817 - 947ms/epoch - 3ms/step
Epoch 10/10
300/300 - 1s - loss: 0.0079 - accuracy: 0.9985 - val_loss: 0.0735 - val_accuracy: 0.9770 - 914ms/epoch - 3ms/step
Baseline Error: 2.30%

Rede Neural Convolucional Simples para MNIST

Agora que você viu como carregar o conjunto de dados MNIST e treinar um modelo perceptron multicamadas simples nele, é hora de desenvolver uma rede neural convolucional ou modelo CNN mais sofisticado.

Keras oferece muitos recursos para a criação de redes neurais convolucionais.

Nesta seção, você criará uma CNN simples para MNIST que demonstra como usar todos os aspectos de uma implementação moderna de CNN, incluindo camadas convolucionais, camadas de pooling e camadas de dropout.

O primeiro passo é importar as classes e funções necessárias.

from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Dropout
from tensorflow.keras.layers import Flatten
from tensorflow.keras.layers import Conv2D
from tensorflow.keras.layers import MaxPooling2D
from tensorflow.keras.utils import to_categorical
...

Em seguida, você precisa carregar o conjunto de dados MNIST e remodelá-lo para ser adequado ao treinamento de uma CNN. No Keras, as camadas usadas para convoluções bidimensionais esperam valores de pixel com as dimensões [pixels][largura][altura][canais].

Observe que você está forçando a chamada última ordenação dos canais para obter consistência neste exemplo.

No caso do RGB, os pixels da última dimensão seriam 3 para os componentes vermelho, verde e azul, e seria como ter três entradas de imagem para cada imagem colorida. No caso do MNIST, onde os valores dos pixels estão em tons de cinza, a dimensão do pixel é definida como 1.

...
# load data
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# reshape to be [samples][width][height][channels]
X_train = X_train.reshape(X_train.shape[0], 28, 28, 1).astype('float32')
X_test = X_test.reshape(X_test.shape[0], 28, 28, 1).astype('float32')

Como antes, é uma boa ideia normalizar os valores dos pixels para o intervalo 0 e 1 e codificar one-hot as variáveis de saída.

...
# normalize inputs from 0-255 to 0-1
X_train = X_train / 255
X_test = X_test / 255
# one hot encode outputs
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
num_classes = y_test.shape[1]

A seguir, defina seu modelo de rede neural.

As redes neurais convolucionais são mais complexas do que os perceptrons multicamadas padrão, então você começará usando uma estrutura simples que usa todos os elementos para obter resultados de última geração. Abaixo resume a arquitetura da rede.

  1. A primeira camada oculta é uma camada convolucional chamada Convolution2D. A camada possui 32 mapas de características, com tamanho 5×5 e função de ativação do retificador. Esta é a camada de entrada que espera imagens com a estrutura descrita acima: [pixels][largura][altura].
  2. A seguir, defina uma camada de pooling que leva o máximo, chamada MaxPooling2D. Está configurado com um tamanho de pool de 2×2.
  3. A próxima camada é uma camada de regularização usando dropout chamada Dropout. Ele é configurado para excluir aleatoriamente 20% dos neurônios na camada para reduzir o sobreajuste.
  4. A seguir está uma camada que converte os dados da matriz 2D em um vetor chamado Flatten. Ele permite que a saída seja processada por camadas padrão totalmente conectadas.
  5. A seguir está uma camada totalmente conectada com 128 neurônios e uma função de ativação do retificador.
  6. Finalmente, a camada de saída possui dez neurônios para as dez classes e uma função de ativação softmax para gerar previsões semelhantes a probabilidades para cada classe.

Como antes, o modelo é treinado usando perda logarítmica e o algoritmo ADAM gradiente descendente.

...
def baseline_model():
	# create model
	model = Sequential()
	model.add(Conv2D(32, (5, 5), input_shape=(28, 28, 1), activation='relu'))
	model.add(MaxPooling2D(pool_size=(2, 2)))
	model.add(Dropout(0.2))
	model.add(Flatten())
	model.add(Dense(128, activation='relu'))
	model.add(Dense(num_classes, activation='softmax'))
	# Compile model
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	return model

Você avalia o modelo da mesma forma que antes com o perceptron multicamadas. A CNN cabe em dez épocas com um tamanho de lote de 200.

...
# build the model
model = baseline_model()
# Fit the model
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=200, verbose=2)
# Final evaluation of the model
scores = model.evaluate(X_test, y_test, verbose=0)
print("CNN Error: %.2f%%" % (100-scores[1]*100))

Depois de juntar tudo isso, o exemplo completo está listado abaixo.

# Simple CNN for the MNIST Dataset
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Dropout
from tensorflow.keras.layers import Flatten
from tensorflow.keras.layers import Conv2D
from tensorflow.keras.layers import MaxPooling2D
from tensorflow.keras.utils import to_categorical
# load data
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# reshape to be [samples][width][height][channels]
X_train = X_train.reshape((X_train.shape[0], 28, 28, 1)).astype('float32')
X_test = X_test.reshape((X_test.shape[0], 28, 28, 1)).astype('float32')
# normalize inputs from 0-255 to 0-1
X_train = X_train / 255
X_test = X_test / 255
# one hot encode outputs
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
num_classes = y_test.shape[1]
# define a simple CNN model
def baseline_model():
	# create model
	model = Sequential()
	model.add(Conv2D(32, (5, 5), input_shape=(28, 28, 1), activation='relu'))
	model.add(MaxPooling2D())
	model.add(Dropout(0.2))
	model.add(Flatten())
	model.add(Dense(128, activation='relu'))
	model.add(Dense(num_classes, activation='softmax'))
	# Compile model
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	return model
# build the model
model = baseline_model()
# Fit the model
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=200)
# Final evaluation of the model
scores = model.evaluate(X_test, y_test, verbose=0)
print("CNN Error: %.2f%%" % (100-scores[1]*100))

Após a execução do exemplo, é impressa a precisão do teste de treinamento e validação para cada época e, ao final, é impressa a taxa de erro de classificação.

Observação: seus resultados podem variar de acordo com a natureza estocástica do algoritmo ou procedimento de avaliação, ou com diferenças na precisão numérica. Considere executar o exemplo algumas vezes e compare o resultado médio.

As épocas podem levar cerca de 45 segundos para serem executadas na GPU (por exemplo, na AWS). Você pode ver que a rede atinge uma taxa de erro de 1,19%, o que é melhor do que nosso modelo perceptron multicamadas simples acima.

Epoch 1/10
300/300 [==============================] - 4s 12ms/step - loss: 0.2372 - accuracy: 0.9344 - val_loss: 0.0715 - val_accuracy: 0.9787
Epoch 2/10
300/300 [==============================] - 4s 13ms/step - loss: 0.0697 - accuracy: 0.9786 - val_loss: 0.0461 - val_accuracy: 0.9858
Epoch 3/10
300/300 [==============================] - 4s 13ms/step - loss: 0.0483 - accuracy: 0.9854 - val_loss: 0.0392 - val_accuracy: 0.9867
Epoch 4/10
300/300 [==============================] - 4s 13ms/step - loss: 0.0366 - accuracy: 0.9887 - val_loss: 0.0357 - val_accuracy: 0.9889
Epoch 5/10
300/300 [==============================] - 4s 14ms/step - loss: 0.0300 - accuracy: 0.9909 - val_loss: 0.0360 - val_accuracy: 0.9873
Epoch 6/10
300/300 [==============================] - 4s 14ms/step - loss: 0.0241 - accuracy: 0.9927 - val_loss: 0.0325 - val_accuracy: 0.9890
Epoch 7/10
300/300 [==============================] - 4s 14ms/step - loss: 0.0210 - accuracy: 0.9932 - val_loss: 0.0314 - val_accuracy: 0.9898
Epoch 8/10
300/300 [==============================] - 4s 14ms/step - loss: 0.0167 - accuracy: 0.9945 - val_loss: 0.0306 - val_accuracy: 0.9898
Epoch 9/10
300/300 [==============================] - 4s 14ms/step - loss: 0.0142 - accuracy: 0.9956 - val_loss: 0.0326 - val_accuracy: 0.9892
Epoch 10/10
300/300 [==============================] - 4s 14ms/step - loss: 0.0114 - accuracy: 0.9966 - val_loss: 0.0322 - val_accuracy: 0.9881
CNN Error: 1.19%

Rede Neural Convolucional Maior para MNIST

Agora que você viu como criar uma CNN simples, vamos dar uma olhada em um modelo capaz de obter resultados próximos do que há de mais moderno.

Você importará as classes e funções e, em seguida, carregará e preparará os dados da mesma forma que no exemplo anterior da CNN.

# Larger CNN for the MNIST Dataset
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Dropout
from tensorflow.keras.layers import Flatten
from tensorflow.keras.layers import Conv2D
from tensorflow.keras.layers import MaxPooling2D
from tensorflow.keras.utils import to_categorical
# load data
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# reshape to be [samples][width][height][channels]
X_train = X_train.reshape((X_train.shape[0], 28, 28, 1)).astype('float32')
X_test = X_test.reshape((X_test.shape[0], 28, 28, 1)).astype('float32')
# normalize inputs from 0-255 to 0-1
X_train = X_train / 255
X_test = X_test / 255
# one hot encode outputs
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
num_classes = y_test.shape[1]
...

Desta vez, você definirá uma grande arquitetura CNN com camadas convolucionais adicionais de pooling máximo e camadas totalmente conectadas. A topologia da rede pode ser resumida da seguinte forma:

  1. Camada convolucional com 30 mapas de características de tamanho 5×5
  2. Camada de pooling atingindo no máximo 2*2 patches
  3. Camada convolucional com 15 mapas de características de tamanho 3×3
  4. Camada de pooling atingindo no máximo 2*2 patches
  5. Camada de abandono com probabilidade de 20%
  6. Achatar camada
  7. Camada totalmente conectada com 128 neurônios e ativação do retificador
  8. Camada totalmente conectada com 50 neurônios e ativação do retificador
  9. Camada de saída
...
# define the larger model
def larger_model():
	# create model
	model = Sequential()
	model.add(Conv2D(30, (5, 5), input_shape=(28, 28, 1), activation='relu'))
	model.add(MaxPooling2D(pool_size=(2, 2)))
	model.add(Conv2D(15, (3, 3), activation='relu'))
	model.add(MaxPooling2D(pool_size=(2, 2)))
	model.add(Dropout(0.2))
	model.add(Flatten())
	model.add(Dense(128, activation='relu'))
	model.add(Dense(50, activation='relu'))
	model.add(Dense(num_classes, activation='softmax'))
	# Compile model
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	return model

Como nos dois experimentos anteriores, o modelo se ajusta a dez épocas com um tamanho de lote de 200.

...
# build the model
model = larger_model()
# Fit the model
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=200)
# Final evaluation of the model
scores = model.evaluate(X_test, y_test, verbose=0)
print("Large CNN Error: %.2f%%" % (100-scores[1]*100))

Depois de juntar tudo isso, o exemplo completo está listado abaixo.

# Larger CNN for the MNIST Dataset
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Dropout
from tensorflow.keras.layers import Flatten
from tensorflow.keras.layers import Conv2D
from tensorflow.keras.layers import MaxPooling2D
from tensorflow.keras.utils import to_categorical
# load data
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# reshape to be [samples][width][height][channels]
X_train = X_train.reshape((X_train.shape[0], 28, 28, 1)).astype('float32')
X_test = X_test.reshape((X_test.shape[0], 28, 28, 1)).astype('float32')
# normalize inputs from 0-255 to 0-1
X_train = X_train / 255
X_test = X_test / 255
# one hot encode outputs
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
num_classes = y_test.shape[1]
# define the larger model
def larger_model():
	# create model
	model = Sequential()
	model.add(Conv2D(30, (5, 5), input_shape=(28, 28, 1), activation='relu'))
	model.add(MaxPooling2D())
	model.add(Conv2D(15, (3, 3), activation='relu'))
	model.add(MaxPooling2D())
	model.add(Dropout(0.2))
	model.add(Flatten())
	model.add(Dense(128, activation='relu'))
	model.add(Dense(50, activation='relu'))
	model.add(Dense(num_classes, activation='softmax'))
	# Compile model
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	return model
# build the model
model = larger_model()
# Fit the model
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=200)
# Final evaluation of the model
scores = model.evaluate(X_test, y_test, verbose=0)
print("Large CNN Error: %.2f%%" % (100-scores[1]*100))

A execução do exemplo imprime precisão nos conjuntos de dados de treinamento e validação de cada época e uma taxa de erro de classificação final.

Observação: seus resultados podem variar de acordo com a natureza estocástica do algoritmo ou procedimento de avaliação, ou com diferenças na precisão numérica. Considere executar o exemplo algumas vezes e compare o resultado médio.

O modelo leva cerca de 100 segundos para ser executado por época. Este modelo um pouco maior atinge uma taxa de erro de classificação respeitável de 0,83%.

Epoch 1/10
300/300 [==============================] - 4s 14ms/step - loss: 0.4104 - accuracy: 0.8727 - val_loss: 0.0870 - val_accuracy: 0.9732
Epoch 2/10
300/300 [==============================] - 5s 15ms/step - loss: 0.1062 - accuracy: 0.9669 - val_loss: 0.0601 - val_accuracy: 0.9804
Epoch 3/10
300/300 [==============================] - 4s 14ms/step - loss: 0.0771 - accuracy: 0.9765 - val_loss: 0.0555 - val_accuracy: 0.9803
Epoch 4/10
300/300 [==============================] - 4s 14ms/step - loss: 0.0624 - accuracy: 0.9812 - val_loss: 0.0393 - val_accuracy: 0.9878
Epoch 5/10
300/300 [==============================] - 4s 15ms/step - loss: 0.0521 - accuracy: 0.9838 - val_loss: 0.0333 - val_accuracy: 0.9892
Epoch 6/10
300/300 [==============================] - 4s 15ms/step - loss: 0.0453 - accuracy: 0.9861 - val_loss: 0.0280 - val_accuracy: 0.9907
Epoch 7/10
300/300 [==============================] - 4s 14ms/step - loss: 0.0415 - accuracy: 0.9866 - val_loss: 0.0322 - val_accuracy: 0.9905
Epoch 8/10
300/300 [==============================] - 4s 14ms/step - loss: 0.0376 - accuracy: 0.9879 - val_loss: 0.0288 - val_accuracy: 0.9906
Epoch 9/10
300/300 [==============================] - 4s 14ms/step - loss: 0.0327 - accuracy: 0.9895 - val_loss: 0.0245 - val_accuracy: 0.9925
Epoch 10/10
300/300 [==============================] - 4s 15ms/step - loss: 0.0294 - accuracy: 0.9904 - val_loss: 0.0279 - val_accuracy: 0.9910
Large CNN Error: 0.90%

Esta não é uma topologia de rede otimizada. Nem é uma reprodução de uma topologia de rede de um artigo recente. Há muitas oportunidades para você ajustar e melhorar este modelo.

Qual é a melhor pontuação de taxa de erro que você pode alcançar?

Poste sua configuração e melhor pontuação nos comentários.

Recursos no MNIST

O conjunto de dados MNIST é muito bem estudado. Abaixo estão alguns recursos adicionais que você pode querer examinar.

  • A página oficial do conjunto de dados MNIST
  • Página de Rodrigo Benenson que lista resultados do estado da arte
  • Competição Kaggle que usa este conjunto de dados (verifique os scripts e as seções do fórum para obter exemplos de código)
  • Modelo somente leitura treinado no MNIST que você pode testar em seu navegador (muito legal)

Resumo

Neste post você descobriu o problema de reconhecimento de dígitos manuscritos MNIST e modelos de aprendizado profundo desenvolvidos em Python usando a biblioteca Keras que são capazes de alcançar excelentes resultados.

Trabalhando neste tutorial, você aprendeu:

  • Como carregar o conjunto de dados MNIST em Keras e gerar gráficos do conjunto de dados
  • Como remodelar o conjunto de dados MNIST e desenvolver um modelo perceptron multicamadas simples, mas com bom desempenho sobre o problema
  • Como usar Keras para criar modelos de redes neurais convolucionais para MNIST
  • Como desenvolver e avaliar modelos CNN maiores para MNIST, capazes de resultados próximos de classe mundial.

Você tem alguma dúvida sobre reconhecimento de caligrafia com aprendizado profundo ou sobre esta postagem? Faça sua pergunta nos comentários e farei o possível para responder.

Artigos relacionados