Case Study · 研究作品 · 語音合成引擎

知音 Zhiyin

模型無關的本機中文語音引擎 —— 情緒可控、可商用、跑在消費級 8GB 顯卡上

角色:獨力設計與實作
時程:2026
狀態:Studio v0.2.0 已發布 · 技術報告 v0.3
實驗平台:RTX 2080 Super Max-Q(8GB)
4 開源引擎系統性評比
3.3% 微調後留出句拼音 CER
0.892 音色餘弦(異人對照 0.423)
4 個語音引擎的系統性評比

名字取自「高山流水」—— 俞伯牙鼓琴,鍾子期聽懂他琴音中的意境。知音 = 真正聽懂你聲音的人。

研究問題

有聲書與互動敘事需要情緒豐富、可克隆音色、且能商用的中文語音合成。現有方案各有缺口:雲端商用 API 有成本與資料主權疑慮;多數開源模型情緒控制薄弱或中文品質不足。而「最佳引擎」會隨開源生態快速變動 —— 今天選定的模型,三個月後可能就被超越。

所以我沒有直接挑一個模型來用,而是先問:能不能讓「換模型」變成一件廉價的事? 這個問題定義了整個系統的架構。

  1. RQ1(架構):能否以單一抽象層讓多個相依互斥的 TTS 引擎熱插拔,使引擎評比與替換成為低成本操作?
  2. RQ2(引擎選型):在「克隆 × 情緒 × 中文品質 × 可商用 × 8GB 本地」的聯合約束下,開源引擎的真實能力邊界為何?組合式(聲音轉換串接)與端到端路線孰優?
  3. RQ3(可控性):配音級的細部控制(讀音 / 停頓 / 氣口 / 音效 / 情緒)能否獨立於單一引擎、在引擎層統一提供 —— 包括對「缺乏對應輸入通道」的引擎?
  4. RQ4(可靠性):取樣式 TTS 的單次輸出不可靠(吞字 / 截斷),量產級「近 100% 正確」應由什麼機制保證?
  5. RQ5(資料效率):業餘使用者需要錄多少素材,克隆品質才「夠」?品質各維度(音色 / 咬字 / 情緒)對素材量的敏感度是否相同?

系統設計

四個設計決策,依序解決 RQ1 到 RQ4。

  1. 1 · 後端抽象(the swappable seam)

    定義 Backend 介面(is_up()、synthesize(voice, text, lang, params)、capabilities())。角色語音庫以 backend 欄指定引擎;引擎核心不知道任何特定模型的細節。新增引擎 = 實作介面 + 註冊一行。此設計後續兩度兌現:四引擎評比與主力對調皆零遷移成本。

  2. 2 · 跨環境隔離(multi-env isolation)

    各引擎相依互斥:GPT-SoVITS 綁 torch 2.0 / cu118 + Python 3.9;IndexTTS-2 需 torch 2.8 / cu126;Kokoro 需 onnxruntime ≥ 1.20。我不強迫它們共存於單一環境,而是每個重型引擎跑自己的 venv,主程式以 subprocess 橋接(stdin 餵 JSON、stdout 收結果)—— 如同呼叫外部工具,完全不污染彼此。副效益:上游程式碼一行都不進我的 repo,授權界線乾淨。

  3. 3 · 引擎層配音標記系統(capability lifting)

    行內標記 【情緒】、[停N](插入 N 秒靜音)、[氣口:嘆](插入真人氣口聲)、[音效:x]、字{pinyin}(讀音覆寫)全部實作在引擎層(標記解析 → 切段 → 逐段合成 → 素材插入 → 重採樣拼接),因此任何後端零成本自動獲得。設計原則:引擎層功能天生全模型共用;模型內在能力(輸入通道)不可移植。

  4. 4 · ASR 驗證重抽產線(生產閉環)

    常駐 Whisper worker(載一次,查核由 30–60 秒降到秒級)+ 引擎級 QC:synthesize(qc=True) 執行「生成 → 算拼音 CER → 超標換 seed 重抽 → 用盡取最佳」,貫穿單句、標記混排到整本有聲書 CLI。把取樣式生成的可靠性從「碰運氣」變成「可保證」。

評測方法

所有引擎經同一 Backend 介面、同一參考音檔測試,客觀度量沿用零樣本 TTS 的主流協定(Seed-TTS eval):

  1. 內容正確性 — 拼音字錯率(pinyin-CER)

    合成音經 faster-whisper(large-v3、CPU int8、beam=5)轉寫,與目標文本各自轉為無調號拼音音節序列(pypinyin;數字先中文化以消除格式假錯),取音節級 Levenshtein 編輯距離對目標長度之比。取拼音層的理由:繁簡同拼音、且直接度量「發音對不對」。

  2. 音色保真 — 說話者嵌入餘弦

    以 ERes2NetV2(16 kHz fbank)對合成音與該說話者真實錄音集合中心取餘弦相似度;同批真實錄音彼此的平均餘弦與異人對照提供上下錨點,避免「0.8 到底算高還算低」的空泛判讀。

  3. 主觀驗收 — 母語者對照逐字文本盲聽

    所有關鍵決策要求客觀 + 主觀一致方定案。兩者互補且缺一不可:ASR 抓吞字錯字,但其語言模型會「腦補」修正真實誤讀(誤讀「昏→分」CER 為 0),也聽不出底噪與節奏;耳測則受單次印象與期望偏誤影響。

