82TensorFlow 2 模型部署方法实践--使用 Flask 框架部署模型

使用 Flask 框架部署模型

环境配置

首先创建 Python3.6 虚拟环境,虚拟环境方便我们更好地管理依赖。
同时为了留出足够的空间以保证第三方库正常安装,我们需要临时删除线上环境中的一些非必要文件。

$sudo rm -rf ../../opt
$virtualenv -p /usr/bin/python3.6 pyenv

进入虚拟环境,安装本节课程需要的库:Flask,OpenCV,TensorFlow 和 skimage。

$ . pyenv/bin/activate
$ pip install flask==1.1.1 opencv-python==4.1.2.30 tensorflow scikit-image==0.16.2 --no-cache-dir -i https://mirrors.aliyun.com/pypi/simple/

下载预训练模型,并将其放于 ~/.keras/models 目录下。

$ wget https://labfile.oss.aliyuncs.com/courses/1435/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_224.h5
$ mkdir -p ~/.keras/models
$ cp mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_224.h5 ~/.keras/models

通过客户端发送 POST 请求

如目前的互联网公司为开发者提供的人工智能开放平台,允许开发者申请账号后调用他们的 API 接口,接口文档举例描述如下:
描述:调用者提供图片文件或者图片 URL,进行图片分析,识别图片中的物体。
调用 URL:XXX.com/image_recognition。
调用方法:POST。

请求参数:
必选:
api_key: 调用此 API 的凭证。
三选一
image_url: 图片的 URL。
image_file: 一个图片,二进制文件,需要用 POST multipart/form-data 的方式上传。
image_base64: Base64 编码的二进制图片数据。

创建客户端脚本

我们先模仿开发者的行为,创建客户端来调用 API 接口。在桌面创建文件脚本 client.py,首先导入需要的库。

import base64
from skimage import data
import requests
import cv2

接下来对导入 NumPy 格式的图片,OpenCV 提供了 cv2.imencode 来把 NumPy 格式的图片编码成流数据,放到内存缓存中,函数 cv2.imdecode 可以从编码的数据流恢复到 NumPy 数组。

# 从 skimage 中获取图片
image = data.chelsea()

# OpenCV 图像的数据类型也是 NumPy 2 维数组
_, content = cv2.imencode('.jpg', image)
# 将流数据用 base64 编码
image_base64 = base64.b64encode(content)

最后封装数据,发送到服务端 http://0.0.0.0:8080/mobilenet ,并打印结果。

# 制作指定的数据格式
data = {'image_base64': image_base64}
# 向服务端地址 http://0.0.0.0:8080/mobilenet 发送数据
r = requests.post('http://0.0.0.0:8080/mobilenet', data=data)
# 打印从服务端获得的结果
print(r.text)

创建服务端

客户端脚本创建完成后,我们接下来实现服务端的功能。在桌面创建文件脚本 service.py,首先导入需要的库。

import base64
import cv2
import numpy as np
from flask import Flask, request
from tensorflow.keras.applications.mobilenet_v2 import MobileNetV2, preprocess_input, decode_predictions

对服务和模型进行初始化,并导入预训练模型。

app = Flask(__name__)
model = MobileNetV2(weights='imagenet')

实现路由 mobilenet 中图像识别的功能。

@app.route('/mobilenet', methods=("POST",))
def mobilenet():
    # 获取从客户端传来的数据中 image_base64 字段的值
    image_base64 = request.form['image_base64']
    # 将 Base64 解码
    image_str = base64.b64decode(image_base64)
    # 将解码后的字符串转成 uint8 的 NumPy
    image_np = np.fromstring(image_str, np.uint8)
    # 用函数 cv2.imdecode 将编码的数据流恢复到 NumPy 数组
    image = cv2.imdecode(image_np, cv2.IMREAD_COLOR)
    # 调整图片大小为模型输入的大小
    image = cv2.resize(image, (224, 224))
    # 制作输入数据
    x = np.expand_dims(image, 0)
    x = preprocess_input(x)
    # 获得输出向量
    output = model.predict(x)
    # 解码输出向量,这里只取第一个结果
    preds = decode_predictions(output,top=1)
    pred = np.squeeze(preds)
    # 返回预测结果
    return pred[1]

