电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
Tengine推理框架之初见
分 享
扫描二维码分享
Tengine推理框架之初见
Tengine
AI
嵌入式AI
逸珺
关注
发布时间: 2020-07-10
丨
阅读: 1155
[导读] 前段时间从电子芯吧客白嫖了一块开发板EAIDK310,该平台的一个主要应用就是Tengine是OPEN AI LAB针对于**嵌入式终端平台**以及**终端**AI应用场景特点,采用模块化设计为终端人工智能量身打造的高效、简洁、高性能的前端推理计算框架。对于人工智能,本人还是小白,本文仅记录个人学习Tengine的笔记,文章中一定有大量错误,分享笔记也是希望能起到交流求证的作用,让自己能逐步深入了解人工智能的技术知识。 ## Tengine的特点 AI之于嵌入式应用而言,个人理解存在下面一些痛点: - 应用问题场景多样性,工业/医疗/智慧城市/智能家居等等 - 嵌入式平台多样性,单片机/DSP/处理器/FPGA等等 - 对于应用开发而言,AI曲高和寡、高深莫测,无法落地 - 很多框架都侧重在算法训练,对于嵌入式部署比较好的方案则相对较少 - ...... Tengine的特点: - 定位于嵌入式平台推理计算框架 - 支持TensorFlow、Caffe、PyTorch、MXNet、ONNX、PaddlePaddle等流行训练框架。 - 跨芯片平台适配 - 超轻量无依赖,甚至可以在MCU上部署 - 全栈部署移植支持 - ..... ## Tengine的框架初了解 图片及介绍自Tengine用户手册 ![](https://cf04.ickimg.com/bbsimages/202007/ce485169f57557a5cf082e2944026929.png) 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 大致分析了几个源文件: ![](https://cf04.ickimg.com/bbsimages/202007/9ca9d5d4a93c4f376da9d266e6ddce3b.png) 主要实现了计算框架,实现了调度器、设备、驱动、平台计算优化相应的框架。 # operator 对于operator个人理解是基本算子的实现层,比如卷积、GRU(GRU是LSTM网络)、RELU(激活函数)等,比如激活函数,有哪些呢?sigmoid、tanh、ReLU 、Leaky Relu、RReLU、softsign 、softplus,对于这些概念想深入学习,可以去参考机器学习、深度学习的理论书籍。从实现的角度,都封装为了一些基本的数学处理类,实现了相应的数学数值计算。 ![](https://cf04.ickimg.com/bbsimages/202007/2b4bb8cbda4c60c16d406a64186192b8.png) ## Tengine应用代码分析 参考./Tengine/examples/classification.cpp ```c++ int main(int argc, char* argv[]) { gExcName = std::string(argv[0]); int repeat_count = DEFAULT_REPEAT_CNT; std::string model_name; std::string tm_file; std::string label_file; std::string image_file; std::vector
hw; std::vector
ms; int img_h = 0; int img_w = 0; float scale = 0.0; float mean[3] = {-1.0, -1.0, -1.0}; int res; while((res = getopt(argc, argv, "m:n:t:l:i:g:s:w:r:h")) != -1) { switch(res) { case 'm': tm_file = optarg; break; case 'l': label_file = optarg; break; case 'i': image_file = optarg; break; case 'g': hw = ParseString
(optarg); if(hw.size() != 2) { std::cerr << "Error -g parameter.\n"; show_usage(); return -1; } img_h = hw[0]; img_w = hw[1]; break; case 's': scale = strtof(optarg, NULL); break; case 'w': ms = ParseString
(optarg); if(ms.size() != 3) { std::cerr << "Error -w parameter.\n"; show_usage(); return -1; } mean[0] = ms[0]; mean[1] = ms[1]; mean[2] = ms[2]; break; case 'r': repeat_count = std::strtoul(optarg, NULL, 10); break; case 'h': show_usage(); return 0; default: break; } } if (tm_file.empty()) { std::cerr << "Error: Tengine model file not specified!" << std::endl; show_usage(); return -1; } if(image_file.empty()) { std::cerr << "Error: Image file not specified!" << std::endl; show_usage(); return -1; } if(label_file.empty()) { label_file = DEFAULT_LABEL_FILE; std::cout << "Label file not specified, use default [" << label_file << "]." << std::endl; } // check input files if(!check_file_exist(tm_file) || !check_file_exist(label_file) || !check_file_exist(image_file)) return -1; if(img_h == 0) { img_h = DEFAULT_IMG_H; std::cout << "Image height not specified, use default [" << DEFAULT_IMG_H << "]" << std::endl; } if(img_w == 0) { img_w = DEFAULT_IMG_W; std::cout << "Image width not specified, use default [" << DEFAULT_IMG_W << "]" << std::endl; } if(scale == 0.0) { scale = DEFAULT_SCALE; std::cout << "Scale value not specified, use default [" << scale << "]" << std::endl; } if(mean[0] == -1.0 || mean[1] == -1.0 || mean[2] == -1.0) { mean[0] = DEFAULT_MEAN1; mean[1] = DEFAULT_MEAN2; mean[2] = DEFAULT_MEAN3; std::cout << "Mean value not specified, use default [" << mean[0] << ", " << mean[1] << ", " << mean[2] << "]" << std::endl; } if(model_name.empty()) model_name = tm_file; const char* _model_file = model_name.c_str(); const char* _image_file = image_file.c_str(); const char* _label_file = label_file.c_str(); const float* _channel_mean = mean; tengine::Net somenet; tengine::Tensor input_tensor; tengine::Tensor output_tensor; std::cout << "tengine library version: " << get_tengine_version() << "\n"; if(request_tengine_version("1.0") < 0) return -1; std::cout << "\nModel name : " << model_name << "\n" << "tengine model file : " << tm_file << "\n" << "label file : " << label_file << "\n" << "image file : " << image_file << "\n" << "img_h, imag_w, scale, mean[3] : " << img_h << " " << img_w << " " << scale << " " << mean[0] << " " << mean[1] << " " << mean[2] << "\n"; /@@* load model */ somenet.load_model(NULL, "tengine", _model_file); /@@* prepare input data */ input_tensor.create(img_w, img_h, 3); get_input_data(_image_file, (float* )input_tensor.data, img_h, img_w, _channel_mean, scale); /@@* forward */ somenet.input_tensor(0, 0, input_tensor); double min_time, max_time, total_time; min_time = __DBL_MAX__; max_time = -__DBL_MAX__; total_time = 0; for(int i = 0; i < repeat_count; i++) { double start_time = get_current_time(); somenet.run(); double end_time = get_current_time(); double cur_time = end_time - start_time; total_time += cur_time; if (cur_time > max_time) max_time = cur_time; if (cur_time < min_time) min_time = cur_time; printf("Cost %.3f ms\n", cur_time); } printf("Repeat [%d] min %.3f ms, max %.3f ms, avg %.3f ms\n", repeat_count, min_time, max_time, total_time / repeat_count); /@@* get result */ somenet.extract_tensor(0, 0, output_tensor); /@@* after process */ PrintTopLabels(_label_file, (float*)output_tensor.data, 1000); std::cout << "--------------------------------------\n"; std::cout << "ALL TEST DONE\n"; return 0; } ``` 对于上述代码,梳理一下有哪些关键点运行Tengine推理引擎: ![](https://cf04.ickimg.com/bbsimages/202007/a68b889d1975289126da4fe3a4e718b7.jpg) 总结下来: - 导入模型 - 输入待预测的数据(本例为图像) - 运行推理引擎,调用run方法 - 获取结果 ## 应用 EAIDK-310板子,具有下面的硬件资源: ![](https://cf04.ickimg.com/bbsimages/202007/5db541e7811826e5e1d3cec34b28d952.png) ![](https://cf04.ickimg.com/bbsimages/202007/fa8ad2c7bece3f419848266a96c4b2e8.png) 厂家已提供了Linux系统源代码以及相应Tengine编译部署使用例子,如果想要基于该硬件平台做一个实际的项目,这里描述下我的一些想法,比如实现一个人脸识别门禁: ![](https://cf04.ickimg.com/bbsimages/202007/bd0ede82fbb974cb6b8a9c532ce81237.png) 有哪些开发工作需要去做呢? - USB摄像头图像导入,官方有相应的例程。当然也可以自己去做图像数据采集部分程序。比如可以使用libusb很容易将数据采集进控制板 - 门禁控制,比如可以绘制一个继电器控制板,从而控制门禁。对于软件开发而言,需要编写一个GPIO控制的字符设备驱动程序。 - 模型训练。对于模型训练,可以通过学习Tensorflow等有名的框架入手,并结合深度学习理论知识,学习如何训练模型,导出模型 - 系统集成控制软件,按照上述推理例程。 ## 总结一下 本文旨在学习一下Tengine的总体概念,形成对嵌入式AI部署落地的基本概念,梳理一下要实现一个嵌入式AI端侧项目的思路概念,应该从何入手。本人对于人工智能属于小白一枚,文章纯属学习笔记,所以认知错误一定难免。
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
逸珺
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交