Skip to content

约 7 分钟阅读

10. 说话人识别与分离 ​

识别模型回答“说了什么”,没有回答“是谁说的”。会议纪要要把文字归到人,客服质检要分开坐席和客户,智能音箱想只听机主的话,都要额外的说话人技术。

“是谁说的”其实是几个不同的问题:

  • 说话人分离(diarization):这段录音里有几个人,各自在什么时间说话。它只给匿名编号,不知道谁是谁。
  • 声纹验证:这段声音是不是事先登记过的某个人。
  • 目标说话人 ASR(TS-ASR):在混合语音里,只把登记过的那个人的话转写成文字。

同一段两人交替并有重叠的音频,在 VAD、说话人分离、声纹验证、目标说话人 ASR 四个任务下的输出

这些任务的输出不能互相顶替,这是本篇最重要的一点。下面先讲它们共用的基础:声纹嵌入。

声纹嵌入:把一段声音变成一个向量 ​

说话人模型的核心是一个嵌入提取器:输入一段语音的 Fbank 特征,经过 TDNN 或 ResNet 一类的网络逐帧提取特征,再做一次统计池化(把整段的帧向量汇总成均值和方差),最后输出一个固定长度的向量,通常一两百维。

训练目标是让同一个人不同录音的向量靠近,不同人的向量分开。使用时,比较两个向量的余弦相似度:越接近 1,越可能是同一个人。

模型来源特点
ECAPA-TDNN2020 年论文长期的强基线,很多工具包都有实现
CAM++阿里 3D-Speaker,2023约 720 万参数,比 ECAPA-TDNN 小而准
ERes2Net / ERes2NetV2阿里 3D-Speaker,2023—2024多个尺寸,中文数据上训练的版本多
ResNet 系列、ReDimNetWeSpeaker 工具包社区维护活跃,覆盖多种结构

这些模型在公开测试集 VoxCeleb1-O 上的等错误率(EER)已经低于 1%,但不同论文的测试协议(是否清洗试验列表、是否做得分归一化)不同,只能在同一张表里比较。

EER 和阈值 ​

验证本质上是在相似度上设一个阈值。阈值调低,冒充者更容易通过(误接受);阈值调高,本人更容易被拒(误拒绝)。两种错误率相等的那个点就是 EER。

EER 只是比较模型用的单一数字,产品不会恰好工作在这个点上。门禁类场景要把误接受压得很低,宁可让本人多说一次;个性化推荐类场景则可以放宽。阈值要在目标设备、目标人群的录音上重新标定。

验证为什么在实验室准、到现场不准 ​

  • 录音太短:一两秒的语音里说话人信息不够,相似度波动大。
  • 信道不一致:登记用手机近讲,验证用会议室远场麦克风,向量会整体偏移。
  • 情绪、感冒、年龄变化:同一个人的声音本身在变。
  • 重放和合成攻击:录音回放、11 篇讲的声音克隆都能骗过只看声纹的系统。

所以声纹适合个性化和轻量身份判断,不适合做支付、开门的唯一凭证,这一点在 07 篇也提过。

说话人分离:谁在什么时候说 ​

分离要回答的是一整段录音里的时间线,有两条技术路线。

级联:切片、提嵌入、聚类 ​

传统做法分三步:先用 VAD 找出语音区间并切成短窗;对每个短窗提取声纹嵌入;再把所有嵌入聚类,同一类就是同一个人。早期的 x-vector 加谱聚类系统都是这个结构。

它的好处是模块可替换、人数不设上限。难点在两处:一是重叠语音,一个短窗里两个人同时说话,嵌入会落在两人中间;二是聚类需要看到整段录音才最准,天然偏离线。

现在常用的 pyannote.audio 和 DiariZen 是改进后的混合结构:先在每个几秒的窗口内用端到端分割模型判断“这个窗口里有几个人、各自何时说、哪里重叠”,再对各窗口里的说话人提嵌入、跨窗口聚类。窗口内能处理重叠,跨窗口不设人数上限。sherpa-onnx 的说话人分离也是这个结构(pyannote 分割模型 + 3D-Speaker 或 WeSpeaker 嵌入 + 聚类)。

端到端:模型直接输出每个人的活动 ​

端到端分离(EEND)让一个模型直接输出“每一帧上,第 1 到第 N 个说话人各自是否在说话”。重叠语音不再是特例,两个人同时说话就是两路同时为 1。

NVIDIA 的 Sortformer 系列属于这一类,用“按首次出现时间排序”的训练方式解决了“哪一路对应谁”的排列问题,并支持流式:

