7.10. CMSIS-NN

TFLM 所遍历的算子列表大多是少数几个重量级算子:卷积(convolution) 将一小格学习到的权重在输入张量上滑动,并在每个位置写入加权和;深度可分离卷积(depthwise convolution) 对每个通道执行同样的操作;全连接(fully-connected) 在输入向量与权重矩阵之间做矩阵乘法;池化(pooling) 通过在小邻域内取最大值或平均值来缩小张量;像 ReLU 和 sigmoid 这样的 激活函数(activation functions) 则对每个值逐点运行。一次视觉推理的绝大部分周期都消耗在这少数几个算子内部。

如果以直白的方式实现,它们在微控制器上会很慢。CMSIS-NN 是 Arm 提供的快速算子库——用汇编手工调优,量化为 tensor I/O 所描述的 int8uint8 整数值,并针对 CPU 的 SIMD 指令编写。SIMD——单指令多数据(Single Instruction, Multiple Data)——让 CPU 在同一个周期内对多个值执行同一种算术运算。普通的标量乘加每个周期产生一个结果;而 SIMD 乘加把多个值打包进一个宽寄存器,并一次性产生所有结果。

H7 和 RT1062 上的 Cortex-M7 配有 Arm 的 DSP 扩展,它在一个 32 位寄存器中容纳四个 int8 值,并在一个周期内对全部四个执行乘加。AE3 上的 Cortex-M55 配有 Helium——正式名称为 MVE,即 M-profile 向量扩展——它在一个 128 位寄存器中容纳十六个 int8 通道,每周期吞吐量是前者的四倍。Helium 是一套更宽的 CPU 指令集,而非加速器;同一芯片上的 Ethos-U55 NPU 才是加速器。

随固件发布的 TFLM 构建都与 CMSIS-NN 链接,TFLM 会在运行时把每个重量级算子分派给适合该摄像头的 SIMD 变体。在 AE3 上,分派要稍微复杂一些:Vela 编译器已经离线遍历过模型,并把可由 NPU 执行的相连算子片段——子图(subgraphs)——标记为分派给 Ethos-U。在推理时,这些子图作为一个整块在加速器上运行,其余部分则回退到 M55 上的 Helium CMSIS-NN。

浮点算子完全绕过 CMSIS-NN,转而通过 TFLM 的可移植参考内核运行。int8 模型与浮点模型之间的精度差距通常很小;而吞吐量差距很大。出于这个原因,随摄像头发布的模型都量化为 int8