我们使用Flask作为REST API部署ML项目,并通过Flutter应用程序对其进行访问


引言


机器学习已经无处不在,几乎不可能找到不直​​接或间接使用它的软件。 让我们创建一个小型应用程序,该应用程序可以将图像上传到服务器,以便以后使用ML进行识别。 然后,我们将通过移动应用程序使它们可用,并按内容搜索文本。


我们将Flask用于REST API,将Flutter用于移动应用程序,将Keras用于机器学习。 我们使用MongoDB作为数据库来存储有关图像内容的信息,对于信息,我们采用已经训练有素的ResNet50模型。 如有必要,我们可以使用Keras中可用的save_model()load_model()方法替换模型。 初始加载模型时,后者将需要大约100 MB。 您可以在文档中了解其他可用模型。


让我们从Flask开始


如果您不熟悉Flask,只需在应用程序中添加app .route('/')装饰器到控制器即可创建路由,其中app是应用程序变量。 一个例子:


from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, World!' 

当您启动并转到默认地址127.0.0.1:5000 /时,我们将看到答案Hello World! 您可以在文档中阅读有关如何执行更复杂的操作的信息


让我们开始创建一个完整的后端:


 import os import tensorflow as tf from tensorflow.keras.models import load_model from tensorflow.keras.preprocessing import image as img from keras.preprocessing.image import img_to_array import numpy as np from PIL import Image from keras.applications.resnet50 import ResNet50,decode_predictions,preprocess_input from datetime import datetime import io from flask import Flask,Blueprint,request,render_template,jsonify from modules.dataBase import collection as db 

如您所见,导入包含tensorflow ,我们将其用作keras的后端,以及numpy来处理多维数组。


 mod = Blueprint('backend', __name__, template_folder='templates', static_folder='./static') UPLOAD_URL = 'http://192.168.1.103:5000/static/' model = ResNet50(weights='imagenet') model._make_predict_function() 

在第一行中,我们创建了一个蓝图,以更方便地组织应用程序。 因此,您将需要使用mod .route('/')装饰控制器。 在imagenet上经过预训练的Resnet50模型需要调用_make_predict_function()进行初始化。 如果没有此步骤,就有可能出错。 通过替换生产线可以使用另一种型号


 model = ResNet50(weights='imagenet') 


 model = load_model('saved_model.h5') 

控制器的外观如下:


 @mod.route('/predict', methods=['POST']) def predict(): if request.method == 'POST': # ,    if 'file' not in request.files: return "someting went wrong 1" user_file = request.files['file'] temp = request.files['file'] if user_file.filename == '': return "file name not found ..." else: path = os.path.join(os.getcwd()+'\\modules\\static\\'+user_file.filename) user_file.save(path) classes = identifyImage(path) db.addNewImage( user_file.filename, classes[0][0][1], str(classes[0][0][2]), datetime.now(), UPLOAD_URL+user_file.filename) return jsonify({ "status":"success", "prediction":classes[0][0][1], "confidence":str(classes[0][0][2]), "upload_time":datetime.now() }) 

在上面的代码中,将下载的图像传递给identifyImage(file_path)方法,该方法实现如下:


 def identifyImage(img_path): image = img.load_img(img_path, target_size=(224,224)) x = img_to_array(image) x = np.expand_dims(x, axis=0) # images = np.vstack([x]) x = preprocess_input(x) preds = model.predict(x) preds = decode_predictions(preds, top=1) print(preds) return preds 

首先,我们将图像转换为224 * 224的大小,因为 我们的模型需要的是他。 然后我们传递给model.predict()预处理图像字节。 现在,我们的模型可以预测图像上的内容(需要top = 1以获得单个最可能的结果)。


使用db.addData()函数将收到的有关图像内容的数据保存在MongoDB中。 这是相关的代码:


 from pymongo import MongoClient from bson import ObjectId client = MongoClient("mongodb://localhost:27017") # host uri db = client.image_predition #Select the database image_details = db.imageData def addNewImage(i_name, prediction, conf, time, url): image_details.insert({ "file_name":i_name, "prediction":prediction, "confidence":conf, "upload_time":time, "url":url }) def getAllImages(): data = image_details.find() return data 

由于我们使用了蓝图,因此可以将API的代码放在单独的文件中:


 from flask import Flask,render_template,jsonify,Blueprint mod = Blueprint('api',__name__,template_folder='templates') from modules.dataBase import collection as db from bson.json_util import dumps @mod.route('/') def api(): return dumps(db.getAllImages()) 

如您所见,我们使用json返回数据库数据。 您可以在地址127.0.0.1中查看结果:5000 / api


当然,以上只是最重要的代码。 可以在GitHub存储库中查看整个项目。 有关Pymongo的更多信息可以在这里找到。


我们创建Flutter应用程序


移动版本将通过REST API接收有关其内容的图像和数据。 结果如下:



ImageData类封装图像数据:


 import 'dart:convert'; import 'package:http/http.dart' as http; import 'dart:async'; class ImageData { // static String BASE_URL ='http://192.168.1.103:5000/'; String uri; String prediction; ImageData(this.uri,this.prediction); } Future<List<ImageData>> LoadImages() async { List<ImageData> list; //complete fetch .... var data = await http.get('http://192.168.1.103:5000/api/'); var jsondata = json.decode(data.body); List<ImageData> newslist = []; for (var data in jsondata) { ImageData n = ImageData(data['url'],data['prediction']); newslist.add(n); } return newslist; } 

在这里,我们得到json,将其转换为ImageData对象的列表然后使用LoadImages()函数将其返回给Future Builder。


将图像上传到服务器


 uploadImageToServer(File imageFile) async { print("attempting to connecto server......"); var stream = new http.ByteStream(DelegatingStream.typed(imageFile.openRead())); var length = await imageFile.length(); print(length); var uri = Uri.parse('http://192.168.1.103:5000/predict'); print("connection established."); var request = new http.MultipartRequest("POST", uri); var multipartFile = new http.MultipartFile('file', stream, length, filename: basename(imageFile.path)); //contentType: new MediaType('image', 'png')); request.files.add(multipartFile); var response = await request.send(); print(response.statusCode); } 

要使Flask在本地网络上可用,请禁用调试模式,然后使用ipconfig查找ipv4地址。 您可以像这样启动本地服务器:


 app.run(debug=False, host='192.168.1.103', port=5000) 

有时,防火墙可以阻止应用程序访问本地主机,因此必须重新配置或禁用它。




所有应用程序源代码都可以在github上找到 。 以下链接将帮助您了解正在发生的事情:


凯拉斯: https ://keras.io/


Flutter: https : //flutter.dev/


MongoDB: https//www.tutorialspoint.com/mongodb/


哈佛Python和Flask课程: https//www.youtube.com/watch?v = j5wysXqaIV8 t = 5515s (第2、3、4讲尤为重要)


GitHub: https : //github.com/SHARONZACHARIA

Source: https://habr.com/ru/post/zh-CN460995/


All Articles