跳转至

专业

信号

傅里叶

时域和频域

时域是指对信号在时间上的变化进行描述, 也就是说, 时域分析关注的是信号随着时间的推移如何变化. 时域分析主要分析信号在不同时间点的幅度和形状, 关注的是信号随着时间的变化.

频域是通过傅里叶变换等方法将信号从时域转为频率的角度来描述. 他关注的是信号包含了哪些频率成分以及他们的强度(幅度)和相位. 在频域中, 我们将信号分解为不同频率成分, 分析这些频率成分的强度和相位, 揭示信号的频谱特性.

时域和频域的关系:

  • 时域的局部性vs.频域的全局性: 时域主要关注信号的局部变化(例如某一时刻的值), 而频域则关注信号的全局组成(即信号在各个频率上的分布)
  • 傅里叶变换: 时域和频域之间可以通过傅里叶变换或者离散傅里叶变换互相转换. 傅里叶变换可以将一个时域信号分解为不同频率的正弦波的和.
  • 应用的差异: 在时域中, 分析主要是关注信号的形状和波动, 例如音频信号的强弱, 图像的亮度变化; 在频域中, 分析主要关注信号的频率成分. 例如, 通过频域分析, 可以识别出音频信号中的噪声频率, 从而进行滤波消除.

傅里叶级数

傅里叶级数告诉我们, 任何一个周期函数都可以表示为一系列正余弦函数的叠加. 如果函数的周期是\(T\), 那么傅里叶级数的基函数是\(\sin(\frac{2\pi n}{T} x)\)\(\cos(\frac{2\pi n}{T} x)\), 其中\(n\)是整数. 为啥能够这么表示, 因为这些正余弦函数之间互相正交, 他们两两之间的内积是0(可以想象一下坐标系, 我们用两个单位向量来表示任意一个向量, 我们要求这个单位向量必须是正交的, 这里也是类似的道理). 什么是正余弦的内积? 就是把两个函数在一个周期内的乘积积分.

例如, 周期为\(2\pi\)的函数可以写成\(f(x) = a_0 + \sum\limits_{n=1}^{\infty} (a_n \cos(nx) + b_n \sin(nx))\), 在普通的线性代数里面, 一个向量可以写成\(v=c_1e_1+c_2e_2+c_3e_3\), 傅里叶级数做的是同样的事情, 只不过: 向量->函数; 坐标轴->正余弦函数.

他给出了一个完全不同的看待函数的视角, 那就是将各个不同振动频率所对应的能量的大小, 每一个正余弦波函数前面的系数, 就代表这个正余弦周期的振动有多少显著. 将这些振动强度的系数一一列出, 我们就可以还原出原来的函数情况. 这些系数有一个专业的名词, 叫做频谱, 频率周期大的基底对应于低频区, 频率周期小的基底对应于高频区. 这个频谱就是对这个特定周期的音色对精确的定义.

如何求解\(a_n\)\(b_n\)呢? 假设\(f(x)\)的周期为\(2\pi\), 这个和直角坐标系下的思路是类似的, 直角坐标系下, 我们求向量在某个基方向上的投影. 我们要求的是函数\(f(x)\)在基函数\(\cos(nx), \sin(nx)\)方向上的分解. 现在求\(a_n\)为例, 把展开式的两边乘上\(\cos(nx)\), 然后再在区间\([-\pi, \pi]\)上积分: \(\int_{-\pi}^{\pi} f(x) \cos(nx) dx\), 右边展开之后, 会得到很多积分, 由于正交性, \(k\neq n\)时积分为\(0\), 只剩下\(a_n \int_{-\pi}^{\pi} \cos^2(nx) dx\), 而\(\int_{-\pi}^{\pi} \cos^2(nx) dx = \pi\), 于是得到\(a_n = \frac{1}{\pi} \int_{-\pi}^{\pi} f(x) \cos(nx) dx\). 同理, 求得\(b_n = \frac{1}{\pi} \int_{-\pi}^{\pi} f(x) \sin(nx) dx\).

傅里叶变换

傅里叶变换其实是把这个思想从周期函数推广到任意函数. 傅里叶级数只适用于周期函数, 如果函数不是周期的, 就没有固定周期\(T\). 我们可以做一个思想实验, 假设我们把一个非周期函数强行看作是一个周期函数, 周期取一个非常大的\(T\), 那么傅里叶级数的基函数是\(\sin(\frac{2\pi n}{T}x), \cos(\frac{2\pi n}{T}x)\), 对应的频率是\(\frac{n}{T}\), 当\(T\)越来越大的时候, 会发生两个变化, 第一, 频率之间的间隔越来越小, \(\Delta f=\frac{1}{T}\rightarrow 0\), 原来是离散的频率, 逐渐变为连续的频率. 第二, 原来的傅里叶级数里面的求和, 在极限情况下变为一个积分.

