背景

前阵子女朋友去读研,授课是全英的,加之又有很多专业名词,有时就会出现理解能力跟不上讲课速度的情况。

因此借助课堂回放/录音复习也变成了一项每周必做的工作,但是完全回看一个三小时长的课程显然是不现实的,所以,音视频转文字就成了必选项。

方案选择

商用 ASR 服务大多难以实现高精度转写

我是飞书妙记的会员,所以遇到了这个需求,我马上想到先用妙记试试。

然而,尝试转录的结果表明,妙记在专业课程上的转录准确度相当差,无法满足通过文字转录来提高复习速度的需求。

妙记转录结果:词汇未转录、转录错误问题频发
妙记转录结果:词汇未转录、转录错误问题频发

其他商用服务(如通义听悟、讯飞听见、Notta 等)的转录效果和飞书妙记差不太多,大体原因有三点:

  1. 手机远距离收音比较差,音频文件质量不高。
  2. 一般的商业自动语音识别(ASR-Automatic Speech Recognition)服务主要面对会议等日常场景。但若音频内容含有过多的专业词汇,此类 ASR 服务则有点力不从心了。
  3. 商业 ASR 服务需在速度、准确性和成本之间取得平衡,高准确度通常需要以成本变高、速度变慢作为代价。

基于问题 2 和 3 ,我放弃了继续寻找其他商业 ASR 服务的想法。

Whisper 的惊艳效果

因为平时业务里我自己基于 OpenAI 发布的 Whisper API 写了不少工作流,所以我又试了试 Whisper(Large-v2)的转录效果。——非常惊艳,甚至连符号的写法(theta_i^t)它都转写了出来。

Whipser LargeV2 转录结果:精度高到甚至照顾到了符号写法
Whipser LargeV2 转录结果:精度高到甚至照顾到了符号写法

这里简要介绍一下 Whisper,Whisper 是 OpenAI(没错,还是 chatGPT 背后的公司)在 2022 年 9 月开源的音频转文本的模型,它的转写精确度非常高。

但想使用 Whisper 进行转写也并非易事。它有两种实现方式:云端 Or 本地。

云端转写的优势在于不会受到本地机器性能的限制,且速度相对较快。但它存在两个问题:

  1. 项目处理流程复杂:OpenAI 的 Whisper API 限制单次请求的音频大小为 25Mb,而一节 3h 的音频通常都会有大几十 MB。这就需要对音频先做分段处理,再请求结果,最后合并结果。如果是 mp4 文件则还需要从中抽取音频文件,这个过程里没少踩坑。
  2. 成本问题:OpenAI 的 Whisper 模型 1min 收费 0.006 美元,1h 的音频按照 7.3 的汇率需要收费 2.7 元。坦白讲,Whisper 的 API 价格非常便宜了,几乎只是 Google Speech2Text API 的四分之一。但是,如果我们假设有 5 门课程,每堂课长 3小时,每周有一次课,那么每个月的转写成本 = 5 x 3 x 4 x 2.7 = 162 元,这个价格还是有点肉疼。

本地转写的话倒是没有上述两个问题,但本地转写的麻烦之处在于:

  1. 笔记本上缺乏好的硬件:纯 CPU 模式跑 Whisper 速度非常慢,3 小时的音频可能需要十几个小时才能转写完毕。如果想用 GPU 加速,根据Whisper 模型显存需求表格,官方的 Whisper-large 模型需要 10G 显存,我这台核显本实在是力不从心。后续的 Whisper cpp 项目 倒是大幅降低了内存需求,但这也引出了第二个问题。
  2. 环境部署复杂:OpenAI 开放的毕竟是项目代码,自己写代码适配的成本还是有点高。Whisper 的 GUI 客户端在 Mac 上不少(Whisper Transcription、MacWhisper..),Windows 上也有 Buzz ,然而要找到一个支持 GPU 加速的客户端依然十分困难。

且不论是云端转还是本地转,上述方案只是实现了音频转文字的过程,但却少了一个直观的用户界面,帮助我们快速通过文字理解音频内容。

