订阅业界RSS CSDN首页> 业界

Edgeboard试用 — 基于CIFAR10分类模型的移植

发表于2019-07-04 15:53| 来源CSDN| 作者Litchll

摘要:前言 在上一周的测试中,我们按照官方给的流程,使用EasyDL快速实现了一个具有性别检测功能的人脸识别系统,那么 今天,我们将要试一下通过Paddlepaddle从零开始,训练一个自己的多分类模型,并进行嵌入式部署。 整个训练 过程和模型在:https://aistudio.baidu.com/aistudio/projectDetail/61103 下面详细介绍模型训练...

前言


在上一周的测试中,我们按照官方给的流程,使用EasyDL快速实现了一个具有性别检测功能的人脸识别系统,那么

今天,我们将要试一下通过Paddlepaddle从零开始,训练一个自己的多分类模型,并进行嵌入式部署。 整个训练

过程和模型在:https://aistudio.baidu.com/aistudio/projectDetail/61103 下面详细介绍模型训练的过程.

数据集准备


我们使用CIFAR10数据集。CIFAR10数据集包含60,000张32x32的彩色图片,10个类别,每个类包含6,000张。其中

50,000张图片作为训练集,10000张作为验证集。

  1. !mkdir ‐p /home/aistudio/.cache/paddle/dataset/cifar 
  2. # wget将下载的文件存放到指定的文件夹下,同时重命名下载的文件,利用‐O 
  3. !wget "http://ai‐atest.bj.bcebos.com/cifar‐10‐python.tar.gz" ‐O cifar‐10‐python.tar.gz 
  4. !mv cifar‐10‐python.tar.gz /home/aistudio/.cache/paddle/dataset/cifar/ 

模型结构


