MaixPy 如何调用K210上的KPU为AI 加速?常用调用方式讲解

描述

一、模型使用和硬件加速原理#

前面我们知道了模型是一组数据结构以及很多参数, 最终以一个文件比如 kmodel 格式的文件的形式存在。
而这个模型要能在 MaixPy 的程序里面被使用, 首先需要程序能够理解 kmodel 这个文件的格式, 并且支持模型里面的算法,这样才能按照模型的描述将输入经过一些裂计算过程后得到输出。

所以,重点就是支持模型里面的算法,称 算子, 理论上,我们可以用软件去实现这些算子, 就可以成功运行模型了, 而执行软件的物理器件是 CPU , 神经网络模型的计算量很大,加上我们输入的是图片,图片本身的数据量就挺庞大, 就算是 K210 400MHz 的主频, 也无法满足流畅的推算模型。

所以, 要么升级 CPU,但是成本太高, 要么做一个专用的硬件, 让这个硬件专门去特定的算法,因为不像 CPU 一样要做通用计算, 所以速度会非常快,在电脑上, 我们通常使用专用的图像加速卡即 GPU 来加速图形计算, 在 K210 上,这个专门的硬件叫做 KPU (Kendryte Proccess Unit),第一个单词是公司名, 其实和其它芯片的 NPU (神经网络处理单元 Neural-network Processing Unit) 做的事情是一样的。

在 MaixPy 里面,已经集成了推导模型的代码,同时使用了 KPU 进行计算加速,使用时无需编写很多代码,只需要调用几个函数即可快速运行模型

二、关于 KPU#

虽然 KPU 是能够加速模型运算了, 但是由于成本、时间、功耗、体积、发热、应用领域定位等各种因素,它的能力并不能像专业领域的强力 NPU 一样,包含了每一种算子,它只能处理一部分。

KPU 实现了 卷积、批归一化、激活、池化 这 4 种基础操作的硬件加速, 但是它们不能分开单独使用,是一体的加速模块。

所以, 在 KPU 上面推理模型, 以下要求(如果不需要训练和设计模型,暂时不需要仔细了解):

内存限制

K210 有 6MB 通用 RAM 和 2MB KPU 专用 RAM。模型的输入和输出特征图存储在 2MB KPU RAM 中。权重和其他参数存储在 6MB 通用 RAM 中。

哪些算子可以被 KPU 完全加速?

下面的约束需要全部满足。

特征图尺寸:输入特征图小于等于 320x240 (宽x高) 同时输出特征图大于等于 4x4 (宽x高),通道数在 1 到 1024。

Same 对称 paddings (TensorFlow 在 stride=2 同时尺寸为偶数时使用非对称 paddings)。

普通 Conv2D 和 DepthwiseConv2D,卷积核为 1x1 或 3x3,stride 为 1 或 2。

最大池化 MaxPool (2x2 或 4x4) 和 平均池化 AveragePool (2x2 或 4x4)。

任意逐元素激活函数 (ReLU, ReLU6, LeakyRelu, Sigmoid...), KPU 不支持 PReLU。

哪些算子可以被 KPU 部分加速?

非对称 paddings 或 valid paddings 卷积, nncase 会在其前后添加必要的 Pad 和 Crop(可理解为 边框 与 裁切)。

普通 Conv2D 和 DepthwiseConv2D,卷积核为 1x1 或 3x3,但 stride 不是 1 或 2。 nncase 会把它分解为 KPUConv2D 和一个 StridedSlice (可能还需要 Pad)。

MatMul 算子, nncase 会把它替换为一个 Pad(到 4x4)+ KPUConv2D(1x1 卷积和) + Crop(到 1x1)。

TransposeConv2D 算子, nncase 会把它替换为一个 SpaceToBatch + KPUConv2D + BatchToSpace。

以上说明来自这里

三、模型转换#

前面说到, 模型其实就是一组数据结构和参数数据,不同的软件只能识别特定格式的模型, KPU 只认.kmodel格式的模型, 一般用电脑训练的模型则不是, 比如 tensorflow 是 .h5 格式或者 .tflite 格式, 要给 KPU 使用, 必须经过变成 kmodel 格式, 可以使用 nncase 这个工具来达到模型转换的目的
如果你需要转换模型, 具体使用方法请自行查看这个仓库里面的介绍

四、kmodel V3 模型 和 V4 模型#

由于代码更新, 在过程中产生了两个大版本, V3 和 V4, 其中 V3 模型是指用 nncase v0.1.0 RC5 转换出来的模型; V4模型指用 nncase v0.2.0 转换出来的模型

两者有一定的不同,所以现在两者共存, V3 代码量更少,占用内存小,效率也高,但是支持的算子少; V4 支持的算子更多,但是都是软件实现的,没有硬件加速,内存使用更多,所以各有所长。 MaixPy 的固件也可以选择是否支持 V4。

五、MaixPy 中使用模型 kmodel#

加载 SD 卡 (TF 卡)中的模型

将模型放到 SD 卡, 然后加载

 

   import KPU as kpu
   m = kpu.load("/sd/test.kmodel")

 

Copy

加载 Flash 中的模型

