
最近圈里总有朋友问我同一个问题Python做语音识别到底该上哪个库网上搜来搜去翻来覆去就是whisper和vosk这两个名字可真到动手的时候很多人连环境都搭不起来更别说搞清楚两个框架到底怎么分工了。我这次把两条路线都完整跑了一遍从环境配置到批量转写从麦克风实时识别到各种报错排查踩了不少坑也总结了不少经验今天就一次性整理出来希望能帮你少走点弯路。这个合集的定位很明确如果你只是想快速把一段录音变成文字或者想给手里的小项目加一个离线语音助手入口那么接下来的内容既照顾小白也保留进阶技巧。whisper擅长“把录音仔细听明白”适合会议纪要、字幕生成和批量转写vosk则强在“边听边出字”适合命令词唤醒、实时对话和嵌入式场景。两个结合起来用才是目前本地语音识别最舒服的姿势。1. 项目概述whisper 和 vosk 到底差在哪1.1 两种方案的核心定位差异很多人把whisper和vosk混为一谈其实它们的出发点和适用场景完全不同。whisper是OpenAI开源的多语言语音识别系统这个名字在社区里已经成了“高精度离线转写”的代名词。它的实现路径是先把音频切成30秒左右的窗口转成log-mel声谱图交给一个Transformer编码器去提取特征再由解码器逐token地生成文本。这种端到端的方式对乱环境和多语言混说的鲁棒性很强英文、中文、其他语种都能识别甚至能直接做翻译。代价是模型体积和资源开销都比较可观而且它是批量处理逻辑想用它做实时语音助手延迟和算力都吃不消。vosk则是从Kaldi生态里长出来的轻量级工具包。Kaldi是语音识别领域的“老牌工业级底座”vosk把其中最稳定的部分封装成了Python可直接调用的接口。它的模型很小中文小模型几十MB就能跑识别速度极快更重要的是支持流式识别——按小块喂音频边喂边出字。这种特性决定了它更适合做语音唤醒、本地命令词识别、嵌入式设备上的交互系统等对实时性要求高的场景。两者并不冲突甚至可以说互补。whisper解决的是“事后把这堆音频变成高质量文本”的问题vosk解决的是“一边听一边响应”的问题。1.2 为什么不用在线API而是本地离线的两个方案日常交流里也有不少人提到讯飞、百度和阿里的云端识别接口甚至有人说在ESP32上通过IDF方式接入讯飞的识别服务。确实为了嵌入式硬件跑语音识别对接云端API是很常见的路线但那是在设备资源极其有限的约束下才不得已的选择。在普通PC、树莓派、服务器或者你自己写的桌面工具里我更推荐whisper和vosk原因有三点。第一是隐私。音频往云端一传等于把对话内容交给别人不管合同里怎么写心里始终不踏实。本地识别所有数据留在自己机器上这一点对很多内部工具来说其实是硬性需求。第二是成本和稳定性。在线API按调用量计费长时间音频更要花不少钱同时它依赖网络一旦断网或者服务限流整个流程就卡住。本地方案一次性下载模型之后都是免费的离线环境下也能照常跑。第三是链路可控。在线API的完整链路是一套黑盒你不知道中间被截断与否也无法干预。用whisper和vosk你可以切音频、做分段、改热词、调采样率整条流水线都能自己掌控。调试起来更踏实也更方便针对性优化。1.3 我的选型结论直接给结论后面照着做就行一次性高质量转写、字幕生成、会议纪要选whisper实时监听、命令词识别、语音唤醒选vosk。如果两者硬要配合我建议用vosk做唤醒词和命令词听到指定指令之后再调用whisper去处理一段更长的录音内容这样既保证了响应速度又拿到了转写质量。我自己的项目里就是这么做的米家场景自动化那边用vosk识别“开灯”“关灯”这几个固定指令电脑端的会议录音归档则全部交给whisper跑一遍srt字幕。上个月跑了二十多个小时的会议录音整体效果稳定后面详细说。2. 前置准备Python环境搭建与依赖处理2.1 创建虚拟环境避免依赖打架Python版本建议用3.9到3.11之间。whisper依赖PyTorch而PyTorch对不同Python版本的适配有差异vosk的依赖很薄但为了不污染系统Python同样建议放进虚拟环境里管理。创建虚拟环境的标准流程很简单python -m venv venv source venv/bin/activate # Linux / macOS venv\Scripts\activate # Windows激活之后命令行前面会出现(venv)标记说明你已经在虚拟环境里了。后面装的任何包都不会影响系统其他项目这是做Python项目最基础也最重要的一步。2.2 pip换源让安装不再卡死whisper装依赖的时候要拉很多包PyTorch的体积又是出了名的庞大如果你直接走默认的官方PyPI在国内网络下很容易看到进度条长时间不动或者干脆超时断开。这个问题的解决办法是先把pip默认源切到国内镜像比如清华源pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple以后所有pip install都会自动走这个源速度会有质的提升。如果某个包在清华源上版本滞后或者报404也可以用命令行参数临时切到阿里云镜像pip install openai-whisper -i https://mirrors.aliyun.com/pypi/simple/这一步虽小但能帮你省掉大量折腾时间。别小看它很多人折腾半天下载超时其实就差这一行配置。2.3 whisper 安装与 ffmpeg 的坑安装whisper其实就一条命令pip install openai-whisper但这条命令会自动把PyTorch一起装进来。默认情况下pip会尝试安装一个带CUDA支持的版本体积好几个GB如果你的电脑没有NVIDIA显卡这部分就是纯浪费。更稳妥的做法是先手动安装CPU版PyTorch再安装whisperpip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu pip install openai-whisper反正我踩过这个坑第一次二话不说直接pip install openai-whisper结果硬盘少了快6个G后来才发现安装的是cuda版实际根本用不到。另外whisper处理mp3、m4a等压缩格式音频时底层依赖FFmpeg解码。如果没有安装FFmpeg代码会直接报错或者抛出找不到解码器的提示。安装方式分系统看# Ubuntu / Debian sudo apt install ffmpeg # macOS brew install ffmpegWindows上则是去FFmpeg官网下载release包解压之后把bin目录的完整路径加到系统环境变量PATH里。配置结束后打开新的命令行窗口输入ffmpeg -version如果能正常回显版本信息就说明FFmpeg已经就绪。这一步非常关键我在后面会再讲一个和它相关的典型报错。2.4 vosk 安装与中文模型下载vosk的安装比whisper简单得多pip install vosk它没有一堆重量级依赖装完就能用。真正需要注意的是模型文件。vosk官方提供多种语言的模型中文有两个档位小模型大约40MB左右适合快速验证和简单命令词大模型通常在1GB以上识别率更高适合自由转写。去vosk官网的models页面下载对应模型解压后放到项目目录里就行。注意不要改动解压出来的文件夹结构也不要手动改文件夹名字代码传入的路径要指向包含模型文件的目录。我当时用的小模型第一次跑通的时候还略带失望因为速度太快了反而没有成就感。但后来用它做唤醒词才发现这种“快”在实时场景里才是真香。3. whisper 实操把音视频批量转成文字3.1 第一次成功调用whisper的Python接口非常简洁最短的调用代码只有三行核心逻辑import whisper model whisper.load_model(base) result model.transcribe(demo.mp3) print(result[text])第一次运行会自动下载base模型几十MB到一百多MB左右取决于网络情况。下载完成后它会把音频整个读入然后按30秒窗口逐段转写。只要FFmpeg环境没问题几秒钟后就能看到识别结果。这里有个很重要的经验第一次建议用一段清晰、无背景噪音的录音去测试把链路跑通。不要一上来就用嘈杂的会议录音否则你会分不清是模型能力问题还是环境配置问题。3.2 模型选型与关键参数whisper官方提供了五个档次的模型从小到大分别是tiny、base、small、medium和large。参数规模越大识别准确率越高但计算量也越大。模型参数量约适用场景CPU上的体感速度tiny39M快速测试、英文简单音频很快接近实时base74M通用转写、中文短音频普通CPU可用small244M中文长音频、会议录音CPU慢建议GPUmedium769M高质量转写、复杂口音显存至少4GBlarge1550M对准确率要求极高的任务显存建议8GB以上实际使用中我的建议是中文语音场景至少用base起步认真的项目直接上small或medium。tiny在中文上的表现很不理想很多语句会断得让人怀疑人生。如果你有NVIDIA显卡默认会用GPU推理没有GPU就务必在调用时加上fp16False否则在CPU上会出现数据类型不对的报错。另一个重要的参数是language。whisper默认会自动检测语言但自动检测有时会误判尤其音频开头是音乐或者静音的时候。显式指定语言能消除这类问题result model.transcribe( demo.mp3, languagezh, fp16False, )这样它就只会按中文去解码而不会一会儿误判成英文一会儿又跳回中文整体转写稳定性会好很多。3.3 中文场景的参数调优中文跟英文在whisper里的表现差异挺明显。中文是单音节表意文字同一段话在口语、书面语和方言口音之间跨度很大如果不加任何干预经常会遇到专有名词被识别成同音字或者完全不相干的词。我在一次技术分享录音里“Transformer”被识别成“调换符魔”当场笑出声但想想也证明热词机制的必要性。解决办法是使用initial_prompt参数也就是给模型一个“先入为主”的上下文提示。它不会限制模型只认这些词但会显著提高这些词的权重。例如result model.transcribe( meeting.wav, languagezh, initial_prompt以下是普通话技术会议的录音内容涉及Python编程、语音识别、whisper模型、OpenAI、语音助手。, fp16False, temperature0.0, verboseFalse, )temperature0.0的作用是让解码过程完全确定性不引入随机采样这样可以减少模型“一本正经地编造”句子的幻觉现象虽然会牺牲一点点表达多样性但转写场景要的就是稳定。verboseFalse则是关掉控制台逐段打印保持日志干净。如果想要带时间戳的字幕文件result[segments]里每一项都有start和end字段直接遍历就能生成自己的srt。其实更省事的办法是直接用whisper自带的命令行工具whisper audio.wav --model base --language zh --output_format srt这行命令会在当前目录下直接生成一个srt字幕文件做视频字幕的朋友可以省掉很多手工活。3.4 长音频分段与多进程加速whisper的底层虽然会自动切30秒窗口但实际操作中一次性扔一个两小时的长音频进去还是会遇到两个问题一是内存和耗时都在涨尤其是CPU环境下长时间跑风扇声能盖过一切二是长音频里经常有说话人切换、语气词、长时间停顿这些都会影响连续窗口的识别稳定性导致后半段上下文错乱。更稳妥的方案是先按静音点切段再分批交给whisper。切段可以先用FFmpeg的静音检测做粗切ffmpeg -i long.wav -af silencedetectnoise-30dB:d1.0 -f null -运行后在输出日志里能看到每一段静音的起止时间根据这些时间点用FFmpeg把原音频切成多个片段。这里有一个实用的细节切分的时候要保证片段之间有一秒左右的重叠避免恰好把一个词拦腰切断。转写完成之后丢掉重叠区再去拼接文本。分段之后如果机器是多核CPU还可以用Python的多进程并行处理每个片段效率提升大概是核心数的倍数。一段两小时的会议录音我在8核CPU上跑small模型从原来几十分钟缩短到十几分钟虽然依然不如GPU但至少不会让人等得心焦。4. vosk 实操离线实时识别才是主场4.1 加载模型与基础识别流程vosk的使用方式和whisper差异很大也更贴近流式处理的思维方式。它不像whisper那样一次性吞下一整段音频再返回结果而是把音频切成小块一块一块喂给识别器每喂一块都可能见到两种结果要么AcceptWaveform返回True说明当前这句话已经完整识别结束可以从Result()里取最终文本要么返回False你只能从PartialResult()里拿到中间结果也就是“目前拼了一半”的文字。基础初始化代码from vosk import Model, KaldiRecognizer model Model(vosk-model-small-cn-0.22) rec KaldiRecognizer(model, 16000) rec.SetWords(True)第二个参数16000是采样率vosk模型统一要求16kHz单声道的PCM音频。如果你的音频是44.1kHz CD音质或者48kHz视频音轨必须先统一重采样到16kHz否则识别器接收到的频谱特征和模型预期对不上结果就是全无输出或者满屏乱码。这一步是新手最容易忽视的坑但也是整个vosk链路的命根子。4.2 音频文件识别完整流程用Python标准库的wave模块读取WAV音频逐块喂给识别器是vosk离线文件识别最经典的姿势import json import wave from vosk import Model, KaldiRecognizer model Model(vosk-model-small-cn-0.22) rec KaldiRecognizer(model, 16000) wf wave.open(demo.wav, rb) if wf.getframerate() ! 16000 or wf.getnchannels() ! 1: raise ValueError(音频必须是16kHz单声道WAV) while True: data wf.readframes(4000) if len(data) 0: break if rec.AcceptWaveform(data): result json.loads(rec.Result()) print(result[text]) final_result json.loads(rec.FinalResult()) print(最终结果:, final_result[text])这里有一个我印象极其深刻的坑如果循环结束之后不调用FinalResult()最后一句文本会凭空消失。因为AcceptWaveform只有在识别器判断“这句话已经完整”的时候才返回True如果音频最后一个块刚好卡在句子中间剩余部分就留在缓冲区里必须用FinalResult()把它强制刷出来。另外喂数据块的尺寸也是个讲究活。readframes(4000)表示每次读取4000个采样点大约对应0.25秒的16kHz音频识别实时性很好。你可以调大一些但一次喂太多会增大延迟调太小则会让识别器频繁切换状态增加碎片化。4.3 麦克风实时识别麦克风实时识别只需要把上面的文件读取换成PyAudio采集。安装PyAudio之前要确认系统里有PortAudio库Linux下通常是sudo apt install portaudio19-devWindows下如果pip install pyaudio安装失败可以先装pipwin再安装或者直接找对应当前Python版本的whl文件离线安装。反正这两个平台都容易出小问题别灰心卡在安装阶段是正常的。实时识别示例import json import pyaudio from vosk import Model, KaldiRecognizer model Model(vosk-model-small-cn-0.22) rec KaldiRecognizer(model, 16000) p pyaudio.PyAudio() stream p.open( formatpyaudio.paInt16, channels1, rate16000, inputTrue, frames_per_buffer8000, ) stream.start_stream() print(开始监听按 CtrlC 停止) try: while True: data stream.read(2000, exception_on_overflowFalse) if rec.AcceptWaveform(data): result json.loads(rec.Result()) if result[text]: print(识别:, result[text]) else: partial json.loads(rec.PartialResult()) if partial[partial]: print(临时结果:, partial[partial], end\r) except KeyboardInterrupt: pass finally: stream.stop_stream() stream.close() p.terminate()这个脚本在普通笔记本上跑起来毫无压力CPU占用很低而且响应延迟基本感觉不到。我在树莓派4B上跑过同样的小模型也能保持比较流畅的实时显示这就是轻量方案的优势。4.4 限定语法命令词识别的准确率翻倍如果你只是想识别“开灯”“关灯”“暂停”“继续”这类固定指令千万别做自由转写直接给识别器限制语法范围效果立竿见影。vosk允许你在初始化时传入一个JSON数组格式的语法约束rec KaldiRecognizer(model, 16000, [开灯, 关灯, 暂停, 继续])加了语法约束之后识别器只会在给定的词表里找最接近的匹配等于把搜索空间从全词汇缩小到几个候选词误报率直线下降准确率和响应速度同时提高。我做过一次对比自由转写模式识别“开灯”大概有七成胜率噪音环境下甚至更低换成语法约束之后几乎百发百中。但这里的反面教训是如果目标是自由对话转写千万别手贱加语法约束。语法约束会大幅压缩识别词汇表导致很多正常的句子被强行映射到最近的指令上输出变得莫名其妙。两种模式要分开用同一个进程里如果想切换需要重新创建一个KaldiRecognizer实例。4.5 自由转写场景的几个进阶技巧自由转写模式下vosk的准确率相比whisper会弱一些特别是在复杂语音和专有名词上但通过几个小技巧可以逼近实用水平。第一SetWords(True)必须开。它会让识别结果里带上每一个词的时间戳这对于后处理对齐文本和音频非常有帮助也能帮你发现识别器在哪一段开始“跑偏”。第二适当使用SetMaxAlternatives(3)。这会为每个识别结果输出多个候选文本你可以根据前后文用规则挑一个最合理的相当于做了一层简单的重打分。就是下面这种写法rec.SetWords(True) rec.SetMaxAlternatives(3)第三尽量保证音频源干净。vosk对背景噪音和混响的容忍度不如whisper实测中离麦克风30厘米和10厘米的识别率差别非常大。如果做桌面语音助手建议配一个增益不太高的麦克风然后在代码里做一次简单的音量归一化把波形缩放到合理区间再喂给识别器。5. 常见问题与排查技巧实录5.1 ffmpeg 报错whisper直接崩很多人第一次跑whisper识别mp3代码刚执行到model.transcribe()就抛出一个错误看起来像是找不到解码器。十有八九是FFmpeg没装好。我在2.3节已经说过安装方法这里再补充一个验证技巧如果你在命令行里执行ffmpeg -version有输出但Python里依然报错多半是IDE或者服务进程没有继承更新后的PATH。这时候重启终端、重启IDE甚至重启电脑问题往往就消失了。5.2 中文识别准确率低专有名词总错这是个老生常谈但每次都有新朋友问。中文识别率低的原因通常有两个方向一是模型本身能力不够whisper的tiny和base模型对中文长句的鲁棒性偏弱vosk的小模型更是只适合短句二是输入音频质量太差背景音乐、混响、低音量都会让识别率断崖式下降。先把音频处理干净再考虑换大模型。whisper可以用initial_prompt注入专有名词vosk可以用语法约束或者后处理替换。不要指望一个模型天生什么都能听对语言识别这行预处理和后处理占了一半的工程。5.3 女声识别率是不是真的更低这个话题挺有意思我在社区里也看到过“女声语音识别为什么比男声更低”的讨论。从我实测的whisper和vosk表现来看女声在某些预设模型上的误识别率确实比男声稍高尤其是语速快、音调高、带语气词的情况下模型容易“吞字”。业内通常把这归因为三点。第一很多开源训练语料里男声比例偏高模型对男性基频范围的特征更“熟悉”对女性较高基频的泛化就弱一些。第二女声的基频通常比男声高一个量级落入特征分布边缘后声学模型更容易混淆相近的音素。第三语速和气息习惯的差异也会影响断句质量。这更多是数据分布问题而不是原理上注定识别不了。实操上提升方案很直接尽量用更大的模型或者针对具体人的声音采集少量样本做微调日常使用中注意麦克风增益和距离避免人声信号过弱。至少在我自己的实测中换用medium模型后女声识别的错字率下降非常明显。5.4 GPU 显存不足与 CPU 太慢whisper的模型档位越往上显存需求越离谱。我用一张4GB显存的卡跑small模型理论上是够的但如果音频比较长还是偶尔会弹出显存不足的报错。应对方法有三条。一是换小模型samll换base速度提升非常直观。二是确保fp16False这行参数在CPU环境被设置好如果你明明没有GPU但代码里默认开了fp16反而会因为数据类型问题报错。三是开启按段处理一次只转写一个音频片段处理完释放显存再处理下一个。大模型虽好但小模型配上好的预处理链路往往才是工程上最划算的答案。5.5 vosk 高频报错速查表我在调试里经常被身边朋友问同一批问题汇总成一个表格先存着以后直接看。现象可能原因解决办法初始化时报“Model missing”模型路径不对目录里没有mdl模型文件检查目录结构路径指向解压后含模型文件的目录识别结果全是空字符串PCM采样率不是16kHz或者不是单声道用FFmpeg重采样为16kHz单声道WAV实时识别延迟高frames_per_buffer和stream.read的大小不匹配减小每次读取的字节数控制在2000-4000字节量级最后一句永远丢失没有调用FinalResult()处理完数据后强制调用FinalResult()取尾部结果麦克风没声音PyAudio的设备索引不对遍历设备列表把input设备换成实际使用的麦克风结尾两者怎么配合用这套组合我实际用下来的体会是whisper和vosk根本不是竞争关系更像是一个负责“仔细听、慢慢写”另一个负责“现场接话、即时反应”。我最舒服的搭配是vosk在后台常驻识别固定唤醒词和命令词响应速度毫秒级当它听到“开始录音”这类指令时再触发一段whisper的批量转写流程把刚才录下来的音频一次性变成正式文本。这样既兼顾了交互的实时性又拿到了高质量的转写结果。最后再分享一个小技巧不管是whisper还是vosk识别前都先检查音频的采样率、声道数和格式能用PCM WAV就优先用PCM WAV能重采样就提前重采样。我自己被采样率坑过太多次后来专门写了一段预处理脚本所有音频进来先统一到16kHz单声道再走识别流程问题立刻少了一大半。工具本身没那么复杂重要的其实是把它放到合适的位置并且把预处理后处理搭完整。