whisper.cpp 本地离线语音转文字:中文SRT字幕生成实战指南
先说结论如果你需要把一段录音、课堂笔记、会议记录或视频对白转成文字又想完全免费、离线运行、不把音频传到任何服务器whisper.cpp 是当前最实用的一条路。它把 OpenAI 的 Whisper 模型移植到了纯 C/C 环境不依赖 Python 生态单文件编译后直接跑CPU 上就能用而且默认就支持中文识别输出 SR T 字幕也就是一条命令的事。这篇文章我会把从零部署到实际生成中文 SRT 字幕的完整过程拆开讲清楚覆盖 Windows、macOS、Linux 三套系统也会把我踩过的坑和调参心得一并写出来。1. 先想清楚whisper.cpp 适合干什么不适合干什么1.1 它的核心优势不是“准确率最高”而是“本地离线 轻量可嵌入”很多人第一次听说 whisper.cpp是因为 OpenAI 官方 Whisper 模型需要在 Python 环境里安装 openai-whisper还要配 PyTorch。虽然也能用但整套依赖体积好几 GB启动慢对一台只有 4GB 内存的旧笔记本来说属于灾难。whisper.cpp 直接把模型推理用 C 语言重写采用 GGML 张量库编译产物就一个可执行文件模型文件也经过量化压缩最小的tiny模型只有 75MB 左右base约 142MB。它能跑在纯 CPU 上不需要 NVIDIA GPU也不需要 CUDA 环境这正好覆盖了我这种“手头只有一块核显轻薄本”的场景。1.2 不适合的场景也要提前知道whisper.cpp 不是银弹。如果你要处理的是 10 小时以上的长音频转写建议优先考虑用 faster-whisper 或云端 API因为纯 CPU 推理速度仍然比不过优化过的 GPU 方案。另外它的实时性也不够好官方提供了 stream 示例但实际效果只能说“能跑”达不到同声传译级别。如果你需要的是中文方言识别、专业领域词汇识别比如医疗、法律术语whisper.cpp 默认模型的效果只能算“能听懂普通话标准音”得更进一步做 prompt 定制或微调而微调本身并不在 whisper.cpp 的范畴里。1.3 一台普通电脑能不能跑先看这个底线配置以我自己的使用经验x86_64 CPU 只要支持 AVX 指令集跑base模型就没问题。ARM 芯片比如 Apple Silicon 或树莓派也能编译运行只是官方针对不同架构优化程度不同。内存方面tiny模型量化后占用不到 300MBbase不到 500MBsmall可能接近 1GBmedium则要 2GB 以上。磁盘空间很小编译环境加模型文件加起来不超过 2GB。如果你只有 1 核 2GB 的云服务器也能用但建议只跑tiny或base并且把并发线程数调低避免 OOM。2. 环境准备与编译三个平台的完整命令2.1 Windows用 CMake 构建比用 MinGW 更省心Windows 下可以不用装 Visual Studio 那么大一个 IDE装一个 Build Tools for Visual Studio 就够了。官方仓库提供了 CMake 构建方式。以下是我实测可跑通的流程# 1. 安装依赖用 winget 或手动装 winget install Git.Git winget install -e --id Kitware.CMake winget install -e --id Ninja-build.Ninja # 2. 克隆仓库 git clone https://github.com/ggml-org/whisper.cpp.git cd whisper.cpp # 3. 构建 cmake -B build -DCMAKE_BUILD_TYPERelease cmake --build build --config Release -j 4 # 4. 编译出的主程序在 build\bin\main.exe 和 build\bin\whisper-cli.exe如果你是老派用户也可以直接跑仓库里的Makefile用 MinGW-w64 环境执行make但我在 Windows 上曾经因为缺少 pthread 库踩过坑。CMake Ninja 这套组合最稳链接更快也方便后续打开 VS 调试。构建完成后建议先用build\bin\whisper-cli.exe --help验证一下是否正常。2.2 macOS一条 make 命令搞定但要注意编译工具链macOS 上最简单的方法是装 Xcode Command Line Tools它自带 clang 和 make。执行git clone https://github.com/ggml-org/whisper.cpp.git cd whisper.cpp makeApple Silicon 用户还会自动启用 Accelerate 框架加速不需要额外配置。如果你要发挥 Metal GPU 能力仓库里有ggml-metal相关代码但默认编译通常不开启 Metal需要加WHISPER_METAL1参数make WHISPER_METAL1不过我的实测是 Metal 加速只对长音频推理有感知上的提升短音频反而因为初始化开销体现不出来新手可以先用纯 CPU 跑通全流程之后再按需开启。2.3 Linux服务器无 GPU 也能跑得非常舒服Linux 上最顺畅的路径跟 macOS 几乎一样sudo apt update sudo apt install -y git build-essential git clone https://github.com/ggml-org/whisper.cpp.git cd whisper.cpp make -j$(nproc)如果是 CentOS/RHEL 系把build-essential换成gcc-c make即可。我在一台只有 2 核 4GB 内存的 ECS 上编译过全程大约两三分钟运行时把线程数设成 2内存占用稳定在 400MB 左右完全没有问题。所以不要被“AI”两个字吓到这个项目对服务器的要求远远低于跑大模型推理的那些框架。3. 模型选择下载官方预训练模型并转换为 GGML 格式3.1 不一定要用 Python但用脚本下载最省事从 v1.5 开始whisper.cpp 可以直接加载官方多语言模型转换为 GGML 格式后的文件仓库models目录下提供了download-ggml-model.sh脚本。这个脚本本质上会去 HuggingFace 的ggerganov/whisper.cpp仓库取量化好的 GGML 模型命名规则类似ggml-base.bin。执行方式# 在 whisper.cpp 目录下执行 bash ./models/download-ggml-model.sh base它会自动识别 CPU 架构选择合适的量化版本下到models目录。这个脚本有交互提示需要确认目标目录。你当然也可以直接到 HuggingFace 页面手点下载但脚本更省事。老版本里还有个convert-pt-to-ggml.py用于把 PyTorch 模型转成 GGML那是远古流程现在完全没必要。要下载中文效果更好的模型我建议先用base或small做测试因为在纯 CPU 上medium的推理时间会把你折磨到失去耐心。3.2 量化等级怎么选看懂 q5_0、q8_0、f16 的差异GGML 量化模型的命名后缀不是随便标的f16表示半精度浮点原始权重q8_0是 8-bit 量化q5_0和q4_0是更激进的 4-bit/5-bit 量化。选型原则很简单内存够用就选f16或q8_0内存紧张就选q5_0。从我转写中文素材的经验来看q5_0的base模型在普通话清晰录音下准确率和f16差别非常小但模型体积少了三分之一。tiny模型的q5_0在带噪语音里错误率会明显升高所以如果场景是访谈、车内录音这种噪音多的尽量别用tiny。下表是我整理的实测参考模型量化类型大小CPU 内存占用中文普通话效果推荐场景tinyq5_0约 75MB约 300MB勉强能识别容易错字语音命令、快速测试baseq5_0约 142MB约 500MB日常对话基本可用视频字幕、课堂录音basef16约 285MB约 700MB比 q5_0 稍稳对准确率要求稍高的短音频smallq5_0约 466MB约 1.5GB明显更好能处理方言口音会议记录、播客转写mediumq5_0约 1.5GB约 3GB最接近云端效果高质量视频字幕3.3 手动下载模型并指定路径如果脚本下载太慢你可以直接从 HuggingFace 的镜像下载或者用下载工具。将模型文件放到models文件夹后运行时通过 -m 参数指定路径不需要重新编译。示例./build/bin/whisper-cli -m ./models/ggml-base.bin -f test.mp3文件名不一定要符合某个模式只要你给清楚绝对路径或相对路径程序都会去读。常见错误是下载下来文件不完整导致加载时报failed to open或 Q 维数不对所以下载后建议看一眼文件大小是否跟说明一致。4. 第一次转写命令行参数逐项拆解4.1 最小可用的转写命令假设你已经有了一个音频文件meeting.mp3执行./build/bin/whisper-cli -m ./models/ggml-base.bin -f meeting.mp3 -l zh -otxt-l zh是告诉模型用中文模式识别-otxt是输出纯文本文件。不加-otxt的话默认只是在控制台打印彩色输出想要落盘必须写参数。运行起来后你会看到模型加载、音频解码、多个线程并行计算的打印日志。实测一段 3 分钟的普通话录音在 4 核 i5 上跑base模型大概耗时 40 秒左右这是正常速度别以为是卡住了。4.2 关键参数线程、语言、输出格式、时间戳whisper.cpp 的参数都是短横线形式我用得最多的就这几组-t 4 # 指定推理线程数一般等于 CPU 物理核心数 -p 2 # 指定处理线程数用于解码音频等预处理 -l zh # 指定语言为中文不指定会让模型自动检测但自动检测会多花时间 -otxt # 输出 txt 文本 -srt # 输出 SRT 字幕 -vtt # 输出 VTT 字幕适合网页播放器 -of # 输出文件前缀例如 -of out 会生成 out.srt -m # 指定模型文件 -f # 输入音频/视频文件一个更完整的示例./build/bin/whisper-cli -m ./models/ggml-small.bin -f ./audio/lecture.mp3 -l zh -t 6 -of ./out/lecture -srt -otxt这条命令会把lecture.mp3转成./out/lecture.srt和./out/lecture.txt线程数设为 6。我的个人建议是-t不要超过物理核心数超线程逻辑核心加进去反而会因为内存带宽竞争导致速度下降。4.3 常见输出格式的手工调整让 SRT 更符合你的需求默认生成的 SRT 时间码是标准格式但有时你想把两条太短的片段合并或者强制最长字幕长度可以用后处理脚本完成。whisper.cpp 本身不提供“合并短句”的命令行开关但它在源码仓库里附带了 Python 示例脚本examples/whisper.wasm之外的一个talk.py之类的说明。实际上最直接的做法是直接用-srt生成再后处理。比如我用一段 5 分钟的视频生成 SRT 后经常发现有很多单字条比如“嗯”、“啊”这些可以合并到前一条。5. 中文 SRT 字幕生成实战从视频文件直接出字幕5.1 不需要先提取音轨whisper.cpp 内置 ffmpeg 解码支持whisper.cpp 的核心库直接依赖 FFmpeg 做解码所以输入可以是 mp4、mkv、avi 等任何 FFmpeg 支持的格式。一个常见误区是认为必须先把视频转成 mp3/wav其实没必要直接指定视频文件路径即可./build/bin/whisper-cli -m ./models/ggml-small.bin -f ./video/myvideo.mp4 -l zh -srt -of ./output/myvideo程序会自动读取视频里的音轨。如果视频有多个音轨默认选哪个我不太确定但通常选第一个。如果你要用指定音轨建议先用 ffmpeg 手动抽取。实际处理中我遇到一次问题视频是 5.1 声道whisper.cpp 默认混流时可能只取一轨结果识别出的文字带上明显的背景音乐干扰。后来我手动先将音频转为 16kHz 单声道 wav效果立刻干净很多。转音频命令ffmpeg -i myvideo.mp4 -ar 16000 -ac 1 -c:a pcm_s16le audio.wav5.2 SRT 文件生成后的实用整理中文标点与断句默认模型输出的 SRT 可能没有很好的标点中文标点基本是半角逗号句号。如果你需要更规整的字幕文本可以拿 Python 做个轻量清洗。我给一个我常用的思路不是完整脚本但很直接import re with open(output/myvideo.srt, r, encodingutf-8) as f: lines f.readlines() # 只处理字幕文本行非空、非序号、非时间轴 for i, line in enumerate(lines): if line.strip() and -- not in line and line.strip().isdigit() is False: line re.sub(r\s, , line) # 去掉多余空格 # 句号后面加换行等按需写 lines[i] line with open(output/myvideo_clean.srt, w, encodingutf-8) as f: f.writelines(lines)不过我要提醒清洗逻辑不要过度否则时间轴和文本行会错位。最安全的做法是逐行判断保留时间轴原样只替换文本行内容。另一个更省事的方法是不清洗直接把 SRT 交给剪辑软件比如剪映、Premiere它们会自动处理标点显示。5.3 中文长句自动分行的经验whisper.cpp 默认按音频能量和静音段断句有时一句话特别长在 SRT 里占满两行甚至三行播放体验不好。我实测下来可以在命令行加上--max-len参数有些版本叫-ml控制每行最多字符比如./build/bin/whisper-cli -m ./models/ggml-small.bin -f audio.wav -l zh -srt --max-len 20 -of short_line不过不同版本对--max-len的实现略有差异如果你用的版本不支持编译时可以查看--help。另一种方式是生成 SRT 后用脚本按标点切割把长句拆成多条字幕但时间码需要插值这个稍微复杂一点适合有一定编程基础的人。6. 中文识别准确率优化我能给你的几条实战经验6.1 语言参数与 initial prompt 的作用-l zh指定中文之后识别语言不会乱跑。但这还不够你可以用-prompt传入一段“提示文本”引导模型使用特定词汇或风格。比如你想让模型更偏向书面语可以写./build/bin/whisper-cli ... -l zh -prompt 以下是普通话的会议记录请使用书面语进行转写。这个 prompt 不是用来做自然语言指令的Whisper 模型实际上会用这串文字作为解码的上下文它跟那种“大模型聊天提示词”不太一样。实测对专业术语有一定的纠偏作用比如把“Transformer”这种词放进 prompt 里可以减少模型把它听成“变压器”的概率如果你要识别特定产品名这个办法真的有用。6.2 采样策略beam search 与 temperatureWhisper 的默认解码是贪婪采样greedy也就是每一步选概率最大的 token速度最快但容易出现“一根筋”错误。对于中文识别beam search 可以提高稳定性但会明显变慢。命令行参数是-beam 5 # 使用 5 个 beam默认是 -1 表示使用贪婪采样 -temp 0.0 # 温度设为 0让输出更确定性我实测对于会议录音beam 5相比 greedy 准确率提升不大但时间增加约 20%。如果你的机器性能充足建议开 beam search 并配合--max-context或-mc参数设置上下文窗口。需要注意的是beam search 在 whisper.cpp 里的实现不会像 faster-whisper 那样支持完整的互信息但已经足够改善常见的谐音错误。6.3 音频预处理降噪、归一化远比换大模型更重要我做了好多次对比测试同一个 noisy 录音用small模型识别错误率约 15%把噪音先降掉比如用 ffmpeg 高通滤波、或者用 RNNoise再用base模型错误率能降到 5% 以内。这听起来反直觉但事实就是音频质量对 Whisper 的影响权重高于模型大小。最简单的 ffmpeg 降噪可以这么写ffmpeg -i noisy.mp3 -af highpassf80,lowpassf8000,afftdnnf-25 -ar 16000 -ac 1 clean.wavafftdn是 ffmpeg 自带的降噪滤波器nf-25表示降噪强度数值可调。对普通录音这段命令就能把底噪压掉大半。如果你要处理的是远程会议软件录出来的音频建议先用speexdsp或 Adobe Audition 做修整再扔进 whisper.cpp。6.4 口音与专有名词没有银弹但可以“换模型反复验证”普通话标准、录音清晰那medium模型几乎能达到 95% 以上的字准率。但一旦视频里带点粤语腔、闽南腔识别结果就可能出现常识性错误而且这些错误有很强的“一本正经胡说八道”特点比如把“唔该”听成“五怪”。我的处理方法是对同一句话跑多个模型base 和 small然后人工对比选择合理版本。批量转写时也可以用 whisper.cpp 的--translate参数把中文翻译成英文但那个对中文翻译质量一般不如直接用翻译模型。7. 性能调优与嵌入到自己的程序7.1 线程与批处理之外还有几个隐藏加速参数除了-t你还可以试试-ps叫做 perplexity 之类的不展开、-c或--context设置上下文大小-bs设置 beam search 的 beam size。从更底层看ggml支持 OpenMP 和 Metal 两种并行方式。在 Linux 上确保编译时开启 OpenMPcmake -B build -DCMAKE_BUILD_TYPERelease -DGGML_OPENMPONWindows 的 CMake 默认也可能开启 OpenMP但 MSVC 需要安装 OpenMP 组件。如果你用的是 MinGW可能要多一步-fopenmp链接。想确认是否启用运行程序时日志会显示n_threads和processor。7.2 把 whisper.cpp 集成到 C/C 程序简洁的 API 设计这个项目的价值不只在于命令行。它的核心库提供了一套简单到不能再简单的 C 接口你可以把转录能力嵌入到你自己的工具里。基本流程是#include whisper.h struct whisper_context *ctx whisper_init_from_file(models/ggml-base.bin); struct whisper_full_params params whisper_full_default_params(WHISPER_SAMPLING_GREEDY); params.print_realtime false; params.language zh; if (whisper_full(ctx, params, samples, sample_count) ! 0) { // 处理错误 } int n_segments whisper_full_n_segments(ctx); for (int i 0; i n_segments; i) { const char *text whisper_full_get_segment_text(ctx, i); printf(%s\n, text); } whisper_free(ctx);这个 API 的坑在于输入采样率必须是 16kHz 单声道 float 数组。你用 ffmpeg 解码的时候就需要注意转成这个格式。官方示例examples/main/main.cpp里有完整的read_wav代码可以照抄。我做过一个小工具把系统麦克风实时采集流切成 5 秒一段循环喂给 whisper.cpp虽然延迟明显但作为演示足够。7.3 交叉编译与嵌入式树莓派上跑到什么程度树莓派 4B 上编译 whisper.cpp 很顺利用tiny模型转 10 秒音频需要 3 秒左右用base则要 15 秒实时率远达不到 1。树莓派 5 性能稍好一些但也不适合对长音频做实时转写。如果你真要在嵌入式设备上跑建议用tiny加量化q4_0并把线程数设为 4关闭实时打印。另外仓库里还有wgpu和Metal的相关示例不过那些都是实验性的稳定程度不如 CPU 路径。8. 常见问题排查我替你把这些坑都踩了一遍8.1 安装编译时报错找不到pthread或omp.hLinux 上如果报pthread找不到通常是缺少 glibc 的开发包执行sudo apt install libc6-dev就能解决。Windows 上如果报omp.h找不到需要确认 CMake 有没有找到 OpenMP。官方文档其实写了编译依赖新手容易忽略。我的建议是尽量使用 CMake 而不是手写 make因为 CMake 会自动探测 OpenMP。8.2 运行时提示failed to load model或invalid model file模型文件下载不完整是最常见原因。你可以跟 HuggingFace 上列出的 SHA 值比对一下或者用模型文件大小判断。还有一种情况是模型文件路径中带有中文或空格程序在读取时可能没问题但在解析路径时某些库会出幺蛾子。保险起见代码和模型路径都只用英文和下划线。8.3 识别结果全是空或只有标点这个问题之前困扰了我一段时间。后来发现不是因为模型问题而是输入音频采样率不对。whisper.cpp 内部强制要求 16kHz 单声道如果输入是 44.1kHz 立体声有时不会报错但解码出来是乱码或空白。先用 ffmpeg 转成标准格式再喂给模型基本都能解决ffmpeg -i input.mp3 -ar 16000 -ac 1 -f s16le output.raw如果你已经用了-f参数直接指向 mp3 文件程序会通过 ffmpeg libavformat 自动转但我在某些 mp3 变体上遇到过兼容性问题转成 wav 后就没再出现过。8.4 SRT 时间轴错乱如何修复whisper.cpp 的 SRT 输出具有 1 秒粒度的时间戳因为内部是按 32ms 的窗口扫描最后四舍五入到秒。如果你的视频字幕要求毫秒级精准可能需要用后处理来微调。我看到过有用户在 issue 里提交过精度问题官方回应是设计如此因为许多剪辑工具只需要秒级就够了。如果你想得到更精细的时间戳可以尝试把音频切成小块再分别转写但这样做会丢失跨片段上下文容易导致断句奇怪。我更推荐用--split-on-word之类的参数或者干脆接受秒级精度。9. 进阶用法一个自动批量生成视频字幕的工作流9.1 批处理脚本从视频目录到 SRT 全集如果你有一整个课程或播客系列需要转字幕手动一条条敲命令会累死。我在 Linux 和 macOS 下写了一个简单的批处理脚本放在仓库目录里跑#!/bin/bash set -e MODEL./models/ggml-small.bin INPUT_DIR./videos OUTPUT_DIR./subtitles mkdir -p $OUTPUT_DIR for file in $INPUT_DIR/*.mp4; do base$(basename $file .mp4) echo Processing $base ... ./build/bin/whisper-cli -m $MODEL -f $file -l zh -t 8 \ -of $OUTPUT_DIR/$base -srt -otxt done实际跑的时候记得把-t 8改成你机器的核心数否则太吃 CPU 会导致其他工作卡顿。Windows 下可以写 PowerShell 的Get-ChildItem循环逻辑一样。9.2 用 whisper.cpp 生成字幕之外还能干嘛很多人只拿它做字幕其实还能做语音搜索和关键词检索。转出来的文本带着时间戳把 JSON 格式输出打开-oj然后按时间索引就能做出一个带定位的语音检索系统。我就是用这个思路给一批课程视频生成了文字索引之后搜某个知识点时直接跳到对应时间点非常方便。输出 JSON 的命令./build/bin/whisper-cli -m ./models/ggml-base.bin -f lecture.mp4 -l zh -oj -of lectureJSON 里包含每一段的开始时间、结束时间和文本喂给任何搜索引擎或数据库都直接可用。9.3 与其他本地工具组合谁说一定要在线 API把 whisper.cpp 与本地大模型工具链组合起来能实现很多花活。比如生成字幕文本后用本地大模型做摘要、重点提取甚至翻译成其他语言。整个流程完全不依赖外部网络数据也不出本机。我在一些保密项目里就是这么干的先 whisper.cpp 出转写稿再用 Ollama 跑摘要再输出结构化会议纪要。你说它完美吗谈不上但贵在可控。10. 写在最后一点心得和工具推荐我自己是从 openai-whisper 转到 whisper.cpp 的最大的变化就是启动时间从 6 秒降到 0.3 秒内存占用从 2GB 降到 500MB。对于“日常短音频转文字”这个需求whisper.cpp 已经成了我电脑里的常驻工具。如果你平时剪视频、录播客、整理采访录音我强烈建议你一定要试一下base和small两个模型找到适合自己的那个平衡点。我个人的选择是闲聊和短视频脚本用base正式访谈和课程字幕用small再长的音频就分段跑同时搭配beam 5提升准确率。最后再分享一个小技巧如果你同时处理多个音频文件建议按批次把每个音频文件转成 16kHz 单声道 wav 后放到同一目录然后并行启动多个 whisper-cli 进程每个进程指定不同的-t参数这样能榨干多核 CPU 的最后一滴性能。别一次性开几十个进程四核机器跑两个进程就够了多了反而会因为内存带宽互相拖累。这个项目迭代速度很快文章里写的命令在后续版本里可能略有变化但核心思路是不会变的。