深色模式
语音 / ASR / TTS 集成
摘要:本文面向需要在生产环境落地语音能力的工程师。我们给出一个可拼接的语音流水线:OpenAI Whisper(ASR 语音识别)→ pyannote/speaker-diarization-3.1(说话人分离)→ 业务/LLM → CosyVoice 2(TTS 语音合成)。覆盖模型版本、预处理、串联代码、显存与成本、回滚与故障排查、以及声纹/合成内容的合规红线。命令与输出均标注
[未实测],版本相关项已显式标注。
适用版本与前提
- GPU:ASR/TTS 单卡 6–16GB 起步即可(medium ~5GB、large-v3 ~10GB、turbo ~6GB);pyannote 可 CPU 运行。
- 软件:
openai-whisper或faster-whisper(CTranslate2 加速)、pyannote.audio>=3.1、torch、torchaudio、cosyvoice(CosyVoice 2)。 - 已阅读 overview.md 了解语音链路「多模型流水线」范式。
许可是语音链路的硬门槛
- Whisper:MIT 许可,可商用自部署。
- pyannote/speaker-diarization-3.1:权重需在 Hugging Face 分别接受
pyannote/segmentation-3.0与pyannote/speaker-diarization-3.1用户协议后方可下载;pipeline 本身 MIT,但生产使用官方 README 提示可考虑其商业版。部署前请法务确认。 - CosyVoice 2:开源权重带社区许可,商用前核对仓库 LICENSE。
核心概念:语音链路四段式
语音不是单模型,而是四个可独立替换的阶段:
- ASR(语音→文本):Whisper。输入 16kHz 单声道音频,以 30s 为窗编码,输出文本 + 时间戳。
- 说话人分离 Diarization(可选):pyannote。输出「谁在什么时候说话」(RTTM)。
- 文本后处理 / LLM:把带说话人与时间戳的文本接入业务。
- TTS(文本→语音):CosyVoice 2。支持零样本音色克隆、流式合成(首包 ~150ms)。
架构/原理
Whisper:encoder-decoder Transformer
- 音频重采样到 16kHz 单声道,取 log-Mel 频谱:v1–v2 用 80 mel bins,large-v3 增至 128。
- 以 30 秒为窗分块;短于 30s 补零,长于 30s 顺序分窗。
- 单一模型多任务:通过特殊 token 支持「同名语言转录 / 翻译为英语 / 语言识别 / 时间戳预测」。
- 不原生做说话人分离——这是生产必须补 pyannote 的原因。
pyannote:纯 PyTorch 的分离流水线
- 3.1 版移除了对 onnxruntime 的依赖,分割与嵌入均用 PyTorch,部署更顺。
- 输入 16kHz 单声道;立体声自动下混为单声道、异采样率自动重采样。
- 默认 CPU 运行,需
.to(cuda)才用 GPU。 - 支持
num_speakers/min_speakers/max_speakers约束。
CosyVoice 2:LLM + 流匹配 TTS
- 用 FSQ Speech Tokenizer(码本 6561,100% 激活)替代 VQ,提升发音准确率。
- 以 Qwen2.5-0.5B 为文本 backbone,统一支持流式/非流式。
- 首包延迟 ~150ms(官方称),支持情感/口音等细粒度指令,支持零样本音色克隆。
模型版本速查(来源:OpenAI 论文 arXiv:2212.04356 + 模型卡 / pyannote README / CosyVoice 2 论文 arXiv:2412.10117)
- Whisper 尺寸:tiny 39M、base 74M、small 244M、medium 769M、large 1550M;large-v3(2023-11,128 mel bins,99 语,WER 较 v2 降 10–20%)、large-v3-turbo(2024-10,decoder 砍到 4 层、809M、约 8× 提速,不支持翻译)。
- Whisper WER(公开基准,属上限非真实分布):LibriSpeech test-clean ~2.7%、HF Open ASR 均值 ~7.44%、AMI 会议 ~15.95%。
- pyannote 3.1 DER(无 collar、含重叠语音的「Full」设定,来源 README):VoxConverse ~11.3%、DIHARD3 ~21.7%、AMI headset ~18.8%。
生产实践(::: tip)
落地建议
- 加 VAD 前置:Whisper 在静音段会幻觉出大段文本。用 pyannote VAD 或 Silero VAD 先切掉无语音段,再送 ASR。
- 先分离还是先识别? 常见顺序:VAD → ASR(整段)→ pyannote(整段)→ 按时间戳对齐,把每段文本归到说话人。也可用 WhisperX 之类封装好的对齐方案(下游第三方,
[未实测],需自行验证)。 - 长音频分片:Whisper 30s 窗串行,长文件建议并行分片 + 时间戳拼接;注意切点在词中间会断词,优先切在静音处。
- TTS 音色管理:零样本克隆需用户提供参考音频,注意留存授权与用途声明。
操作步骤 / 配置
1. 安装
bash
# ASR:官方 whisper 或更快的 faster-whisper
pip install openai-whisper # 官方实现(需 ffmpeg)
pip install faster-whisper # CTranslate2 加速版(可选)
# 说话人分离
pip install "pyannote.audio>=3.1" torchaudio
# TTS
pip install cosyvoice # 或按官方仓库 README 安装 [版本相关]
# 系统依赖
# ffmpeg 必须可用:apt-get install ffmpeg (Linux) / brew install ffmpeg (macOS)
ffmpeg -version1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
ffmpeg 缺失
Whisper 依赖 ffmpeg 读音频。容器镜像里常缺,务必在 Dockerfile 装好,否则运行时才报「FileNotFound」。
2. Whisper 转录(含时间戳)
python
import whisper
model = whisper.load_model("large-v3") # 也可 medium / large-v3-turbo
# 自动语言检测;如需固定语言:model.transcribe(path, language="zh")
result = model.transcribe("meeting.wav", verbose=False)
for seg in result["segments"]:
print(f"[{seg['start']:.1f}-{seg['end']:.1f}] {seg['text']}")1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
用 faster-whisper(更低显存、更快,[未实测]):
python
from faster_whisper import WhisperModel
model = WhisperModel("large-v3", device="cuda", compute_type="float16")
segments, info = model.transcribe("meeting.wav", beam_size=5)
for s in segments:
print(f"[{s.start:.1f}-{s.end:.1f}] {s.text}")1
2
3
4
5
6
2
3
4
5
6
3. pyannote 说话人分离
python
from pyannote.audio import Pipeline
import torch
# 需先在 HF 接受 pyannote/segmentation-3.0 与 speaker-diarization-3.1 协议
pipeline = Pipeline.from_pretrained(
"pyannote/speaker-diarization-3.1",
use_auth_token="hf_你的TOKEN", # 也可用 HF_TOKEN 环境变量
)
pipeline.to(torch.device("cuda")) # 默认 CPU,显式上 GPU
diarization = pipeline("meeting.wav", min_speakers=1, max_speakers=5)
# diarization 为 Annotation;可导出 RTTM
with open("meeting.rttm", "w") as f:
diarization.write_rttm(f)
for turn, _, speaker in diarization.itertracks(yield_label=True):
print(f"[{turn.start:.1f}-{turn.end:.1f}] {speaker}: ...")1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
4. 把 ASR 与分离对齐(归并到说话人)
python
# 思路:用 pyannote 的时间区间,截取 Whisper 落在其中的 segment 文本
def merge_asr_diarization(asr_segments, diarization):
out = []
for turn, _, speaker in diarization.itertracks(yield_label=True):
text = " ".join(
s["text"] for s in asr_segments
if s["end"] > turn.start and s["start"] < turn.end
)
if text.strip():
out.append((speaker, turn.start, turn.end, text))
return out
# 结果示例:[SPEAKER_1, 0.0, 12.3, "今天我们讨论上线计划……"]1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
5. CosyVoice 2 语音合成(TTS)
bash
# 参考官方仓库启动推理 / 服务(具体 CLI 以仓库 README 为准,[版本相关])
# 零样本克隆:提供 prompt 音频 + 文本;流式首包约 150ms(官方称,[未实测])1
2
2
python
# 伪代码示意(API 形态以 CosyVoice 2 官方仓库为准,[未实测][版本相关])
from cosyvoice.cli.cosyvoice import CosyVoice2
model = CosyVoice2("FunAudioLLM/CosyVoice2-0.5B")
# inference_instruct / inference_zero_shot 等接口1
2
3
4
2
3
4
CosyVoice 2 能力(来源:官方主页 / 论文)
支持零样本音色克隆、跨语言合成、情感/口音指令控制;流式首包 ~150ms;MOS 评测约 5.53(接近商业系统);在 Seed-TTS 难度测试集上创最低字错误率。具体接口与权重名以官方仓库为准,[版本相关]。
验证
bash
# 1. 环境检查
ffmpeg -version
python -c "import whisper, pyannote.audio, torch; print('ok', torch.cuda.is_available())"
# 2. pyannote 令牌与许可校验(报错 401 见故障排查)
python -c "from huggingface_hub import HfApi; \
api=HfApi(); print(api.model_info('pyannote/speaker-diarization-3.1', token='hf_你的TOKEN'))"
# 3. 端到端冒烟:准备一段 16kHz 单声道 wav,跑 ASR+分离,确认输出非空1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
验证清单
- [ ] ffmpeg 可用,音频能被读取。
- [ ] Whisper 输出带时间戳的文本,静音段未产生长段幻觉(已加 VAD 则更干净)。
- [ ] pyannote 正常返回 SPEAKER 标签;约束
num_speakers生效。 - [ ] ASR 与分离时间对齐后,文本能正确归到说话人。
- [ ] TTS 产出可播放音频,音色与参考一致(若用克隆)。
回滚 / 清理
变更与资源清理
- 模型升级(如 large-v2 → large-v3-turbo)会改变输出风格与是否支持翻译(turbo 不支持翻译),切量前务必离线比对。
- 磁盘:Whisper large-v3 约 3GB、pyannote 约 2GB(diarization 1.5GB + segmentation 0.5GB)、CosyVoice 2 权重按 backbone 数 GB;清理
~/.cache/huggingface前确认无在线任务。 - 进程:长音频批处理可能占满 CPU/GPU,用 cgroup / 限流避免影响同机其它服务。
- 回滚:保留旧模型名与启动参数,按流量切回。
故障排查
| 现象 | 可能原因 | 处理 |
|---|---|---|
| Whisper 静音段输出长串乱码 | 模型幻觉 | 加 VAD 前置过滤无语音段 |
401 Unauthorized(pyannote) | token 无权限 / 未接受协议 | 在 HF 接受两个模型协议;等几分钟缓存生效;确认 token 为 hf_ 开头且有 Read 权限 |
KeyError: 'qwen2_vl' | (ASR 链路不相关)误用 VLM 依赖 | 与本篇无关,见 vlm.md |
| TTS 首包慢 | 未走流式 / 冷启动 | 用流式接口;预热;检查 GPU 是否到位 |
| 长音频 OOM / 超时 | 30s 窗串行过长 | 并行分片 + 时间戳对齐;限制并发 |
| 说话人标错/过多 | 噪声/重叠/人数估计偏差 | 用 min/max_speakers 约束;提升音频质量 |
计数与 3D 空间弱项
Whisper 在计数、人物识别、3D 空间感知上相对薄弱(官方 Qwen2-VL 博客也自陈类似局限)。会议/客服等强结构化场景,建议下游用规则或 LLM 做后校验,勿直接信任原始输出。
安全与合规
声纹与合成内容是高压线
- 声纹 = 生物识别信息:pyannote 分离出的说话人标签 + 音频本身,在多数司法辖区属个人敏感信息。须告知用户、取得同意、最小化留存、加密存储。
- 语音不出域:会议/通话含高度敏感内容,ASR 与分离务必自部署,禁止送第三方 API(除非已签 DPA)。
- TTS 深度伪造风险:零样本音色克隆可被用于仿冒。必须:仅克隆已授权音色、在合成内容中可标识、留存用途与授权记录、限制调用权限。
- 传输与日志:音频与转录文本走 TLS/内网;日志禁止明文落盘原始音频路径与转录全文(尤其含 PII 的)。
- 留存最小化:转录/分离结果按数据保留策略定时清理,不长期囤积。
成本 / 性能
- 显存:Whisper medium ~5GB、large-v3 ~10GB、turbo ~6GB;pyannote 约 2GB(可 CPU);CosyVoice 2 按 backbone 数 GB。
- 速度:large-v3-turbo 相对 large-v3 约 8× 提速(官方称,
[未实测]);turbo 不支持翻译任务,多语言翻译场景勿用。 - CPU vs GPU:pyannote 默认 CPU,长音频很慢,上 GPU 显著加速;Whisper GPU 收益明显。
- 吞吐:批量长音频建议分片并行 + 批处理;单请求 30s 窗串行是主要时延来源。
- 量化:
faster-whisper的float16/int8可降显存提速度,但低资源语言可能掉点,需验证。