Como conectar rapidamente uma rede neural a um site





Neste material, propõe-se, com um pouco de esforço, conectar python 3.7 + flask + tensorflow 2.0 + keras + pequenas inclusões de js e exibir uma certa interatividade na página web. O usuário, desenhando na tela, enviará números para reconhecimento, e o modelo previamente treinado usando a arquitetura CNN reconhecerá o desenho resultante e produzirá o resultado. O modelo é treinado no conhecido conjunto de dígitos manuscritos MNIST, portanto, ele reconhecerá apenas dígitos de 0 a 9, inclusive. O Windows 7 é usado como o sistema no qual tudo isso vai girar.



Pequena introdução



O triste sobre os livros de aprendizado de máquina é que o código se torna desatualizado quase assim que o livro em si é publicado. E é bom se o autor da publicação apoiar seu filho, mantendo e atualizando o código, mas muitas vezes tudo é limitado ao que eles escrevem - aqui está o requirements.txt, instale pacotes desatualizados e tudo funcionará.



Aconteceu desta vez também. Lendo o Aprendizado Profundo de Python Prático para a Web, de Anubhav Singh, Sayak Paul, tudo correu bem no início. No entanto, após o primeiro capítulo, o feriado acabou. O mais desagradável é que os requisitos declarados nos requisitos foram geralmente cumpridos.



Os próprios desenvolvedores dos pacotes tensorflow e keras colocaram lenha na fogueira. Um pacote funciona apenas com um determinado outro e, seja um rebaixamento de um deles ou um pandeiro do xamã.

Mas isso não é tudo. Acontece que alguns pacotes também dependem da arquitetura!



Assim, na ausência de uma alternativa ao iron, tensorflow 2.0 foi instalado em uma plataforma com Celeron j1900 e, como se viu, não há instrução AVX2 lá:





E a opção via pip install tensorflow não funcionou.



Mas nem tudo fica tão triste com o desejo e a internet!



A variante com tensorflow 2.0 foi implementada por meio de roda - github.com/fo40225/tensorflow-windows-wheel/tree/master/2.0.0/py37/CPU/sse2 e instalação x86: vc_redist.x86.exe, x64: vc_redist.x64 .exe (https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads).



O Keras foi instalado com a versão mínima com a qual "se tornou compatível" com o tensorflow - Keras == 2.3.0.



Portanto



pip install tensorflow-2.0.0-cp37-cp37m-win_amd64.whl
      
      





e



pip install keras==2.3.0
      
      





Aplicação principal



Vamos considerar o código do programa principal.



flask_app.py

#code work with scipy==1.6.1, tensorflow @ file:///D:/python64/tensorflow-2.0.0-cp37-cp37m-win_amd64.whl,
#Keras==2.3.0

from flask import Flask, render_template, request
import imageio
#https://imageio.readthedocs.io/en/stable/examples.html
#from scipy.misc import imread, imresize
#from matplotlib.pyplot import imread
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import model_from_json
from skimage import transform,io

json_file = open('model.json','r')
model_json = json_file.read()
json_file.close()
model = model_from_json(model_json)
model.load_weights("weights.h5")
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
#graph = tf.get_default_graph()
graph = tf.compat.v1.get_default_graph()

app = Flask(__name__)

@app.route('/')
def index():
    return render_template("index.html")
import re
import base64

def convertImage(imgData1):
    imgstr = re.search(r'base64,(.*)', str(imgData1)).group(1)
    with open('output.png', 'wb') as output:
        output.write(base64.b64decode(imgstr))

@app.route('/predict/', methods=['GET', 'POST'])
def predict():
    global model, graph
    
    imgData = request.get_data()
    convertImage(imgData)
    #print(imgData)
   
    #x = imread('output.png', mode='L')
    #x.shape
    #(280, 280)
    x = imageio.imread('output.png',pilmode='L')
    #x = imresize(x, (28, 28))
    #x = x.resize(x, (28, 28))
    x = transform.resize(x, (28,28), mode='symmetric', preserve_range=True)
    #(28, 28)
    #type(x)
    #<class 'numpy.ndarray'>

    x = x.reshape(1, 28, 28, 1)
    #(1, 28, 28, 1) 
    x = tf.cast(x, tf.float32)
    
    # perform the prediction
    out = model.predict(x)        
    #print(np.argmax(out, axis=1))
    # convert the response to a string
    response = np.argmax(out, axis=1)
    return str(response[0])

if __name__ == "__main__":
    # run the app locally on the given port
    app.run(host='0.0.0.0', port=80)
# optional if we want to run in debugging mode
    app.run(debug=True)

      
      









Pacotes carregados:


from flask import Flask, render_template, request
import imageio
#https://imageio.readthedocs.io/en/stable/examples.html
#from scipy.misc import imread, imresize
#from matplotlib.pyplot import imread
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import model_from_json
from skimage import transform,io

      
      





Como o imread descobriu, o imresize está obsoleto desde scipy == 1.0. Não está claro como tudo funcionou para o autor, visto que o livro é relativamente novo (2019). Com o scipy moderno == 1.6.1, a versão livre do código não funcionou.



Carregamos do disco e compilamos o modelo de rede neural:




json_file = open('model.json','r')
model_json = json_file.read()
json_file.close()
model = model_from_json(model_json)
model.load_weights("weights.h5")
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
#graph = tf.get_default_graph()
graph = tf.compat.v1.get_default_graph()

      
      





Aqui, nós o substituímos por tf.compat.v1.get_default_graph () devido à incompatibilidade.



A seguir está a parte do servidor do frasco. "Desenho" do template da página:




