Paddle-Lite 安卓端部署
Paddle-Lite 安卓端部署
最近因為疫情原因宅在家里,就搜集了些照片用PaddleDetection訓練了一個口罩分類的模型,摸索了一下Paddle-Lite的Andriod部署,恰好Paddle-Lite最近也有比較大的迭代更新,這篇博客記錄了我的摸索過程和一點點心得。
我不太熟悉Andriod開發(fā),此demo僅僅在Paddle-Lite-DemoPaddle-Lite-Demo的基礎上替換模型,修改了少量代碼,以跑通訓練和部署流程為目的。
如果此文能幫到大家,請給個小星星吧 Github
廢話不多說,先給大家展示一下效果:
因為本人爬蟲能力有限,也就搜集了600多張圖片,數(shù)據(jù)集太小,最后的mAP大概在90左右。
正文開始:
1.PaddlePaddle開發(fā)環(huán)境:
如果自己沒有支持CUDA的GPU設備的話可以選擇百度官方的AI-Studio。
自己有設備的話可以參照PaddleDection安裝說明配置開發(fā)環(huán)境。
注:請注意匹配PaddlePaddle版本(1.7)和PaddleDetection分支(0.2)。
2.模型訓練:
具體訓練流程請參考PaddleDetection官方教程。
我采用的模型是yolov3_mobilenet_v1。如果你還沒開始訓練的話,請你選擇yolo系列的模型,因為不同模型的輸入輸出有所不同。
3.模型導出:
假設你模型已經(jīng)訓練完畢并且保存在了
/_path_/_to_/_dir_/PaddleDetection/output/yolov3_mobilenet_v1_mask請在PaddleDection目錄下執(zhí)行以下代碼:
python tools/export_model.py -c configs/yolov3_mobilenet_v1_mask.yml \--output_dir=./inference_model \-o weights=output/yolov3_mobilenet_v1_mask/model_final預測模型會導出到inference_model/yolov3_mobilenet_v1_mask目錄下,模型名和參數(shù)名分別為__model__和__params__。
具體導出方法請參考:這里。
4.模型轉(zhuǎn)換:
工具準備
模型轉(zhuǎn)換需要用到Paddle-Lite提供的模型轉(zhuǎn)換工具:opt
這里我們使用Paddle-Lite官方發(fā)布的版本,Paddle-Lite Github倉庫的release界面,選擇release版本下載對應的轉(zhuǎn)化工具。
也可以參考文檔自行編譯。
轉(zhuǎn)換模型
我們將opt工具拷貝到PaddleDetection目錄下,執(zhí)行以下命令:
./opt --model_file=yolov3_mobilenet_v1_mask/__model__ \--param_file=/yolov3_mobilenet_v1_mask/__params__ \--optimize_out=mask \--optimize_out_type=naive_buffe(3) 更詳盡的轉(zhuǎn)化命令總結(jié):
./opt \--model_dir=<model_param_dir> \--model_file=<model_path> \--param_file=<param_path> \--optimize_out_type=(protobuf|naive_buffer) \--optimize_out=<output_optimize_model_dir> \--valid_targets=(arm|opencl|x86|npu|xpu) \--prefer_int8_kernel=(true|false) \--record_tailoring_info =(true|false) 選項 說明 --model_dir 待優(yōu)化的PaddlePaddle模型(非combined形式)的路徑 --model_file 待優(yōu)化的PaddlePaddle模型(combined形式)的網(wǎng)絡結(jié)構(gòu)文件路徑。 --param_file 待優(yōu)化的PaddlePaddle模型(combined形式)的權(quán)重文件路徑。 --optimize_out_type 輸出模型類型,目前支持兩種類型:protobuf和naive_buffer,其中naive_buffer是一種更輕量級的序列化/反序列化實現(xiàn)。若您需要在mobile端執(zhí)行模型預測,請將此選項設置為naive_buffer。默認為protobuf。 --optimize_out 優(yōu)化模型的輸出路徑。 --valid_targets 指定模型可執(zhí)行的backend,默認為arm。目前可支持x86、arm、opencl、npu、xpu,可以同時指定多個backend(以空格分隔),Model Optimize Tool將會自動選擇最佳方式。如果需要支持華為NPU(Kirin 810/990 Soc搭載的達芬奇架構(gòu)NPU),應當設置為npu, arm。 --prefer_int8_kernel 若待優(yōu)化模型為int8量化模型(如量化訓練得到的量化模型),則設置該選項為true以使用int8內(nèi)核函數(shù)進行推理加速,默認為false。 --record_tailoring_info 當使用 根據(jù)模型裁剪庫文件 功能時,則設置該選項為true,以記錄優(yōu)化后模型含有的kernel和OP信息,默認為false。** 如果待優(yōu)化的fluid模型是非combined形式,請設置–model_dir,忽略–model_file和–param_file。
如果待優(yōu)化的fluid模型是combined形式,請設置–model_file和–param_file,忽略–model_dir。
優(yōu)化后的模型為以.nb名稱結(jié)尾的單個文件。**
到這里我們已經(jīng)得到了mask.nb這個模型文件。
5. 準備Paddle-Lite-Demo
參考Github的readme準備demo。
在官方 release 預編譯庫下載編譯庫并替換demo中的庫,或者手動編譯。
Android更新預測庫
替換jar文件:將生成的build.lite.android.xxx.gcc/inference_lite_lib.android.xxx/java/jar/PaddlePredictor.jar替換demo中的Paddle-Lite-Demo/PaddleLite-android-demo/image_classification_demo/app/libs/PaddlePredictor.jar
替換arm64-v8a jni庫文件:將生成build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/java/so/libpaddle_lite_jni.so庫替換demo中的Paddle-Lite-Demo/PaddleLite-android-demo/image_classification_demo/app/src/main/jniLibs/arm64-v8a/libpaddle_lite_jni.so
替換armeabi-v7a jni庫文件:將生成的build.lite.android.armv7.gcc/inference_lite_lib.android.armv7/java/so/libpaddle_lite_jni.so庫替換demo中的Paddle-Lite-Demo/PaddleLite-android-demo/image_classification_demo/app/src/main/jniLibs/armeabi-v7a/libpaddle_lite_jni.so.
注意,一定要換的最新的預測庫
編譯運行object_detection_demo,確保能運行。
6.替換原demo中的模型
原demo運行成功,接下來該換上我們自己的模型了。
1. 將模型拷貝到 Paddle-Lite-Demo/PaddleLite-android-demo/object_detection_demo/app/src/main/assets/models/mask 目錄下并改名為model.nb。
2. 將訓練模型時的mask_label_list文件拷貝到 Paddle-Lite-Demo/PaddleLite-android-demo/object_detection_demo/app/src/main/assets/labels/mask_label_list 。
3. 修改 Paddle-Lite-Demo/PaddleLite-android-demo/object_detection_demo/app/src/main/res/values/strings.xml 文件。
<string name="MODEL_PATH_DEFAULT">models/mask</string> <string name="LABEL_PATH_DEFAULT">labels/mask_label_list</string><string name="INPUT_SHAPE_DEFAULT">1,3,320,320</string> <string name="INPUT_MEAN_DEFAULT">0.485,0.456,0.406</string> <string name="INPUT_STD_DEFAULT">0.229,0.224,0.225</string>4.修改代碼:
將:
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};修改為:
protected long[] inputShape = new long[]{1, 3, 320, 320}; protected float[] inputMean = new float[]{0.485f, 0.456f, 0.406f}; protected float[] inputStd = new float[]{0.229f, 0.224f, 0.225f};** 其中320為模型中圖片輸入的大小,如果你的模型為608,請改成608。*
修改模型輸入部分為:
// Set input shapeTensor inputTensor0 = getInput(0);inputTensor0.resize(inputShape);Tensor inputTensor1 = getInput(1);inputTensor1.resize(new long[] {1,2});// Pre-process image, and feed input tensor with pre-processed dataDate start = new Date();int channels = (int) inputShape[1];int width = (int) inputShape[3];int height = (int) inputShape[2];float[] inputData = new float[channels * width * height];if (channels == 3) {int[] channelIdx = null;if (inputColorFormat.equalsIgnoreCase("RGB")) {channelIdx = new int[]{0, 1, 2};} else if (inputColorFormat.equalsIgnoreCase("BGR")) {channelIdx = new int[]{2, 1, 0};} else {Log.i(TAG, "Unknown color format " + inputColorFormat + ", only RGB and BGR color format is " +"supported!");return false;}int[] channelStride = new int[]{width * height, width * height * 2};for (int y = 0; y < height; y++) {for (int x = 0; x < width; x++) {int color = inputImage.getPixel(x, y);float[] rgb = new float[]{(float) red(color) / 255.0f, (float) green(color) / 255.0f,(float) blue(color) / 255.0f};inputData[y * width + x] = (rgb[channelIdx[0]] - inputMean[0]) / inputStd[0];inputData[y * width + x + channelStride[0]] = (rgb[channelIdx[1]] - inputMean[1]) / inputStd[1];inputData[y * width + x + channelStride[1]] = (rgb[channelIdx[2]] - inputMean[2]) / inputStd[2];}}} else if (channels == 1) {for (int y = 0; y < height; y++) {for (int x = 0; x < width; x++) {int color = inputImage.getPixel(x, y);float gray = (float) (red(color) + green(color) + blue(color)) / 3.0f / 255.0f;inputData[y * width + x] = (gray - inputMean[0]) / inputStd[0];}}} else {Log.i(TAG, "Unsupported channel size " + Integer.toString(channels) + ", only channel 1 and 3 is " +"supported!");return false;}inputTensor0.setData(inputData);inputTensor1.setData(new int[] {320,320});Date end = new Date();preprocessTime = (float) (end.getTime() - start.getTime());修改模型的輸出的處理部分:
yolo 的模型輸出為坐標值,ssd為坐標點的相對值,這里統(tǒng)一/320 就轉(zhuǎn)換成了相對值,省去了調(diào)整加框部分代碼的麻煩。
至此,Andriod端的部署就完成了。試著運行一下吧!
附
關(guān)于Andriod端部署:
直接替換模型不修改代碼的話是跑不通的,主要是因為屬于沒有預處理成模型的能接收的數(shù)據(jù)。
這里表現(xiàn)在:
在替換模型之后記得要修改模型的與處理部分,以及模型輸出的處理部分。
如果對模型的部署還有問題,歡迎大家來paddle-lite官方群(696965088)和小伙伴們一起探討。
apk下載鏈接:https://pan.baidu.com/s/1uWTRb0EvV6gQJF8x8D2pPQ 密碼:utl2
模型文件下載鏈接:https://pan.baidu.com/s/1Qn3j8tBfHG5JOZ-Ye7oU-Q 密碼:cuir
完整代碼鏈接:https://github.com/ralph0813/Paddle_Lite_Object_Detection_Demo
如果對模型的部署還有問題,歡迎大家來paddle-lite官方群(696965088)和小伙伴們一起探討。
apk下載鏈接:https://pan.baidu.com/s/1uWTRb0EvV6gQJF8x8D2pPQ 密碼:utl2
模型文件下載鏈接:https://pan.baidu.com/s/1Qn3j8tBfHG5JOZ-Ye7oU-Q 密碼:cuir
完整代碼鏈接:https://github.com/ralph0813/Paddle_Lite_Object_Detection_Demo
注:僅供測試使用,非百度官方發(fā)布模型
總結(jié)
以上是生活随笔為你收集整理的Paddle-Lite 安卓端部署的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sql包含怎么写_实习生简历怎么写?都包
- 下一篇: 一个正方形截取四分之一后如何四等分