根据欧拉公式 \(e^{ix}=\cos x+i\sin x\),我们可以使用 \(e^{ix}\) 统一表示正弦和余弦,其中 \(\sin x=\frac{e^{ix}-e^{-ix}}{2i}\),\(\cos x=\frac{e^{ix}+e^{-ix}}{2}\).对于周期为 \(T\) 的函数,傅里叶级数中的基函数需要具有相同周期,因此频率变为 \(\frac{2\pi n}{T}\).将上述关系代入 \(a_n\cos\!\left(\frac{2\pi n}{T}x\right)+b_n\sin\!\left(\frac{2\pi n}{T}x\right)\),得到 \(a_n\left(\frac{e^{i\frac{2\pi n}{T}x}+e^{-i\frac{2\pi n}{T}x}}{2}\right)+b_n\left(\frac{e^{i\frac{2\pi n}{T}x}-e^{-i\frac{2\pi n}{T}x}}{2i}\right)\).整理 \(e^{i\frac{2\pi n}{T}x}\)\(e^{-i\frac{2\pi n}{T}x}\) 的系数,可得 \(\left(\frac{a_n}{2}+\frac{b_n}{2i}\right)e^{i\frac{2\pi n}{T}x}+\left(\frac{a_n}{2}-\frac{b_n}{2i}\right)e^{-i\frac{2\pi n}{T}x}\).定义复系数 \(c_n=\frac{a_n}{2}-\frac{ib_n}{2}\),\(c_{-n}=\frac{a_n}{2}+\frac{ib_n}{2}\),于是有 \(a_n\cos\!\left(\frac{2\pi n}{T}x\right)+b_n\sin\!\left(\frac{2\pi n}{T}x\right)=c_n e^{i\frac{2\pi n}{T}x}+c_{-n} e^{-i\frac{2\pi n}{T}x}\).如果把所有正负 \(n\) 一起写,就得到周期为 \(T\) 的傅里叶级数复指数形式 \(f(x)=\sum_{n=-\infty}^{\infty}c_n e^{i\frac{2\pi n}{T}x}\),其中复系数可以写为 \(c_n=\frac{1}{T}\int_{-T/2}^{T/2}f(x)e^{-i\frac{2\pi n}{T}x}\,dx\),而当 \(n=0\)\(c_0\) 对应原来的常数项 \(a_0\).

定义\(\omega_n = \frac{2\pi n}{T}\), 相邻频率的间隔是\(\Delta \omega = \frac{2\pi}{T}\), 改写\(c_n = \frac{1}{2\pi} \left( \int_{-T/2}^{T/2} f(x) e^{-i\omega_n x} dx \right) \Delta\omega\), 定义\(F(\omega_n) = \int_{-T/2}^{T/2} f(x) e^{-i\omega_n x} dx\), 于是有\(f(x) = \frac{1}{2\pi} \sum_{n=-\infty}^{\infty} F(\omega_n) e^{i\omega_n x} \Delta\omega\). 如果函数不是周期函数, 我们可以想象\(T\rightarrow \infty\). 这个时候, \(\Delta \omega\)趋近于0, 于是求和极限变为求积分, \(\sum_{n}(\cdots)\Delta\omega \quad \rightarrow \quad \int_{-\infty}^{\infty}(\cdots)d\omega\), 于是得到\(f(x) = \frac{1}{2\pi} \int_{-\infty}^{\infty} F(\omega) e^{i\omega x} d\omega\). 反过来是\(F(\omega) = \int_{-\infty}^{\infty} f(x) e^{-i\omega x} dx\). 每个频率的系数就是\(F(\omega)\).

离散傅里叶变换

离散傅里叶变换, DFT, Discrete Fourier Transform是一种数学变换, 用于将离散的时间信号(通常是数字信号)从时域转为频域. 为啥我们要考虑离散呢? 因为我们处理的是数字信号, 我们需要处理的是离散的时间信号. DFT的主要作用和FT是一样的, 将一个信号表示为若干个频率成分的组合, 帮助我们分析信号的频谱特性.

连续傅里叶变换是\(F(\omega)=\int_{-\infty}^{\infty}f(x)e^{-i\omega x}\,dx\), 现实中计算机无法处理连续函数, 所以第一步是采样. 假设以采样间隔\(\Delta x\)采样, \(x_n=n\Delta x,\quad n=0,1,\dots,N-1\). 得到离散序列\(f_n = f(x_n)\), 此时的积分可以近似为黎曼和, \(F(\omega)\approx \sum_{n=0}^{N-1} f_n e^{-i\omega x_n}\,\Delta x\).

Now, 因为这个时候我们只有\(N\)个数据, 若我们尝试用很多频率表示信号, 则未知数(分量的个数)>方程数(数据的个数), 这样解就不唯一, 甚至会有无穷多种解, 因此最多只能有\(N\)个独立的系数, 即最多只能确定\(N\)个频率分量, 所以频率必须取一组离散的值. 若未知数<方程数, 那么可能存在某些向量\(f\)无法表示为小于\(N\)个基函数构成的线性组合, 所以基函数的数量必须是\(N\). 在长度为\(N\Delta x\)的区间上, 我们的基函数的周期为\(T=N\Delta x\), 所对应的频率是\(\omega_k=\frac{2\pi k}{T},\quad k=0,1,\dots,N-1\). 于是\(F(\omega_k)=\sum_{n=0}^{N-1} f_n e^{-i\omega_k x_n}\,\Delta x\). 把\(x_n=n\Delta x, \omega_k=\frac{2\pi k}{N\Delta x}\)代入, 得到\(\omega_k x_n = \frac{2\pi k}{N\Delta x}(n\Delta x) = \frac{2\pi kn}{N}\). 于是公式变成\(F_k=\sum_{n=0}^{N-1} f_n e^{-i\frac{2\pi kn}{N}}\). 这就是DFT. 逆DFT为\(f_n=\frac{1}{N}\sum_{k=0}^{N-1}F_k e^{i\frac{2\pi kn}{N}}\). 这里要强调的一点是, 当\(f_k\)是实数的时候, 指数函数的对称性会导致频谱自动形成共轭对称, 因为频谱是共轭对称的, 所以只有一半频谱是独立信息, 如果DFT的采样点数是\(N\), 那么真正独立的频率数量是\(N/2+1\).