@app.route('/')
def index():
    return render_template("index.html")

      
      





A parte que converte a imagem em uma matriz numérica:




import re
import base64

def convertImage(imgData1):
    imgstr = re.search(r'base64,(.*)', str(imgData1)).group(1)
    with open('output.png', 'wb') as output:
        output.write(base64.b64decode(imgstr))

      
      





Função de previsão principal:




def predict():
    global model, graph
    
    imgData = request.get_data()
    convertImage(imgData)
    #print(imgData)
   
    #x = imread('output.png', mode='L')
    #x.shape
    #(280, 280)
    x = imageio.imread('output.png',pilmode='L')
    #x = imresize(x, (28, 28))
    #x = x.resize(x, (28, 28))
    x = transform.resize(x, (28,28), mode='symmetric', preserve_range=True)
    #(28, 28)
    #type(x)
    #<class 'numpy.ndarray'>

    x = x.reshape(1, 28, 28, 1)
    #(1, 28, 28, 1) 
    x = tf.cast(x, tf.float32)
    
    # perform the prediction
    out = model.predict(x)        
    #print(np.argmax(out, axis=1))
    # convert the response to a string
    response = np.argmax(out, axis=1)
    return str(response[0])

      
      





As linhas foram comentadas, que foram substituídas por outras funcionais, e as conclusões de linhas separadas também foram deixadas para maior clareza.



Como funciona



Depois de iniciar com o comando python flask_app.py , o servidor flask local é iniciado, exibindo index.html intercalado com js.



O usuário desenha um número na tela, clica em "prever". A imagem "voa" para o servidor, onde é salva e convertida em um array digital. Em seguida, a CNN entra na batalha, reconhecendo o dígito e retornando a resposta na forma de um dígito.



A rede nem sempre dá a resposta correta, porque estudado por apenas 10 eras. Isso pode ser observado se você desenhar uma figura "polêmica", que pode ser interpretada de diferentes maneiras.



* Você pode girar o controle deslizante para aumentar ou diminuir a espessura do contorno do número para fins de reconhecimento.



A segunda versão do programa é via API, curl



O usuário carrega sua imagem com um número para reconhecimento no servidor e clica em "enviar":







Vamos substituir index.js pelo seguinte:



index.js:
$("form").submit(function(evt){
	evt.preventDefault();
	var formData = new FormData($(this)[0]);
	$.ajax({
		url: '/predict/',
		type: 'POST',
		data: formData,
		async: false,
		cache: false,
		contentType: false,
		enctype: 'multipart/form-data',
		processData: false,
		success: function (response) {
			$('#result').empty().append(response);
		}
	});
	return false;
});

      
      







O modelo da página também mudará:



index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>MNIST CNN</title>
</head>
<body>
<h1>MNIST Handwritten Digits Prediction</h1>
<form>
<input type="file" name="img"></input>
<input type="submit"></input>
</form>
<hr>
<h3>Prediction: <span id="result"></span></h3>
<script
src='https://code.jquery.com/jquery-3.6.0.min.js'></script>
<script src="{{ url_for('static',filename='index.js') }}"></script>
</body>
</html>

      
      







O programa principal também mudará ligeiramente:



flask_app2.py

#code work with scipy==1.6.1, tensorflow @ file:///D:/python64/tensorflow-2.0.0-cp37-cp37m-win_amd64.whl,
#Keras==2.3.0

from flask import Flask, render_template, request
import imageio
#https://imageio.readthedocs.io/en/stable/examples.html
#from scipy.misc import imread, imresize
#from matplotlib.pyplot import imread
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import model_from_json
from skimage import transform,io


json_file = open('model.json','r')
model_json = json_file.read()
json_file.close()
model = model_from_json(model_json)
model.load_weights("weights.h5")
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
#graph = tf.get_default_graph()
graph = tf.compat.v1.get_default_graph()

app = Flask(__name__)

@app.route('/')
def index():
    return render_template("index.html")

import re
import base64

def convertImage(imgData1):
    imgstr = re.search(r'base64,(.*)', str(imgData1)).group(1)
    with open('output.png', 'wb') as output:
        output.write(base64.b64decode(imgstr))

@app.route('/predict/', methods=['POST'])
def predict():
    global model, graph
    
    imgData = request.get_data()
    try:
        stringToImage(imgData)
    except:
        f = request.files['img']
        f.save('image.png')
       
    #x = imread('output.png', mode='L')
    #x.shape
    #(280, 280)
    x = imageio.imread('image.png',pilmode='L')
    #x = imresize(x, (28, 28))
    #x = x.resize(x, (28, 28))
    x = transform.resize(x, (28,28), mode='symmetric', preserve_range=True)
    #(28, 28)
    #type(x)
    #<class 'numpy.ndarray'>

    x = x.reshape(1, 28, 28, 1)
    #(1, 28, 28, 1) 
    x = tf.cast(x, tf.float32)
    
    # perform the prediction
    out = model.predict(x)        
    #print(np.argmax(out, axis=1))
    # convert the response to a string
    response = np.argmax(out, axis=1)
    return str(response[0])

if __name__ == "__main__":

    # run the app locally on the given port
    app.run(host='0.0.0.0', port=80)
# optional if we want to run in debugging mode
    app.run(debug=True)


      
      







Tudo começa de forma semelhante - python flask_app2.py



Opção Curl (para janelas)



Baixe o curl



Na linha de comando do Windows, envie o comando:




curl -X POST -F img=@1.png http://localhost/predict/

      
      





onde 1.png é uma imagem com um número (ou um caminho para ela).

Um dígito reconhecido chegará em resposta.



Arquivos para download - download .



All Articles