将模型下载到 Flash, 然后加载

 

   import KPU as kpu
   model_addr_in_flash = 0x300000
   m = kpu.load(model_addr_in_flash)

 

Copy

此处的 model_addr_in_flash 为模型在 Flash 中的偏移地址,模型可以通过 kflash.py 或者 kflash_gui 烧录到 Flash 对应的地址中

准备输入

一般情况下,我们会使用图像作为输入:

直接使用摄像头采集的数据作为输入:

 

   img = sensor.snapshot()

 

Copy

这里 img 就可以直接作为输入, 这里需要 注意: snapshot() 函数采集到图片后,会将图片数据放到两个地方
(1) RGB565 内存块, 图像以 RGB565 的形式存放在一块内存中,方便图像处理的函数使用,注意在内存中的排序是 [像素1 RGB, 像素2 RGB...]
(2) RGB888 内存块, 图像以 R8G8B8 的形式存放在另一块内存中,注意在内存中的排序是 [所有像素 R, 所有像素 G, 所有像素 B], 我们也称之为 AI 内存

其中,实际上作为 KPU 输入的数据是 RGB888 区域, 这个在前面的文档 MaixPy 图像及常用操作 章节中有仔细讲解过

从文件读取,或者将修改过的摄像头图像作为输入:

直接从摄像头采集的图像会自动填充 RGB888 区域,但是我们使用图像处理函数比如 image.resize() 时,只会修改 RGB565,没有修改 RGB888,因为同时修改两处内存需要耗费大量时间,而 KPU 的输入又是 RGB888 内存块, 所以在需要进行 KPU 运算时, 需要同步(刷新)一下 RGB888 内存块, 使用 img.pix_to_ai() 来进行同步,否则先前的修改将不会在 KPU 上生效。
比如:

 

   img = image.Image("/sd/test.jpg")   # 从 sd 卡读取 test.jpg 作为输入
   img.pix_to_ai()                    # 同步 `RGB888` 内存块

 

Copy

 

   img = sensor.snapshot()      # 获取摄像头采集的数据
   img = img.resize(240, 240)   # 使用图像处理函数处理原图像
   img.pix_to_ai()              # 同步 `RGB888` 内存块

 

Copy

前向运行模型

前向运行模型,也就是按照 输入到输出 的方向走一遍模型计算, 通过输入得出输出的值:

 

   feature_map = kpu.forward(m, img)

 

Copy

这里得到了 feature_map, 是一个特征图, 比如我们前面将的 小球 和 玩具 的分类,输出特征图是两个节点, 每个节点表示了是对应物体的概率,我们将特征图转换为 list 对象:

 

   p_list = feature_map[:]
   print(p_list)

 

Copy

就可以得到类似 [0.9, 0.1] 这样的置信度结果了

六、KPU使用过程中的常见问题#

6.1. KPU能够加载多大的模型?#

C 语言代码运行模型:
当 K210 运行 C 代码时,能够加载 < 6MB 左右的模型, 具体要看 C 代码的内容。
MaixPy 运行模型:
* 当运行 MaixPy (minimum版本) 时,能够加载 4MB 左右的模型。 如果不使用摄像头和 LCD, 最大可以加载 5MB 左右的模型(因为摄像头和 LCD 的缓冲区占用了很多内存,但实际应用也没多大意义了)
* 当运行 MaixPy (完整版) 时,能够加载 2MB 左右的模型
* 另外也支持实时从 Flash 加载模型, 理论上只要单层使用内存不超过 2MB, 整体模型可以无限大,只不过要牺牲一点运算速度。 使用方法看这里。 如果对原理和实现感兴趣,可以看这里

6.2. 报错"memory overflow"怎么办?#

出现这个问题,根据前面讲到过的 系统内存管理 可知,一般有两个可能性:

报错的地方跟系统堆无关系, 可能是 GC 内存不够导致,可以适当增加 GC 的总内存大小

由于模型过大引起的。可以依次尝试如下解决方案:

更换 MaixPy mini 版本固件

进行模型剪枝优化

使用 kpu.load_flash 接口运行时实时加载模型,只是执行效率降低一点

如果内存不足,且 kpu.load_flash 性能无法满足, 那么你可能需要使用 C SDK 进行开发。

6.3. 报错"load error,only support kmodel v3/v4"怎么办?#

出现这个问题可以尝试如下解决方案:

如果为加载 Flash 中的模型,请确保 flash offset 填写正确,并保证和 MaixPy 固件的地址没有冲突(模型在 Flash 中的地址太靠前,然后往 Flash 烧录入固件时, 固件大小超过了模型所在的起始地址, 导致模型被破坏)

如果是采用 nncase 0.2.0 进行转换的 kmodel V4,请尝试采用 nncase 0.1.0 进行转换,从而生成 kmodel V3

6.4. 我想实现不同模型的选择加载(例如按下按钮运行目标分类,再次按下按钮则运行目标检测),应该怎么写程序?#

因为内部 RAM 有限,所以当需要切换不同模型进行 kpu.load(address) 前,请先执行 kpu.deinit(k210model) 释放之前模型占用的内存,然后再加载新的模型。 也就是分时复用内存

审核编辑 黄昊宇

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

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分