引擎評比結果

引擎路線中文情緒控制結論
GPT-SoVITS v2Pro少樣本克隆佳無原生情緒旋鈕初判備案 → 真人微調後升為日常主力
CosyVoice2指令式 + 克隆實測偏方言 / 不穩指令文字淘汰(中文品質不足)
seed-vc(VC 串接)對任意 TTS 做情緒轉換視來源轉換式淘汰(情緒稀釋)
IndexTTS-2零樣本克隆 + 解耦情緒原生佳emo_vector / emo_text / emo_audio + 強度 α採用為特種兵
表:四引擎在「克隆 × 情緒 × 中文 × 可商用 × 8GB」聯合約束下的評比結論。

三軸總結(情緒 / 細部控制 / 自然度):沒有單一開源模型三軸全優。系統的價值正在於以架構讓「每個角色選定當下最適引擎」成為零成本決策 —— 並隨生態演進隨時翻盤。真人錄音到位後的重測就推翻了我的初判:IndexTTS-2 的中文咬字弱屬引擎固有(高品質真人參考音亦然),而 GPT-SoVITS 經真人微調後咬字近乎全對、中英混搭最乾淨、速度 2–8 秒 / 句。主力因此對調 —— 而這次對調的遷移成本是零,因為架構早就準備好了。

兩個負面結果(可轉移的知識)

負面結果通常不會被寫進作品集,但它們往往比成功更有價值 —— 因為它們能讓別人少走一條死路。

  1. ① 聲音轉換串接會「逐層稀釋」情緒

    「先用任意 TTS 生中性語音,再用 seed-vc 做情緒轉換」的串接路線,實測情緒逐層被稀釋:VC 為保真音色與內容,會壓抑情緒位移,最終強度不足以與競品區隔。結論:情緒應在合成階段原生產生,而非後處理轉換。此發現直接淘汰整條 cascade 路線。

  2. ② 合成語音不可被零樣本二次克隆(「複製的複製」)

    MOSS-VoiceGenerator 從文字描述設計音色,自身直接合成的 CER 為 0%;但把該合成音當參考音餵給 IndexTTS-2 做零樣本克隆,CER 退化到 11–57%(強情緒 35–39%),且降 temperature、降 emo_alpha、換 seed 皆無法挽救。→ 設計音色必須由其生成模型直接合成,不可作為另一克隆模型的參考音。

  3. ③ 但微調可解 —— 負結果的出口

    以一個設計音生成 24 句訓練資料(固定 seed 鎖音色)→ ASR-QC 清洗(剔除瑕疵句,留 21)→ GPT-SoVITS 本地微調(8GB、約 15 分鐘)。留出句拼音 CER 3.3%、音色餘弦 0.892(異人對照 0.423),且高於來源片段彼此的一致性(0.716)—— 微調不只保真,還修正了生成式音色的跨句漂移。機制:零樣本克隆是「照著唸」(瑕疵被繼承放大),微調是「學會這個音色的分布」(瑕疵被平均掉)。

效能實證:8GB 的硬邊界

初期觀察到 IndexTTS-2「每句約 40 分鐘」。逐項測量後,延遲實由三者構成且須分別處理:外部 GPU 競用、模型載入、推論本身。

項目數值意義
模型載入(一次性)~183 秒常駐 worker 後只付一次
純推論(每句)~84–89 秒真正的邊際成本
每章 100 句(常駐)≈ 2.6 小時對照:一次性載入 7.5 小時
fp32 每步52 → 105 → 350 秒顯存溢出後崩潰式變慢
表:fp16、GPU 空載下的實測。載入而非推論主導小批量延遲;fp32 佔約 7.8GB,逼近 8GB 上限後溢入系統共享記憶體。
  • 常駐 worker 是最大槓桿:載入一次、迴圈接單,把每句成本從「重複載入」降到純推論。
  • fp16 在 8GB 上是必要條件,不是選用:fp32 會溢出顯存並崩潰式變慢,提速應改走減少擴散步數或換更大顯存,而非提高精度。
  • 延遲對「機器是否空載」極度敏感:主機被其他程式佔用時,擴散每步由約 3.5 秒暴增至約 240 秒(慢約 70 倍)。生產應在空載機或離峰批次執行。

資料效率:要錄多少素材才夠?

「我需要錄多久?」是業餘使用者的第一個問題。我以同一說話者資料的子集(零樣本 / 5 / 10 / 20 / 28 句)各訓一版,同留出句量測:

素材量音色餘弦(vs 真人中心)留出句 CER(單次)
零樣本(僅 1 句參考)0.767~9%
5 句(約 30 秒)0.885~9%
10 / 20 / 28 句0.886 / 0.889 / 0.878(持平)7%–32%(高方差)
表:音色相似度在約 5 句即飽和;咬字正確率則與素材量無關。

