使用Flask服务部署算法模型

1. 使用Flask服务部署算法

最近在公司快吧我恶心死了,好不容易把算法给弄完了,组长又让我把算法给部署到测试服务器上,这让我懵逼了,我刚开始工作没多久,也不知道怎么部署算法。组长就把接口的配置文件,还有让我使用Flask服务,好了就只剩我自己摸索了,话说真的不难,就是开始不会给我恶心到了。

解释说明: 当我们完成了一个算法模型后,我们对这个算法模型进行部署,总不能我们做完了算法,然后直接就打包扔给前后端了吧,人家也不懂啊,人家也不知道如何调用你这个算法啊,你得提供算法模型的接口。因此,为了让后端使用我们的代码,我们需要提供接口,那么这个接口是什么呢???这个接口是让后端输入算法模型的必要参数,算法通过接口获取到参数(数据)然后去调用相应的算法,得到结果后已后端要求的指定格式返回(一般为json格式)。

那么问题来了:我们该怎么样去写这个接口呢???往往后端[java,python等]和算法人员[python,c++等]使用编程语言不一样,而且做算法的也不能去跟后端说算法的内部代码怎么调用,简单的来说我们不能够使用一套代码直接调用。这个时候就想到了http网络的方式,后端通过网络请求的方式将必要的参数或数据发送给我们,我们使用一个服务获取到这个参数和数据,然后自己去调用算法,这就是接口。这个服务,我目前只用到了Flask服务(本人不是做后端,也不知道这里面有啥,只会简单的调用)。

那么这个服务这么编写呢??请看下面。

1.1 Flask服务的运行过程

第一步: 客户端通过浏览器发起HTTP请求。
第二步: Web服务器吧后端的请求交给Flask程序处理。
第三步:Flask处理完毕后,通过指定的方式或格式返回给浏览器。

1.2 Flask服务的一个helloworld

# 导入 Flask 类
from flask import Flask

# 创建了这个类的实例,参数是应用模块或者包的名称,它会指向程序所在的模块,但一般都是传递__name__。
app = Flask(__name__)


# 使用 route() 装饰器来告诉 Flask 触发函数的 URL
@app.route("/")
def hello():
    return "Hello World!"


if __name__ == "__main__":
    # 使用 run() 函数来运行本地服务器和我们的应用
    app.run()

当我们运行这个程序的时候,会出现以下内容:
使用Flask服务部署算法模型_第1张图片
解释说明: 它告诉了我们通过http://127.0.0.1:5000 这个网址去访问我们的服务,当我们访问后会出现以下内容:
使用Flask服务部署算法模型_第2张图片
因此我们可以看出,Flask服务主要的主代码主要包含一下内容:

  • 创建Flask实例
  • 使用@app.route(“/”) 装饰某一个函数,以此来获得我们访问这个服务时内部所需要做的事。
  • 运行这个代码:app.run()

题外话:

 在这其中,我们发现我们的访问地址默认是http://127.0.0.1:5000,
 	其中:http://127.0.0.1是网址
 	     5000 是端口号
 我们可以使用如下代码来指定的我们的网络地址和端口号:
 	app.config['HOST'] = '127.0.0.1'
    app.config['PORT'] = 5000
 然后再运行:
 app.run(app.config['HOST'], app.config['PORT'])

2. 使用Flask服务部署算法模型

解释说明:这里我做了一个目标检测的算法,我们通过Flask服务获取到算法的一些参数,调用算法进行计算,然后将结果返回给浏览器(在实际情况中是需要和后端交互的,一般是后端给我们返回的格式)主要过程如下:

  • 通过Flask服务获取到算法的一些参数,和需要检测的图片
  • 调用算法,进行检测计算
  • 返回json格式的算法结果

2.1 接收值和返回值的格式

解释说明: 这里我给出公司在进行算法测试时给的一部分服务的接收值格式和返回值格式。
使用Flask服务部署算法模型_第3张图片
解释说明:

从表中可以看出,在Flask的服务端,我们需要接受的参数有三个,分别为:
	
	imageType : 这个参数没什么多大的意义,只是告诉我们这个图片是属于检测哪种的图片。
	debug : 这个参数是告诉算法检测后需不需要吧图片保存下来,如果是0,则不需要保存,只需要返回检测后
			的结果图片,如果是1则需要保存。
	imageFile : 客户端传过来的需要检测的图片数据(网页传入当然是数据流啦)
	
返回参数:
	returnStatus : 返回的状态,为0时表示出现问题了,具体是哪些问题会在returnMessage中指出,为1时表示
	                成功,也会在returnMessage中写入成功的内容。
    余下参数不一一介绍了。

2.2 Flask步骤

2.1 Flask服务的过程代码