模型发布最多人数流式许可证
Sortformer 4spk v12024-124否CC-BY-NC-4.0,非商用
Streaming Sortformer 4spk v2.120254是NVIDIA Open Model License
Nemotron-3-Diarization2026-098流式和离线OpenMDW 1.1

端到端模型的人数上限写在结构里:输出只有 N 路,第 N+1 个人进来,模型只能把他并到已有的某一路。

匿名编号与跨窗口一致 ​

分离输出的 spk0、spk1 只在这一次会话里有意义,而且编号本身没有含义:同一段录音换个起点重跑,两个人的编号可能对调。流式分离还要保证同一个人在第 1 分钟和第 10 分钟拿到同一个编号,这需要模型或外部逻辑维护一个“说话人缓存”。

要把匿名编号变成真实身份,只能再拿每一路的声音和登记声纹做验证。直接把 spk0 当成某个固定身份(机主、坐席),是生产系统里最常见的错误之一。按出现顺序编号只说明谁先开口,不说明是谁。

常用方案 ​

方案路线适合注意
pyannote.audio 4 + community-1混合:窗口内分割 + 聚类离线会议、播客、通用基线权重需在 Hugging Face 申请访问,CC-BY-4.0
3D-Speaker 分离级联中文会议Apache-2.0
sherpa-onnx 分离混合:pyannote 分割 + 聚类端侧、离线只有离线模式
Streaming Sortformer / Nemotron-3-Diarization端到端实时会议、需要处理重叠有人数上限
DiariZen混合:窗口内分割 + 聚类研究对比代码 MIT,权重非商用

DER:分离错了多少 ​

分离的标准指标是分离错误率 DER,它把三类错误加起来,除以参考语音的总时长:

text
DER = (漏检时长 + 虚警时长 + 说话人混淆时长) / 参考语音总时长
  • 漏检:有人在说,系统判成没人说。
  • 虚警:没人在说,系统判成有人说。
  • 混淆:有人在说,系统也判对了“有人”,但归错了人。

重叠区也按人计:参考里两个人在说、系统只给出一个人,少掉的那一人份时长算漏检。

例如一段会议参考语音共 100 秒,系统漏掉 6 秒、多报 4 秒、归错人 8 秒,DER 就是 (6 + 4 + 8) / 100 = 18%。重叠部分按人分别计时:两个人同时说了 2 秒,参考总时长就算 4 秒。

DER 的数字离开评分设置就没有意义。要同时写清两个设置:是否留边界容差(早期研究常在每个边界两侧各忽略 0.25 秒,现在 pyannote 等工具默认不留),以及是否计入重叠语音。不留容差、计入重叠,DER 会明显变高。不同论文、不同模型卡的数字,只有设置相同时才能比较;参考标注的方式(人工标注还是强制对齐生成)也会影响结果。

DER 好也不代表转写归属正确。会议转写最终要看的是每句话的文字有没有归到对的人,句子跨越两个人时怎么处理,这些都要在业务层另外评估。

目标说话人 ASR ​

只想要某一个人的话时,有两种做法:

  1. 先分离再识别:分离出每一路,用声纹找出目标那一路,再把那一路的时间段送进 ASR。实现简单,但重叠语音里目标的话会被一起裁掉或混进别人的声音。
  2. 目标说话人 ASR:把目标的登记录音(有的模型也接受声纹向量,但不少模型只接受参考波形,只保存向量不够用)和混合语音一起送进识别模型,由模型直接只转写目标说的话。重叠时也有机会把目标的话分辨出来,但需要专门训练的模型,输入格式必须严格遵守训练时的约定。

和它相邻的还有两个任务:多说话人 ASR 一次转写所有人的话并按时间排列(如微软的 SOT 方法);带说话人归属的 ASR 在转写所有人的同时标出是谁。NVIDIA 的 multitalker-parakeet 则是另一种组合:不需要登记录音,而是用流式分离的结果作为条件,为每个说话人各跑一路识别。

目标说话人 ASR 有一个特有的验收问题:目标根本没说话时,输出必须为空。只测正例(目标在说)会漏掉“把别人的话当成目标的话”这类错误。一条生产链路怎样组织登记参考、分离证据和目标识别,怎样处理极短片段和重叠,见流式语音技术实践:角色证据与目标说话人识别。

总结 ​

说话人技术要先分清问题:分离给的是匿名时间线,验证回答“是不是这个人”,目标说话人 ASR 只转写指定的人。三者共用声纹嵌入,但输出互相不能顶替。评估时,验证看目标场景下的误接受和误拒绝,分离看写明设置的 DER,目标识别要同时测正例和“目标不在场”的负例。

下一篇:11. 语音合成,从“听”转到“说”。

别急,先让缓存热一下。