如何提高语音识别准确率:八个实用方法
「识别得不好」不是诊断结论。在几乎所有真实案例里,质量问题都出自少数几个具体原因——而其中大部分是在你这一侧解决的,在音频离开你的服务器之前。下面按值得尝试的先后顺序列出。
先测量,不要猜
没有数字,任何改动都只是口味问题。取 20 到 30 条和线上流量分布相似的录音,写下实际说了什么,然后计算字错率:替换加删除加插入,除以说出的词数。
pip install jiwer
from jiwer import wer
score = wer(reference_text, recognised_text)
print(f"WER: {score:.1%}")
有两点比绝对数值更重要。第一,在你自己的音频上测量,而不是公开基准。第二,按你在意的东西加权:如果业务需要订单号,那么 5% 的字错率配上全错的数字是失败,15% 的字错率配上干净的数字才是成功。
方法一:给模型干净的音频
识别模型内部使用 16 kHz 单声道。超出的部分会被丢弃,缺失的部分永远补不回来:
- 不要二次压缩。mp3 转 wav 再转 mp3 的文件,两轮的失真都留在里面。
- 不要上采样。把 8 kHz 的电话音频转成 44 kHz,只是增加字节,不增加信息。
- 混成单声道,前提是两个声道是同一段对话;如果两个声道是不同的人,就保持分离。
ffmpeg -i input.wav -ac 1 -ar 16000 -b:a 64k clean.mp3
重度降噪是那个通常会适得其反的例外:激进的滤波会啃掉辅音,识别反而更差。能在麦克风端解决噪音就在麦克风端解决。
方法二:说明这是什么语种
自动判定在长录音上几乎总是对的,在三秒的短片段上明显差一些。如果音频流是单一语种,就直接说明:
r = client.audio.transcriptions.create(
model="whisper-1", file=f, language="zh",
)
这消除了一整类故障——短句被判成另一种语言,然后返回一堆不知所云的内容。同时也略微加快处理,因为跳过了语种判定这一步。
方法三:用 prompt 喂进词表
价值最高、也最常被忽略的一个参数。prompt 不会出现在响应里,它把识别向你列出的词偏移:
r = client.audio.transcriptions.create(
model="whisper-1", file=f,
prompt="Products: Kestrel, Kestrel Pro, Hawknest. Agents: Duarte, Ivanova, Ng.",
)
写得短而具体。20 到 40 个真实出现过的术语有效;一整段泛泛的描述没有任何作用。按场景轮换词表:计费相关的工单队列和硬件相关的队列,需要的术语并不一样。
提示词还携带风格。如果你希望转录保留标点和大小写,就把提示写成一个标点完整的句子——模型倾向于沿用给它的风格。
方法四:切掉静音
长段静音正是幻觉的来源:没有东西可识别时,模型可能自行产出填充文本。它同时也在花钱,因为计费按音频分钟。
ffmpeg -i input.mp3 -af silenceremove=start_periods=1:stop_periods=-1:stop_duration=2:stop_threshold=-45dB out.mp3
阈值要小心:切得太狠会把轻声说话连同静音一起删掉。放进流水线之前先在几个文件上验证效果。
方法五:按停顿切,不要按时钟切
长录音无论如何都要切开,才能装进 25 MB 的限制。切在哪里很关键:切点落在句子中间,两侧的词都会丢。
# 按静音而不是固定分钟切分
ffmpeg -i long.mp3 -af silencedetect=noise=-40dB:d=0.7 -f null - 2> pauses.txt
把检测到的停顿作为候选切点,每段保持在 5 到 20 分钟。更短会丢失段与段之间的上下文,更长则没有额外好处。
方法六:选择带置信度的响应格式
verbose_json 返回的不只是文本,还有分段和带概率的语种判定结果。这是自动质检的原料:
r = client.audio.transcriptions.create(
model="whisper-1", file=f, response_format="verbose_json",
)
if r.language_probability < 0.6:
queue_for_review(r) # 可疑录音,转人工检查
把可疑的那百分之几路由到人工复核队列,对整条流水线质量的提升,远比继续压榨识别本身最后那一个百分点划算。
方法七:做后处理,而不是重新识别
有些错误是系统性的:同一个产品名每次都以同样的方式识别错。一份替换词典一行就修好,而且不花钱:
FIXES = {"cast rail": "Kestrel", "hawk nest": "Hawknest"}
for wrong, right in FIXES.items():
text = text.replace(wrong, right)
词典要根据真实见过的错误来建,不要凭想象。一周生产流量里攒下的十条替换,通常比任何参数调优都更能消除可见的错误。
方法八:处理空结果和编造内容
有两种失败模式值得在代码里显式处理:空结果,以及可疑的泛泛结果。幻觉过滤器会切掉片尾字幕、「感谢观看」这类经典情况,所以空字符串确实意味着没有语音。
text = r.text.strip()
if not text:
return None # 是静音而不是错误,不要重试
对空录音重试只会消耗分钟数。记一条日志,跳过,继续下一条。
哪些做法没有用
- 把 8 kHz 录制的音频提高采样率。信息已经没了。
- 提交视频文件而不是音轨。画面不会被使用。
- 写很长的 prompt。只有开头部分被采纳,一大段文字反而稀释了真正重要的术语。
- 对同一个文件反复重试,指望这次运气好。结果是稳定的;要改就改输入或参数。
用你自己的录音试一试。 注册只需一分钟,免费额度足够判断识别质量。
免费获取 API 密钥常见问题
字错率应该在什么水平?
在支持良好的语种上、语音清晰时,个位数百分比是正常的。电话音频、背景噪音、抢话和浓重口音都会推高它。请在自己的录音上测量——公开基准说明不了你的流量。
prompt 参数真的有用吗?
有用,而且是成本最低的改进手段。把音频中出现的人名、产品和编号列成一份短清单,会明显减少最关键那些词上的错误。
应该指定语种还是让它自动判定?
音频流是单一语种时就指定,短片段尤其如此。消息确实来自不同语种时,保留自动判定。
发送前值得降噪吗?
轻度归一化有帮助;激进降噪通常有害,因为它会损伤辅音。噪音严重时,改善录音环境比任何滤波器都管用。
长录音每段切多大合适?
5 到 20 分钟,按停顿切而不是按固定间隔切。这样句子保持完整,也留在 25 MB 的请求限制之内。
相关阅读
- 如何把通话录音转成文字 — 分步教程:几分钟内通过 API 把电话录音转成文字,包含 Python 和 C# 代码示例、响应格式说明,以及常见错误的处理办法。
- 语音消息转文字:Telegram、WhatsApp 及其他即时通讯工具 — 如何自动把语音消息转成文字:处理 ogg/opus 格式、提升短录音准确率、应对静音,以及可直接使用的聊天机器人代码。
- 如何为音频存档搭建批量转录流水线 — 转录上万条录音而不丢失任何一条:任务队列、并发控制、重试策略、崩溃后续跑,以及如何把账单控制在预期之内。