我们有时会提供速度至关重要的关键服务。例如,我们有数百万张酒店图片,我们需要对其进行“吸引力”评估。每次浏览每张图片都非常耗时,如果操作不当可能会花费数周时间。因此,优化预测过程中的每个步骤(从模型大小到创建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方法的资源和状态转换)。