信号为E时,如何让语音识别脱“网”而出? (2)

作为语音识别的路由器,特征提取环节的运算量并不大。然而其作为声学模型拓扑结构的输入,间接影响着深度学习的运算量,是我们在嵌入式ASR中要考虑的问题。

2.帧率抖动

5s统计一次直播流视频帧率,1min计算一次帧率方差,方差过大,视为推流帧率抖动.

3.声学模型(acoustic model)

声学模型作为语音识别的CPU,其重要性不言自喻。

一般地,它占据着语音识别大部分的运算开销,直接影响着语音识别系统的性能。传统语音识别系统普遍基于GMM-HMM的声学模型,其中GMM对语音声学特征的分布进行建模,HMM则用于对语音信号的时序性进行建模。

2006年深度学习兴起以后,深度神经网络(DNN,Deep Neural Networks)被应用于声学模型。

近十多年,声学模型的上深度学习的发展一路高歌,各种CNN、RNN、TDNN的拓扑结构如雨后春笋一一冒出,关于深度学习在声学模型的更多介绍见文。

对于嵌入式LVCSR来说,选择合适的DNN拓扑结构,并用合理的优化在手机实现结构的运算,是声学模型在其的核心诉求。

4.语言模型(language model)

语言模型,NLP从业者相对更为熟悉。在语音识别里,语言模型用来评估一个句子(即图2的词语序列)出现的概率高低。

在语言模型的实现算法中,最常见的为n-gram模型(n-gram models),利用当前词前面的n个词来计算其概率,是一个上下文有关模型。几年来,神经语言模型(Neural language models)使用词汇Embedding来预测,也得到广泛的发展与应用。

在嵌入式ASR中,由于计算资源要留予声学模型,所以语言模型采用的依旧是n-gram的思想。那么在有限的内存中,如何最大化存储语言模型,是嵌入式ASR要解决的问题。

5.发音词典

发音词典,是语音识别的内存条。内存能将硬盘的数据读入,并使用cpu进行运算。同样的,发音词典,能将语言模型的词条序列转化为音素序列,并用声学模型进行分数评估运算。

发音词典是连接声学模型和语言模型的桥梁,他的大小直接影响声学模型和语言模型的发挥空间。

在嵌入式ASR中,发音词典的大小,与语言模型的规模互相共鸣,所以要解决的问题可以与语言模型归为一谈。

6.解码器

解码器,估计这个词的来自英文decoder的直译,笔者认为更恰当的名字应称为识别器。之所以叫解码器,还有另外一个比较形象的原因。以16bit语音数据为例,计算机的存储是一堆我们看不懂的short类型数字,如同密码一般。语音识别能破解这些密码,将明文展示在我们面前。

所以通俗来讲,解码器就是将语音识别各个流程串联的代码工程。一般云端采用与WFST(带权优有限状态自动机)搭档的静态解码器,可以更方便地综合处理语音识别的各个环节。而嵌入式为了节省语言模型的内存开支,采用特定的动态解码器。

03

开始优化这些组件——速度和内存优化

为了优化这些“部件”占用的时间与内存,我们做了一系列工作:

neon计算优化,奇异值分解优化,哈夫曼编码优化。

1.neon优化声学模型计算

neon的计算优化,已是广大工程师们的老生常谈,机器学习相关的T族们更是耳熟能详。在嵌入式ASR引擎中,我们对核心高频运算的函数进行了neon优化,采用了汇编语言进行编写,最终有效提高了25%的计算速度。

接下来,本文现以实现char类型向量乘的介绍优化的实现,分三版本来介绍:

A. 优化前的朴素版

B. neon c版

C. neon汇编版

首先,我们将要实现的函数是:

/** * 实现两个char类型向量乘 * start_a: 向量A * start_b: 向量B * cnt:向量元素个数 * result:向量乘返回存储变量 */ void vector_product_neon(const char * start_a, const char * start_b, int & result, const int cnt);

A. 优化前朴素版

void vector_product_neon(const char * start_a, const char * start_b, int & result, const int cnt) { int res = 0; for(int j = 0; j < cnt; j++) { res += int(*start_a) * int(*start_b); start_a++; start_b++; } result = res; }

B. neon c版

Neon寄存器能实现128位空间的并行运算,对于char类型的向量乘而言,两两相乘的结果在short类型范围内,故可8个为一组实现。以下代码,8个元素一组,一次循环处理两组。在我们的深度学习运算中,隐层的向量长度保证为16倍数,实现代码如下:

void vector_product_neon(const char * start_a, const char * start_b, int & result, const int cnt) { int res = 0; int32x4_t neon_sum = vdupq_n_s32(0); int8x8_t neon_vector1; int8x8_t neon_vector2; for(int j = 0; j < cnt / 16; j++) { neon_vector1 = vld1_s8((char *)start_a); neon_vector2 = vld1_s8((char *)start_b); int16x8_t neon_tmp = vmull_s8(neon_vector1, neon_vector2); start_a += 8; start_b += 8; neon_vector1 = vld1_s8((char *)start_a); neon_vector2 = vld1_s8((char *)start_b); neon_tmp = vmlal_s8(neon_tmp, neon_vector1, neon_vector2); neon_sum = vaddw_s16(neon_sum, vget_low_s16(neon_tmp)); neon_sum = vaddw_s16(neon_sum, vget_high_s16(neon_tmp)); start_a += 8; start_b += 8; } for(int j = 0; j < 4; j++) res += vgetq_lane_s32(neon_sum, j); result = res; }

C. neon汇编版

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyxddy.html