微控制器上的神经网络(NNoM)
NNoM 是一个专门为微控制器设计的高级推理神经网络库。
亮点
- 通过一行代码将 Keras 模型部署为 NNoM 模型。
- 支持复杂结构;Inception、ResNet、DenseNet、Octave 卷积...
- 用户友好的接口。
- 高性能后端选择。
- 板载预编译 - 运行时零解释器性能损失。
- 板载评估工具;运行时分析、Top-k、混淆矩阵...
NNoM 的结构如下所示:
更多详细信息请参见开发指南。
欢迎使用问题进行讨论。 欢迎提交拉取请求。QQ群/TIM 群:763089399。
最新更新 - v0.4.x
循环层 (RNN) (0.4.1)
在版本 0.4.1 中实现了循环层 (简单RNN、GRU、LSTM)。支持 statful
和 return_sequence
选项。
新结构接口 (0.4.0)
NNoM 提供了一个新的层接口,称为 结构化接口,所有标记为 _s
后缀。该接口旨在使用一个 C 结构体提供层的所有配置。与对人类友好的层 API 不同,这个结构化 API 对机器更友好。
每通道量化 (0.4.0)
新的结构化 API 支持 卷积层 的每通道量化(每轴量化)和扩展。
新脚本 (0.4.0)
从 0.4.0 起,NNoM 将切换到默认使用结构化接口来生成模型头文件 weights.h
。对应结构化接口的脚本是 nnom.py
,而层接口对应的脚本是 nnom_utils.py
。
许可证
自 nnom-V0.2.0 起,NNoM 根据 Apache 许可证 2.0 发布。 许可证和版权信息可在代码中找到。
为什么选择 NNoM?
NNoM 的目标是提供一个轻量级、用户友好和灵活的接口,以便快速部署到 MCU 上。
如今,神经网络更宽、更深、更密集。
[1] Szegedy, C., Liu, W., Jia, Y., Sermanet, P., Reed, S., Anguelov, D., ... & Rabinovich, A. (2015). Going deeper with convolutions. In Proceedings of the IEEE conference on computer vision and pattern recognition (pp. 1-9).
[2] He, K., Zhang, X., Ren, S., & Sun, J. (2016). Deep residual learning for image recognition. In Proceedings of the IEEE conference on computer vision and pattern recognition (pp. 770-778).
[3] Huang, G., Liu, Z., Van Der Maaten, L., & Weinberger, K. Q. (2017). Densely connected convolutional networks. In Proceedings of the IEEE conference on computer vision and pattern recognition (pp. 4700-4708)。
2014 年后,神经网络的发展更注重结构优化,以提高效率和性能,这对小型平台(如 MCU)尤为重要。 然而,可用的 MCU 神经网络库过于底层,使得与这些复杂结构的使用变得非常困难。
因此,我们构建了 NNoM,以帮助嵌入式开发者更快、更简单地将神经网络模型直接部署到 MCU 上。
NNoM 将为开发者管理结构、内存及其他所有内容。所有你需要做的就是输入你的新测量值并获取结果。
安装
NNoM 可以作为 Python 包安装
pip install git+https://github.com/majianjia/nnom@master
NNoM 需要Tensorflow 版本 <= 2.14
。
有多种安装选项,请参见 TensorFlow 文档。
例如:
pip install 'tensorflow-cpu<=2.14.1'
注意:Tensorflow 2.14 支持直到 Python 3.11。 然而,不支持 Python 3.12。
访问 C 文件
NNoM 中的 C 头文件和源代码分布在 nnom_core
Python 包中。
你可以通过运行以下命令找到其位置。
python -c "import nnom_core; print(nnom_core.__path__[0])"
在你的构建系统中,添加 inc/
和 port/
目录作为包含目录,并编译 src/*.c
文件。
文档
指南
性能
有许多文章比较了 NNoM 与其他著名 MCU AI 工具,如 TensorFlow Lite、STM32Cube.AI 等。
苏黎世应用科学大学的 Raphael Zingg 等人 在他们的论文 "人工智能在微控制器上的应用" 中比较了 nnom 与 tflite, cube 和 e-Ai,博客 https://blog.zhaw.ch/high-performance/2020/05/14/artificial-intelligence-on-microcontrollers/
Usman Ali Butt 在都灵理工大学(POLITECNICO DI TORINO)制作了以下比较,见 论文: 在低成本嵌入式系统中部署人工神经网络(ANN)
两篇文章都表明,NNoM不仅可以与其他流行的神经网络框架媲美,而且在推理时间方面更快,有时内存占用更少。
注意: 这些图表和表格归其作者所有。详情和版权请参阅其原始论文。
示例
文档化的示例
请查看 示例 并选择一个开始。
可用操作
注意: NNoM 现在支持 HWC 和 CHW 格式。目前某些操作可能不支持两种格式。请查看表格了解当前状态。
核心层
层 | 结构体 API | 层 API | 备注 |
---|---|---|---|
卷积 | conv2d_s() | Conv2D() | 支持 1/2D,支持扩张 (New!) |
反卷积 (New!) | conv2d_trans_s() | Conv2DTrans() | 在开发中 |
深度卷积 | dwconv2d_s() | DW_Conv2D() | 支持 1/2D |
全连接 | dense_s() | Dense() | |
Lambda | lambda_s() | Lambda() | 单输入/单输出匿名操作 |
批归一化 | N/A | N/A | 该层已通过脚本合并到上一个 Conv 中 |
展平 | flatten_s() | Flatten() | |
重塑 (New!) | reshape_s() | N/A | |
SoftMax | softmax_s() | SoftMax() | 只有层 API |
激活 | N/A | Activation() | 激活层实例 |
输入/输出 | input_s()/output_s() | Input()/Output() | |
上采样 | upsample_s() | UpSample() | |
零填充 | zeropadding_s() | ZeroPadding() | |
剪裁 | cropping_s() | Cropping() |
RNN 层
层 | 状态 | 结构体 API | 备注 |
---|---|---|---|
循环神经网络层 (New!) | Alpha | rnn_s() | RNN 层封装 |
简单单元 (New!) | Alpha | simple_cell_s() | |
GRU 单元 (New!) | Alpha | gru_cell_s() | 门控循环网络 |
LSTM 单元 (New!) | Alpha | lstm_s() | 长短期记忆 |
激活
激活可以作为层独立使用,也可以作为"actail"附加到之前的层,以减少内存占用。
目前没有结构化的激活 API,因为激活通常不会作为层单独使用。
激活 | 结构体 API | 层 API | 激活 API | 备注 |
---|---|---|---|---|
ReLU | N/A | ReLU() | act_relu() | |
Leaky ReLU (New!) | N/A | LeakyReLU() | act_leaky_relu() | |
Adv ReLU(New!) | N/A | N/A | act_adv_relu() | 高级ReLU, 斜率, 最大值, 阈值 |
TanH | N/A | TanH() | act_tanh() | |
硬 TanH (New!) | N/A | TanH() | 仅后端 | |
Sigmoid | N/A | Sigmoid() | act_sigmoid() | |
硬 Sigmoid (New!) | N/A | N/A | N/A | 仅后端 |
池化层
池化 | 结构体 API | 层 API | 备注 |
---|---|---|---|
最大池化 | maxpool_s() | MaxPool() | |
平均池化 | avgpool_s() | AvgPool() | |
总和池化 | sumpool_s() | SumPool() | |
全局最大池化 | global_maxpool_s() | GlobalMaxPool() | |
全局平均池化 | global_avgpool_s() | GlobalAvgPool() | |
全局总和池化 | global_sumpool_s() | GlobalSumPool() | 动态输出迁移 |
矩阵操作层
矩阵 | 结构体 API | 层 API | 备注 |
---|---|---|---|
连接 | concat_s() | Concat() | 通过任何轴连接 |
乘法 | mult_s() | Mult() | |
加法 | add_s() | Add() | |
减法 | sub_s() | Sub() |
依赖
NNoM 现在默认使用本地纯 C 后端实现。因此,不需要特殊依赖。
不过,您需要启用 libc
以进行动态内存分配 malloc()、free() 和 memset()
。或者,您可以在系统中移植到等效的内存方法。
优化
CMSIS-NN/DSP 是一个面向 ARM-Cortex-M4/7/33/35P 优化的后端。您可以选择它以获得最高 5 倍的性能提升。NNoM 会在满足条件时使用 CMSIS-NN 中的等效方法。
详情请查看 移植和优化指南。
已知问题
转换器不支持隐式定义的激活
该脚本目前不支持隐式激活:
x = Dense(32, activation="relu")(x)
请改用显式激活:
x = Dense(32)(x)
x = Relu()(x)
提高准确性的提示
- 在每个卷积层之后附加一个批归一化,以限制激活范围,从而有助于量化。BN 在 NNoM 中不会增加额外的计算。
- 不要训练太多周期。大量的周期会增加激活中的极端数值 -> 降低量化分辨率。
- 为瓶颈留出足够的数据,不要在模型输出前压缩数据,否则在量化时会丢失信息。
联系方式
也可以联系我进行现场支持。
发表文章时需要引用
如果您有任何问题,请使用上述联系方式与我联系。
示例:
@software{jianjia_ma_2020_4158710,
author = {Jianjia Ma},
title = {{一款适用于微控制器的高级神经网络库(NNoM)}},
month = oct,
year = 2020,
publisher = {Zenodo},
version = {v0.4.2},
doi = {10.5281/zenodo.4158710},
url = {https://doi.org/10.5281/zenodo.4158710}
}