3、语音信号预处理:预加重、分帧、加窗的原理与实现

语音信号在进入MFCC特征提取流水线之前,必须经过一系列预处理操作。原始语音波形是连续时域信号,且其频谱能量分布极不均衡——高频段能量通常远低于低频段,同时信号本身具有非平稳特性。若直接对整段语音进行傅里叶变换,将无法捕捉语音的短时动态特征。因此,预加重、分帧、加窗构成了MFCC提取的第一道工序,其质量直接决定了后续滤波器组能量计算的准确性。本章将深入剖析这三项操作的数学原理、工程实现方法及参数选择依据,并通过一个完整的案例演示预处理流程对特征结果的影响。

语音信号预处理链路与信号变化 原始语音 s(n) 采样率 16kHz, 16bit 频谱: 高频能量衰减 预加重 y(n) y(n)=s(n)-α·s(n-1) α=0.97, 高频提升约20dB 分帧 x_m(n) 帧长25ms=400点 帧移10ms=160点 加窗 w(n) 汉明窗 w(n)=0.54-0.46cos 频谱对比 原始频谱(高频衰减) 预加重后(高频提升) 分帧与加窗 帧1 帧2 帧3 汉明窗包络 帧移160点,帧长400点 输出帧 x_m(n)·w(n) → FFT 典型参数配置(16kHz采样率) 预加重系数 α = 0.97 帧长 = 25ms (400点) 帧移 = 10ms (160点) 窗函数 = 汉明窗 FFT点数 = 512 (补零) 帧数 ≈ 总时长/帧移 + 1

上图展示了语音信号从原始波形到可进行FFT分析的短时平稳帧的完整转换链路。预加重环节通过一阶高通滤波器补偿语音信号的高频衰减;分帧操作将长时非平稳信号切分为25ms左右的短时片段,近似满足平稳性假设;加窗则进一步削弱帧边界处的频谱泄漏。三者协同工作,为后续MFCC特征提取提供了符合频域分析要求的输入信号。

3.1 预加重:补偿高频能量衰减

核心原理

语音信号在发声过程中,声门激励的频谱特性呈现约-6dB/倍频程的下降趋势,即频率每升高一倍,能量衰减约6dB。同时,口唇辐射效应也会引入高频衰减。这种天然的高频衰减导致语音信号的高频段(如清音、摩擦音所在的2-8kHz区域)能量远低于低频段(如元音基频及其谐波所在的0-1kHz区域)。若不进行补偿,后续MFCC提取时,高频段的滤波器组能量将非常微弱,导致特征向量对高频语音信息不敏感。

预加重采用一阶FIR高通滤波器实现,其差分方程为:

y(n) = s(n) - α · s(n-1)

其中,s(n)为原始语音采样点,y(n)为预加重后的输出,α为预加重系数(通常取值0.95~0.99,标准值为0.97)。该滤波器的频率响应为:

H(e^jω) = 1 - α · e^(-jω)
幅度响应: |H(e^jω)| = √(1 + α² - 2α·cosω)

当α=0.97时,在8kHz频率点(对应数字角频率ω=π)处,增益约为1.97倍(约5.9dB),而在低频段(如100Hz)增益接近0.03(约-30dB),从而实现了约36dB的高频相对提升。

工程实现要点:预加重滤波器在Python中可通过 scipy.signal.lfilter([1, -α], [1], s) 实现,也可使用 numpy.convolve(s, [1, -α], mode='same')。注意边界处理:第一个采样点s(0)没有s(-1),通常直接令y(0)=s(0)或补零处理。

判断方法与参数选择

预加重系数的选择需要根据实际语音特性进行调整。判断标准如下:

  • 观察频谱斜率:对预加重前后的语音分别计算功率谱,若预加重后频谱在2-4kHz范围内趋于平坦,则系数合适;若仍明显下降,可适当增大α至0.98~0.99。
  • 信噪比考量:若语音中高频噪声较大(如环境风声、电路噪声),过大的α会放大噪声,此时应降低α至0.95~0.96。
  • 采样率适配:对于8kHz采样率的电话语音,α通常取0.95;对于16kHz宽带语音,α取0.97;对于44.1kHz高保真语音,α可适当增大至0.98。
采样率推荐α值适用场景高频提升量(4kHz)
8kHz0.95电话语音、窄带通信约4.5dB
16kHz0.97标准语音识别、会议系统约5.9dB
44.1kHz0.98音乐分析、高保真录音约6.8dB

