VPS自托管Whisper语音转文字和Piper TTS
在无GPU的普通CPU VPS上运行 faster-whisper 和 Piper,了解模型磁盘占用、CPU转录速度及如何部署兼容 OpenAI 的 HTTP 接口。
简述:自托管语音转文字和 TTS
自托管语音转文字和 TTS(文字转语音)是可以在自有服务器上运行的成本最低的一类 AI。转录通过 faster-whisper 运行时调用 Whisper。语音合成使用 Piper。两者都可在完全没有 GPU 的普通 CPU VPS 上运行。较小的 Whisper 模型需要约 484 MB 磁盘空间,Piper 语音模型是一个远低于 150 MB 的文件。
因此,音频适合作为起点。自托管图像生成器和自托管视频生成在实际可用前都需要 GPU。音频不需要。
本指南涵盖这两个方向,以及连接它们的中间层。Whisper 将音频转换为文字。Piper 将文字转换为音频。在两者前面部署一个兼容 OpenAI 的 HTTP 服务器后,现有客户端只需修改 base URL,即可指向您的服务器,其他内容无需更改。
本文提及的所有版本截至 2026 年 8 月均为当前版本。
为什么选择 faster-whisper,而不是参考 Whisper 软件包
OpenAI 的 whisper 软件包使用 PyTorch 运行模型。faster-whisper 则在 CTranslate2 上运行相同的模型权重。CTranslate2 是专为 transformer 模型编写的推理引擎。两者的权重完全相同,因此转录结果也相同。差异完全来自运行时。
CTranslate2 会在加载权重时进行量化,因此 compute_type="int8" 只需一个参数,不需要单独的转换步骤。它也不依赖 PyTorch。pip install faster-whisper 会安装 CTranslate2、tokenizer 和用于音频解码的 PyAV,因此虚拟环境大小为数百 MB,而不是数 GB。对于使用 40 GB 磁盘的 VPS,这一差异决定了磁盘空间是充裕还是紧张。
下面是该项目针对 small 模型在 CPU 上发布的实测数据。基准测试使用 Intel Core i7-12700K 的 8 个线程转录 13 分钟音频。x_realtime 列表示 13 分钟音频时长除以实测耗时:7.6 表示一段 13 分钟的录音在略多于 1 分钟 40 秒内完成。
The data behind this chart
[
{
"label": "openai/whisper, fp32",
"x_realtime": 1.9,
"seconds": 418,
"memory_mb": "2,335"
},
{
"label": "whisper.cpp, fp32",
"x_realtime": 6.2,
"seconds": 125,
"memory_mb": "1,049"
},
{
"label": "faster-whisper, fp32",
"x_realtime": 5.0,
"seconds": 157,
"memory_mb": "2,257"
},
{
"label": "faster-whisper, int8",
"x_realtime": 7.6,
"seconds": 102,
"memory_mb": "1,477"
},
{
"label": "faster-whisper, int8, batch 8",
"x_realtime": 15.3,
"seconds": 51,
"memory_mb": "3,608"
}
]请如实查看第二行。在 fp32 下,whisper.cpp 在这台 CPU 上比 faster-whisper 更快,分别为 6.2x 和 5.0x 实时速度,而且内存占用不到后者的一半。它使用的是与 本地 LLM 技术栈中的 llama.cpp 部分相同的 ggml 系列。启用 int8 和批处理后,faster-whisper 领先,达到 15.3x 实时速度,但需要占用 3,608 MB RAM。因此,如果需要 Python API 和批处理,请选择 faster-whisper;如果 RAM 是无法增加的约束,请选择 whisper.cpp。
第一行才是本节的重点。在同一台机器上,参考软件包的速度是 1.9x 实时速度。这意味着 1 小时音频需要半小时 CPU 时间。
Whisper 模型权重需要多少磁盘空间?
The data behind this chart
[
{
"label": "tiny",
"weights_mb": 75.5
},
{
"label": "base",
"weights_mb": 145
},
{
"label": "small",
"weights_mb": 484
},
{
"label": "medium",
"weights_mb": "1,530"
},
{
"label": "large-v3",
"weights_mb": "3,090"
}
]以下是已发布的 CTranslate2 float16 转换版本。请注意 compute_type="int8" 不会做什么:它不会缩小下载文件。权重以 float16 格式下载,CTranslate2 在加载时于内存中对其进行量化。因此,无论以 float16 还是 int8 运行,large-v3 在磁盘上都需要 3,090 MB。int8 可以减少 RAM 占用并提升速度,但不会减少磁盘占用。
模型首次使用时会下载到 ~/.cache/huggingface/hub。如果 VPS 的根卷空间较小,请使用 download_root 参数或 HF_HOME 环境变量,将其指向有足够空间的位置。否则,首次运行会耗尽磁盘空间,进程会在下载过程中途退出。
在不破坏系统 Python 的情况下安装 faster-whisper
Ubuntu 24.04 和 Debian 13 将系统 Python 标记为由外部管理。在虚拟环境之外运行 pip install faster-whisper 会立即停止,并显示:
error: externally-managed-environment这是打包系统在保护由 apt 管理的文件。请使用虚拟环境。
sudo apt update
sudo apt install -y python3-venv ffmpeg
python3 -m venv ~/stt
~/stt/bin/pip install --upgrade pip
~/stt/bin/pip install faster-whisper==1.2.11.2.1 是当前版本,于 October 2025 发布。faster-whisper 通过 PyAV 解码音频。PyAV 自带 ffmpeg 库,因此无需单独安装 ffmpeg 二进制文件即可读取 mp3 或 m4a。上面的 ffmpeg 软件包用于本指南后面的处理视频步骤。
在下载三 gigabytes 的模型权重前,先检查安装结果:
~/stt/bin/python -c "from faster_whisper import WhisperModel; print('ok')"如果显示包含 ok 的行,说明 wheel 已成功安装。如果显示包含 ImportError 且命名为 ctranslate2,说明适用于当前架构的 wheel 未安装。这种情况会发生在 32-bit ARM 镜像上。
转录会议录音或语音备忘录
将以下内容保存为 transcribe.py:
from faster_whisper import WhisperModel
model = WhisperModel("small", device="cpu", compute_type="int8", cpu_threads=4)
segments, info = model.transcribe("meeting.m4a", beam_size=5, vad_filter=True)
print("language: %s (%.2f)" % (info.language, info.language_probability))
for segment in segments:
print("[%.2fs -> %.2fs] %s" % (segment.start, segment.end, segment.text))使用 ~/stt/bin/python transcribe.py 运行。首次运行会下载模型权重,因此一段时间内不会输出任何内容。之后,模型会在几秒内从缓存加载。
segments 是一个生成器,因此必须对其进行迭代后才会开始转录。有人会统计 transcribe() 调用的耗时,看到它立即返回,就以为程序出错了。实际上没有出错。处理过程发生在循环中。
vad_filter=True 会先运行 Silero VAD(语音活动检测),在 Whisper 处理音频前删除静音片段。对于存在较长间隔的会议录音,这是最大的单项提速方法,因为 Whisper 完全不会处理不含语音的音频。这样还可以抑制 Whisper 在接收到静音并尝试从中识别词语时产生的重复句循环。
将 cpu_threads 设置为实际拥有的 CPU 核心数。将其设置为高于 vCPU 数量会使转录变慢,因为额外线程会争用同一个核心,调度器还要为每次线程切换付出开销。
Jellyfin 媒体库的字幕
Jellyfin 会读取放在视频旁边且与视频同名的外部字幕文件,因此,将 Movie (2019).en.srt 放在 Movie (2019).mkv 旁边后,它会显示为英语字幕轨道,无需转码,也无需重建媒体库。
先提取音频。Whisper 会在内部将所有音频重采样为 16 kHz 单声道,因此直接提供 16 kHz 单声道音频,可以减少两端的处理开销:
ffmpeg -i "Movie (2019).mkv" -vn -ac 1 -ar 16000 -c:a pcm_s16le "Movie (2019).wav"faster-whisper 不提供 SRT 写入器,因此需要自行格式化各个片段。将以下内容保存为 srt.py:
import sys
from faster_whisper import WhisperModel
def ts(seconds):
ms = int(round(seconds * 1000))
hours, ms = divmod(ms, 3600000)
minutes, ms = divmod(ms, 60000)
secs, ms = divmod(ms, 1000)
return "%02d:%02d:%02d,%03d" % (hours, minutes, secs, ms)
model = WhisperModel("small", device="cpu", compute_type="int8")
segments, info = model.transcribe(sys.argv[1], vad_filter=True)
with open(sys.argv[2], "w", encoding="utf-8") as out:
for index, segment in enumerate(segments, start=1):
out.write("%d\n" % index)
out.write("%s --> %s\n" % (ts(segment.start), ts(segment.end)))
out.write("%s\n\n" % segment.text.strip())然后遍历媒体库:
for f in /srv/media/films/*.mkv; do
ffmpeg -nostdin -y -i "$f" -vn -ac 1 -ar 16000 -c:a pcm_s16le "${f%.mkv}.wav"
nice -n 15 ~/stt/bin/python srt.py "${f%.mkv}.wav" "${f%.mkv}.en.srt"
rm -f "${f%.mkv}.wav"
done-nostdin 并非装饰性参数。如果省略它,ffmpeg 会从循环的标准输入读取数据,吞掉文件列表的其余内容,导致循环无错误地在处理一部影片后停止。
开始前先评估所需时间。按照上面的 7.6x 速度,一部 100 分钟的影片大约需要 13 分钟 CPU 时间,因此处理一个包含 50 部影片的媒体库需要运行一整夜。在共享 vCPU 方案上,速度还会更慢;这正是 nice 的作用:字幕处理会让出资源,不影响服务器执行其他实际任务。
使用 Piper 进行文本转语音
Piper 是一种神经网络文本转语音引擎,可在 CPU 上运行 ONNX 语音模型。它内置 espeak-ng,可将文本转换为音素,因此无需单独安装音素化器。当前版本为 1.6.0,发布于 July 2026。
python3 -m venv ~/tts
~/tts/bin/pip install piper-tts==1.6.0
~/tts/bin/python -m piper.download_voices en_US-lessac-medium
~/tts/bin/python -m piper -m en_US-lessac-medium -f test.wav -- 'This is a test.'download_voices 会在工作目录中写入两个文件:.onnx 权重文件和 .onnx.json 配置文件。配置文件包含采样率和说话人列表。这两个文件必须放在一起。如果将语音文件保存在固定目录中,请在两个命令中都传入 --data-dir,否则播放器会在工作目录中查找,找不到不在该目录中的语音文件。
文本前的 -- 也很重要。没有它时,任何以连字符开头的句子都会被解析为命令行选项。
语音模型提供多个质量级别。中等质量的英语语音模型约为 60 MB,高质量模型约为其两倍。更高的质量意味着模型更大,并且每秒语音需要更多 CPU,而不是使用不同的说话人。
不要在循环中调用 CLI。每次调用都会加载模型,而对于短句,模型加载占据了主要开销。请改为运行 HTTP 服务器:
~/tts/bin/pip install 'piper-tts[http]==1.6.0'
~/tts/bin/python -m piper.http_server -m en_US-lessac-medium --host 127.0.0.1 --port 5000curl -X POST -H 'Content-Type: application/json' \
-d '{ "text": "This is a test." }' \
-o test.wav localhost:5000/synthesize如果生成的 test.wav 可以播放,就说明运行正常。该端点使用的是 Piper 自己的格式,不是 OpenAI 的格式,因此期待 /v1/audio/speech 的客户端无法与其通信。下一节将解决这个问题。
使用 unit 文件让它持续运行,不要依赖即将关闭的终端:
[Unit]
Description=Piper text to speech HTTP server
After=network-online.target
[Service]
User=piper
ExecStart=/home/piper/tts/bin/python -m piper.http_server -m en_US-lessac-medium --data-dir /home/piper/voices --host 127.0.0.1 --port 5000
Restart=on-failure
[Install]
WantedBy=multi-user.targetsudo systemctl enable --now piper 会启动它,并在重启后将其恢复运行。如果上面的 curl 没有返回内容,journalctl -u piper -n 50 中会记录原因;最常见的原因是缺少语音文件。
兼容 OpenAI 的服务器形式
大多数处理音频的软件已经支持 OpenAI 音频 API:向 /v1/audio/transcriptions 发送 multipart POST 请求来处理文件,向 /v1/audio/speech 发送 JSON POST 请求来处理句子。自行提供这两个路径后,客户端只需修改一项配置,即基础 URL。
Speaches 是一个同时支持两个方向的服务器。它在 OpenAI 路径后运行 faster-whisper 进行转录,并使用 Piper 或 Kokoro 生成语音。当前版本是 2025 年 12 月发布的 v0.9.0-rc.3,仍处于 1.0 之前。因此请固定镜像标签,并在升级前阅读发行说明。
curl --silent --remote-name https://raw.githubusercontent.com/speaches-ai/speaches/master/compose.yaml
curl --silent --remote-name https://raw.githubusercontent.com/speaches-ai/speaches/master/compose.cpu.yaml
export COMPOSE_FILE=compose.cpu.yaml
docker compose up --detach如果不想维护 compose 文件,也可以使用单容器形式:
docker run --rm --detach --publish 8000:8000 --name speaches \
--volume hf-hub-cache:/home/ubuntu/.cache/huggingface/hub \
ghcr.io/speaches-ai/speaches:latest-cpu命名卷是最容易被遗漏的部分。没有它,模型缓存会存储在容器内部。因此每次重启后,服务器都必须重新下载数 GB 的权重,之后才能响应第一个请求。
在 /v1/audio/speech 能够响应之前,语音功能需要先下载一个声音:
uvx speaches-cli model download speaches-ai/Kokoro-82M-v1.0-ONNX完成后,任何 OpenAI 客户端都可以使用,只需修改基础 URL:
from pathlib import Path
from openai import OpenAI
openai = OpenAI(base_url="http://localhost:8000/v1", api_key="cant-be-empty")
res = openai.audio.speech.create(
model="speaches-ai/Kokoro-82M-v1.0-ONNX",
voice="af_heart",
input="Hello, world!",
response_format="mp3",
speed=1,
)
with Path("output.mp3").open("wb") as f:
f.write(res.response.read())必须填写占位符 api_key,因为 OpenAI 客户端库拒绝发送不带该字段的请求。在配置真实密钥之前,服务器会忽略其值。
这里还值得介绍另一个项目:vox-box。它是 Python 软件包,而不是容器,并通过 Whisper、FunASR、Bark、Dia 或 CosyVoice 提供相同的路径。当前版本是 2025 年 12 月发布的 0.0.21,需要 Python 3.10 或更高版本。
python3 -m venv ~/voice
~/voice/bin/pip install vox-box==0.0.21
~/voice/bin/vox-box start --huggingface-repo-id Systran/faster-whisper-small \
--data-dir ~/voice/data --host 127.0.0.1 --port 8010该项目自带的示例绑定到 80 端口,因此需要 root。在还运行其他服务的服务器上,更适合让反向代理转发到高位端口。vox-box start 一次只能使用一个模型,因此要覆盖两个方向,需要在第二个端口上运行第二个实例,并为其指定语音仓库 ID。
先询问服务器已加载的内容,然后将该 ID 填入 model 字段:
curl http://127.0.0.1:8010/v1/modelscurl http://127.0.0.1:8010/v1/audio/transcriptions \
-H "Content-Type: multipart/form-data" \
-F file="@voice-note.m4a" \
-F model="faster-whisper-small"形式为 {"text": "..."} 的 JSON 请求体表示整个路径都正常工作。如果模型名称返回 404,说明你是在猜模型名称,而不是读取 /v1/models 的输出。
这些服务器默认都不会启用身份验证。请将它们绑定到 127.0.0.1,并通过 VPN 或要求凭据的反向代理访问。将 /v1/audio/transcriptions 暴露在公网 IP 上,相当于为任何找到它的人提供免费的 CPU;而他们一定会找到它。
自托管助手的语音前端
当两个方向都能通过 OpenAI 路径正常响应后,就可以使用聊天前端驱动它们。Open WebUI 及其替代方案可在设置中接受兼容 OpenAI 的音频基础 URL,因此一台服务器即可使用 Ollama 运行本地 LLM,在两端完成语音闭环。
请如实评估延迟,因为这 3 个步骤会在同一组 CPU 核心上依次运行。一个 10 秒的问题,按上文 7.6x 的实时性能计算,转录大约需要 1.3 秒,而且这还没开始让模型读取任何 token。再加上 CPU 生成 token 的时间,整个往返过程会慢到让测试人员以为服务已经崩溃。批量转录在 CPU 上运行没有问题,但实时对话不行。这时,配备 GPU 的 VPS才开始体现其价格价值。
GPU 什么时候才真正值得使用?
The data behind this chart
[
{
"label": "openai/whisper, fp16",
"x_realtime": 5.5,
"seconds": 143,
"memory_mb": "4,708"
},
{
"label": "faster-whisper, fp16",
"x_realtime": 12.4,
"seconds": 63,
"memory_mb": "4,525"
},
{
"label": "faster-whisper, int8",
"x_realtime": 13.2,
"seconds": 59,
"memory_mb": "2,926"
},
{
"label": "faster-whisper, int8, batch 8",
"x_realtime": 48.8,
"seconds": 16,
"memory_mb": "4,500"
}
]在 GPU 上,大型模型以 int8 运行时,速度达到实时的 13.2 倍,占用 2,926 MB VRAM;启用批处理后可达到 48.8 倍。请特别注意两张图表没有说明什么。它们使用的是不同模型:GPU 行使用 large-v2,CPU 行使用 small。GPU 不会让 small 模型快 6 倍,而是让高准确率模型变得可用。
这些数字的来源
两张图表都复现了 faster-whisper README 中发布的基准测试。音频是一个 13 分钟的单文件。CPU 行在 Intel Core i7-12700K 上使用 8 个线程,GPU 行在配备 8 GB VRAM 的 NVIDIA RTX 3070 Ti 上使用 CUDA 12.4。秒数和内存列采用已发布的数据。x_realtime 列是在此基础上计算得出:用 780 秒音频时长除以实测时间,并四舍五入到 1 位小数。CPU 图表中的内存列表示系统 RAM,GPU 图表中的内存列表示 VRAM。
共享 vCPU 方案无法达到上述 CPU 数据。该基准测试独占使用了高性能桌面处理器上的 8 个线程。请将 7.6 倍视为上限,并先使用 time 在真实录音上测试自己的服务器,再据此制定计划。
以下 4 条经验规则在实践中通常成立:
- 偶尔转写自己的录音:使用 CPU、small 和 int8。2 个专用 vCPU 足够。
- 需要连夜批量处理,例如批量生成字幕:使用 CPU、small 或 medium、int8,并通过
nice运行。 - 任何交互式任务,或希望在一个晚上处理完整个音频库:使用 GPU。
- Piper:始终使用 CPU。这种大小的语音模型几乎无法从 GPU 中获得收益。
如果您正在评估同一硬件还能承载哪些任务,请参阅关于哪些 AI 模型可以自行托管的更广泛问题,其中介绍了能够或无法容纳的模型大小。
故障模式及对应的错误信息
error: externally-managed-environment 安装失败。系统 Python 受到发行版保护。请按照上文所示创建虚拟环境。
GPU 主机上的 cuDNN 不匹配。 故障信息如下:
Unable to load any of {libcudnn_ops.so.9.1.0, libcudnn_ops.so.9.1, libcudnn_ops.so.9, libcudnn_ops.so}
Invalid handle. Cannot load symbol cudnnCreateTensorDescriptorCTranslate2 4.5.0 及更高版本在 CUDA 12 下要求 cuDNN 9,而主机安装的是 cuDNN 8。请安装 cuDNN 9,或使用 pip install --force-reinstall ctranslate2==4.4.0 固定到较旧的运行时版本。只能选择其中一种。两者同时使用会回到原来的状态。
This CTranslate2 package was not compiled with CUDA support 是另一种故障,但表现相似。已安装的 wheel 是仅支持 CPU 的构建版本。请在 GPU 主机上重新创建虚拟环境,让 pip 再次选择 wheel。
转录文本中反复出现相同短语。 一句话循环出现十次,几乎总是因为静音或音乐被当作语音解码。请先启用 vad_filter=True。如果问题仍然存在,请试听对应片段:接近静音的音频无法为 Whisper 提供足够的依据,因此它会重复上一次有把握的猜测。
检测到的语言错误。 Whisper 只根据前 30 秒进行猜测。info.language_probability 明显低于 1.0,表示模型不确定;录音开头包含音乐或串音时就会出现这种情况。如果已经知道语言,请传入 language="en"。
进程输出 Killed 后停止。 这是内核的内存不足终止器导致的,dmesg 会显示对应的 oom-kill 日志行。仅加载权重时,large-v3 就需要超过 3 GB 内存,此外还需要工作内存。在 2 GB VPS 上,使用 int8 的 small 是能够运行的最大模型。
Piper 找不到语音。 除非传入 --data-dir,否则播放器会在当前工作目录中查找。请对预期目录运行 ls,确认 .onnx 和 .onnx.json 都存在;只存在其中一个会失败,两个都不存在也一样。
FAQ
可以在仅使用 CPU 的 VPS 上运行语音转文本吗?
可以。对于批处理任务,这是合理的选择。使用 int8 的 small 模型时,项目发布的基准测试显示,在桌面 i7 的 8 个线程上,转录 13 分钟音频需要 102 秒,约为实时速度的 7.6 倍,占用 1,477 MB 内存。共享 vCPU 方案的速度会更慢,因此请在自己的服务器上进行测量。Piper 的文本转语音更简单,在任何情况下都不需要 GPU。
应该使用哪种 Whisper 模型大小?
从 int8 的 small 模型开始。该模型占用 484 MB 磁盘空间,处理清晰的录音语音效果良好。如果口音或背景噪声导致无法接受的错误,请升级到 medium;只有在准确率比其他因素都重要时,才使用 large-v3,因为它需要 3,090 MB 磁盘空间和超过 3 GB 内存。反过来,75.5 MB 的 tiny 模型适合语言检测和测试处理流程,不适合生成供人阅读的转录文本。
为什么 faster-whisper 比原始 Whisper 软件包更快?
模型相同,但运行时不同。参考软件包在 PyTorch 中运行 Whisper,而 faster-whisper 使用 CTranslate2 运行相同的权重。CTranslate2 是为 transformer 推理构建的引擎,会在加载时对权重进行量化,并且不需要安装 PyTorch。在已发布的 CPU 基准测试中,参考软件包的速度为实时速度的 1.9 倍,而使用 int8 的 faster-whisper 为 7.6 倍,同时内存占用更低。
为什么我的转录文本会反复重复同一句话?
Whisper 将静音或音乐误识别为语音,并反复使用上一次有把握的猜测。在 transcribe() 调用中设置 vad_filter=True。该设置会先运行 Silero 语音活动检测,在模型处理音频前移除静音片段。如果仍然重复,请检查音频电平,因为录音如果几乎全程静音,模型就没有可供处理的有效内容。
如何在自己的服务器上提供兼容 OpenAI 的音频端点?
运行实现 POST /v1/audio/transcriptions 和 POST /v1/audio/speech 的服务器,然后通过新的基础 URL 将客户端指向该服务器。Speaches 是一种选择,它以容器镜像形式发布,并提供 CPU 构建,使用 faster-whisper 进行转录,使用 Piper 或 Kokoro 进行语音处理。vox-box 是另一种选择,可通过 pip install vox-box 安装,并使用 vox-box start --huggingface-repo-id 启动;它为每个进程提供一个模型。两者默认都不会启用身份验证,因此请绑定到 localhost,并在前面配置代理或 VPN。