百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

代码详解:构建一个简单的Keras+深度学习REST API

toyiye 2024-07-09 22:47 14 浏览 0 评论

在本教程中,我们将介绍一个简单的方法来获取Keras模型并将其部署为REST API。本文所介绍的示例将作为你构建自己的深度学习API的模板/起点——你可以扩展代码,根据API端点的可伸缩性和稳定性对其进行定制。




具体而言,我们将了解:

· 如何(以及如何不)将Keras模型加载到内存中,以便有效地进行推理

· 如何使用Flask web框架为我们的API创建端点

· 如何使用我们的模型进行预测,用JSON-ify转换它们,并将结果反馈到客户端

· 如何使用cURL和Python来调用我们的Keras REST API

在本教程结束时,你将能很好地理解创建Keras REST API所需的组件(以最简单的形式)。

请随意使用本指南中提供的代码作为你自己的深度学习REST API起点。


配置开发环境


假设Keras已经配置并安装在你的机器上。如果没有,请确保使用官方安装说明安装Keras(https://keras.io/#installation)。

然后,需要安装Flask (http://flask.pocoo.org/)(及其相关的依赖项),一个Python web框架,这样就可以构建API端点了。还需要请求(http://docs.python-requests.org/en/master/),这样就可以使用API了。

有关的pip安装命令如下:

$ pip install flask gevent requests pillow



构建你的Keras REST API


Keras REST API独立于一个名为run_keras_server.py的文件中。为了简单起见,我们将安装保存在一个文件中——安装启用也可以很容易地模块化。

在 run_keras_server.py中,你会发现三个函数,即:

· load_model:用于加载训练好的Keras模型,并为推理做准备。

· prepare_image:这个函数在通过我们的网络进行预测之前对输入图像进行预处理。如果你没有使用图像数据,则可能需要考虑将名称更改为更通用的prepare_datapoint,并应用一些可能需要的缩放/标准化。

· predict:API的实际端点可以将请求中的输入数据分类,并将结果反馈给客户端。

# import the necessary packagesfrom keras.applications import ResNet50from keras.preprocessing.image import img_to_arrayfrom keras.applications import imagenet_utilsfrom PIL import Imageimport numpy as npimport flaskimport io

# initialize our Flask application and the Keras modelapp = flask.Flask(__name__)model = None


第一个代码片段处理导入了所需的程序包,并且对Flask应用程序和模型进行了初始化。

在此,我们定义load_model函数:

def load_model():

# load the pre-trained Keras model (here we are using a model

# pre-trained on ImageNet and provided by Keras, but you can

# substitute in your own networks just as easily)

global model

model = ResNet50(weights="imagenet")


顾名思义,这个方法负责将我们的架构实例化,并从磁盘加载权重。

为了简单起见,将使用在ImageNet数据集上预先训练过的ResNet50架构。

如果你正在使用自定义模型,则需要修改此函数以从磁盘加载架构+权重。

在对任何来自客户端的数据进行预测之前,首先需要准备并预处理数据:

def prepare_image(image, target):

# if the image mode is not RGB, convert it

if image.mode != "RGB":

image = image.convert("RGB")

# resize the input image and preprocess it

image = image.resize(target)

image = img_to_array(image)

image = np.expand_dims(image, axis=0)

image = imagenet_utils.preprocess_input(image)

# return the processed image

return image


这个函数:

· 接受输入图像

· 将模式转换为RGB(如果需要)

· 将大小调整为224x224像素(ResNet的输入空间维度)

· 通过平均减法数组和缩放对阵列进行预处理

此外,在通过模型传递输入数据之前,应该根据某一预处理、缩放或标准化来修改这个函数。

现在可以定义predict函数了——该方法会处理对/predict端点的任何请求:

@app.route("/predict", methods=["POST"])def predict():

# initialize the data dictionary that will be returned from the

# view

data = {"success": False}

# ensure an image was properly uploaded to our endpoint

if flask.request.method == "POST":

if flask.request.files.get("image"):

# read the image in PIL format

image = flask.request.files["image"].read()

image = Image.open(io.BytesIO(image))

# preprocess the image and prepare it for classification

image = prepare_image(image, target=(224, 224))

# classify the input image and then initialize the list

# of predictions to return to the client

preds = model.predict(image)

results = imagenet_utils.decode_predictions(preds)

data["predictions"] = []

# loop over the results and add them to the list of

# returned predictions

for (imagenetID, label, prob) in results[0]:

r = {"label": label, "probability": float(prob)}

data["predictions"].append(r)

# indicate that the request was a success

data["success"] = True

# return the data dictionary as a JSON response

return flask.jsonify(data)


数据字典用于存储希望反馈到客户端的所有数据。现在,它包含一个布尔值,用来表示预测是否成功——还将使用此字典来存储对传入数据进行的所有预测的结果。

为了接收输入的数据,我们会检查是否:

· 请求方法是POST(支持我们将任意数据发送到端点,包括图像、JOSN、编码数据等)

· 在POST期间,图像被传递到“文件”属性中

然后,将接收到的数据:

· 以PIL格式读取

· 进行预处理

· 将其通过我们的网络

· 循环结果,并将其单独添加到data["predictions"]列表中

· 以JSON格式将响应反馈给客户端

如果使用的是非图像数据,则应删除该请求文件代码,并解析原始输入数据,或者使用request.get_json()将输入数据自动解析为Python字典/对象。

现在只需启动我们的服务:

# if this is the main thread of execution first load the model and# then start the serverif __name__ == "__main__":

print(("* Loading Keras model and Flask starting server..."

"please wait until server has fully started"))

load_model()

app.run()


首先调用load_model从磁盘加载Keras模型。

对load_model的调用是一个阻止操作——阻止web服务在模型完全加载之前启动。如果未能确保模型完全载入内存中,在启动web服务之前也没有做好推理准备,就可能会遇到以下情况:

1. 请求被发送到服务器

2.服务器接受请求,预处理数据,然后尝试将其传递到模型中

3.由于模型还未完全加载,脚本将会出错!

在构建自己的Keras REST APIs时,务必确保插入逻辑,以保证在接受请求前模型就已加载并准备好进行推理。


如何不在REST API中加载Keras模型


你可能想在predict函数中加载模型,如下所示:

...

# ensure an image was properly uploaded to our endpoint

if request.method == "POST":

if request.files.get("image"):

# read the image in PIL format

image = request.files["image"].read()

image = Image.open(io.BytesIO(image))

# preprocess the image and prepare it for classification

image = prepare_image(image, target=(224, 224))

# load the model

model = ResNet50(weights="imagenet")

# classify the input image and then initialize the list

# of predictions to return to the client

preds = model.predict(image)

results = imagenet_utils.decode_predictions(preds)

data["predictions"] = []...


该代码意味着每次有新请求时都将加载模型。这太低效了,甚至会导致系统内存耗尽。

如果尝试运行上面的代码,你会注意到API将运行得特别慢(尤其是在模型很大的情况下)——这是由于为每个新请求加载模型的I/O和CPU操作开销太大所致。

为了了解服务器内存是如何因此轻易崩溃的,假设服务器同时收到N个传入请求。同样,这意味着将有N个模型同时加载到内存中。同时,如果模型较大(如ResNet),那么存储在RAM中的N个模型副本很容易就会耗尽系统内存。

所以,除非你有一个非这样做不可的理由,否则请尽量避免为每个新的传入请求加载一个新的模型实例。

注意:这里我们假定使用的是默认的单线程Flask服务器。如果将其部署到多线程服务器,那么即使使用本文前面讨论的“更正确”的方法,内存中仍会加载多个模型。如果你打算使用专用服务器,如Apache或nginx,则应该考虑使管道更具可扩展性。


启动你的Keras Rest API


启动Keras REST API服务很简单。

打开终端,执行:

$ python run_keras_server.py

Using TensorFlow backend.

* Loading Keras model and Flask starting server...please wait until server has fully started

...

* Running on http://127.0.0.1:5000


从输出中可以看到,首先加载模型,然后可以启动Flask服务器。

现在可以通过http://127.0.0.1:5000访问服务器。

但是,如果将IP地址+端口复制粘贴到浏览器中,会出现以下情况:

这是因为在Flask URLs路由中没有设置索引/主页。

那么试着通过浏览器访问/predict端点:



这是因为在Flask URLs路由中没有设置索引/主页。

那么试着通过浏览器访问/predict端点:




出现了“方法不允许”错误。该错误是由于浏览器正在执行GET请求,但/predict只接受一个POST(我们将在下一节中演示如何执行)。


使用cURL测试Keras REST API


在测试和调试Keras REST API时,请考虑使用cURL(https://curl.haxx.se/)(无论如何,cURL都是一个值得去学习如何使用的好工具)。

下图是我们想要进行分类的图像——一只狗,更具体而言,是一只比格犬:




我们可以使用curl将该图像传递给API,并找出ResNet认为该图像包含的内容:

$ curl -X POST -F image=@dog.jpg 'http://localhost:5000/predict'{

"predictions": [

{

"label": "beagle",

"probability": 0.9901360869407654

},

{

"label": "Walker_hound",

"probability": 0.002396771451458335

},

{

"label": "pot",

"probability": 0.0013951235450804234

},

{

"label": "Brittany_spaniel",

"probability": 0.001283277408219874

},

{

"label": "bluetick",

"probability": 0.0010894243605434895

}

],

"success": true}


-x标志和POST值表示我们正在执行POST请求。

我们提供-F image=@dog.jpg来表示正在提交表单编码的数据。然后将image键设置为dog.jpg文件的内容。在dog.jpg之前提供@意味着我们希望cURL加载图像的内容并将数据传递给请求。

最后的终点是:http://localhost:5000/predict

请注意输入的图像是如何以99.01%的置信度被正确地分类为“比格犬”的。余下的五大预测及其相关概率也包含在Keras API的响应之内。



以编程方式使用Keras REST API


你很可能会向Keras REST API提交数据,然后以某种方式利用反馈的预测——这就要求我们以编程的方式处理来自服务器的响应。

这是一个使用requests Python程序包的简单过程(http://docs.python-requests.org/en/master/):

# import the necessary packagesimport requests

# initialize the Keras REST API endpoint URL along with the input# image pathKERAS_REST_API_URL = "http://localhost:5000/predict"IMAGE_PATH = "dog.jpg"

# load the input image and construct the payload for the requestimage = open(IMAGE_PATH, "rb").read()payload = {"image": image}

# submit the requestr = requests.post(KERAS_REST_API_URL, files=payload).json()

# ensure the request was successfulif r["success"]:

# loop over the predictions and display them

for (i, result) in enumerate(r["predictions"]):

print("{}. {}: {:.4f}".format(i + 1, result["label"],

result["probability"]))

# otherwise, the request failedelse:

print("Request failed")


KERAS_REST_API_URL指定端点,而IMAGE_PATH是在磁盘上输入图像的路径。

使用IMAGE_PATH加载图像,然后将payload构建到请求中。

考虑到有效载荷,我们可以使用requests.post调用将数据发布到端点。在指示requests调用的末尾附加.json() :

1.服务器的响应应该是JSON格式的

2.希望JSON对象能够自动解析和反序列化

一旦有了请求r的输出,就可以检查分类是否成功,然后循环r["predictions"]。

要运行指令simple_request.py,首先要确保run_keras_server.py(即 Flask web服务器)正在运行。然后在一个单独的框架中执行下列命令:

$ python simple_request.py

1. beagle: 0.9901

2. Walker_hound: 0.0024

3. pot: 0.0014

4. Brittany_spaniel: 0.0013

5. bluetick: 0.0011


我们成功地调用了Keras REST API,并通过Python得到了模型的预测。

注意,本文中的代码仅用于指导,而非生产级别,也不能在高负载和大量传入请求的情况下进行扩展。

该方法最好在以下情况下使用:

· 为自己的Keras深度学习模型快速建立一个REST API

· 端点不会受到严重影响

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码