Formatando os dados para o TensorFlow
A Parte 1 desta série de blogs demonstrou as vantagens de usar um banco de dados relacional para armazenar e realizar a exploração de dados de imagens usando instruções SQL simples. Neste tutorial, parte 2, os dados usados na primeira parte serão acessados de um banco de dados MariaDB Server e convertidos nas estruturas de dados necessárias ao TensorFlow. Os resultados da aplicação do modelo para classificar novas imagens serão armazenados em uma tabela relacional para posterior análise.
Este é um tutorial rápido de um programa TensorFlow com os detalhes descritos à medida que avançamos. Se você não estiver familiarizado com os conceitos básicos, um bom lugar para começar é este tutorial do TensorFlow, “Classificação básica:classificar imagens de roupas“. Alguns dos exemplos e código no tutorial são usados aqui.
Pacotes adicionais necessários
Alguns pacotes adicionais são necessários para construir e treinar o modelo de classificação de imagem:
- Picolé implementa protocolos binários para serializar e desserializar uma estrutura de objeto Python.
- NumPy fornece suporte para matrizes e matrizes grandes e multidimensionais, juntamente com funções matemáticas de alto nível para operar nessas matrizes.
- TensorFlow é uma biblioteca Python para computação numérica rápida. É uma biblioteca básica que pode ser usada para criar modelos de Deep Learning diretamente ou usando bibliotecas de wrapper que simplificam o processo criado com base no TensorFlow.
- Keras é uma biblioteca de rede neural de código aberto escrita em Python.
import pickleimport numpy as npimport tensorflow as tffrom tensorflow import kerasprint('Tensorflow version:', tf.__version__)print('Numpy version:', np.__version__)Tensorflow version:2.0.0Numpy version:1.16.2Recuperar Imagens
Depois que os pacotes forem importados, a próxima etapa é recuperar as imagens de treinamento do banco de dados e dividir os dados em dois numpy matrizes. Primeiro, precisamos inicializar os arrays de imagens de treinamento (train_images) e de rótulos de treinamento (train_labels). Como já vetorizamos as imagens, podemos usar o atributo img_vector para preencher o array train_images com a instrução SQL abaixo.
# Inicialize o numpy arraystrain_images =np.empty((60000,28,28), dtype='uint8')train_labels =np.empty((60000), dtype='uint8')# Recupere as imagens de treinamento do databasesql="SELECT img_label, img_vector, img_idx \FROM tf_images INNER JOIN img_use ON img_use =use_id \WHERE use_name ='Treinamento'"cur.execute(sql)result =cur.fetchall()# Preencha os arrays numpy. row[2] contém o índice de imagem para a linha no resultado:nparray =pickle.loads(row[1])train_images[row[2]] =nparraytrain_labels[row[2]] =row[0]
De maneira semelhante, as imagens para teste podem ser recuperadas do banco de dados. O numpy arrays usados neste caso são test_images e test_labels. Nesse caso, os dados de teste são 10.000 imagens com resolução de 28×28 pixels.
# Inicialize o numpy arraystest_images =np.empty((10000,28,28), dtype='uint8')test_labels =np.empty((10000), dtype='uint8')# Recupere as imagens de teste do databasesql="SELECT img_label, img_vector, img_idx \FROM tf_images INNER JOIN img_use ON img_use =use_id \WHERE use_name ='Teste'"cur.execute(sql)result =cur.fetchall()# Preencha os arrays numpy. row[2] contém o índice de imagem para a linha no resultado:nparray =pickle.loads(row[1])test_images[row[2]] =nparraytest_labels[row[2]] =row[0]
Finalmente, cada imagem é mapeada para um único rótulo. Os nomes dos rótulos são armazenados na tabela de categorias e carregados no array class_names:
sql="SELECT class_name FROM Categories"cur.execute(sql)class_names =cur.fetchall()Pré-processar os dados
Os dados devem ser pré-processados antes de treinar a rede. Se você inspecionar a primeira imagem no conjunto de treinamento, verá que os valores de pixel ficam no intervalo de 0 a 255:
plt.figure()plt.imshow(train_images[0])plt.colorbar()plt.grid(False)plt.show()
acima:imagem do conjunto de dados fashion_mnist
Antes de alimentar as imagens no modelo de rede neural, os valores precisam ser dimensionados para um intervalo de 0 a 1. Para isso, divida os valores por 255. É importante que o conjunto de treinamento e o conjunto de teste sejam pré-processados da mesma maneira .
Você pode usar matplotlib para exibir as primeiras 25 imagens para verificar se os dados estão no formato correto e prontos para construir e treinar a rede:
train_images =train_images / 255.0test_images =test_images / 255.0plt.figure(figsize=(10,10))for i in range(25):plt.subplot(5,5,i+1)plt.xticks([ ])plt.yticks([])plt.grid(False)plt.imshow(train_images[i], cmap=plt.cm.binary)plt.xlabel(class_names[train_labels[i]])plt.show()
acima:imagens do conjunto de dados fashion_mnist
Construindo o modelo
Depois que os dados forem pré-processados em dois subconjuntos, você poderá prosseguir com um treinamento de modelo. Esse processo envolve “alimentar” o algoritmo com dados de treinamento. O algoritmo processará os dados e produzirá um modelo capaz de encontrar um valor de destino (atributo) em novos dados — ou seja, classificar a imagem apresentada à rede neural.
A maioria das redes neurais de aprendizado profundo são produzidas encadeando camadas simples.
A primeira camada da rede transforma o formato da imagem de um array bidimensional (de 28 por 28 pixels) para um array unidimensional (de 28 * 28 =784 pixels). Esta camada não tem parâmetros para aprender; apenas reformata os dados.
Depois que os pixels são achatados, a rede consiste em duas camadas totalmente conectadas que precisam ser ativadas. Em uma rede neural, a função de ativação é responsável por transformar a entrada ponderada somada do nó na ativação do nó ou saída para aquela entrada.
A primeira camada densa tem 128 nós (ou neurônios) e está usando um método de ativação de Unidade Linear Retificada (ReLU). A função de ativação linear retificada é uma função linear por partes que produzirá a entrada diretamente se for positiva, caso contrário, a saída será zero.
A segunda (e última) camada é uma camada softmax de 10 nós. Uma função softmax gera um vetor que representa as distribuições de probabilidade de uma lista de resultados potenciais. Ele retorna uma matriz de 10 pontuações de probabilidade que somam 1. Cada nó contém uma pontuação que indica a probabilidade de a imagem atual pertencer a uma das 10 classes.
A maioria das camadas, como tf.keras.layers.Dense, possui parâmetros que são aprendidos durante o treinamento.
modelo =keras.Sequential([keras.layers.Flatten(input_shape=(28, 28)),keras.layers.Dense(128, ativação='relu'),keras.layers.Dense(10, ativação='softmax')])Compilando o modelo
A etapa de compilação do modelo é usada para adicionar mais algumas configurações antes de estar pronto para o treinamento. Nesse caso, as configurações a seguir são habilitadas.
- Otimizador:atualiza o modelo com base nos dados que ele vê e na função de perda (veja abaixo).
- Função de perda — mede a precisão do modelo durante o treinamento. Você deseja minimizar essa função para "orientar" o modelo na direção certa.
- Métricas—Monitore as etapas de treinamento e teste. O exemplo a seguir usa precisão, a fração das imagens classificadas corretamente.
model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy'])Treinando o modelo
O treinamento do modelo de rede neural requer as etapas a seguir.
- Alimente os dados de treinamento ao modelo.
- O modelo aprende a associar imagens e rótulos.
- Faça previsões sobre um conjunto de testes.
- Verifique se as previsões correspondem aos rótulos da matriz test_labels.
Para iniciar o treinamento, chame o método model.fit—assim chamado porque “ajusta” o modelo aos dados de treinamento:
model.fit(train_images, train_labels, epochs=10)Treine em 60.000 amostrasEpoch 1/1060000/60000 [===============================] - 5s 83us/amostra - perda:0,4964 - precisão:0,8236Epoch 2/1060000/60000 [===============================] - 4s 65us/amostra - perda:0,3735 - precisão:0,8642Epoch 3/1060000/60000 [===============================] - 3s 55us/amostra - perda:0,3347 - precisão:0,8773Epoch 4/1060000/60000 [===============================] - 3s 56us/amostra - perda:0,3106 - precisão:0,8861Epoch 5/1060000/60000 [===============================] - 3s 58us/amostra - perda:0,2921 - precisão:0,8924s - perda:0,2928 - precisão - ETA:0s - perda:0,2925 - precisãoEpoch 6/1060000/60000 [==============================] - 3s 57us/amostra - perda:0,2796 - precisão:0,8969sEpoch 7/1060000/60000 [==============================] - 4s 70us/amostra - perda:0,2659 - precisão:0,9007Epoch 8/1060000/60000 [==============================] - 4s 61us/amostra - perda:0,2548 - precisão:0,9042Epoch 9/1060000/60000 [ ==============================] - 4s 61us/amostra - perda:0,2449 - precisão:0,9084Epoch 10/1060000/60000 [==============================] - 5s 76us/amostra - perda:0,2358 - precisão:0,9118
No final de cada época, a rede neural é avaliada em relação ao conjunto de validação. É a isso que se referem a perda e a precisão.
Avaliar a precisão e prever
Para estimar a precisão geral do modelo, calcule a média de todas as dez ocorrências do valor de precisão, neste caso 88%.
Em seguida, execute model.evaluate no conjunto de teste para obter a precisão preditiva da rede neural treinada em dados não vistos anteriormente.
test_loss, test_acc =model.evaluate(test_images, test_labels, verbose=2)10000/1 - 0s - perda:0,2766 - precisão:0,8740
O conjunto de dados de teste é menos preciso que o conjunto de dados de treinamento. Nesse caso, essa lacuna entre a precisão do treinamento e a precisão do teste representa o overfitting. O oposto é o underfitting. Se você quiser aprender mais sobre este tópico, eu recomendo Overfitting vs. Underfitting:A Conceptual Explanation por Will Koehrsen.
Neste ponto, podemos fazer algumas previsões sobre as imagens em nosso conjunto de dados de treinamento.
previsões =model.predict(test_images)predictions[0]array([1.90860412e-08, 8.05085235e-11, 1.56402713e-08, 1.66699390e-10,7.86950158e-11, 4.33062996e-06, 2.4904906e -08, 1.20656565e-02,3.80084719e-09, 9.87929940e-01], dtype=float32)
A saída de model.predict é uma matriz de 10 números com a probabilidade de uma instância pertencer a cada classe. Persistir os resultados no banco de dados MariaDB para análises e relatórios adicionais é uma boa ideia. Abaixo está um exemplo de como iterar na matriz de previsões para criar uma tupla e inseri-la em prediction_results tabela.
sql ="INSERT INTO forecast_results (img_idx, img_use, T_shirt_Top, Calça, Pulôver, Vestido, Casaco, Sandália, Camisa, Tênis, Bolsa, Bota de tornozelo, etiqueta)VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s);"i =0for linha em previsões:insert_tuple =(str(i), str(2), str (linha[0]), str(linha[1]), str(linha[2]), str(linha[3]), str(linha[4]), str(linha[5]), str(linha [6]), str(linha[7]), str(linha[8]), str(linha[9]), str(test_labels[i]))cur.execute(sql, insert_tuple)conn.commit() i +=1
Mais uma vez, uma simples instrução SQL pode ser usada para verificar se os dados foram carregados.
sql ="SELECT T_shirt_Top, Calça, Pulôver, Vestido, Casaco, Sandália, Camisa, Tênis, Bolsa, Ankle_boot, class_name como 'Test Label'FROM forecast_results JOIN Categories ON label =class_idx WHERE img_idx =1"display( pd. read_sql(sql,conn) )
T_shirt_Top | Calças | Suéter | Vestido | Casaco | Sandália | Camisa | Tênis | Bolsa | Ankle_boot | Etiqueta de teste |
0,00001 | 0,0 | 0,997912 | 0,0 | 0,001267 | 0,0 | 0,00081 | 0,0 | 0,0 | 0,0 | Suéter |
Previsões de plotagem
Algumas funções de plotagem para exibir as previsões são definidas abaixo (Funções gráficas).
Vamos recuperar uma nova imagem do conjunto de testes e exibir a classificação da rede neural com base na probabilidade de previsão.
sql ="SELECT img_idx, label FROM forecast_results WHERE img_idx =1"cur.execute(sql)result =cur.fetchone()plt.figure(figsize=(6,3))plt.subplot(1,2, 1)plot_image(result[0], previsões[resultado[0]], test_labels, test_images)plt.subplot(1,2,2)plot_value_array(result[0], previsões[resultado[0]], test_labels)plt. mostrar()
acima:imagem do conjunto de dados fashion_mnist
Neste caso, o modelo foi capaz de classificar a imagem corretamente com 100% de precisão. Em seguida, vamos executar uma consulta para recuperar as primeiras 15 imagens do conjunto de testes e classificá-las.
sql ="SELECT img_idx, label FROM forecast_results LIMIT 15"num_rows =5num_cols =3plt.figure(figsize=(2*2*num_cols, 2*num_rows))cur.execute(sql)result =cur.fetchall() para linha no resultado:plt.subplot(num_rows, 2*num_cols, 2*row[0]+1)plot_image(row[0], previsões[row[0]], test_labels, test_images)plt.subplot(num_rows, 2 *num_cols, 2*row[0]+2)plot_value_array(row[0], previsões[row[0]], test_labels)plt.tight_layout()plt.show()
acima:imagens do conjunto de dados fashion_mnist
Como você pode ver, haverá casos em que o modelo pode estar errado, conforme mostrado na última linha, coluna esquerda. Nesse caso, um tênis foi classificado como sandália (em vermelho).
Em resumo
Embora a integração entre o TensorFlow e o MariaDB Server seja fácil, os benefícios dessa integração são substanciais:
- O uso de dados relacionais no aprendizado de máquina pode reduzir a complexidade da implementação. Cientistas de dados e engenheiros de dados podem usar uma linguagem comum para realizar tarefas de exploração e manipulação de dados.
- A eficiência obtida ao acessar, atualizar, inserir, manipular e modificar dados pode acelerar o tempo de lançamento no mercado.
- A capacidade de armazenar os resultados do modelo de volta no banco de dados permite que usuários finais e analistas executem consultas e relatórios usando ferramentas de relatórios amigáveis, como o Tableau.
Licença MIT
O conjunto de dados Fashion MNIST (fashion_mnist) aproveitado por este blog está licenciado sob a Licença MIT, Copyright © 2017 Zalando SE, https://tech.zalando.com
O código-fonte aproveitado por este blog é adaptado do tutorial “Classificação básica:Classificar imagens de roupas”, licenciado sob a Licença MIT, Copyright (c) 2017 François Chollet.
A permissão é concedida, gratuitamente, a qualquer pessoa que obtenha uma cópia deste software e dos arquivos de documentação associados (o “Software”), para lidar com o Software sem restrições, incluindo, sem limitação, os direitos de usar, copiar, modificar, mesclar , publicar, distribuir, sublicenciar e/ou vender cópias do Software e permitir que as pessoas a quem o Software é fornecido o façam, sujeito às seguintes condições:
O aviso de direitos autorais acima e este aviso de permissão devem ser incluídos em todas as cópias ou partes substanciais do Software.
O SOFTWARE É FORNECIDO “COMO ESTÁ”, SEM GARANTIA DE QUALQUER TIPO, EXPRESSA OU IMPLÍCITA, INCLUINDO MAS NÃO LIMITADO ÀS GARANTIAS DE COMERCIALIZAÇÃO, ADEQUAÇÃO A UM DETERMINADO FIM E NÃO VIOLAÇÃO. EM NENHUMA CIRCUNSTÂNCIA OS AUTORES OU DETENTORES DE DIREITOS AUTORAIS SERÃO RESPONSÁVEIS POR QUALQUER REIVINDICAÇÃO, DANOS OU OUTRA RESPONSABILIDADE, SEJA EM UMA AÇÃO DE CONTRATO, ATO ILÍCITO OU DE OUTRA FORMA, DECORRENTE DE, DE OU EM CONEXÃO COM O SOFTWARE OU O USO OU OUTRAS NEGOCIAÇÕES NO PROGRAMAS.
Referências
Converter a própria imagem para a imagem do MNIST
matplotlib:tutorial de imagem
5 maneiras pelas quais a IA está transformando a experiência do cliente
A digitalização está reinventando os negócios
O que é classificação de imagens?
Introdução ao Python Biblioteca de Deep Learning TensorFlow
Funções gráficas
def plot_image(i, predictions_array, true_label, img):predictions_array, true_label, img =previsions_array, true_label[i], img[i]plt.grid(False)plt.xticks([])plt.yticks([ ])plt.imshow(img, cmap=plt.cm.binary)predicted_label =np.argmax(predictions_array)if predict_label ==true_label:color ='blue'else:color ='red'plt.xlabel("{} { :2.0f}% ({})".format(class_names[predicted_label],100*np.max(predictions_array),class_names[true_label]),color=color)def plot_value_array(i, predictions_array, true_label):predictions_array, true_label =previsão_array, true_label[i]plt.grid(False)plt.xticks(range(10))plt.yticks([])thisplot =plt.bar(range(10), forecasts_array, color="#777777")plt .ylim([0, 1])predicted_label =np.argmax(predictions_array)thisplot[predicted_label].set_color('red')thisplot[true_label].set_color('blue')