快速离散傅里叶变换

前面讲的DFT是\(F_k=\sum_{n=0}^{N-1} f_n e^{-i\frac{2\pi kn}{N}},\quad k=0,1,\dots,N-1\). 如果直接按照这个定义去算, 那么每个\(F_k\)都要做\(N\)次乘加, 一共有\(N\)\(F_k\), 所以总计算量大概是\(O(N^2)\), 而FFT的作用就是, 用更加聪明的方法, 算出完全一样的\(F_k\), 把复杂度下降到\(O(N\log N)\). 具体算法这里不介绍了, FFT最终算出来的是\(F_0, F_1, ..., F_{N-1}\), 也就是信号的频谱系数.

基础

语音信号是一个非平稳的随机信号. 所谓非平稳, 就是他的统计特性会随着时间变化, 比如均值, 能量, 频谱分布都不是固定不变的. 因为认为说话的时候, 发声器官在一直运动, 音素也在不断切换, 所以语音整体上是一个不平稳的信号. 但是在很短的时间范围内, 通常取10ms-30ms, 发音状态变化不大, 这时可以把他近似为短时平稳信号, 这就是为啥语音处理通常采用分帧, 加窗等短时分析的方法, 比如短时能量, 短时过零率, 短时傅里叶变化等分析手段, 本质上都是基于短时平稳的假设.

语音产生模型, 通常叫做声源-声道模型, 他的核心思想是, 语音可以看为是"激励源"经过"声道系统"滤波后的输出. 激励源主要有两类: 一类是清音, 比如s, f, 他的激励更像随机噪声; 一类是浊音, 比如a, o, m, 他的激励来自声带周期震动, 更像周期脉冲. 这些激励经过口腔, 咽腔, 鼻腔构成的声道后, 被不同的共振特征塑形, 最终形成不同的语音.

一些概念: 基频(Fundamental Frequency, F0), 是声带震动的时候, 产生的一个最低的频率. 谐波是基频的整数倍频率的频率分量. 浊音这种周期性的激励源的频谱只由谐波构成, 这是因为每个周期的起点和终点, 信号的值和相位必须完全一样. 如果基频是100hz, 那么他的谐波就是100hz, 200hz, 300hz等. 而清音的激励更像随机噪声, 他的频谱是连续的, 没有明显的谐波结构. 共振峰是声道对某些频率的共振增强, 在频谱上表现为能量特别强的频率峰值, 声带产生的信号本来包含很多谐波, 但是这些谐波经过声道之后, 并不都是的样强, 有些频率会被放大, 有些会被削弱, 被声道共振增强的那些频率位置, 就叫做共振峰. 说白了, 声带决定谐波的位置, 声道决定谐波的强弱. 如果你把这些谐波的峰值连起来, 就会得到一个比较平滑的曲线, 叫做包络曲线.

数字化过程

  • 采样: 采样就是在时间轴上每隔固定的时间取一个点, 把连续时间信号变为离散的时间信号. 根据奈奎斯特采样定理, 采样频率必须至少是信号最高频率的两倍, 才能保证信号可以被完全重建. 传统电话把语音带宽限制在300到3400Hz, 所以根据奈奎斯特定理, 取8kHz就够了, 因为8kHz的采样频率可以捕捉到4kHz的频率成分并确保重建. 16hKz常用于宽带语音, 因为它能表示到8kHz频率成分, 适合语音识别, 语音合成和高质量通话. 44.1kHz常用于音乐, 因为人耳能听到的最高频率大约是20kHz, 44.1kHz的采样频率可以捕捉到22.05kHz的频率成分, 确保音乐的高频细节被保留.
  • 量化: 量化是把连续的幅值映射到有限个离散等级. 它会引入量化误差, 等级越多, 位数越高, 失真越小. 比如8bit, 16bit, 本质上就是用不同的精度表示每个采样值.
  • 编码: 编码是把量化后的数字信号转换成特定格式的二进制数据, 以便存储和传输. 常见的编码格式有PCM, ADPCM, MP3, AAC等. PCM是最原始的编码方式, 直接把量化后的数值以二进制形式存储. ADPCM是一种压缩编码方式, 通过预测和差分来减少数据量. MP3和AAC是有损压缩编码方式, 通过去除人耳不敏感的频率成分来大幅减少数据量.

预处理

  • 预加重: 预加重本质上是一个一阶高通滤波器(一种电子信号处理电路, 允许高于截止频率的信号通过). 为什么需要预加重, 因为自然语言在发身过程中, 声门吉利和声道辐射会让频谱呈现"低频强, 高频弱"的趋势, 也就是高频能量通常衰减比较快. 这样会导致后续频谱特征里, 高频细节不明显. 预加重就是为了补偿这种衰减, 更利于分析共振峰, 清辅音等高频信息.
  • 分帧: 分帧就是把连续语音按照时间切分为一小段一小段的短帧, 每帧单独分析. 为什么需要分帧, 因为在很短的时间内, 语音的统计特征变化不大, 可以近似看为平稳信号, 这样才能对每一帧做傅里叶变换, 求能量. 这就是"短时分析"的核心思想. 通常取20ms-50ms的原因是在工程上比较平衡的范围. 帧太短, 频率分辨率不够; 帧太长, 又会破坏短时平稳的假设. 如果采样率是16kHz, 20ms的帧长就是320个采样点.

