Edgeboard試用 — 基于CIFAR10分類模型的移植

前言


在上一周的測試中,我們按照官方給的流程,使用EasyDL快速實現(xiàn)了一個具有性別檢測功能的人臉識別系統(tǒng),那么

今天,我們將要試一下通過Paddlepaddle從零開始,訓練一個自己的多分類模型,并進行嵌入式部署。 整個訓練

過程和模型在:https://aistudio.baidu.com/aistudio/projectDetail/61103 下面詳細介紹模型訓練的過程.

數(shù)據(jù)集準備


我們使用CIFAR10數(shù)據(jù)集。CIFAR10數(shù)據(jù)集包含60,000張32x32的彩色圖片,10個類別,每個類包含6,000張。其中

50,000張圖片作為訓練集,10000張作為驗證集。

!mkdir ‐p /home/aistudio/.cache/paddle/dataset/cifar # wget將下載的文件存放到指定的文件夾下,同時重命名下載的文件,利用‐O !wget "http://ai‐atest.bj.bcebos.com/cifar‐10‐python.tar.gz" ‐O cifar‐10‐python.tar.gz !mv cifar‐10‐python.tar.gz /home/aistudio/.cache/paddle/dataset/cifar/

模型結(jié)構(gòu)


我們選擇了以三個卷積層串聯(lián)一個全連接層的輸出,作為貓狗分類的預測,采用固定維度輸入,輸出為分類數(shù)

def convolutional_neural_network(img): # 第一個卷積‐池化層 conv_pool_1 = fluid.nets.simple_img_conv_pool( input=img, # 輸入圖像 filter_size=5, # 濾波器的大小 num_filters=20, # filter 的數(shù)量。它與輸出的通道相同 pool_size=2, # 池化層大小2*2 pool_stride=2, # 池化層步長 act="relu") # 激活類型 # 第二個卷積‐池化層 conv_pool_2 = fluid.nets.simple_img_conv_pool( input=conv_pool_1, filter_size=5, num_filters=50, pool_size=2, pool_stride=2, act="relu") # 第三個卷積‐池化層 conv_pool_3 = fluid.nets.simple_img_conv_pool( input=conv_pool_2, filter_size=5, num_filters=50, pool_size=2, pool_stride=2, act="relu") # 以softmax為激活函數(shù)的全連接輸出層,10類數(shù)據(jù)輸出10個數(shù)字 prediction = fluid.layers.fc(input=conv_pool_3, size=10, act='softmax') return prediction

訓練&驗證


接下來在Paddlepaddle fluid上,進行訓練。整個訓練代碼見附件train.py 模型驗證,采用附件predict.py的代碼進

行驗證與運行時間的測量,選取一張狗的圖:dog.jpg (可以fork首頁鏈接aistudio平臺上的demo) 連續(xù)預測10000

次,輸出如下:

CPU 運行結(jié)果為:預處理時間為0.0006270000000085929,預測時間為:16.246494 Out: im_shape的維度: (1, 3, 32, 32) The run time of image process is 0.0006270000000085929 The run time of predict is 16.246494 results [array([[5.0159363e‐04, 3.5942634e‐05, 2.5955746e‐02, 4.7745958e‐02, 9.9251214e‐03, 9.0146154e‐01, 1.9564393e‐03, 1.2230080e‐02, 4.7619540e‐08, 1.8753216e‐04]], dtype=float32)] infer results: dog
GPU V100 運行結(jié)果為:預處理時間為0.0006390000000067175,預測時間為:15.903074000000018 Out: im_shape的維度: (1, 3, 32, 32) The run time of image process is 0.0006390000000067175 The run time of predict is 15.903074000000018 results [array([[5.0159392e‐04, 3.5942641e‐05, 2.5955772e‐02, 4.7746032e‐02, 9.9251205e‐03, 9.0146142e‐01, 1.9564414e‐03, 1.2230078e‐02, 4.7619821e‐08, 1.8753250e‐04]], dtype=float32)] infer results: dog

可以看到,模型可以正確的識別出圖片中的動物為狗,接下來,我們就要嘗試將這個模型部署到Edgeboard上面。

模型導出


我們需要將模型保存為模型文件model以及權(quán)重文件params,可以采用如下Paddle的API進行保存如圖所示,在AiStudio的左側(cè)打開模型文件所在的文件夾,下載mlp-model、mlp-params兩個文件。

在Edgeboard上部署模型,完成預測


1、新建工程文件夾,目錄結(jié)構(gòu)如下(可以仿照sample里的resnet、inception例程):

