项目地址如下:
dwsjoan/SRAS: Speech Recognition and Simple AI Summary:可用于本地语音转文字、说话人分割及简易的AI总结,搭配web端操作界面。 (github.com)
这个项目的起源是堆砌模型希望能实现会议语音识别+ai总结任务,考虑到语音转文本后区分说话人能让AI更好地理解会议内容所以再加一个说话人识别。最后发现搭了半天不如funasr一套下来。写个博客回顾一下整体过程,想到哪写到哪,比较乱。
首先去找了语音识别模型,语音转文本结果的质量是一切的基础。在github上看到了stt项目,项目用的fast-whipser并搭了一个前端界面。试了一下,fast-whipser确实很快,好像算是领军模型了。但结果会有很多繁体字,而且是按句识别的,句子和句子之间的标点要么没有要么瞎打,需要后期组合长句并添加标点。所以任务list又新加两项繁简转化和长句合并加标点。尝试使用initial_prompt "以下是普通话的句子,这是一段语音记录。" 得到的标点结果也不太好,但是,加了initial_prompt之后结果确实比加之前好。之前看到过加initial_prompt "以下是普通话内容,请转录为中文简体。" 来改进繁体字问题,但是实验中发现使用此prompt之后模型会直接把转录成繁体字的大段文本删掉。此外,whisper实验看出来的两个硬伤1.识别中文结果可能会出现奇怪的大段单词重复,这段文本会覆盖该段该对应的识别结果。2.他会把中英混合的语音里的英文直接翻译成中文。
仅fast-whipser语音转文字生成的文本对于AI会议记录还是有点匮乏,最好能区分说话人,用以获得更明确的信息。github上找到了一个完整的项目,whisper-diarization,用的whisper语音转文本和NeMo说话人识别。但测试下来观察到NeMo的存在两个问题,一个是结果的时间戳可能不太准确,可能识别到的某个句子的时间区间并不对应于完整的一个句子,这个还好解决一点,比如whisper-diarization的默认初始解决方案就是start时间戳在哪个区间就认为是哪个speaker。第二个问题是最麻烦的,NeMo有一些句子压根识别不到,所以该句子对应的一整个时间段NeMo是一点没给说话人识别结果,而whisper-diarization则是默认如果句子没有给出说话人,那就按前一个句子的说话人来。
看了whisper-diarization的代码,感觉亮点在于后面用了标点模型结果和单词级说话人识别结果结合,重新对每个单词的说话人进行了修正,按标点模型结果划分句子,然后取句子里所有单词的speaker,哪个speaker占最多这句话就分给哪个speaker。但他使用的标点模型好像是直接给出长句中每个单词可能对应的标点,不支持中文,其他中文加标点模型则需要进一步处理结果,而且这个方法修正不了NeMo无标注的句子的错误划分。
然后又去看了WhisperX的代码,他的整体思路是,先Silero做vad然后Whisper做句子级别的语音转录,然后结合wav2vec2模型做一下单词级的时间划分,完事儿用pyannote-audio的v3.1做一下语音人声划分,最后用语音人声划分时间戳和语音转录的时间戳结果综合一下,每一句话和语音人声划分的哪个部分重叠最多,就分配给哪个人,只不过他同时做了句子和单词级别的说话人分配。
实施起来,最大重叠判定说话人和单词start时间/音频中段时间在哪个区间就判断是哪个说话人,感觉前者更靠谱,就用了前者的说话人对齐策略。剩下的就是决定用哪个语音人声划分模型。查到目前两大巨头,就是NeMo和pyannote。pyannote可以提前设置说话人数,但是他的3.1模型需要access token申请,虽然操作不复杂,但不知道为什么需要不停地重复申请,所以被pass掉。
到目前为止最主要的模型其实就定下来了,Fast-Whisper(内含Silero VAD)和NeMo,剩下的就是搞繁体字转简体和加标点。
繁体字转简体用的opencc,加标点的话,因为punctuate-all模型不支持中文,所以针对中文的加标点模型我在github上翻了翻,锁定了仨,funasr、PunctuationModel、zhpr。Funasr是阿里开源的,里面加标点模型也封装好了直接用就行,PunctuationModel和zhpr好像都是个人开发的,PunctuationModel能自己建数据集来训练模型,我找了ACL-duconv,DuRecDial,KdConv和Neural_Topic_Models四个对话数据集来训练(比起其他奇怪的对话数据集来说质量算不错的了,有些数据集内容都太抽象了),结果训了一下午训出来了不能使,bug调不出来。funasr和zhpr对比了相同句子的标点添加效果,感觉funasr加标点会更保守一点(就是加的少一些),但是又在一些国人口头禅上的分割效果不错,考虑到语音对话内容肯定更口头一点,所以选了funasr。流程就是同一个speaker的连续句子会被拼接然后加标点。此外,whisper对英文加标点能力是不错的,对比了一下同一段音频直接使用whisper的标点、删除whisper标点后使用punctuate-all重新加标点,whisper在合理的地方大多都加了标点而且更加合理。所以最后标点修正也成了一个用户选项。
但是这么一套下来,whisper识别出的错字是纠正不了的,比如“毕设”他会识别为“必设”,因为他对中文还是缺乏大量训练数据。
实际上还试过用llm(ERNIE Speed)同时做繁转简、加符号和错别字修改。但是他仍然会有返回其他多余文本的情况(也许是prompt没有调到最好),没办法完全剔除,而且根据他的理解对句子进行修改后有可能改掉一些不应该改的内容,所以后续就删了。说到底任务不够小,想让他一下处理的东西太多了。
所以到了后期最大的问题就是,1.whisper转文本错别字太多,2.NeMo语音人划分有些句子根本没识别出来也没划分结果。先看后一个问题,对于一些NeMo压根识别不出来人声的句子,本来想着要用标点添加模型通过取前后几句话然后加标点,然后看看这句话应该属于上一个speaker还是下一个speaker,但是现实是有些句子应该是属于上一个speaker的,但也确实和上一句应该有“。?”这类断句的,所以行不通。这时候又想到llm了,我句子繁转简了标点也加了,只对于一些没有归属的句子让llm判断一下他和前一个句子是不是用一个人说的,至少有一定语义信息能用来判断的。结果不够好,有些和前一个是同一个说话人,llm仍然会划出来。而且就算能百分百准确判断,我们仅能知道本句和上一句是不是同一个人,假设不是同一个人,没法知道说话者是其他人中的哪个,刚开始寻思着对付对付吧直接写成在剩下的人里随机取,后来想了想也太离谱了。再之后改成把已判断说话者的上文(包含了每段内容和每段的speaker,从末尾数向上15段,以说话者切换作为段的区分。因为考虑到全部上文都传给他有可能文本量太大)给AI让他把句子分给某个人,返回json格式直接取speaker,try except捕获错误,如果同一句话超过五次AI返回的回复格式都错误,就直接默认分给最后一个人。然后,效果还是很拉,遂删除。
至于前一个问题,后续修正没什么很好的方案,不如直接换模型,whisper留着但是针对中文选择比whisper更适合的模型。找中文为主基调的语音识别模型,去github找了四个最热的,FunASR,PaddleSpeech,qwen-audio,masr,除了PaddleSpeech其他都在本机上跑着试了试,masr太慢了结果也不太好,qwen-audio只能处理30s音频,PaddleSpeech需要配固定版本PaddlePaddle,而且处理音频的时长也有限制,FunASR试下来结果最好,对于中文一些词的识别结果比whisper更准确。
后面就是并行问题。whisper直接就能并行。funasr不能并行,只能一个一个来。网上有通过队列来解决的,设定创建pipeline实例的数量,一个实例2GB显存,每个线程等着取队列中的实例用,实例都在用就阻塞,但是一个不停CUDA out of memory一个是创建funsar的pipeline导致时长增加很多,本来做成了分支,如果拖拽进来多个文件且用funasr就搞pipeline那一套,如果只有单个文件那就直接上automodel。测试了以下,同样两个音频,直接用funasr一个一个来总共处理时间90s,用pipeline并行之后125s,决定还是多放一个处理多文件但不并行的选择,所有线程共用一个funasr auto模型,用线程锁控制一个一个用,在我本机上是比pipeline快。NeMo借鉴WD里的方法改成用subprocess.Popen跑单独的文件,谁用谁跑,正好NeMo是生成文件后面是读文件取结果,只要并行的时候用key把生成的文件区分开就行了,可以和whisper并行跑。
所以整体上,就是可选funasr还是whisper,funasr用了他的VAD+说话者划分+标点添加模型,whisper这边就是可以处理中文也可以处理别的语言但是对中文结果不如funasr。
最后结果直接给llm(ERNIE Speed或者gemini1.5flash),第一次给他结果让他给出音频类型和该音频应该总结什么,第二次让他根据音频类型和总结需求给出总结。效果比直接让他总结好一些,但是整体看一般,不同音频的总结重点不一样,同一段音频不同的人对他的总结需求也不一样,这个他很难把握。gemini1.5flash确实比ERNIE Speed好很多,以至于需要专门针对ERNIE Speed改prompt但是效果还是不好。第一次让返回音频类型和总结需求因为我要应用到summary的prompt里,所以让以json格式返回,俩模型都可能格式有问题,所以搞了个while不返回正确格式就一直重新回复。
后续的话可以在前面加个去噪模型,很多会议录音太嘈杂了,对结果影响不小。还有AI总结的部分也可以再改改。
版权归原作者 对我是 所有, 如有侵权,请联系我们删除。