特征提取

  1. 短时能量: 短时能量描述的是某一帧的声音是强还是弱. 通常用来做语音端点检测(VAD)
  2. 过零率: 单位时间内波形穿过0的次数. 高频的成分多, 波形变化越快, 过零率高; 低频的成分多, 波形变化越慢, 过零率低. 通常用来区分清音和浊音.
  3. 基音周期: 浊音是周期性的震动. 基音周期是\(T_0\), 基音频率是\(F_0=\frac{1}{T_0}\). 通常用来识别说话人, 声调分析
  4. 共振峰: 是声道共振产生的频率峰值, 是包络网格中的峰值, 声源经过声道滤波之后, 不同频率被不同程度增强, 增强最明显的位置就是共振峰.
  5. LPC(线性预测技术): 当前语音可以用过去几个采样点线性预测. 他主要是估计声道的滤波器参数.
  6. 倒谱: 是对语音频谱做进一步变换后的表示, 常用于将声源信息和声道信息分离开. 计算步骤如下, 对语音做傅里叶变换得到频谱, 取幅度谱, 取对数, 再做逆傅里叶变换. 为什么能够分离呢, 这是因为在语音模型里面, 声音=激励源*滤波器(卷积), 取对数之后, 乘积能够变成加和, 所以声源和声道更容易分开.
  7. MFCC: 是基于Mel频率尺度和倒谱分析提取出来的一些特征. 其过程包括预加重, 分帧, 加窗, FFT, Mel滤波器组, 取对数, DCT. MFCC的本质是用少量的数字来表示语音频谱的整体形状, 它先把声音转换为类似人耳听到的频率结构, 再从这个结构里面提取语音特征.
  8. 频带能量: 将频谱切为几块, 分别计算每块的能量
  9. 谱熵: 衡量频谱混乱程度的信息论指标
  10. Fbank: 时域语音 → STFT/功率谱 → Mel 滤波器组 → Fbank(Mel filterbank energies)→ log → log-Mel / log-Fbank → DCT → MFCC

分析方法

短时傅里叶变换

短时傅里叶变换, STFT, = 滑动窗口 + 每帧做DFT. 来看一张STFT的效果图.

下面一张图中, 颜色表示的是magnitude, 变为了一张2D图像:

小波分析

小波分析和STFT是类似的, 但是小波分析做的是"可变分辨率"的分析. 关键差别在窗口上. STFT的做法, 本质上是拿一个固定长度的窗, 沿着时间轴一段一段地切开, 再对每一段做傅里叶变换. 这样你确实能得到时频图, 但是, 这个窗口一旦定了, 时间分辨率和频率分辨率就一起定了. 窗短一点, 时间分辨率高, 频率分辨率低; 窗长一点, 时间分辨率低, 频率分辨率高. 而小波分析不一样, 看高频的时候, 用短窗; 看低频的时候, 用长窗.

关于小波, 推荐这篇文章: https://charleechan.github.io/MyWiki/Hardware/2_SigCirSys/SigProc/wavelet1.html

倒谱分析

倒谱分析可以理解为对频谱再做一次分析的方法. 普通的频谱分析是, 先把时域信号变到频域, 看里面有哪些频率成分. 倒谱分析是更进一步, 先求频谱, 再取对数, 然后再做一次反变换. 这样做的好处是, 原来在频域里面乘在一起的东西, 会更容易被分开.

一个语音信号, 常常可以看为声带激励*声道响应. 这两个因素在普通频谱里面掺在一起, 不容易分开, 但是经过取对数之后, 他们会在频域里面分布到不同的位置, 更加容易分离.

3A算法

AEC

AEC, Acoustic Echo Cancellation, 声学回声消除是一种常见的语音处理算法, 它的作用是消除扬声器播放声音之后被麦克风再次采集到的回声. 在语音通话中, 远端用户的声音会通过扬声器播放出来, 这些声音会在房间中传播并被本地麦克风再次采集. 如果不做处理, 远端用户会听到自己的声音延迟返回, 这就是回声问题.

AEC的基本思想是, 系统知道扬声器正在播放什么声音, 因此可以用这段声音作为参考信号, 通过算法预测它在麦克风中形成的回声, 然后从麦克风信号中把这部分回声剪掉. 简单理解就是三步: 1. 获取扬声器播放的参考信号; 2. 根据房间声学环境估计回声; 3. 从麦克风信号中减去这部分回声.

