没有读过本系列前几期文章的朋友,需要先回顾下已发表的文章:
上期回顾
CMSIS-NN作为在i.MX RT上运行深度神经网络的关键幕后英雄,在上期中终于闪亮登场,并且在i.MX RT强大算力的支持下,分类10样物体只是小试牛刀——每秒能识别多达50次!
不过,那个模型只有87K个参数,4个层,在很多专业人士的眼里还算不上“深度”网络。这一次,咱们就带上CMSIS-NN重装上阵,踏入深水区!
奇怪,这与穿糖葫芦有半毛钱关系吗?不急不急,看到后面就明白啦:)
为什么是深度神经网络
深度学习的基础是构建多层神经网络(NN)。
神经网络作为深度学习的基础技术,模型表达能力强,尤其是善于处理非结构化的多媒体数据。对于开发者而言,神经网络相比众多机器学习算法,需要的数学基础更少,相对单纯,容易掌握。这些最终使软硬件开发资源也最为丰富。
其实小编是想说,传统的机器学习算法种类多,对数学要求高,小编底子薄,可以捡容易理解的做~
神经网络的构建块
虽然神经网络可以很深,链接关系可以错综复杂,但基本构建块——也就是层(layer),种类却并不多,主角就更少了。而且,主角在MCU上也都是跑得动的。构建块从运算结构上看,大多是主运算搭配后加工的方式。
主运算和后加工的常见种类都屈指可数:
是不是很有一种三板斧打天下的感觉?
这其中,普通卷积层(CNN)是最“万能”的。在设计一个新的模型时,常常可以先用若干个卷积层提取特征,如果需要分类,尾部再追加一个全连接层。
如果MCU算力强大或存储空间不足,可以把CNN拆分成逐通道卷积(DS-CNN)尾随一个1x1 CNN,以在相同参数下获取更强的表达能力,或在相同表达能力下节省空间。反之对于算力较弱的MCU,则可适当把CNN替换成全连接层(FC),它们虽然参数多,但算力需求通常远远小于CNN。
穿糖葫芦——连接构建块
使用这三种基本构建块并且适当处理输出后,形成神经网络的一级运算,也称为一个层。每个层就犹如电路中的一个基本元件,按串联、并联、混联的方式搭建在一起,就能组成强大的神经网络。
此外,在训练模型期间,还会加入一些辅助运算,比如批正则化、随机丢弃等,以提高训练效率。在把模型转换成可供部署的形式时,会去掉或合并它们。
回忆前面神坛上的某种CIFAR-10模型,它是3个卷积层和一个全连接层,彼此手拉手心连心,这不由得让小编满满地回忆起儿时的一大奢侈品:
普通糖葫芦——直筒式连接
其实,神经网络就是喂给计算机吃的糖葫芦。
比如,前几期的主角,CIFAR-10 CNN模型:
这样看来,咱们最简单的CIFAR-10模型就是“大红果甘薯藕片”糖葫芦了,是不是很开胃?
什么,肚子小吃不下(内存不足)?没关系,我们可以把大红果换成一瓣桔子(DS-CNN)和一个小红果(1x1 CNN):
为了糖葫芦容易穿,最上面要留一个大红果,我们保证没核!
什么?胃软消化不了(算力不足)?没关系,我们可以多用容易消化的藕片
不过,提醒一下,藕断丝连,藕片的丝比较多(全连接层的连接很密),并且块头非常大(参数多),适合大胃王啊(大Flash),实在吃不了我们可以减量不加价(压缩FC层)~
这种穿糖葫芦式地叠加运算单元,是构建神经网络最常用最简单的一种,这是微型/小型网络的首选,已能应对大部分MCU上深度学习问题。这种结构的buffer管理也非常简单,ping-pong双buffer即可应对。
拔丝糖葫芦——残差连接
这些糖葫芦还能改得更美味不?当然可以,下面隆重介绍“拔丝蓝莓糖葫芦”:
棕色的焦糖丝,小小的蓝莓,口水ing… 顺便说,用这种配方的糖葫芦一般很长,比天津大麻花还长,动不动就穿几十个这种“什锦小坨”,价格(算力需求)嘛,自然也比较贵了。
(别问我为什么,这个配方的主人管这个糖葫芦叫MobileNetV2, “什锦小坨”称作”bootleneck”,拔丝叫“残差连接”。后来又出了短一点超值版糖葫芦叫MobileFaceNet)
通过这样把某层的输出再加到几层之后,就可以轻松创建容易训练的更深的网络,能胜任更复杂的问题,如识别人脸,以及分辨上百种以上的物体。这种用深度换“宽度”(每层参数大小)的方法非常适合MCU这类空间受限的平台。这种结构需要更灵活的buffer管理,不能再继续使用ping-pong buffer。
“硬塞”糖葫芦——拆分与拼接
那边谁在说,牙不好嗓子眼儿小(RAM/Cache不足)?没关系,我们给您切开。不过,作为服务费,总量有了减少并且搭配了便宜点的甘薯(减少了模型参数),敬请谅解~
早期的神经网络构建块中包含的参数较多,比如卷积核常用5x5,后面创新的结构常常把单个构建块化整为零成一个小型的子网络,既减少了参数又改进了性能。要支持这种结构,需要在buffer管理中再添加拼接操作的支持。
简化工艺——组合构建块
什么,赶时间等不及穿?没关系,我们贴心地为顾客预制了糖葫芦的半成品,只要把它们穿好就行了!
尽管神经网络的结构很丰富,但整体上看仍然是直筒式的顺序处理结构。在搭建较深的神经网络时,一些片段常常重复出现,于是在设计网络时可以像宏定义一般预制复合结构,再串在一起,这样可以简化设计,并且灵活多用。
拔丝糖葫芦试吃——基于 MobileFaceNet的实时人脸识别演示
废话不多,先上图:
不给配料表的食品不是放心食品,没有源代码的demo不是负责任的demo,这个demo的原代码以MIT开源,位于https://github.com/RockySong/micropython-rocky/tree/mfn_demo
等等,谁看到了一个似曾相识的字眼——micropython!
没错,这个demo就是构建在micropython和OpenMV的软件基础上,借助了OpenMV-RT项目中的图像提取与人脸检测功能,并使用python编写的demo业务逻辑。
不过,我们并没有使用OpenMV的神经网络支持——它不支持MobileFaceNet需要的残差连接。而是用前两期中我们介绍的部署工具和流程,转换成了调用CMSIS-NN的C代码,集成进去的。
再聊“拔丝糖葫芦”之MobileFaceNet
MobileFaceNet有31个“什锦小坨”(bottleneck单元)外加几个大红果(CNN),算上甘薯(激活层),总共有100多个层,共100万个参数!和袖珍糖葫芦CIFAR-10相比,可谓是庞然大物了!
当然了,它的能力也非常强悍。和只能分类10样物体的CIFAR-10相比,它能分辨的脸无上限,并且添加删除人脸不需要重新训练模型!
您没看错,无数张脸并且不用训练!这么神奇的能力是怎么做到的呢?原来,它没有最后的全连接(FC)层来分类,而是输出了128个数。这128个数经过这100多个层的处理,高度抽象地概括了一个人的脸部特征。只要数据库里存储了不同的特征,就能识别不同的人啦!
既然是这样,使用就简单了!首先是录入人脸。过程很简单,先站在镜头前面摆好pose,openmv软件会在100ms内迅速地检测出你的可爱小脸蛋,然后切成64x64点再送给MobileFaceNet过一遍,得出128个数存进数据库。后面,你就可以脱掉马甲,带上墨镜,涂满口红……,倒饬一番后再回到镜头,它照样认出你——300ms识别一次,识别率约97.5%。
别吃惊,这水平还只能算是个玩具。真正商用的人脸识别系统,比亲妈都更能记住你的模样,不管老了还是胖了瘦了。而且,如果拿假人头套、照片什么的去骗它,那是骗不过的!这一切要做到实时顺畅,就需要非常强大的软件和算力,并不是通用MCU能hold住的。
既然是玩具,为什么还要弄呢?其实,小编们制作这个demo的时候还刚刚入坑,懵懵懂懂,而人脸识别又是深度学习说什么也绕不过的一个话题,于是想挑战一个高难度的。另一方面,也是为了验证自己开发的工具是否完善,并且检验一下i.MX RT1060的综合性能。
更重要的是,通过这个极限操作,我们学会了怎么做强化训练,怎么优化模型、优化代码以适应MCU的胃口,为下一步在MCU上打造一个方便实用的机器学习平台做好了准备,这样的平台将为客户实现那些难度低于人脸识别的应用,提供实用而可靠的环境。