Tengine推理框架之初见

[导读] 前段时间从电子芯吧客白嫖了一块开发板EAIDK310,该平台的一个主要应用就是Tengine是OPEN AI LAB针对于嵌入式终端平台以及终端AI应用场景特点,采用模块化设计为终端人工智能量身打造的高效、简洁、高性能的前端推理计算框架。对于人工智能,本人还是小白,本文仅记录个人学习Tengine的笔记,文章中一定有大量错误,分享笔记也是希望能起到交流求证的作用,让自己能逐步深入了解人工智能的技术知识。

Tengine的特点

AI之于嵌入式应用而言,个人理解存在下面一些痛点:

  • 应用问题场景多样性,工业/医疗/智慧城市/智能家居等等
  • 嵌入式平台多样性,单片机/DSP/处理器/FPGA等等
  • 对于应用开发而言,AI曲高和寡、高深莫测,无法落地
  • 很多框架都侧重在算法训练,对于嵌入式部署比较好的方案则相对较少
  • ……

Tengine的特点:

  • 定位于嵌入式平台推理计算框架
  • 支持TensorFlow、Caffe、PyTorch、MXNet、ONNX、PaddlePaddle等流行训练框架。
  • 芯片平台适配
  • 超轻量无依赖,甚至可以在MCU上部署
  • 全栈部署移植支持
  • …..

Tengine的框架初了解

图片及介绍自Tengine用户手册

Tengine 由六大模块组成: core/operator/serializer/executor/driver/wrapper

  • core : 提供系统的基本组件和功能。
  • operator: 在此处定义了基本的 operators 框架, 比如 convolution(卷积), relu,pooling池化 等等。
  • serializer: 用来导入已保存的模型。该 serializer 框架可扩展以支持不同的框架模型,包括自定义模型格式。Tengine 可以直接导入 Caffe/ONNX/Tensorflow/MXNet 模型是或者 Tengine 自己的模型。
  • executor: 实现运行 graph 和 operators 的代码。 主要实现了计算优化调度,内置了一个计算框架,主要定义了
  • driver: 实际硬件的适配器,并通过 HAL API 向设备执行器提供服务。单个 driver 可以创建多个设备。
  • wrapper: 为不同的框架提供 API 封装。caffe API wrapper 和 tensorflow API wrapper 现在都可以工作了。

对其中的operator/executor稍加分析:

executor

大致分析了几个源文件:

主要实现了计算框架,实现了调度器、设备、驱动、平台计算优化相应的框架。

operator

对于operator个人理解是基本算子的实现层,比如卷积、GRU(GRU是LSTM网络)、RELU(激活函数)等,比如激活函数,有哪些呢?sigmoid、tanh、ReLU 、Leaky Relu、RReLU、softsign 、softplus,对于这些概念想深入学习,可以去参考机器学习、深度学习的理论书籍。从实现的角度,都封装为了一些基本的数学处理类,实现了相应的数学数值计算。

Tengine应用代码分析