AEC通常依赖自适应滤波算法来估计回声, 最常见的是LMS和NLMS.这些算法会不断调整滤波器的参数, 使得预测的回声越来越接近真实回声, 从而实现更好的消除效果.

  1. LMS算法是怎样的

    远端语音经过播放, 记作\(\mathbf{x}(n)\), 这些声音经过空间, 桌面反射, 设备外壳等路径传到麦克风, 形成回声. 可写成\(d(n) = y(n) + v(n)\), 其中, \(y(n)\)是真实回声, \(v(n)\)是本地语音+噪声. AEC的目标是估计\(y(n)\), 然后做\(e(n) = d(n) - \hat{y}(n)\), 这里的\(e(n)\)就是消除回声后的信号. 代入\(d(n)\)之后, 得到\(e(n)=(y(n) - \hat{y}(n)) + v(n)\), 其中\(y(n) - \hat{y}(n)\)是回声残余, \(v(n)\)是本地语音+噪声. AEC的目标就是让回声残余尽可能小.

    LMS更新可以表示为\(\mathbf{w}(n+1)=\mathbf{w}(n)+\mu e(n)\mathbf{x}(n)\). 其中\(\mathbf{w}(n)\)是滤波器权重, \(\mu\)是步长参数, \(e(n)\)是当前的误差信号, \(\mathbf{x}(n)\)是扬声器参考信号. 这个更新公式的意思是, 根据当前的误差信号和参考信号来调整滤波器权重, 使得预测的回声更接近真实回声. 我们的目的是最小化均方误差\(E[e^2(n)]\). 自适应滤波器的输出是\(\hat y(n)=\mathbf w^T(n)\mathbf x(n)\), 代入误差得到\(e(n)=d(n)-\mathbf w^T(n)\mathbf x(n)\), 均方误差为\(J(\mathbf w)=E[e^2(n)]\). 梯度下降的形式是\(\mathbf w(n+1)=\mathbf w(n)-\mu \nabla J(\mathbf w)\), 因为\(\nabla J(\mathbf w) = E[-2e(n)\mathbf x(n)]\), 将其代入, 得到\(\mathbf w(n+1) = \mathbf w(n)+2\mu e(n)\mathbf x(n)\), 这里的2可以被吸收掉, 最终得到\(\mathbf w(n+1) = \mathbf w(n)+\mu e(n)\mathbf x(n)\).

  2. 为什么AEC适合用自适应滤波

    因为声学回声路径不是固定的, 人会移动设备, 房间会变化, 设备外壳, 桌面状态会变化. 而LMS的优点在于, 他的计算量比较低, 实现简单, 可在线自适应, 适合实时系统.

  3. 为什么AEC里面经常用NLMS而不是纯LMS

    NMLS的更新公式: \(\mathbf{w}(n+1)=\mathbf{w}(n)+ \frac{\mu}{\|\mathbf{x}(n)\|^2+\delta}e(n)\mathbf{x}(n)\). 比LMS多了一个归一化项: \(\frac{1}{\|\mathbf{x}(n)\|^2+\delta}\). 这个归一化项的作用是, 当参考信号\(\mathbf{x}(n)\)的能量较大时, 降低更新步长, 当参考信号能量较小时, 增加更新步长. 这样可以提高算法的稳定性和收敛速度, 特别是在参考信号能量变化较大的情况下. 类似于Adam优化器中的自适应学习率.

  4. AEC会遇到哪些问题

    • 延迟估计: 因为扬声器播放的声音到达麦克风需要一定的时间, 需要估计扬声器信号到达麦克风的延迟, 从而把回声放到正确的时间位置.
    • 双讲检测: DTD, Double Talk Detection, 如果远端在说话, 本地人也在说话, 麦克风信号就会变成回声+本地语音, 此时算法可能把本地语音认为回声误差, \(e(n)\)不在主要反映回声估计误差, 而会混入大量本地说话成分, 从而错误更新滤波器, 导致回声消除效果变差, 滤波器发散, 声音失真. 因此需要DTD检测是否出现双讲, 如果检测到双讲, 暂停或者减弱滤波器更新, 保持当前的声学模型.
    • 残余回声抑制: RES, 进一步压低没有被完全消除的回声, 即使AEC工作正常, 也很难把回声完全消干净. 因此通常在AEC后面加一个RES模块, 对可疑频段进行衰减.

ANS

ANS, Automatic Noise Suppression, 自动噪声抑制是一种语音处理算法, 用于降低环境噪声, 让语音更加清晰. 麦克风采集到的信号通常包含两个部分, 语音+回声+环境噪声. 环境噪声可能来自空调声, 风扇声音, 键盘声音, 交通声音, 人群背景声音.

ANS的基本思想是先估计噪声, 再对噪声进行抑制. 一般流程是分析音频信号, 估计当前环境噪声的能量, 判断哪些频率成分主要是噪声, 对这些频率进行衰减. 处理后得到的信号主要保留语音成分.

传统ANS通常基于频域噪声估计, 常见的方法包括谱算法(使用STFT, 然后估计噪声谱, 干净语音谱 = 带噪语音谱 - 估计噪声谱), 维纳滤波, MMSE估计方法. 现代系统中也常用深度学习降噪模型来进一步提升效果.

维纳滤波可以理解为一种按照置信度调节保留多少声音的降噪方法. 它不像简单的谱算法那样直接减掉噪声, 而是给每个频率点乘一个权重: 像语音的部分多保留, 像噪声的部分多压低.

AGC

AGC, Automatic Gain Control, 自动增益控制是一种语音处理算法, 用于自动调整音频信号的音量, 使声音保持在合适且稳定的范围. 麦克风采集到的语音音量可能变化很大, 例如, 说话的人离麦克风远近不同, 有的人说话声音很小, 有的人很大, 用户说话声音时大时小. 如果不做处理, 就会出现, 声音太小听不清, 声音太大产生失真或者爆音. AGC的作用就是自动调整音量, 是的输出声音保持稳定清晰.

AGC会是时间检测输入信号的能量(音量), 然后根据目标音量自动调整增益, 如果声音太小->提高增益(放大), 如果声音太大->降低增益(衰减). 目标是让输出信号维持在一个合适的能量范围.

