Falconvs.Flask-选择哪一个来创建可扩展的深度学习RESTAPI

我们有时会提供速度至关重要的关键服务。例如,我们有数百万张酒店图片,我们需要对其进行“吸引力”评估。每次浏览每张图片都非常耗时,如果操作不当可能会花费数周时间。因此,优化预测过程中的每个步骤(从模型大小到创建rest服务)至关重要。在本文中,我们希望分享我们的经验,以及Falcon是否可以用作创建可扩展的机器学习rest API的可行替代方案。

Falcon是什么?

Falcon就像Flask,一个用Python编写的轻量级微框架,用于构建web api和应用程序后台。与Flask不同,Falcon的主要关注点是REST api,因为它根本不适合提供HTML页面。它有一个干净的设计,包含HTTP和REST架构风格。

Falcon的简单代码示例:

import falcon

class HelloResource:

def on_get(self, req, resp):

resp.status = falcon.HTTP_200

resp.body = Hello World!

api = falcon.API()

api.add_route(/, HelloResource())

深度学习REST API

对于Flask来说,已经有很多关于它如何用于为机器学习模型创建预测服务的文章。对于Falcon,有关如何使用它来创建机器学习API的教程列表比Flask要少。尽管如此,无论是Flask还是Falcon,将机器学习模型作为API部署的理念仍然保持不变。通常,您训练机器学习模型,然后将其保存(例如,在scikit中,您可以pickle您的模型)。然后,您将机器学习模型包含在具有一些数据预处理逻辑的rest API中。在我们的例子中,我们训练了一个MNIST数据集上的简单CNN,Keras和Tensorflow作为后端(。然后,我们将模型存储到单个HDF5文件中,该文件包含模型的体系结构,模型的权重以及一些其他信息,如训练配置,优化器状态等。

之后,我们使用该模型来创建预测服务。这是Python代码:

import base64

import json

import falcon

import numpy as np

from io import BytesIO

from PIL import Image, ImageOps

def convert_image(image):

img = Image.open(image).convert(L)

inverted_img = ImageOps.invert(img)

data = np.asarray(inverted_img, dtype=int32)

rescaled_data = (data / 255).reshape(1, 28, 28, 1)

return rescaled_data

class PredictResource(object):

def __init__(self, model):

self.model = model

def on_get(self, req, resp):

resp.status = falcon.HTTP_200

resp.body = Hello World!

def on_post(self, req, resp):

"""

(echo -n {"image": "; four_test.png; echo "}) |

curl -H "Content-Type: application/json" -d @- :8000/predict

"""

image = json.loads(req.stream.read())

decoded_image = base64.b64decode(image.get(image))

data = convert_image(BytesIO(decoded_image))

predicted_data = self.model.predict_classes(data)[0]

output = {prediction: str(predicted_data)}

resp.status = falcon.HTTP_200

resp.body = json.dumps(output, ensure_ascii=False)

基本上,我们创建了一个端点,我们需要将包含在base64中解码的图像的json文件发布到此端点,然后以可用于我们在Keras中训练的模型的格式转换图像。输出是预测。您已经可以看到,模型未在on_post函数中显式启动,因为我们不希望每次发出请求时都将模型重新加载到内存中。这是非常低效的。相反,我们resource通过将其作为参数传递,在Falcon之外启动模型(资源只是API中可以通过URL访问的所有内容)。

import os

import falcon

from keras.models import load_model

from .predict import PredictResource

api = application = falcon.API()

def load_trained_model():

global model

model = load_model(os.path.join(os.path.dirname(__file__), model/cnn_model.h5))

model._make_predict_function() # make keras thread-safe

return model

predict = PredictResource(model=load_trained_model())

api.add_route(/predict, predict)

然后为了运行Falcon API,我们使用Gunicorn作为WSGI HTTP服务器(不像Flask,它没有内置的Web服务器)和NGINX用于代理服务器。

负载测试

现在是时候测试我们的预测服务了。我们使用Locust进行负载测试。它是用Python编写的,开源的,使用起来非常简单。

我们在OpenShift上部署了应用程序,两个工作人员使用Gevent。为了确保服务的稳定性,我们还扩展了三个容器,也称为pod。

以下是200个模拟用户的结果,每个用户产生的hatch rate为1秒,

从表格(时间以毫秒 - 毫秒给出),我们可以看到我们已经为Flask和Falcon运行了大约10k的请求。我们还可以看到两个框架的最小响应时间与19ms相同。尽管Flask在平均响应时间和最大响应时间方面稍好一些,但我们可以得出结论,两者的表现非常相似。

结论

从我们的测试中我们得出结论,Falcon不一定比Flask更快。当然,可以进行更多的测试,例如更多的请求和更多的模拟用户。但是,至少我们可以总结一下Falcon和Flask表现相似,即当我们只需要创建API时,Falcon可以用作可行的替代方案。代码设计非常好,更适合REST API(在使用Falcon开发时,您应该考虑映射到HTTP方法的资源和状态转换)。