一个理想的转文字结果页面可以借鉴妙记的设计:

  • 左侧是音视频播放,而右侧是完整的字幕方便阅读。
  • 在点击字幕时,能立刻跳转到对应的音频位置进行播放。

Potplayer 的字幕浏览器倒是能变相实现这个效果,但它文本一长就被截断了,也就是勉强能用的程度。

PotPlayer 自带的字幕浏览器 Alt+E
PotPlayer 自带的字幕浏览器 Alt+E

Memo,可能是目前最好用的 Whisper 客户端

后来某天逛小众软件的时候发现了 Memo AI 的内测,它的功能刚好能满足我的需求。

  • 不论 Mac 还是 Windows,调用 Whisper 都支持 GPU 加速。哪怕核显本也能有不错的速度。
  • 结果页面既可以快速浏览全部转写内容,也可以点击字幕跳转播放。
  • 字幕支持一键翻译,双语对照更方便。
  • 进一步做了批量转写、AI 对话等功能。
Memo 页面截图
Memo 页面截图

这可真是刚想瞌睡就有人送枕头。有了 Memo 之后,下面就可以介绍我们整个转录工作的流程了。

操作流程

音视频文件来源

音视频文件主要有两种来源:学校提供的课程视频回放、手机/录音笔的录音结果。

课程视频回放下载

某些课程在 Canvas LMS 平台上提供了视频回放的功能,但却没有视频下载的选项。

不过网站视频的下载也并不困难,视频是基于 m3u8 的,并且没有加密。只需要获取到 m3u8 地址,随后使用 m3u8下载器就能把视频下载到本机。

至于提取视频url的工具,我推荐使用 xifangczy/cat-catch: 猫抓 chrome资源嗅探扩展。通过使用「猫抓」,可解决大约 80% 的网页视频下载问题。

m3u8 下载器则推荐 N_m3u8DL-CLI,一键搞定。

录音

没有提供视频回放的课程只能通过自己录音来解决。设备不外乎两种:手机或者录音笔。

至于具体选什么设备,这里得根据自己的情况来确定。

通常,手机的远距离收音效果比较一般。在课堂这样的场合,如果不能坐在前排,老师的声音往往会被其他噪音掩盖。

录音笔的效果通常会比手机更好,一般 100-200 元的价格就可以满足需求。

如果是部分留学&专业课程场景,不建议为了实时语音转文字功能而选择某些录音笔,可能会有三个比较大的问题:

  1. 实时转写准确度很差:从理论上讲,实时转写的难度更大,而非实时的商用 ASR 服务转写的准确度已经是那个德性了,那么在专业课场景下,实时转写几乎毫无用处。
  2. 人在境外可能会有网络问题:实时转写实际上是把录到的音频发送到制造商的服务器,服务器再把转译结果发送回来。但很多厂家的服务器都在国内,如果人在国外,可能经常会遇到网络中断的问题。
  3. App 的维护差:目前,主打免费实时语音转文字的录音笔通常都配有相应的客户端,但这些客户端的长期稳定性很难让人放心。像讯飞的录音笔就硬生生地拆出了三个独立的手机应用(极智、听见、语记),不同的录音笔只能连接特定的应用,而有的应用在 iOS 上已经下架一年多了。。

文件前置处理(可选)

降噪&响度均衡&人声增强

有时, 录音效果可能比较尴尬。这可能是由于手机的收音能力较弱,或者是录音位置与声源之间的距离过大。这种情况下,我们可能会遇到以下问题:

  • 在录音中,目标声源的音量过小,以至于无法听清楚。
  • 录音中存在大量噪声。
  • 录音中前后两部分的声音音量存在很大差异,导致播放时声音大小起伏不定。

因此,在进行转录前,最好先对音频进行一些处理,这可以有效提升 Whisper 转录的效果。我主要做三个操作:人声增强、降噪、响度均衡。

具体的软件在 Windows 上我使用 Pr,在 Mac 上我使用 Final Cut Pro。

Pr2020,选项和数值仅供参考。
Pr2020,选项和数值仅供参考。
Final Cut Pro, 选项和数值仅供参考。
Final Cut Pro, 选项和数值仅供参考。