AGC通常包含两个关键参数: Attack(攻击时间), 声音突然变大的时候, 降低增益的速度; Release(释放时间), 声音变小的时候, 恢复增益的速度.

AGC 是自动增益控制,目标是让输出语音电平稳定在合适范围.常见实现是按帧计算 RMS 电平,与目标电平比较后动态调整增益,同时用 attack/release 做平滑,避免声音抽动.为了防止削顶,后面通常还会接 limiter.在语音系统里,我更倾向于用 VAD-aware AGC,这样可以避免在静音时把背景噪声拉起来.它通常放在 AEC 和 ANS 后面.

音频检测

主要分为四大类:

  1. 基于规则和传统信号处理的方法: 主要利用短时能量, 过零率, 频带能量, 谱熵, MFCC, log-mel等特征, 再结合阈值, 模板匹配或者相似度分析来做检测. 这类方法的优点是实现简单, 可解释性强, 实时性好, 适合数据少, 资源受限, 异常模式比较固定的场景.
  2. 传统机器学习方法: 一般流程是先做预处理和特征提取, 再使用GMM, SVM, Random Forest, XGBoost等分类器完成检测. 这类方案比纯规则方法泛化能力更好, 适合中小规模数据和相对明确的分类任务.
  3. 全监督式深度学习方法: 是现在比较主流的方法, 通常把音频转为log-mel频谱图或者其他时频特征, 再输入CNN, CRNN, Transformer, Conformer等模型做分类或者检测. 如果数据量足够, 标准质量高, 这类方法的效果通常最好, 尤其适合做故障音识别, 异常音分类, 声音事件检测等任务.
  4. 半监督方法: 在工业质检或者机械故障检测中, 我们往往只能拿到"正常"的声音, 很难收集到"故障"的声音. 这类方法的核心思路是基于重构. 使用AutoEncoder或者VAE. 模型只学习如何还原正常的声音, 当异常的声音输入的时候, 重构误差会激增, 从而触发报警.
  5. 自监督方法: 类似于大模型的训练, 模仿NLP领域的BERT/GPT逻辑. 现在海量的无标注音频上训练, 学习音频的通用表示, 再在特定检测任务上进行微调. 例如AudioMAE, Wav2Vec, Hubert, BEATs.

语音文本多模态融合方法

  1. 特征级融合: 在模型的前端将两种模态的原始特征或者提取后的特征向量进行拼接, 随后输入统一的神经网络.
  2. 策略级融合: 每个模态由独立的子模型处理并得出初步结论, 如情感分类概率, 最后通过特定规则汇总这些结果
  3. 模型级融合: 在模型的中间层进行交互, 这是目前主流的方案, 包括张脸融合(两个模态特征向量的外积); 注意力机制; 门控机制.
  4. 跨模态表征对其: 为了消除语音和文本在空间分布上的差异, 将他们映射到同一个共享语义空间, 对比学习, 细粒度对齐.
  5. 端到端大模型融合: 将音频信号转换为类似文本的离散单元, 使得模型能够像文本一样读音频.

评测指标

  • 准确率指标

    • WER, Word Error Rate, 词错误率. 定义通常为WER=(S+D+I)/N, 其中S是替换错误, D是删除错误, I是插入错误, N是参考文本里面的词数. 英文等以空格分词较自然的语言, WER基本是默认指标. 很多ASR论文和benchmark都把它作为第一主目标.

      三种错误

      假设标准答案(reference)是:

      "我 喜欢 学习"

      识别结果(hypothesis)如果不同, 就会出现这三类错误.

      替换错误 S(Substitution)是"本来该是 A, 结果识别成了 B".

      例如标准答案是: "我 喜欢 学习"

      识别结果变成: "我 喜欢 睡觉"

      这里"学习"被错认成"睡觉", 这就是 1 个替换错误. 也就是: 正确词被另一个词替代了.

      删除错误 D(Deletion)是"标准答案里有这个词, 但识别结果漏掉了".

      例如标准答案是: "我 喜欢 学习"

      识别结果变成: "我 学习"

      这里"喜欢"丢了, 没有被识别出来, 这就是 1 个删除错误. 也就是: 少识别了一个本来应该有的词.

      插入错误 I(Insertion)是"标准答案里没有这个词, 但识别结果多出来了".

      例如标准答案是: "我 喜欢 学习"

      识别结果变成: "我 很 喜欢 学习"

      这里多出来一个"很", 这就是 1 个插入错误. 也就是: 凭空多识别了一个词.

    • CER, Character Error Rate, 字错误率. 对于中文, 日文这类"字/字形单位"更加自然的语言, 常用CER. 它和WER的思想一样, 只是把统计单位从"词"换成了"字/字符". 近年的系统报告里面通常会写"英文报WER, 中文报CER"

    • SER, Setence Error Rate, 句错误率, 表示整句是否完全识别正确, 只要一句里面有一个错就错. 他更加严格, 但通常不是第一主指标.
  • 效率指标

    • RTF: Real-Time Factor, 实时率, 即"处理耗时/音频时长", RTF<1通常表示比实时更快, 能满足实时或者近实时推理需求.
    • 延迟: 如果是流式ASR, 还会特别关注延迟, 常会区分首字延迟, 端到端延迟, 稳定输出延迟等.

