音不逮而文不至

缘由

笔者身为文科专业的学生,教授上课磅礴的表达欲和屏幕上贫瘠的PPT的鲜明的对比,经常使我非常苦恼:屏幕上的PPT就如清汤寡水般对课程的学习和期末的复习起不上太大的帮助,然而教授飞快的语速和庞杂的内容又令人难以分辨哪里的内容值得记下、哪里只是课堂内容的补充。除此之外,受限于拼音输入法的重码率,即使把键盘敲得火星四射,也难以跟上教授的语速。仔细思考可选的解决方案,比如疫情期间给未到校的同学录视频,这或许是个方法,但是需要一台相机以及三脚架,更别提教授们有的也会对这黑黢黢的镜头产生恐惧影响课程的质量,而且课堂上的信息高密度的集中在教授的语音之中,只有很少一部分分布在光学信息中,所以录得的视频中的绝大部分的信息都是没有必要的,反而最重要的音频却处在次等公民的地位了。
因此我选择把教授的语音信息记录下来,所以选择了索尼的录音笔 PCM-D100,能够尽量还原我在课堂上听到的声音。不过仅有音频是不太够的(如果足够的话,也就没有写这篇文章的动机了),我回想起「网课时代」腾讯会议中的一个云回放功能,就是在保留了会议画面和声音之外,还自动为音频转写了字幕,并且可以通过字幕来快速检索视频内容。当我第一次使用这个功能的时候就不由得赞叹:「这也忒方便了!」
试想一下,我正准备补充我的笔记,但面前摆放着的是两个多小时的录音,那么我必须凭借课上的记忆来检索音频,并手动转写我所听到的内容。而现实是就算在理想的情况下,即课上的每一分钟都集中精神听课且都有印象的留存,我也几乎无法直接定位到想要的内容,需要借助二分法和上下文推理才能找到。更常见的情况是,两个小时中会有断断续续走神,凭借记忆来检索音频会有不小的难度,而且由于印象之间并不连贯,所以很容易漏掉其中的内容。这样就体现出自动语音识别转写的作用了。我可以通过 Aegisub 读取字幕和音频文件,通过文字快速检索上下文确定我想要的内容。美中不足的是,专业术语越多的音频转写的效果就越差(比如笔者上的古代汉语、古代文学等),不过即使是同音字,这也比单纯的用记忆检索音频要更准确、更有效率。

分析之后,就应该设计工作流了。

录音

录音设备是 Sony PCM-D100 ,选择的录制模式是最高档 LPCM 192kHz/24bit,为了尽量保存音频的细节,防止内容丢失。

即使是最高档的 LPCM 192kHz/24bit 立体声,码率也不过 10mbps,一般的SD存储卡也毫无压力。存储卡的速度等级可以参考金士顿的《SD卡和microSD卡速度等级指南》[1]

Sony PCM-D100 录音模式 LPCM 192kHz/24bit
Sony PCM-D100 录音模式 LPCM 192kHz/24bit

不过就算如此录得的音频的体积也是稍微有些夸张的。由于 Sony 机器文件系统的限制,单个音频文件大小不能超过 2GiB 。一堂三节课的大课大概是两个小时,最终会得到3段满时长的文件和1段剩下的小尾巴。7~8GiB的体积是很正常的。得到的音频文件虽然大,但是记录的信息是十分丰富的。我有一些强迫症,音频里的噪声我总是想要去除,这时候就得有请强大的RX 7 了。

iZopote RX 7 Audio Editor
iZopote RX 7 Audio Editor

关于 Rx 7 的强大在这里我就不多赘述了,网上有很多的教程讲述如何使用它的AI降噪功能。这里就简单说一下我会使用的几个功能。

  • 最好用当然是 Repair Assistant ,它会自动检测音频中的问题,如果音频中不存在太大的毛病,这个功能便足矣。
  • 其次就是 Voice De-noise ,它会自动学习音频中的声音模型,然后针对此段音频的特点来进行降噪。

如果 Rx 7 仍然不能消除噪音对语音的影响,可以试试 Adobe Audition 的自动降噪、减少混响和滤波器,虽然操作完之后语音会变得不自然(像语音聊天软件中的过度降噪一样),但是吐字至少能听得清楚。在降噪这方面,其实也无需多言,它只是工作流中的一个小环节,没有必要耗费太多的时间(如果有细致的修音需要,互联网上有很多比我更加专业的教程可供查阅)。接下来的语音识别才是重头戏。

