大家好,我是 杰哥编程
MFCC语音特征提取算法优化(CRUDer思维)
重点提示:本文适用于对MFCC已经有一定的了解了的小伙伴
刚听说mfcc这个东西的小伙看着篇 肯定是看不懂的
可以翻到文章最下面看看我的参考链接,预习一下,
并且看明白python的mfcc源码后再来看我的博客
下面是我的gitee仓库,欢迎大家关注↓ gitee源码仓库链接跳转
对我熟系的小伙伴知道我在各个博客或媒体平台上的自我介绍 是嵌入式人工智能这一块的开发
也确实我当时在外企工作的时候 是辅助于AI工程师做一些嵌入式上相关的事情的 所以有线上的粉丝和线下学生对我过去的工作内容感兴趣 为了满足线下学生和线上粉丝对我过去经历感兴趣的
无理要求
,我~摊牌了 我不装了 我是巨佬 比大佬还大的那种
决定出一期关于我过去工作的一次任务
如果有大佬发现不合理的地方,欢迎斧正
我仅以
程序员的视角
讲解自己是如何优化MFCC算法的大体思路仅交流,不是技术教学,不是技术教学,不是技术教学
因为很久没碰mfcc 所以
很多地方忘了
其中有许多不正确的地方,和优化不足的地方
欢迎斧正 指点 交流
本篇文章适用于对MFCC已经有一定的了解了的小伙伴 并且后面对mfcc的讲解中也都很简化,如果你没看过mfcc源码, 可能会觉得哪有那么简单,我很高傲,在扯犊子之类的想法
这是我在S..Y公司第一次接触的 也是自己独立完成的, 也是最后剩一丢丢没完美优化完整(对我来说), 当我刚接手这个项目的时候我是完全懵逼的 因为我师傅只是说让我把这个任务优化一下 让运行速度快一点 而我大学学的是数字图像处理,不是数字信号处理 对语音信号完全不知道,且不知道从哪下手找博客 于是我直接啃源码,啃了好几天,顶不住 去问师傅,他说你去了解下mfcc 我心中一万个马儿奔腾而过....
r
这个过程经历了从python转为c++再转成c代码
包括数据采集读取+mfcc特征处理+ai推理
当我接到这个任务的时候整个过程在2.3s~2.7s左右
但采集的音频数据确是1秒内的,算法的处理速度完全跟不上
怎么办!
平台
硬件: 一个多核的cortex-M4开发板
采集设备: 一个mic头 、SD卡里放静态的音频数据
软件:
公司自研的AI推理库 nnablar好像是这名 你们可以github搜一下 开源的
python的librosa.feature.mfcc库源码
特征
特征是什么?特征是大学噩梦! 学过复变、线代的、考过研的小伙伴都忘不了!!!
MFCC
上面是mfcc的整个流程图
在优化方式中,我把他们分成了3类
但是在讲之前 我相信各位巨佬
都听说过一个专业词:空间换时间
这是性能优化中最常用的一种方法
也是能够最大化优化时间的一种方法
搜了一下 没有一个比较好的解释
那就让我在这片文章中给他一个定义:
即理想的时间范围内,某些系数会因空间位置的变化而发生变化,但不会因时间的变化而发生变化,我们便可以把他们提前计算出来,用于后续计算的重复利用
是不是清晰易懂 哈哈哈哈
好正式进入技术阶段,
我会借用一下其他博客对上面流程图中对mfcc内部各个功能的解释
并用自己的理解通过白话文告诉大家!
第一类
一元参数,且系数不会随时间发生任何变化
这类在软件层面 没啥优化空间 影响权重最小
我能想到的就是:
- 数组的访问改为指针的访问
- 结构体成员的布局
- 移位实现乘除
预加重
流程图中第一个方框的第一步
<math xmlns="http://www.w3.org/1998/Math/MathML"> y ( t ) = x ( t ) − a x ( t − 1 ) y(t)= x(t)-ax(t-1) </math>y(t)=x(t)−ax(t−1)
预加重处理其实是将语音信号通过一个高通滤波器
等式右边除了和时间有关系的x参数 另一个系数a并没有任何变化
功率谱
流程图中第二个方框
功率谱(周期图periodogram) 对语音信号的频谱取模平方得到语音信号的谱线能量
\
ptyhon
librosa.power_to_db
将频谱图转换为声波图
就是取个平方
<math xmlns="http://www.w3.org/1998/Math/MathML"> P = ∣ F F T ( x i ) ∣ 2 N P= {\vert FFT(x_i) \vert ^2 \over N} </math>P=N∣FFT(xi)∣2
均归一化滤波器
流程图中第七个方框
其实应该是第六个方框DCT里的最后一个操作
均值归一化
简单地从所有帧中减去每个系数的平均值
filter_banks -= (numpy.mean(filter_banks, axis=0) + 1e-8)
第二类
记录指针地址来代替空间划分的利用率 也节省了数据的搬运时间 并且在使用的时候再去计算 给我感觉效率更高
分帧
流程图中第一个方框的第二步
在预加重之后,我们需要将信号分成短时帧。因此在大多数情况下,语音信号是非平稳的,对整个信号进行傅里叶变换是没有意义的,因为我们会随着时间的推移丢失信号的频率轮廓。语音信号是短时平稳信号。因此我们在短时帧上进行傅里叶变换,通过连接相邻帧来获得信号频率轮廓的良好近似。
比较好玩 是一个滑动的窗口
后续会对滑动的窗口分别进行处理
如果先分好帧 会占用h很大一块空间 数据搬运也会浪费时间
我的前辈写的c代码就是直接用指针来记录分帧的位置
包括网上看到的也挺多是这么做的
然后我印象中是把分帧和后面的几个计算融合到一起 分一次帧就算一次
效率上主观感觉会更高 最后结果想差貌似不大
当时也确实没去做大量实验验证有实质效率提升
第三类
空间换时间
加窗
流程图中第一个方框的第3步
将信号分割成帧后,我们再对每个帧乘以一个窗函数,如Hamming窗口。以增加帧左端和右端的连续性。抵消FFT假设(数据是无限的),并减少频谱泄漏
<math xmlns="http://www.w3.org/1998/Math/MathML"> W ( n , a ) = ( 1 − a ) − a cos ( 2 π n N − 1 ) W(n,a)= (1 - a) - a \cos ({2 \pi n \over N-1}) </math>W(n,a)=(1−a)−acos(N−12πn)\
可以看到hamming窗的计算就是一个三角周期函数
系数a是默认值
系数n是分帧的的长度(采样点数)
所以满足第二类的分类
mel滤波
流程图中第4个方框
人的听觉系统是一个特殊的非线性系统,它响应不同频率信号的灵敏度是不同的。在语音特征的提取上,人类听觉系统做得非常好,它不仅能提取出语义信息, 而且能提取出说话人的个人特征,这些都是现有的语音识别系统所望尘莫及的。如果在语音识别系统中能模拟人类听觉感知处理特点,就有可能提高语音的识别率。
梅尔频率倒谱系数(Mel Frequency Cepstrum Coefficient, MFCC)考虑到了人类的听觉特征,先将线性频谱映射到基于听觉感知的Mel非线性频谱中,然后转换到倒谱上。
写这篇博客的时候我看到别的博客上写着 频率转换为mel刻度的公式是一个对数函数
多少个滤波器 通常是40个 根据你的项目需求可能不一样 是一个固定的参数
音频采样频率也是在做mfcc特征处理前 或者说采集音频前就确定的参数
公式:
一个是三角滤波器的频率响应定义
我就不写了 直接网上截图\
所以可以确定得是他确实是可以通过提前计算好 后面直接拿来用
参考python源码melsp = librosa.feature.melspectrogram
因为我现在写博客的电脑没有python环境 不然可以把源码贴出来
很久没碰了 忘记python源码是怎样的 不然可以跟你们解析下
不过没关系
已经了解的人应该能知道根据上面的那个格子里都是三角形图片
他是根据采样率和默认滤波器个数来算出的一个矩阵好像是
所以满足第二类的分类
取对数
流程图中第5个方框
每个滤波器组输出的对数能量
公式: 就是个取对数函数
对数函数
DCT
流程图中第6个方框 系数就是个余弦函数 同理于hamming窗口化
水不动了 你们自己看公式:
FFT的蝶形逆序
我是参考这篇博客的:zhuanlan.zhihu.com/p/135259438
但我当时在工作的时候并没有把他的计算速率优化到我最满意的程度
细节不讲了 你们自己去看这篇博客
然后几个我印象中理解的核心点与大家交流一下
第一点:
蝶形逆序里面 不需要每个都进行一次排序
需要进行逆序的次数是可以计算出来的,并且是小于等于一半的
这个一半的概念就是假如有8个要逆序 只需要排序<=4次以内 就可以完成逆序
我当时觉得我在努力理解一下应该就能知道怎么算出来了
但是当时领导给了我其他的任务
并且我已经把算法优化到1秒以内了 就没再去深究了
然后我取了个巧 直接取一半次数
所以我只需要算一半的次数 虽然他最后排序次数不到一半次数
但是也能节省一定量的时间
第二点: 旋转因子的计算也费时间
但是我也忘记他是不是可以提前计算出来
然后放着准备随时使用的了
over 高兴
文章有点水
但是希望能对以后有需要优化的小伙伴 在网上苦恼搜索优化方法的时候有一定的帮助
对我来说足够了
毕竟我本身也不是算法优化工程师 嘿嘿