目前比较火的ASR/TTS项目

  • ASR这边, 第一梯队还是Whisper生态, openai/whisper仍然是通用多语言识别的经典基线, 官方仓库明确支持多语言语音识别, 语音翻译和语言识别; 而SYSTRAM/faster-whisper用CTranslate2重写之后, 更加偏向工程落地, 同等精度下更快, 占用更少内存. 如果主要看中文, FunASR/SenseVoice现在很值得关注. Fun-ASR支持31种语言, 低时延转写, 对东亚和东南亚语言做了重点优化. SenseVoice也在FunAduioLLM体系里面, 对VAD, 标点, 说话人相关能力配套地也比较完整.

IVR项目

  1. 这个IVR项目和传统的IVR有啥区别?

    IVR, Interactive Voice Response. 传统的IVR是固定的菜单树, 用户必须案件或者说出关键词, 路径僵硬; AI IVR支持自然语言表达, 比如"我想查一下保单", 或者"我要人工客服", 系统可以直接理解意图并路由. 优势在于交互自然, 路径更短, 转人工率和误转率更加容易优化.

  2. 整个系统的架构是怎么样的?

    用户来电/电频流接入 -> 前置音频处理 -> ASR(FunASR)转文本 -> LLM进行意图识别 -> 分流 -> 人工/大模型TTS合成回复 -> 返回给用户.

  3. 为什么选择FunASR?

    因为FunASR在中文语音识别场景里工程化比较方便, 流式/非流式能力比较使用, 部署成本相对较好, 适合做电话语音场景. 电话场景音质一般, 噪声多, 所以更加看重实时性, 鲁棒性和部署效率, 而不是单点精度.

  4. 为什么使用vLLM部署大模型?

    因为这个场景对并发和相应延迟比较敏感, vLLM在吞吐, KV Cache管理, 批处理调度上更加适合上线服务, 相比起huggingface的推理服务, vLLM更加适合高并发场景, 能显著提升token生成的效率, 降低单位请求成本.

  5. LLM在你的IVR里面具体负责什么?

    意图分类, 路由决策, 上下文理解, 回复生成.

  6. 如何保证大模型输出稳定, 不胡说?

    一是做prompt约束, 限制输出的格式; 二是把可执行动作收敛为固定的标签, 比如转人工, 投诉建议; 三是低置信度的时候走兜底策略, 比如追问或者转人工; 四是关键业务不依赖自由生成的文本, 而是模型去调用知识库.

  7. 这个项目里面最大的技术难点是啥?

    最大的难点是电话场景下的端到端控制延迟. 因为用户对语音交互的延迟非常敏感. ASR, LLM, TTS任意环节慢了都会影响体验. 所以我重点做了流式识别, 模型服务部署的优化, 播报文本压缩以及异常兜底.

  8. 如何优化延迟?

    • ASR测: 流式识别, 边说变出文本
    • LLM测: vLLM部署, 控制prompt长度, 减少无效上下文, 限制输出token
    • TTS测: 缩短回复文本, 优先播报关键句, 必要的时候分段合成
    • 系统测: 异步, 缓存常见回复, 超时回退
  9. 如何评估这个系统的成果?

    可以是意图识别准确率, 首轮命中率, 平均通话时长, 转人功率, 误转率, 用户中断率, 任务完成率.

  10. CosyVoice在项目里为什么合适?

    CosyVoice适合自然度比较好的中文语音合成, 在IVR里面比传统的机械播报体验更好.

  11. 电话场景里面用户打断播报怎么办?

    系统需要只是barge-in, 也就是用户在TTS播报过程中插话的时候, 能及时停播并重新进入识别流程.

  12. 如何处理多轮上下文?

    上一轮的用户意图, 系统问题, 槽位信息要保留, 但是不能无限堆叠上下文, 否则会导致延迟和成本上升. 可以只保留对导航决策有价值的关键信息, 做轻量上下文管理.

  13. 这个系统上线之后会重点监测什么?

    接口时延, ASR报错率, LLM请求超时, TTS失败率, 回话中断率, 转人功率突增, 热点意图分布变化.

  14. 如果继续优化会怎么做?

    • 优化意图识别和业务知识覆盖
    • 更加细粒度的路由和个性化应答
    • 通过日志回流做数据闭环, 持续优化prompt, 语料和模型效果

近些年ASR的趋势

领域正在从以转写为中心的ASR往更加广义的spoken language understanding/Speech LLM迁移, 就是说不止关心说了什么, 还关心怎么说, 说话人, 情绪, 环境声等等. 现在大致有三条并行主线. 第一条是ASR/cascaded pipeline, 先转写, 再交给LLM, 再接TTS, 这条线没有小时, 反而因为可控, 好调试, 容易接工具和业务系统, 工程里仍然很常见.; 第二条是现在最热的, end-to-end speech-to-speech/realtime voice agent, 这条路线的目标不是先把语音完整转为文字再思考, 而是直接处理连续语音输入, 并尽量低延迟地输出语音, 追求更加自然地打断, 接话和表达. OpenAI的Realtime API明确就是给低延迟双向音频设计交互设计的. Qwen2.5-Omni和LLaVA-Omni这类论文也都把streaming, 低时延, 自然语音交互放在核心位置; 第三条是从transcript-only走向richer speech understanding. 也就是说, 大家越来越不满足于"先转成文字再理解", 因为这会丢掉说话风格, 韵律, 情绪和其他副语言信息. 近些年的论文和综述都在强调, 端到端LLM的价值之一, 就是能保留这些ASR文本化之后会损失的信息.