# -*- coding:utf-8 -*-
# coding=utf8
import logging
import cv2
import numpy as np
from flask import jsonify, Flask, request, Response, render_template
from werkzeug.utils import secure_filename
from werkzeug.exceptions import RequestEntityTooLarge

from detect import BaseDetector

app = Flask(__name__, template_folder='./')
app.config['DEBUG'] = False
app.config['MAX_CONTENT_LENGTH'] = 20 * 1024 * 1024
# app.config['DATA']='/data/hbpw_uploads'
app.config['DATA'] = '/data/shudian_uploads'

app.config['ALLOWED_EXTENSIONS'] = set(['png', 'PNG', 'jpg', 'JPG', 'jpeg', 'JPEG', 'bmp', 'BMP'])
app.config['JSON_AS_ASCII'] = False
app.config['HOST'] = '127.0.0.1'
app.config['PORT'] = 5000
app.config['JSON_AS_ASCII'] = False
app.config['JSONIFY_MIMETYPE'] = "application/json;charset=utf-8"
gunicorn_logger = logging.getLogger('gunicorn.error')
app.logger.handlers = gunicorn_logger.handlers
app.logger.setLevel(gunicorn_logger.level)
url = "http://" + app.config['HOST'] + ":" + str(app.config['PORT'])
net = BaseDetector()

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1] in app.config['ALLOWED_EXTENSIONS']

@app.route('/', methods=['GET'])
def index():
    return render_template('test.html')

if __name__ == '__main__':
    app.run(app.config['HOST'], app.config['PORT'], debug=False, threaded=True)

解释说明: 创建了Flask服务,并指定了服务内部的参数,如网络地址和端口号,注意这里的 debug和输入参数的debug是不一样的。当服务开启时,用户访问网址时,我们返回了一个网页test.html,网页地址的效果如下:
在这里插入图片描述
网页的源代码如下:

DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>接口测试title>
    head>
    <body align=center>
        <form action='/Upload' method='post' enctype='multipart/form-data'>
            <label>调试模式label><input name="debug" value="1">
            <select name='imageType'>
                <option value='jcps'>基础设施破损option>
            select>
            <input type='file' name='imageFile'>
            <input type='submit' value='上传图像'>
        form>
    body>
html>

解释说明: 我们可以通过选择图片上载图片至网页,点击上传图像将数据推送给Flask服务。
在Flask服务中写一个函数,接受这些数据,然后把需要的东西送入算法中,进行运算,返回给浏览器。
接收函数如下所示:

@app.route('/Upload', methods=['POST'])
def upload():
    try:
        upload_file = request.files['imageFile']
        img_type = request.values['imageType']
        debug = request.values['debug']
        if upload_file == None or img_type == None or debug == None:
            returnStatus = '0'
            returnMessage = '必要参数为空'
            json_result = {'returnStatus': returnStatus, 'returnMessage': returnMessage}
            return jsonify(returnResult=json_result)

        file_name = secure_filename(upload_file.filename)
        if allowed_file(file_name):
            img_stream = upload_file.stream.read()
            img_np_arr = np.frombuffer(img_stream, np.uint8)
            raw_img = cv2.imdecode(img_np_arr, cv2.IMREAD_COLOR + cv2.IMREAD_IGNORE_ORIENTATION)
            json_result = net.run(raw_img, file_name, img_type, app.config['DATA'], url, debug)
            # #json_result=JsonUTF8toUnicode(json_result)
            return jsonify(returnResult=json_result)
        else:
            returnStatus = '0'
            returnMessage = '上传图像格式不匹配'
            json_result = {'returnStatus': returnStatus, 'returnMessage': returnMessage}
            # json_result=JsonUTF8toUnicode(json_result)
            return jsonify(returnResult=json_result)

    except RequestEntityTooLarge as e:
        returnStatus = '0'
        allowedsize = app.config['MAX_CONTENT_LENGTH'] / 1024 / 1024
        returnMessage = '上传图像过大,图像应低于 ' + str(allowedsize) + ' M'
        json_result = {'returnStatus': returnStatus, 'returnMessage': returnMessage}
        return jsonify(returnResult=json_result)

解释说明:
我们采用如下方式接受参数:

upload_file = request.files['imageFile']      #图片数据流
img_type = request.values['imageType']   # 图片类型
debug = request.values['debug']               # debug模式

采用了以下代码调用了算法,至于算法的调用那就是我们的本行了,自行去编写,按照指定的格式返回值就行了。

json_result = net.run(raw_img, file_name, img_type, app.config['DATA'], url, debug) #调用算法

好吧 写的很烂,但这只是我记录这次算法部署的过程。后续还涉及到了算法镜像的打包,docker容器开机自启动步骤等,可以百度的。

你可能感兴趣的:(目标检测,flask,python,后端)