
零样本 TTS 的开源生态最近几年爆发得厉害——F5-TTS、CosyVoice 2、XTTS-v2、Bark、Index-TTS 2 一堆能给一段 5 秒的 prompt 音频、克隆音色念任意文本的模型陆续开源。但生态里始终缺一件事统一的客观评测集——每家 demo 页放的测试句都不同数字互不可比想跑 MOS 主观打分又贵又慢。字节跳动 2024 年 6 月放出 Seed-TTS 论文 时同步开源了 seed-tts-eval——这是 Seed-TTS 论文附带的客观评测集加打分脚本。有意思的是项目里明确说明出于 AI safety 考虑不发布 Seed-TTS 本体的模型权重和代码——这个只放评测、不放模型的选择反而让 seed-tts-eval 变成了一把中立的第三方尺子——任意 zero-shot TTS 模型都可以用同一套数据加同一套 metric 出数字再对着论文里的 Seed-TTS 打分看差距。这篇博客把 seed-tts-eval 完整跑通一遍数据集怎么组织、WER 和 SIM 两个 metric 怎么算、然后拿 3 个中英双语的开源 zero-shot TTS 模型(F5-TTS、XTTS-v2、CosyVoice 2)在这套评测集上跑出真实数字。一、结论先行3 个开源 TTS 的实测选型先把结论摆出来。用 seed-tts-eval 的 mini 抽样(EN 20 条加 ZH 20 条加 hardcase 10 条 · 共 50 条)跑 3 个中英双语 zero-shot TTS拿到的真实 WER 和 SIM 数字模型EN WER↓ZH WER↓hardcase WER↓ZH SIM↑一句话选型F5-TTS2.57%1.51%9.70%0.756EN 场景首选 · hardcase 稳定性也强 · Apache 2.0 免费商用CosyVoice 25.50%0.47%12.18%0.742ZH 常规场景碾压 · Apache 2.0 免费商用XTTS-v25.65%1.31%15.08%0.618唯一覆盖 EN 加 ZH 之外的其他语言 · CC BY-NC 禁商用3 条关键选型判断——英语场景选 F5-TTS——EN WER 2.57% 全场最低ZH SIM 0.756 全场最高Apache 2.0中文常规场景选 CosyVoice 2——ZH WER 0.47% 一骑绝尘但 hardcase 掉分 25.9 倍 · 遇到长句加绕口令要小心多语言(西/法/德等)只能选 XTTS-v2——虽然 WER 略差但唯一覆盖 EN 加 ZH 之外的语言做商业产品别用(CC BY-NC)评测数据全部实测——测试集 3000 条、Whisper-large-v3 加 Paraformer-zh 打 WER、WavLM-large 打 SIM。想给自己的 TTS 出这套数字继续往下读——项目介绍加装依赖加完整 pipeline 加端到端 recipe 都在。二、seed-tts-eval 的四大能力2.1 客观评测数据集(3 档加 VC 子集)seed-tts-eval 提供 3 档 zero-shot TTS 测试集加 2 档语音转换(VC)测试集。子集文件条数数据来源内容特点EN test-ttsen/meta.lst1088Common Voice英文日常句ZH test-ttszh/meta.lst2020DiDiSpeech-2中文日常句ZH hardcasezh/hardcase.lst400DiDiSpeech-2 加自采绕口令 / 长句 / 重复字EN VCen/non_para_reconstruct_meta.lst1086Common Voice非平行语音转换ZH VCzh/non_para_reconstruct_meta.lst2018DiDiSpeech-2非平行语音转换每一行是一条 test sample用|分成 4 段utt | prompt_text | prompt_wav | infer_textutt是样本编号后续合成音的文件名也是{utt}.wavprompt_text是 prompt 音频的文本转写zero-shot 模型可能用它做文本对齐prompt_wav是 3 到 10 秒的音色参考音频路径(相对数据集根)infer_text是要合成的目标文本一条真实的 zh/meta.lst 例00005038-00000006|也基本上与国家政治或总统职务无关。|prompt-wavs/00005038-00000041.wav|王后为此日夜伤感说我就像块不长庄稼的地。这天然就是 zero-shot voice cloning 的输入格式——给音色参考、给目标文本、模型输出目标音色念的目标文本。2.2 WER 打分脚本(内容真实度)合成音过 ASR 再跟 ground truth 算编辑距离——衡量念得对不对。中文按字符切开算相当于 CER。seed-tts-eval 自己不训练 ASR而是直接调两个业界最强的开源 ASR:语言用的 ASR出处加载方式ENWhisper-large-v3OpenAI 开源首次运行时从 HuggingFaceopenai/whisper-large-v3自动拉(~3 GB)ZHParaformer-zh阿里达摩院开源通过 FunASR 加载首次运行时从 ModelScope 自动拉seed-tts-eval 提供的是run_wer.py——一个薄薄的编排脚本遍历合成音、调 Whisper 或 Paraformer 转录、用 jiwer 算编辑距离、繁简转换、去标点、中文按字切开算 CER。真正的 ASR 智能都是别人训练好的模型贡献的。这个指标跟 MOS 音质分数是完全独立的维度——一段音质极高但吞字漏字的合成音MOS 会给高分WER 会直接暴露问题。part7 里出现过 OuteTTS 音质分数最高、但 WER 中等的反直觉背离就是这套双维度设计存在的意义。2.3 SIM 打分脚本(说话人相似度)零样本 voice cloning 的核心考察是能不能把 prompt 的音色迁移到合成音上。WER 无关这件事——念得再准、如果音色跟 prompt 完全不像克隆就是失败的。SIM 提取 speaker embedding、算合成音跟 prompt_wav 的余弦相似度分数越高、音色越像。seed-tts-eval 也不训练说话人验证模型——它复用的是微软 UniSpeech 团队在 VoxCeleb 上微调好的 WavLM-large speaker verification 模型组件出处加载方式WavLM-large backbone微软开源通过 s3prl 自动拉(~1.18 GB)WavLM speaker verification 微调头微软 UniSpeech 团队seed-tts-eval Google Drive 转发官方 ckpt(~1.30 GB)微软 UniSpeech 原始下载地址在 msranlcmtteamdrive.blob.core.windows.net,seed-tts-eval 把它转发到自己的 Google Drive 只是为了方便统一入口——本质上你从任一处下的都是同一个 ckpt。seed-tts-eval 提供的是verification_pair_list_v2.py——同样是一个编排脚本遍历 pair、加载 WavLM 提 embedding、算余弦相似度、汇总平均分。一句话总结——seed-tts-eval 的 metric 载体是 3 个第三方开源模型(Whisper / Paraformer / WavLM)项目本身提供的是数据集加打分编排脚本加中文语言学处理逻辑。这也是它能作为中立第三方标准的原因——大家用的都是同一套外部模型、数字互相可比。2.4 一键 shell 脚本seed-tts-eval 提供两个 shell 入口bash cal_wer.sh {meta_lst} {wav_dir} {en|zh} bash cal_sim.sh {meta_lst} {wav_dir} {wavlm_ckpt_path}原生脚本走多 GPU 并行分片依赖字节内部环境变量$ARNOLD_WORKER_GPU和sudo split直接跑会有一些不便下面会展示怎么绕过这层、用 Python 直接调底层的run_wer.py和verification_pair_list_v2.py。三、装依赖与下数据集3.1 clone 仓库gitclone--depth1https://github.com/BytedanceSpeech/seed-tts-eval.gitcdseed-tts-eval# 国内墙时可换 hf-mirror.com,或设置 HF_ENDPOINThttps://hf-mirror.com仓库里已经把thirdparty/UniSpeech/downstreams/speaker_verification/作为 vendored 目录提交进去不需要额外 clone UniSpeech 子模块。3.2 依赖清单seed-tts-eval 的requirements.txt只列了最必要的 5 个包但实际跑 WER 和 SIM 还需要 jiwer(WER 计算)、zhon(中文标点)、zhconv(繁简转换)、fire 和 s3prl(WavLM 说话人验证):# 核心依赖pipinstallfunasr modelscope librosa torch torchaudio# WER 打分需要pipinstalljiwer4zhon zhconv# 注意:jiwer 4.0 移除了 compute_measures API,seed-tts-eval 的 run_wer.py 依然用旧 API,必须降到 3.x# SIM 打分(UniSpeech 说话人验证)需要pipinstallfire s3prl3.3 下测试集加 WavLM 权重seed-tts-eval 的测试集加 WavLM 微调权重都放在 Google Drive:pipinstallgdown# 测试集 · 1.24 GBgdown 1GlSjVfSHkW3-leKKBlfrjuuTGqQ_xaLPtarxf seedtts_testset.tar# WavLM speaker verification 微调 · 1.30 GBgdown1-aE1NfzpRCLxA4GUxX9ITI3F9LlbtEGP# 输出到 wavlm_large_finetune.pth国内如果直接拉 Google Drive 有困难gdown 支持 HTTP 代理gdown --proxy http://127.0.0.1:xxxx {file_id}或者用镜像站(hf-mirror.com 只覆盖 HuggingFace 内容、Google Drive 需要自己代理)。解压完的目录结构seedtts_testset/ ├── en/ │ ├── meta.lst # 1088 条 EN 测试 │ ├── non_para_reconstruct_meta.lst # 1086 条 EN VC │ ├── prompt-wavs/ # 1007 个 prompt 音频 │ └── wavs/ # 1092 个 ground truth 音频 └── zh/ ├── meta.lst # 2020 条 ZH 测试 ├── hardcase.lst # 400 条 ZH 极限测试 ├── non_para_reconstruct_meta.lst # 2018 条 ZH VC ├── prompt-wavs/ # 1010 个 prompt 音频 └── wavs/ # 2020 个 ground truth 音频3.4 UniSpeech 目录的一个坑thirdparty/UniSpeech/downstreams/speaker_verification/目录下有一个select.py——它会 shadow 掉 Python 标准库的select模块——原生 shell 脚本因为直接 cd 进这个目录再运行 Python标准库的subprocess/numpy在 import 时会走到本地select.py触发循环 import 崩溃AttributeError: partially initialized module selectors has no attribute SelectSelector (most likely due to a circular import)解法很简单——把本地select.py重命名一下(它是一个独立的 T-SNE 可视化工具不被评测流程用到):mvthirdparty/UniSpeech/downstreams/speaker_verification/select.py\thirdparty/UniSpeech/downstreams/speaker_verification/_select_utility.py四、两大 metric · WER 加 SIM4.1 中间文件格式(WER 加 SIM 共用)seed-tts-eval 的run_wer.py不直接吃 meta.lst而是吃一个 3 段格式的中间文件{合成 wav 绝对路径}|{prompt wav 绝对路径}|{目标文本}原生流程用get_wav_res_ref_text.py做这层转换但它默认用os.path.dirname(metalst)拼 prompt_wav 相对路径子集抽样后就找不到 prompt-wavs。手动生成中间文件更简单frompathlibimportPath TESTSETPath(./seedtts_testset)WAV_DIRPath(./wav_out/en_mini)# 合成音输出目录lines_out[]forlineinopen(TESTSET/en/meta.lst):utt,prompt_text,prompt_wav_rel,infer_textline.strip().split(|)[:4]syn_wavWAV_DIR/f{utt}.wavprompt_wav_absTESTSET/en/prompt_wav_relifnotsyn_wav.exists():continuelines_out.append(f{syn_wav}|{prompt_wav_abs}|{infer_text})open(wav_ref_text.txt,w).write(\n.join(lines_out))4.2 WER · EN · Whisper-large-v3# 在 seed-tts-eval 仓库根目录下# python run_wer.py {wav_ref_text} {raw_output} en# python average_wer.py {raw_output} {final_output}importsubprocess subprocess.run([python,run_wer.py,wav_ref_text.txt,wer_raw.txt,en])subprocess.run([python,average_wer.py,wer_raw.txt,wer_final.txt])run_wer.py内部做的事拉 openai/whisper-large-v3 权重(3 GB)逐条读合成音重采样到 16 kHz过 Whisper 转录强制languageenglish避免 Whisper 误判转录结果加 ground truth 都去掉全部标点全小写调jiwer.compute_measures算 WER、substitutions、deletions、insertionsaverage_wer.py汇总所有样本的 WER输出平均值。4.3 WER · ZH · Paraformer-zh中文分支跟 EN 完全平行只是 ASR 换成 FunASR 的 Paraformer-zh:subprocess.run([python,run_wer.py,wav_ref_text.txt,wer_raw.txt,zh])subprocess.run([python,average_wer.py,wer_raw.txt,wer_final.txt])run_wer.py里的中文分支干的事拉 funasr/paraformer-zh(通过 modelscope 装)逐条转录繁简转换(zhconv.convert(transcription, zh-cn))按字符切开—— .join([x for x in truth])让 jiwer 以字为单位算编辑距离相当于 CER4.4 WER · CER 陷阱说一句seed-tts-eval 的中文 WER 其实是 CER(按字算)。part7 里踩过一个坑——中英混合合成音只喂英文 ASR会导致 CER 只算英文部分、中文部分被 discard、数字虚低。seed-tts-eval 把 EN / ZH 严格分开成两个子集每个子集只喂对应语言的 ASR避免了这个陷阱。所以这里出来的 WER 数字可以直接比。4.5 SIM · verification_pair_list_v2 的输入SIM 的输入是同一份wav_ref_text.txt——verification_pair_list_v2.py会自动读第 1 段(合成 wav)和第 2 段(prompt wav)。4.6 SIM · 完整调用importsubprocess,os envos.environ.copy()# 加 speaker_verification 到 PYTHONPATH,让 from verification import 能找到env[PYTHONPATH]thirdparty/UniSpeech/downstreams/speaker_verificationsubprocess.run([python,thirdparty/UniSpeech/downstreams/speaker_verification/verification_pair_list_v2.py,wav_ref_text.txt,--model_name,wavlm_large,--checkpoint,wavlm_large_finetune.pth,--scores,sim_out.txt,--wav1_start_sr,0,--wav2_start_sr,0,--wav1_end_sr,-1,--wav2_end_sr,-1,--device,cuda:0,],envenv)# sim_out.txt 最后一行是: avg score: 0.6622--wav1_start_sr到--wav2_end_sr这几个采样点参数是给 verification 内部做窗口截取用的0到-1意味着整段音频。4.7 SIM · WavLM 底层做的事verification()函数流程加载 WavLM-large backbone(通过 s3prl · 首次运行会自动拉 1.18 GB 权重)加载 speaker verification 微调头(wavlm_large_finetune.pth)对合成 wav 加 prompt wav 分别提取 speaker embedding算余弦相似度 → SIM(范围 -1 到 1越高越像)4.8 SIM · 参考值锚点Seed-TTS 论文表 3 报告了 test-zh 上的 SIM 数字Seed-TTS 本体0.796Baseline(未公开模型):0.750 附近这次实测的 3 个开源模型中F5-TTS · zh 达到 0.756、CosyVoice 2 · zh 达到 0.742——都跟论文里的 baseline 处在同一量级。五、装三个开源 TTS · 端到端跑通选 3 个中英双语支持的 zero-shot voice cloning 开源模型模型架构中英能力License主要接口F5-TTSNAR flow matchingEN 加 ZHApache 2.0F5TTS.infer(ref_file, ref_text, gen_text, file_wave)XTTS-v2多语言 codec-LM多语言CC BY-NC · 禁商用tts.tts_to_file(text, speaker_wav, language, file_path)CosyVoice 2Flow matchingEN 加 ZHApache 2.0cosyvoice.inference_zero_shot(text, prompt_text, prompt_wav)注意商用许可——XTTS-v2 是 CC BY-NC(禁止商用)· 只适合研究评测做产品选 F5-TTS 或 CosyVoice 2。5.1 F5-TTSF5-TTS 的接口跟 seed-tts-eval 的 meta.lst 四元组完美对齐——ref_file对应prompt_wav、ref_text对应prompt_text、gen_text对应infer_text。# pip install f5-ttsfromf5_tts.apiimportF5TTSimporttorch f5F5TTS(modelF5TTS_v1_Base,devicecudaiftorch.cuda.is_available()elsecpu)# 遍历 subset meta.lstforlineinopen(seedtts_testset/en/meta.lst):utt,prompt_text,prompt_wav_rel,infer_textline.strip().split(|)[:4]prompt_wavfseedtts_testset/en/{prompt_wav_rel}f5.infer(ref_fileprompt_wav,ref_textprompt_text,gen_textinfer_text,file_wavefout/f5tts/en_mini/{utt}.wav,remove_silenceTrue,seed42,)5.2 XTTS-v2XTTS-v2 通过 Coqui-TTS 装载接口不需要 prompt_text只用 speaker_wav 作音色参考要注意language参数——EN 传en,ZH 传zh-cn。# pip install coqui-ttsimportos os.environ[COQUI_TOS_AGREED]1fromTTS.apiimportTTS ttsTTS(tts_models/multilingual/multi-dataset/xtts_v2).to(cuda)forlineinopen(seedtts_testset/zh/meta.lst):utt,prompt_text,prompt_wav_rel,infer_textline.strip().split(|)[:4]tts.tts_to_file(textinfer_text,speaker_wavfseedtts_testset/zh/{prompt_wav_rel},languagezh-cn,file_pathfout/xtts/zh_mini/{utt}.wav,)5.3 CosyVoice 2CosyVoice 不走 pip 包需要 clone 仓库加下 CosyVoice2-0.5B 权重(通过 modelscope):gitclone https://github.com/FunAudioLLM/CosyVoice.gitcdCosyVoicegitsubmodule update--init--recursivepipinstall-rrequirements.txt||true# 忽略 whisper build failpipinstallopenai-whisper hyperpyyaml gdown matcha-tts wetextsetuptools80# 下 0.5B 权重(约 5 GB)python-cfrom modelscope import snapshot_download; snapshot_download(iic/CosyVoice2-0.5B, local_dir./models/cosyvoice2/CosyVoice2-0.5B)调用接口跟 F5-TTS 一样对齐 meta.lst:importsys,torchaudio sys.path.insert(0,./CosyVoice)sys.path.insert(0,./CosyVoice/third_party/Matcha-TTS)fromcosyvoice.cli.cosyvoiceimportCosyVoice2 cosyvoiceCosyVoice2(./models/cosyvoice2/CosyVoice2-0.5B,load_jitFalse,load_trtFalse,fp16False,)forlineinopen(seedtts_testset/zh/meta.lst):utt,prompt_text,prompt_wav_rel,infer_textline.strip().split(|)[:4]forjincosyvoice.inference_zero_shot(infer_text,prompt_text,fseedtts_testset/zh/{prompt_wav_rel},streamFalse,):torchaudio.save(fout/cosyvoice2/zh_mini/{utt}.wav,j[tts_speech],cosyvoice.sample_rate)break5.4 三个模型都跑完 · 数字见 §一3 个模型 × 3 个子集 9 组数字(150 条合成音)已经在 §一 摆过——F5-TTS 英文最强 · CosyVoice 2 中文常规集最强 · XTTS-v2 多语言唯一。散点图和一句话选型也在 §一。下面聊 hardcase 榜单——常规集看不出的差距hardcase 全暴露。六、hardcase 加使用建议6.1 hardcase 榜单 · TTS 的极限测试zh/hardcase.lst是 seed-tts-eval 里最难的一档400 条数据混合了几种极端场景绕口令——“贝贝背水杯水杯贝贝背。水杯贝贝背贝贝背水杯。”重复字——“要求赔偿粉尘粉尘粉尘粉尘粉尘粉尘污染。”长句加复杂并列——“小光和小刚抬着水桶上山岗。上山岗歇歇凉拿起竹竿玩打仗。乒乒乒乓乓乓打来打去砸了缸。”文言加口语混合——“他是一他像一块石头一样凝然不动。”三个模型在 hardcase 上的 WER 相比常规 ZH 全线大幅上升F5-TTS · 1.51% →9.70%· 涨 6.4 倍XTTS-v2 · 1.31% →15.08%· 涨 11.5 倍CosyVoice 2 · 0.47% →12.18%· 涨 25.9 倍观察——CosyVoice 2 常规 ZH 最强但 hardcase 上被 F5-TTS 反超——说明零样本 TTS 的稳态能力和极限稳定性是两回事。选型时不能只看常规集上的头名还得看 hardcase 掉分幅度——业务场景里遇到用户输入长句加绕口令加生僻字时谁掉得少谁真正稳。这也是 hardcase 榜单的价值所在——常规集上大家 WER 都在 1% 到 2% 之间分不出高下只有 hardcase 才能把模型的天花板暴露出来。6.2 该用发论文对齐 baseline任何 zero-shot TTS 论文都可以在 test-zh / test-en / hardcase 上出数字跟 Seed-TTS 论文表 3 的数字直接比选型开源 TTS上面这样跑一次WER 加 SIM 两个数字直接告诉你哪个模型适合你的场景模型迭代评测自研 zero-shot TTS 每次改进都可以用这套 metric 跟踪进度voice cloning 音色迁移评测——SIM 分数是当前公开可用的最标准量化方式6.3 不该用纯文本转语音(非 zero-shot)没有 prompt_wav 就不适用直接用 DNSMOS、UTMOS、NISQA 打无参考 MOS 更快对话 / 交互式合成——seed-tts-eval 是单句合成不测多轮对齐和 turn-taking音质专项评测——WER 只关注内容真实度、SIM 只关注音色相似度音质好坏(自然度 / 底噪 / 电流音)需要额外接 MOS 打分器6.4 建议搭配 MOS 一起用seed-tts-eval 提供 WER 加 SIM 两个客观维度但**音质自然度这个维度还是空缺**——一段合成音可能 WER 完美、SIM 高分但因为韵律太机械 / 底噪严重MOS 依然会低。完整的 zero-shot TTS 评测建议是seed-tts-eval WER → 念得对不对 seed-tts-eval SIM → 声音像不像 prompt DNSMOS / UTMOS / NISQA → 音质自然度怎么样三个维度独立、缺一不可。想要 MOS 打分器的一站式跑通可以配合 BLOG-mos 里的方案组合使用。6.5 关于 Seed-TTS 论文seed-tts-eval 只是 Seed-TTS 论文 的评测部分。论文本体讲的是字节自己的 TTS 架构——基于 speech tokenizer 加 language model 加 diffusion 的 3 阶段方案能做 zero-shot voice cloning、跨语言、编辑、说话人插值。感兴趣可以对着论文和这次实测的开源模型数字看理解字节在 zero-shot TTS 上的技术积累相比开源生态领先多少。零样本 TTS 是这一两年最卷的领域之一seed-tts-eval 提供的是一把中立、可比、可复现的尺子。跑一次成本不高(小样本 30 分钟以内、全量 3000 条 3 到 6 小时)但拿到的数字可以直接跟论文加其他开源模型对齐——比自己造测试句造 MOS 榜要专业得多。