总结一下:

  • cascaded pipeline
  • speech-to-speech
  • richer speech understanding

面经

  1. 你在做语音识别或者语音合成项目的时候, 遇到过哪些噪音干扰问题?

    在做语音识别项目的时候, 比较常见的噪音干扰主要有四类, 第一类是环境音, 比如地铁, 马路, 风扇, 空调声; 第二类是人声混叠, 比如有多个人同时说话, 旁边有人插话; 第三类是设备噪声, 比如麦克风底噪, 电流声, 回声; 第四类是传输或者采集问题, 比如削顶, 混响, 音量过低或者过高. 这些问题会直接影响前端特征提取和后端识别结果. 比如VAD切分不准, 关键词丢失, 声学模型误识别, 音量过低或者过高.

  2. 如果模型在安静的环境下识别准确, 但是在嘈杂环境在下降明显, 怎么改进?

    1. 训练数据方面: 将训练数据改成真正的多条件训练. 最常见, 最稳的做法, 是用真实噪声或者接近真实场景的噪声去做混合, 再叠加混响, 让模型在不同SNR(信噪比), 不同噪声模型, 不同房间条件下都见过数据; SpecAgument这种时频遮挡也通常值得一开. 还有一条很实用: 不要一开始就全部扔超低SNR, 可以按照curriculum learning, 从较高SNR或者较容易样本逐步过度到更难样本. 总结: *多条件训练, SpecAugment, curriculum learning.*
    2. 前端方面: 单独先做一个"通用降噪", 再把增强后的波形喂给rew'tre发的啥的我的我弟弟22222222222222222好; 原因是增强会引入语音失真, ASR不一定喜欢; 更稳的是: 一条是前端e'w'w'e'wtre'w'te'w't增强后的音频. 近期有研究发现, 零样本场景下, 通用降噪即使让听感更干净, 也可能让Whisper等模型识别更差.
    3. 模型方面: 把backbone换为更强的鲁棒编码器并在目标噪声域上做continue pretraining(比如HuBERT, Wav2Vec, Conformer encoder); 训练的时候显式加入clean-noisy一致性约束或对比学习, 让同一句话在干净和嘈杂条件下的表征更加接近. 在解码端加入更强的语言建模. 或者进一步可以做蒸馏, 让clean teacher教noisy student学到更加稳定的表示.
    4. 场景方面: 专门针对典型嘈杂场景做小模型的微调.
  3. 你为什么想做语音算法, 你觉的语音交互的未来在哪里?

    语音算法在我看来是一个充满潜力的领域, 尤其是随着人工智能和机器学习技术的进步, 语音交互已经变得越来越智能和人性化. 我认为, 语音交互的未来主要体现在以下几个方面:

    1. 语音会从"指令式"变为"实时对话式". 未来好的语音系统要能打断, 能顺着上下文继续, 能边听边想边说. OpenAI的Realtime API就是按照低延迟实时语言来设计的. Google也明确把Gemini Live作为可以中途打断, 改话题, 继续追问的交互.
    2. 语音会和视觉融合, 变成多模态的入口. 单出的"听你说"已经不够, 系统还要"看见你在看什么". Google已经把相机, 屏幕共享, 屏幕高亮引导接入Gemini Live, 且面向45+语言, 150+国家推出, Google还披露, Gemini Live的对话平均比文本长5倍, 说明语音在探索式, 陪伴式, 现场式任务里有天然优势.
    3. 真正的分水岭是能不能调用工具和跨应用执行. 未来用户不满足于"给建议", 而是希望它直接帮你预约, 查消息, 发消息, 操作家居, 处理工作流. Amazon对于Alexa+的定位就是"get things done", Google也在把Calendar, Keep, Tasks, Messages, Maps接入Live. Apple这些年推迟推出的也是"理解个人上下文并在app间采取行动的siri".
    4. 语音交互会越来越个性化. 这也是最难的一关, 真正有价值的语音助手, 不是声音更像人, 而是更懂你的日程, 偏好, 关系和设备环境. Amazon把personalized放在Alex+的核心位置. Apple对更个性化的Siri的延迟, 说明一旦涉及个人的上下文, 跨应用执行和稳定性, 工程难度会骤增.
    5. 语音越像真人, 越必须解决冒充, 深伪, 误导和认证问题.
    6. 商业上会先在垂直场景爆发, 而不是先全面取代所有通用助手. 因为客服, 教育, 销售, 预约, 现场指导这些场景的回报最好算. OpenAI在介绍gpt-realtime的时候直接点名customer support, personal assitance, education.
  4. 解释一下对语音信号处理中MFCC特征的理解

    MFCC可以理解成, 把语音信号里面"人耳更敏感, 对语音内容更有用"的谱包络信息提取出来, 再压缩为一小组特征. 直觉上他不是直接描述波形长什么样, 而是在描述: 这段短时语音的频谱形状, 尤其是声道/共振峰形成的整体包络.

    典型流程是: 分帧加窗, FFT, 过Mel滤波器组, 将频率映射到更加接近人耳感知的Mel频率, 低频分辨率更细, 高频更粗. 人耳对响度更接近对数感知, 而且取log之后, 乘性变化会变成加性变化, 更加稳定; 最后做DCT, 把Mel频谱的相关性压缩掉, 得到一组系数, 就是MFCC.