案例拆解:预加重对清音识别的改善

某语音识别系统在安静办公室环境下采集了500条中文命令词,其中包含大量以清音开头的词汇(如"请"、"谢谢"、"测试")。初始系统未启用预加重,识别准确率为91.2%。分析发现,错误主要集中在清音起始的词汇上,其高频段(3-6kHz)能量极低,导致MFCC特征中高频系数(如第8-13维)几乎为零。

实施预加重后(α=0.97),清音段的高频能量得到约6dB的提升,MFCC高频维度的方差显著增大。重新训练模型后,识别准确率提升至94.8%,其中清音起始词汇的错误率降低了42%。代价是低频段(0-500Hz)的相对能量略有下降,但通过后续滤波器组的归一化处理,这一影响被有效抵消。

经验提醒:预加重并非越大越好。当α超过0.99时,滤波器接近微分器,会过度放大高频噪声,导致特征向量中高频维度被噪声主导。建议在安静环境下使用0.97,在噪声环境下适当降低至0.95。

3.2 分帧:将非平稳信号切分为短时平稳片段

核心原理

语音信号是典型的非平稳信号,其统计特性(如均值、方差、频谱)随时间快速变化。然而,在10-30ms的短时间内,语音的声学特性可视为近似不变——声带振动周期(约5-15ms)和声道形状变化速率(约10-20ms)决定了这一时间尺度内信号具有准平稳性。分帧操作正是利用这一特性,将长时语音信号切分为一系列短时片段,每个片段可独立进行傅里叶分析。

分帧涉及两个关键参数:

  • 帧长(Frame Length):每个帧包含的采样点数。典型值为20-30ms。对于16kHz采样率,25ms对应400个采样点。帧长过短(<10ms)会导致频率分辨率不足,无法区分相近的谐波;帧长过长(>50ms)则破坏平稳性假设,引入频谱模糊。
  • 帧移(Frame Shift):相邻两帧起始点之间的间隔。典型值为5-15ms。帧移决定了帧之间的重叠程度,重叠率通常为50%-75%。帧移过大会丢失语音动态变化信息,帧移过小则产生大量冗余计算。

设总采样点数为N,帧长为L,帧移为S,则总帧数M的计算公式为:

M = floor((N - L) / S) + 1

例如,一段2秒的语音(N=32000),帧长L=400,帧移S=160,则M = floor((32000-400)/160) + 1 = 198帧。

操作流程与边界处理

  1. 确定参数:根据采样率和应用场景选择帧长与帧移。语音识别常用25ms/10ms,语音合成常用20ms/5ms。
  2. 计算帧数:按上述公式计算总帧数。
  3. 提取帧数据:第m帧的采样点范围为 [m·S, m·S + L - 1]。
  4. 边界处理:若最后一帧超出信号长度,可采用补零(Zero-padding)或丢弃该帧。补零会引入人为不连续,但影响较小;丢弃则可能丢失尾部信息。
常见误区:部分初学者将分帧理解为"切割"——即帧与帧之间完全独立、无重叠。实际上,标准分帧必须包含重叠区域(通常50%以上),否则帧与帧之间的过渡会丢失语音的动态变化信息,导致MFCC特征在时间轴上出现不自然的跳变。

案例拆解:帧长选择对共振峰分辨率的影响

在元音/a/的频谱分析中,前三个共振峰(F1≈800Hz, F2≈1200Hz, F3≈2500Hz)是识别元音的关键特征。当帧长为10ms(160点)时,FFT频率分辨率为100Hz(16000/160),F1和F2之间的400Hz间隔尚可分辨,但F2和F3之间的1300Hz间隔则被严重平滑。当帧长增加至25ms(400点)时,频率分辨率提升至40Hz,三个共振峰清晰可辨。

然而,帧长增加也带来了时间分辨率下降的问题。在快速语流中,辅音到元音的过渡段仅持续20-30ms,若帧长超过30ms,过渡段的动态特征将被平均化,影响声学模型的区分能力。因此,实际系统需要在频率分辨率和时间分辨率之间取得平衡,25ms/10ms是经过大量实验验证的折中方案。

3.3 加窗:抑制频谱泄漏

核心原理

