在 CH32V003 上,神经网络通过量化感知训练表现出色,这让我很受鼓舞,我想知道这可以走多远。我们可以将神经网络压缩多少,同时仍然在 MNIST 数据集上实现良好的测试精度?当谈到绝对低端微控制器时,几乎没有比 应广单片机Padauk 8 位微控制器更引人注目的目标了。这些微控制器针对最简单和成本最低的应用进行了优化。该产品组合中最小的设备 PMS150C 具有 1024 个 13 位字一次性可编程内存和 64 字节 RAM,比 CH32V003 小一个数量级以上。此外,它具有基于专有累加器的 8 位架构,而不是功能更强大的 RISC-V 指令集。
是否也可以在应广单片机PMS150C 上实现可以对手写数字进行分类的 MNIST 推理引擎?
在 CH32V003 上,我使用了从 28×28 缩小到 16×16 的 MNIST 样本,这样每个样本占用 256 字节的存储空间。如果有 16kb 的闪存可用,这是完全可以接受的,但如果只有 1kword 的 rom,这就太多了。因此,我首先将数据集缩小到 8×8 像素。
上图显示了两种分辨率下数据集中的几个样本。在 16×16 下仍然很容易区分不同的数字。在 8×8 下仍然可以猜出大多数数字,但会丢失大量信息。
令人惊讶的是,仍然可以训练机器学习模型以令人印象深刻的准确度识别这些非常低分辨率的数字。重要的是要记住,测试数据集包含 10000 张模型在训练期间看不到的图像。非常小的模型准确识别这些图像的唯一方法确实是识别常见模式。我训练了许多不同的网络组合,以了解网络内存占用和可实现准确度之间的权衡。
参数探索
上图显示了我的超参数探索实验的结果,比较了具有不同权重配置和量化级别(从 1 位到 4 位)的模型,用于 8×8 和 16×16 的输入图像。最小的模型必须在没有数据增强的情况下进行训练,否则它们不会收敛。
同样,测试准确度和网络的内存占用之间存在明显的关系。增加内存占用可将准确度提高到一定程度。对于 16×16,在上限可以实现约 99% 的准确度,而对于 8×8 测试样本,可以实现约 98.5% 的准确度。考虑到 8×8 的信息损失很大,这仍然令人印象深刻。
对于小型模型,8×8 的准确度比 16×16 更高。原因是第一层的大小在小模型中占主导地位,而对于 8×8 输入,该大小减少了 4 倍。
令人惊讶的是,即使在小到半千字节的模型上,也可以实现超过 90% 的测试准确率。这意味着它可以装入微控制器的代码内存中!既然已经确定了一般可行性,我需要进一步调整以适应 MCU 的限制。
训练目标模型
由于 RAM 限制为 64 字节,因此模型结构在推理过程中必须使用最少数量的潜在参数。我发现可以使用窄至 16 的层。这会将推理期间的缓冲区大小减少到仅 32 字节,一个输入缓冲区和一个输出缓冲区各 16 字节,其余 32 字节用于其他变量。8×8 输入模式直接从 ROM 读取。
此外,我使用了间距不规则的 2 位权重(-2、-1、1、2),以便简化推理代码的实现。我还跳过了层规范化,而是使用恒定移位来重新调整激活。这些变化略微降低了准确性。最终的模型结构如下所示。
综合考虑,我最终得到了一个准确率为 90.07% 的模型,总共 3392 位(0.414 千字节),1696 个权重,如下面的日志所示。右侧面板显示训练模型的第一层权重,它直接掩盖了测试图像中的特征。与更高精度的模型相比,每个通道似乎同时结合了许多特征,看不到任何可辨别的模式。
在微控制器上的实现
在第一次迭代中,我使用了 Padauk 微控制器的稍大版本 PFS154。该设备的 ROM 和 RAM 是原来的两倍,并且可以重新刷新,这极大地简化了软件开发。推理代码的 C 版本(包括调试输出)几乎开箱即用。下面,您可以看到预测和标签,包括最后一层输出。
void
fc_innerloop_mem(
uint8_t
loops) {
sum = 0;
do
{
weightChunk = *weightidx++;
__asm
idxm a, _activations_idx
inc _activations_idx+0
t0sn _weightChunk, #6
sl a ;
if
(weightChunk & 0x40) in = in+in;
t0sn _weightChunk, #7
neg a ;
if
(weightChunk & 0x80) in =-in;
add _sum+0,a
addc _sum+1
sl a
subc _sum+1
... 3x more ...
__endasm;
}
while
(--loops);
int8_t
sum8 = ((
uint16_t
)sum)>>3;
// Normalization
sum8 = sum8 < 0 ? 0 : sum8;
// ReLU
*output++ = sum8;
}
将所有内容压缩以适合较小的 PMS150C 是另一回事。用 C 语言对这些设备进行编程时的一个主要问题是,每个函数调用都会消耗 RAM 用于返回堆栈和函数参数
应广单片机产品应用领域
玩具类:
RF/IR遥控直升飞机,遥控车,PS游戏机,儿童智能玩具,动物语音玩具,游戏方向盘,儿童学习机等
家电类:
电磁炉、电炒锅、电饭煲、热水壶,健康秤、冰箱控制系统、遥控器、电风扇、洗衣机控制模块、消毒柜、智能家居系统,遥控灯具等
电子消费类:
电子万年历、温度湿度计、跑步计速器、按摩器、数码复读录音、笔电子礼品、电子密码锁,镍氢,锂电池充电器,超声波测距,防盗报警器,灯饰控制,舵机
其它:
智能温湿度记录仪、智能温控器、计时器、计数器、电机调速控制器、鼠标、键盘、移动存贮盘