我們有時會提供速度至關重要的關鍵服務。例如,我們有數百萬張酒店圖片,我們需要對其進行“吸引力”評估。每次瀏覽每張圖片都非常耗時,如果操作不當可能會花費數週時間。因此,優化預測過程中的每個步驟(從模型大小到創建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作為後端(https://github.com/keras-team/keras/blob/master/examples/mnist_cnn.py)。然後,我們將模型存儲到單個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 @- http://127.0.0.1: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方法的資源和狀態轉換)。
閱讀更多 不靠譜的貓 的文章