每当提到机器学习,大家自然就会联想到大量的 GPU 服务器集群,巨大的计算资源。那么,有没有可能在 ufun 这样一块只有 256KB Flash 和 48KB RAM 的小板子上加载 Tensorflow,Keras,Pytorch 等主流机器学习模型呢?当然可以! 

 

基于RT-Thread在STM32F103上运行机器学习模型_github

 

下面的视频就是在 ufun 上加载一个 Keras 训练导出的卷积神经网络模型,虽然模型小巧,但是用来跑经典的手写体识别也有 90+% 的精度

 

基于RT-Thread在STM32F103上运行机器学习模型_文件系统_02

下面是运行模型进行手写体识别:

基于RT-Thread在STM32F103上运行机器学习模型_文件系统_03

那么这是怎么办到的呢?

主要分为四步

首先 demo 上运行的是 RT-Thread 物联网操作系统,利用 RT-Thread 的文件系统从 SD 卡读取机器学习模型然后按照 Google 的 Protobuf v3 的协议进行解析,将解析的数据转换为 onnx 模型并打印出来,最后利用模型结构和权值进行推断 (inference)。

 

1

 · 运行文件系统 ·  

基于RT-Thread在STM32F103上运行机器学习模型_文件系统_04

第一步就是要在ufun 上面运行一个文件系统,也就是先跑起来 RT-Thread,再打开文件系统就可以了,其实操作起来非常简单,都不怎么需要写代码

1.1 准备 RT-Thread 环境