最后在地址 0.0.0.0:8080 启动服务。host= 若不填,服务则会在 127.0.0.1 启动,这样的话是无法访问 Web 服务的。

if __name__ == '__main__':
    app.run(host='0.0.0.0',port=8080)

至此,服务端脚本构建完毕。

启动程序

在终端输入 python service.py 来启动服务端。
新建一个终端,进入虚拟环境并运行客户端。

$ . pyenv/bin/activate
$ python client.py

我们即可得到相应的请求结果。
本小节实验完成后在服务端终端窗口按下 Ctrl + C 终止服务,继续下面的实验。

通过 HTML 发送 POST 请求

在某些情况下会有非开发者使用服务,这时候可以提供一个网页界面,让用户通过网页上传图片的方式进行识别,即有一个前端的 HTML 服务给用户上传图片,图片上传后提交到后端的 Flask 服务,由 Flask 服务进行图像识别,并返回结果到 HTML 页面上。
首先在桌面创建两个文件夹 static 和 templates,前者用于存放 HTML 上传的图片,后者用于存放 HTML 文件。

创建上传 HTML 页面

在 templates 文件夹下创建 HTML 文件 upload.html,上传功能页面将在此文件实现。上传页面需要创建一个表单元素,然后包含两个 input 元素,一个用于从本地上传文件,一个用于提交。



  
    
    

对 upload.html 进行预览:选择 upload.html 文件,右键,选择 Open With,然后选择使用 Preview 打开


image.png

创建结果 HTML 页面

在 templates 文件夹下创建 HTML 文件 result.html,结果显示页面将在此文件实现。为了界面的反复使用,我们可以把 upload.html 中的元素也包含到里面,同时在下方显示结果。



  
    
{{predict}}

创建 Flask 应用

在桌面创建文件脚本 web.py,该脚本用于接收 HTML 传过来的图像数据,进行图像识别后返回结果。
首先导入需要的库。

from flask import Flask, render_template, request
import numpy as np
import cv2
from tensorflow.keras.applications.mobilenet_v2 import MobileNetV2, preprocess_input, decode_predictions

对服务和模型进行初始化,并导入预训练模型。

app = Flask(__name__)
model = MobileNetV2(weights='imagenet')

实现图像识别的功能,当获取到的是 POST 传过来的数据,则开始识别,不然就返回 upload.html 界面。 在这里我们使用 render_template 模板,其功能是先引入 HTML 文件,然后根据后面传入的参数,对 HTML 进行修改渲染。

@app.route('/', methods=['POST', 'GET'])
def main_page():
    if request.method == 'POST':
        file = request.files['file']
        # 将图片存在 static 文件夹中
        file.save('static/'+file.filename)
        # 读取图片
        image = cv2.imread('static/'+file.filename)
        # OpenCV 读取图片是 BGR 格式,需要转换为 RGB
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        # 放缩图片到 224 * 224
        image = cv2.resize(image, (224, 224))
        x = np.expand_dims(image, 0)
        # 图片预处理
        x = preprocess_input(x)
        # 进行预测
        output = model.predict(x)
        # 取 top1 的预测结果
        preds = decode_predictions(output, top=1)
        predict = np.squeeze(preds)
        # 返回数据
        return render_template('result.html', filename=file.filename, predict=predict)
    # GET 方法返回 upload.html
    return render_template('upload.html')

最后在地址 0.0.0.0:8080 启动服务。同样,必须指定 host= ,否则服务默认会在 127.0.0.1 启动,这样的话是无法访问 Web 服务的。

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)

启动程序

在终端输入 python web.py 启动程序。
点击右侧工具栏中的 Web 服务 按钮,访问 Web 服务进行提交图片。
https://labfile.oss.aliyuncs.com/courses/1435/3-1.mp4
最后,你可以通过终端下载本次实验的完整代码进行练习:

wget https://labfile.oss.aliyuncs.com/courses/1435/full_code_3.zip
unzip full_code_3.zip

你可能感兴趣的:(82TensorFlow 2 模型部署方法实践--使用 Flask 框架部署模型)