發現一:音色相似度在約 5 句(30 秒)即飽和 —— 「像本人」的成本極低。其後素材的價值在情緒參考音、氣口素材與穩定性,不在音色。

發現二:咬字正確率與素材量無關,由取樣隨機性主導 —— 同一模型同一句換 seed,CER 在 3%–58% 間波動。方法論警示:單次 CER 不足以評判模型好壞(我曾因兩次壞 roll 誤判「28 句版壞掉」,第三個 seed 即近全對)。→ 量產穩定性的正解是逐段 ASR 驗證 + 重抽,而非堆素材。

這條曲線直接轉化為產品的分級錄音台本:1 句可玩 / 5–10 句很像本人 / 完整版含情緒與氣口。

方法論:兩次自我糾錯

這個專案教給我的東西,比它的結論更重要。兩次我都錯了,而且是測量把我糾正過來的。

  1. 「40 分鐘 / 句」之謎 = 外部競用,不是引擎

    在 GPU 被其他程式(遊戲)佔用約 100% 時測得 40 分 / 句;GPU 空載重測,同一句降至 270 秒。若我當時直接下結論「這引擎太慢」,就會砍掉後來成為特種兵的模型。教訓:歸因前先排除環境變因 —— 用測量,不要用直覺。

  2. 「28 句版壞掉」= 取樣方差,不是模型

    我曾因連續兩次不良取樣,判定某個訓練版本壞了。換第三個 seed 後近乎全對。這次糾錯直接催生了 ASR-QC 重抽產線 —— 把「單次結果不可信」這個認知,變成系統的一個元件。

  3. 清晰度問題的歸因:文本個案 + 參考音,不是精度

    某句被母語者判定咬字不清,我一度懷疑 fp16 或引擎缺陷。逐項排除後真正原因是:(1) 該句為短促驚嘆 + 強情緒的特定文本個案;(2) 我當時用合成語音(edge-tts)當克隆參考 —— 又是「複製的複製」。在歸咎模型或精度前,先以受控變因逐一排除,否則會誤砍 fp16(在此硬體為必要)而走錯路。

效度威脅與限制

這是一份單機案例研究,樣本數小。我把限制寫在這裡,而不是等人來問 —— 結論應讀作「工程決策證據 + 可重現的實驗設計」,而非族群統計推論。

Threats to Validity

  • 樣本規模:說話者 n = 2、留出句每組 3 句、聽者 1 人;甜蜜點曲線為單說話者案例。擴大樣本(說話者 / 句子 / 聽者 / 多 seed 統計)是升級為正式論文的首要工作。
  • 裁判偏差:單一 ASR 裁判(Whisper large-v3),其語言模型修正效應對「誤讀類」錯誤系統性低估。多裁判交叉(如 Paraformer)可緩解。
  • 單次取樣方差:同條件 CER 可在 3%–58% 間波動;文中凡單次數字均應視為點估計。
  • 同音字替換的語義副作用:替換後文本供引擎的 BERT 前端讀取,語義改變對韻律的影響未量化;且映射表基於「主讀音」,聲調連讀變化(sandhi)未建模。
  • 授權分析非法律意見:各引擎授權條款為工程側閱讀,商用發布前應法務複核。
  • 硬體外推:全部效能結論綁定 8GB Turing 單卡;更大顯存或新架構下結論可能不同。

安全姿態

  • 無 eval / exec / pickle / os.system / shell=True;所有 subprocess 一律清單參數(無 shell 注入面)。
  • 常駐 worker 走 stdin / stdout 管線(子行程,非對外網路服務)。
  • HTTP 服務僅綁 127.0.0.1,從不 0.0.0.0 —— 上游 API 的 /tts 端點可讀任意檔並具 RCE 風險。
  • 只載信任來源的模型:.pth / .ckpt 等同 pickle 反序列化,會執行任意程式碼。
  • 商用風險不在引擎授權(MIT / 寬鬆),而在克隆誰的聲音 —— 真人音色需取得本人同意。

技術棧

PythonGPT-SoVITSIndexTTS-2MOSS-VoiceGeneratorKokorofaster-whisperpypinyinERes2NetV2PyTorchsubprocess 橋接 / 多 venv 隔離FastAPIEBU R128 loudnorm

未來工作

  1. 擴大樣本與多裁判評測 —— 補齊上方「效度威脅」列出的統計不足,是把這份技術報告升級為正式論文的首要工作。
  2. 演法預設庫 —— 預錄專業演繹片段庫,讓不會配音的使用者「挑演法」而非自己演,構成「錄 30 秒 → 挑演法 → 出廠即專業」的低門檻配音路徑。
  3. 雲端微調 IndexTTS-2 —— 讓設計音在特種兵引擎上也取得三軸全優;目前無現成工具,屬 DIY 訓練工程。
  4. 實時變聲 / 歌聲合成 —— 與現有錄音資產共用,延伸至互動場景。

延伸閱讀