参考./Tengine/examples/classification.cpp

 
  1. int main(int argc, char* argv[])
  2. {
  3. gExcName = std::string(argv[0]);
  4. int repeat_count = DEFAULT_REPEAT_CNT;
  5. std::string model_name;
  6. std::string tm_file;
  7. std::string label_file;
  8. std::string image_file;
  9. std::vector<int> hw;
  10. std::vector<float> ms;
  11. int img_h = 0;
  12. int img_w = 0;
  13. float scale = 0.0;
  14. float mean[3] = {-1.0, -1.0, -1.0};
  15.  
  16. int res;
  17. while((res = getopt(argc, argv, "m:n:t:l:i:g:s:w:r:h")) != -1)
  18. {
  19. switch(res)
  20. {
  21. case 'm':
  22. tm_file = optarg;
  23. break;
  24. case 'l':
  25. label_file = optarg;
  26. break;
  27. case 'i':
  28. image_file = optarg;
  29. break;
  30. case 'g':
  31. hw = ParseString<int>(optarg);
  32. if(hw.size() != 2)
  33. {
  34. std::cerr << "Error -g parameter.\n";
  35. show_usage();
  36. return -1;
  37. }
  38. img_h = hw[0];
  39. img_w = hw[1];
  40. break;
  41. case 's':
  42. scale = strtof(optarg, NULL);
  43. break;
  44. case 'w':
  45. ms = ParseString<float>(optarg);
  46. if(ms.size() != 3)
  47. {
  48. std::cerr << "Error -w parameter.\n";
  49. show_usage();
  50. return -1;
  51. }
  52. mean[0] = ms[0];
  53. mean[1] = ms[1];
  54. mean[2] = ms[2];
  55. break;
  56. case 'r':
  57. repeat_count = std::strtoul(optarg, NULL, 10);
  58. break;
  59. case 'h':
  60. show_usage();
  61. return 0;
  62. default:
  63. break;
  64. }
  65. }
  66.  
  67. if (tm_file.empty())
  68. {
  69. std::cerr << "Error: Tengine model file not specified!" << std::endl;
  70. show_usage();
  71. return -1;
  72. }
  73.  
  74. if(image_file.empty())
  75. {
  76. std::cerr << "Error: Image file not specified!" << std::endl;
  77. show_usage();
  78. return -1;
  79. }
  80. if(label_file.empty())
  81. {
  82. label_file = DEFAULT_LABEL_FILE;
  83. std::cout << "Label file not specified, use default [" << label_file << "]." << std::endl;
  84. }
  85. // check input files
  86. if(!check_file_exist(tm_file) || !check_file_exist(label_file) || !check_file_exist(image_file))
  87. return -1;
  88.  
  89. if(img_h == 0)
  90. {
  91. img_h = DEFAULT_IMG_H;
  92. std::cout << "Image height not specified, use default [" << DEFAULT_IMG_H << "]" << std::endl;
  93. }
  94. if(img_w == 0)
  95. {
  96. img_w = DEFAULT_IMG_W;
  97. std::cout << "Image width not specified, use default [" << DEFAULT_IMG_W << "]" << std::endl;
  98. }
  99. if(scale == 0.0)
  100. {
  101. scale = DEFAULT_SCALE;
  102. std::cout << "Scale value not specified, use default [" << scale << "]" << std::endl;
  103. }
  104. if(mean[0] == -1.0 || mean[1] == -1.0 || mean[2] == -1.0)
  105. {
  106. mean[0] = DEFAULT_MEAN1;
  107. mean[1] = DEFAULT_MEAN2;
  108. mean[2] = DEFAULT_MEAN3;
  109. std::cout << "Mean value not specified, use default [" << mean[0] << ", " << mean[1] << ", " << mean[2] << "]" << std::endl;
  110. }
  111. if(model_name.empty())
  112. model_name = tm_file;
  113. const char* _model_file = model_name.c_str();
  114. const char* _image_file = image_file.c_str();
  115. const char* _label_file = label_file.c_str();
  116. const float* _channel_mean = mean;
  117.  
  118. tengine::Net somenet;
  119. tengine::Tensor input_tensor;
  120. tengine::Tensor output_tensor;
  121.  
  122. std::cout << "tengine library version: " << get_tengine_version() << "\n";
  123. if(request_tengine_version("1.0") < 0)
  124. return -1;
  125.  
  126. std::cout << "\nModel name : " << model_name << "\n"
  127. << "tengine model file : " << tm_file << "\n"
  128. << "label file : " << label_file << "\n"
  129. << "image file : " << image_file << "\n"
  130. << "img_h, imag_w, scale, mean[3] : " << img_h << " " << img_w << " " << scale << " " << mean[0] << " "
  131. << mean[1] << " " << mean[2] << "\n";
  132.  
  133. /@@* load model */
  134. somenet.load_model(NULL, "tengine", _model_file);
  135.  
  136. /@@* prepare input data */
  137. input_tensor.create(img_w, img_h, 3);
  138. get_input_data(_image_file, (float* )input_tensor.data, img_h, img_w, _channel_mean, scale);
  139.  
  140. /@@* forward */
  141. somenet.input_tensor(0, 0, input_tensor);
  142. double min_time, max_time, total_time;
  143. min_time = __DBL_MAX__;
  144. max_time = -__DBL_MAX__;
  145. total_time = 0;
  146. for(int i = 0; i < repeat_count; i++)
  147. {
  148. double start_time = get_current_time();
  149. somenet.run();
  150. double end_time = get_current_time();
  151. double cur_time = end_time - start_time;
  152.  
  153. total_time += cur_time;
  154. if (cur_time > max_time)
  155. max_time = cur_time;
  156. if (cur_time < min_time)
  157. min_time = cur_time;
  158.  
  159. printf("Cost %.3f ms\n", cur_time);
  160. }
  161. printf("Repeat [%d] min %.3f ms, max %.3f ms, avg %.3f ms\n", repeat_count, min_time, max_time, total_time / repeat_count);
  162.  
  163. /@@* get result */
  164. somenet.extract_tensor(0, 0, output_tensor);
  165.  
  166. /@@* after process */
  167. PrintTopLabels(_label_file, (float*)output_tensor.data, 1000);
  168. std::cout << "--------------------------------------\n";
  169. std::cout << "ALL TEST DONE\n";
  170.  
  171. return 0;
  172. }

对于上述代码,梳理一下有哪些关键点运行Tengine推理引擎:

总结下来:

  • 导入模型
  • 输入待预测的数据(本例为图像)
  • 运行推理引擎,调用run方法
  • 获取结果

应用

EAIDK-310板子,具有下面的硬件资源:

厂家已提供了Linux系统源代码以及相应Tengine编译部署使用例子,如果想要基于该硬件平台做一个实际的项目,这里描述下我的一些想法,比如实现一个人脸识别门禁:

有哪些开发工作需要去做呢?

  • USB摄像头图像导入,官方有相应的例程。当然也可以自己去做图像数据采集部分程序。比如可以使用libusb很容易将数据采集进控制板
  • 门禁控制,比如可以绘制一个继电器控制板,从而控制门禁。对于软件开发而言,需要编写一个GPIO控制的字符设备驱动程序。
  • 模型训练。对于模型训练,可以通过学习Tensorflow等有名的框架入手,并结合深度学习理论知识,学习如何训练模型,导出模型
  • 系统集成控制软件,按照上述推理例程。

总结一下

本文旨在学习一下Tengine的总体概念,形成对嵌入式AI部署落地的基本概念,梳理一下要实现一个嵌入式AI端侧项目的思路概念,应该从何入手。本人对于人工智能属于小白一枚,文章纯属学习笔记,所以认知错误一定难免。

地址:https://www.icxbk.com/article/detail/1495.html

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值