去除大段空白

下载的课程回放视频通常都包含 20 至 30 分钟的课间休息时间,我通常会选择把这部分片段从视频里切掉。这么做有两个目的:

  1. 减小文件体积:一个 3 小时的回放视频大约占据 1.5G 的空间,移除 30 分钟的视频可以节省约 200M 的存储空间。
  2. 降低 Whisper 出现幻觉的可能:虽然 Whisper 的转写精度相当高,但它偶尔也会出现幻觉——即从某一时间点开始,转录文本会不断重复某句话。而大量的空白时间可能会触发这种幻觉。

手动删除空白时间段内容是一种很糙的解决方案,更优雅的方案是结合声音活动检测(VAD, Voice Activity Detection)来做转写,自动忽略掉空白时间段。(开发者说 Memo 的后续版本会接入 VAD)

至于切视频,可以使用 Pr/FCP 等剪辑软件,但这种方式需要重新编码处理后的视频,比较耗时间。愿意折腾一下的话此处建议使用 ffmpeg 通过复制流的方式来做,通常 3 小时的视频文件切掉空白只需几秒钟。

核心的命令为两个:

从视频里提取特定时间段

$$ffmpeg -i "{OriFileLoc}" -ss {RangeTimeStart} -to {RangeTimeEnd} -c:v copy -c:a copy "{OriFileParDir}\temp\temp_video_{temp_video_index}{OriFileExtension}"

利用 concat 衔接多个视频(格式一致)

$$ffmpeg -y -hide_banner -vsync 0 -safe 0 -f concat -i "{OriFileParDir}\temp\fileList.txt" -c copy "{OriFileParDir}\RmBlank_{OriFileName}{OriFileExtension}"

你可以把本地文件地址丢给 chatGPT 让他直接给你生成最终的命令,抑或使用 QuickCut: Your most handy video processing software 或者我编写的 Quicker 动作

确定 Prompt

Memo 里 Whisper 的转写选项
Memo 里 Whisper 的转写选项

Whisper 的 Prompt 和 chatGPT 的 Prompt 是两种东西

与传统的 ASR 模型不同,Whisper 在某些情况下可以借助 Prompt 提升音频转文字的准确度。

虽然同属于 OpenAI,但 Whisper 和 chatGPT 的 Prompt 差别非常大。

如果 Whisper 的 Prompt 是 "请尽力保持每句话的完整 并给每句话都添加标点符号",你会发现这句 Prompt 没有任何作用,Whisper 的转录结果里可能还是会出现大量中断的句子,或者转录结果无标点。

这是因为 Whisper 是在尝试学习 Prompt 里的风格(style),而非具体的指令。

那什么是 Prompt 的「风格」?

根据 Whisper 的 cookbookWhisper prompt 的 issues 和我的实际体验,Prompt 的风格可以表现在下面几个方面:

风格 1:Prompt 里是否包含标点符号。

Whisper 的转录结果有时会缺乏标点符号,这个时候你可以使用带标点符号的 Prompt 的来引导 Whisper 添加标点。

  1. 「生于忧患,死于安乐。岂不快哉?」
  2. 「请为转写的文本添加标点符号」

Prompt 1 是有意义的,Prompt 2 是无意义的。

不过 Whisper 也不是能每一次都学会 Prompt 的风格添加标点符号。实测在 Whisper-large v2 模型下,通常英文内容使用 prompt 可以为结果添加标点,中文内容则要看运气。

风格2:一些容易拼写错误的人名、产品名、公司名

这里比较好理解,不同的词汇可能会有同一个发音。你可以在 Prompt 里指定你希望的拼写格式。

比如一段对话里提到了张三和李四,而「张三」的音同「章三」、「樟叁」...

那 Prompt 里就可以包含 「张三,李四。」

风格3:是否包含一些填充词(filler words)

这里直接引用官方的说法来解释,翻译成中文会有点怪异。

The model may also leave out common filler words in the audio. If you want to keep the filler words in your transcript, you can use a prompt that contains them: "Umm, let me think like, hmm... Okay, here's what I'm, like, thinking."

