FunASR SOND 数据预处理链路:把会议混音变成"谁在说话"的时间轴
【免费下载链接】FunASROpen-source speech recognition toolkit for training, inference, streaming ASR, VAD, punctuation, speaker diarization pipelines, and OpenAI-compatible/MCP serving.项目地址: https://gitcode.com/GitHub_Trending/fun/FunASR
上周你手里有一段三人群组的会议录音,最后五分钟大家互相抢话。把这段音频喂给 FunASR 的 SOND 说话人日志(Speaker Diarization)模型,你要的不是文字稿,而是一条时间轴:每一时刻是谁在说、重叠处又是谁在同时说。在这段混音波形和这条时间轴之间发生的,就是本文要拆的 SOND 数据预处理与前端处理链路。顺便解释一下说话人日志:它像一位会议秘书,不只记录"说了什么",还要记下"是谁说的"。
这条链路要解决的三个真实问题
抢话。传统做法是"先切段、再判人",但重叠语音连"切段"这一步都做不到——两个人同时出声的片段没法归给任何一个人。SOND 直接从混音波形逐帧输出说话人组合,前提是特征里必须保留所有说话人的信息,前端不能只"听清一个人"。
长录音、杂来源。仓库模型表里 SOND 中文版用 AliMeeting 120 小时数据训练,不同房间、麦克风、距离、音量各不相同。同一段"你好",在这台设备上能量高、那台设备上能量低,特征分布对不齐,模型就没法学会稳定的判别规律。
已知参会者。SOND 除了音频还接收一段说话人画像(profile,即已知说话人的声纹嵌入)。模型用"当前声音和画像的相似度"辅助判断,这意味着同一条链路要同时服务音频特征和嵌入向量,两边的口径必须一致。
全流程鸟瞰:波形进来,"谁在说"出去
整条链路五个环节:前端提特征 → 训练时做数据增强 → utterance 级归一化 → 编码器与说话人嵌入对齐打分 → 解码出逐帧说话人组合。注意三个环节(增强、画像扰动、说话人顺序打乱)只在训练时生效,推理时是"裸链路"。
下面按数据流动顺序,四步拆完。
第一步:把混音变成 23 维 Mel 特征
编码器不吃波形,吃矩阵。FunASR 的 EEND/SOND 系列常配 WavFrontendMel23:librosa 分帧做 STFT,过 23 带 Mel 滤波器组,取 log10 能量。Mel 刻度模拟人耳对频率的敏感度不均(对低频更敏感、对高频更迟钝),可以理解为按这个心理刻度画出的频谱热力图。
| 参数 | 默认值 | 为什么这么设 |
|---|---|---|
| 窗长 | 1024 采样点(128ms @8kHz) | 太短频率分辨率不够,太长会把两个人的声音切换抹糊 |
| 帧移 | 256 采样点(32ms) | 帧间 75% 重叠,时间分辨率和算力各让一步 |
| Mel 带数 | 23 | 判别"谁在说"不需要 80 维那么细,维度省,编码快 |
| 特征形式 | log10 能量 + 按维减去整句均值 | 音量差异在这里先被压平一轮,等于免费的粗归一化 |
之后可选做帧拼接再降采样(lfr_m/lfr_n,默认 1 即不做)来压缩时间轴。易错点:eend_ola_feature.py 的 Mel 计算是按 8kHz 写的(对应英文 CallHome 版模型),中文 AliMeeting 版是 16k——采样率必须跟模型名对齐,别拿 16k 音频直接进 8k 前端。
第二步:训练时随机"破坏"特征——SpecAug 三件套
120 小时的语料终究有限。SpecAugment 的思路是故意把频谱图局部涂掉、扭曲,像把练习册故意涂花着练,逼模型不依赖某一片频率或某一刻的细节。实现在 funasr/models/specaug/,作用于上一步的 23 维特征:
| 变换 | 默认参数 | 模拟什么 |
|---|---|---|
| 时间扭曲 | window=5 帧,bicubic 插值 | 说话快慢不一;总帧数 ≤ 2×window 时自动跳过,防止短音频被扭曲过头 |
| 频率掩码 | 2 条,每条宽 0~20 带 | 信道/麦克风造成的频带缺失 |
| 时间掩码 | 2 条 | 声音短暂被遮挡、断断续续 |
说话人画像那一侧另有 profileaug 扰动,同样只在训练时生效。易错点:推理时 SpecAug 会被self.training自动门控跳过,不用手动关;但 CUDA 上时间扭曲的插值不可复现,如果你在对比两次运行的差异,先把apply_time_warp关掉再排查。
第三步:utterance 级归一化,把不同录音拉到同一基线
前端已经按维减过一次均值,UtteranceMVN 再补一刀更严格的:对每条语音的有效帧求均值并减去,把"这句整体响/闷"的直流偏移消掉,让不同录音的特征落在同一水平线上。
| 参数 | 默认值 | 改了会怎样 |
|---|---|---|
| norm_means | True | 关掉后句间能量差异重新进入特征,判别边界变飘 |
| norm_vars | False | 故意只减均值不除方差——相对响度对"谁在说"本身有信息量,除掉反而丢线索 |
| eps | 1e-20 | 防止除零,实际很少触发 |
易错点:链路里顺序是"先增强、后归一化"。如果反过来先归一化再打掩码,掩掉的区域会拉偏统计量,等于自己给自己埋噪声。另外归一化求均值时会自动屏蔽零填充帧,batch 里长短不一的音频不会被 padding 拖累。
第四步:重叠语音怎么变成标签——幂集编码
上一步产出的是特征,监督信号这边也要改造。普通分类标签"这一帧是 A"无法表达"A、B 同时在说",SOND 用 PSE(Power-Set Encoding):每个说话人对应二进制的一位——1 号说话人是 2^0,2 号是 2^1,"A、B 同时在说"就是对应位相加得到一个整数。代码里直接生成 2^0~2^15 的幂次权重表,与 0/1 帧标签相乘求和即可。
| 参数 | 默认值 | 为什么这么设 |
|---|---|---|
| max_spk_num | 16 | PSE 的词表是 2^16 种组合;上限必须 ≥ 实际可能同时说话的人数,小了会丢标签,大了分类头输出爆炸 |
| 长度容差 | ±2 帧 | 编码器卷积下采样后输出会比标签短一截,代码允许差 2 帧并截断对齐,再长就对齐不住了 |
易错点:训练时还会对说话人做"在线顺序打乱"(profile 和标签同步随机置换),防止模型记住"1 号说话人总坐左边"这类位置偏见。复现训练日志里的 DER 时,这个打乱本身不可复现,属正常现象。
训练 vs 推理:链路上的开关在哪
| 环节 | 训练 | 推理 |
|---|---|---|
| 23 维 Mel 前端 | 开 | 开 |
| SpecAug 三件套 | 开,每次前向随机 | 跳过(self.training门控) |
| 画像增强 / 说话人顺序打乱 | 开 | 关 |
| Utterance 归一化 | 开 | 开 |
| freeze_encoder | 可选,微调时冻住编码器 | — |
| 输出 | PSE logits + DER 分量(漏检/虚警/混淆) | logits,取 argmax 还原说话人组合 |
解码侧还会把逐帧预测展开成 0/1 矩阵,分别统计说话人漏检、虚警、混淆,合成 DER——训练时这些分量直接进 stats 日志,调参时比总 DER 更有诊断价值。
上手清单:跑之前确认六件事
- ⚠️ 先核对采样率与模型一致:英文 CallHome 版 8k,中文 AliMeeting 版 16k,前端口径跟着模型走。
- 训练保持 SpecAug 默认全开;怀疑结果不可复现时,先关时间扭曲再查别处。
- 推理不用手动关增强,门控自动生效;确认
max_spk_num≥ 你预期同时说话的人数。 - 标签和预测长度差 1~2 帧属卷积下采样正常现象,超过 2 帧再排查配置。
- 微调预训练 SOND 时考虑
freeze_encoder=True,只训打分头和解码器,省算力也防过拟合。 - 看日志别只盯总 DER:sad_mr(漏检)、mi/fa/cf 分量各管一段,分别定位问题。
延伸资源
- SOND 主模型与 PSE/DER 实现:funasr/models/sond/
- 23 维 Mel 前端与流式前端:funasr/frontends/wav_frontend.py
- SpecAug 模块(时间扭曲/双掩码):funasr/models/specaug/
- SOND 中英文模型卡片(参数规模、训练数据):model_zoo/modelscope_models_zh.md
下一步建议:挑一段含重叠语音的短录音,分别用训练态(开增强)和推理态各跑一遍前端,打印两次的特征 shape 和均值,你会直观看到增强与归一化各自改变了什么。
【免费下载链接】FunASROpen-source speech recognition toolkit for training, inference, streaming ASR, VAD, punctuation, speaker diarization pipelines, and OpenAI-compatible/MCP serving.项目地址: https://gitcode.com/GitHub_Trending/fun/FunASR
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考