资讯动态

语音噪声抑制仿真:谱减法、维纳滤波与MMSE对比及参数调优

发布时间:2026/10/20 7:35:23 来源:尧图企业网站定制
1. 先搞明白噪声抑制在语音仿真里到底解决什么问题每次带学生做语音信号处理的仿真课设噪声抑制技术总是最后一个交上来的模块也是写评语时最费口舌的部分。原因不难理解降噪的输入是人声输出也是人声但参数稍微一动就失衡——减多了语音发闷减少了噪声残留更麻烦的是稍不留神还会冒出一串串水烧开一样的音乐噪声。这篇文章接着语音信号处理仿真系列往下走单讲噪声抑制这一节。我会从最基础的带噪语音模型说起对比谱减法、维纳滤波和MMSE估计三条路线然后给出一套可以直接跑的谱减法仿真流程最后聊聊效果评估和几个最容易让人头秃的问题。做语音课程仿真、论文复现或者要给一个语音前端模块做原型验证的这篇应该能让你少走不少弯路。1.1 带噪语音模型与信噪比所有讨论的起点做语音增强仿真第一件事不是打开代码写滤波器而是把数学模型写在纸上。绝大多数降噪算法都建立在加性噪声模型上x(n) s(n) d(n)其中s(n)是干净语音d(n)是噪声x(n)是麦克风拾取到的带噪信号。这个模型假设噪声与语音不相关、且只以相加方式混入忽略了卷积混响、回声和非线性失真。真实场景当然没这么简单但仿真里所有经典方法都以它为起点——搞清楚这一点后面调参数时才不会糊里糊涂。接下来是信噪比。在仿真里信噪比不是一个自然形成的值而是我们手动控制的。定义是SNR 10·log10( Σs² / Σd² )所以给语音加噪到5dB这句话实际操作是这样做的先算干净语音的平均功率再算原始噪声的平均功率然后按照目标信噪比缩放噪声幅度。snr_db 5.0 p_sig np.mean(clean**2) p_noi np.mean(noise**2) noise noise * np.sqrt(p_sig / (10**(snr_db/10) * p_noi)) x clean noise很多同学第一次写仿真图省事直接给噪声乘个固定小系数然后说这是带噪语音。这种做法的问题在于你根本不知道输入信噪比是多少后面比较算法好坏就没有依据。规范的做法是让程序自动按目标SNR缩放这样0dB、5dB、10dB、15dB都能做出一组可复现的测试数据。还有一点需要提醒用来生成带噪语音的噪声和算法内部估计出来的噪声功率谱必须是两条线。有的代码在降噪时直接把真实噪声谱代入这在仿真里叫作弊。因为真实算法拿不到这个信息它只能从带噪信号本身去估计。所以规范写法是先混合出带噪信号再让降噪模块只面对x(n)工作。1.2 噪声的种类决定方法选择有一个常见的误解降噪算法是通用的。实际上不同噪声类型直接决定方法的上限。仿真里常见的噪声可以分成几大类噪声类型频谱特点典型场景对谱减法的影响平稳噪声白噪声、粉噪声频谱长时间稳定风扇、实验室底噪前几帧即可估计噪声谱效果好非平稳噪声人群嘈杂声、键盘声频谱随时间变化咖啡厅、办公室固定噪声估计会失效残留明显有色噪声能量集中在某些频段车底噪声、空调低频需要按频带分别处理瞬时冲击声持续时间短、突刺状关门声、拍手谱减类方法基本无效白噪声只是性能测试的基线不代表实际场景。我在仿真里最推荐的做法是准备两到三种噪声一段程序生成的平稳噪声一段自己录的办公室底噪大约10秒就够一段多说话人混叠的嘈杂声。这样至少能看出算法在平稳和非平稳条件下的差距报告写起来也有说服力。1.3 噪声抑制的目标质量与可懂度不是一回事很多人调参数时只盯着一个目标听感上噪声小。但语音处理里有两个概念必须分开——语音质量quality和可懂度intelligibility。质量说的是听起来舒不舒服、背底噪声残不残留可懂度说的是听的人能听清多少个字。这两者经常是矛盾的。过度压制噪声会让背景很干净但会把轻辅音比如s、sh、f也给压掉听感上清爽了内容反而听不清。我在某次仿真对比里统计过把过减因子从1.5提到2.5输出信噪比涨了接近2dB但听写率不升反降听起来像隔着一层布在说话。所以在开始调算法之前先问自己一句这个增强结果给谁用如果是语音通信前端重点是听感自然如果是给语音识别前端重点是保住语音的发音结构尤其是高频段的摩擦音信息如果只是教学演示那只要把谱减法有音乐噪声这个现象展示清楚就够了。目标不同参数的取值方向完全不同。这一点定了之后才能谈具体方法选型。2. 三大主流方法谱减法、维纳滤波与MMSE估计的原理对比2.1 谱减法直觉最友好但音乐噪声如影随形谱减法的思想一句话就能讲完既然带噪信号语音噪声那我把噪声从幅度谱里减掉剩下的不就是语音具体做法是对带噪信号做短时傅里叶变换得到幅度谱|X|和相位谱再利用静音段估计出噪声幅度谱|N|然后做减法|Ŝ| |X| - α·|N|减完如果出现负数就截断为0或者压到某个下限。处理后的幅度谱配上原来的带噪相位做逆傅里叶变换就得到增强语音。这里有个关键设计相位直接沿用带噪信号的相位。人的听觉系统对短时傅里叶变换的相位失真相对不敏感而且如果硬要去估计干净相位误差带来的影响反而更大。仿真中第一次写谱减法先严格保持相位不处理后面再想别的花样。但谱减法的遗产问题正是从这里来的噪声幅度谱的估计有方差。每一帧、每个频点上噪声的瞬时值都在均值附近波动减去均值后那些波动大的频点就会有一些幸存者。这些幸存者是随机出现的短时谱峰听起来就是滴答滴答的音乐噪声。这是谱减法的固有属性不是代码bug后面第六节我会专门讲怎么压住它。2.2 维纳滤波从统计最优角度做增益维纳滤波是谱减法的一个升级版。它不再做减这个动作而是给每个频点乘一个增益H(k)H(k) ξ(k) / (1 ξ(k))其中ξ(k)是先验信噪比也就是该频点上语音功率和噪声功率的比值。语音强的频点增益接近1语音弱的频点增益接近0相当于一个随信噪比自动变化的自适应滤波器。维纳滤波看起来很优雅但在实际仿真里直接套这个公式效果往往不如预期原因在于我们并不知道真实的ξ。工程上有一个很有名的技巧叫决策定向先验信噪比估计decision-directedξ̂(n,k) α·(|Ŝ(n-1,k)|² / P̂d(k)) (1-α)·max(γ(n,k)-1, 0)这里γ(n,k)是后验信噪比即当前帧带噪功率与噪声功率之比。等式的意思很简单这一帧的先验信噪比一部分来自上一帧增强结果的平方一部分来自当前帧的瞬时观测用α通常取0.98做加权平均。这个平滑处理对压音乐噪声非常有效是维纳滤波能超过谱减法的关键一行代码。我在仿真课里经常跟学生说先别急着写MMSE先把维纳滤波配决策定向跑明白。因为其中上一帧结果回灌到当前帧估计的递归思路理解透了之后MMSE也只是把增益函数换得更精细而已。2.3 MMSE-STSA把先验信噪比用到极致MMSE-STSA的全称是最小均方误差短时谱幅度估计。它优化的目标不是谱减那样的直接差值最小而是让增强后的语音幅度谱在均方误差意义上最接近干净语音的幅度谱。求解结果也是一个增益函数但形式上复杂得多涉及零阶和一阶修正贝塞尔函数G ≈ (√π·ν / 2γ) · exp(-ν/2) · [(1ν)·I0(ν/2) ν·I1(ν/2)]其中ν由先验信噪比和后验信噪比共同决定。这个公式在仿真里没有必要手推你只需要理解三件事第一它用了更精确的统计模型所以在低信噪比下比谱减法更稳第二它的增益天然带有平滑性音乐噪声比谱减法少很多第三它的计算代价比谱减法高一个量级实时实现要算查表。如果不想自己实现贝塞尔函数也可以用现成的音频增强库里的MMSE函数作为对比基准至少能把改进空间量化出来。2.4 三种方法的仿真适用场景对比方法实现复杂度音乐噪声适合场景推荐起步参数谱减法低几十行代码明显快速原型、教学演示α1.5β0.01维纳滤波中需要递归估计较低实时通信前端α0.98MMSE-STSA高涉及贝塞尔函数低离线高质量增强直接调用现成实现我的建议是课程仿真以谱减法为主体认真写完这是理解噪声抑制的钥匙然后至少把维纳滤波跑出来做对比因为它的增益平滑思路是后面一切进阶方法的地基。MMSE可以只做结果分析不用在实现上死磕。3. 仿真环境准备数据、分帧与噪声估计的细节3.1 数据准备用什么样的语音和噪声先说语音。仿真不需要很长的素材一段5到10秒的安静录音足够。要求是单声道、16kHz采样率、16比特量化。自己录也行但环境一定要安静否则干净语音本身就有噪后面评估时所有指标都是歪的。如果从公开语音库选取也务必确认文件是不是已经过了预处理——有的音频本身带混响会干扰噪声抑制效果归因。再说噪声。平稳噪声可以直接用随机数生成白噪声就是np.random.randn粉噪声可以用白噪声做低通滤波近似得到。非平稳噪声建议自己准备一段把两三个人的语音叠在一起或者录一段办公室日常声音。混合时注意噪声和语音尽量保持相同的采样率和格式否则要先重采样再混合。实操里有一个我踩过的坑wav文件读进来可能是整型int16计算功率时没有转成浮点结果信噪比怎么算都不对。正确的流程是先归一化到[-1,1]的范围再运算sr, data wavfile.read(clean.wav) clean data.astype(np.float32) / 32768.0最后写回文件时再放大回去enhanced.astype(np.int16)这一来一回的精度损失虽然不大但每次都要检查不然会凭空多出一些高频毛刺。3.2 分帧加窗与短时傅里叶变换的参数选择语音是非平稳信号但可以假设在20到40毫秒的短时窗内是平稳的所以仿真里必须先分帧。常用参数是窗长25ms、帧移10ms在16kHz采样率下对应窗长400点、帧移160点。这两个值不是随便定的25ms的窗既有足够频率分辨率看清基频和谐波又有较好的时间分辨率跟随语音节奏变化帧移10ms通常足够密重建时不容易出现空洞。窗函数建议用汉明窗。原因有两个一是主瓣宽度和旁瓣衰减的折中比较好频谱泄漏少二是汉明窗的重叠相加重建特性很友好窗口平方和后能达到一个比较平稳的值逆变换时除以窗平方和不会放大噪声。FFT点数建议取512。窗长400点补零到512点频率间隔从40Hz变成31.25Hz。注意补零并不会提高真实频率分辨率它只是把谱线插值得更密让观察谱形更平滑。分辨率是由窗长决定的这一点在写报告时容易被误解顺手澄清一下。参数之间的取舍要心里有数窗长越长频率分辨率越好但时间上越糊瞬态变化比如爆破音会被磨平窗长越短跟踪噪声变化越灵敏但每个频点上的谱估计方差越大音乐噪声越重。25ms就是在两者之间一个比较实用的平衡点。3.3 噪声功率谱估计前几帧静音段的正确用法谱减法这类方法的前提是噪声是慢变的可以用语音间隙的样本估计出噪声谱。仿真中最常见的做法是取带噪语音的前几帧做平均。noise_frames 10 noise_pow np.mean(X[:noise_frames]**2, axis0) noise_mag np.sqrt(noise_pow)这里有个默认前提前几帧里没有语音。很多公开语音素材开头都有0.1到0.3秒的静音所以取前5到10帧是安全的。但如果素材开头直接是人声这个估计就会偏高把语音的起始部分也当成噪声减掉了造成开头音量骤降。遇到这种情况一个简单的处理是先做一个能量门限计算每个短时帧的幅度均值把幅度最小的那几帧当作噪声。更讲究一点可以用最短统计方法——在一段滑动窗口内取各频点的最小值作为噪声估计。这个思路对非平稳噪声依然有效因为它假设真正的语音活动不会长时间占据所有频点噪声总会在某些时刻暴露出来。还有一个小技巧噪声功率谱估计本身也要做时间平滑。比如每来一帧就更新一次noise_pow 0.8·old 0.2·new这样噪声谱不会突然跳变后面的减法结果就稳定很多。4. 手写一个谱减法降噪的完整仿真流程4.1 代码骨架与核心逻辑终于到动手环节。下面这段代码是我在仿真课上给学生的标准模板只依赖numpy、scipy没有任何花哨操作目的在于把谱减法的五个步骤讲清楚加噪、分帧加窗、FFT、谱减、重建。import numpy as np from scipy.io import wavfile from scipy.fft import rfft, irfft def add_noise(clean, snr_db, seed0): rng np.random.default_rng(seed) noise rng.standard_normal(len(clean)) p_sig np.mean(clean**2) p_noi np.mean(noise**2) noise noise * np.sqrt(p_sig / (10**(snr_db/10) * p_noi)) return clean noise, noise def spectral_subtraction(x, sr, alpha1.5, beta0.01, win_len400, hop160, n_fft512): window np.hamming(win_len) n_frames 1 (len(x) - win_len) // hop mag np.zeros((n_frames, n_fft//2 1)) phase np.zeros_like(mag) for i in range(n_frames): seg x[i*hop : i*hopwin_len] * window spec rfft(seg, n_fft) mag[i] np.abs(spec) phase[i] np.angle(spec) noise_mag np.mean(mag[:10], axis0) mag_clean mag - alpha * noise_mag floor beta * np.mean(mag, axis0) mag_clean np.maximum(mag_clean, floor) out np.zeros(len(x)) win_sum np.zeros(len(x)) for i in range(n_frames): spec_clean mag_clean[i] * np.exp(1j * phase[i]) seg irfft(spec_clean, n_fft)[:win_len] out[i*hop : i*hopwin_len] seg * window win_sum[i*hop : i*hopwin_len] window**2 out out / win_sum return out sr, data wavfile.read(clean.wav) clean data.astype(np.float32) / 32768.0 x_noisy, true_noise add_noise(clean, snr_db5) y spectral_subtraction(x_noisy, sr) wavfile.write(noisy.wav, sr, (x_noisy*32767).astype(np.int16)) wavfile.write(enhanced.wav, sr, (y*32767).astype(np.int16))几个容易写错的地方我直接说在前面。rfft的输出长度是n_fft//21逆变换irfft只能吃这个长度的复数谱别传错了。重建时除以的是win_sum而不是frame数因为汉明窗的重叠区域会有能量叠加不除会导致每帧重叠的地方音量忽高忽低。最后是去边缘效应首尾几帧因为窗口覆盖不全重建后会有轻微失真真实系统会用更复杂的分析窗处理教学仿真里忽略也没关系。4.2 关键参数的位置与物理含义这段代码里值得手工调节的参数就那么几个我整理成一张表方便调试的时候对号入座。参数代码位置常用范围物理含义调参方向α过减因子alpha1.0~2.0噪声去除强度增大则噪声小但语音失真β谱下限系数beta0.0005~0.05防止幅度减成负数增大则音乐噪声少、底噪高噪声估计帧数mag[:10]3~10噪声谱平滑程度太少则估计抖动大窗长win_len320~480频率-时间分辨率折中16kHz下400点是基准帧移hopwin_len/2帧间重叠程度必须小于窗长否则重建有洞为什么β不能等于0只要减出负数就截断为0那么该频点这一帧就没信息了重建出来的信号会有一堆死点听感像砂纸。设一个小的谱下限相当于把零抬到一个非常小的地板既保留了该频点的时间连续性又能掩盖残余噪声的起伏是压音乐噪声最便宜的手段。α的取值范围也和输入信噪比有关。输入信噪比低时为了把噪声压下去α需要大一些输入信噪比高时α要调小否则把语音自己的弱音能量也减掉了。第5节我会给一组经验值。4.3 输出对比时域波形与语谱图怎么看跑完代码先别急着听声音先画三张图import matplotlib.pyplot as plt plt.figure(figsize(10, 8)) for i, (sig, name) in enumerate([(clean, clean), (x_noisy, noisy), (y, enhanced)]): plt.subplot(3, 1, i1) plt.specgram(sig, Fssr, NFFT400, noverlap240) plt.title(name) plt.tight_layout() plt.savefig(compare.png, dpi150)看语谱图有几个重点。第一看整体的底噪颜色有没有变淡尤其在2kHz以上的高频区域白噪声的能量应该被明显压低。第二看语音的横条纹共振峰还在不在如果在低频这一段出现了大面积的空白说明语音被削过头了。第三看有没有随机分布的孤立亮点亮点越密集说明音乐噪声越严重。波形图主要看包络。干净语音的波形包络是一段一段的对应音节增强后的包络应该基本保持这个节奏如果出现大量不规则的细碎抖动说明每帧之间的增益变化太剧烈需要做增益平滑。5. 效果评估从客观指标到语谱图检查5.1 客观指标SNR、分段SNR与感知评估仿真报告里不能只写听起来不错必须上指标。最基础的指标是整体信噪比提升量def overall_snr(clean, err): return 10*np.log10(np.mean(clean**2) / np.mean(err**2) 1e-12) snr_in overall_snr(clean, x_noisy - clean) snr_out overall_snr(clean, y - clean) print(finput SNR: {snr_in:.2f} dB, output SNR: {snr_out:.2f} dB)但整体SNR有一个坑它把所有帧等权平均了语音帧功率大、噪声帧功率小于是最后结果主要被语音帧主导残留噪声反而容易被掩盖。所以工程上更常用分段信噪比segmental SNR也就是把信号切成20到30毫秒的帧逐帧算SNR再取平均。计算时要丢掉静音帧因为这些帧的误差全是残留噪声会无限拉低分数反而不符合人耳对静音段噪声没那么敏感的事实。def seg_snr(clean, enhanced, frame320, hop160): n_frames 1 (len(clean)-frame)//hop scores [] for i in range(n_frames): s clean[i*hop:i*hopframe] e enhanced[i*hop:i*hopframe] - s if np.mean(s**2) 1e-6: scores.append(10*np.log10(np.mean(s**2)/np.mean(e**2)1e-12)) return np.mean(scores)严格的项目会用感知语音质量评估算法PESQ这类标准化工具。它以干净语音为参考预测带噪语音或增强语音的听感分数范围-0.5到4.5。这类工具在某些环境里不是开箱即用课程仿真如果没有条件就先用分段SNR加语谱图说明问题分数差距足够支撑结论。5.2 主观听感与语谱图检查指标只能说明整体趋势最终判断还是要靠耳朵和眼睛。我的习惯是戴耳机听三个文件原始干净语音、带噪语音、增强语音。重点听两类东西一是背景底噪是否从沙沙声变成了湿漉漉的水泡声如果是后者说明音乐噪声压不住二是辅音细节找几个带s、f、sh的词如果这些摩擦音明显变弱甚至消失说明高频被误杀要把α降下来。语谱图检查的维度和听感是互补的。客观指标优秀但主观听感差的例子很常见——比如输出SNR涨了3dB但语音听起来像机器人在说话。这时候要看增强语音的语谱图里是不是多了很多不自然的横纹通常是增益曲线不平滑造成的。判断增强是否合格我一般按这个顺序先看语音结构保留度再看底噪抑制量最后才看音乐噪声是否影响听感。5.3 不同信噪比下的表现对比噪声抑制算法在不同输入信噪比下的表现差异极大这一块是仿真报告里最能体现工作量地方。我给个经验值供参考输入信噪比谱减法大体表现参数建议0 dB噪声明显降低但失真严重α2.0β0.02注意听辅音5 dB平衡性较好适合作为基准测试点α1.5β0.0110~15 dB降噪空间有限处理不当反而损伤语音α1.2以下β0.00520 dB基本不需要降噪可以不处理或只做极轻的增益一个合格的仿真评测至少要做三个输入信噪比比如0、5、10dB每个点都记录输出分段SNR和听感描述。只测一个点说明不了算法的稳定性因为谱减法最怕的就是高分贝下过度处理。6. 仿真中最常踩的坑音乐噪声、过衰减与参数调节6.1 音乐噪声的来源与抑制音乐噪声的本质我在第2节提过噪声功率谱的估计值有方差经过减法之后那些波动比较大、瞬时值超过均值的频点被保留成一个个随机谱峰。这些谱峰持续时间短、频率随机人耳听起来就是水滴声或鸟叫声。它跟噪声估计准不准没直接关系就算你前10帧估计得非常好每一帧本身的统计波动依然存在。压音乐噪声有几条路。最简单的是抬高谱下限β直接把那些微小的残余峰全部压到地板以下代价是底噪变高一点。第二条路是降低过减因子α减法没有那么狠残余峰不会那么突出但整体噪声也降得少。第三条路是增益平滑——把每一帧算出的增益G(n,k)做时间上的指数平滑G np.maximum(1 - alpha * noise_mag[i] / mag[i], 0) if i 0: G_smooth G else: G_smooth 0.8 * G 0.2 * G_prev这个0.8和0.2说明当前帧增益80%来自历史20%来自当前观测相当于给增益曲线上保险防止突变。实际调试时往往是三条路组合使用β设一个不过分的地板α取中等值再叠加一层平滑。组合拳打下来音乐噪声基本能压到不影响听感的程度。6.2 语音过衰减与断续感另一个高频问题是断续感或罐子声。现象是语音听起来缺少自然连贯性像每隔几十毫秒被剪断一次。根因在于谱减法对所有帧、所有频点一视同仁静音段和语音段用同一套减法参数语音弱的频段和噪声主导的频段也共用同一个α。结果就是在语音能量较低的过渡段幅度被减到地板以下音节之间的连续能量被切断听感就一卡一卡。解决思路是让参数随频率和随帧自适应。最简单的自适应是按频带区分α因为多数环境噪声在高频段更突出而语音的基频和低次谐波集中在低频所以可以为高频设更大的α低频设更小的αfreq_axis np.linspace(0, sr/2, n_fft//21) alpha_f np.where(freq_axis 1000, 2.0, 1.2) G np.maximum(1 - alpha_f * noise_mag[i] / mag[i], 0)另一个更通用的办法是引入语音存在概率估计当前帧像语音的程度语音概率高时把增益往上提少抑制语音概率低时把增益往下压多抑制。这个概念在维纳滤波和MMSE里都是标配如果你从谱减法往维纳滤波迁移这段思路完全可以直接复用。6.3 参数调节的着手点很多同学拿到算法第一反应是调α但我的经验是参数调节有顺序调错顺序会陷入越调越乱的循环。推荐流程先固定噪声估计模块。确认前几帧取的确实是静音段噪声谱平滑且稳定。再设β。先用一个偏大的值0.02保证任何情况下都不会出现负数幅度把音乐噪声压住。然后调α。从1.5起步听增强语音噪声还明显就加到2.0语音发闷就降到1.2。如果还有零星音乐噪声不要急着继续加大β先尝试给增益加时间平滑平滑通常能保留更多语音细节。最后才用指标验收。输出SNR提升是一个参考但一定要配合辅音可懂度检查。最后分享一个我自己的习惯每次调参都用固定随机种子把参数和对应的分段SNR、听感描述记成一张表。比如α1.5时噪声残余是什么样、α2.0时语音损失是什么样都写清楚。这样过几天回来看还能快速定位问题写仿真报告时这张表本身就是最好的实验记录素材。噪声抑制这门手艺参数记下来了才是自己的光靠耳朵当时觉得好第二天就忘了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