‐sample_image_catdog       ‐build       ‐image       ‐include              ‐paddlepaddle‐mobile              ‐...       ‐lib             ‐libpaddle‐mobile.so       ‐model           ‐mlp              ‐model              ‐params       ‐src              ‐fpga_cv.cpp              ‐main.cpp

2、將AiStudio上導出來的模型放置在model里的mlp文件夾,修改名字為model、params

3、新建 CMakeLists.txt

cmake_minimum_required(VERSION 3.5.1) project(paddle_edgeboard) set(CMAKE_CXX_STANDARD 14) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ‐pthread") add_definitions(‐DPADDLE_MOBILE_FPGA_V1) add_definitions(‐DPADDLE_MOBILE_FPGA) set(PADDLE_LIB_DIR "${PROJECT_SOURCE_DIR}/lib" ) set(EASYDL_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include" ) set(PADDLE_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include/paddle‐mobile" ) set(APP_NAME "paddle_edgeboard" ) aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC) find_package(OpenCV QUIET COMPONENTS core videoio highgui imgproc imgcodecs ml video) include_directories(SYSTEM ${OpenCV_INCLUDE_DIRS}) #list(APPEND Caffe_LINKER_LIBS ${OpenCV_LIBS}) message(STATUS "OpenCV found (${OpenCV_CONFIG_PATH}),${OpenCV_LIBS}") #add_definitions(‐DUSE_OPENCV) include_directories(${EASYDL_INCLUDE_DIR}) include_directories(${PADDLE_INCLUDE_DIR}) LINK_DIRECTORIES(${PADDLE_LIB_DIR}) add_executable(${APP_NAME} ${SRC}) target_link_libraries(${APP_NAME} paddle‐mobile) target_link_libraries(${APP_NAME} ${OpenCV_LIBS} )

4、main.cpp

#include <iostream> #include "io/paddle_inference_api.h" #include "math.h" #include <stdlib.h> #include <unistd.h> #include <fstream> #include <ostream> #include <fstream> #include <iomanip> #include <typeinfo> #include <typeindex> #include <vector> #include<ctime> #include "fpga/KD/float16.hpp" #include "fpga/KD/llapi/zynqmp_api.h" using namespace paddle_mobile; #include <opencv2/highgui.hpp> #include <opencv2/imgproc.hpp> using namespace cv; cv::Mat sample_float; static std::vector<std::string> label_list(10); void readImage(std::string filename, float* buffer) { Mat img = imread(filename); if (img.empty()) { std::cerr << "Can't read image from the file: " << filename << std::endl; exit(‐1); } Mat img2; resize(img, img2, Size(32,32)); img2.convertTo(sample_float, CV_32FC3); int index = 0; for (int row = 0; row < sample_float.rows; ++row) { float* ptr = (float*)sample_float.ptr(row); for (int col = 0; col < sample_float.cols; col++) { float* uc_pixel = ptr; // uc_pixel[0] ‐= 102; // uc_pixel[1] ‐= 117; // uc_pixel[1] ‐= 124; float r = uc_pixel[0]; float g = uc_pixel[1]; float b = uc_pixel[2]; buffer[index] = b / 255.0; buffer[index + 1] = g / 255.0; buffer[index + 2] = r / 255.0; // sum += a + b + c; ptr += 3; // DLOG << "r:" << r << " g:" << g << " b:" << b; index += 3; } } // return sample_float; } PaddleMobileConfig GetConfig() { PaddleMobileConfig config; config.precision = PaddleMobileConfig::FP32; config.device = PaddleMobileConfig::kFPGA; // config.model_dir = "../models/mobilenet/"; config.prog_file = "../model/mlp/model"; config.param_file = "../model/mlp/params"; config.thread_num = 4; return config; } int main() { clock_t startTime,endTime; zynqmp::open_device(); std::cout << " open_device success " << std::endl; PaddleMobileConfig config = GetConfig(); std::cout << " GetConfig success " << std::endl; auto predictor = CreatePaddlePredictor<PaddleMobileConfig, PaddleEngineKind::kPaddleMobile>(config); std::cout << " predictor success " << std::endl; startTime = clock();//計時開始 float data[1 * 3 * 32 * 32] = {1.0f}; readImage("../image/cat.jpg", data); endTime = clock();//計時結(jié)束 std::cout << "The run time of image process is: " <<(double)(endTime ‐ startTime) / CLOCKS_PER_SEC << "s" << std::endl; PaddleTensor tensor; tensor.shape = std::vector<int>({1, 3, 32, 32}); tensor.data = PaddleBuf(data, sizeof(data)); tensor.dtype = PaddleDType::FLOAT32; std::vector<PaddleTensor> paddle_tensor_feeds(1, tensor); PaddleTensor tensor_out; tensor_out.shape = std::vector<int>({}); tensor_out.data = PaddleBuf(); tensor_out.dtype = PaddleDType::FLOAT32; std::vector<PaddleTensor> outputs(1, tensor_out); std::cout << " before predict " << std::endl; predictor‐>Run(paddle_tensor_feeds, &outputs); std::cout << " after predict " << std::endl; // assert(); endTime = clock();//計時結(jié)束 std::cout << "The run time of predict is: " <<(double)(endTime ‐ startTime) / CLOCKS_PER_SEC << "s" << std::endl; float* data_o = static_cast<float*>(outputs[0].data.data()); for (size_t j = 0; j < outputs[0].data.length() / sizeof(float); ++j) { std::cout << "output[" << j << "]: " << data_o[j] << std::endl; } int index = 0; float max = 0.0; for (int i = 0;i < 10; i++) { float val = data_o[i]; if (val > max) { max = val > max ? val : max; iindex = i; } } label_list = {"airplane", "automobile", "bird", "cat", "deer", "dog", "frog", "horse", "ship", "truck" }; std::cout << "Result" << " is " << label_list[index] << std::endl; return 0; }

5、編譯運行

insmod /home/root/workspace/driver/fpgadrv.ko cd /home/root/workspace/sample/sample_image_catdog mkdir build cd build rm ‐rf * cmake .. make ./paddle_edgeboard

修改main文件要預測的圖像:

6、修改main文件后重復執(zhí)行預測,可得結(jié)果如下:圖像處理時間大概為:0.006秒,預測時間大概為:0.008秒

7、連續(xù)預測10000次所用時間為:23.7168

性能對比(連續(xù)預測10000次 單位:秒)

image.png

總結(jié)

優(yōu)點:

1. EdgeBoard內(nèi)置的Paddle-Mobile,可以與Paddle訓練出來的模型進行較好的對接。

2. 預測速度上: Edge在預測小模型的時候,能與雙核CPU和GPU在一個數(shù)量級,估計是模型較小,batch size也

為1,gpu,cpu的性能優(yōu)勢抵不過通信的開銷,后續(xù)將進行大模型、高batch size的測試。

3. 提供的demo也足夠簡單,修改起來難度很低。

不足:

1. Paddle-Mobile相關文檔具有一定門檻,且較為分散。初次使用的時候會走一些彎路出現(xiàn)問題的時候往往是個

黑盒,不易于定位。在這次進行模型訓練的嘗試中,出現(xiàn)過一次op不支持的情況,我們在官網(wǎng)上甚至沒有找

到支持的op列表,這個在開發(fā)哥們的支持下升級版本后解決。如果后續(xù)能在穩(wěn)定的固件版本下使用,并有比

較易用的sdk,開發(fā)門檻可能會進一步降低。

極客網(wǎng)企業(yè)會員

免責聲明:本網(wǎng)站內(nèi)容主要來自原創(chuàng)、合作伙伴供稿和第三方自媒體作者投稿,凡在本網(wǎng)站出現(xiàn)的信息,均僅供參考。本網(wǎng)站將盡力確保所提供信息的準確性及可靠性,但不保證有關資料的準確性及可靠性,讀者在使用前請進一步核實,并對任何自主決定的行為負責。本網(wǎng)站對有關資料所引致的錯誤、不確或遺漏,概不負任何法律責任。任何單位或個人認為本網(wǎng)站中的網(wǎng)頁或鏈接內(nèi)容可能涉嫌侵犯其知識產(chǎn)權(quán)或存在不實內(nèi)容時,應及時向本網(wǎng)站提出書面權(quán)利通知或不實情況說明,并提供身份證明、權(quán)屬證明及詳細侵權(quán)或不實情況證明。本網(wǎng)站在收到上述法律文件后,將會依法盡快聯(lián)系相關文章源頭核實,溝通刪除相關內(nèi)容或斷開相關鏈接。

2019-07-16
Edgeboard試用 — 基于CIFAR10分類模型的移植
前言在上一周的測試中,我們按照官方給的流程,使用EasyDl快速實現(xiàn)了一個具有性別檢測功能的人臉識別系統(tǒng),那么今天,我們將要試一下通過Paddlepaddle從

長按掃碼 閱讀全文