我们选择了以三个卷积层串联一个全连接层的输出,作为猫狗分类的预测,采用固定维度输入,输出为分类数

  1. def convolutional_neural_network(img): 
  2. # 第一个卷积‐池化层 
  3. conv_pool_1 = fluid.nets.simple_img_conv_pool( 
  4. input=img, # 输入图像 
  5. filter_size=5, # 滤波器的大小 
  6. num_filters=20, # filter 的数量。它与输出的通道相同 
  7. pool_size=2, # 池化层大小2*2 
  8. pool_stride=2, # 池化层步长 
  9. act="relu") # 激活类型 
  10. # 第二个卷积‐池化层 
  11. conv_pool_2 = fluid.nets.simple_img_conv_pool( 
  12. input=conv_pool_1
  13. filter_size=5
  14. num_filters=50
  15. pool_size=2
  16. pool_stride=2
  17. act="relu"
  18. # 第三个卷积‐池化层 
  19. conv_pool_3 = fluid.nets.simple_img_conv_pool( 
  20. input=conv_pool_2
  21. filter_size=5
  22. num_filters=50
  23. pool_size=2
  24. pool_stride=2
  25. act="relu"
  26. # 以softmax为激活函数的全连接输出层,10类数据输出10个数字 
  27. prediction = fluid.layers.fc(input=conv_pool_3size=10act='softmax'
  28. return prediction 

训练&验证


接下来在Paddlepaddle fluid上,进行训练。整个训练代码见附件train.py 模型验证,采用附件predict.py的代码进

行验证与运行时间的测量,选取一张狗的图:dog.jpg (可以fork首页链接aistudio平台上的demo) 连续预测10000

次,输出如下:

  1. CPU 运行结果为:预处理时间为0.0006270000000085929,预测时间为:16.246494 
  2. Out: 
  3. im_shape的维度: (1, 3, 32, 32) 
  4. The run time of image process is 
  5. 0.0006270000000085929 
  6. The run time of predict is 
  7. 16.246494 
  8. results [array([[5.0159363e‐04, 3.5942634e‐05, 2.5955746e‐02, 4.7745958e‐02, 
  9. 9.9251214e‐03, 9.0146154e‐01, 1.9564393e‐03, 1.2230080e‐02, 
  10. 4.7619540e‐08, 1.8753216e‐04]], dtype=float32)] 
  11. infer results: dog 
  1. GPU V100 运行结果为:预处理时间为0.0006390000000067175,预测时间为:15.903074000000018 
  2. Out: 
  3. im_shape的维度: (1, 3, 32, 32) 
  4. The run time of image process is 
  5. 0.0006390000000067175 
  6. The run time of predict is 
  7. 15.903074000000018 
  8. results [array([[5.0159392e‐04, 3.5942641e‐05, 2.5955772e‐02, 4.7746032e‐02, 
  9. 9.9251205e‐03, 9.0146142e‐01, 1.9564414e‐03, 1.2230078e‐02, 
  10. 4.7619821e‐08, 1.8753250e‐04]], dtype=float32)] 
  11. infer results: dog 

可以看到,模型可以正确的识别出图片中的动物为狗,接下来,我们就要尝试将这个模型部署到Edgeboard上面。

模型导出


我们需要将模型保存为模型文件model以及权重文件params,可以采用如下Paddle的API进行保存如图所示,在AiStudio的左侧打开模型文件所在的文件夹,下载mlp-model、mlp-params两个文件。

在Edgeboard上部署模型,完成预测


1、新建工程文件夹,目录结构如下(可以仿照sample里的resnet、inception例程):

  1. ‐sample_image_catdog 
  2.       ‐build 
  3.       ‐image 
  4.       ‐include 
  5.              ‐paddlepaddle‐mobile 
  6.              ‐... 
  7.       ‐lib 
  8.             ‐libpaddle‐mobile.so 
  9.       ‐model 
  10.           ‐mlp 
  11.              ‐model 
  12.              ‐params 
  13.       ‐src 
  14.              ‐fpga_cv.cpp 
  15.              ‐main.cpp 

2、将AiStudio上导出来的模型放置在model里的mlp文件夹,修改名字为model、params

3、新建 CMakeLists.txt

  1. cmake_minimum_required(VERSION 3.5.1) 
  2. project(paddle_edgeboard) 
  3. set(CMAKE_CXX_STANDARD 14) 
  4. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ‐pthread") 
  5. add_definitions(‐DPADDLE_MOBILE_FPGA_V1) 
  6. add_definitions(‐DPADDLE_MOBILE_FPGA) 
  7. set(PADDLE_LIB_DIR "${PROJECT_SOURCE_DIR}/lib" ) 
  8. set(EASYDL_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include" ) 
  9. set(PADDLE_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include/paddle‐mobile" ) 
  10. set(APP_NAME "paddle_edgeboard" ) 
  11. aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC) 
  12. find_package(OpenCV QUIET COMPONENTS core videoio highgui imgproc imgcodecs ml video) 
  13. include_directories(SYSTEM ${OpenCV_INCLUDE_DIRS}) 
  14. #list(APPEND Caffe_LINKER_LIBS ${OpenCV_LIBS}) 
  15. message(STATUS "OpenCV found (${OpenCV_CONFIG_PATH}),${OpenCV_LIBS}") 
  16. #add_definitions(‐DUSE_OPENCV) 
  17. include_directories(${EASYDL_INCLUDE_DIR}) 
  18. include_directories(${PADDLE_INCLUDE_DIR}) 
  19. LINK_DIRECTORIES(${PADDLE_LIB_DIR}) 
  20. add_executable(${APP_NAME} ${SRC}) 
  21. target_link_libraries(${APP_NAME} paddle‐mobile) 
  22. target_link_libraries(${APP_NAME} ${OpenCV_LIBS} ) 

4、main.cpp

  1. #include <iostream> 
  2. #include "io/paddle_inference_api.h" 
  3. #include "math.h" 
  4. #include <stdlib.h> 
  5. #include <unistd.h> 
  6. #include <fstream> 
  7. #include <ostream> 
  8. #include <fstream> 
  9. #include <iomanip> 
  10. #include <typeinfo> 
  11. #include <typeindex> 
  12. #include <vector> 
  13. #include<ctime> 
  14. #include "fpga/KD/float16.hpp" 
  15. #include "fpga/KD/llapi/zynqmp_api.h" 
  16. using namespace paddle_mobile; 
  17. #include <opencv2/highgui.hpp> 
  18. #include <opencv2/imgproc.hpp> 
  19. using namespace cv; 
  20. cv::Mat sample_float; 
  21. static std::vector<std::string> label_list(10); 
  22. void readImage(std::string filename, float* buffer) { 
  23. Mat img = imread(filename); 
  24. if (img.empty()) { 
  25. std::cerr << "Can't read image from the file: " << filename << std::endl
  26. exit(‐1); 
  27. Mat img2; 
  28. resize(img, img2, Size(32,32)); 
  29. img2.convertTo(sample_float, CV_32FC3); 
  30. int index = 0
  31. for (int row = 0; row < sample_float.rows; ++row) { 
  32. float* ptr = (float*)sample_float.ptr(row); 
  33. for (int col = 0; col < sample_float.cols; col++) { 
  34. float* uc_pixel = ptr
  35. // uc_pixel[0] ‐= 102; 
  36. // uc_pixel[1] ‐= 117; 
  37. // uc_pixel[1] ‐= 124; 
  38. float r = uc_pixel[0]; 
  39. float g = uc_pixel[1]; 
  40. float b = uc_pixel[2]; 
  41. buffer[index] = b / 255.0; 
  42. buffer[index + 1] = g / 255.0; 
  43. buffer[index + 2] = r / 255.0; 
  44. // sum += a + b + c; 
  45. ptr += 3; 
  46. // DLOG << "r:" << r << " g:" << g << " b:" << b
  47. index += 3; 
  48. // return sample_float; 
  49. PaddleMobileConfig GetConfig() { 
  50. PaddleMobileConfig config; 
  51. config.precision = PaddleMobileConfig::FP32; 
  52. config.device = PaddleMobileConfig::kFPGA; 
  53. // config.model_dir = "../models/mobilenet/"
  54. config.prog_file = "../model/mlp/model"
  55. config.param_file = "../model/mlp/params"
  56. config.thread_num = 4
  57. return config; 
  58. int main() { 
  59. clock_t startTime,endTime; 
  60. zynqmp::open_device(); 
  61. std::cout << " open_device success " << std::endl
  62. PaddleMobileConfig config = GetConfig(); 
  63. std::cout << " GetConfig success " << std::endl
  64. auto predictor = 
  65. CreatePaddlePredictor<PaddleMobileConfig
  66. PaddleEngineKind::kPaddleMobile>(config); 
  67. std::cout << " predictor success " << std::endl
  68. startTime = clock();//计时开始 
  69. float data[1 * 3 * 32 * 32] = {1.0f}; 
  70. readImage("../image/cat.jpg", data); 
  71. endTime = clock();//计时结束 
  72. std::cout << "The run time of image process is: " <<(double)(endTime ‐ startTime) / 
  73. CLOCKS_PER_SEC << "s" << std::endl
  74. PaddleTensor tensor; 
  75. tensor.shape = std::vector<int>({1, 3, 32, 32}); 
  76. tensor.data = PaddleBuf(data, sizeof(data)); 
  77. tensor.dtype = PaddleDType::FLOAT32; 
  78. std::vector<PaddleTensor> paddle_tensor_feeds(1, tensor); 
  79. PaddleTensor tensor_out; 
  80. tensor_out.shape = std::vector<int>({}); 
  81. tensor_out.data = PaddleBuf(); 
  82. tensor_out.dtype = PaddleDType::FLOAT32; 
  83. std::vector<PaddleTensor> outputs(1, tensor_out); 
  84. std::cout << " before predict " << std::endl
  85. predictor‐>Run(paddle_tensor_feeds, &outputs); 
  86. std::cout << " after predict " << std::endl
  87. // assert(); 
  88. endTime = clock();//计时结束 
  89. std::cout << "The run time of predict is: " <<(double)(endTime ‐ startTime) / CLOCKS_PER_SEC 
  90. << "s" << std::endl
  91. float* data_o = static_cast<float*>(outputs[0].data.data()); 
  92. for (size_t j = 0; j < outputs[0].data.length() / sizeof(float); ++j) { 
  93. std::cout << "output[" << j << "]: " << data_o[j] << std::endl
  94. int index = 0
  95. float max = 0.0; 
  96. for (int i = 0;i < 10; i++) { 
  97. float val = data_o[i]; 
  98. if (val > max) { 
  99. max = val > max ? val : max; 
  100. iindex = i; 
  101. label_list = {"airplane", "automobile", "bird", "cat", "deer", "dog", "frog", "horse", 
  102. "ship", "truck" }; 
  103. std::cout << "Result" << " is " << label_list[index] << std::endl
  104. return 0; 

5、编译运行

  1. insmod /home/root/workspace/driver/fpgadrv.ko 
  2. cd /home/root/workspace/sample/sample_image_catdog 
  3. mkdir build 
  4. cd build 
  5. rm ‐rf * 
  6. cmake .. 
  7. make 
  8. ./paddle_edgeboard 

修改main文件要预测的图像:

6、修改main文件后重复执行预测,可得结果如下:图像处理时间大概为:0.006秒,预测时间大概为:0.008秒

7、连续预测10000次所用时间为:23.7168

性能对比(连续预测10000次 单位:秒)


平台 前处理耗时 模型预测耗时
Edgeboard 0.006 23.7168
CPU(AISTUDIO平台双核8G) 0.000627 16.2464
GPU(AISTUDIO平台双核8G+GPU V100 16GB) 0.000639 15.9030

总结


优点:

1. EdgeBoard内置的Paddle-Mobile,可以与Paddle训练出来的模型进行较好的对接。

2. 预测速度上: Edge在预测小模型的时候,能与双核CPU和GPU在一个数量级,估计是模型较小,batch size也

为1,gpu,cpu的性能优势抵不过通信的开销,后续将进行大模型、高batch size的测试。

3. 提供的demo也足够简单,修改起来难度很低。

不足:

1. Paddle-Mobile相关文档具有一定门槛,且较为分散。初次使用的时候会走一些弯路出现问题的时候往往是个

黑盒,不易于定位。在这次进行模型训练的尝试中,出现过一次op不支持的情况,我们在官网上甚至没有找

到支持的op列表,这个在开发哥们的支持下升级版本后解决。如果后续能在稳定的固件版本下使用,并有比

较易用的sdk,开发门槛可能会进一步降低。