分帧操作本质上是将无限长语音信号与一个矩形窗相乘,即截取一段有限长度信号。矩形窗在时域上表现为突然截断,在频域上对应主瓣窄但旁瓣高的sinc函数(第一旁瓣仅比主瓣低13.3dB)。这种旁瓣泄漏会导致频谱能量从主瓣扩散到旁瓣,产生"频谱泄漏"现象——即本应集中在某个频率的能量扩散到邻近频率,造成频谱模糊。

加窗操作是在分帧之后,将每一帧信号与一个窗函数相乘,使帧边缘的幅度平滑过渡到零,从而抑制频谱泄漏。常用的窗函数包括:

  • 汉明窗(Hamming):w(n) = 0.54 - 0.46·cos(2πn/(L-1)),0 ≤ n ≤ L-1。主瓣宽度适中,第一旁瓣衰减约43dB,是语音处理最常用的窗函数。
  • 汉宁窗(Hann):w(n) = 0.5 - 0.5·cos(2πn/(L-1))。主瓣稍宽,旁瓣衰减更快(约32dB),适合需要更平滑频谱的场景。
  • 布莱克曼窗(Blackman):w(n) = 0.42 - 0.5·cos(2πn/(L-1)) + 0.08·cos(4πn/(L-1))。旁瓣衰减最大(约58dB),但主瓣最宽,频率分辨率最低。
窗类型主瓣宽度第一旁瓣衰减频率分辨率适用场景
矩形窗2π/L-13.3dB最高瞬态分析(不推荐用于语音)
汉宁窗4π/L-31.5dB较高通用频谱分析
汉明窗4π/L-42.7dB较高语音识别、MFCC提取
布莱克曼窗6π/L-58.1dB较低高动态范围频谱分析

实现方法与参数验证

在Python中,加窗操作可通过以下代码实现:

import numpy as np

def pre_emphasis(signal, alpha=0.97):
    """预加重"""
    return np.append(signal[0], signal[1:] - alpha * signal[:-1])

def framing(signal, frame_len=400, frame_shift=160):
    """分帧"""
    num_frames = (len(signal) - frame_len) // frame_shift + 1
    frames = np.zeros((num_frames, frame_len))
    for i in range(num_frames):
        start = i * frame_shift
        frames[i] = signal[start:start + frame_len]
    return frames

def windowing(frames, win_type='hamming'):
    """加窗"""
    frame_len = frames.shape[1]
    if win_type == 'hamming':
        win = np.hamming(frame_len)
    elif win_type == 'hann':
        win = np.hanning(frame_len)
    elif win_type == 'blackman':
        win = np.blackman(frame_len)
    else:
        win = np.ones(frame_len)  # 矩形窗
    return frames * win

# 完整预处理流程
def preprocess(signal, fs=16000):
    # 1. 预加重
    emphasized = pre_emphasis(signal, alpha=0.97)
    # 2. 分帧
    frames = framing(emphasized, frame_len=int(0.025*fs), frame_shift=int(0.010*fs))
    # 3. 加窗
    windowed_frames = windowing(frames, win_type='hamming')
    return windowed_frames

验证加窗效果的方法:对加窗前后的帧分别计算FFT幅度谱,观察频谱泄漏情况。加窗后,频谱中不应出现明显的"裙边"(即主瓣两侧的周期性波纹),且各频率分量之间的间隔应更加清晰。

常见误区与总结复盘

误区一:认为加窗会"丢失"帧边缘的语音信息。实际上,由于帧移小于帧长,帧边缘的信息会在相邻帧中完整保留,加窗只是降低了当前帧边缘的权重,并未真正丢弃信息。
误区二:混淆窗函数与FFT点数。窗函数作用于时域信号,FFT点数决定频域采样密度。即使窗函数长度为400点,FFT点数也可以设置为512(补零)或1024,以获得更精细的频谱采样。
总结复盘:预加重、分帧、加窗三者构成一个完整的预处理链路,缺一不可。预加重解决频域能量不均衡问题,分帧解决信号非平稳问题,加窗解决频谱泄漏问题。在实际工程中,建议将三者封装为一个统一的预处理函数,并针对不同采样率和应用场景提供参数配置接口。预处理质量可通过以下指标评估:①预加重后频谱平坦度;②帧间特征连续性(相邻帧MFCC的欧氏距离不应突变);③加窗后频谱旁瓣衰减程度。