风格4:简体中文和繁体中文

因为中文有两种,有的时候 Whisper 会把音频以繁体中文的形式转录。此时用简体中文来陈述 Prompt 大概率可以解决此问题。

一般建议 Whisper 的 Prompt 语言和转录的音频语言一致。即英文音频用英语 Prompt,中文音频用简中/繁中 Prompt。

风格5:通过对话风格的 Prompt 变相实现转录结果区分说话人

这个技巧来自于 prompt vs prefix in DecodingOptions ,有的时候会有效果。

单纯的 Whisper 模型并不能在转录结果里区分出说话人,想要区分说话人,一般会配合 Pyannote 来做。但在某些场景下,我们可以通过 Prompt 来变相实现区分说话人——每一句的转录结果都属于单个说话人,不会出现某一句的转录结果属于多个说话人的情况。

方法就是用一个对话式的 Prompt:

  • 「- How are you? - I'm fine, thank you.」
  • 「- 吃了没?- 吃了。」

播客这种场景可以试试。

Whisper Prompt 的其他 Tips

  • Whisper 的 Prompt 应该尽可能长一些,太短的 prompt 的风格难以学习。
  • Whisper 的 Prompt 只会使用 224 个 token,如果超过 224 个 token 则会默认使用最后 224 个 token。token 的概念可以参考这篇文章,但 Memo 的 Prompt 直接限制了字符串长度,这里就不用在乎 token 长度了,只需要尽量让自己的 prompt 完整输入到 Memo 输入框里就行。
  • Whisper 的 Prompt 最好可以和音频的源语言保持一致。

怎样快速生成 Prompt?

因为我主要转录的是课程内容,所以我的 Whisper Prompt 通常会使用这节课的关键词列表。如果课程有讲义 pdf 文件,我会直接把它丢给 Claude,然后:

summarize the file, response me with format like :"keyword1, keyword2, keyword3,keyword4.."

如果课程没有讲义,我会手动输入几个关键词,或者直接从课本里随便挑一段话作为 prompt。

转写

模型选择

Whisper 转写需要指定模型。模型越大,转写越精准,转写速度越慢。

Whisper 模型与说明
Whisper 模型与说明

GPU 加速

Memo 在 Windows 和 Mac 上都实现了 GPU 加速。

Windows

Windows 电脑如果是独显机器大部分应该没什么问题,部分显卡的测试转录速度如下:

RTX4090 显卡在 Large 模型下转录 1.2 小时音频时间为 7 分钟

RTX3080Ti 显卡在 Large 模型下转录 1.2 小时音频时间为 8 分钟

RTX3080Ti 显卡在 Large 模型下转录 0.4 小时日漫时间为 2 分钟

RTX3060 laptop 显卡在 Large 模型下转录 1.2 小时音频时间为 20 分钟

RTX3060 在 Large 模型下转录 1.2 小时音频时间为 12 分钟

6600XT 显卡在 Large 模型下转录 1.2 小时音频时间为 11 分钟

5700XT 显卡在 Large 模型下转录 1.2 小时音频时间为 15 分钟

如果你的电脑只有核显,那可能会遇到显存不足的问题。

但这里如果是 AMD 的核显,你可以尝试在 Bios 里调整显存大小。(高风险行为,谨慎操作)

