目錄
Keras簡介
Keras是一套由Python實現(xiàn)的高級神經(jīng)網(wǎng)絡(luò)API,可以基于不同的后臺(backend)實現(xiàn),包括TensoreFlow,CNTK,MXNet(基于Keras-MXNet,處于孵化階段)和Theano(已停止開發(fā))。Keras可以讓用戶能快速的開發(fā)出原型,支持流行的卷積網(wǎng)絡(luò)(Convolutional Networks)、循環(huán)網(wǎng)絡(luò)(Recurrent Networks)或者兩者的組合,并且可以無縫的在CPU和GPU上運行。
Keras模型分類
Keras提供了兩種類型的模型,順序(Sequential)模型和函數(shù)式(Functional)模型:
- 順序模型,由一組線性的層堆棧組成(a linear stack of layers),是一種最簡單的模型類型,API簡單明了,詳情查看官方文檔:Guide to the Sequential model
- 函數(shù)式模型,可以定義更復(fù)雜的模型網(wǎng)絡(luò)結(jié)構(gòu),比如多輸出模型,有向無環(huán)圖,具有共享層的多模型,詳情查看官方文檔:Guide to the Functional API
以上兩套API,生成的模型類型是keras.models.Sequential(繼承自Model)和keras.models.Model類,除了以上兩種類型模型,從Keras 2.2.0開始,支持用戶繼承keras.models.Model來創(chuàng)建完全自定義化模型類型,詳情查看官方文檔模型自類化(Model subclassing)。本文中我們主要討論非子類化(non-subclassing)模型的部署,關(guān)于子類化模型(subclassing)的部署會在后面介 紹。
Keras模型部署準備
在上一篇文章《自動部署開源AI模型到生產(chǎn)環(huán)境:Sklearn、XGBoost、LightGBM、和PySpark》中我們介紹了如何通過AutoDeployAI的AI模型自動部署和管理系統(tǒng)DaaS(Deployment-as-a-Service)來自動部署傳統(tǒng)開源機器學習模型(包括Scikit-learn、XGBoost、LightGBM、和PySpark等),這里我們詳細介紹如果通過DaaS來自動部署Keras深度神經(jīng)網(wǎng)絡(luò)模型,同樣我們需要:
- 安裝Python DaaS-Client
- 初始化DaasClient
- 創(chuàng)建項目
詳細準備工作,請參考以上文章中的部署準備
部分。完整的代碼,請參考Github上的Notebook:deploy-keras.ipynb
默認部署Keras模型
- 基于Keras Sequence API訓練一個簡單的多分類模型,使用Scikit-learn中的
Iris
數(shù)據(jù):
from keras.models import Sequential
from keras.layers import Dense
from keras.optimizers import Adam
model = Sequential()
model.add(Dense(10, input_shape=(4,), activation='relu'))
model.add(Dense(8, activation='relu'))
model.add(Dense(6, activation='relu'))
model.add(Dense(3, activation='softmax'))
model.compile(Adam(lr=0.04), 'categorical_crossentropy', ['accuracy'])
model.fit(X_train, pd.get_dummies(y_train).values, epochs=100)
- 發(fā)布Keras模型:
publish_resp = client.publish(model,
name='keras-iris',
mining_function='classification',
x_test=X_test,
y_test=y_test,
description='A Keras classification model')
pprint(publish_resp)
publish_resp是一個字典類型的結(jié)果,記錄了模型名稱,和發(fā)布的模型版本。
{'model_name': 'keras-iris', 'model_version': '1'}
- 測試Keras模型:
test_resp = client.test(publish_resp['model_name'],
model_version=publish_resp['model_version'])
pprint(test_resp)
test_resp是一個字典類型的結(jié)果,記錄了測試REST API信息,如下:
{'access_token': 'A-LONG-STRING-OF-BEARER-TOKEN-USED-IN-HTTP-HEADER-AUTHORIZATION',
'endpoint_url': 'https://daas.autodeploy.ai/api/v1/test/deployment-test/daas-python37-faas/test',
'payload': {'args': {'X': [{'dense_1_input': [5.7, 4.4, 1.5, 0.4]}],
'model_name': 'keras-iris',
'model_version': '1'}}}
使用requests
庫調(diào)用測試API:
response = requests.post(test_resp['endpoint_url'],
headers={'Authorization': 'Bearer {token}'.format(token=test_resp['access_token'])},
json=test_resp['payload'],
verify=False)
pprint(response.json())
返回結(jié)果:
{'result': [{'dense_4': [[0.996542751789093,
0.0034567660186439753,
4.955750227964018e-07]]}],
'stderr': [],
'stdout': []}
- 正式部署Keras模型:
deploy_resp = client.deploy(model_name='keras-iris',
deployment_name='keras-iris-svc',
model_version=publish_resp['model_version'],
replicas=1)
pprint(deploy_resp)
返回結(jié)果:
{'access_token': 'A-LONG-STRING-OF-BEARER-TOKEN-USED-IN-HTTP-HEADER-AUTHORIZATION',
'endpoint_url': 'https://daas.autodeploy.ai/api/v1/svc/deployment-test/keras-iris-svc/predict',
'payload': {'args': {'X': [{'dense_1_input': [5.7, 4.4, 1.5, 0.4]}]}}}
使用requests
庫調(diào)用正式API:
response = requests.post(deploy_resp['endpoint_url'],
headers={'Authorization': 'Bearer {token}'.format(token=deploy_resp['access_token'])},
json=deploy_resp['payload'],
verify=False)
pprint(response.json())
結(jié)果如下:
{'result': [{'dense_4': [[0.996542751789093,
0.0034567660186439753,
4.955750227964018e-07]]}]}
除了使用Keras API,對于TensorFlow,DaaS也同樣支持使用tf.keras API訓練的模型。
自定義部署Keras模型
- 基于tf.Keras Functional API訓練模型,使用Keras中的
MNist
數(shù)據(jù)來識別用戶輸入的數(shù)字,以下代碼參考Functional API on MNIST:
# load MNIST dataset
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# from sparse label to categorical
num_labels = len(np.unique(y_train))
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
# reshape and normalize input images
image_size = x_train.shape[1]
x_train = np.reshape(x_train,[-1, image_size, image_size, 1])
x_test = np.reshape(x_test,[-1, image_size, image_size, 1])
x_train = x_train.astype('float32') / 255
x_test = x_test.astype('float32') / 255
# network parameters
input_shape = (image_size, image_size, 1)
batch_size = 128
kernel_size = 3
filters = 64
dropout = 0.3
# use functional API to build cnn layers
inputs = Input(shape=input_shape)
y = Conv2D(filters=filters,
kernel_size=kernel_size,
activation='relu')(inputs)
y = MaxPooling2D()(y)
y = Conv2D(filters=filters,
kernel_size=kernel_size,
activation='relu')(y)
y = MaxPooling2D()(y)
y = Conv2D(filters=filters,
kernel_size=kernel_size,
activation='relu')(y)
# image to vector before connecting to dense layer
y = Flatten()(y)
# dropout regularization
y = Dropout(dropout)(y)
outputs = Dense(num_labels, activation='softmax')(y)
# build the model by supplying inputs/outputs
model = Model(inputs=inputs, outputs=outputs)
# network model in text
model.summary()
# classifier loss, Adam optimizer, classifier accuracy
model.compile(loss='categorical_crossentropy',
optimizer='adam',
metrics=['accuracy'])
# train the model with input images and labels
model.fit(x_train,
y_train,
validation_data=(x_test, y_test),
epochs=3,
batch_size=batch_size)
為了可以快速訓練出模型,修改原程序中epochs=20為3。
- 發(fā)布Keras模型:
publish_resp = client.publish(model,
name='keras-mnist',
mining_function='classification',
x_test=x_test,
y_test=y_test,
description='A tf.Keras classification model')
pprint(publish_resp)
結(jié)果如下:
{'model_name': 'keras-mnist', 'model_version': '1'}
- 測試Keras模型。登陸DaaS Web客戶端,查看
keras-mnist
模型信息:
模型輸入字段input_1
,維數(shù)為(,28,28,1),輸出字段dense
,維數(shù)為(,10)。切換到測試
標簽頁,我們看到DaaS自動存儲了一條測試數(shù)據(jù),點擊提交
命令,測試該條數(shù)據(jù),如圖:
- 自定義部署Keras模型。
keras-mnist
模型輸入數(shù)據(jù)是由一張28*28的黑底白字灰度圖像生成的numpy數(shù)組,在REST API使用JSON傳輸數(shù)據(jù)時,因為JSON作為一種文本格式,在存儲傳輸大的列表時有性能劣勢,并且需要調(diào)用端做圖像預(yù)處理工作,增加了客戶端使用的負擔。我們希望這些都可以在服務(wù)器端完成:接收二進制圖像文件,預(yù)處理圖像,并且轉(zhuǎn)成模型需要的numpy數(shù)組。在DaaS中,我們可以通過創(chuàng)建自定義部署腳本來完成該該任務(wù),它允許用戶在模型部署中添加任意的數(shù)據(jù)預(yù)處理和后處理操作。切換到實時預(yù)測
標簽頁,點擊命令生成自定義實時預(yù)測腳本
,生成預(yù)定義腳本:
點擊作為API測試
命令,頁面切換到測試頁面,修改preprocess_files
函數(shù),并且添加處理圖像的函數(shù):
def rgb2gray(rgb):
"""Convert the input image into grayscale"""
import numpy as np
return np.dot(rgb[...,:3], [0.299, 0.587, 0.114])
def resize_img(img_to_resize):
"""Resize image to MNIST model input dimensions"""
import cv2
r_img = cv2.resize(img_to_resize, dsize=(28, 28), interpolation=cv2.INTER_AREA)
r_img.resize((1, 28, 28, 1))
r_img = 1 - r_img
return r_img
def preprocess_image(img_to_preprocess):
"""Resize input images and convert them to grayscale."""
if img_to_preprocess.shape == (28, 28):
img_to_preprocess.resize((1, 28, 28, 1))
img_to_preprocess = 1 - img_to_preprocess / 255
return img_to_preprocess
grayscale = rgb2gray(img_to_preprocess)
processed_img = resize_img(grayscale)
return processed_img
def preprocess_files(args):
"""preprocess the uploaded files"""
files = args.get('files')
if files is not None:
# get the first record object in X if it's present
if 'X' in args:
record = args['X'][0]
else:
record = {}
args['X'] = [record]
# TODO add your own custom opeartions, e.g. loading images, make transformation, then write back into X
import matplotlib.image as mpimg
for key, file in files.items():
img = mpimg.imread(file)
record[key] = preprocess_image(img)
return args
輸入函數(shù)名predict
,選擇請求正文基于表單
,輸入名稱input_1
,選擇文件,點擊上傳測試圖像2.png
,點擊提交
,右側(cè)響應(yīng)頁面顯示結(jié)果為:
{
"result": [
{
"dense": [
[
4.0412282942270394e-7,
5.612335129967505e-8,
0.9999896287918091,
0.0000014349453749673557,
2.1778572326623669e-13,
3.3688251840913175e-12,
1.8931906042851665e-10,
1.7151558395767097e-8,
0.000008489014362567104,
6.33769703384246e-10
]
]
}
],
"stderr": [
"Some warning messages here"
],
"stdout": []
}
繼續(xù)修改postprocess
函數(shù)為:
def postprocess(result):
"""postprocess the predicted results"""
import numpy as np
return [int(np.argmax(np.array(result).squeeze(), axis=0))]
重新提交
,右側(cè)響應(yīng)頁面顯示結(jié)果為:
測試完成后,可以創(chuàng)建正式的部署,切換到部署
標簽頁,點擊命令添加網(wǎng)絡(luò)服務(wù)
,輸入服務(wù)名稱mnist-svc
,其他使用默認選項,點擊創(chuàng)建
。進入到部署頁面后,點擊測試
標簽頁,該界面類似之前的腳本測試界面,輸入函數(shù)名predict
,請求正文選擇基于表單
,輸入名稱input_1
,類型選擇文件,點擊上傳測試的圖片后,點擊提交:
到此,正式部署已經(jīng)測試和創(chuàng)建完成,用戶可以使用任意的客戶端程序調(diào)用該部署服務(wù)。點擊以上界面中的生成代碼
命令,顯示如何通過curl命令調(diào)用該服務(wù),測試如下:
-
通過ONNX部署Keras模型:
- 轉(zhuǎn)換模型到ONNX:
import onnxmltools onnx_model = onnxmltools.convert_keras(model, model.name)
- 發(fā)布ONNX模型:
publish_resp = client.publish(onnx_model, name='keras-mnist-onnx', mining_function='classification', x_test=x_test, y_test=y_test, description='A tf.Keras classification model in ONNX') pprint(publish_resp)
結(jié)果如下:
{'model_name': 'keras-mnist-onnx', 'model_version': '1'}
- 測試ONNX模型:登陸DaaS Web客戶端,查看
keras-onnx-mnist
模型信息:
模型輸入字段input_1
,維數(shù)為(N,28,28,1),輸出字段dense
,維數(shù)為(N,10)。切換到測試
標簽頁,我們看到DaaS自動存儲了一條測試數(shù)據(jù),點擊提交
命令,測試該條數(shù)據(jù),如圖:
我們看到,該ONNX模型和原生Keras模型測試結(jié)果是一致的。
- 自定義部署ONNX模型:參考以上Keras模型,流程相同,就不再這里贅述。關(guān)于ONNX格式詳情以及部署,可以參考文章《使用ONNX部署深度學習和傳統(tǒng)機器學習模型》
總結(jié)
通過以上的演示,我們可以看到,DaaS在部署深度學習模型時的優(yōu)勢:既可以一鍵式創(chuàng)建默認部署,又可以靈活的自定義部署,滿足用戶多樣的部署需求。關(guān)于其他深度學習框架的部署,比如Pytorch,MXNet等,會在后續(xù)的文章中介紹。
參考
- DaaS-Client:https://github.com/autodeployai/daas-client
- AutoDeployAI:https://www.autodeploy.ai/