@HarshalsinghCN: 我打造了一个开源的 Hinglish TTS,性能碾压市面所有模型。我没有任何研究背景。上周我 w…

X AI KOLs Timeline 工具

摘要

一位开发者记录了构建开源 Hinglish 文本转语音系统的过程,该系统通过修复上游推理 bug 并增加轻量级预处理封装,实现了超越现有模型的效果,且在无需训练或 GPU 资源的情况下达到了高质量。

我构建了一个开源的 Hinglish TTS,性能碾压市面所有模型。我没有任何研究背景。上周我正在使用 Ira AI companion,对此公司产生了好奇,于是开始深挖他们的技术栈。我立下了一个看似愚蠢的目标:从零开始打造全球最佳的 Hinglish TTS。我记录了每一个遇到的死胡同、每一次架构决策、每一个错误以及我的解决思路。完整构建日志(开源):https://harrrshall.github.io/hinglish-tts/ @aiweekendsxyz @blrwritersroom
查看原文 导出为 Word 导出为 PDF
查看缓存全文

缓存时间: 2026/05/12 12:55

我构建了一个开源的 Hinglish TTS,性能超越了市面上所有模型。我没有任何研究背景。上周我在使用 Ira AI companion,对背后的公司产生了兴趣,开始深挖他们的技术栈。我定下了一个看似愚蠢的目标:从零开始打造全球最好的 Hinglish TTS。我记录了每一个死胡同、每一次架构决策、每一个错误以及我的解决思路。完整构建日志(开源):https://harrrshall.github.io/hinglish-tts/ @aiweekendsxyz @blrwritersroom —

我是如何构建开源 Hinglish TTS 的 — Harshal Singh

来源:https://harrrshall.github.io/hinglish-tts/

摘要

本文介绍了一个开源 Hinglish 文本转语音(TTS)系统的构建过程,作者在开始前对 TTS 或语音处理毫无基础。最终系统在四类输入(纯天城文、纯拉丁字母、混合脚本、含印度专有名词的英语)上实现了 4.70/5.0 的平均可懂度,超越了所有同类开源模型。核心结论在于:上游推理代码中的两个 bug,加上一个仅 14 行的预处理封装——无需训练、无需微调、无需标注数据——就实现了从 2.13 到 4.70 的全部性能提升。

