如何提升编程术语的多语言语音识别准确率?
结论
fine-tuning 通常不适合作为第一步,也不是唯一选择。可以先针对代码切换场景搭建完整的识别链路:
- 使用长连接 streaming,避免把语音切成缺少上下文的短片段。
- 为编程术语、缩写和产品名配置 vocabulary biasing、phrase hints 或 phrase list。
- 保留候选结果,再结合术语表、上下文和语言模型重新排序。
- 只对置信度较低且可能包含术语的时间区域进行局部重识别。
- 使用专门的术语错误率评估系统,再判断是否需要训练定制模型。
多语言课程的主要难点是 code-switching,也就是在一句话中频繁切换自然语言、英文术语和缩写。如果只是增加英文模型的调用次数,模型可能把母语中的相似发音识别成英文,产生更多错误。
不要把音频切得太短
短音频缺少上下文,模型很难判断一个音节究竟是普通词、英文术语还是字母缩写。静音、键盘声和句尾残音也更容易被补成并不存在的内容。
实时处理应优先使用持续的 streaming session,由 VAD 和语义停顿共同确定切片位置。如果必须自行切片,可以保留少量重叠区域:
chunk 1: [0.0s ---------------- 6.0s]
chunk 2: [5.2s ---------------- 11.2s]
chunk 3: [10.4s --------]
这里的 0.8s 重叠只是示例,实际值需要根据语速、网络延迟和验证集调整。合并结果时,应比较重叠区域内的 token 或词序列,不能直接拼接字符串。
还需要区分两类边界:
- VAD 用于判断说话是否暂停。
- Endpointing 用于判断一句话是否可以提交。
遇到短暂停顿时,可以输出 provisional result,同时保留识别上下文。最终字幕应等到句子边界较稳定时再提交。
给识别器提供术语上下文
大多数商业 STT 服务都支持某种形式的 phrase hints、speech context、phrase list 或 custom vocabulary。不同服务、区域和 API 版本提供的名称与能力可能不同,具体应以所用接口的文档为准。
术语表除了标准拼写,还应记录可能的读法:
[
{
"canonical": "PostgreSQL",
"spoken_forms": ["Postgres", "Postgre S Q L"]
},
{
"canonical": "SQL",
"spoken_forms": ["S Q L", "sequel"]
},
{
"canonical": "C++",
"spoken_forms": ["C plus plus"]
},
{
"canonical": "kubectl",
"spoken_forms": ["cube control", "cube C T L"]
},
{
"canonical": "NumPy",
"spoken_forms": ["num pie", "numpy"]
}
]
术语表适合按课程动态加载。Python 课程可以优先加入 NumPy、pandas 和 PyTorch,数据库课程则加入 PostgreSQL、ACID 和 MVCC。一次加入大量与课程无关的英文词,会扩大搜索空间,也可能提高误识别率。
如果 API 支持设置权重,应使用验证集调节。权重太低可能不起作用,太高又可能让模型把普通词强行识别成术语。
不要直接按两个 streams 的 timestamps 合并
不同语言模型在 tokenization、停顿处理和时间戳定义上可能存在差异。即使处理同一段音频,两个模型给出的词边界也不会自然对齐。按绝对 timestamp 逐词选择高概率结果并不可靠。
可以采用下面的处理结构:
音频
→ 主识别器生成文本、时间区间和候选结果
→ 检测低置信度区间
→ 仅对疑似英文术语的区间运行辅助识别
→ 对齐候选序列
→ 根据术语表和上下文重排序
候选序列可以通过动态规划来对齐,例如使用基于编辑距离或 Dynamic Time Warping 的方法。时间戳只负责限制搜索范围,不能作为唯一的匹配条件。
如果服务能够返回 N-best hypotheses,应先尝试重排这些候选,避免重新识别整句话。正确术语有时已经出现在第二或第三候选中。
限制英文模型的替换范围
对每个低置信度片段都调用 English model,容易引入大量多余的英文词。低 confidence 只能说明模型不确定,无法证明对应位置一定是英文。
满足以下条件时,再启动英文重识别:
- 当前片段的置信度较低。
- 前后文涉及函数、框架、命令、文件名或 API。
- 英文候选在拼写或发音上接近术语表中的词。
- 新候选的得分比原候选有足够提升。
- 替换后没有破坏整句的语法和时间边界。
下面是一段简化的后处理示例。代码中的阈值仅用于说明逻辑,实际使用前需要根据数据重新标定:
from dataclasses import dataclass
from difflib import SequenceMatcher
@dataclass
class Token:
text: str
confidence: float
start: float
end: float
GLOSSARY = {
"postgres": "PostgreSQL",
"postgre sql": "PostgreSQL",
"c plus plus": "C++",
"num pie": "NumPy",
"cube control": "kubectl",
}
def similarity(a: str, b: str) -> float:
return SequenceMatcher(None, a.lower(), b.lower()).ratio()
def find_glossary_candidate(text: str):
best_key = None
best_score = 0.0
for spoken_form in GLOSSARY:
score = similarity(text, spoken_form)
if score > best_score:
best_key = spoken_form
best_score = score
if best_key is None:
return None, 0.0
return GLOSSARY[best_key], best_score
def normalize_token(token: Token) -> str:
# 示例阈值,不应直接用于生产环境
if token.confidence >= 0.65:
return token.text
candidate, score = find_glossary_candidate(token.text)
if candidate is not None and score >= 0.82:
return candidate
return token.text
生产系统还需要参考课程主题、相邻词、音素距离和 N-best 分数。字符串相似度本身无法覆盖所有发音变体。
在识别后增加受约束的术语纠错
识别完成后,可以使用语言模型或规则系统修正术语格式,例如:
see plus plus → C++
pie torch → PyTorch
node jay ess → Node.js
get request → GET request
纠错范围必须受术语表约束,同时保留原始识别文本、候选和分数。如果让通用生成模型自由改写整段字幕,它可能删除口误、补入原音频中不存在的代码,或擅自把讲师的原意改得更通顺。
纠错输入可以采用下面的结构:
{
"course_topic": "Python data processing",
"recognized_text": "我们先用 num pie 创建一个 array",
"allowed_terms": ["NumPy", "array", "pandas"],
"instruction": "只修正 allowed_terms 对应的识别错误,不改写其他内容"
}
即使设置了这些约束,也要验证每次替换是否能对应到原音频区间。
单独处理缩写和代码符号
API、SDK、SQL 既可能逐个字母读,也可能作为单词读。C#、C++、.NET 和 Node.js 还包含普通 ASR 不擅长输出的符号。
处理时可以分成两步:
- 先让 ASR 识别可发音的形式,例如
C sharp、C plus plus。 - 再由规范化模块根据术语表转换为
C#、C++。
这类转换不能全局执行。例如,普通英语中的 “sharp” 不一定表示 #。只有课程主题、相邻词和术语候选都支持时,才能进行转换。
代码内容还要区分“讲解文字”和“逐字符听写”。讲师开始拼写变量名时,系统需要进入 spelling mode,分别处理字母、数字、下划线和大小写提示。普通句子模型通常无法准确还原代码。
改善音频输入同样重要
比较模型之前,应先统一音频条件:
- 避免反复进行有损编码。
- 保持采样率、声道数和音量稳定。
- 控制回声、背景音乐和键盘声。
- streaming 过程中不要丢帧,也不要在每个小片段的首尾截断音素。
- 如果音频中同时有讲师声和翻译播报声,应使用独立音轨或回声消除,防止合成语音被重新送入 ASR。
过强的音频预处理也可能损伤辅音和字母读音,因此需要用真实课程录音验证降噪强度。
什么时候才需要 fine-tuning
完成切片、术语偏置、候选重排和后处理后,如果仍有大量能够稳定复现的术语错误,再考虑 fine-tuning 或供应商提供的 Custom Speech 能力。
训练数据至少需要覆盖:
- 实际授课语言和口音。
- 自然出现的中英文切换位置。
- 真实的麦克风、房间噪声和网络音频条件。
- 字母缩写的不同读法。
- 术语在完整句子中的读法,而非只有孤立单词。
- 正确且一致的标注规范。
只用合成语音或术语单词表训练,可能在测试样本上得到明显改善,却无法反映真实课堂的效果。课程主题、框架名称和讲师口音都会变化,定制模型也需要持续维护。
对于托管 STT,“fine-tuning” 不一定意味着可以直接更新底层模型参数。部分服务只提供声学适配、文本数据适配、自定义词汇或定制端点,实际可用能力取决于产品和部署区域。
用术语指标评估,不要只看整体 WER
整体 Word Error Rate 可能掩盖术语识别问题。课程中普通句子的占比通常很高,即使所有框架名称都识别错误,WER 仍可能看起来可以接受。
除了 WER,还可以单独计算术语错误率:
[ TER = \frac{S_t + D_t + I_t}{N_t} ]
其中:
- (S_t) 是术语替换数。
- (D_t) 是术语删除数。
- (I_t) 是错误插入的术语数。
- (N_t) 是参考文本中的术语总数。
测试集应按照语言、讲师、课程主题、口音、噪声条件和 code-switching 密度分组。实时系统还要记录首个 provisional result 的延迟、最终结果延迟,以及字幕被反复改写的次数。
推荐的实施顺序
先用真实课程录音建立固定测试集,再按以下顺序优化:
- 统一音频输入并检查是否丢帧。
- 改用连续 streaming,调整 VAD 和 endpointing。
- 根据课程加载规模较小且内容相关的术语表。
- 保存 N-best、confidence 和时间区间。
- 对可能包含术语的低置信度区域进行局部重识别。
- 使用受约束的术语规范化和候选重排。
- 分别统计 WER、TER、错误插入率和实时延迟。
- 如果错误仍有稳定规律,再使用定制模型或 fine-tuning。
按照这套流程,更容易定位具体环节的问题,也方便在 Whisper、Google STT、Azure STT 和其他识别引擎之间迁移。
备注:内容仅供参考。
