0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何把Tengine Python API移植到Tengine Lite上

电子设计 来源:电子设计 作者:电子设计 2020-12-15 00:30 次阅读

大佬说选择移植Tengine Python API这个任务,一方面是因为他之前做过涉及Python和C++/C交互的开源项目工作,如 MXNet 中DLPack的Python API和他自己的开源项目 MobulaOP,这些工作让他踩了不少的坑;另一方面是因为他认为,了解一个框架需要先把例子跑起来, 就像学习一门新的编程语言要先跑通它的Hello World程序。通过这次任务,可以了解基于Tengine Lite的图像分类实现,对TengineLite有一个初始的、直观的感受。

以下为大佬第一人称自述/~

初探源码

这个任务的工作可以用一句话来概括: 在Tengine跑起Python例子, 再在Tengine Lite跑起同样的例子。

第一步需要找到Tengine Python API的例子。但Tengine没有的Python例子, 也没有Python API的文档。想从单元测试入手,但也没有Python API的单元测试,难怪移植Python API的任务难度比移植C++ API的任务难度高。还有什么方法可以了解Python API的用法呢? 阅读源码。Tengine的Python API放在pytengine文件夹, 里面有八个模块,分别是:base、context、device、 graph、libinfo、node、tengine和tensor,可以重点关注base, graph和tensor。

从base.py源码中可以看出Tengine使用ctypes的形式进行Python和C++/C的交互,把动态链接库libtengine.so读取后保存到变量/_LIB中,通过/_LIB.可以调用Tengine的C API. graph.py实现计算图部分的API,tensor.py实现了作为输入和输出的张量API。

编译和导入模块

找到Python API代码的位置后,编译Tengine,并尝试在Python中导入tengine模块。从libinfo.py中可以了解到,Tengine Python API会在Python API目录和环境变量LD/_LIBRARY/_PATH所指向的目录中,查找动态链接库libtengine.so.这时遇到了第一个Bug,我用的操作系统没有定义LD/_LIBRARY/_PATH这个环境变量,而API里直接用下标访问的形式取这个环境变量的值,出现了KeyError的错误。改成os.environ.get('LD/_LIBRARY/_PATH', '')即可。设置好动态链接库的路径后,可以成功导入pytengine模块了。

编写Python分类示例

Tengine提供C++/C的分类示例代码,在examples目录下,可以作为编写Python例子时的参考。其中,classification.cpp用了Tengine的C++ API,比C API多了Net类的封装,Net类封装了对于计算图Graph的操作。而classification/_old/_api.cpp用的是Tengine的C API. 由于Tengine Python API调用的是Tengine C API,因此可以拿classification/_old/_api.cpp作为参考。Tengine Python API封装得很简洁,很容易能找到每个Python函数调用的C函数。C++分类示例代码和Python API的代码互相对照,就可以写出Python分类示例的代码。写完后就可以尝试运行了。

运行Python分类示例

运行Python分类示例并不顺利,一开始就在构建计算图上出错了。定位到Python API的源码后, 发现是在以下两行出错。

# pytengine/tengine/graph.py:L24-L25 
params = [ c_str(item) for item in kwarg] 
self.graph = _LIB.create_graph(ctypes.c_void_p(context), c_str(model), *params)

create/_graph有三个参数: 第一个参数context是模型执行的上下文,第二个参数是模型的格式,第三个参数是模型的文件名。这两行代码看起来没什么毛病,和C++例子里的调用方式是一模一样的。但出错的原因就在给变量params赋值的这一行,里面的item是一个临时变量,当这条语句结束时,item离开了作用域就被释放了。而c/_str(item)是指向原来item的位置,变成了一个野指针。为了解决这个问题,可以把该行改为params = [ c/_str(kwarg[i]) for i in range(len(kwarg)) ],此时c/_str(kwarg[i])指向的是变量kwargs中存储的值,变量kwargs在调用函数create/_graph时仍在作用域内。

另外遇到的一个复杂的问题和Tensor类有关, pytengine的Tensor类还不完善,无法取出Tensor里的数据.。对照C语言写的例子修改Tensor的buf函数,得到数据内存地址,占用的内存大小,类型,尺寸后,转换为NumPy数组。 需要注意一下Tengine Lite前端的执行步骤:

# 建立计算图并读取模型文件
tm_file graph = tg.Graph(None, 'tengine', tm_file) 
# 取出输入
Tensor input_tensor = graph.getInputTensor(0, 0) 
# 设置输入Tensor的尺寸 
dims = [1, 3, img_h, img_w] 
input_tensor.shape = dims
# 预先运行以分配资源, 必须加上 
graph.preRun() 
# 设置输入数据的内存地址, 这里的data是尺寸为(3, img_h, img_w)的NumPy数组. 注意: 这里不会检查shape 
input_tensor.buf = data
# 以同步方式让网络进行推断(前向传播), 其中1表示使用同步的方式 
graph.run(1) # 1 is blocking 
# 取出输出的Tensor 
output_tensor = graph.getOutputTensor(0, 0) 
# 将Tensor转为NumPy数组 
output = np.array(output_tensor.buf)

