本文是基于nnom神经网络框架实现的。nnom是一个为单片机定制的神经网络框架,可以实现tensorflow 模型的量化和部署到单片机上,可以在cortex m4/7/33等arm内核的单片机上实现加速(stm32,lpc,nordic nrf 等等)。
nnom和本文代码可以在后台回复:“麦克风降噪”领取。
stm32实现单麦克风实时神经网络(rnn)降噪演示
硬声创作者:麻博士在科研
这个例子是根据著名的 rnnoise (https://jmvalin.ca/demo/rnnoise/) 的降噪方法进行设计的。整体进行了一些简化和定点化的一些修改。
本例与rnnoise主要的区别如下:
此例子并非从rnnoise的模型直接转换而来,而是从新基于keras训练一个新模型,并转化成nnom模型。
音频信号处理去掉了pitch filtering的部分。
rnn 网络定点化时,根据定点模型的一些特点进行了部分神经网络结构修改。
训练数据集选择上使用了微软的可定制语音数据库而不是rnnoise收集的数据库。
此例子用到的三方库如下,大部分为宽松许可,但请大家在使用时遵循他们的开源协议。
rnnoise (https://jmvalin.ca/demo/rnnoise/)
microsoft scalable noisy speech dataset (https://github.com/microsoft/ms-snsd)
python speech features (https://github.com/jameslyons/python_speech_features)
arduino_fft (https://github.com/lloydroc/arduino_fft)
cmsis (https://github.com/arm-software/cmsis_5)
nnom本身许可为 apache-2.0,详细信息请看nnom 开源主仓库下的许可信息 (https://github.com/majianjia/nnom).
一些背景知识
如何用神经网络进行语音降噪?
神经网络降噪通常有两种方式:
语音信号直入神经网络模型,神经网络全权进行识别处理并输出降噪后的语音信号。
神经网络识别语音和噪音的特征,动态输出增益,使用传统信号处理方法进行滤波。
rnnoise 使用的是第二种方法。
实际进行语音降噪(滤波)的部分,是一个均衡器,也就是大家播放器内调节低音高音的那个玩意儿。而均衡器(equalizer)的本质是很多个平行的带通滤波器(bandpass filter). 我们神经网络的输出,就是均衡器内各个filter band的增益。
那输入是什么?跟之前的 keyword spotting例子(https://github.com/majianjia/nnom/tree/master/examples/keyword_spotting) 一样,我们这里使用了梅尔倒频谱 (mfcc)。如果不熟悉的小伙伴,可以回去看看kws的解释或自行百度。
跟rnnoise有一些不一样的是我们的例子使用mfcc和梅尔刻度 (mel-scale) 而不是他们用的opus-scale 或者响度刻度 (bark-scale)。单纯从刻度的对比上,他们其实差别不是很大。感兴趣的同学可以自己去查查他们的区别。
系统图如下
运行步骤
如果想看详细的解析,请跳到文章后半部分。这里介绍在rtt和stm32l476板子上把这套算法跑起来的步骤。
1.下载语音数据集
这里我们使用的数据集是微软的可定制语音数据集microsoft scalable noisy speech dataset (ms-snsd: https://github.com/microsoft/ms-snsd)。我们可以定制时长,噪音类型,噪音混合信噪比等。你需要把整个仓库下载在 ms-snsd/文件夹内。整个数据库有2.x gb大佬们请自行进行github加速。
下载完后,你就可以用它生成我们需要的干净的语音和带噪音的语音。同时我们还控制了语音混合的程度,也就是信噪比(snr).
在ms-snsd/目录下修改 noisyspeech_synthesizer.cfg 就可以配置需要生成的语音文件,推荐配置如下:
sampling_rate: 16000audioformat: *.wavaudio_length: 60silence_length: 0.0total_hours: 15snr_lower: 0snr_upper: 20total_snrlevels: 3如果打算快速测试一下,可以把 total_hour 减少为1或者2小时。
修改完后,运行 noisyspeech_synthesizer.py 就可以生成我们需要的音频wav文件了。我们需要一对一的干净的语音和带噪音的语音,它们分别在ms-snsd/cleanspeech_training 和 ms-snsd/noisyspeech_training 内。
2. 生成训练数据集
之前一步获取到的是.wav文件,而我们训练神经网络使用的是 mfcc 和 gains。
现在我们可以运行例子提供的gen_dataset.py来计算mfcc和gains。它最终会生成一个dataset.npz文件。
在这个文件里面,你可以配置这些内容
需要mfcc的特征数(同时也会修改均衡器equalizer的banpass filter的数量)。修改 num_filter = 20即可。通常数字在10到26。
这个脚本也会生成一个c工程使用的滤波器参数文件equalizer_coeff.h (generate_filter_header(...))。在c语音的均衡器中会使用这个头文件。
另外,这个脚本还会生成两个demo音频。一个叫_noisy_sample.wav 另一个叫 _filtered_sample.wav。前者为从训练集里面选出的一个带噪音的范例,后者为用gains和均衡器滤波后文件。基本上,这个文件代表了这个降噪方法的最好的水平。后文会有详细的说明怎么生成这个gains。
3. 训练模型
当dataset.npz生成后,我们就可以跑 main.py来训练keras模型了。训练好的模型会保存在目录下model.h5
因为我们的模型最终要放在单片机上跑,rnn 每次处理一个 timestamp,所以我们的模型设置为stateful=true 和 timestamps=1。这样的设置对于训练并不是很理想,因为反向传播(bp)没有办法很好的在很小的batch上工作。我们的batch尽量设置得很大。这里设置batchsize >= 1024。
同时,这一步会把我们之前的噪音范例_noisy_sample.wav ,使用rnn生成的gains来滤波filtered_sig = voice_denoise(...)(可以对比我们真实gains降噪的结果)。滤波后的文件保存为_nn_filtered_sample.wav。
在最后,调用nnom的api generate_model(...) 生成nnom模型文件 weights.h。
4. rnn 在 nnom 上部署
本例提供了sconstruct, 所以你可以直接在目录下运行 scons 来编译。默认使用目录下的main.c 编译成pc可执行程序。支持32/64bit windows。理论上也支持linux。
这个二进制文件可以直接对 .wav 文件降噪并生成一个新的 .wav文件,使用方法如下:
注意:仅仅支持16khz 1ch的格式。(程序不解析wav只复制文件头)。
win powershell: .\rnn-denoise [input_file] [output_file] 或者拖拽.wav 文件到编译完成的*.exe上
linux: 大家自己试试
比如,运行这个指令生成定点rnn滤波后的音频:.\rnn-denoise _noisy_sample.wav _nn_fixedpoit_filtered_sample.wav
到此,目录下一共有四个音频,大家可以试听一下。
_noisy_sample.wav --> 原始带噪音文件_filtered_sample.wav --> 用真实gains降噪的文件(训练的gains)_nn_filtered_sample.wav --> keras浮点模型gains 降噪_nn_fixedpoit_filtered_sample.wav --> nnom定点模型gains降噪关于演示可以看文章顶部的视频。
不过,大家可以先看个图视觉上感受一下。filtered by nnom是我们单片机上的效果,对比keras是模型原始输出的效果。而truth gain是模型训练输入的参考,也就是最原始最好的效果。可以看到这个算法滤掉的不少的东西,具体是不是噪声。。。再说。
以下是一大波细节讲解
总的来说,我推荐大家看 gen_dataset.py 和 main.py里面的步骤,很多详细的步骤都在注释里面有相关的解释。
关于训练数据
x_train 里面包含了13或者20个(默认)mfcc,除此之外,还有前10个mfcc特征的第一和第二导数(derivative)。这些为常用的语音识别特征。所以一共有 33 到 40 个特征。
训练数据的生成步骤
y_train 里面有两组数据,一组是gains,另一个叫 vad
gains 与rnnoise方法相同,为 clean speech/noisy speech 在每一个band上的能量的开平方。是一组0-1的值,组大小为均衡器的带通滤波器个数。
vad 全称叫 voice active detection。为一个0-1的值,指示是否有语音。计算方法为检测一个窗口内总能量的大小是否超过一个阈值。
语音能量和激活阈值
关于 gains 和 vad
在默认的模型里面,有两个输出,其中一个便是vad。在main_arm.c (单片机版本的demo)里面,这个vad值控制了板子上的一个led。如果vad > 0.5 led 会被点亮。
下图为使用keras的模型识别 vad时刻 和 gains 的计算结果。
在语音中各个频段的增益
关于均衡器
这里使用了20(默认)或者13个带通滤波器(filter band)来抑制噪音所在的频率。实际上你可以设置成任何值。不过我推荐 大于10且小于30。每一个独立的带通滤波器的-3db点都与它附近的带通滤波器的-3db点相交。响频曲线如下:
滤波器响应频率范围
音频信号会平行地通过这些带通滤波器,在最后把他们相加在一起。因为滤波器是交叉的,最终混合的信号幅度过大导致数值溢出,所以最终混合的信号会乘上一个0.6(并非数学上验证的数值)再保存在int16 数组内。
关于rnn模型的结构
这里提供了两个不同的rnn模型。一个是与rnnoise 类似的模型,各个rnn层之间包含很多的支线。这些支线会通过 concatenate 合并在一起。这个模型还会提供一个vad输出。整个模型一共约 120k的权重。比rnnoise稍高因为做了一些针对定点模型的改变。其实这个模型有点过于复杂了,我尝试过减少模型参数,仍然可以很好的进行降噪。大佬们可以大胆地调整参数。如图下图所示。
另一个模型是一个简单的多层gru模型,这个模型不提供vad输出。非常震惊的是这个模型也能提供不错的效果。
如果想尝试这个简单的模型,在main.py里面修改 history = train(...) 成 train_simple(...)。
keras的rnn需要把 stateful=true 打开,这样nnom在每计算一个timestamps的时候才不会重置state。
mcu 例子
这里提供了一个 mcu 的文件main_arm.c。这个文件针对 stm32l476-discovery 的麦克风做了移植,可以直接使用板载麦克风进行语音降噪。
例子通过一个绿色 led(pe8)输出vad检测的结果,有人说话时就会亮。
除了单片机相关的代码,功能上mcu代码main_arm.c与pc代码main.c完全一致, 本例虽然做了音频降噪,但是并没有针对音频输出写驱动,所以降噪输出是被直接抛弃了。大家可以自己写保存或者回放的代码。
如果你使用的是 arm-cortex m系列的mcu,做以下设置可以提升性能 (参考下面性能测试章节)。
打开 nnom 的 cmsis-nn 后端,参考 porting and optimization guide (https://github.com/majianjia/nnom/blob/master/docs/porting_and_optimisation_guide.md)
在 mfcc.h里面,打开 platform_arm 宏定义来使用arm_fft。
mcu 上的性能测试
传统的 rnnoise 不止包含了浮点模型,还包括了其他计算(比如pitch filtering),导致总计算量在40mflops左右。即是换成定点计算,一般的单片机也会很吃力。
本例中,浮点fft,定点rnn模型,浮点均衡器(滤波器),并去掉了pitch filtering(额其实是因为我不知道怎么用)。我对这里使用的几个运算量大的模块进行了测试,分别是mfcc部分(包含fft),神经网络部分,还有均衡器。
测试环境为
board: stm32l476-discovery
mcu: stm32l476, 超频到 140mhz cortex-m4f
音频输入: 板载pdm麦克风
音频输出: 无
ide: keil mdk
测试条件:
神经网络后端: cmsis-nn 或 local c (本地后端)
fft 库(512点): arm_rfft_fast_f32 或 纯fft arduino_fft
优化等级: -o0/-o1 或 -o2
均衡器滤波器数目: 13 band 或者 20 band
需要注意的是,这里使用的音频格式为 16khz 1ch,所以我们每次更新(fft窗口:512,overlapping=50%)只有 256/16000 = 16ms 的时间来完成所有的计算。
13 band equalizer
可以看到,在完全优化的情况下,最短用时仅仅6.18ms 相当于38% 的cpu占用。在不适用arm加速库的情况下,也完全在16ms内。因为所有的计算量是固定的,测试下来同一项目内时间没有太多的波动。
20 band equalizer
20个 band的情况下,在开启优化后也可以实现实时的语音降噪。
模型编译log
单片机内神经网络模型载入的log
单片机内神经网络模型性能
点击阅读原文,即可下载硬声app。
基于ARM和FPGA的DMD驱动波形实验平台设计与实现
专利表示Apple Watch可能先获屏下指纹技术
飞兆半导体获中国格力电器核心供应商表现奖
基于16位8通道DASAD7606的系统布局考虑
比亚迪联手华为 共同推动汽车行业与轨道交通行业发展
【技术分享】STM32实现单麦克风实时神经网络降噪
IC芯片的概述及分类
5G网络,能够带来什么样的全新的智能家居体验
LM1876的单电源供电音频功率放大电路
任正非:2000个英雄上前线,是炮灰还是炮弹?
Vedanta、富士康合资晶圆厂项目修改规划
国际知名车企的配套电池供货不足
CC2340开发环境搭建
赛灵思是如何从FPGA进入ACAP世界的
三句话生成CPU!中科院ChipGPT攻克AI芯片设计?
区块链是一项相对不稳定的新技术
接触器线圈220v接380v会怎样 交流接触器线圈电压看哪里
CPU前端总线详细介绍
iphone8什么时候上市?iphone8最新消息:致敬乔布斯,苹果iphone8能否开启库克时代?
音响器材中的滤波用电解电容器