游戏爆显存?显示器黑屏花屏?BIOS 没有调整选项?AMD 核显显存大小调整教程(UniversalAMDFormBrowser AMD 台式及笔记本改核显显存教程,核显还只有 512M?

我手里的笔记本是 R7-6800H,32G 内存,出厂分配 4G 作为 GPU 显存。在 Large 模型下,开启 GPU 加速后显存会占满,转录时长:音频时长大概等于 1:2 。

我尝试把显存开到 8G,跑 Whisper 时大概会占用 6G 显存,但速度相较于 4G 显存无提升。所以如果调整 Bios 的显存大小,通常调整到 4G 就足够使用了。

Mac

Intel 的机器我手上没有,主要分享一下 M 系列的机器速度。

M 系列的 GPU 加速倍数近似和 GPU 核心数成正比:

  • M1 Max 32 gpu core ,转录时长:音频时长 = 1:12
  • M2 Pro 16 gpu core,转录时长:音频时长 = 1:6

Mac GPU 核心数对比可参考 Mac processor comparison: M1 & M2 vs Intel | Macworld

使用 Whisper GPU 加速时,除了消耗 GPU,同时也会消耗大量内存。我用 Large 模型时 Memo 占用 6.4 G 内存。

Whisper Large GPU 加速时内存占用 6.4G
Whisper Large GPU 加速时内存占用 6.4G

转写结果优化处理

Whisper 原始转录结果
Whisper 原始转录结果

如果是在为视频创建简易的字幕文件,那到这里基本可以收工了。但如果是在为需要快速阅读全文的课程或会议等场景进行操作,那么此处可以对转写结果做一些优化,主要解决两个问题:

  • 有的句子被拆分成了两行,调用翻译的时候不方便。(比如 00:15-00:19)
  • 每行的字数太短,读起来很累。最好是有个段落模式,每个时间点的内容多一些。

开发者说这两个功能未来的版本都会实现,但在没实现之前只能靠我们自己来处理了。我写了一段 Python 的脚本将原始的字幕转换成段落模式的字幕:Adjust_SubTitle: 调节 Whisper 转录生成的 srt 文件,避免一句话被分成两行,避免一句话过短。

你可以导出原始转录的 srt 文件,然后修改 AdjustSrt.py 里的 srt 文件路径,运行脚本后会自动在原始路径下生成段落模式的 srt 的字幕。最后只需重新创建 Memo 任务,同时指定需导入的音频和字幕文件即可。

合并句子成段落

因为段落模式的实现逻辑是基于原始转录结果里的标点符号,所以运行脚本的前置条件是 Whisper 生成的字幕里有标点

除此之外,基于标点符号的段落模式只能用于只有一个说话人的情况。否则可能会导致一句话里出现了多个说话人,容易造成误解。

潜在问题与更多场景

Whisper 的不足

虽然 Whisper 结合上下文的能力很强,转写准确度非常高。但 Whisper 也有两个比较麻烦的问题:

令人头疼的幻觉问题

所谓「幻觉」是指 Whisper 转录的文本和音频不相关,而是在不断重复之前的转录结果。

Whisper 幻觉:重复某一句话
Whisper 幻觉:重复某一句话

我体验下来目前主要是两种情况会造成幻觉:

  1. 大段的空白内容。
  2. 低录音质量,有比较大的噪音。

1 比较好处理,Memo 之后会引入 VAD 自动过滤空白内容。

同时根据 Ignore repeated prompt by heimoshuiyu · Pull Request #1253 · openai/whisper 这个 issue,设定 --condition_on_previous_text False ,也就是不让 Whisper 用前文的转写结果转录后面的音频,也能降低空白引起幻觉的概率,但代价是丧失了 Whisper 在上下文理解上的优势。(开发者说后续会开放此参数。)

但 2 目前还没找到比较好的解决方案,只能是再尝试对音频调整降噪、人声增强的强度试试..

薛定谔的标点符号

从天梯榜上看,Whisper 的英语转录效果名列前茅,而对于中文的转录效果在中等水平。

上文提到过,如果是英文音频,那合适的 Prompt 大概率可以给转写结果添加标点,但中文音频则有概率无法添加标点。此时就比较尴尬了,我也没有找到比较好的解决方案,如果有朋友知道怎么解决,欢迎评论区里分享。

我曾经尝试利用 gpt 3.5 来给生成的文本添加标点符号,但由于 gpt 也存在幻觉,它经常直接将文本合并,然后时间戳和文本就对不上了。。如果用 4 的话成本则有点高。

Whisper 在不同语言的转写效果,数值越低越好。

适合使用 Whisper 的人群

  • 对音频转文字有强需求,同时愿意用更长的时间换更高的转写精度。
  • 对隐私敏感,音频不能传到互联网的场景。
  • 手里有不错的电脑设备(16G 内存+,能开启 GPU 加速)。

希望这篇分享对你有所帮助,以上。