把这些问题解决后,就能在Tengine上运行Python示例了。

需要注意的是,Tengine Lite和Tengine在做推断前,都需要调用preRun()函数对资源进行分配,这是必须要调用的。 虽然现在的Python API用起来有点复杂,但相信之后会封装得更好的。

从Tengine到Tengine Lite

在Tengine上成功运行Python示例后,移植就变得方便了。直接把pytengine文件夹下的所有代码, 以及Python分类示例复制粘贴到Tengine Lite中,将动态库名称从libtengine.so改为libtengine-lite.so, 然后运行Python分类示例。不出意料,出错了。 原因是Tengine Lite在设置输入Tensor的数据内存地址时,也会检查数据的大小,而之前的Python API的数据大小的计算是错误的。 修复Bug后, 成功在Tengine Lite上运行图像分类示例。移植完成。不得不夸一下Tengine Lite的C API兼容性做得真好!

在EAIDK-310上运行Tengine Lite的Python图像分类示例

之前参加OPEN AI LAB的活动,得到了一块EAIDK-310开发板, 刚好可以在上面进行测试。
这里使用可爱的虎猫(Tiger Cat)作为测试图片,模型采用MobileNet。 图片和模型都可以在Tengine项目的页面中找到链接 (Tengine快速上手指南)。

下载代码

[openailab@localhost proj]$ git clone https://github.com/OAID/Tengine 
# 进入Tengine的目录 
cd Tengine 
[openailab@localhost Tengine]$ git branch 
* tengine-lite

当前Tengine的默认分支是Tengine Lite。

2. 编译Tengine Lite

mkdir build 
cd build 
cmake .. 
make -j2

注意不要把编译线程数设太大,因为在最后编译MobileNet SSD例子时消耗显存比较多。六分钟多可以编译完。

3. 配置Tengine Lite的Python API编辑/home/openailab/.bashrc,再最后一行后面加入:

export TENGINE_LITE_PATH=/home/openailab/proj/Tengine 
export PYTHONPATH=$PYTHONPATH:$TENGINE_LITE_PATH/pytengine 
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$TENGINE_LITE_PATH/build/src/

环境变量TENGINE_LITE_PATH设置为Tengine Lite的根目录路径, 设置好后重新打开终端。 打开Python, 能成功导入tengine。

[openailab@localhost examples]$ python 
Python 3.6.5 (default, Mar 29 2018, 17:45:40) 
[GCC 8.0.1 20180317 (Red Hat 8.0.1-0.19)] on linux 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import tengine

4. 将三个文件cat.jpg/, mobilenet.tmfile/, synset/_words.txt放在examples的目录下, 文件结构如下所示:

[openailab@localhost examples]$ pwd 
/home/openailab/proj/Tengine/examples

[openailab@localhost examples]$ tree 
. 
├── cat.jpg 
├── classification.py 
├── mobilenet.tmfile 
├── synset_words.txt

5. 运行examples文件夹下的图像分类示例classification.py

[openailab@localhost examples]$ python classification.py 
n02123159 tiger cat 8.5975923538208 
n02119022 red fox, Vulpes vulpes 7.954988956451416 
n02119789 kit fox, Vulpes macrotis 7.867891311645508 
n02113023 Pembroke, Pembroke Welsh corgi 7.427407264709473 
n02123045 tabby, tabby cat 6.364651679992676

由此,就能够在Tengine Lite上正确预测出虎猫啦 : )

下一步工作

对于Tengine Lite Python API,我觉得在API设计方面可以进一步改进。比如把数据预处理, 计算图构建等操作隐藏起来,比如:

image = cv2.imread('./cat.jpg') 
model = tg.Model(tm_file) 
pred = model(image)

这样可以减少出错概率, 一些错误比如忘记对数据做预处理,使用的数据内存分布(NCHW还是NHWC)不正确,忘记调用prerun。

本次Tengine Python API移植任务,大佬做的工作是编写一个Python的图像分类示例,在Tengine上跑通代码,再将pytengine移植(复制)到Tengine Lite上, 再在Tengine Lite上跑通代码,其中还修复了pytengine中的一些bug。

更多Tengine相关内容请关注Tengine-边缘AI推理框架专栏。

审核编辑 黄昊宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • python
    +关注

    关注

    56

    文章

    4782

    浏览量

    84456
  • Tengine
    +关注

    关注

    1

    文章

    47

    浏览量

    2838
