02.21 代碼詳解:構建一個簡單的Keras+深度學習REST API

在本教程中,我們將介紹一個簡單的方法來獲取Keras模型並將其部署為REST API。本文所介紹的示例將作為你構建自己的深度學習API的模板/起點——你可以擴展代碼,根據API端點的可伸縮性和穩定性對其進行定製。


代碼詳解:構建一個簡單的Keras+深度學習REST API



具體而言,我們將瞭解:

· 如何(以及如何不)將Keras模型加載到內存中,以便有效地進行推理

· 如何使用Flask web框架為我們的API創建端點

· 如何使用我們的模型進行預測,用JSON-ify轉換它們,並將結果反饋到客戶端

· 如何使用cURL和Python來調用我們的Keras REST API

在本教程結束時,你將能很好地理解創建Keras REST API所需的組件(以最簡單的形式)。

請隨意使用本指南中提供的代碼作為你自己的深度學習REST API起點。

代碼詳解:構建一個簡單的Keras+深度學習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


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時,務必確保插入邏輯,以保證在接受請求前模型就已加載並準備好進行推理。

代碼詳解:構建一個簡單的Keras+深度學習REST API


如何不在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


啟動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端點:


代碼詳解:構建一個簡單的Keras+深度學習REST API


這是因為在Flask URLs路由中沒有設置索引/主頁。

那麼試著通過瀏覽器訪問/predict端點:


代碼詳解:構建一個簡單的Keras+深度學習REST API



出現了“方法不允許”錯誤。該錯誤是由於瀏覽器正在執行GET請求,但/predict只接受一個POST(我們將在下一節中演示如何執行)。

代碼詳解:構建一個簡單的Keras+深度學習REST API


使用cURL測試Keras REST API


在測試和調試Keras REST API時,請考慮使用cURL(https://curl.haxx.se/)(無論如何,cURL都是一個值得去學習如何使用的好工具)。

下圖是我們想要進行分類的圖像——一隻狗,更具體而言,是一隻比格犬:


代碼詳解:構建一個簡單的Keras+深度學習REST API



我們可以使用curl將該圖像傳遞給API,並找出ResNet認為該圖像包含的內容:

$ curl -X POST -F [email protected] '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 [email protected]來表示正在提交表單編碼的數據。然後將image鍵設置為dog.jpg文件的內容。在dog.jpg之前提供@意味著我們希望cURL加載圖像的內容並將數據傳遞給請求。

最後的終點是:http://localhost:5000/predict

請注意輸入的圖像是如何以99.01%的置信度被正確地分類為“比格犬”的。餘下的五大預測及其相關概率也包含在Keras API的響應之內。


代碼詳解:構建一個簡單的Keras+深度學習REST 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

· 端點不會受到嚴重影響


分享到:


相關文章: