在线时间4752 小时
UID3441752
注册时间2017-11-21
NXP金币82792
TA的每日心情 | 开心 2025-7-11 08:53 |
---|
签到天数: 301 天 连续签到: 2 天 [LV.8]以坛为家I
管理员
  
- 积分
- 39283
- 最后登录
- 2025-7-22
|
编译模型的编译器—glow推理引擎初探
看到本文标题,相信不少观众都会有点迷茫,发出这样的灵魂拷问,什么,模型还可以编译?你在骗人吧?小编摸着良心告诉大家,小编真的没有说谎,本期,就为大家介绍一下这个伴随着NXP eIQ工具打包推出的推理引擎。
看过小编前几期文章的朋友们,肯定对推理引擎这一名词不那么陌生了,所谓推理引擎就是能够在设备端运行AI模型的一个小东西,只有有了Ta,模型才可以在设备端享受那夕阳下的奔跑。
而小编已经介绍过了两款推理引擎,分别是NNCU,以及Tflite Micro。今天这个小家伙叫做GLOW,glow的英文意思是光辉,但是在这里,此GLOW可非彼glow,这里的GLOW是graph + lowering的简写,意思是说,通过这个工具,在针对大量不同的上层model中的ops,到下层不同硬件设备的实现上,尽可能通过一些精简的指令进行表达。
来个比较官方的解释,Glow是用于神经网络图的机器学习编译器(https://github.com/pytorch/glow)。它旨在优化神经网络图并为各种硬件设备生成代码。
Glow有两种形式:即时(JIT)编译,即在执行模型之前在运行时执行编译;和Ahead of Time(AOT)编译,其中编译是脱机执行的,并生成一个目标文件(捆绑包),该文件随后与应用程序代码链接。
本文将主要介绍如何创建集成使用GlowAOT编译器,即采用脱机的方式进行模型的编译,随后将生成的包集成到工程中的所需步骤。GLOW下载网址https://www.nxp.com.cn/downloads ... /eIQ_Glow_Win64.exe。
本文代码基于EVK-MIMXRT1060SDK中的eiq_examples\glow_lenet_mnist,版本为2.9.1中,下载地址https://mcuxpresso.nxp.com/en/builder?hw=EVK-MIMXRT1060,请注意勾选eIQ选项然后下载SDK。
生成Bundle Bundle代表了模型编译后生成的二进制目标文件。model-compiler工具用来生成Bundle,其流程为:
当然,无论是浮点还是量化模型,工具本身都是支持的。但是,考虑到所生成代码的尺寸,以及运行时间,量化模型当然是首选。强烈建议生成量化模型。
Glow还有一个秘密武器,由于Glow采用LLVM作为后端,这使得他具有了跨平台的模型编译能力。通过编译选项可以进行目标平台的设置:
适用Cortext-M7的指令
-target=arm -mcpu=cortex-m7 -float-abi=hard
适用Cortext-M33的指令
-target=arm -mcpu=cortex-m33 -float-abi=hard
编译量化模型的完整指令,请注意这里所标红的地方,是为了使用CMSIS-NN库中的函数进行加速所必需的:
- model-compiler.exe -model=models\lenet_mnist -model-input=data,float,[1,1,28,28] -emit-bundle=bundle
- -backend=CPU -target=arm -mcpu=cortex-m7 -float-abi=hard
- -load-profile=profile.yml
- -quantization-schema=symmetric_with_power2 scale
- -quantization-precision-bias=Int8 -use-cmsis
复制代码 其他参数说明:
这样,运行结束后,会在bundle路径中生成如下文件:
获取量化参数
上一节说到,进行模型量化时候需要传入一个profile.yml,用于指导编译器进行模型量化,而image-classifier工具就是为了生成这一文件。
使用方法,将测试图片放到目录下,并当作参数传入命令行,如果想要使用CMSIS-NN库进行加速,请注意标红部分:
- image-classifier.exe
- images\*.png
- -image-mode=0to1
- -image-layout=NCHW
- -image-channel-order=BGR
- -model=models\lenet_mnist
- -model-input-name=data
- -dump-profile=profile.yml
- -quantization-schema=symmetric_with_power2 scale
- -quantization-precision-bias=Int8
复制代码 参数说明:
模型集成
下面介绍如何以静态方式,将上述通过Glow编译成功的代码集成到工程中。所谓静态方式,是将所有文件,包括权重文件添加到工程中,编译下载到板子上运行。
第一步 导入glow_bundle_utils.h头文件到main.c,并假设模型编译出的文件为lenet_mnist.h一并导入到main.c。
- // Bundle include
- #include "lenet_mnist.h"
- #include "glow_bundle_utils.h"
复制代码 第二步 为权重,输入输出tensor,以及中间层激活变量声明buffer,注意权重是以include的方式导入:
- // Statically allocate memory for constant weights (model weights) and initialize.
- GLOW_MEM_ALIGN(LENET_MNIST_MEM_ALIGN)
- uint8_t constantWeight[LENET_MNIST_CONSTANT_MEM_SIZE] = {
- #include "lenet_mnist.weights.txt"
- };
- // Statically allocate memory for mutable weights (model input/output data).
- GLOW_MEM_ALIGN(LENET_MNIST_MEM_ALIGN)
- uint8_t mutableWeight[LENET_MNIST_MUTABLE_MEM_SIZE];
- // Statically allocate memory for activations (model intermediate results).
- GLOW_MEM_ALIGN(LENET_MNIST_MEM_ALIGN)
- uint8_t activations[LENET_MNIST_ACTIVATIONS_MEM_SIZE];
复制代码 第三步 通过宏GLOW_GET_ADDR获取模型的输入与输出指针:
- // Bundle input data absolute address.
- uint8_t *inputAddr = GLOW_GET_ADDR(mutableWeight, LENET_MNIST_data);
- // Bundle output data absolute address.
- uint8_t *outputAddr = GLOW_GET_ADDR(mutableWeight, LENET_MNIST_softmax);
复制代码 第四步 初始化出入数据,即用户需要在运行模型之前对inputAddr进行赋值,需要自行准备imageData,这里需要说明,glow默认输入数据为float类型:
- // Produce input data for bundle.
- // Copy the pre-processed image data into the bundle input buffer.
- memcpy(inputAddr, imageData, sizeof(imageData));
复制代码 第五步 现在万事俱备了,让模型跑起来,你没有看错,就是如此简单的一句话:
- lenet_mnist(constantWeight, mutableWeight, activations);
复制代码 第六步 运行完之后,结果会被保存在outputAddr所指向的内存区域,但是要注意,结果是以float型数据保存的,处理时要以float*型指针访问数据:
- // Get classification top1 result and confidence.
- float *out_data = (float*)(outputAddr);
- float max_val = 0.0;
- uint32_t max_idx = 0;
- for(int i = 0; i < LENET_MNIST_OUTPUT_CLASS; i++) {
- if (out_data[i] > max_val) {
- max_val = out_data[i];
- max_idx = i;
- }
- }
复制代码 第七步 运行结果:
Having Fun ....
至此,利用Glow进行模型的编译,然后集成到工程中的流程就介绍完毕了。相信大家已经对Glow这个奇妙的模型编译器产生了浓厚的兴趣。祝大家玩的开心哟!
|
|