whisper

2022年11月底以来,OpenAI 这公司总是给我们不少的惊喜。它造出了火遍全球的 GPT-3.5 a.k.a. "ChatGPT"以及最先进的大语言模型 GPT-4,还顺手开源了目前最好的离线语言识别引擎 Whisper 。我的工作流中的语音识别部分也正是基于 OpenAI 的 Whisper。如果没有 OpenAI 我们该怎么办啊(悲

我为什么选择离线语音识别?
  • 隐私与数据可控
  • 在线语音识别或者语音识别 API 中奇奇怪怪敏感词屏蔽
  • 在线付费——NO~;离线免费——YES!
  • 效率相差无几

笔者在设计工作流的时候发现了3个 Whisper 引擎的开源项目:

OpenAI 官方虽然设计并开发了 whisper ,但是在软件实现上却是最差的。这主要是因为 whisper 的软件部分使用 python 编写而且模型没有做任何优化,导致转写速度慢而且占用资源大。尤其是使用 OpenAI 官方的命令行软件尝试在 cuda 加速模式下运行 large-v2 模型时,一般的显卡(比如笔者笔记本上的 RTX 3070)会直接爆显存(官方文档中注明需要超过 10GiB 显存,这至少需要 RTX 3080 及以上的显卡)而无法运行。

OpenAI Whisper
模型类型参数数量英语模型多语言模型所需显存相对速度^✝
tiny39 Mtiny.entiny~1 GiB~32x
base74 Mbase.enbase~1 GiB~16x
small244 Msmall.ensmall~2 GiB~6x
medium769 Mmedium.enmedium~5 GiB~2x
large1550 MN/Alarge~10 GiB1x

✝ 即音频时间与处理时间的倍率
数据引用自: openai/whisper

而 whisper.cpp 使用 C++ 重构了 whisper,程序运行效率比原版要高出不少,但是有两点严重的问题:(1)whisper.cpp 只能使用CPU进行计算,无法使用 GPU 加速;(2)whisper.cpp 只能输入 16kHz/16bit 的波形音频(.wav文件),无法处理更高采样率和位深度的音频,转写的准确率会有所降低。

笔者目前认为最为合适的是 whisper-ctranslate2 。它是基于 faster-whisper 开发的命令行软件,而 faster-whisper 使用 ctranslate2 重构了官方的 whisper 程序,并且将官方的模型进行了一定的优化。虽然模型的文件体积变大(官方的large-v2.pt模型为 3.0GiB 而 faster-whisper 的faster-whisper-large-v2.bin模型超过了 5GiB ),但是在 cuda 加速模式中,显存的占用大为减少。

CUDA加速模式下的 Large-v2 模型
项目浮点精细度所需时间最大显存占用最大内存占用
openai/whisperfp164分30秒11325 MiB9439 MiB
faster-whisperfp1654秒4755 MiB3244 MiB

运行在 NVIDIA Tesla V100S(CUDA 11.7.1)
数据引用自 faster-whisper

在所有变量一致的情况下,不同项目的准确率是相同的,只有效率是不同的,因为其他第三方项目的模型都是基于官方模型转换的。下图为官方 large-v2 模型处理不同语言的偏差值(越低越好)[2]

从表中实际上可以读出,似乎 Whisper 转写中文的准确率不是很高,但是需要考虑到 OpenAI 仅使用了68万小时的中文语料来训练 Whisper 相比其他类似产品需要几百万小时来训练,能达到这样的准确率已经足以体现出大模型的优势。

Buzz

如果转写的音频时长不长,笔者在这里推荐一个将 openai/whisper 和 whisper.cpp 封装起来的图形界面软件:chidiwilliams/buzz。Buzz 虽然仅支持 CPU 计算,但是可以跨平台使用,兼容 macOS / Windows / Linux,也支持麦克风输入的实时语音识别。

Release 下载地址: https://github.com/chidiwilliams/buzz/releases

  • Whisper: OpenAI官方提供的 whisper 模型。
  • Whisper.cpp 模型相比于官方模型处理速度要快,但是由于采样率和位深度的限制精度不甚理想。(似乎在 Windows 的版本中并不支持 Whisper.cpp)
  • HuggingFace: Buzz 支持 huggingface 上的 whisper 兼容模型,这些模型都对不同的语言进行了微调。只要输入仓库名,buzz就可以自动下载,第二次使用时会自动读取本地的缓存。
  • OpenAI Whisper API: Buzz 支持 OpenAI 的在线 API,不过笔者并没有测试过,所以就不详细说了。

whisper-ctranslate2

如果你像笔者一样有一台搭载着英伟达(Nvidia)中高端显卡(具体地说是显存大于 4GiB)的游戏本,你就可以体验数倍于 openai/whisper 转换效率的 whisper-ctranslate2。

可以打开任务管理器查看自己的显卡是否是较新的英伟达(Nvidia)版本,以及查看专用 GPU 内存是否大于 4GiB。

任务管理器截图

准备工作我们需要去微软商店下载 Windows Terminal 方便接下来的命令行操作
👉 打开微软商店

除此之外,还要下载以下内容:

或者

使用笔者的OneDrive网盘进行下载。

1.安装Python

首先安装 Python3.10.11

Tip

注意勾选 Add python.exe to PATH,点击 Install Now

2.安装CUDA和CUDNN

然后安装cuda_12.1.0_531.14_windows.exe

打开目录C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.1
cudnn-windows-x86_64-8.8.1.3_cuda12-archive.zip压缩包中的binincludelib文件夹拖至空白处,将各自文件夹中的内容拷贝进各对应的文件夹内(如果有重复文件,请选择替换文件)。

打开 Windows Terminal
输入 sysdm.cpl 打开「系统属性」 -> 「高级」 -> 「环境变量」,在「系统变量」中编辑Path,添加两行:

  • C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.1\bin
  • C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.1\lib\x64

3.安装&使用whisper-ctranslate2

输入命令 pip install -U whisper-ctranslate2 使用 python 包管理器 pip 安装 whisper-ctransalte2
安装成功后,直接键入 whisper-ctranslate2 可以查看使用文档。

例子中的命令
whisper-ctranslate2 "D:\录音\专业课录音\古代文学\第8周\第8周 - 柳永、秦观\第8周.opus" --model large-v2 --device cuda --verbose True --language Chinese

"D:\录音\...\第8周.opus": 要识别的音频文件
--model: 所使用的识别模型
--device: 计算的设备,cuda 即使用英伟达的显卡计算,还可以使用 cpu 等
--verbose: 是否展示处理细节,布尔值,TrueFalse
--language: 音频中的语言,可以留空,程序会花费30秒时间自动识别语言

如果本地缓存中没有模型文件,whisper-ctranslate2将会自动下载
如果本地缓存中没有模型文件,whisper-ctranslate2将会自动下载
whisper-ctranslate2使用文档
usage: whisper-ctranslate2 [-h]
[--model {tiny,tiny.en,base,base.en,small,small.en,medium,medium.en,large-v1,large-v2}]
[--model_dir MODEL_DIR] [--output_dir OUTPUT_DIR]
[--output_format {txt,vtt,srt,tsv,json,all}] [--verbose VERBOSE]
[--task {transcribe,translate}]
[--language {af,am,ar,as,az,ba,be,bg,bn,bo,br,bs,ca,cs,cy,da,de,el,en,es,et,eu,fa,fi,fo,fr,gl,gu,ha,haw,he,hi,hr,ht,hu,hy,id,is,it,ja,jw,ka,kk,km,kn,ko,la,lb,ln,lo,lt,lv,mg,mi,mk,ml,mn,mr,ms,mt,my,ne,nl,nn,no,oc,pa,pl,ps,pt,ro,ru,sa,sd,si,sk,sl,sn,so,sq,sr,su,sv,sw,ta,te,tg,th,tk,tl,tr,tt,uk,ur,uz,vi,yi,yo,zh,Afrikaans,Albanian,Amharic,Arabic,Armenian,Assamese,Azerbaijani,Bashkir,Basque,Belarusian,Bengali,Bosnian,Breton,Bulgarian,Burmese,Castilian,Catalan,Chinese,Croatian,Czech,Danish,Dutch,English,Estonian,Faroese,Finnish,Flemish,French,Galician,Georgian,German,Greek,Gujarati,Haitian,Haitian Creole,Hausa,Hawaiian,Hebrew,Hindi,Hungarian,Icelandic,Indonesian,Italian,Japanese,Javanese,Kannada,Kazakh,Khmer,Korean,Lao,Latin,Latvian,Letzeburgesch,Lingala,Lithuanian,Luxembourgish,Macedonian,Malagasy,Malay,Malayalam,Maltese,Maori,Marathi,Moldavian,Moldovan,Mongolian,Myanmar,Nepali,Norwegian,Nynorsk,Occitan,Panjabi,Pashto,Persian,Polish,Portuguese,Punjabi,Pushto,Romanian,Russian,Sanskrit,Serbian,Shona,Sindhi,Sinhala,Sinhalese,Slovak,Slovenian,Somali,Spanish,Sundanese,Swahili,Swedish,Tagalog,Tajik,Tamil,Tatar,Telugu,Thai,Tibetan,Turkish,Turkmen,Ukrainian,Urdu,Uzbek,Valencian,Vietnamese,Welsh,Yiddish,Yoruba}]
[--threads THREADS] [--temperature TEMPERATURE]
[--temperature_increment_on_fallback TEMPERATURE_INCREMENT_ON_FALLBACK] [--best_of BEST_OF]
[--beam_size BEAM_SIZE] [--patience PATIENCE] [--length_penalty LENGTH_PENALTY]
[--suppress_tokens SUPPRESS_TOKENS] [--initial_prompt INITIAL_PROMPT]
[--condition_on_previous_text CONDITION_ON_PREVIOUS_TEXT]
[--compression_ratio_threshold COMPRESSION_RATIO_THRESHOLD]
[--logprob_threshold LOGPROB_THRESHOLD] [--no_speech_threshold NO_SPEECH_THRESHOLD]
[--word_timestamps WORD_TIMESTAMPS] [--prepend_punctuations PREPEND_PUNCTUATIONS]
[--append_punctuations APPEND_PUNCTUATIONS] [--device DEVICE] [--vad_filter VAD_FILTER]
[--vad_min_silence_duration_ms VAD_MIN_SILENCE_DURATION_MS]
[--device_index [DEVICE_INDEX ...]]
[--compute_type {default,auto,int8,int8_float16,int16,float16,float32}]
[--model_directory MODEL_DIRECTORY] [--print_colors PRINT_COLORS]
audio [audio ...]

positional arguments:
audio audio file(s) to transcribe

options:
-h, --help show this help message and exit
--model {tiny,tiny.en,base,base.en,small,small.en,medium,medium.en,large-v1,large-v2}
name of the Whisper model to use (default: small)
--model_dir MODEL_DIR
the path to save model files; uses ~/.cache/whisper-ctranslate2 by default (default: None)
--output_dir OUTPUT_DIR, -o OUTPUT_DIR
directory to save the outputs (default: .)
--output_format {txt,vtt,srt,tsv,json,all}, -f {txt,vtt,srt,tsv,json,all}
format of the output file; if not specified, all available formats will be produced (default:
all)
--verbose VERBOSE whether to print out the progress and debug messages (default: True)
--task {transcribe,translate}
whether to perform X->X speech recognition ('transcribe') or X->English translation
('translate') (default: transcribe)
--language {af,am,ar,as,az,ba,be,bg,bn,bo,br,bs,ca,cs,cy,da,de,el,en,es,et,eu,fa,fi,fo,fr,gl,gu,ha,haw,he,hi,hr,ht,hu,hy,id,is,it,ja,jw,ka,kk,km,kn,ko,la,lb,ln,lo,lt,lv,mg,mi,mk,ml,mn,mr,ms,mt,my,ne,nl,nn,no,oc,pa,pl,ps,pt,ro,ru,sa,sd,si,sk,sl,sn,so,sq,sr,su,sv,sw,ta,te,tg,th,tk,tl,tr,tt,uk,ur,uz,vi,yi,yo,zh,Afrikaans,Albanian,Amharic,Arabic,Armenian,Assamese,Azerbaijani,Bashkir,Basque,Belarusian,Bengali,Bosnian,Breton,Bulgarian,Burmese,Castilian,Catalan,Chinese,Croatian,Czech,Danish,Dutch,English,Estonian,Faroese,Finnish,Flemish,French,Galician,Georgian,German,Greek,Gujarati,Haitian,Haitian Creole,Hausa,Hawaiian,Hebrew,Hindi,Hungarian,Icelandic,Indonesian,Italian,Japanese,Javanese,Kannada,Kazakh,Khmer,Korean,Lao,Latin,Latvian,Letzeburgesch,Lingala,Lithuanian,Luxembourgish,Macedonian,Malagasy,Malay,Malayalam,Maltese,Maori,Marathi,Moldavian,Moldovan,Mongolian,Myanmar,Nepali,Norwegian,Nynorsk,Occitan,Panjabi,Pashto,Persian,Polish,Portuguese,Punjabi,Pushto,Romanian,Russian,Sanskrit,Serbian,Shona,Sindhi,Sinhala,Sinhalese,Slovak,Slovenian,Somali,Spanish,Sundanese,Swahili,Swedish,Tagalog,Tajik,Tamil,Tatar,Telugu,Thai,Tibetan,Turkish,Turkmen,Ukrainian,Urdu,Uzbek,Valencian,Vietnamese,Welsh,Yiddish,Yoruba}
language spoken in the audio, specify None to perform language detection (default: None)
--threads THREADS number of threads used for CPU inference (default: 0)
--temperature TEMPERATURE
temperature to use for sampling (default: 0)
--temperature_increment_on_fallback TEMPERATURE_INCREMENT_ON_FALLBACK
temperature to increase when falling back when the decoding fails to meet either of the
thresholds below (default: 0.2)
--best_of BEST_OF number of candidates when sampling with non-zero temperature (default: 5)
--beam_size BEAM_SIZE
number of beams in beam search, only applicable when temperature is zero (default: 5)
--patience PATIENCE optional patience value to use in beam decoding, as in https://arxiv.org/abs/2204.05424, the
default (1.0) is equivalent to conventional beam search (default: 1.0)
--length_penalty LENGTH_PENALTY
optional token length penalty coefficient (alpha) as in https://arxiv.org/abs/1609.08144, uses
simple length normalization by default (default: 1.0)
--suppress_tokens SUPPRESS_TOKENS
comma-separated list of token ids to suppress during sampling; '-1' will suppress most special
characters except common punctuations (default: -1)
--initial_prompt INITIAL_PROMPT
optional text to provide as a prompt for the first window. (default: None)
--condition_on_previous_text CONDITION_ON_PREVIOUS_TEXT
if True, provide the previous output of the model as a prompt for the next window; disabling
may make the text inconsistent across windows, but the model becomes less prone to getting
stuck in a failure loop (default: True)
--compression_ratio_threshold COMPRESSION_RATIO_THRESHOLD
if the gzip compression ratio is higher than this value, treat the decoding as failed
(default: 2.4)
--logprob_threshold LOGPROB_THRESHOLD
if the average log probability is lower than this value, treat the decoding as failed
(default: -1.0)
--no_speech_threshold NO_SPEECH_THRESHOLD
if the probability of the <|nospeech|> token is higher than this value AND the decoding has
failed due to `logprob_threshold`, consider the segment as silence (default: 0.6)
--word_timestamps WORD_TIMESTAMPS
(experimental) extract word-level timestamps and refine the results based on them (default:
False)
--prepend_punctuations PREPEND_PUNCTUATIONS
if word_timestamps is True, merge these punctuation symbols with the next word (default:
"'“¿([{-)
--append_punctuations APPEND_PUNCTUATIONS
if word_timestamps is True, merge these punctuation symbols with the previous word (default:
"'.。,,!!??::”)]}、)
--device DEVICE device to use for CTranslate2 inference (default: auto)
--vad_filter VAD_FILTER
Enable the voice activity detection (VAD) to filter out parts of the audio without speech.
This step is using the Silero VAD model https://github.com/snakers4/silero-vad. (default:
False)
--vad_min_silence_duration_ms VAD_MIN_SILENCE_DURATION_MS
When `vad_filter` is enabled, audio segments without speech for at least this number of
milliseconds will be ignored. (default: 2000)
--device_index [DEVICE_INDEX ...]
Device IDs where to place this model on (default: 0)
--compute_type {default,auto,int8,int8_float16,int16,float16,float32}
Type of quantization to use (see https://opennmt.net/CTranslate2/quantization.html) (default:
auto)
--model_directory MODEL_DIRECTORY
Directory where to find a CTranslate Whisper model (e.g. fine-tuned model) (default: None)
--print_colors PRINT_COLORS
Print the transcribed text using an experimental color coding strategy to highlight words with
high or low confidence (default: False)

默认情况下 whisper-ctranslate2 会输出(1)带时间戳的字幕文件(srt、vtt等)(2)纯文本形式的txt(3)其他类型的文件(json、tsv)
我用到的也就是srt和txt两种。

Aegisub

Aegisub on macOS
Aegisub on macOS
导入音频文件和 srt 文件后就可以对应检索了。

Aegisub: https://aegisite.vercel.app


本站由 @anonymity 使用 Stellar 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。