首先,利用 git 下载 rt-thread 最新的源码,因为 ufun 的 SDIO 是最近才提的一个 PR,所以一定要用最新的源码,如果嫌 github 下载太慢,可以参照下面先从 gitee 飞速下载,然后更改项目地址为 github。(ps:RT-Thread的官网上就有个下载中心入口,地址:https://www.rt-thread.org/page/download.html请复制至外部浏览器打开

1git clone https://gitee.com/rtthread/rt-thread
2cd rt-thread
3git remote rm origin
4git remote add origin https://github.com/RT-Thread/rt-thread 
5git pull origin master

这样很快就下载好了最新的 RT-Thread 源码,接下来需要下载 RT-Thread 的 env 工具链(链接:https://www.rt-thread.org/page/download.html),关于 env 的配置可以参照这里,既然有官方文档,这里就不赘述了。(文档链接:https://www.rt-thread.org/document/site/programming-manual/env/env/)

1.2 运行文件系统

在电脑上进入 rt-thread 目录下 stm32f103-yf-ufun 的 bsp,例如

1F:\rt-thread\bsp\stm32\stm32f103-yf-ufun

然后右键打开 ConEmu:

基于RT-Thread在STM32F103上运行机器学习模型_2d_05

输入 menuconfig 就可以看到 Kconfig 的图形配置界面了。

基于RT-Thread在STM32F103上运行机器学习模型_文件系统_06

用小键盘的上下键导航,回车键确认,进入

1Hardware Drivers Config → On-chip Peripheral Drivers

用空格选中 Enable SDCARD (sdio)

基于RT-Thread在STM32F103上运行机器学习模型_github_07

然后退出保存配置文件,并生成项目,在 env 里输入:

1scons --target=mdk5 -s

就会自动根据配置生成 Keil 的项目文件,打开 Keil 项目:

基于RT-Thread在STM32F103上运行机器学习模型_机器学习_08

修改 Application/main.c 挂载 SD 卡:

基于RT-Thread在STM32F103上运行机器学习模型_机器学习_09向????滑动查看全部
 1#include <rtthread.h>
 2#include <rtdevice.h>
 3#include <board.h>
 4#include <dfs_fs.h>
 5int main(void)
 6{
 7    while (1)
 8    {
 9        rt_thread_mdelay(500);
10                if (dfs_mount("sd0", "/", "elm", 0, 0) == 0)
11                {
12                        rt_kprintf("Filesystem initialized!\n");
13                        break;
14                }
15                else
16                {
17                        rt_kprintf("Failed to initialize filesystem!");
18                }
19    }
20
21    return RT_EOK;
22}

 

编译下载到开发板上,就可以看到文件系统了。记得插上 SD 卡,用 ls 命令就可以看到 SD 卡里面的文件了,非常方便

基于RT-Thread在STM32F103上运行机器学习模型_github_10

 

 

 

 

 

 

 

 

2

 加载保存 Protobuf v3 数据

基于RT-Thread在STM32F103上运行机器学习模型_文件系统_04

既然有了文件系统,接下来就是解析 Google 的 Protobuf 文件格式了,因为机器学习模型是用的 Protobuf v3 的格式。

首先需要更新一下 RT-Thread 的软件包,因为相关的软件包也是最近才提交上去的。同样在之前打开的 env 下输入:

1pkgs --upgrade

这样就可以看到最新的软件包了,同样输入 menuconfig 进入图形配置界面,选中软件包 protobuf-c:

1→ RT-Thread online packages → IoT - internet of things
基于RT-Thread在STM32F103上运行机器学习模型_2d_12

记得选上软件包的例程。

基于RT-Thread在STM32F103上运行机器学习模型_机器学习_13

然后在 env 里面下载选中的软件包,就可以编译运行下载到板子上了。和之前一样,在 env 下输入

1pkgs --update
2scons --target=mdk5 -s
当代码传到板子上之后,打开串口就可以输入命令测试 protobuf 的解析了:
1msh /> protobuf_encode_decode 10 20
2---- Encoding ---
3Encoding 4 serialized bytes
4---- Decoding ---
5Received: a=10 b=20
6msh />
Protobuf 是 Google 设计的一种高效存储二进制文件的协议,具体格式这里就不介绍了,可以看到上面的例程里先编码2个数据,再将编码的数据解析出来。

当然也可以将数据编码保存到 SD 卡,再从 SD 卡里面解析出来:

 1# 编码保存到文件
 2msh />protobuf_encode_to_file 10 20
 3---- Encoding ---
 4Encoding 4 serialized bytes
 5---- Saving ---
 6Written to file amessage.onnx.
 7
 8# 从文件解码
 9msh />protobuf_decode_from_file
10---- Reading ---
11amessage.onnx file size = 4
12Read from file amessage.onnx
13---- Decoding ---
14Received: a=10 b=20
15msh />
上面的例程都是解析的 protobuf-c-latestexamples 目录下的 amessage.proto 协议。

 

 

 

 

 

 

 

 

 

3

 · 加载解析 onnx 模型 ·  

基于RT-Thread在STM32F103上运行机器学习模型_文件系统_04

现在有了文件系统,也可以读取 Protobuf 的数据了,接下来自然就是解析机器学习模型了。

这里简单介绍一下要解析的 onnx 机器学习模型,现在机器学习平台很多 Tensorflow,TFlite,Keras,Pytorch,mxnet, Caffe,各个平台都有自己的模型格式,而且互不兼容,于是为了统一机器学习的模型,现在有了 onnx (Open Neural Network Exchanges) 这样一套通用的机器学习模型,上面的不同平台模型都可以转换为 onnx 模型,所以如果能加载 onnx 的模型,那么基本上就是支持了所有的主流机器学习框架了。

和之前一样,进入 env 选中软件包 onnx-parser:

1→ RT-Thread online packages → IoT - internet of things
基于RT-Thread在STM32F103上运行机器学习模型_文件系统_15

选中后保存配置,下载软件包并生成 Keil 项目:

1pkgs --update
2scons --target=mdk5 -s

熟悉了 RT-Thread 的开发流程后,一切都变得很简单了,编译项目上传到开发板上运行,记得把 stm32f103-yf-ufunpackagesonnx-parser-latestexamples 下面的 onnx 模型复制到 SD 卡里面,有2个模型 onnx-lg.onnx 和 onnx-sm.onnx 一大一小,保存好模型后插入 SD开打开串口。

输入命令

1onnx_parser_example /mnist-sm.onnx

就可以看到解析出来的模型结构了,2 层卷积,Relu,Maxpool,最后 2 个 Dense 以及 Softmax 输出。

 1msh />onnx_parser_example /mnist-sm.onnx
 2
 3--- Reading from /mnist-sm.onnx ---
 4File size /mnist-sm.onnx is 2319
 5msh />---- Model info ----
 6IR Version is 5
 7Produceer name is keras2onnx
 8Produceer version is 1.5.1
 9Produceer version is onnx
10---- Graph Info ----
11---- Graph Input Info ----
12Graph inputs number: 1
13Input name conv2d_8_input
14Input type FLOAT
15Input dimension 4
16N x 28 x 28 x 1
17---- Graph Output Info ----
18Graph outputs number: 1
19Output name dense_10/Softmax:0
20Output type FLOAT
21Output dimension 2
22? x 10
23---- Graph Node Info ----
24Graph nodes number: 15
25Transpose   : conv2d_8_input                 ->    adjusted_input1                [Transpose6]
26Conv        : adjusted_input1                ->    convolution_output1            [conv2d_8]
27Relu        : convolution_output1            ->    conv2d_8/Relu:0                [Relu1]
28MaxPool     : conv2d_8/Relu:0                ->    pooling_output1                [max_pooling2d_8]
29Conv        : pooling_output1                ->    convolution_output             [conv2d_9]
30Relu        : convolution_output             ->    conv2d_9/Relu:0                [Relu]
31MaxPool     : conv2d_9/Relu:0                ->    pooling_output                 [max_pooling2d_9]
32Transpose   : pooling_output                 ->    max_pooling2d_9/MaxPool:0      [Transpose1]
33Reshape     : max_pooling2d_9/MaxPool:0      ->    flatten_5/Reshape:0            [flatten_5]
34MatMul      : flatten_5/Reshape:0            ->    transformed_tensor1            [dense_9]
35Add         : transformed_tensor1            ->    biased_tensor_name1            [Add1]
36MatMul      : biased_tensor_name1            ->    transformed_tensor             [dense_10]
37Add         : transformed_tensor             ->    biased_tensor_name             [Add]
38Softmax     : biased_tensor_name             ->    dense_10/Softmax:01            [Softmax]
39Identity    : dense_10/Softmax:01            ->    dense_10/Softmax:0             [Identity1]

如果输入 free 命令,可以看到:

1msh />free
2total memory: 38288
3used memory : 6496
4maximum allocated memory: 27812

运行 RT-Thread 后一共有大约 37 KB 的内存,实际用了 27KB 左右,还剩下了 10KB,所以用 ufun 加载一个小型的 onnx 通用机器学习模型是完全没有问题的。

 

 

 

 

 

 

 

 

 

 

 

4

 · 运行模型进行预测 ·  

基于RT-Thread在STM32F103上运行机器学习模型_文件系统_04

 

依旧是进入 env 选中软件包 onnx-backend:

1→ RT-Thread online packages → IoT - internet of things
基于RT-Thread在STM32F103上运行机器学习模型_2d_17

同样选中后保存配置,下载软件包并生成 Keil 项目:

1pkgs --update
2scons --target=mdk5 -s
编译项目上传到开发板上运行,输入命令
1onnx_mnist
基于RT-Thread在STM32F103上运行机器学习模型_机器学习_18

实际上只消耗了 16KB 内存,所以甚至可以运行在只有 20KB RAM 的 STM32F103C8T6 上面。

 

end

 · 总结 ·  

基于RT-Thread在STM32F103上运行机器学习模型_文件系统_04

机器学习的训练过程非常消耗计算资源,但是一旦模型训练好,视输入数据和模型大小,进行推断 (inference) 的过程其实也可以在 MCU 上运行。不过这个例子没有做量化和计算图优化,只是因为模型非常小,计算量也不大所以才可以很快地运行,之后有机会再慢慢改进。

 

E

 

基于RT-Thread在STM32F103上运行机器学习模型_加载_20

RT-Thread


让物联网终端的开发变得简单、快速,芯片的价值得到最大化发挥。Apache2.0协议,可免费在商业产品中使用,不需要公布源码,无潜在商业风险。

扫描二维码,关注我们