3 步跑通 WhisperLiveKit Sortformer 实时说话人区分
【免费下载链接】WhisperLiveKitReal-time, local speech-to-text with streaming ASR, speaker diarization, translation, and OpenAI/Deepgram-compatible APIs.项目地址: https://gitcode.com/GitHub_Trending/wh/WhisperLiveKit
周二下午的产品评审会,五个人围着桌子抢话,会后你只拿到一份不分归属的文字稿,想确认"那句话是谁说的"只能回去翻录音。WhisperLiveKit 的 Sortformer 说话人区分功能把这件事变成实时的:转录流里每一行都带一个说话人编号,边说边出标签。
Sortformer 在解决什么问题
Sortformer(默认模型nvidia/diar_streaming_sortformer_4spk-v2)本质上做的是逐帧说话人分类:把音频流切成 1 秒的块,对每一帧输出 4 个说话人的概率,并在块与块之间维护一个"说话人缓存",让同一个人整场会议保持同一个编号。这和"录完一整段再全局聚类"的离线做法是两条路线。
| 维度 | 传统做法 | 本方案做法 |
|---|---|---|
| 处理方式 | 整段音频离线批处理 | 流式,每 1 秒音频走一次推理 |
| 标签延迟 | 录音结束后数分钟 | 边说边出标签 |
| 说话人编号 | 全局聚类,长音频中 ID 可能漂移 | 188 帧增量说话人缓存,会话内编号稳定 |
| 运行门槛 | 常依赖 HF 账号下载模型 | NeMo 本地加载,离线可用,最多 4 人 |
顺带一提,它输出的是匿名编号(speaker 1、speaker 2),不关心这个人叫什么——名字映射需要你自己在外层做。
一条音频块如何变成说话人标签
按数据流拆成 4 个阶段,对应 sortformer_backend.py 里的实际代码路径:
- 音频累积分块:16 kHz PCM 经 WebSocket 进来,缓冲满 1 秒(chunk_len 10 帧 × 下采样 10)才发出一个块;静默期用
insert_silence补偿全局时间偏移,保证区分时间轴和转录时间轴始终对齐。 - Mel 特征提取:
AudioToMelSpectrogramPreprocessor(25 ms 窗、128 维 Mel)把音频块转成频谱图,再拼接上一块末尾的 99 帧(约 0.99 秒)作为左上下文,块与块之间不留信息断层。 - 流式推理:
forward_streaming_step逐块前向,流式状态里带着 188 帧的说话人缓存(spkcache)和最近语音的 FIFO 队列,输出逐帧 4 分类概率;缓存每 144 帧刷新一次。 - 片段合并与 token 对齐:逐帧取 argmax,把相邻同说话人帧合并成
SpeakerSegment(start, end);tokens_alignment再让每个转录 token 按最大时间重叠匹配到某个片段,得到一个 1 起编号的说话人,前端输出"编号 + 文本"的行。
图中区分模块与 ASR、翻译流水线并行运行,它的片段输出只被下游对齐模块消费,不参与文本生成本身——这也解释了为什么关掉转录仍能单独拿到区分结果。
本地跑通实时说话人区分
🚀 最短可运行路径分 3 步:
1️⃣克隆并装依赖:git clone https://gitcode.com/GitHub_Trending/wh/WhisperLiveKit,进入目录后uv sync --extra cu129 --extra diarization-sortformer。完成后启动服务不会抛缺 NeMo 的 SystemExit(GPU 环境加 cu129 extra,纯 CPU 只装 diarization-sortformer)。
2️⃣启动服务:uv run wlk --model small --diarization --language en。完成后 8000 端口出 Web UI(Docker 用户直接docker compose up --build wlk-gpu-sortformer)。
3️⃣录音验证:打开 localhost:8000,让两个人交替说几句。你会看到每行文本带上 speaker 1 / speaker 2 标签,交替时编号切换,且标签跟着人走而不是跟着句子走。
不同说话人的文本行用不同颜色区分,行首编号与录音里实际的语音交接点一一对应。
Sortformer 参数怎么调
这些参数目前写死在_load_model里,没有暴露成 CLI 参数;想调就得改 sortformer_backend.py,或者用--sortformer-model-path指向自己改过配置的.nemo文件。
| ⚙️ | 参数名 | 默认值 | 可调范围 | 调大 / 调小的后果 |
|---|---|---|---|---|
| chunk_len | 10 帧(约 1 秒/次推理) | 5–15 | 调大减少推理次数、省算力,但标签出得慢;调小实时性更好 | |
| chunk_left_context | 10(代码实际拼接约 99 帧 Mel) | 5–15 | 调大边界判断更稳,单次推理计算量上升 | |
| spkcache_len | 188 | 150–250 | 调大说话人记忆更长、长对话编号更稳;调小易换号 | |
| spkcache_update_period | 144 | 100–200 | 调小缓存更新更勤、更准但更耗算力;调大长对话易串号 |
如果只调一个:追低延迟先动 chunk_len,追长对话编号稳定先动 spkcache_len。
说话人区分延迟和混淆怎么办
现象:启动即 SystemExit,提示 "Sortformer diarization requires NeMo"。原因是只装了基础包,没装区分依赖。处理:uv sync --extra diarization-sortformer,或pip install "whisperlivekit[diarization-sortformer]"。
现象:首次运行长时间无输出,像卡死。原因是在从 Hugging Face 拉取diar_streaming_sortformer_4spk-v2模型。处理:内网/离线环境提前下载.nemo文件,用--sortformer-model-path指向本地路径。
现象:超过 4 个人时编号混乱、互相抢标签。原因是模型按最多 4 说话人训练(4spk),说话人缓存也只保留 188 帧的历史。处理:场景控制在 4 人以内;超长会议里偶发换号属于该架构的已知边界,不是配置错误。
这个功能适合什么场景
适合:2–4 人会议、访谈、播客录制这类需要实时"谁说了什么"的流式转文字带说话人标签场景,且环境能本地跑 NeMo(GPU 优先)。
不适合:4 人以上的大场;需要识别"具体是谁"的场景(只能拿到匿名编号);以及纯 CPU 且负载重的机器——每 1 秒一次的推理在低算力下容易追不上实时。
编号和文本从同一条流水线出来,这是 Sortformer 说话区分的核心价值:先试试把 spkcache_len 提到 250 再录 30 分钟会议,看编号还稳不稳。
【免费下载链接】WhisperLiveKitReal-time, local speech-to-text with streaming ASR, speaker diarization, translation, and OpenAI/Deepgram-compatible APIs.项目地址: https://gitcode.com/GitHub_Trending/wh/WhisperLiveKit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考