收藏 人收藏

    评论

    相关推荐

    在设备利用AI Edge Torch生成式API部署自定义大语言模型

    MediaPipe LLM Inference API 让开发者们能够将一些最受欢迎的 LLM 部署设备。现在,我们很高兴能进一步拓展对模型的支持范围,并让大家部署设备,而且具
    的头像 发表于 11-14 10:23 336次阅读
    在设备<b class='flag-5'>上</b>利用AI Edge Torch生成式<b class='flag-5'>API</b>部署自定义大语言模型

    如何利用pythonAPI查询IP地址?

    Python中,直接查询IP地址的地理位置或详细信息(如所属国家、城市等)通常需要依赖外部API服务,因为Python标准库本身不提供直接查询IP地址地理位置的功能。以下是一个使用requests
    发表于 08-28 11:55

    是否能将libwebsokets移植ESP8266利用libwebsokets进行网络应用开发?

    1. 如题目,请问是否能将 libwebsokets 移植 ESP8266 利用 libwebsokets进行网络应用开发? 2. libwebsokets 是纯C实现,如何设置ESP8266
    发表于 07-22 06:22

    0.9.3的sdk的程序移植1.0的sdk,重启后一直挂机的原因?

    0.9.3的sdk原来的程序移植1.0的sdk,下载完成后,不断电,程序运行正常,但重启后一直挂机。 load 0x40100000, len 816, room 16 1
    发表于 07-12 07:28

    将ADF移植RTOS大概都需要哪些步骤?一般移植周期多久呢?

    1 ADF是免费的开源软件吗? 2 将ADF移植RTOS大概都需要哪些步骤?一般移植周期多久呢?
    发表于 06-28 08:03

    如何移植http/https serversoftAP

    网页服务器移植 worksapceesp-idfexampleswifigetting_startedsoftAP 可以通过网页进行配置 现在是一点思路都没有,各种 大神/牛哥,给点指导,谢谢了
    发表于 06-19 06:14

    第四讲:单片机STC89C52+RA8889驱动控制彩屏 代码移植范例(包含API接口)

    51单片机通过RA8889控制驱动彩屏,移植API参考程序
    的头像 发表于 06-06 13:59 1349次阅读
    第四讲:单片机STC89C52+RA8889驱动控制彩屏 代码<b class='flag-5'>移植</b>范例(包含<b class='flag-5'>API</b>接口)

    讯飞星火Lite API开放免费永久,星火Pro/Max API价格0.2元

    5月22日,科大讯飞宣布旗下讯飞星火Lite API完全免费向公众开放,满足在线联网搜索及低算力推理与模型精调等特殊需求。同时,讯飞星火Pro/Max API的定价则为每万tokens收取0.21元。
    的头像 发表于 05-22 11:43 1058次阅读

    STemWin移植STM32后无法显示怎么解决?

    最近在玩STemWin,但是移植STM32后无法显示,单步调试的时候进入不了打点函数,我用的是IAR,驱动代码没有优化,仅仅是想先用打点函数来实现一下看看效果,后续再做优化,请教一下大神们,这个该怎么解决
    发表于 05-13 07:20

    STM32F407的程序移植STM32F103的芯片上面,需要修改哪些内容?

    STM32F407的程序移植STM32F103的芯片上面,需要修改哪些内容?
    发表于 04-10 07:38

    如何stm32F103程序移植stm32g030k6t6

    怎么stm32F103程序移植stm32g030k6t6
    发表于 04-03 07:57

    【算能RADXA微服务器试用体验】Radxa Fogwise 1684X Mini 规格

    通过网络可以了解,算能RADXA微服务器的具体规格: 处理器:BM1684X 算力:高达32Tops INT8峰值算力 内存:16GB LPDDR4X 内存 存储:64GB eMMC 编程框架
    发表于 02-28 11:21

    ELF 1技术贴|如何将Python3.6.9移植开发板

    如何进行交叉编译并移植ELF1开发板。在网址:https://www.python.org/ftp/python/下载
    的头像 发表于 02-22 14:06 405次阅读
    ELF 1技术贴|如何将<b class='flag-5'>Python</b>3.6.9<b class='flag-5'>移植</b><b class='flag-5'>到</b>开发板<b class='flag-5'>上</b>

    OpenHarmony 移植:build lite 编译构建过程

    这些疑惑,会对 build lite 编译构建过程有个更深入的理解。 1、产品解决方案代码是如何被调用编译的 在文件 buildliteBUILD.gn 配置文件中的构建目标 //build/lite
    的头像 发表于 02-19 16:19 907次阅读

    嵌入式学习-ElfBoard ELF 1板卡-移植python3.6.9

    /lsb_release 再次make install编译,在_install目录下会生成bin、include、lib、share文件夹。6.移植交叉编译好的pythonARM开发板:(1)在
    发表于 01-24 16:50