感谢虫叔[@圈圈虫]的邀请,准备出个Tengine-lite的学习笔记与测评。
刚看NCNN没多久,发了几篇学习笔记,碰巧被虫叔看到,想来也是非常幸运了。
昨天刚在笔记本上编译好tengine,这篇就先记录一下tengine在windows上的编译过程。
环境准备
虽然标题写的是用Clion通过cmake编译,但是在安装tengine的时候还是离不开Visual Studio的
- CMake >= 3.13
- Visual Studio >= 2015
- mingw64
- git
cmake和mingw64的bin目录需要添加到环境变量中
注:下载mingw64的时候,建议下载压缩包,不要下载online installer,要不然要安装到猴年马月去了。这里提供个下载链接
https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/8.1.0/threads-posix/sjlj/x86_64-8.1.0-release-posix-sjlj-rt_v6-rev0.7z/download
构建Tengine-lite
下载tengine,切换到tengine-lite分支
git clone https://github.com/OAID/Tengine.git tengine-lite
然后使用"x86 Native Tools Command Prompt for VS 201x" 或者 "x64 Native Tools Command Prompt for VS 201x"进行构建,这两个工具在开始菜单都能找到
然后安装tengine给的步骤输入指令
# 把cmake添加到环境变量中
set PATH=X:/your/cmake/bin;%PATH%
# 切换到tengine的下载目录
cd /d X:/your/downloaded/Tengine
# 新建build文件夹,切换到build目录
mkdir build
cd build
# VS2017执行
cmake.exe -G "Visual Studio 15 2017 Win64" -DTENGINE_OPENMP=OFF -DTENGINE_BUILD_EXAMPLES=OFF ..
# VS2019执行
::cmake.exe -G "Visual Studio 16 2019" -A x64 -DTENGINE_OPENMP=OFF ..
# cmake构建,并安装
cmake.exe --build . --parallel 8
cmake.exe --build . --target install
到此C语言CPU版本的tengine就构建成功了,这里我们默认构建的是release版本。
在构建的过程中,尝试启用编译C++API与VULKAN,结果失败了,大致看了一下原因是说pthread的头文件没有找到,说明windows的支持还并不是特别完善。具体原因在后续的测评的文章里再探究。(实际开发谁用windows /狗头)
此时build/install下生成了include和lib目录,我们将这两个目录及文件复制到我们自己的工程下即可。
项目实践
安装Clion,新建一个C++可执行文件的项目
配置工具链
配置CMake
项目结构如下
其中include目录下的文件是从下载的tengine-lite/examples/common/复制来的
CMakeLists文件的内容如下
cmake_minimum_required(VERSION 3.19)
project(tengine_demo)
add_executable(tengine_demo ./main.cpp src/tengine_operations.c)
# 包含目录
include_directories(${PROJECT_SOURCE_DIR}/include)
# 导入库文件
set(tengine-lib ${PROJECT_SOURCE_DIR}/lib/tengine-lite.lib
${PROJECT_SOURCE_DIR}/lib/tengine-lite.dll)
target_link_libraries(${PROJECT_NAME} ${tengine-lib})
main文件的作用是执行mobilenet分类网络,看官方给的流程代码感觉和TensorRT有些类似,计算图的流程的创建非常清晰,而且使用上又像NCNN方便。问题就是使用的是c语言,不是面向对象的,因此要记一些常用的api函数,不能通过对象直接调用。
main文件代码我将官方的文件进行了删减,主要是想把流程写清楚,在实际使用的时候,还需要做每一个步骤的情况判定,代码如下
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include "common.h"
#include "c_api.h"
#include "tengine_operations.h"
#define DEFAULT_IMG_H 224
#define DEFAULT_IMG_W 224
#define DEFAULT_SCALE1 1.f
#define DEFAULT_SCALE2 1.f
#define DEFAULT_SCALE3 1.f
#define DEFAULT_MEAN1 104.007
#define DEFAULT_MEAN2 116.669
#define DEFAULT_MEAN3 122.679
#define DEFAULT_LOOP_COUNT 1
#define DEFAULT_THREAD_COUNT 1
#define DEFAULT_CPU_AFFINITY 255
int tengine_classify(const char* model_file, const char* image_file, int img_h, int img_w, const float* mean,
const float* scale, int loop_count, int num_thread, int affinity)
{
/* set runtime options */
struct options opt;
opt.num_thread = num_thread;
opt.cluster = TENGINE_CLUSTER_ALL;
opt.precision = TENGINE_MODE_FP32;
opt.affinity = affinity;
/* inital tengine */
init_tengine();
/* create graph, load tengine model xxx.tmfile */
graph_t graph = create_graph(NULL, "tengine", model_file);
/* set the shape, data buffer of input_tensor of the graph */
int img_size = img_h * img_w * 3;
int dims[] = {1, 3, img_h, img_w}; // nchw
float* input_data = ( float* )malloc(img_size * sizeof(float));
tensor_t input_tensor = get_graph_input_tensor(graph, 0, 0);
set_tensor_shape(input_tensor, dims, 4);
set_tensor_buffer(input_tensor, input_data, img_size * 4);
/* prerun graph, set work options(num_thread, cluster, precision) */
prerun_graph_multithread(graph, opt);
/* prepare process input data, set the data mem to input tensor */
get_input_data(image_file, input_data, img_h, img_w, mean, scale);
run_graph(graph, 1);
/* get the result of classification */
tensor_t output_tensor = get_graph_output_tensor(graph, 0, 0);
float* output_data = ( float* )get_tensor_buffer(output_tensor);
int output_size = get_tensor_buffer_size(output_tensor) / sizeof(float);
print_topk(output_data, output_size, 5);
/* release tengine */
free(input_data);
postrun_graph(graph);
destroy_graph(graph);
release_tengine();
return 0;
}
int main()
{
int loop_count = DEFAULT_LOOP_COUNT;
int num_thread = DEFAULT_THREAD_COUNT;
int cpu_affinity = DEFAULT_CPU_AFFINITY;
std::string model_file = "./model/mobilenet.tmfile";
std::string image_file = "E:/000000000785.jpg";
int img_h = DEFAULT_IMG_H;
int img_w = DEFAULT_IMG_W;
float mean[3] = {-DEFAULT_MEAN1, -DEFAULT_MEAN2, -DEFAULT_MEAN3};
float scale[3] = {DEFAULT_SCALE1, DEFAULT_SCALE2, DEFAULT_SCALE3};
if (tengine_classify(model_file.c_str(), image_file.c_str(), img_h, img_w,
mean, scale, loop_count, num_thread, cpu_affinity) < 0)
return -1;
return 0;
}
最后使用cmake构建项目,使用clion直接运行项目,需要注意的是,我们需要将model/model_name.tmfile 也放入到cmake的build目录下才能让程序加载模型文件
在github上可以搜到的关于tengine-lite的项目太少,搜索tengine倒是有很多,不过还要人眼过滤掉隔壁alibaba的项目,所以放一下本篇文章的项目链接https://github.com/shaoeric/tengine-lite-classification-demo
本期笔记就写这些,等看一看源码之后再去做做源码解读和上手
原文链接:https://zhuanlan.zhihu.com/p/375906721
作者:闪电侠的右手
推荐阅读
更多Tengine相关内容请关注Tengine-边缘AI推理框架专栏。