说话人分离,我试了四套方案才搞定
我有个一直在跑的小工具,把语音备忘录自动转成结构化笔记:录完几分钟,整理好的文稿就自己躺在那儿了。用了大半年,转写本身早就不是问题,现在随便一个模型转出来都够用。一直没搞定的是说话人分离,diarization,就是分清楚哪句话是谁说的。我有一段 28 分钟的录音,从头到尾就两个人在聊,转出来给我标了五个说话人。讲道理,这挺离谱的。
这条路我前后换了四套方案:Gemini 切块、Senko、豆包,最后落在火山的妙记上。整个过程记一下,中间还有两个跟 diarization 没关系、但卡了我很久的坑,一起记下来给后来人。
第一版:Gemini 切块
最早的方案是 Gemini。长音频有个坑:整个文件直接丢给它,超过十几分钟它就开始不老实,跳段、循环、自己编时间戳。所以得先切块,按静音边界切成十来分钟一段,并行去转,让每段都在模型的清醒区间里。
转写质量没问题,但切块带来一个新问题:每一块是独立转的,每一块的说话人编号都从「说话人0」开始编。第一块的说话人0,到第二块可能就变成说话人1了,块和块之间编号对不上。
我做的补救是相邻两块留 60 秒重叠区,重叠区里同一句话在两块里各有一个归属,拿这个去投票,把编号对齐。大部分时候管用。但只要有一个缝合点投歪了,后面整条的编号全跟着错位,说话人数量就一路虚高。缝合点越多,出错的机会越多,反正这条路我后来是放弃了。
音频切块各自从0编号,靠重叠区投票缝合,一处投歪后面全部错位
第二版:Senko 全局分离
既然跨块缝合不靠谱,那就不缝了,对整段音频做一次全局 diarization。我接的是 Senko,在苹果芯片上跑 CoreML,比 pyannote 那套快几十倍。它确实比缝合稳,至少编号全程一致,不会转到一半换人。
但它有个改不掉的毛病:把短插话当成新人。真实对话里全是「嗯」「对啊」「然后呢」这种搭话,一个人在讲,另一个人时不时应一声。Senko 经常把这种一两个字的应答判成第三个、第四个说话人。28 分钟那段它好歹判对了 2 人,换几段长一点的录音,数字立马又虚高上去了。
中间试了一下豆包
这中间我去试了火山引擎的豆包,主要想看国产模型在中英混说上是不是更顺。转写质量确实是惊喜:中文、中英混说都很干净,带标点,口语也顺。这本来就是我最初的痛点,它解决得比 Gemini 那套漂亮。
但说话人这边更崩:极速版把那个 28 分钟判了 5 个人,2.0 判 4 个,比 Senko 还虚。
到这儿我大概看明白了:通用模型顺手做的 diarization,普遍偏激进,宁可多切几个人也不肯合并。它分不清「嗯」是搭话还是新人,干脆全算新人。
通用 ASR 把「嗯」这类搭话误判成新说话人,两个人被数成五个
坑一:火山有两套鉴权
试豆包的过程里卡了我大半天的,其实不是模型,是鉴权。
火山的语音服务有两套账号体系:老控制台给你一组 AppID + AccessToken,新控制台给你一个 x-api-key,而两套绑定的可用资源不一样。我一开始拿老的 AppID 去调 2.0 的接口,一直返回 resource not granted——配额明明有,就是不让用,怎么查都查不出问题。折腾半天才反应过来:2.0 和后面要讲的妙记,都得走新控制台的 x-api-key。换了鉴权头,立马就通了。
这件事文档里写得很隐蔽。如果你调火山语音接口也撞了「资源没授权」,先检查是不是认错了鉴权方式,大概率就是这个。
最后落在妙记上
真正解决问题的是火山的妙记(Lark Minutes ASR)。它跟前面几套的差别在于,diarization 是服务端做的,跟转写一次调用一起出结果,客户端什么都不用缝、什么都不用投票。
我拿 5 段真实录音做了对比,其中 4 段是两人对话:
| 录音 | 极速版 | 2.0 | 妙记 |
|---|---|---|---|
| 28min | 5 | 4 | 2 |
| 38min | 3 | 3 | 2 |
| 57min | 3 | 3 | 2 |
| 68min | 4 | 3 | 2 |
| 3.45h(剧集) | — | 10 | 3 |
四段两人对话,妙记每段都判 2 人,另外两家全程虚高。3.45 小时那段是个多角色的剧集音频,别家要么文件传不上去,要么判出 10 个人,妙记一次吃下来判 3 个。3 个不一定全对,但比 10 个靠谱太多了。
所以我现在的做法就是:diarization 别指望通用模型顺手给你做,直接交给一个专门做这件事的服务端模型。前面那些切块投票、全局分离,说白了都是在替一个不擅长这件事的模型做补救,模型本身判不准,客户端再怎么折腾也就那样。
坑二:妙记要公网链接,跨境上传 34KB/s
妙记有个硬要求:不收你直接上传的文件,只认一个公网能下载的 URL。所以本地录音得先传到一个对象存储上,再把链接给它。
我用了火山自家的 TOS(对象存储),桶开在上海。妙记从上海取文件是境内链路,飞快。慢的是另一头:我从美国往上海传文件,单线程 34KB/s,一个 8 兆的文件传了将近四分钟。这个速度显然没法用。
查了一下,跨境链路限的主要是单条连接的速度,带宽本身其实还有富余。所以解法很直接:并行分片上传,把一个文件切成多段同时传。换上之后,同样的文件 5 秒传完。
跨境单线程上传被限速到34KB/s,把文件切成多段并行传就能跑满带宽
整套换完之后,我那个转录器和对应的转写 skill,默认都走妙记了。录音丢进去,出来的文稿带着对的说话人标签,这事到这儿算是解决了。
如果你也在折腾会议录音的工具链,我之前还写过一篇开源方案横评,可以一起看。