目录

  1. 为什么选择 Hinglish? (https://harrrshall.github.io/hinglish-tts/#why-hinglish)
  2. 审计:测试现有模型 (https://harrrshall.github.io/hinglish-tts/#audit)
  3. 错误 #1:单一 ASR 评分对 Hinglish 无效 (https://harrrshall.github.io/hinglish-tts/#mistake1)
  4. 时长 Bug:为什么 IndicF5 会输出静音 (https://harrrshall.github.io/hinglish-tts/#duration-bug)
  5. Mode C:欠训练的 Embedding 问题 (https://harrrshall.github.io/hinglish-tts/#mode-c)
  6. IndicXlit 修复方案:总共仅 14 行代码 (https://harrrshall.github.io/hinglish-tts/#indicxlit)
  7. 当 IndicXlit 出错时 (https://harrrshall.github.io/hinglish-tts/#whitelist)
  8. 与其他模型的对比 (https://harrrshall.github.io/hinglish-tts/#comparison)
  9. UTMOS 问题:当自动指标在印地语上撒谎时 (https://harrrshall.github.io/hinglish-tts/#utmos)
  10. Kaggle 灾难纪实 (https://harrrshall.github.io/hinglish-tts/#kaggle)
  11. 方向 1:模型真的理解语音学吗? (https://harrrshall.github.io/hinglish-tts/#direction1)
  12. 方向 2:模型是在按音素还是字素思考? (https://harrrshall.github.io/hinglish-tts/#direction2)
  13. 产品到底是什么 (https://harrrshall.github.io/hinglish-tts/#product)
  14. 评估基础设施:可复用于任何 Hinglish TTS (https://harrrshall.github.io/hinglish-tts/#eval-infra)
  15. 所有错误总结 (https://harrrshall.github.io/hinglish-tts/#mistakes)
  16. 下一步计划 (https://harrrshall.github.io/hinglish-tts/#next)
  17. 元教训 (https://harrrshall.github.io/hinglish-tts/#meta)

刚开始时,我对文本转语音一无所知。真的是一无所知。不懂模型,不懂术语,分不清声码器和流匹配扩散 Transformer 的区别。我从没听过 “IndicXlit” 这个词,也不知道梅尔频谱图是什么。我只有一个目标:构建最好的开源英语 TTS 模型。这个目标只维持了三天。然后我意识到,我解决的是错误的问题。这就是实际发生的故事。每一次走错的弯路,每一次“等等,指标在骗我们”的顿悟,每一次凌晨两点在 Kaggle 上的 kernel 崩溃。最终,我们得到了一个在严格的三 ASR 评估体系下得分 4.70/5.0 的系统,击败了全球所有其他支持 Hinglish 的开源 TTS 模型。而它仅用 14 行代码就实现了。没有训练,没有微调,没有 GPU 预算。以下是全部内容。—

为什么选择 Hinglish?

Hinglish 不是洋泾浜语,也不是蹩脚英语。它是一种完全成熟的语码转换语域,拥有自己的语法。母语者会在句子中间,有时甚至在短语中间,在天城文印地语和拉丁字母英语之间切换。它看起来是这样的:

“Boss को बता देनाkalमैंleaveपर रहूँगा, kuch personal काम है।”

一句话。三种文字。两种语言。这就是 1 亿印度城市人口每天在 WhatsApp 上交流的方式。没有任何开源 TTS 模型能很好地处理它。 这成了我决定要解决的问题。

审计:测试现有模型

真正的第一步不是构建任何东西,而是评估现有模型。我挑选了四个候选模型和一个包含 30 个句子的评估集,旨在全面压力测试该问题的各个维度:

类别数量测试内容
pure_devanagari8天城文标准印地语
pure_roman8拉丁字母书写的口语化 Hinglish
mixed_script8句中文字切换
english_with_NE6含印度人名地名的英语

我测试的四个模型:

  1. Kokoro v1.0(hexgrad,82M 参数),英语 TTS 冠军,带有印地语模式
  2. Indic Parler-TTS(AI4Bharat,880M 参数),大型、专注于印度语系、基于文本描述条件生成
  3. IndicF5(AI4Bharat,330M 参数),流匹配 TTS,在印度语系文字音频上训练
  4. SPRINGLab F5-Hindi(另一个 Indic F5-TTS 变体)

所有测试均在 Kaggle T4 GPU(免费层级,本项目零预算)上运行。初步结果,使用单一 ASR 系统(AssemblyAI)评分:

模型总分
Kokoro v1.03.03
Indic Parler-TTS2.73
IndicF51.97
SPRINGLab F5-Hindi相近

Kokoro 胜出。案子结了。直接上 Kokoro 做印地语吧。然后我实际去听了听输出音频。 **在印地语句子上的听感,IndicF5 并不比 Kokoro 差,反而更好。**元音是对的,送气音是对的,节奏也是对的。数字肯定有问题。 教训:在印地语音频上,自动指标会撒谎。

错误 #1:单一 ASR 评分对 Hinglish 无效

使用单一 ASR 系统对 Hinglish 这类语言进行 TTS 评分存在根本问题。ASR 模型是在特定数据分布上训练的。AssemblyAI 的 Universal-2 在英语上表现优异,处理印地语也还算合理,但它在各个类别上的表现并不均衡。对于纯拉丁字母书写的 Hinglish(如 yaar tu kal kya kar raha tha),一个主要在正式文本上训练的模型产生的转录结果,会与在对话数据上训练的模型完全不同。而单一模型的失败直接变成了你的得分。更糟的是:当 TTS 输出是天城文而参考文本是拉丁字母(或反之)时,字符错误率(CER)毫无意义。kyaक्या 意思相同、发音一致,但它们之间的 CER 是 1.0。最大误差。仿佛它们没有任何共同字符。我吃了大亏才学到这一点。花了一周时间信任那些实际上在衡量 ASR 偏差而非模型质量的数字。

修复方案:

  1. **三 ASR 共识机制。**AssemblyAI Universal-2 + Deepgram Nova-3 + Groq Whisper-large-v3。三者取多数票。每个系统的失败模式不同,因此共识结果可靠性大幅提升。
  2. **评分前进行文字规范化。**在计算任何错误率之前,使用名为 IndicXlit 的转写库将参考文本和 ASR 转录文本统一规范化为天城文。kyaक्याofficeऑफिस。现在比较的双方使用相同的文字,CER 才能测量它本该测量的东西。

这两项改动修复了评分流水线。数字变得可信了。但它们也揭示了实际情况有多糟糕。—

时长 Bug:为什么 IndicF5 会输出静音

在使用修正后的评估流水线跑完全部 30 个句子后,IndicF5 的重新评分为 5.0 分中的 2.13 分。不是因为它听起来差,而是因为 30 个输出中有 21 个要么是静音,要么被截断到 0.8 到 2.5 秒,而句子本需要 3 到 6 秒的音频。有些东西从根本上坏了。不在模型权重里,而在推理代码中。我开始找 bug。在 f5_tts/infer/utils_infer.py 第 451 行附近,时长计算逻辑是这样的:

# This is the original IndicF5 code — this is the bug
ref_text_len = len(ref_text.encode("utf-8"))  # BUG
gen_text_len = len(gen_text.encode("utf-8"))  # BUG
duration = ref_audio_len + int(ref_audio_len / ref_text_len * gen_text_len / speed)

模型正在使用 UTF-8 字节数来估算应分配多少音频画布时长。思路是:如果参考文本长 ref_text_len 字节,对应音频为 ref_audio_len 帧,那么长度为 gen_text_len 字节的生成文本应该需要成比例的时间。

**缺陷在于:**天城文字符在 UTF-8 中每个约占 3 个字节。ASCII 字符每个占 1 个字节。参考片段是带有天城文转录的印地语音频。100 个字符 × 3 字节 = 300 字节。参考音频假设为 3 秒。生成文本是拉丁字母 Hinglish。100 个字符 × 1 字节 = 100 字节。因此模型分配了:3 秒 × (100/300) = 1 秒的画布,而句子实际需要 3 秒。模型正确地填满了这 1 秒。然后音频就戛然而止。句子被截断了 66%。这就是“静音或跳过”的真实面目。不是静音,只是被截断的音频导致 ASR 无法解码。

修复只需 4 行代码:

# Fixed version — count characters, not bytes
ref_text_len = sum(1 for c in ref_text if not c.isspace())  # count non-whitespace chars
gen_text_len = sum(1 for c in gen_text if not c.isspace())
duration = ref_audio_len + int(ref_audio_len / ref_text_len * gen_text_len / speed)
print(f"DEBUG-PATCH: ref_chars={ref_text_len} gen_chars={gen_text_len} "
      f"ref_frames={ref_audio_len} gen_frames={duration - ref_audio_len} "
      f"text={gen_text[:50]!r}")

字符计数与文字类型无关。一个 是一个字符。一个 k 也是一个字符。画布分配变得与实际内容长度成正比,而不是受编码字节数的干扰。我称之为 Mode A 故障。模型生成了正确的内容,但画布被截断了。该补丁在 import 时自动应用,重写 site-packages 文件,因此用户无需手动修改。打补丁后,silence_or_skip 从 20/30 降至 16/30。有进展。但总分仅从 2.13 升至 2.20。还有其他问题。—

Mode C:欠训练的 Embedding 问题

画布大小修正后,输出时长现在正常了。但听听拉丁字母输入的实际输出:

输入:kal mujhe office jaana hai 输出的 ASR 转录:ऐई अ एफे रेने आए

这完全是音节噪声。模型生成的音频听起来隐约像印地语的轮廓,但完全无法理解。同一句子中的天城文 token(मुझेजानाहै)渲染得非常完美。只有拉丁字母/ASCII token 产生了乱码。这就是 Mode C 故障:模型用乱码填满了正确的画布,因为 ASCII 字符的输入字符 embedding 训练不足。IndicF5 是基于印度语系文字音频和天城文文本输入构建和训练的。Embedding 矩阵包含每个 Unicode 字符的条目,包括 A 到 Z。但这些条目从未在有意义的数据上训练过。模型接收到字符 k 的近似随机向量,并输出近似随机的声学结果。这不是权重的 bug,而是训练分布的缺失。

**我的第一直觉是微调。**在拉丁字母印地语数据上训练 ASCII embedding 行。那将耗费数周时间、数百 GPU 小时,并且需要我没有的标注数据。

我没有微调。我改用了预处理。

IndicXlit 修复方案:总共仅 14 行代码

如果模型处理不了 ASCII 输入,那就别给它 ASCII 输入。IndicXlit 是 AI4Bharat 开源的双向转写库。它将拉丁字母文本转换为天城文。kalकलofficeऑफिसyaarयार。核心洞察:评估流水线已经在使用 IndicXlit 来规范化转录文本以便评分。在输入送入模型之前,对输入应用同样的规范化。

from ai4bharat.transliteration import XlitEngine
engine = XlitEngine("hi", beam_width=4, src_script_type="en")

def to_unified_devanagari(text: str) -> str:
    """Convert Hinglish text (any script mix) to Devanagari for IndicF5 input."""
    out = []
    for token in tokenize(text):
        if is_devanagari(token):
            out.append(token)  # pass through unchanged
        elif is_ascii_alpha(token):
            out.append(translate(token))  # Roman → Devanagari
        else:
            out.append(token)  # punctuation, numbers: pass through
    return "".join(out)

预处理是对称的。规范化评分参考文本的同一个函数,也用于规范化 TTS 输入。模型接收的内容与评分衡量的内容之间不存在不对称。

添加预处理后的结果(评分标准 v2.0,三 ASR):

类别原始+时长补丁+预处理变化
pure_devanagari4.624.624.62+0.00
pure_roman1.001.004.38+3.38
mixed_script1.621.884.88+3.00
english_with_NE1.001.004.33+3.33
overall2.132.204.57+2.37
silence_or_skip20/3016/300/30全部修复

从 2.13 跃升至 4.57。零静音。无训练。无新权重。仅用 14 行核心逻辑就 bridging 了损坏模型与可用模型之间的鸿沟。(白名单表增加了 59 个额外条目——4 个功能词 + 36 个英语借词 + 19 个印度专有名词——那是数据,不是代码。)—

当 IndicXlit 出错时

IndicXlit 并不完美。在少数短 token 上,它会应用英语发音规则而非印地语发音规则。最严重的几个:

拉丁 token含义IndicXlit 输出正确应为
mai我(第一人称)माई(英语的 “my”)मैं
tu你(非正式)टू(英语的 “to”)तू
aaएए
hu是(第一人称)हू(丢失鼻音)हूं

这些是用拉丁字母书写的最常见印地语功能词。IndicXlit 看到它们会猜测是英语。像 mai ghar aa raha hu(我正在回家)这样的句子会变成 माई घर एए रहा हू。五个词错了三个。修复方法是在 IndicXlit 处理 token 之前运行一个查找表:

ROMAN_HINDI_FUNCTION_WORDS = {
    "mai": "मैं",  # "I" — IndicXlit gives "माई"
    "tu": "तू",    # "you" — IndicXlit gives "टू"
    "aa": "आ",     # "come" — IndicXlit gives "एए"
    "hu": "हूं",   # "am" — IndicXlit gives "हू"
}

类似地,Hinglish 中常见的英语借词有固定的标准天城文形式,IndicXlit 可能无法稳定输出:

ENGLISH_LOAN_CANONICAL = {
    "office": "ऑफिस",
    "laptop": "लैपटॉप",
    "presentation": "प्रेज़ेंटेशन",
    "leave": "लीव",
    "boss": "बॉस",
    "party": "पार्टी",
    # ... 36 entries total
}

第三个查找表 INDIAN_NE_CANONICAL 对印度专有名词(城市、人名、品牌)执行相同操作,这些词在 ASR 转录中经常被搞乱:

INDIAN_NE_CANONICAL = {
    "bengaluru": "बेंगलुरु",
    "chennai": "चेन्नई",
    "mumbai": "मुंबई",
    "aishwarya": "ऐश्वर्या",
    "tata": "टाटा",
    # ... 19 entries total (cities, people, brands)
}

添加全部三个白名单并重新运行评估(现为评分标准 v2.1):

类别v2.0v2.1变化
pure_roman4.384.75+0.38
mixed_script4.884.88+0.00
english_with_NE4.334.50+0.17
pure_devanagari4.624.62+0.00
overall4.574.70+0.13

5.0 分中的 4.70 分。最终得分。

与其他模型的对比

同样的 30 个句子。同样的评分标准。同样的三 ASR 流水线。五个模型:

模型总分pure_romanmixedeng_NEpure_dev
本工具包4.704.754.884.504.62
Kokoro v1.0 (Hindi)3.902.503.884.834.62
Indic Parler-TTS3.401.753.004.674.50
IndicF5 (unpatched)2.131.001.621.004.62
SPRINGLab F5-Hindi2.071.001.381.004.62

差距在 pure_roman 上最大,这是印度聊天、WhatsApp 和语音接口的主流输入格式。在最关键的项目上,领先 Kokoro 2.25 分。

Kokoro 在 english_with_NE 上领先(4.83 对 4.50),因为它有更深的英语训练基础,能原生处理英语模式下的专有名词发音。我们的工具包将 “Bengaluru” 转写为印地语发音。K

相似文章

@Honcia13: 开源TTS直接卷疯了!园区诈骗又有新武器? 清华 OpenBMB 刚刚放出 VoxCPM2: 200亿参数 + 200万小时多语言数据训练,48kHz录音棚级音质! 最狠的是——完全不用Tokenizer,直接在连续潜空间做扩散自回归,细…

X AI KOLs Timeline

清华大学 OpenBMB 发布了 VoxCPM2,这是一个拥有 200 亿参数的开源多语言 TTS 模型,支持无需 Tokenizer 的连续潜空间扩散自回归生成,具备 48kHz 录音棚级音质和强大的声音克隆与设计能力。