8 类合成音节,24 条训练、16 条测试,4 维手工特征配最近邻:不加额外噪声时 16 / 16 全对; 噪声加到信号标准差的 3% 掉到 0.875,5% 掉到 0.500,10% 只剩 0.125。
上一节做文字问答,这一节做声音。语音识别是 NLP 里最不受文本技巧影响的一块,它更像信号处理。
一、声音要变成文字,中间隔着三层
- 波形。每秒 16000 个采样点,一段 3 秒的话就是 48000 个数。
- 特征。分帧之后每帧算一组数(能量、过零率、频谱形状)。这一步把 48000 个数压成几百组。
- 声学模型。把特征序列映射成音素或字。这一步才需要训练,也是沙箱里做不了的部分。 Demo 做的是前两层加一个最近邻分类器。它证明「特征能分开合成的音节」,不证明「能识别真人说话」。 这个区别必须说清,否则读者会以为语音识别就这么简单。
二、四个手工特征各管什么
对数能量:这一帧有多响。区分有声和无声最直接。 过零率:波形穿过零点的频繁程度。浊音(a、o)低,清音(s、f)高。 谱质心:频谱能量的重心,粗略对应「音高」。 谱滚降:85% 的能量集中在这个频率以下,反映高频成分多少。 四个数就把一帧说完了。 真实系统用 13 维 MFCC 加一阶二阶差分,共 39 维,原理是同一类——都是把频谱的形状压成几个数。
三、干净时 100%,加 3% 噪声就开始垮
不加额外噪声时 16 / 16。这个满分没什么可夸的:8 个类别只差基频和谐波数,特征上分得干干净净。 噪声按信号标准差的百分比加进去,同一套代码同一套特征: 1% 和 2% 还是 16/16,3% 掉到 14 / 16,5% 掉到 8 / 16,10% 只剩 2 / 16。 垮得这么快,原因在特征本身。 谱质心和过零率对宽带噪声极敏感——一点噪声就把高频段填满,质心立刻往上跳。 更麻烦的是标准化:训练集里谱质心几乎不变,标准差很小,一标准化,这点波动被放大几十倍, 直接盖过其他维度。
四、真 ASR 靠什么扛住噪声
三条,都是 Demo 没有的: 1. 在噪声数据上训练。训练集里就混入各种噪声,模型学会忽略它。 2. 用神经网络代替手工特征。让它自己从波形里学抗噪的表示,不用人猜哪个特征是稳的。 3. 语言模型纠错。声学模型听错「四是四」,语言模型能根据上下文改回来。 所以真实语音识别的准确率不是靠某一个环节,而是三环叠加。任何一环单独拿出来看,都不惊艳。 这一节能带走的结论是:手工特征在实验室里够用,在真实环境里不够。 这也是深度学习在语音领域全面取代传统方法的原因。
五、动手 Demo:音节识别:4 维手工特征加最近邻,噪声一过 3% 就垮
合成 8 类音节(基频 4 档 × 谐波 2 档),每类 3 条做训练、2 条做测试; 每段算对数能量、过零率、谱质心、谱滚降四个特征,标准化后做最近邻分类; 先看干净的一档,再把宽带噪声按信号标准差的 1% 到 20% 逐档加进去,打出每档的准确率。
sp = np.abs(np.fft.rfft(x * np.hanning(len(x))))
cen = float((sp * fr).sum() / sp.sum()) # 谱质心
cum = np.cumsum(sp) / sp.sum()
roll = float(fr[int(np.searchsorted(cum, 0.85))])
q = (feat(x) - mu) / sd # 标准化
pred = ytr[int(np.argmin(((Z - q) ** 2).sum(1)))]
实跑输出:
--------------------------------------------------------------------
语音识别链路:从波形到音节模板(不是真 ASR)
--------------------------------------------------------------------
采样率 8000 Hz,每段 0.25 秒
类别数 8(基频 4 档 × 谐波 2 档)
训练样本 24 条(每类 3 条)
测试样本 16 条(每类 2 条,另加宽带噪声)
特征 对数能量、过零率、谱质心、谱滚降(4 维)
先看干净的一档(不加额外噪声):
最近邻准确率 16 / 16 = 1.000
类别 测试条数 答对
-----------------------------
f0=150 谐波1 2 2
f0=150 谐波3 2 2
f0=220 谐波1 2 2
f0=220 谐波3 2 2
f0=300 谐波1 2 2
f0=300 谐波3 2 2
f0=420 谐波1 2 2
f0=420 谐波3 2 2
再把宽带噪声按信号标准差的百分比加进去,同一套代码同一套特征:
噪声(占信号标准差) 答对 准确率
--------------------------------------
0% 16 / 16 1.000
1% 16 / 16 1.000
2% 16 / 16 1.000
3% 14 / 16 0.875
5% 8 / 16 0.500
10% 2 / 16 0.125
20% 2 / 16 0.125
曲线在 3% 之后就垮。垮得这么快,原因在特征本身:谱质心和过零率对宽带噪声极敏感——一点噪声就把高频段填满,质心立刻往上跳,而训练集里这个维度几乎不变,标准化之后它的波动被放大了几十倍。
「合成音上 100%」这句话本身没什么价值:类别只差基频和谐波数,本来就分得开。真 ASR 要处理口音、连读、混响、说话人差异,沙箱里没有声学模型,那个数我给不了。
干净那一档 8 个类别全对,每类 2 条。这个结果说明特征和流程是通的,不说明方法强。 噪声扫描表是这一节的重点:0%、1%、2% 都是 1.000,3% 掉两个,5% 掉一半,10% 只剩两个。 曲线不是缓慢下滑,是断崖。 断崖的原因我写在正文里了——谱质心这个维度在训练集里几乎不变,标准化把它的波动放大了。 如果改用分频带对数能量(比如 0~250、250~500、500~1000、1000~4000 Hz 四段),抗噪会好一些, 但也就是推到 5% 到 10% 之间,不会质变。
30 秒实验
- 把噪声档位加密到 3%、4%、5%,看准确率是怎么一档一档掉的。
- 把谱质心这个特征删掉,只用三个特征,看抗噪有没有变好。
- 想一想:为什么在噪声数据上训练能提高抗噪能力?
常见误区
- 「语音识别就是分类」——分类只是最后一步,前面还有波形、分帧、特征。
- 「手工特征够用了」——合成音上够用,真人带噪环境差得远。
- 「准确率 100% 说明方法好」——类别只差基频,本来就分得开。
自测三问(点开对答案)
1. 语音识别中间那三层是什么? —— 波形、特征、声学模型。沙箱里只能做前两层加一个简单分类器。 2. 过零率用来区分什么? —— 浊音和清音:浊音波形慢、过零率低;清音接近噪声、过零率高。 3. 为什么噪声一过 3% 准确率就断崖? —— 谱质心和过零率对宽带噪声极敏感,标准化之后这点波动被放大几十倍。声音处理完,下一节回到文字,做一件更见功力的事:翻译。
- ☐ (本节完,下一节 6.2.4 机器翻译:无界沟通)