В данном материале предлагается, приложив небольшие усилия, соединить python 3.7+flask+tensorflow 2.0+keras+небольшие вкрапления js и вывести на web-страницу определенный интерактив. Пользователь, рисуя на холсте, будет отправлять на распознавание цифры, а ранее обученная модель, использующая архитектуру CNN, будет распознавать полученный рисунок и выводить результат. Модель обучена на известном наборе рукописных цифр MNIST, поэтому и распознавать будет только цифры от 0 до 9 включительно. В качестве системы, на которой все это будет крутиться, используется windows 7.
Небольшое вступление
.Чем печальны книги по машинному обучению, так, пожалуй, тем, что код устаревает почти с выходом самой книги. И хорошо, если автор издания поддерживает свое дитя, сопровождая и обновляя код, но, зачастую все ограничивается тем, что пишут вот вам requirements.txt, ставьте устаревшие пакеты, и все заработает.
Так вышло и в этот раз. Читая Hands-On Python Deep Learning for the Web авторства Anubhav Singh, Sayak Paul, сначала все шло хорошо. Однако, после первой главы праздник закончился. Самое неприятное было то, что заявленные требования в requirements в целом соблюдались.
Масло в огонь подлили и сами разработчики пакетов tensorflow и keras. Один пакет работает только с определенным другим и, либо даунгрейд одного из них либо бубен шамана.
Но и это еще не все. Оказывается, что некоторые пакеты еще и зависимы от архитектуры используемого железа!
Так, за неимением алтернативы железа, устанавливался tensorflow 2.0 на платформу с Celeron j1900 и, как оказалось, там нет инструкции AVX2:
И вариант через pip install tensorflow не работал.
Но не все так грустно при наличии желания и интернета!
Вариант с tensorflow 2.0 удалось реализовать через wheel github.com/fo40225/tensorflow-windows-wheel/tree/master/2.0.0/py37/CPU/sse2 и установку x86: vc_redist.x86.exe, x64: vc_redist.x64.exe (http://personeltest.ru/aways/support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads).
Keras был установлен с минимальной версией, с которой он стал совместим с tensorflow Keras==2.3.0.
Поэтому
pip install tensorflow-2.0.0-cp37-cp37m-win_amd64.whl
и
pip install keras==2.3.0
Основное приложение.
Рассмотрим код основной программы.
#code work with scipy==1.6.1, tensorflow @ file:///D:/python64/tensorflow-2.0.0-cp37-cp37m-win_amd64.whl,#Keras==2.3.0from flask import Flask, render_template, requestimport imageio#https://imageio.readthedocs.io/en/stable/examples.html#from scipy.misc import imread, imresize#from matplotlib.pyplot import imreadimport numpy as npimport tensorflow as tffrom tensorflow.keras.models import model_from_jsonfrom skimage import transform,iojson_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 reimport base64def 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)
Подгрузили пакеты:
from flask import Flask, render_template, requestimport imageio#https://imageio.readthedocs.io/en/stable/examples.html#from scipy.misc import imread, imresize#from matplotlib.pyplot import imreadimport numpy as npimport tensorflow as tffrom tensorflow.keras.models import model_from_jsonfrom skimage import transform,io
Как выяснилось imread, imresize устарели еще со времен scipy==1.0. Непонятно, как у автора все работало, учитывая, что книга относительно нова (2019). С современной scipy==1.6.1 книжный вариант кода не работал.
Загружаем с диска, компилируем модель нейросети:
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()
Здесь произведена замена на tf.compat.v1.get_default_graph() в виду несовместимости.
Далее часть, относящаяся к серверу на flask. Прорисовка шаблона страницы:
@app.route('/')def index(): return render_template("index.html")
Часть, преобразующая картинку в числовой массив:
import reimport base64def convertImage(imgData1): imgstr = re.search(r'base64,(.*)', str(imgData1)).group(1) with open('output.png', 'wb') as output: output.write(base64.b64decode(imgstr))
Основная функция предсказания:
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])
Закоментированы строки, которые были заменены на рабочие, а также оставлены выводы отдельных строк для наглядности.
Как все работает.
После запуска командой python flask_app.py запускается локальный flask-сервер, который выводит index.html с вкраплением js.
Пользователь рисует на холсте цифру, нажимает predict. Картинка улетает на сервер, где сохраняется и преобразуется в цифровой массив. Далее в бой вступает CNN, распознающая цифру и возвращающая ответ в виде цифры.
Сеть не всегда дает верный ответ, т.к. обучалась всего на 10 эпохах. Это можно наблюдать, если нарисовать спорную цифру, которая может трактоваться по-разному.
*Можно покрутить слайдер, увеличивая или уменьшая толщину начертания цифры для целей распознавания.
Второй вариант программы через API,curl
.Поользователь загружает на сервер свое изображение с цифрой для распознавания и нажимает отправить:
Заменим 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;});
Шаблон страницы также изменится:
<!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><scriptsrc='https://code.jquery.com/jquery-3.6.0.min.js'></script><script src="{{ url_for('static',filename='index.js') }}"></script></body></html>
Немного изменится и основная программа:
#code work with scipy==1.6.1, tensorflow @ file:///D:/python64/tensorflow-2.0.0-cp37-cp37m-win_amd64.whl,#Keras==2.3.0from flask import Flask, render_template, requestimport imageio#https://imageio.readthedocs.io/en/stable/examples.html#from scipy.misc import imread, imresize#from matplotlib.pyplot import imreadimport numpy as npimport tensorflow as tffrom tensorflow.keras.models import model_from_jsonfrom skimage import transform,iojson_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 reimport base64def 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)
Запускается все похоже python flask_app2.py
Вариант с curl (для windows)
.Скачиваем curl
В командной строке windows отправляем команду:
curl -X POST -F img=@1.png http://localhost/predict/
где 1.png картинка с цифрой (или она же с путем к ней).
В ответ прилетит распознанная цифра.
Файлы для скачивания скачать.