Case Study · 研究作品 · 語音合成引擎
知音 Zhiyin
模型無關的本機中文語音引擎 —— 情緒可控、可商用、跑在消費級 8GB 顯卡上
名字取自「高山流水」—— 俞伯牙鼓琴,鍾子期聽懂他琴音中的意境。知音 = 真正聽懂你聲音的人。
研究問題
有聲書與互動敘事需要情緒豐富、可克隆音色、且能商用的中文語音合成。現有方案各有缺口:雲端商用 API 有成本與資料主權疑慮;多數開源模型情緒控制薄弱或中文品質不足。而「最佳引擎」會隨開源生態快速變動 —— 今天選定的模型,三個月後可能就被超越。
所以我沒有直接挑一個模型來用,而是先問:能不能讓「換模型」變成一件廉價的事? 這個問題定義了整個系統的架構。
- RQ1(架構):能否以單一抽象層讓多個相依互斥的 TTS 引擎熱插拔,使引擎評比與替換成為低成本操作?
- RQ2(引擎選型):在「克隆 × 情緒 × 中文品質 × 可商用 × 8GB 本地」的聯合約束下,開源引擎的真實能力邊界為何?組合式(聲音轉換串接)與端到端路線孰優?
- RQ3(可控性):配音級的細部控制(讀音 / 停頓 / 氣口 / 音效 / 情緒)能否獨立於單一引擎、在引擎層統一提供 —— 包括對「缺乏對應輸入通道」的引擎?
- RQ4(可靠性):取樣式 TTS 的單次輸出不可靠(吞字 / 截斷),量產級「近 100% 正確」應由什麼機制保證?
- RQ5(資料效率):業餘使用者需要錄多少素材,克隆品質才「夠」?品質各維度(音色 / 咬字 / 情緒)對素材量的敏感度是否相同?
系統設計
四個設計決策,依序解決 RQ1 到 RQ4。
- 1 · 後端抽象(the swappable seam)
定義
Backend介面(is_up()、synthesize(voice, text, lang, params)、capabilities())。角色語音庫以backend欄指定引擎;引擎核心不知道任何特定模型的細節。新增引擎 = 實作介面 + 註冊一行。此設計後續兩度兌現:四引擎評比與主力對調皆零遷移成本。 - 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 · 引擎層配音標記系統(capability lifting)
行內標記
【情緒】、[停N](插入 N 秒靜音)、[氣口:嘆](插入真人氣口聲)、[音效:x]、字{pinyin}(讀音覆寫)全部實作在引擎層(標記解析 → 切段 → 逐段合成 → 素材插入 → 重採樣拼接),因此任何後端零成本自動獲得。設計原則:引擎層功能天生全模型共用;模型內在能力(輸入通道)不可移植。 - 4 · ASR 驗證重抽產線(生產閉環)
常駐 Whisper worker(載一次,查核由 30–60 秒降到秒級)+ 引擎級 QC:
synthesize(qc=True)執行「生成 → 算拼音 CER → 超標換 seed 重抽 → 用盡取最佳」,貫穿單句、標記混排到整本有聲書 CLI。把取樣式生成的可靠性從「碰運氣」變成「可保證」。
評測方法
所有引擎經同一 Backend 介面、同一參考音檔測試,客觀度量沿用零樣本 TTS 的主流協定(Seed-TTS eval):
- 內容正確性 — 拼音字錯率(pinyin-CER)
合成音經 faster-whisper(large-v3、CPU int8、beam=5)轉寫,與目標文本各自轉為無調號拼音音節序列(pypinyin;數字先中文化以消除格式假錯),取音節級 Levenshtein 編輯距離對目標長度之比。取拼音層的理由:繁簡同拼音、且直接度量「發音對不對」。
- 音色保真 — 說話者嵌入餘弦
以 ERes2NetV2(16 kHz fbank)對合成音與該說話者真實錄音集合中心取餘弦相似度;同批真實錄音彼此的平均餘弦與異人對照提供上下錨點,避免「0.8 到底算高還算低」的空泛判讀。
- 主觀驗收 — 母語者對照逐字文本盲聽
所有關鍵決策要求客觀 + 主觀一致方定案。兩者互補且缺一不可:ASR 抓吞字錯字,但其語言模型會「腦補」修正真實誤讀(誤讀「昏→分」CER 為 0),也聽不出底噪與節奏;耳測則受單次印象與期望偏誤影響。
引擎評比結果
| 引擎 | 路線 | 中文 | 情緒控制 | 結論 |
|---|---|---|---|---|
| GPT-SoVITS v2Pro | 少樣本克隆 | 佳 | 無原生情緒旋鈕 | 初判備案 → 真人微調後升為日常主力 |
| CosyVoice2 | 指令式 + 克隆 | 實測偏方言 / 不穩 | 指令文字 | 淘汰(中文品質不足) |
| seed-vc(VC 串接) | 對任意 TTS 做情緒轉換 | 視來源 | 轉換式 | 淘汰(情緒稀釋) |
| IndexTTS-2 | 零樣本克隆 + 解耦情緒 | 原生佳 | emo_vector / emo_text / emo_audio + 強度 α | 採用為特種兵 |
三軸總結(情緒 / 細部控制 / 自然度):沒有單一開源模型三軸全優。系統的價值正在於以架構讓「每個角色選定當下最適引擎」成為零成本決策 —— 並隨生態演進隨時翻盤。真人錄音到位後的重測就推翻了我的初判:IndexTTS-2 的中文咬字弱屬引擎固有(高品質真人參考音亦然),而 GPT-SoVITS 經真人微調後咬字近乎全對、中英混搭最乾淨、速度 2–8 秒 / 句。主力因此對調 —— 而這次對調的遷移成本是零,因為架構早就準備好了。
兩個負面結果(可轉移的知識)
負面結果通常不會被寫進作品集,但它們往往比成功更有價值 —— 因為它們能讓別人少走一條死路。
- ① 聲音轉換串接會「逐層稀釋」情緒
「先用任意 TTS 生中性語音,再用 seed-vc 做情緒轉換」的串接路線,實測情緒逐層被稀釋:VC 為保真音色與內容,會壓抑情緒位移,最終強度不足以與競品區隔。結論:情緒應在合成階段原生產生,而非後處理轉換。此發現直接淘汰整條 cascade 路線。
- ② 合成語音不可被零樣本二次克隆(「複製的複製」)
MOSS-VoiceGenerator 從文字描述設計音色,自身直接合成的 CER 為 0%;但把該合成音當參考音餵給 IndexTTS-2 做零樣本克隆,CER 退化到 11–57%(強情緒 35–39%),且降 temperature、降 emo_alpha、換 seed 皆無法挽救。→ 設計音色必須由其生成模型直接合成,不可作為另一克隆模型的參考音。
- ③ 但微調可解 —— 負結果的出口
以一個設計音生成 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 秒 | 顯存溢出後崩潰式變慢 |
- 常駐 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 句(30 秒)即飽和 —— 「像本人」的成本極低。其後素材的價值在情緒參考音、氣口素材與穩定性,不在音色。
發現二:咬字正確率與素材量無關,由取樣隨機性主導 —— 同一模型同一句換 seed,CER 在 3%–58% 間波動。方法論警示:單次 CER 不足以評判模型好壞(我曾因兩次壞 roll 誤判「28 句版壞掉」,第三個 seed 即近全對)。→ 量產穩定性的正解是逐段 ASR 驗證 + 重抽,而非堆素材。
這條曲線直接轉化為產品的分級錄音台本:1 句可玩 / 5–10 句很像本人 / 完整版含情緒與氣口。
方法論:兩次自我糾錯
這個專案教給我的東西,比它的結論更重要。兩次我都錯了,而且是測量把我糾正過來的。
- 「40 分鐘 / 句」之謎 = 外部競用,不是引擎
在 GPU 被其他程式(遊戲)佔用約 100% 時測得 40 分 / 句;GPU 空載重測,同一句降至 270 秒。若我當時直接下結論「這引擎太慢」,就會砍掉後來成為特種兵的模型。教訓:歸因前先排除環境變因 —— 用測量,不要用直覺。
- 「28 句版壞掉」= 取樣方差,不是模型
我曾因連續兩次不良取樣,判定某個訓練版本壞了。換第三個 seed 後近乎全對。這次糾錯直接催生了 ASR-QC 重抽產線 —— 把「單次結果不可信」這個認知,變成系統的一個元件。
- 清晰度問題的歸因:文本個案 + 參考音,不是精度
某句被母語者判定咬字不清,我一度懷疑 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 / 寬鬆),而在克隆誰的聲音 —— 真人音色需取得本人同意。
技術棧
未來工作
- 擴大樣本與多裁判評測 —— 補齊上方「效度威脅」列出的統計不足,是把這份技術報告升級為正式論文的首要工作。
- 演法預設庫 —— 預錄專業演繹片段庫,讓不會配音的使用者「挑演法」而非自己演,構成「錄 30 秒 → 挑演法 → 出廠即專業」的低門檻配音路徑。
- 雲端微調 IndexTTS-2 —— 讓設計音在特種兵引擎上也取得三軸全優;目前無現成工具,屬 DIY 訓練工程。
- 實時變聲 / 歌聲合成 —— 與現有錄音資產共用,延伸至互動場景。
延伸閱讀
- 知音技術報告(PDF) —— 完整方法、實驗數據與參考文獻
- ClinCalc Case Study —— 同一套「規則優於 LLM」的思路,用在臨床判讀
- Kaizei Case Study —— 跨領域的隱私先行實作(零知識加密)