2011年,微软雷德蒙研究院将DNN(Deep Neural Networks)技术成功应用于英文连续语音识别,获得了相对33%的识别性能提升,识别结果变得可以被大多数人接受,开启了语音识别新潮流。近年来,在智能手表场景中,语音被认为是最佳交互方式,如Ticwatch已使用中文语音交互来操控手表、打电话以及查询信息等。这将带来语音识别请求量的爆发式增长。
然而,由于DNN计算中的矩阵维度通常达到512或以上,并涉及多个高维矩阵-矩阵相乘、矩阵-向量相乘,其模型尺寸有数十兆,计算量很大。常规的解决方案是移动端只负责发送语音和接收识别结果,在后端提供数百台服务器来部署语音识别服务,并利用昂贵的GPU显卡来加速DNN计算,需要耗费大量财力物力。
因此,无需服务器和网络的移动端离线语音识别成为更经济的方式。但是移动端计算性能、存储空间均有限,DNN的实现成为瓶颈之一。本文将从DNN计算和模型存储两方面介绍DNN在移动端的实现技巧,并结合iOS和Android的平台特性分别给出参考解决方案。
一、DNN计算
在语音识别中,DNN主要用作声学模型中观察概率的建模,典型的结构如图1所示。
图1 使用DNN作为声学模型
图1中,使用DNN作为声学模型中观察概率的建模方式,DNN在其中所起的作用是将观察到的声学特征(如MFCC特征序列构成的输入矩阵)映射到HMM框架中的观察概率,其中为DNN模型结构中M层各自的参数矩阵,维度通常达到512及以上。在逐层计算过程中,主要是矩阵-矩阵相乘运算:Y = WX + B,其中W为参数矩阵,X为输入矩阵,B为偏置向量,Y为输出矩阵,并用于下一层的输入。
因此,DNN计算过程其实是矩阵运算。DNN优化问题转化为如何在移动端加速矩阵运算的问题。此外,在语音识别中DNN中每个参数通常采用单精度浮点表示,模型文件较大。
二、矩阵运算性能分析及常见解决方案
1、性能瓶颈
矩阵运算,若采用常规的3层for循环计算方式,将涉及反复读取一段内存(一行或一列)到寄存器的操作,然后再进行浮点运算(一行乘以一列并求和)。众所周知,将数据从内存加载到寄存器的耗时,比从寄存器直接访问数据的耗时要大的多。且内存加载和存储指令的耗时比浮点运算更加昂贵,计算过程的大部分时间处于等待从内存中获取或返回新数据的状态。而这也正是矩阵运算的性能瓶颈所在。
2、移动端常见方案(定点化+neon)
移动端的DNN计算,目前常见的方案是利用定点化(32bit float转为8bit signed char)和NEON(SIMD在ARM架构下的实现)指令集来实现优化加速[2]。但这种方式仅仅是加速了浮点运算,对耗时巨大的内存访问并没有加速。此外,定点化的精度损失也较大。
3、改进方案(矩阵分块+neon)
类似OpenBLAS、ATLAS这种PC或服务器端开源矩阵运算库,最主要的优化是利用矩阵分块增加数据访问局部性,尽可能减少cache miss次数,并利用SIMD(单指令多数据流)并行加速来进一步减少浮点运算耗时。但这些开源库直接移植到移动端,费时费力。针对DNN计算中的矩阵相乘,可以利用该思想,结合ARM的NEON指令集来实现这种方案,并根据ARM的L1 cache大小来优化macro及micro核的设计。
下图是矩阵-向量运算,减少内存操作次数的伪代码示例。
图2 矩阵-向量运算减少内存操作次数的实现
图2中左侧为常规计算方式,右侧每次加载4个元素,虽然总的浮点计算次数(flops)相同,但可减少(2/3)mn次内存操作(memops)。
类似,矩阵-矩阵相乘运算原理的示意图如下图3所示,通过将矩阵分块,可显著降低内存操作次数[1]。
图3 矩阵-矩阵相乘中的矩阵分块示意图
4、性能对比
在项目开发中,详细对比了上述两种方案以及传统的3层for循环方案的矩阵相乘运算耗时。下表是在iPhone 4S真机上,模拟测试两个矩阵相乘的耗时对比。
表1 矩阵相乘的方案性能对比
方案 矩阵尺寸 |
3层for循环 (秒) |
定点化+neon (秒) |
矩阵分块+neon (秒) |
512*512 |
3.10 |
0.81 |
0.25 |
1024*1024 |
23.34 |
5.95 |
1.92 |
由表1可见,采用矩阵分块+neon方案,比定点化+neon加速约3倍,比3层for循环加速约12倍。
三、DNN计算在移动端的实现方案
移动端主要是iOS及Android平台下的实现,需根据平台特性来挑选实现方案。
1、iOS端
iOS自带的硬件加速框架vDSP,在底层实现了矩阵-矩阵、矩阵-向量相乘运算,其基本原理也是采用矩阵分块及NEON加速方案,但在底层进行了深度定制,无需重新实现。具体使用方式可参考vDSP开发手册。表2是在iPhone 4S上利用vDSP的测试结果。
表2 vDSP性能对比
方案 矩阵尺寸 |
矩阵分块+neon (秒) |
vDSP (4S,秒) |
512*512 |
0.25 |
0.07 |
1024*1024 |
1.92 |
0.59 |
由表2可见,在iOS平台下,采用vDSP框架更可取。
2、Android端
Android端尚无类似iOS的vDSP加速框架,采用定点化+neon指令集,或者矩阵分区+neon指令集来实现。需要针对具体Android平台的寄存器大小等硬件性能来设计Macro及Micro核的参数[1]。
3、模型压缩
由于矩阵相乘中,浮点运算耗时占比较小,可直接采用浮点运算,避免定点化带来的精度损失。在语音识别项目中,训练所获得的DNN模型,可进一步采用半精度浮点来压缩模型[4],经测试对识别准确率几乎无影响,可应用于离线语音识别的模型压缩中[3]。单精度浮点、半精度浮点对比如表3所示。
表3 单精度VS半精度
|
bit数 |
结构(符号位+指数位+尾数位) |
取值范围 |
精度 |
单精度float |
32 |
1+8+23 |
-3.40E+38~+3.40E+38 |
6~7位有效数字 |
半精度float |
16 |
1+5+10 |
-65504~65504 |
3~4位有效数字 |
利用半精度浮点,可将DNN模型压缩为原来的一半大小。
四、总结
本文主要介绍了DNN计算在移动端的实现技巧,重点分析了矩阵-矩阵相乘运算中的性能瓶颈,并结合平台特性给出移动端的实现方案。本文所实现的改进方案已成功应用于Android端离线语音识别项目中,识别速度比文献中常见的DNN加速方案提高约3倍。此外,可进一步利用半精度浮点来压缩DNN模型,精度比定点化更高,对语音识别性能的不良影响几乎可以忽略。欢迎感兴趣的同事一起交流学习。
参考资料
[1] BLIS: A Framework for Rapidly Instantiating BLAS Functionality. 2015.
[2] Speeding up Deep Neural Networks for Speech Recognition on ARM Cortex-A Series Processors. 2014.
[3] Accurate and Compact Large Vocabulary Speech Recognition on Mobile Devices. 2013.
[4] https://en.wikipedia.org/wiki/Half-precision_floating-point_format.
网易云新用户大礼包:https://www.163yun.com/gift
本文来自网易实践者社区,经作者胡光龙授权发布。