飞桨模型保存_飞桨实战笔记:自编写模型如何在服务器和移动端部署
?
作為深度學習小白一枚,從一開始摸索如何使用深度學習框架,怎么讓腳本跑起來,到現在開始逐步讀懂論文,看懂模型的網絡結構,按照飛槳官方文檔進行各種模型訓練和部署,整個過程遇到了無數問題。非常感謝飛槳開源社區的大力支持,并熱情答復我遇到的各種問題,使得我可以快速上手。特整理本篇學習筆記,以此回饋網友們的無私付出。大家都共享一點點,一起為深度學習的推進添磚加瓦(哈哈,非常正能量,有木有!)
這篇文章詳細記錄了如何使用百度深度學習平臺——飛槳進行SSD目標檢測模型的訓練、以及如何將模型部署到服務器和移動端。文末給出了筆者認為非常有用的資料鏈接。
本文的代碼基于百度AI Studio官方示例代碼,并能夠在飛槳 1.7.1上跑通,Python版本是3.7。
SSD模型介紹
如果你對經典的CNN模型比較熟悉的話,那么SSD也并不難理解。SSD大體上來說是將圖片分為6種不同大小的網格,找到目標中心的落點,確定物體的位置。在分成不同網格之后,會在此之上取到不同數目的先驗框,對先驗框進行回歸、分類預測。先驗框的數目足夠多,幾乎能夠涵蓋整個圖片,因此我們可以找到包含物體的很多個先驗框,最后進行非極大抑制就能得到正確結果。
b圖就是我們以每個網格為中心,取到的先驗框的示例。c圖的回歸預測找到了目標的位置信息,分類預測確定了物體的類別。a圖代表了最終的結果。
上面的兩個圖片摘自論文_SSD: Single Shot MultiBox Detector_,在論文中SSD是插入到VGG-16網絡中的。
?
通過一個表格我們能夠知道我們從不同層中得到的先驗框尺寸和數目:
總共我們會獲得8732個先驗框。
MobileNet 與 SSD結合
前面說到我們可以很方便地將SSD插入到不同網絡,那么考慮到我們的應用場景,我們可以使用諸如MobileNet網絡來減少計算量。
MobileNet將卷積分為Depthwise和Pointwise兩部分,減少了計算量,同時不會損失過多的精度。也因此在移動設備和嵌入式設備上面有很好的應用前景。更多關于MobileNet的理論信息大家可以在網上找到,這里不做過多講述。
百度AI Studio上官方開源了基于SSD的目標檢測模型的代碼,代碼非常好讀,并可以直接在線運行,同時提供了訓練好的SSD模型。從代碼中我們可以看到,飛槳提供了paddle.fluid.layers.multi_box_head在不同Feature Map上面提取先驗框、計算回歸坐標等,paddle.fluid.layers.ssd_loss計算loss,paddle.fluid.initializer.MSRAInitializer實現以MSRA的方式初始化權重等等。這些API能夠減輕我們的工作量,方便代碼編寫。官方代碼還可以導出,在本地Python 3和飛槳 1.7上執行。
服務器部署
下面我們來使用Paddle Serving作為模型即服務后端。隨著飛槳框架推出1.7版本,Paddle Serving也登上了舞臺。Paddle Serving提出了模型即服務的理念,致力于簡化模型部署到服務器操作,甚至一行命令實現模型部署。有了Paddle Serving,可以大大減輕搭建部署環境的負擔。
?
需要注意的是Paddle Serving目前不支持arm64架構,并且對一些依賴包的版本有要求,所以強烈建議使用Docker進行部署。
首先我們pull到Docker 鏡像:
# Run CPU Docker
docker pull hub.baidubce.com/paddlepaddle/serving:0.2.0
docker run -p 9292:9292 --name test -dit hub.baidubce.com/paddlepaddle/serving:0.2.0
docker exec -it test bash
# Run GPU Docker
nvidia-docker pull hub.baidubce.com/paddlepaddle/serving:0.2.0-gpu
nvidia-docker run -p 9292:9292 --name test -dit hub.baidubce.com/paddlepaddle/serving:0.2.0-gpu
nvidia-docker exec -it test bash
進入容器之后,由于官方縮減了鏡像的大小,我們需要手動安裝需要的依賴包:
python3 -m pip install paddle_serving_server sentencepiece opencv-python pillow -i https://pypi.tuna.tsinghua.edu.cn/simple
鏡像使用的系統是Centos 7,注意直接運行Python的話指向的是Python 2.7.5,你需要使用python3。(Python 2即將停止維護,pip在后續版本也可能不提供支持)。
Paddle Serving與直接利用模型不同的是,除了需要導出inference model以外還需要生成配置文件,定義Feed和Fetch的內容。如果你非常熟悉保存預測模型的接口,那么這并不是一件難事。從零開始訓練一個模型,并應用到Paddle Serving,你可以參考官方的端到端從訓練到部署全流程
這里我們可以直接利用上文提到的AI Studio的開源項目進行提取,真正的提取代碼僅需要兩行:
import paddle_serving_client.io as serving_io
serving_io.save_model(
"ssd_model",
"ssd_client_conf",
{'image': img},
{"prediction": box},
inference_program)
前兩行定義了我們的模型和客戶端配置文件保存位置,后面的兩個dict分別表示feed和fetch的內容,官方文檔的例子表示這是我們在訓練模型時的輸入和輸出。這里的img和box即為輸入網絡的img和網絡輸出的box,我們看下兩個的結構。
img:
name: "img"
type {
type: LOD_TENSOR
lod_tensor {
tensor {
data_type: FP32
dims: -1
dims: 3
dims: 300
dims: 300
}
lod_level: 0
}
}
persistable: false
box:
name: "concat_0.tmp_0"
type {
type: LOD_TENSOR
lod_tensor {
tensor {
data_type: FP32
dims: 1917
dims: 4
}
lod_level: 0
}
}
persistable: false
可以在保存預測模型的時候保存Paddle Serving需要的配置項,或者之后從訓練的代碼中提取出img和box,進行保存。得到Paddle Serving需要的相關文件之后,利用下面的代碼將其部署到服務器上(均在容器內進行,保證生成的模型和客戶端配置和服務器腳本在同一目錄之下):
import os
import sys
import base64
import numpy as np
import importlib
from paddle_serving_app import ImageReader
from multiprocessing import freeze_support
from paddle_serving_server.web_service import WebService
class ImageService(WebService):
def preprocess(self, feed={}, fetch=[]):
reader = ImageReader(image_shape=[3, 300, 300],
image_mean=[0.5, 0.5, 0.5],
image_std=[0.5, 0.5, 0.5])
feed_batch = []
for ins in feed:
if "image" not in ins:
raise ("feed data error!")
sample = base64.b64decode(ins["image"])
img = reader.process_image(sample)
feed_batch.append({"image": img})
return feed_batch, fetch
image_service = ImageService(name="image")
image_service.load_model_config("./ssd_model/")
image_service.prepare_server(
workdir="./work", port=int(9292), device="cpu")
image_service.run_server()
image_service.run_flask()
在代碼中先對得到的image進行了resize,然后交給模型處理。這里使用的是CPU進行預測,需要的話可以修改幾行代碼使其能夠在GPU上預測。使用Paddle Serving并不需要安裝飛槳,所以不會對服務器造成負擔。Paddle Serving內置了數據預處理功能,因此可以直接對圖片進行裁剪等操作。
在客戶端上,僅僅需要幾行代碼就能夠從服務端獲取預測結果:
import requests
import base64
import json
import time
import os
import sys
py_version = sys.version_info[0]
def predict(image_path, server):
if py_version == 2:
image = base64.b64encode(open(image_path).read())
else:
image = base64.b64encode(open(image_path, "rb").read()).decode("utf-8")
req = json.dumps({"feed": [{"image": image}], "fetch": ["prediction"]})
r = requests.post(
server, data=req, headers={"Content-Type": "application/json"}, timeout=60)
try:
print(r.json()["result"]["prediction"])
except ValueError:
print(r.text)
return r
if __name__ == "__main__":
server = "http://[ip]:[port]/image/prediction"
image_list = os.listdir("./images")
start = time.time()
for img in image_list:
image_file = "./images/" + img
res = predict(image_file, server)
end = time.time()
print(end - start)
對圖片進行base64編碼,發送到服務端,獲取結果,非常簡潔和方便。在實際部署的過程中,可以在服務端進行反代和鑒權,只需要寫一個中間件即可,這也是模型即服務帶給大家的便利之處。
?
我們國內服務端的配置是單核CPU(限制使用時間和頻率),算上網絡傳輸和預測的總用時在0.39秒左右,比較快速。返回的數組第一個值代表了對應類別,第二個值代表置信度,后面的值代表坐標比例,實際使用的時候需要設置閾值,放棄可信度較低的值。
移動端部署
移動端部署采用了之前開源的Real-time Object Detector,當時源碼中使用的是YOLO v3模型,這里我們將使其適配SSD模型。在端側部署方面我們使用的是Paddle Lite,這是飛槳系列中的多平臺高性能深度學習預測引擎,提供了多平臺架構下的預測解決方案,還支持C++/Java/Python等語言。
從上次發文到現在,Paddle Lite已經推出了新的版本,2.3版本對很多東西進行了優化,利用手上的安卓手機(麒麟 810)進行SSD目標檢測的用時僅為500ms。這次我們還能夠直接使用官方提供的預編譯庫進行預測,并不需要自己手動編譯一次。下載下來之后我們會得到和上次一樣的文件,PaddlePredictor.jar和一些so鏈接庫,參考之前的推送文章:如何基于Flutter和Paddle Lite實現實時目標檢測,放到相應位置即可。
因為SSD模型的輸入和YOLO v3不一樣,我們需要對安卓端的Predictor.java進行修改,主要考慮輸入的尺寸問題。
// MainActivity.java L41
protected long[] inputShape = new long[]{1, 3, 300, 300};
protected float[] inputMean = new float[]{0.5f, 0.5f, 0.5f};
protected float[] inputStd = new float[]{0.5f, 0.5f, 0.5f};
// Predictor.java L214
// Set input shape
Tensor inputTensor = getInput(0);
inputTensor.resize(inputShape);
// Predictor.java L258
inputTensor.setData(inputData);
// Predictor.java L303
float rawLeft = outputTensor.getFloatData()[i + 2];
float rawTop = outputTensor.getFloatData()[i + 3];
float rawRight = outputTensor.getFloatData()[i + 4];
float rawBottom = outputTensor.getFloatData()[i + 5];
同時我們對于描框的函數進行修改:
// main.dart L127 var ratioW = sizeRed.width / 300; var ratioH = sizeRed.height / 300;
如果在運行的時候出現了空指針錯誤,很可能你沒有升級到最新的預編譯庫,jar和so文件均需要更新。由于上次發布源碼的時候沒有在Gradle腳本中設置自動下載庫,所以需要手動放置預測庫。
寫在最后
從一開始熟悉怎么去使用飛槳深度學習平臺,怎么讓腳本跑起來,到現在開始逐步讀懂論文,了解模型的架構,看官方文檔,過程中遇到了不少問題。通過分析飛槳官方圖像分類示例,查看和修改源碼,輸出調試信息,還在飛槳官方QQ群中得到了不少幫助,學到了很多東西,并最終完成了這次實踐。非常感謝提供幫助的朋友們。飛槳經過多輪更新,在模型訓練和部署上也變得非常簡單,相信會吸引越來越多的開發者使用。
參考鏈接:
如果您加入官方QQ群,您將遇上大批志同道合的深度學習同學。飛槳PaddlePaddle交流3群:703252161。
如果您想詳細了解更多飛槳的相關內容,請參閱以下文檔。
官網地址:
飛槳開源框架項目地址:
GitHub:
Gitee:
總結
以上是生活随笔為你收集整理的飞桨模型保存_飞桨实战笔记:自编写模型如何在服务器和移动端部署的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 紫光展锐发布2022年度盘点:5G芯片完
- 下一篇: 进来看看!2022年度最全手机排行榜出炉